001/*  A composite that contain one actor and mirror the ports and parameters of that actor.
002
003 Copyright (c) 2007-2015 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.io.IOException;
031import java.io.Writer;
032import java.util.Iterator;
033import java.util.LinkedList;
034import java.util.List;
035
036import ptolemy.actor.IOPort;
037import ptolemy.actor.TypedCompositeActor;
038import ptolemy.actor.parameters.MirrorPortParameter;
039import ptolemy.actor.parameters.ParameterMirrorPort;
040import ptolemy.actor.parameters.PortParameter;
041import ptolemy.kernel.ComponentEntity;
042import ptolemy.kernel.ComponentPort;
043import ptolemy.kernel.ComponentRelation;
044import ptolemy.kernel.CompositeEntity;
045import ptolemy.kernel.Entity;
046import ptolemy.kernel.Port;
047import ptolemy.kernel.util.Attribute;
048import ptolemy.kernel.util.ChangeRequest;
049import ptolemy.kernel.util.IllegalActionException;
050import ptolemy.kernel.util.InternalErrorException;
051import ptolemy.kernel.util.KernelException;
052import ptolemy.kernel.util.NameDuplicationException;
053import ptolemy.kernel.util.NamedObj;
054import ptolemy.kernel.util.Workspace;
055import ptolemy.moml.HandlesInternalLinks;
056
057///////////////////////////////////////////////////////////////////
058//// ReflectComposite
059
060/**
061 A composite that contains one actor and mirrors the ports and
062 parameters of that actor. In this base class, ports that are
063 not instances of MirrorPort are not mirrored. The subclass
064 MirrorComposite will mirror those ports.
065
066 @author Ilge Akkaya and Edward A. Lee
067 @version $Id$
068 @since Ptolemy II 10.0
069 @Pt.ProposedRating Yellow (eal)
070 @Pt.AcceptedRating Red (neuendor)
071 */
072public class ReflectComposite extends TypedCompositeActor
073        implements HandlesInternalLinks {
074
075    /** Create an actor with a name and a container.
076     *  The container argument must not be null, or a
077     *  NullPointerException will be thrown.  This actor will use the
078     *  workspace of the container for synchronization and version counts.
079     *  If the name argument is null, then the name is set to the empty string.
080     *  Increment the version of the workspace.
081     *  @param container The container actor.
082     *  @param name The name of this actor.
083     *  @exception IllegalActionException If the container is incompatible
084     *   with this actor.
085     *  @exception NameDuplicationException If the name coincides with
086     *   an actor already in the container.
087     */
088    public ReflectComposite(CompositeEntity container, String name)
089            throws IllegalActionException, NameDuplicationException {
090        super(container, name);
091        _init(true);
092    }
093
094    /** Construct a ReflectComposite in the specified workspace with
095     *  no container and an empty string as a name. You can then change
096     *  the name with setName(). If the workspace argument is null, then
097     *  use the default workspace.  You should set the local director or
098     *  executive director before attempting to send data to the actor
099     *  or to execute it. Add the actor to the workspace directory.
100     *  Increment the version number of the workspace.
101     *  @param workspace The workspace that will list the actor.
102     */
103    public ReflectComposite(Workspace workspace) {
104        super(workspace);
105        _init(true);
106    }
107
108    /** Create an actor with a name and a container that optionally
109     *  mirrors the ports that are instances of ParameterPort.
110     *  The container argument must not be null, or a
111     *  NullPointerException will be thrown.  This actor will use the
112     *  workspace of the container for synchronization and version counts.
113     *  If the name argument is null, then the name is set to the empty string.
114     *  Increment the version of the workspace.
115     *  @param container The container actor.
116     *  @param name The name of this actor.
117     *  @param mirrorParameterPorts If false, then ports that are instances of
118     *   ParameterPort are not mirrored.
119     *  @exception IllegalActionException If the container is incompatible
120     *   with this actor.
121     *  @exception NameDuplicationException If the name coincides with
122     *   an actor already in the container.
123     */
124    public ReflectComposite(CompositeEntity container, String name,
125            boolean mirrorParameterPorts)
126            throws IllegalActionException, NameDuplicationException {
127        super(container, name);
128        _init(mirrorParameterPorts);
129    }
130
131    ///////////////////////////////////////////////////////////////////
132    ////                         public methods                    ////
133
134    /** Clone the object into the specified workspace. This overrides
135     *  the base class to set up the associations in the mirror ports
136     *  and to set a flag indicating that cloning is complete.
137     *  @param workspace The workspace for the new object.
138     *  @return A new NamedObj.
139     *  @exception CloneNotSupportedException If any of the attributes
140     *   cannot be cloned.
141     *  @see #exportMoML(Writer, int, String)
142     */
143    @Override
144    public Object clone(Workspace workspace) throws CloneNotSupportedException {
145        ReflectComposite result = (ReflectComposite) super.clone(workspace);
146
147        // Fix port associations.
148        Iterator entities = result.entityList().iterator();
149
150        while (entities.hasNext()) {
151            Entity insideEntity = (Entity) entities.next();
152            Iterator ports = result.portList().iterator();
153
154            while (ports.hasNext()) {
155                IOPort port = (IOPort) ports.next();
156                Port insidePort = insideEntity.getPort(port.getName());
157
158                if (insidePort instanceof MirrorPort) {
159                    ((MirrorPort) port)
160                            .setAssociatedPort((MirrorPort) insidePort);
161                } else if (insidePort instanceof ParameterMirrorPort) {
162                    ((ParameterMirrorPort) port).setAssociatedPort(
163                            (ParameterMirrorPort) insidePort);
164                }
165            }
166        }
167        return result;
168    }
169
170    /** Override the base class to return a specialized port.
171     *  @param name The name of the port to create.
172     *  @return A new instance of IteratePort, an inner class.
173     *  @exception NameDuplicationException If the container already has a port
174     *  with this name.
175     */
176    @Override
177    public Port newPort(String name) throws NameDuplicationException {
178        try {
179            Port result = new MirrorPort(this, name);
180
181            // NOTE: We would like prevent deletion via MoML
182            // (or name changes, for that matter), but the following
183            // also prevents making it an input, which makes
184            // adding ports via the port dialog fail.
185            // result.setDerivedLevel(1);
186            // Force the port to be persistent despite being derived.
187            // result.setPersistent(true);
188            return result;
189        } catch (IllegalActionException ex) {
190            // This exception should not occur, so we throw a runtime
191            // exception.
192            throw new InternalErrorException(this, ex, null);
193        }
194    }
195
196    /** Create a new ParameterMirrorPort.
197     *  @param name The name of the port to create.
198     *  @return A new instance of PtidesMirrorPort, an inner class.
199     *  @exception NameDuplicationException If the container already has a port
200     *  with this name.
201     */
202    public Port newParameterPort(String name) throws NameDuplicationException {
203        try {
204            PortParameter parameter = new MirrorPortParameter(this, name);
205            return parameter.getPort();
206        } catch (IllegalActionException ex) {
207            // This exception should not occur, so we throw a runtime
208            // exception.
209            throw new InternalErrorException(this, ex, null);
210        }
211    }
212
213    ///////////////////////////////////////////////////////////////////
214    ////                         protected methods                 ////
215
216    /** Override the base class to queue a change request to remove
217     *  any previously contained entity and its mirrored ports, and
218     *  to mirror the ports of the added entity.
219     *  @param entity Entity to contain.
220     *  @exception IllegalActionException If the entity has no name, or the
221     *   action would result in a recursive containment structure, or the
222     *   argument does not implement the TypedActor interface.
223     *  @exception NameDuplicationException If the name collides with a name
224     *   already on the actor contents list, or if the added element is a
225     *   class definition.
226     */
227    @Override
228    protected void _addEntity(ComponentEntity entity)
229            throws IllegalActionException, NameDuplicationException {
230        if (entity.isClassDefinition()) {
231            throw new IllegalActionException(this,
232                    "Cannot place a class definition in an "
233                            + "ReflectComposite actor.");
234        }
235
236        super._addEntity(entity);
237
238        // Issue a change request to add the appropriate
239        // ports and connections to the new entity.
240        ChangeRequest request = new ChangeRequest(this, // originator
241                "Adjust contained entities, ports and parameters") {
242            // Override this to indicate that the change is localized.
243            // This keeps the EntityTreeModel from closing open libraries
244            // when notified of this change.
245            @Override
246            public NamedObj getLocality() {
247                return ReflectComposite.this;
248            }
249
250            @Override
251            protected void _execute() throws Exception {
252                // NOTE: We defer to a change request
253                // because only at this point can we be sure that the
254                // change request that triggered this has completed (i.e. that
255                // the entity being added has been added.
256                synchronized (ReflectComposite.this) {
257                    try {
258                        workspace().getWriteAccess();
259
260                        // Entity most recently added.
261                        ComponentEntity entity = null;
262
263                        // Delete any previously contained entities.
264                        // The strategy here is a bit tricky if this ReflectComposite
265                        // is within a class definition (that is, if it has derived objects).
266                        // The key is that derived objects do not permit deletion (via
267                        // MoML) of contained entities. They cannot because this would
268                        // violate the invariant of classes where derived objects
269                        // always contain the same objects as their parents.
270                        // Thus, if this is derived, we _cannot_ delete contained
271                        // entities. Thus, we should not generate entity removal
272                        // commands.
273                        List priorEntities = entityList();
274                        Iterator priors = priorEntities.iterator();
275
276                        while (priors.hasNext()) {
277                            ComponentEntity prior = (ComponentEntity) priors
278                                    .next();
279
280                            // If there is at least one more contained object,
281                            // then delete this one.
282                            // NOTE: How do we prevent the user from attempting to
283                            // override the contained object in a subclass?
284                            // It doesn't work to not remove this if the object
285                            // is derived, because then derived objects won't
286                            // track the prototype.
287                            if (priors.hasNext()) {
288                                prior.setContainer(null);
289                            } else {
290                                // The last entity in the entityList is
291                                // the one that we just added.
292                                entity = prior;
293                            }
294                        }
295
296                        if (entity == null) {
297                            // Nothing to do.
298                            return;
299                        }
300
301                        Iterator entityPorts = entity.portList().iterator();
302
303                        while (entityPorts.hasNext()) {
304                            ComponentPort insidePort = (ComponentPort) entityPorts
305                                    .next();
306                            // Use a strategy pattern here so that subclasses can control
307                            // which ports are mirrored.
308                            if (!_mirrorPort(insidePort)) {
309                                continue;
310                            }
311                            String name = insidePort.getName();
312
313                            // The outside port may already exist (e.g.
314                            // as a consequence of cloning).
315                            IOPort newPort = (IOPort) getPort(name);
316
317                            if (newPort == null) {
318                                newPort = (IOPort) newPort(name);
319                            }
320
321                            if (insidePort instanceof IOPort) {
322                                IOPort castPort = (IOPort) insidePort;
323                                newPort.setMultiport(castPort.isMultiport());
324                                newPort.setInput(castPort.isInput());
325                                newPort.setOutput(castPort.isOutput());
326                            }
327
328                            // Set up inside connections.
329                            // Do this only if they are not already connected.
330                            List connectedPorts = insidePort
331                                    .connectedPortList();
332
333                            if (!connectedPorts.contains(newPort)) {
334                                ComponentRelation relation = newRelation(
335                                        uniqueName("relation"));
336                                newPort.link(relation);
337                                insidePort.link(relation);
338                            }
339                        }
340                    } finally {
341                        workspace().doneWriting();
342                    }
343                }
344            }
345        };
346
347        requestChange(request);
348    }
349
350    /** Add a port to this actor. This overrides the base class to
351     *  mirror the new port, only if it is an instance of MirrorPort
352     *  in the contained actor, if there is one,
353     *  and to establish a connection to a port on the contained actor.
354     *  @param port The TypedIOPort to add to this actor.
355     *  @exception IllegalActionException If the port is not an instance
356     *   of IteratePort, or the port has no name.
357     *  @exception NameDuplicationException If the port name collides with a
358     *   name already in the actor.
359     */
360    @Override
361    protected void _addPort(Port port)
362            throws IllegalActionException, NameDuplicationException {
363
364        super._addPort(port);
365
366        if ((port instanceof MirrorPort
367                || port instanceof ParameterMirrorPort)) {
368            // Create and connect a matching inside port on contained entities.
369            // Do this as a change request to ensure that the action of
370            // creating the port passed in as an argument is complete by
371            // the time this executes.  Do not use MoML here because it
372            // isn't necessary to generate any undo code.  _removePort()
373            // takes care of the undo.
374            final IOPort castPort = (IOPort) port;
375
376            ChangeRequest request = new ChangeRequest(this,
377                    "Add a port on the inside") {
378                // Override this to indicate that the change is localized.
379                // This keeps the EntityTreeModel from closing open libraries
380                // when notified of this change.
381                @Override
382                public NamedObj getLocality() {
383                    return ReflectComposite.this;
384                }
385
386                @Override
387                protected void _execute() throws Exception {
388                    // NOTE: We defer the construction of the MoML
389                    // change request to here because only at this
390                    // point can we be sure that the change request
391                    // that triggered this has completed.
392                    synchronized (ReflectComposite.this) {
393                        // Create and connect a matching inside port
394                        // on contained entities.
395                        // NOTE: We assume this propagates to derived
396                        // objects because _addPort is called when
397                        // MoML is parsed to add a port to
398                        // MirrorComposite. Even the MirrorCompositeContents
399                        // uses MoML to add this port, so this will
400                        // result in propagation.
401                        try {
402                            workspace().getWriteAccess();
403                            _inAddPort = true;
404
405                            String portName = castPort.getName();
406                            Iterator entities = entityList().iterator();
407
408                            if (entities.hasNext()) {
409                                Entity insideEntity = (Entity) entities.next();
410                                Port insidePort = insideEntity
411                                        .getPort(portName);
412
413                                if (insidePort == null) {
414                                    if (castPort instanceof MirrorPort) {
415                                        insidePort = insideEntity
416                                                .newPort(portName);
417                                    } else if (castPort instanceof ParameterMirrorPort) { // ParameterMirrorPort
418                                        insidePort = ((MirrorComposite) insideEntity)
419                                                .newParameterPort(portName);
420                                    }
421
422                                    if (insidePort instanceof IOPort) {
423                                        IOPort castInsidePort = (IOPort) insidePort;
424                                        castInsidePort
425                                                .setInput(castPort.isInput());
426                                        castInsidePort
427                                                .setOutput(castPort.isOutput());
428                                        castInsidePort.setMultiport(
429                                                castPort.isMultiport());
430                                    }
431                                }
432
433                                if (insidePort == null) {
434                                    // FindBugs was reporting that insidePort could still be null.
435                                    throw new InternalErrorException(
436                                            ReflectComposite.this, null,
437                                            "insidePort is null? castPort "
438                                                    + castPort
439                                                    + " is neither a MirrorPort nor a ParameterMirrorPort?");
440                                }
441
442                                if (insidePort instanceof MirrorPort) {
443                                    ((MirrorPort) castPort).setAssociatedPort(
444                                            (MirrorPort) insidePort);
445                                } else if (insidePort instanceof ParameterMirrorPort) { // ParameterMirrorPort
446                                    ((ParameterMirrorPort) castPort)
447                                            .setAssociatedPort(
448                                                    (ParameterMirrorPort) insidePort);
449                                }
450
451                                // Create a link only if it doesn't already exist.
452                                List connectedPorts = insidePort
453                                        .connectedPortList();
454
455                                // Check if inside port is already connected to a port with that name.
456                                // Skipping this step causes duplicate link attempts between castPort and insidePort
457                                // in the case that _addPort() is called during clone(), in which CompositeEntity.clone()
458                                // will already have created a link between the two ports.
459
460                                Iterator connectedPortsIterator = connectedPorts
461                                        .iterator();
462                                boolean alreadyConnected = false;
463                                while (connectedPortsIterator.hasNext()) {
464                                    Port cp = (Port) connectedPortsIterator
465                                            .next();
466                                    if (cp.getName().equals(portName)) {
467                                        // do not connect
468                                        alreadyConnected = true;
469                                    }
470                                }
471                                if (!alreadyConnected) {
472                                    // There is no connection. Create one.
473                                    ComponentRelation newRelation = newRelation(
474                                            uniqueName("relation"));
475                                    insidePort.link(newRelation);
476                                    castPort.link(newRelation);
477                                }
478                            }
479                        } finally {
480                            workspace().doneWriting();
481                            _inAddPort = false;
482                        }
483                    }
484                }
485            };
486
487            requestChange(request);
488        }
489    }
490
491    /** Override the base class to describe contained entities,
492     *  attributes, and ports, but not inside links or relations.
493     *  The rest of the contents are generated automatically when a
494     *  contained entity is inserted.
495     *  @param output The output to write to.
496     *  @param depth The depth in the hierarchy, to determine indenting.
497     *  @exception IOException If an I/O error occurs.
498     */
499    @Override
500    protected void _exportMoMLContents(Writer output, int depth)
501            throws IOException {
502        Iterator attributes = attributeList().iterator();
503
504        while (attributes.hasNext()) {
505            Attribute attribute = (Attribute) attributes.next();
506            attribute.exportMoML(output, depth);
507        }
508
509        Iterator ports = portList().iterator();
510
511        while (ports.hasNext()) {
512            Port port = (Port) ports.next();
513            port.exportMoML(output, depth);
514        }
515
516        Iterator entities = entityList().iterator();
517
518        while (entities.hasNext()) {
519            ComponentEntity entity = (ComponentEntity) entities.next();
520            entity.exportMoML(output, depth);
521        }
522    }
523
524    /** Return true if the specified inside port should be mirrored.
525     *  This base class returns true if the inside port is an instance
526     *  of MirrorPort.
527     *  @param insidePort The port that may be mirrored.
528     *  @return True if the inside port should be mirrored.
529     */
530    protected boolean _mirrorPort(ComponentPort insidePort) {
531        // do not mirror ports that are not instances of MirrorPort
532        if (insidePort instanceof MirrorPort) {
533            return true;
534        }
535        return false;
536    }
537
538    /** Override the base class to remove the ports and inside relations
539     *  of this actor. This method assumes the caller has write access
540     *  on the workspace.
541     *  @param entity The entity being removed from this entity.
542     */
543    @Override
544    protected void _removeEntity(ComponentEntity entity) {
545        super._removeEntity(entity);
546
547        // Remove all inside relations. This will have the
548        // side effect of removing connections on the inside.
549        Iterator relations = relationList().iterator();
550
551        while (relations.hasNext()) {
552            try {
553                ((ComponentRelation) relations.next()).setContainer(null);
554            } catch (KernelException e) {
555                throw new InternalErrorException(e);
556            }
557        }
558
559        // Have to copy the list to avoid a concurrent
560        // modification exception.
561        Iterator ports = new LinkedList(portList()).iterator();
562
563        while (ports.hasNext()) {
564            Port port = (Port) ports.next();
565
566            try {
567                _inRemoveEntity = true;
568                port.setContainer(null);
569            } catch (KernelException e) {
570                throw new InternalErrorException(e);
571            } finally {
572                _inRemoveEntity = false;
573            }
574        }
575    }
576
577    /** Override the base class to remove the associated port on the
578     *  inside entity and the link to it, if there is one.
579     *  This method assumes the caller has write access on the
580     *  workspace.
581     *  @param port The port being removed from this entity.
582     */
583    @Override
584    protected void _removePort(final Port port) {
585        super._removePort(port);
586
587        // NOTE: Do not use MoML here because we do not want to generate
588        // undo actions to recreate the inside relation and port.
589        // This is because _addPort() will take care of that.
590        // The cast is safe because all my ports are instances of IOPort.
591        Iterator relations = ((IOPort) port).insideRelationList().iterator();
592
593        while (relations.hasNext()) {
594            ComponentRelation relation = (ComponentRelation) relations.next();
595
596            try {
597                relation.setContainer(null);
598            } catch (KernelException ex) {
599                throw new InternalErrorException(ex);
600            }
601        }
602
603        // Remove the ports from the inside entity only if this
604        // is not being called as a side effect of calling _removeEntity().
605        if (_inRemoveEntity) {
606            return;
607        }
608
609        Iterator entities = entityList().iterator();
610
611        while (entities.hasNext()) {
612            Entity insideEntity = (Entity) entities.next();
613            Port insidePort = insideEntity.getPort(port.getName());
614
615            if (insidePort != null) {
616                try {
617                    insidePort.setContainer(null);
618                } catch (KernelException ex) {
619                    throw new InternalErrorException(ex);
620                }
621            }
622        }
623    }
624
625    ///////////////////////////////////////////////////////////////////
626    ////                         private variables                 ////
627
628    /** Flag indicating that we are executing _addPort(). */
629    protected boolean _inAddPort = false;
630
631    /** Flag indicating that we are executing _removeEntity(). */
632    protected boolean _inRemoveEntity = false;
633
634    /** Flag indicating whether to mirror instances of ParameterPort. */
635    protected boolean _mirrorParameterPorts = true;
636
637    ///////////////////////////////////////////////////////////////////
638    ////                         private methods                   ////
639
640    /** Initialize the class.
641     *  @param mirrorParameterPorts If true, then mirror instances of ParameterPort.
642     */
643    private void _init(boolean mirrorParameterPorts) {
644        setClassName("ptolemy.actor.lib.hoc.ReflectComposite");
645        _attachText("_iconDescription",
646                "<svg>\n" + "<rect x=\"-30\" y=\"-20\" "
647                        + "width=\"60\" height=\"40\" "
648                        + "style=\"fill:white\"/>\n" + "<text x=\"-6\" y=\"10\""
649                        + "style=\"font-size:24\">?</text>\n" + "</svg>\n");
650        _mirrorParameterPorts = mirrorParameterPorts;
651    }
652
653    ///////////////////////////////////////////////////////////////////
654    ////                         inner classes                     ////
655
656    ///////////////////////////////////////////////////////////////////
657    //// ReflectCompositeContents
658
659    /** This is a specialized composite actor for use in ReflectComposite.
660     *  In particular, it ensures that if ports are added or deleted
661     *  locally, then corresponding ports will be added or deleted
662     *  in the container.  That addition will result in appropriate
663     *  connections being made.
664     */
665    public static class ReflectCompositeContents extends TypedCompositeActor {
666        // NOTE: This has to be a static class so that MoML can
667        // instantiate it.
668
669        /** Construct an actor with a name and a container.
670         *  @param container The container.
671         *  @param name The name of this actor.
672         *  @exception IllegalActionException If the container is incompatible
673         *   with this actor.
674         *  @exception NameDuplicationException If the name coincides with
675         *   an actor already in the container.
676         */
677        public ReflectCompositeContents(CompositeEntity container, String name)
678                throws IllegalActionException, NameDuplicationException {
679            super(container, name);
680        }
681
682        /** Override the base class to return a specialized port.
683         *  @param name The name of the port to create.
684         *  @return A new instance of MirrorPort.
685         *  @exception NameDuplicationException If the container already has
686         *  a port with this name.
687         */
688        @Override
689        public Port newPort(String name) throws NameDuplicationException {
690            try {
691                return new MirrorPort(this, name);
692            } catch (IllegalActionException ex) {
693                // This exception should not occur, so we throw a runtime
694                // exception.
695                throw new InternalErrorException(this, ex, null);
696            }
697        }
698
699        /** Add a port to this actor. This overrides the base class to
700         *  add a corresponding port to the container using a change
701         *  request, if the port is an instance of MirrorPort and
702         *  does not already exist.
703         *  @param port The TypedIOPort to add to this actor.
704         *  @exception IllegalActionException If the port is not an instance of
705         *   MirrorPort, or the port has no name.
706         *  @exception NameDuplicationException If the port name
707         *  collides with a name already in the actor.
708         */
709        @Override
710        protected void _addPort(final Port port)
711                throws IllegalActionException, NameDuplicationException {
712
713            super._addPort(port);
714
715            if ((port instanceof MirrorPort
716                    || port instanceof ParameterMirrorPort)) {
717
718                final ReflectComposite container = (ReflectComposite) getContainer();
719
720                if (container._inAddPort) {
721                    return;
722                }
723
724                // Use a change request so we can be sure the port
725                // being added is fully constructed.
726                ChangeRequest request = new ChangeRequest(this,
727                        "Add mirror port to the container.") {
728                    // Override this to indicate that the change is localized.
729                    // This keeps the EntityTreeModel from closing open libraries
730                    // when notified of this change.
731                    @Override
732                    public NamedObj getLocality() {
733                        return getContainer();
734                    }
735
736                    @Override
737                    protected void _execute() throws Exception {
738                        try {
739                            workspace().getWriteAccess();
740
741                            if (port instanceof ParameterMirrorPort) {
742                                ParameterMirrorPort newPort = (ParameterMirrorPort) container
743                                        .getPort(port.getName());
744
745                                if (newPort == null) {
746                                    newPort = (ParameterMirrorPort) container
747                                            .newParameterPort(port.getName());
748                                }
749                            } else { // MirrorPort
750                                MirrorPort newPort = (MirrorPort) container
751                                        .getPort(port.getName());
752
753                                if (newPort == null) {
754                                    newPort = (MirrorPort) container
755                                            .newPort(port.getName());
756                                }
757
758                                if (port instanceof IOPort) {
759                                    newPort.setInput(((IOPort) port).isInput());
760                                    newPort.setOutput(
761                                            ((IOPort) port).isOutput());
762                                    newPort.setMultiport(
763                                            ((IOPort) port).isMultiport());
764                                }
765                            }
766                        } finally {
767                            workspace().doneWriting();
768                        }
769                    }
770                };
771                container.requestChange(request);
772            }
773        }
774    }
775}