001/* An IOPort for controllers and refinements in modal models.
002
003 Copyright (c) 2006-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.actor.lib.hoc;
029
030import ptolemy.actor.IOPort;
031import ptolemy.actor.IORelation;
032import ptolemy.actor.TypedIOPort;
033import ptolemy.kernel.ComponentEntity;
034import ptolemy.kernel.Entity;
035import ptolemy.kernel.Port;
036import ptolemy.kernel.Relation;
037import ptolemy.kernel.util.IllegalActionException;
038import ptolemy.kernel.util.NameDuplicationException;
039import ptolemy.kernel.util.Nameable;
040import ptolemy.kernel.util.NamedObj;
041import ptolemy.kernel.util.Workspace;
042
043///////////////////////////////////////////////////////////////////
044//// RefinementPort
045
046/**
047 A port for controllers and refinements in modal models.  This port
048 mirrors certain changes to it in the ports of the container of the container.
049 That container in turn mirrors those changes in other refinements and/or
050 controllers.  This class is designed to work closely with ModalPort,
051 since changes to the ports can be initiated from either class.
052
053 @author Edward A. Lee
054 @version $Id$
055 @since Ptolemy II 5.2
056 @Pt.ProposedRating Red (eal)
057 @Pt.AcceptedRating Red (liuxj)
058 */
059public class RefinementPort extends TypedIOPort {
060    /** Construct a port in the given workspace.
061     *  @param workspace The workspace.
062     *  @exception IllegalActionException If the port is not of an acceptable
063     *   class for the container, or if the container does not implement the
064     *   TypedActor interface.
065     */
066    public RefinementPort(Workspace workspace) throws IllegalActionException {
067        super(workspace);
068        _checkWhetherMirrorIsInput();
069    }
070
071    /** Construct a port with a containing actor and a name
072     *  that is neither an input nor an output.  The specified container
073     *  must implement the TypedActor interface, or an exception will be
074     *  thrown.
075     *  @param container The container actor.
076     *  @param name The name of the port.
077     *  @exception IllegalActionException If the port is not of an acceptable
078     *   class for the container, or if the container does not implement the
079     *   TypedActor interface.
080     *  @exception NameDuplicationException If the name coincides with
081     *   a port already in the container.
082     */
083    public RefinementPort(ComponentEntity container, String name)
084            throws IllegalActionException, NameDuplicationException {
085        super(container, name);
086        _checkWhetherMirrorIsInput();
087    }
088
089    ///////////////////////////////////////////////////////////////////
090    ////                         public methods                    ////
091
092    /** Override the super class method to allow UNKNOWN type if this
093     *  port does not have any inside links.
094     *  @return True if this port does not have any inside links,
095     *   or the super class method returns true.
096     */
097    @Override
098    public boolean isTypeAcceptable() {
099        if (numInsideLinks() == 0) {
100            return true;
101        } else {
102            return super.isTypeAcceptable();
103        }
104    }
105
106    /** Set the connected relation to a bus if this port is a multiport.
107     */
108    @Override
109    public void link(Relation relation) throws IllegalActionException {
110        super.link(relation);
111
112        if (isMultiport()) {
113            ((IORelation) relation).setWidth(IORelation.WIDTH_TO_INFER);
114        }
115    }
116
117    /** Move this object down by one in the list of attributes of
118     *  its container. If this object is already last, do nothing.
119     *  This method overrides the base class to mirror the change
120     *  in any mirror ports.
121     *  Increment the version of the workspace.
122     *  @return The index of the specified object prior to moving it,
123     *   or -1 if it is not moved.
124     *  @exception IllegalActionException If this object has
125     *   no container.
126     */
127    @Override
128    public int moveDown() throws IllegalActionException {
129        boolean disableStatus = _mirrorDisable;
130
131        try {
132            _workspace.getWriteAccess();
133
134            int result = -1;
135
136            if (_mirrorDisable || getContainer() == null) {
137                result = super.moveDown();
138            } else {
139                _mirrorDisable = true;
140
141                boolean success = false;
142                Nameable container = getContainer();
143
144                if (container != null) {
145                    Nameable modal = container.getContainer();
146
147                    if (modal instanceof MultiCompositeActor) {
148                        Port port = ((MultiCompositeActor) modal)
149                                .getPort(getName());
150
151                        if (port instanceof IOPort) {
152                            ((IOPort) port).moveDown();
153                            success = true;
154                        }
155                    }
156                }
157
158                // The mirror port(s), if there are any,
159                // will have called this method and achieved
160                // the moveDown. But if there are no mirror
161                // ports, we need to do it here.
162                if (!success) {
163                    result = super.moveDown();
164                }
165            }
166
167            return result;
168        } finally {
169            _mirrorDisable = disableStatus;
170            _workspace.doneWriting();
171        }
172    }
173
174    /** Move this object to the first position in the list
175     *  of attributes of the container. If  this object is already first,
176     *  do nothing. Increment the version of the workspace.
177     *  This method overrides the base class to mirror the change
178     *  in any mirror ports.
179     *  @return The index of the specified object prior to moving it,
180     *   or -1 if it is not moved.
181     *  @exception IllegalActionException If this object has
182     *   no container.
183     */
184    @Override
185    public int moveToFirst() throws IllegalActionException {
186        boolean disableStatus = _mirrorDisable;
187
188        try {
189            _workspace.getWriteAccess();
190
191            int result = -1;
192
193            if (_mirrorDisable || getContainer() == null) {
194                result = super.moveToFirst();
195            } else {
196                _mirrorDisable = true;
197
198                boolean success = false;
199                Nameable container = getContainer();
200
201                if (container != null) {
202                    Nameable modal = container.getContainer();
203
204                    if (modal instanceof MultiCompositeActor) {
205                        Port port = ((MultiCompositeActor) modal)
206                                .getPort(getName());
207
208                        if (port instanceof IOPort) {
209                            ((IOPort) port).moveToFirst();
210                            success = true;
211                        }
212                    }
213                }
214
215                // The mirror port(s), if there are any,
216                // will have called this method and achieved
217                // the moveToFirst. But if there are no mirror
218                // ports, we need to do it here.
219                if (!success) {
220                    result = super.moveToFirst();
221                }
222            }
223
224            return result;
225        } finally {
226            _mirrorDisable = disableStatus;
227            _workspace.doneWriting();
228        }
229    }
230
231    /** Move this object to the specified position in the list
232     *  of attributes of the container. If this object is already at the
233     *  specified position, do nothing.
234     *  This method overrides the base class to mirror the change
235     *  in any mirror ports.
236     *  Increment the version of the workspace.
237     *  @param index The position to move this object to.
238     *  @return The index of the specified object prior to moving it,
239     *   or -1 if it is not moved.
240     *  @exception IllegalActionException If this object has
241     *   no container or if the index is out of bounds.
242     */
243    @Override
244    public int moveToIndex(int index) throws IllegalActionException {
245        boolean disableStatus = _mirrorDisable;
246
247        try {
248            _workspace.getWriteAccess();
249
250            int result = -1;
251
252            if (_mirrorDisable || getContainer() == null) {
253                result = super.moveToIndex(index);
254            } else {
255                _mirrorDisable = true;
256
257                boolean success = false;
258                Nameable container = getContainer();
259
260                if (container != null) {
261                    Nameable modal = container.getContainer();
262
263                    if (modal instanceof MultiCompositeActor) {
264                        Port port = ((MultiCompositeActor) modal)
265                                .getPort(getName());
266
267                        if (port instanceof IOPort) {
268                            ((IOPort) port).moveToIndex(index);
269                            success = true;
270                        }
271                    }
272                }
273
274                // The mirror port(s), if there are any,
275                // will have called this method and achieved
276                // the moveToIndex. But if there are no mirror
277                // ports, we need to do it here.
278                if (!success) {
279                    result = super.moveToIndex(index);
280                }
281            }
282
283            return result;
284        } finally {
285            _mirrorDisable = disableStatus;
286            _workspace.doneWriting();
287        }
288    }
289
290    /** Move this object to the last position in the list
291     *  of attributes of the container.  If this object is already last,
292     *  do nothing. This method overrides the base class to mirror the change
293     *  in any mirror ports.
294     *  Increment the version of the workspace.
295     *  @return The index of the specified object prior to moving it,
296     *   or -1 if it is not moved.
297     *  @exception IllegalActionException If this object has
298     *   no container.
299     */
300    @Override
301    public int moveToLast() throws IllegalActionException {
302        boolean disableStatus = _mirrorDisable;
303
304        try {
305            _workspace.getWriteAccess();
306
307            int result = -1;
308
309            if (_mirrorDisable || getContainer() == null) {
310                result = super.moveToLast();
311            } else {
312                _mirrorDisable = true;
313
314                boolean success = false;
315                Nameable container = getContainer();
316
317                if (container != null) {
318                    Nameable modal = container.getContainer();
319
320                    if (modal instanceof MultiCompositeActor) {
321                        Port port = ((MultiCompositeActor) modal)
322                                .getPort(getName());
323
324                        if (port instanceof IOPort) {
325                            ((IOPort) port).moveToLast();
326                            success = true;
327                        }
328                    }
329                }
330
331                if (!success) {
332                    result = super.moveToLast();
333                }
334            }
335
336            return result;
337        } finally {
338            _mirrorDisable = disableStatus;
339            _workspace.doneWriting();
340        }
341    }
342
343    /** Move this object up by one in the list of
344     *  attributes of the container. If  this object is already first, do
345     *  nothing.
346     *  This method overrides the base class to mirror the change
347     *  in any mirror ports.
348     *  Increment the version of the workspace.
349     *  @return The index of the specified object prior to moving it,
350     *   or -1 if it is not moved.
351     *  @exception IllegalActionException If this object has
352     *   no container.
353     */
354    @Override
355    public int moveUp() throws IllegalActionException {
356        boolean disableStatus = _mirrorDisable;
357
358        try {
359            _workspace.getWriteAccess();
360
361            int result = -1;
362
363            if (_mirrorDisable || getContainer() == null) {
364                result = super.moveUp();
365            } else {
366                _mirrorDisable = true;
367
368                boolean success = false;
369                Nameable container = getContainer();
370
371                if (container != null) {
372                    Nameable modal = container.getContainer();
373
374                    if (modal instanceof MultiCompositeActor) {
375                        Port port = ((MultiCompositeActor) modal)
376                                .getPort(getName());
377
378                        if (port instanceof IOPort) {
379                            ((IOPort) port).moveUp();
380                            success = true;
381                        }
382                    }
383                }
384
385                // The mirror port(s), if there are any,
386                // will have called this method and achieved
387                // the moveUp. But if there are no mirror
388                // ports, we need to do it here.
389                if (!success) {
390                    result = super.moveUp();
391                }
392            }
393
394            return result;
395        } finally {
396            _mirrorDisable = disableStatus;
397            _workspace.doneWriting();
398        }
399    }
400
401    /** Override the base class so that if the port is being removed
402     *  from the current container, then it is also removed from the
403     *  controller and from each of the refinements.
404     *  @param container The proposed container.
405     *  @exception IllegalActionException If the proposed container is not a
406     *   ComponentEntity, doesn't implement Actor, or has no name,
407     *   or the port and container are not in the same workspace. Or
408     *   it's not null
409     *  @exception NameDuplicationException If the container already has
410     *   a port with the name of this port.
411     */
412    @Override
413    public void setContainer(Entity container)
414            throws IllegalActionException, NameDuplicationException {
415        NamedObj oldContainer = getContainer();
416
417        if (container == oldContainer) {
418            // Nothing to do.
419            return;
420        }
421
422        boolean disableStatus = _mirrorDisable;
423
424        try {
425            _workspace.getWriteAccess();
426
427            if (_mirrorDisable || getContainer() == null) {
428                // Have already called the super class.
429                // This time, process the request.
430                super.setContainer(container);
431            } else {
432                _mirrorDisable = true;
433
434                boolean success = false;
435
436                if (oldContainer != null) {
437                    Nameable modal = oldContainer.getContainer();
438
439                    if (modal instanceof MultiCompositeActor) {
440                        Port port = ((MultiCompositeActor) modal)
441                                .getPort(getName());
442
443                        if (port != null) {
444                            port.setContainer(null);
445                            success = true;
446                        }
447                    }
448                }
449
450                if (!success) {
451                    super.setContainer(container);
452                }
453            }
454        } finally {
455            _mirrorDisable = disableStatus;
456            _workspace.doneWriting();
457        }
458    }
459
460    /** If the argument is true, make the port an input port.
461     *  If the argument is false, make the port not an input port.
462     *  This method overrides the base class to make the same
463     *  change on the mirror ports in the controller and state refinments.
464     *  This method invalidates the schedule and resolved types of the
465     *  director of the container, if there is one.
466     *  It is write-synchronized on the workspace, and increments
467     *  the version of the workspace.
468     *  @param isInput True to make the port an input.
469     *  @exception IllegalActionException If changing the port status is
470     *   not permitted.
471     */
472    @Override
473    public void setInput(boolean isInput) throws IllegalActionException {
474        boolean disableStatus = _mirrorDisable;
475
476        try {
477            _workspace.getWriteAccess();
478
479            if (_mirrorDisable || getContainer() == null
480                    || _automaticallyInput) {
481                // Do not mirror.
482                super.setInput(isInput);
483            } else {
484                _mirrorDisable = true;
485
486                boolean success = false;
487                Nameable container = getContainer();
488
489                if (container != null) {
490                    Nameable modal = container.getContainer();
491
492                    if (modal instanceof MultiCompositeActor) {
493                        Port port = ((MultiCompositeActor) modal)
494                                .getPort(getName());
495
496                        if (port instanceof IOPort) {
497                            ((IOPort) port).setInput(isInput);
498                            success = true;
499                        }
500                    }
501                }
502
503                if (!success) {
504                    super.setInput(isInput);
505                }
506            }
507        } finally {
508            _mirrorDisable = disableStatus;
509            _workspace.doneWriting();
510        }
511    }
512
513    /** Control whether any change should be mirrored in the modal
514     *  model, mode controller, and refinements.
515     *  This is added to allow control by the UI.
516     *  @param disable True if mirroring should not occur.
517     */
518    public void setMirrorDisable(boolean disable) {
519        _mirrorDisable = disable;
520    }
521
522    /** If the argument is true, make the port a multiport.
523     *  If the argument is false, make the port not a multiport.
524     *  This method overrides the base class to make the same
525     *  change on the mirror ports in the controller and state refinments.
526     *  This method invalidates the schedule and resolved types of the
527     *  director of the container, if there is one.
528     *  It is write-synchronized on the workspace, and increments
529     *  the version of the workspace.
530     *  @param isMultiport True to make the port a multiport.
531     *  @exception IllegalActionException If changing the port status is
532     *   not permitted.
533     */
534    @Override
535    public void setMultiport(boolean isMultiport)
536            throws IllegalActionException {
537        boolean disableStatus = _mirrorDisable;
538
539        try {
540            _workspace.getWriteAccess();
541
542            if (_mirrorDisable || getContainer() == null) {
543                // Do not mirror.
544                super.setMultiport(isMultiport);
545            } else {
546                _mirrorDisable = true;
547
548                boolean success = false;
549                Nameable container = getContainer();
550
551                if (container != null) {
552                    Nameable modal = container.getContainer();
553
554                    if (modal instanceof MultiCompositeActor) {
555                        Port port = ((MultiCompositeActor) modal)
556                                .getPort(getName());
557
558                        if (port instanceof IOPort) {
559                            ((IOPort) port).setMultiport(isMultiport);
560                            success = true;
561                        }
562                    }
563                }
564
565                if (!success) {
566                    super.setMultiport(isMultiport);
567                }
568            }
569        } finally {
570            _mirrorDisable = disableStatus;
571            _workspace.doneWriting();
572        }
573    }
574
575    /** Set the name of the port, and mirror the change in all the
576     *  mirror ports.
577     *  This method is write-synchronized on the workspace, and increments
578     *  the version of the workspace.
579     *  @exception IllegalActionException If the name has a period.
580     *  @exception NameDuplicationException If there is already a port
581     *   with the same name in the container.
582     */
583    @Override
584    public void setName(String name)
585            throws IllegalActionException, NameDuplicationException {
586        boolean disableStatus = _mirrorDisable;
587
588        try {
589            _workspace.getWriteAccess();
590
591            if (_mirrorDisable || getContainer() == null) {
592                // Do not mirror.
593                super.setName(name);
594            } else {
595                _mirrorDisable = true;
596
597                boolean success = false;
598                Nameable container = getContainer();
599
600                if (container != null) {
601                    Nameable modal = container.getContainer();
602
603                    if (modal instanceof MultiCompositeActor) {
604                        Port port = ((MultiCompositeActor) modal)
605                                .getPort(getName());
606
607                        if (port != null) {
608                            port.setName(name);
609                            success = true;
610                        }
611                    }
612                }
613
614                if (!success) {
615                    super.setName(name);
616                }
617            }
618        } finally {
619            _mirrorDisable = disableStatus;
620            _workspace.doneWriting();
621        }
622    }
623
624    /** If the argument is true, make the port an output port.
625     *  If the argument is false, make the port not an output port.
626     *  In addition, if the container is an instance of Refinement,
627     *  and the argument is true, find the corresponding port of the
628     *  controller and make it an input and not an output.  This makes
629     *  it possible for the controller to see the outputs of the refinements.
630     *  This method overrides the base class to make the same
631     *  change on the mirror ports in the controller and state refinments.
632     *  This method invalidates the schedule and resolved types of the
633     *  director of the container, if there is one.
634     *  It is write-synchronized on the workspace, and increments
635     *  the version of the workspace.
636     *  @param isOutput True to make the port an output.
637     *  @exception IllegalActionException If changing the port status is
638     *   not permitted.
639     */
640    @Override
641    public void setOutput(boolean isOutput) throws IllegalActionException {
642        boolean disableStatus = _mirrorDisable;
643
644        try {
645            _workspace.getWriteAccess();
646
647            if (_mirrorDisable || getContainer() == null) {
648                // Do not mirror.
649                super.setOutput(isOutput);
650            } else {
651                _mirrorDisable = true;
652
653                boolean success = false;
654                Nameable container = getContainer();
655
656                if (container != null) {
657                    Nameable modal = container.getContainer();
658
659                    if (modal instanceof MultiCompositeActor) {
660                        Port port = ((MultiCompositeActor) modal)
661                                .getPort(getName());
662
663                        if (port instanceof IOPort) {
664                            ((IOPort) port).setOutput(isOutput);
665                            success = true;
666                        }
667                    }
668                }
669
670                if (!success) {
671                    super.setOutput(isOutput);
672                }
673            }
674        } finally {
675            _mirrorDisable = disableStatus;
676            _workspace.doneWriting();
677        }
678    }
679
680    ///////////////////////////////////////////////////////////////////
681    ////                         protected variables               ////
682
683    /** Indicator that the port is automatically an input port
684     *  because it was set to be an output port. This automatic change
685     *  is made in output ports of the ModalController so that if
686     *  a refinement writes to its output port, then that data is
687     *  available in scope for the guard expressions.
688     */
689    protected boolean _automaticallyInput = false;
690
691    // This is protected to be accessible to ModalPort.
692
693    /** If false, then changes the port are mirrored in the container's
694     *  container. This is false by default.
695     */
696    protected boolean _mirrorDisable = false;
697
698    ///////////////////////////////////////////////////////////////////
699    ////                         private methods                   ////
700
701    /** If the container is a MultiCompositeActor and has a mirror port to this
702     *  one that is not an input and is an output, then mark this port
703     *  so that if it becomes an input, that input property is not
704     *  reflected to the mirror port.
705     */
706    private void _checkWhetherMirrorIsInput() {
707        // Need to check whether there is a containing MultiCompositeActor,
708        // and whether its mirror port is also an input.
709        Nameable container = getContainer();
710
711        if (container != null) {
712            Nameable modal = container.getContainer();
713
714            if (modal instanceof MultiCompositeActor) {
715                Port port = ((MultiCompositeActor) modal).getPort(getName());
716
717                if (port instanceof IOPort) {
718                    if (!((IOPort) port).isInput()
719                            && ((IOPort) port).isOutput()) {
720                        _automaticallyInput = true;
721                    }
722                }
723            }
724        }
725    }
726}