001/* An IOPort for multi composite actors.
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 java.util.Iterator;
031
032import ptolemy.actor.TypedIOPort;
033import ptolemy.kernel.ComponentEntity;
034import ptolemy.kernel.ComponentRelation;
035import ptolemy.kernel.Entity;
036import ptolemy.kernel.Port;
037import ptolemy.kernel.Relation;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.kernel.util.NameDuplicationException;
040import ptolemy.kernel.util.Workspace;
041
042///////////////////////////////////////////////////////////////////
043//// MultiCompositePort
044
045/**
046 A port for multi-composite actors.  This port mirrors certain changes to it in
047 the ports of the controller and refinements of the modal model. It is
048 designed to work closely with RefinementPort, since changes to the
049 ports can be initiated in either class.
050
051 @see RefinementPort
052 @see MultiCompositeActor
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 MultiCompositePort 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 MultiCompositePort(Workspace workspace)
067            throws IllegalActionException {
068        super(workspace);
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 MultiCompositePort(ComponentEntity container, String name)
084            throws IllegalActionException, NameDuplicationException {
085        super(container, name);
086    }
087
088    ///////////////////////////////////////////////////////////////////
089    ////                         public methods                    ////
090
091    /** Move this object down by one in the list of attributes of
092     *  its container. If this object is already last, do nothing.
093     *  This method overrides the base class to mirror the change
094     *  in any mirror ports.
095     *  Increment the version of the workspace.
096     *  @return The index of the specified object prior to moving it,
097     *   or -1 if it is not moved.
098     *  @exception IllegalActionException If this object has
099     *   no container.
100     */
101    @Override
102    public int moveDown() throws IllegalActionException {
103        try {
104            _workspace.getWriteAccess();
105
106            int result = super.moveDown();
107
108            if (result != -1) {
109                // Mirror the change in mirror ports.
110                MultiCompositeActor container = (MultiCompositeActor) getContainer();
111                Iterator entities = container.entityList().iterator();
112
113                while (entities.hasNext()) {
114                    Entity entity = (Entity) entities.next();
115                    Port mirrorPort = entity.getPort(getName());
116
117                    if (mirrorPort instanceof RefinementPort) {
118                        RefinementPort castPort = (RefinementPort) mirrorPort;
119                        boolean disableStatus = castPort._mirrorDisable;
120
121                        try {
122                            castPort._mirrorDisable = true;
123                            castPort.moveDown();
124                        } finally {
125                            castPort._mirrorDisable = disableStatus;
126                        }
127                    }
128                }
129            }
130
131            return result;
132        } finally {
133            _workspace.doneWriting();
134        }
135    }
136
137    /** Move this object to the first position in the list
138     *  of attributes of the container. If  this object is already first,
139     *  do nothing. Increment the version of the workspace.
140     *  This method overrides the base class to mirror the change
141     *  in any mirror ports.
142     *  @return The index of the specified object prior to moving it,
143     *   or -1 if it is not moved.
144     *  @exception IllegalActionException If this object has
145     *   no container.
146     */
147    @Override
148    public int moveToFirst() throws IllegalActionException {
149        try {
150            _workspace.getWriteAccess();
151
152            int result = super.moveToFirst();
153
154            if (result != -1) {
155                // Mirror the change in mirror ports.
156                MultiCompositeActor container = (MultiCompositeActor) getContainer();
157                Iterator entities = container.entityList().iterator();
158
159                while (entities.hasNext()) {
160                    Entity entity = (Entity) entities.next();
161                    Port mirrorPort = entity.getPort(getName());
162
163                    if (mirrorPort instanceof RefinementPort) {
164                        RefinementPort castPort = (RefinementPort) mirrorPort;
165                        boolean disableStatus = castPort._mirrorDisable;
166
167                        try {
168                            castPort._mirrorDisable = true;
169                            castPort.moveToFirst();
170                        } finally {
171                            castPort._mirrorDisable = disableStatus;
172                        }
173                    }
174                }
175            }
176
177            return result;
178        } finally {
179            _workspace.doneWriting();
180        }
181    }
182
183    /** Move this object to the specified position in the list
184     *  of attributes of the container. If this object is already at the
185     *  specified position, do nothing.
186     *  This method overrides the base class to mirror the change
187     *  in any mirror ports.
188     *  Increment the version of the workspace.
189     *  @param index The position to move this object to.
190     *  @return The index of the specified object prior to moving it,
191     *   or -1 if it is not moved.
192     *  @exception IllegalActionException If this object has
193     *   no container or if the index is out of bounds.
194     */
195    @Override
196    public int moveToIndex(int index) throws IllegalActionException {
197        try {
198            _workspace.getWriteAccess();
199
200            int result = super.moveToIndex(index);
201
202            if (result != -1) {
203                // Mirror the change in mirror ports.
204                MultiCompositeActor container = (MultiCompositeActor) getContainer();
205                Iterator entities = container.entityList().iterator();
206
207                while (entities.hasNext()) {
208                    Entity entity = (Entity) entities.next();
209                    Port mirrorPort = entity.getPort(getName());
210
211                    if (mirrorPort instanceof RefinementPort) {
212                        RefinementPort castPort = (RefinementPort) mirrorPort;
213                        boolean disableStatus = castPort._mirrorDisable;
214
215                        try {
216                            castPort._mirrorDisable = true;
217                            castPort.moveToIndex(index);
218                        } finally {
219                            castPort._mirrorDisable = disableStatus;
220                        }
221                    }
222                }
223            }
224
225            return result;
226        } finally {
227            _workspace.doneWriting();
228        }
229    }
230
231    /** Move this object to the last position in the list
232     *  of attributes of the container.  If this object is already last,
233     *  do nothing. This method overrides the base class to mirror the change
234     *  in any mirror ports.
235     *  Increment the version of the workspace.
236     *  @return The index of the specified object prior to moving it,
237     *   or -1 if it is not moved.
238     *  @exception IllegalActionException If this object has
239     *   no container.
240     */
241    @Override
242    public int moveToLast() throws IllegalActionException {
243        try {
244            _workspace.getWriteAccess();
245
246            int result = super.moveToLast();
247
248            if (result != -1) {
249                // Mirror the change in mirror ports.
250                MultiCompositeActor container = (MultiCompositeActor) getContainer();
251                Iterator entities = container.entityList().iterator();
252
253                while (entities.hasNext()) {
254                    Entity entity = (Entity) entities.next();
255                    Port mirrorPort = entity.getPort(getName());
256
257                    if (mirrorPort instanceof RefinementPort) {
258                        RefinementPort castPort = (RefinementPort) mirrorPort;
259                        boolean disableStatus = castPort._mirrorDisable;
260
261                        try {
262                            castPort._mirrorDisable = true;
263                            castPort.moveToLast();
264                        } finally {
265                            castPort._mirrorDisable = disableStatus;
266                        }
267                    }
268                }
269            }
270
271            return result;
272        } finally {
273            _workspace.doneWriting();
274        }
275    }
276
277    /** Move this object up by one in the list of
278     *  attributes of the container. If  this object is already first, do
279     *  nothing.
280     *  This method overrides the base class to mirror the change
281     *  in any mirror ports.
282     *  Increment the version of the workspace.
283     *  @return The index of the specified object prior to moving it,
284     *   or -1 if it is not moved.
285     *  @exception IllegalActionException If this object has
286     *   no container.
287     */
288    @Override
289    public int moveUp() throws IllegalActionException {
290        try {
291            _workspace.getWriteAccess();
292
293            int result = super.moveUp();
294
295            if (result != -1) {
296                // Mirror the change in mirror ports.
297                MultiCompositeActor container = (MultiCompositeActor) getContainer();
298                Iterator entities = container.entityList().iterator();
299
300                while (entities.hasNext()) {
301                    Entity entity = (Entity) entities.next();
302                    Port mirrorPort = entity.getPort(getName());
303
304                    if (mirrorPort instanceof RefinementPort) {
305                        RefinementPort castPort = (RefinementPort) mirrorPort;
306                        boolean disableStatus = castPort._mirrorDisable;
307
308                        try {
309                            castPort._mirrorDisable = true;
310                            castPort.moveUp();
311                        } finally {
312                            castPort._mirrorDisable = disableStatus;
313                        }
314                    }
315                }
316            }
317
318            return result;
319        } finally {
320            _workspace.doneWriting();
321        }
322    }
323
324    /** Override the base class so that if the port is being removed
325     *  from the current container, then it is also removed from the
326     *  controller and from each of the refinements.  This method is
327     *  write-synchronized on the workspace.
328     *  @param container The proposed container.
329     *  @exception IllegalActionException If the proposed container is not a
330     *   ComponentEntity, doesn't implement Actor, or has no name,
331     *   or the port and container are not in the same workspace. Or
332     *   it's not null
333     *  @exception NameDuplicationException If the container already has
334     *   a port with the name of this port.
335     */
336    @Override
337    public void setContainer(Entity container)
338            throws IllegalActionException, NameDuplicationException {
339        try {
340            _workspace.getWriteAccess();
341
342            MultiCompositeActor model = (MultiCompositeActor) getContainer();
343
344            if (model != null && container != model) {
345                // The port is being removed from the current container.
346                // Remove it from the mirrored ports.
347                Iterator entities = model.entityList().iterator();
348
349                while (entities.hasNext()) {
350                    Entity entity = (Entity) entities.next();
351                    Port mirrorPort = entity.getPort(getName());
352
353                    if (mirrorPort instanceof RefinementPort) {
354                        RefinementPort castPort = (RefinementPort) mirrorPort;
355                        boolean disableStatus = castPort._mirrorDisable;
356
357                        try {
358                            castPort._mirrorDisable = true;
359                            castPort.setContainer(null);
360                        } finally {
361                            castPort._mirrorDisable = disableStatus;
362                        }
363                    }
364                }
365
366                // Remove the relation as well.
367                ComponentRelation relation = model
368                        .getRelation(getName() + "Relation");
369
370                if (relation != null) {
371                    relation.setContainer(null);
372                }
373            }
374
375            super.setContainer(container);
376        } finally {
377            _workspace.doneWriting();
378        }
379    }
380
381    /** If the argument is true, make the port an input port.
382     *  If the argument is false, make the port not an input port.
383     *  This method overrides the base class to make the same
384     *  change on the mirror ports in the controller and state refinments.
385     *  This method invalidates the schedule and resolved types of the
386     *  director of the container, if there is one.
387     *  It is write-synchronized on the workspace, and increments
388     *  the version of the workspace.
389     *  @param isInput True to make the port an input.
390     *  @exception IllegalActionException If changing the port status is
391     *   not permitted.
392     */
393    @Override
394    public void setInput(boolean isInput) throws IllegalActionException {
395        try {
396            _workspace.getWriteAccess();
397
398            super.setInput(isInput);
399
400            // Mirror the change in mirror ports.
401            MultiCompositeActor container = (MultiCompositeActor) getContainer();
402            Iterator entities = container.entityList().iterator();
403
404            while (entities.hasNext()) {
405                Entity entity = (Entity) entities.next();
406                Port mirrorPort = entity.getPort(getName());
407
408                if (mirrorPort instanceof RefinementPort) {
409                    RefinementPort castPort = (RefinementPort) mirrorPort;
410                    boolean disableStatus = castPort._mirrorDisable;
411
412                    try {
413                        castPort._mirrorDisable = true;
414                        castPort.setInput(isInput);
415                    } finally {
416                        castPort._mirrorDisable = disableStatus;
417                    }
418                }
419            }
420        } finally {
421            _workspace.doneWriting();
422        }
423    }
424
425    /** If the argument is true, make the port a multiport.
426     *  If the argument is false, make the port not a multiport.
427     *  This method overrides the base class to make the same
428     *  change on the mirror ports in the controller and state refinments.
429     *  This method invalidates the schedule and resolved types of the
430     *  director of the container, if there is one.
431     *  It is write-synchronized on the workspace, and increments
432     *  the version of the workspace.
433     *  @param isMultiport True to make the port a multiport.
434     *  @exception IllegalActionException If changing the port status is
435     *   not permitted.
436     */
437    @Override
438    public void setMultiport(boolean isMultiport)
439            throws IllegalActionException {
440        try {
441            _workspace.getWriteAccess();
442
443            super.setMultiport(isMultiport);
444
445            // Mirror the change in mirror ports.
446            MultiCompositeActor container = (MultiCompositeActor) getContainer();
447            Iterator entities = container.entityList().iterator();
448
449            while (entities.hasNext()) {
450                Entity entity = (Entity) entities.next();
451                Port mirrorPort = entity.getPort(getName());
452
453                if (mirrorPort instanceof RefinementPort) {
454                    RefinementPort castPort = (RefinementPort) mirrorPort;
455                    boolean disableStatus = castPort._mirrorDisable;
456
457                    try {
458                        castPort._mirrorDisable = true;
459                        castPort.setMultiport(isMultiport);
460                    } finally {
461                        castPort._mirrorDisable = disableStatus;
462                    }
463                }
464            }
465        } finally {
466            _workspace.doneWriting();
467        }
468    }
469
470    /** Set the name of the port, and mirror the change in all the
471     *  mirror ports.
472     *  This method is write-synchronized on the workspace, and increments
473     *  the version of the workspace.
474     *  @exception IllegalActionException If the name has a period.
475     *  @exception NameDuplicationException If there is already a port
476     *   with the same name in the container.
477     */
478    @Override
479    public void setName(String name)
480            throws IllegalActionException, NameDuplicationException {
481        try {
482            _workspace.getWriteAccess();
483
484            String oldName = getName();
485            super.setName(name);
486
487            // Mirror the change in mirror ports.
488            MultiCompositeActor container = (MultiCompositeActor) getContainer();
489
490            // NOTE: This is called before there is even a container
491            // to originally set the name.
492            if (container != null) {
493                Iterator entities = container.entityList().iterator();
494
495                while (entities.hasNext()) {
496                    Entity entity = (Entity) entities.next();
497                    Port mirrorPort = entity.getPort(oldName);
498
499                    if (mirrorPort instanceof RefinementPort) {
500                        RefinementPort castPort = (RefinementPort) mirrorPort;
501                        boolean disableStatus = castPort._mirrorDisable;
502
503                        try {
504                            castPort._mirrorDisable = true;
505                            castPort.setName(name);
506                        } finally {
507                            castPort._mirrorDisable = disableStatus;
508                        }
509                    }
510                }
511
512                // Rename the corresponding relation.
513                Relation relation = container.getRelation(oldName + "Relation");
514
515                if (relation != null) {
516                    relation.setName(name + "Relation");
517                }
518            }
519        } finally {
520            _workspace.doneWriting();
521        }
522    }
523
524    /** If the argument is true, make the port an output port.
525     *  If the argument is false, make the port not an output port.
526     *  This method overrides the base class to make the same
527     *  change on the mirror ports in the controller and state refinments.
528     *  This method invalidates the schedule and resolved types of the
529     *  director of the container, if there is one.
530     *  It is write-synchronized on the workspace, and increments
531     *  the version of the workspace.
532     *  @param isOutput True to make the port an output.
533     *  @exception IllegalActionException If changing the port status is
534     *   not permitted.
535     */
536    @Override
537    public void setOutput(boolean isOutput) throws IllegalActionException {
538        try {
539            _workspace.getWriteAccess();
540
541            super.setOutput(isOutput);
542
543            // Mirror the change in mirror ports.
544            MultiCompositeActor container = (MultiCompositeActor) getContainer();
545            Iterator entities = container.entityList().iterator();
546
547            while (entities.hasNext()) {
548                Entity entity = (Entity) entities.next();
549                Port mirrorPort = entity.getPort(getName());
550
551                if (mirrorPort instanceof RefinementPort) {
552                    RefinementPort castPort = (RefinementPort) mirrorPort;
553                    boolean disableStatus = castPort._mirrorDisable;
554
555                    try {
556                        castPort._mirrorDisable = true;
557                        castPort.setOutput(isOutput);
558                    } finally {
559                        castPort._mirrorDisable = disableStatus;
560                    }
561
562                    // If the entity is a controller, then set the
563                    // port to also be an input.
564                    if (entity.getName().equals("_Controller")) {
565                        boolean controlPortStatus = castPort._mirrorDisable;
566
567                        try {
568                            castPort._mirrorDisable = true;
569                            castPort.setInput(true);
570
571                            // Mark that the input property is
572                            // automatically set, so that if it
573                            // is changed, that change is not
574                            // mirrored.
575                            if (!isInput()) {
576                                castPort._automaticallyInput = true;
577                            }
578                        } finally {
579                            castPort._mirrorDisable = controlPortStatus;
580                        }
581                    }
582                }
583            }
584        } finally {
585            _workspace.doneWriting();
586        }
587    }
588
589    ///////////////////////////////////////////////////////////////////
590    ////                         protected methods                 ////
591
592    /** Override the base class to ensure that the proposed container
593     *  is a MultiCompositeActor or null.
594     *  @param container The proposed container.
595     *  @exception IllegalActionException If the proposed container is not a
596     *   TypedActor, or if the base class throws it.
597     */
598    @Override
599    protected void _checkContainer(Entity container)
600            throws IllegalActionException {
601        if (!(container instanceof MultiCompositeActor) && container != null) {
602            throw new IllegalActionException(container, this,
603                    "MultiCompositePort can only be contained by MultiCompositeActor objects. "
604                            + "The container was: " + container);
605        }
606    }
607}