001/* A CompositeEntity is a cluster in a clustered graph.
002
003 Copyright (c) 1997-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.kernel;
029
030import java.io.IOException;
031import java.io.Writer;
032import java.lang.ref.WeakReference;
033import java.util.ArrayList;
034import java.util.Collection;
035import java.util.Collections;
036import java.util.Comparator;
037import java.util.Enumeration;
038import java.util.HashMap;
039import java.util.HashSet;
040import java.util.Iterator;
041import java.util.LinkedList;
042import java.util.List;
043import java.util.Map;
044import java.util.Set;
045import java.util.TreeMap;
046
047import ptolemy.kernel.attributes.VersionAttribute;
048import ptolemy.kernel.util.Attribute;
049import ptolemy.kernel.util.Decorator;
050import ptolemy.kernel.util.IllegalActionException;
051import ptolemy.kernel.util.InternalErrorException;
052import ptolemy.kernel.util.KernelException;
053import ptolemy.kernel.util.NameDuplicationException;
054import ptolemy.kernel.util.Nameable;
055import ptolemy.kernel.util.NamedList;
056import ptolemy.kernel.util.NamedObj;
057import ptolemy.kernel.util.Settable;
058import ptolemy.kernel.util.Workspace;
059import ptolemy.util.StringUtilities;
060
061///////////////////////////////////////////////////////////////////
062//// CompositeEntity
063
064/**
065 A CompositeEntity is a cluster in a clustered graph.
066 I.e., it is a non-atomic entity, in that
067 it can contain other entities and relations.  It supports transparent ports,
068 where, in effect, the port of a contained entity is represented by a port
069 of this entity. Methods that "deeply" traverse the topology
070 see right through transparent ports.
071 It may be opaque, in which case its ports are opaque and methods
072 that "deeply" traverse the topology do not see through them.
073 For instance, deepEntityList() returns the opaque entities
074 directly or indirectly contained by this entity.
075 <p>
076 To add an entity or relation to this composite, call its
077 setContainer() method with this composite as an argument.  To
078 remove it, call its setContainer() method with a null argument (or
079 another container). The entity must be an instance of
080 ComponentEntity and the relation of ComponentRelation or an
081 exception is thrown.  Derived classes may further constrain these
082 to subclasses.  To do that, they should override the protected
083 methods _addEntity() and _addRelation() and the public member
084 newRelation().
085 <p>
086 A CompositeEntity may be contained by another CompositeEntity.
087 To set that up, call the setContainer() method of the inside entity.
088 Derived classes may further constrain the container to be
089 a subclass of CompositeEntity.  To do this, they should override
090 setContainer() to throw an exception.  Recursive containment
091 structures, where an entity directly or indirectly contains itself,
092 are disallowed, and an exception is thrown on an attempt to set up
093 such a structure.
094 <p>
095 A CompositeEntity can contain instances of ComponentPort.  By default
096 these ports will be transparent, although subclasses of CompositeEntity
097 can make them opaque by overriding the isOpaque() method to return
098 <i>true</i>. Derived classes may further constrain the ports to a
099 subclass of ComponentPort.
100 To do this, they should override the public method newPort() to create
101 a port of the appropriate subclass, and the protected method _addPort()
102 to throw an exception if its argument is a port that is not of the
103 appropriate subclass.
104 <p>
105 Since contained entities implement the
106 {@link ptolemy.kernel.util.Instantiable} interface,
107 some may be class definitions.  If an entity is a class definition,
108 then it is not included in the lists returned by
109 {@link #entityList()}, {@link #entityList(Class)},
110 {@link #deepEntityList()}, and {@link #allAtomicEntityList()}.
111 Correspondingly, if it is not a class definition, then it is not
112 included in the list returned by {@link #classDefinitionList()}.
113 Contained class definitions are nonetheless required to have names
114 distinct from contained entities that are not class definitions,
115 and the method {@link #getEntity(String)} will return either
116 a class definition or an entity that is not a class definition,
117 as long as the name matches.  Note that contained entities that
118 are class definitions cannot be connected to other entities.
119 Moreover, they cannot be deleted as long as there are either
120 subclasses or instances present.
121
122 @author John S. Davis II, Edward A. Lee, contributor: Christopher Brooks
123 @version $Id$
124 @since Ptolemy II 0.2
125 @Pt.ProposedRating Green (eal)
126 @Pt.AcceptedRating Green (hyzheng)
127 */
128public class CompositeEntity extends ComponentEntity {
129    /** Construct an entity in the default workspace with an empty string
130     *  as its name. Add the entity to the workspace directory.
131     *  Increment the version number of the workspace.
132     */
133    public CompositeEntity() {
134        super();
135        _addIcon();
136    }
137
138    /** Construct an entity in the specified workspace with an empty
139     *  string as a name. You can then change the name with setName().
140     *  If the workspace argument is null, then use the default workspace.
141     *  Add the entity to the workspace directory.
142     *  Increment the version number of the workspace.
143     *  @param workspace The workspace that will list the entity.
144     */
145    public CompositeEntity(Workspace workspace) {
146        super(workspace);
147        _addIcon();
148    }
149
150    /** Create an object with a name and a container.
151     *  The container argument must not be null, or a
152     *  NullPointerException will be thrown.  This entity will use the
153     *  workspace of the container for synchronization and version counts.
154     *  If the name argument is null, then the name is set to the empty string.
155     *  Increment the version of the workspace.
156     *  @param container The container entity.
157     *  @param name The name of the entity.
158     *  @exception IllegalActionException If the container is incompatible
159     *   with this entity.
160     *  @exception NameDuplicationException If the name coincides with
161     *   an entity already in the container.
162     */
163    public CompositeEntity(CompositeEntity container, String name)
164            throws IllegalActionException, NameDuplicationException {
165        super(container, name);
166        _addIcon();
167    }
168
169    ///////////////////////////////////////////////////////////////////
170    ////                         public methods                    ////
171
172    /** Return a list that consists of all the atomic entities in a model.
173     *  This method differs from {@link #deepEntityList()} in that
174     *  this method looks inside opaque entities, whereas deepEntityList()
175     *  does not. The returned list does not include any entities that
176     *  are class definitions.
177     *  @return a List of all atomic entities in the model.
178     */
179    public List allAtomicEntityList() {
180        // We don't use an Iterator here so that we can modify the list
181        // rather than having both an Iterator and a result list.
182        //
183        // Note:
184        // deepEntityList() should be renamed to deepOpaqueEntityList()
185        // allAtomicEntityList() to deepAtomicEntityList()
186        // However, the change would require a fair amount of work.
187        //LinkedList entities = (LinkedList) deepEntityList();
188        List entities = deepEntityList();
189
190        for (int i = 0; i < entities.size(); i++) {
191            Object entity = entities.get(i);
192
193            if (entity instanceof CompositeEntity) {
194                // Remove the composite actor and add its containees.
195                entities.remove(i);
196
197                // Note that removing an element from the list causes
198                // the indices of later elements to shift forward by 1.
199                // We reduce the index i by one to match the index in
200                // the list.
201                i--;
202                entities.addAll(
203                        ((CompositeEntity) entity).allAtomicEntityList());
204            }
205        }
206
207        return entities;
208    }
209
210    /** Allow or disallow connections that are created using the connect()
211     *  method to cross levels of the hierarchy.
212     *  The default is that such connections are disallowed.
213     *  Generally it is a bad idea to allow level-crossing
214     *  connections, since it breaks modularity.  This loss of modularity
215     *  means, among other things, that this composite cannot be cloned.
216     *  Nonetheless, this capability is provided for the benefit of users
217     *  that feel they just must have it, and who are willing to sacrifice
218     *  clonability and modularity.
219     *  @param boole True to allow level-crossing connections.
220     */
221    public void allowLevelCrossingConnect(boolean boole) {
222        _levelCrossingConnectAllowed = boole;
223    }
224
225    /** List the contained class definitions
226     *  in the order they were added
227     *  (using their setContainer() method). The returned list does
228     *  not include any entities that are not class definitions.
229     *  The returned list is static in the sense
230     *  that it is not affected by any subsequent additions or removals
231     *  of class definitions.
232     *  This method is read-synchronized on the workspace.
233     *  @return A list of ComponentEntity objects.
234     *  @see #entityList()
235     */
236    public List classDefinitionList() {
237        try {
238            _workspace.getReadAccess();
239
240            if (_workspace.getVersion() == _classDefinitionListVersion) {
241                return _classDefinitionListCache;
242            }
243
244            List result = new LinkedList();
245
246            // This might be called from within a superclass constructor,
247            // in which case there are no contained entities yet.
248            if (_containedEntities != null) {
249                Iterator entities = _containedEntities.elementList().iterator();
250
251                while (entities.hasNext()) {
252                    ComponentEntity entity = (ComponentEntity) entities.next();
253
254                    if (entity.isClassDefinition()) {
255                        result.add(entity);
256                    }
257                }
258
259                _classDefinitionListCache = result;
260                _classDefinitionListVersion = _workspace.getVersion();
261            }
262
263            return result;
264        } finally {
265            _workspace.doneReading();
266        }
267    }
268
269    /** Clone the object into the specified workspace. The new object is
270     *  <i>not</i> added to the directory of that workspace (you must do this
271     *  yourself if you want it there).
272     *  This method gets read access on the workspace associated with
273     *  this object.
274     *  @param workspace The workspace for the cloned object.
275     *  @exception CloneNotSupportedException If one of the attributes
276     *  cannot be cloned.
277     *  @return A new CompositeEntity.
278     */
279    @Override
280    public Object clone(Workspace workspace) throws CloneNotSupportedException {
281        try {
282            workspace().getReadAccess();
283            // NOTE: The following assumes we will not do an exportMoML()
284            // at the same time we are doing a clone(). Since clone() is used
285            // to instantiate objects, this seems safe. But the field below
286            // is shared with exportMoML().
287            _levelCrossingLinks = new LinkedList<LinkRecord>();
288
289            CompositeEntity newEntity = (CompositeEntity) super.clone(
290                    workspace);
291
292            newEntity._containedEntities = new NamedList(newEntity);
293            newEntity._containedRelations = new NamedList(newEntity);
294
295            // Clone the contained relations.
296            Iterator relations = relationList().iterator();
297            while (relations.hasNext()) {
298                ComponentRelation relation = (ComponentRelation) relations
299                        .next();
300                ComponentRelation newRelation = (ComponentRelation) relation
301                        .clone(workspace);
302                // Assume that since we are dealing with clones,
303                // exceptions won't occur normally.  If they do, throw a
304                // CloneNotSupportedException.
305                try {
306                    newRelation.setContainer(newEntity);
307                    // To support relation groups, duplicate any links
308                    // to other relations. The links to ports will be
309                    // done below in a way that preserves the order of the
310                    // links. Links in ports have an order, whereas links
311                    // in relations do not.
312                    Enumeration links = relation._linkList.getContainers();
313                    while (links.hasMoreElements()) {
314                        Object link = links.nextElement();
315                        if (link instanceof Relation) {
316                            // Create the link only if the corresponding
317                            // relation has been created. This ensures
318                            // that the link is created exactly once.
319                            // Get the relation using a relative name
320                            // in case it's a level-crossing link.
321                            Relation farRelation = newEntity.getRelation(
322                                    ((Nameable) link).getName(this));
323                            if (farRelation != null) {
324                                newRelation.link(farRelation);
325                            }
326                        }
327                    }
328                } catch (KernelException ex) {
329                    throw new CloneNotSupportedException(
330                            "Failed to clone a CompositeEntity: "
331                                    + ex.getMessage());
332                }
333            }
334
335            // Clone the contained classes.
336            Iterator classes = classDefinitionList().iterator();
337
338            while (classes.hasNext()) {
339                ComponentEntity classDefinition = (ComponentEntity) classes
340                        .next();
341                ComponentEntity newSubentity = (ComponentEntity) classDefinition
342                        .clone(workspace);
343
344                // Assume that since we are dealing with clones,
345                // exceptions won't occur normally.  If they do, throw a
346                // CloneNotSupportedException.
347                try {
348                    newSubentity.setContainer(newEntity);
349                } catch (KernelException ex) {
350                    throw new CloneNotSupportedException(
351                            "Failed to clone a CompositeEntity: "
352                                    + KernelException.stackTraceToString(ex));
353                }
354            }
355
356            // Clone the contained entities.
357            Iterator entities = entityList().iterator();
358
359            while (entities.hasNext()) {
360                ComponentEntity entity = (ComponentEntity) entities.next();
361                ComponentEntity newSubentity = (ComponentEntity) entity
362                        .clone(workspace);
363
364                // Assume that since we are dealing with clones,
365                // exceptions won't occur normally.  If they do, throw a
366                // CloneNotSupportedException.
367                try {
368                    newSubentity.setContainer(newEntity);
369                } catch (KernelException ex) {
370                    throw new CloneNotSupportedException(
371                            "Failed to clone a CompositeEntity: "
372                                    + KernelException.stackTraceToString(ex));
373                }
374
375                // Clone the links of the ports of the cloned entities.
376                Iterator ports = entity.portList().iterator();
377
378                while (ports.hasNext()) {
379                    ComponentPort port = (ComponentPort) ports.next();
380                    Enumeration linkedRelations = port.linkedRelations();
381
382                    int index = 0;
383                    while (linkedRelations.hasMoreElements()) {
384                        ComponentRelation rel = (ComponentRelation) linkedRelations
385                                .nextElement();
386
387                        // A null link (supported since indexed links) might
388                        // yield a null relation here. EAL 7/19/00.
389                        if (rel != null) {
390                            //                             if (rel.getContainer() != this) {
391                            //                                 throw new CloneNotSupportedException(
392                            //                                         "Cannot clone a CompositeEntity with "
393                            //                                                 + "level crossing transitions."
394                            //                                                 + "  The relation was: " + rel
395                            //                                                 + ", its container was: "
396                            //                                                 + rel.getContainer()
397                            //                                                 + ", which is not equal to "
398                            //                                                 + this);
399                            //                             }
400
401                            Port newPort = newSubentity.getPort(port.getName());
402
403                            // This may be a level-crossing link, in which case we have to
404                            // defer it to the common container.
405                            if (rel.getContainer() == this) {
406                                // Not a level-crossing link.
407                                ComponentRelation newRelation = newEntity
408                                        .getRelation(rel.getName());
409                                try {
410                                    newPort.link(newRelation);
411                                } catch (IllegalActionException ex) {
412                                    throw new CloneNotSupportedException(
413                                            "Failed to clone a CompositeEntity: "
414                                                    + ex.getMessage());
415                                }
416                            } else {
417                                // It is a level-crossing link.
418                                // Find the common container.
419                                NamedObj container = _commonContainer(port,
420                                        rel);
421                                if (container instanceof CompositeEntity) {
422                                    List<LinkRecord> linkRecords = ((CompositeEntity) container)._levelCrossingLinks;
423                                    if (linkRecords == null) {
424                                        throw new CloneNotSupportedException(
425                                                "Level crossing link goes outside of the class definition boundary: "
426                                                        + port.getFullName()
427                                                        + " and "
428                                                        + rel.getFullName());
429                                    }
430                                    // NOTE: The record has the new port (after cloning), but
431                                    // the old relation (before cloning) because we can't be sure
432                                    // the new relation exists yet.
433                                    LinkRecord record = new LinkRecord();
434                                    record.port = newPort;
435                                    record.relation1 = rel;
436                                    record.relation2 = null;
437                                    record.index = index;
438                                    linkRecords.add(record);
439                                }
440                            }
441                        }
442                        index++;
443                    }
444                }
445            }
446
447            // Clone the inside links from the ports of this entity.
448            Iterator ports = portList().iterator();
449
450            while (ports.hasNext()) {
451                ComponentPort port = (ComponentPort) ports.next();
452                relations = port.insideRelationList().iterator();
453
454                while (relations.hasNext()) {
455                    Relation relation = (Relation) relations.next();
456                    // To support level-crossing links to the inside,
457                    // be sure to get the name of the relation relative to this.
458                    ComponentRelation newRelation = newEntity
459                            .getRelation(relation.getName(this));
460                    Port newPort = newEntity.getPort(port.getName());
461
462                    try {
463                        newPort.link(newRelation);
464                    } catch (IllegalActionException ex) {
465                        throw new CloneNotSupportedException(
466                                "Failed to clone a CompositeEntity: "
467                                        + ex.getMessage());
468                    }
469                }
470            }
471
472            // Finally, clone level-crossing links, if there are any.
473            if (_levelCrossingLinks != null) {
474                for (LinkRecord record : _levelCrossingLinks) {
475                    try {
476                        if (record.port != null) {
477                            String relationName = record.relation1
478                                    .getName(this);
479                            Relation newRelation = newEntity
480                                    .getRelation(relationName);
481                            if (newRelation == null) {
482                                throw new CloneNotSupportedException(
483                                        "Cloning level-crossing links failed. Relation missing: "
484                                                + relationName);
485                            }
486                            record.port.insertLink(record.index, newRelation);
487                        } else {
488                            record.relation1.link(record.relation2);
489                        }
490                    } catch (IllegalActionException ex) {
491                        throw new CloneNotSupportedException(
492                                "Cloning level-crossing links failed: " + ex);
493                    }
494                }
495            }
496
497            return newEntity;
498        } finally {
499            _levelCrossingLinks = null;
500            try {
501                workspace().doneReading();
502            } catch (Throwable ex) {
503                throw new InternalErrorException(this, ex, "Internal Error: "
504                        + "workspace().doneReading() failed?");
505            }
506        }
507    }
508
509    /** Create a new relation and use it to connect two ports.
510     *  It creates a new relation using newRelation() with an automatically
511     *  generated name and uses it to link the specified ports.
512     *  The order of the ports determines the order in which the
513     *  links to the relation are established, but otherwise has no
514     *  importance.
515     *  The name is of the form "_R<i>i</i>" where <i>i</i> is an integer.
516     *  Level-crossing connections are not permitted unless
517     *  allowLevelCrossingConnect() has been called with a <i>true</i>
518     *  argument.  Note that is rarely a good idea to permit level crossing
519     *  connections, since they break modularity and cloning.
520     *  A reference to the newly created relation is returned.
521     *  To remove the relation, call its setContainer() method with a null
522     *  argument. This method is write-synchronized on the workspace
523     *  and increments its version number.
524     *  <p>Note that if this method is being called many times, then
525     *  it may be more efficient to use
526     *  {@link #connect(ComponentPort, ComponentPort, String)}
527     *  instead of this method because this method calls
528     *  {@link #uniqueName(String)} each time, which
529     *  searches the object for attributes, ports, entities and relations
530     *  that may match a candidate unique name.
531     *
532     *  @param port1 The first port to connect.
533     *  @param port2 The second port to connect.
534     *  @return The ComponentRelation that is created to connect port1 and
535     *  port2.
536     *  @exception IllegalActionException If one of the arguments is null, or
537     *   if a disallowed level-crossing connection would result.
538     */
539    public ComponentRelation connect(ComponentPort port1, ComponentPort port2)
540            throws IllegalActionException {
541        try {
542            return connect(port1, port2, uniqueName("_R"));
543        } catch (NameDuplicationException ex) {
544            // This exception should not be thrown.
545            throw new InternalErrorException(this, ex,
546                    "Internal error in CompositeEntity.connect() method!");
547        }
548    }
549
550    /** Create a new relation with the specified name and use it to
551     *  connect two ports. Level-crossing connections are not permitted
552     *  unless allowLevelCrossingConnect() has been called with a true
553     *  argument.   Note that is rarely a good idea to permit level crossing
554     *  connections, since they break modularity and cloning.
555     *  A reference to the newly created alias relation is returned.
556     *  To remove the relation, call its setContainer() method with a null
557     *  argument. This method is write-synchronized on the workspace
558     *  and increments its version number.
559     *  @param port1 The first port to connect.
560     *  @param port2 The second port to connect.
561     *  @param relationName The name of the new relation.
562     *  @return The ComponentRelation that is created to connect port1 and
563     *  port2.
564     *  @exception IllegalActionException If one of the arguments is null, or
565     *   if a disallowed level-crossing connection would result, or if the two
566     *   ports are not in the same workspace as this entity.
567     *  @exception NameDuplicationException If there is already a relation with
568     *   the specified name in this entity.
569     */
570    public ComponentRelation connect(ComponentPort port1, ComponentPort port2,
571            String relationName)
572            throws IllegalActionException, NameDuplicationException {
573        if (port1 == null || port2 == null) {
574            throw new IllegalActionException(this,
575                    "Attempt to connect null port.");
576        }
577
578        if (port1.workspace() != port2.workspace()
579                || port1.workspace() != _workspace) {
580            throw new IllegalActionException(port1, port2,
581                    "Cannot connect ports because workspaces are different.");
582        }
583
584        try {
585            _workspace.getWriteAccess();
586
587            ComponentRelation ar = newRelation(relationName);
588
589            if (_levelCrossingConnectAllowed) {
590                port1.liberalLink(ar);
591            } else {
592                port1.link(ar);
593            }
594
595            // Have to catch the exception to restore the original state.
596            try {
597                if (_levelCrossingConnectAllowed) {
598                    port2.liberalLink(ar);
599                } else {
600                    port2.link(ar);
601                }
602            } catch (IllegalActionException ex) {
603                port1.unlink(ar);
604                throw ex;
605            }
606
607            return ar;
608        } finally {
609            _workspace.doneWriting();
610        }
611    }
612
613    /** Return an iterator over contained objects. In this class,
614     *  this is an iterator over attributes, ports, classes,
615     *  entities, and relations.
616     *  @return An iterator over instances of NamedObj contained by this
617     *   object.
618     */
619    @Override
620    public Iterator containedObjectsIterator() {
621        return new ContainedObjectsIterator();
622    }
623
624    /** Return a list that consists of all the transparent and opaque
625     *  composite entities in a model.  This method differs from
626     *  {@link #allAtomicEntityList()} in that this method returns
627     *  CompositeEntities and allAtomicEntityList() returns atomic
628     *  entities.  This method differs from {@link #deepEntityList()}
629     *  in that this method returns only opaque and transparent
630     *  CompositeEntities, whereas deepEntityList() returns opaque
631     *  ComponentEntities.  The returned list of this method does not
632     *  include any entities that are class definitions.
633     *  The {@link #entityList(Class)} method only returns entities in the
634     *  current level, this method traverses the hierarchy.
635     *  @return a List of all transparent and opaque composite entities in the
636     *  model.  A transparent composite is a composite that does not
637     *  contain a director.  An opaque composite is a composite that does
638     *  contain a director.  Note that class definitions are also returned.
639     */
640    public List<CompositeEntity> deepCompositeEntityList() {
641        try {
642            _workspace.getReadAccess();
643            LinkedList result = new LinkedList();
644
645            // This might be called from within a superclass constructor,
646            // in which case there are no contained entities yet.
647            if (_containedEntities != null) {
648                Iterator entities = _containedEntities.elementList().iterator();
649                while (entities.hasNext()) {
650                    ComponentEntity entity = (ComponentEntity) entities.next();
651                    if (/*!entity.isClassDefinition() */
652                    /* &&!entity.isOpaque() */
653                    entity instanceof CompositeEntity) {
654                        result.add(entity);
655                        result.addAll(((CompositeEntity) entity)
656                                .deepCompositeEntityList());
657                    }
658                }
659            }
660
661            return result;
662        } finally {
663            _workspace.doneReading();
664        }
665    }
666
667    /** List the opaque entities that are directly or indirectly
668     *  contained by this entity.  The list will be empty if there
669     *  are no such contained entities. This list does not include
670     *  class definitions nor anything contained by them.
671     *  This method is read-synchronized on the workspace.
672     *  @return A list of opaque ComponentEntity objects.
673     *  @see #classDefinitionList()
674     *  @see #allAtomicEntityList()
675     */
676    public List deepOpaqueEntityList() {
677        try {
678            _workspace.getReadAccess();
679            List results = new ArrayList();
680            _deepOpaqueEntityList(results);
681            return results;
682        } finally {
683            _workspace.doneReading();
684        }
685    }
686
687    /** List the opaque entities that are directly or indirectly
688     *  contained by this entity.  The list will be empty if there
689     *  are no such contained entities. This list does not include
690     *  class definitions nor anything contained by them.
691     *  This method is read-synchronized on the workspace.
692     *  @return A list of opaque ComponentEntity objects.
693     *  @see #classDefinitionList()
694     *  @see #allAtomicEntityList()
695     */
696    public List deepEntityList() {
697        try {
698            _workspace.getReadAccess();
699
700            LinkedList result = new LinkedList();
701
702            // This might be called from within a superclass constructor,
703            // in which case there are no contained entities yet.
704            if (_containedEntities != null) {
705                Iterator entities = _containedEntities.elementList().iterator();
706                while (entities.hasNext()) {
707                    ComponentEntity entity = (ComponentEntity) entities.next();
708
709                    if (!entity.isClassDefinition()) {
710                        if (entity.isOpaque()) {
711                            result.add(entity);
712                        } else {
713                            result.addAll(((CompositeEntity) entity)
714                                    .deepEntityList());
715                        }
716                    }
717                }
718            }
719
720            return result;
721        } finally {
722            _workspace.doneReading();
723        }
724    }
725
726    /** List the NamedObjs that are directly or indirectly
727     *  contained by this entity.  The list will be empty if there
728     *  are no such contained NamedObjs. This list does not include
729     *  class definitions nor anything contained by them.
730     *  This method is read-synchronized on the workspace.
731     *  This method differs from deepEntityList() in that deepEntityList()
732     *  does not look inside opaques.
733     *  @return A list of opaque ComponentEntity objects.
734     *  @see #classDefinitionList()
735     *  @see #allAtomicEntityList()
736     */
737    public List deepNamedObjList() {
738        try {
739            _workspace.getReadAccess();
740
741            LinkedList result = new LinkedList();
742
743            // This might be called from within a superclass constructor,
744            // in which case there are no contained entities yet.
745            if (_containedEntities != null) {
746                Iterator entities = _containedEntities.elementList().iterator();
747                while (entities.hasNext()) {
748                    ComponentEntity entity = (ComponentEntity) entities.next();
749
750                    if (!entity.isClassDefinition()) {
751                        // if (entity.isOpaque()) {
752                        result.add(entity);
753                        if (!entity.isOpaque()) {
754                            result.addAll(((CompositeEntity) entity)
755                                    .deepNamedObjList());
756                        }
757                    }
758                }
759            }
760
761            return result;
762        } finally {
763            _workspace.doneReading();
764        }
765    }
766
767    /** Return a set with the relations that are directly or indirectly
768     *  contained by this entity.  The set will be empty if there
769     *  are no such contained relations.
770     *  This method is read-synchronized on the workspace.
771     *  @return A set of ComponentRelation objects.
772     */
773    public Set<ComponentRelation> deepRelationSet() {
774        try {
775            _workspace.getReadAccess();
776
777            Set<ComponentRelation> result = new HashSet<ComponentRelation>();
778
779            _addAll(result, relationList());
780
781            // This might be called from within a superclass constructor,
782            // in which case there are no contained entities yet.
783            if (_containedEntities != null) {
784
785                for (Object entityObject : _containedEntities.elementList()) {
786                    ComponentEntity entity = (ComponentEntity) entityObject;
787                    if (!entity.isClassDefinition()) {
788                        if (entity instanceof CompositeEntity) {
789                            _addAll(result, ((CompositeEntity) entity)
790                                    .deepRelationSet());
791                        }
792                    }
793                }
794            }
795
796            return result;
797        } finally {
798            _workspace.doneReading();
799        }
800    }
801
802    /** Enumerate the opaque entities that are directly or indirectly
803     *  contained by this entity.  The enumeration will be empty if there
804     *  are no such contained entities. The enumeration does not include
805     *  any entities that are class definitions.
806     *  This method is read-synchronized on the workspace.
807     *  @deprecated Use deepEntityList() instead.
808     *  @return An enumeration of opaque ComponentEntity objects.
809     */
810    @Deprecated
811    public Enumeration deepGetEntities() {
812        return Collections.enumeration(deepEntityList());
813    }
814
815    /** List the contained entities in the order they were added
816     *  (using their setContainer() method). The returned list does
817     *  not include any class definitions.
818     *  The returned list is static in the sense
819     *  that it is not affected by any subsequent additions or removals
820     *  of entities.
821     *  This method is read-synchronized on the workspace.
822     *  @return A list of ComponentEntity objects.
823     *  @see #classDefinitionList()
824     */
825    public List entityList() {
826        try {
827            _workspace.getReadAccess();
828
829            if (_workspace.getVersion() == _entityListVersion) {
830                List entityList = _entityListCache.get();
831                if (entityList != null) {
832                    return entityList;
833                }
834            }
835
836            List result = new LinkedList();
837
838            // This might be called from within a superclass constructor,
839            // in which case there are no contained entities yet.
840            if (_containedEntities != null) {
841                Iterator entities = _containedEntities.elementList().iterator();
842
843                while (entities.hasNext()) {
844                    ComponentEntity entity = (ComponentEntity) entities.next();
845
846                    if (!entity.isClassDefinition()) {
847                        result.add(entity);
848                    }
849                }
850
851                _entityListCache = new WeakReference<List>(result);
852                _entityListVersion = _workspace.getVersion();
853            }
854
855            return result;
856        } finally {
857            _workspace.doneReading();
858        }
859    }
860
861    /** Return a list of the component entities contained by this object that
862     *  are instances of the specified Java class.  If there are no such
863     *  instances, then return an empty list. The returned list does not
864     *  include class definitions.
865     *  This method is read-synchronized on the workspace.
866     *  @param filter The class of ComponentEntity of interest.
867     *  @param <T> The type corresponding to the class of interest.
868     *  @return A list of instances of specified class.
869     *  @see #classDefinitionList()
870     */
871    public <T> List<T> entityList(Class<T> filter) {
872        try {
873            _workspace.getReadAccess();
874
875            List<T> result = new LinkedList<T>();
876
877            // This might be called from within a superclass constructor,
878            // in which case there are no contained entities yet.
879            if (_containedEntities != null) {
880                Iterator entities = _containedEntities.elementList().iterator();
881
882                while (entities.hasNext()) {
883                    ComponentEntity entity = (ComponentEntity) entities.next();
884
885                    if (filter.isInstance(entity)
886                            && !entity.isClassDefinition()) {
887                        result.add((T) entity);
888                    }
889                }
890            }
891
892            return result;
893        } finally {
894            _workspace.doneReading();
895        }
896    }
897
898    /** Return a sequence of MoML link attributes that describe
899     *  any link between objects (ports, entities, and relations) that are
900     *  present in the <i>filter</i> argument.  Both ends of the link
901     *  must be present in <i>filter</i> for MoML to be generated for that
902     *  link.  The <i>filter</i>
903     *  argument normally contains ports, relations, and entities
904     *  that are contained by this composite entity. If it contains
905     *  an entity, then that is equivalent to containing all the ports
906     *  contained by that entity.  It is recommended to use a collection
907     *  class (such as HashSet) for which the contains() method is
908     *  efficient.
909     *  <p>
910     *  If the filter argument is null, then return all the links that this
911     *  composite is responsible for (i.e., apply no filtering).  If the
912     *  argument is an empty collection, then return none of the links.  The
913     *  links that this entity is responsible for are the inside links of
914     *  its ports, and links on ports contained by contained entities.
915     *  <p>
916     *  If any link is found where both ends of the link are inherited objects,
917     *  then that link is not exported. It is assumed that the base class
918     *  will export that link.  For this purpose, a port of a contained
919     *  entity is deemed to be an inherited object if it is itself a class
920     *  element <i>and</i> its container is an inherited object.
921     *  @param depth The depth below the MoML export in the hierarchy.
922     *  @param filter A collection of ports, parameters, and entities, or
923     *   null to apply no filtering.
924     *  @return A string that describes the links present in the
925     *  <i>filter</i>.
926     *  @exception IOException If an I/O error occurs.
927     */
928    public String exportLinks(int depth, Collection filter) throws IOException {
929        // To get the ordering right,
930        // we read the links from the ports, not from the relations.
931        StringBuffer result = new StringBuffer();
932
933        // First, produce the inside links on contained ports.
934        Iterator ports = portList().iterator();
935
936        while (ports.hasNext()) {
937            ComponentPort port = (ComponentPort) ports.next();
938            // Skip the port if it is not persistent.
939            if (port == null || !port.isPersistent()) {
940                continue;
941            }
942            Iterator relations = port.insideRelationList().iterator();
943
944            // The following variables are used to determine whether to
945            // specify the index of the link explicitly, or to leave
946            // it implicit.
947            int index = -1;
948            boolean useIndex = false;
949
950            while (relations.hasNext()) {
951                index++;
952
953                ComponentRelation relation = (ComponentRelation) relations
954                        .next();
955
956                // Skip the relation if it is not persistent.
957                if (relation != null && !relation.isPersistent()) {
958                    continue;
959                }
960
961                if (relation == null) {
962                    // Gap in the links.  The next link has to use an
963                    // explicit index.
964                    useIndex = true;
965                    continue;
966                }
967
968                // If both ends of the link are inherited objects, then
969                // suppress the export. This depends on the level of export
970                // because if both ends of the link are implied, then the
971                // link is implied.
972                if (_commonImplier(relation, depth, port, depth)) {
973                    continue;
974                }
975
976                // Apply filter.
977                if (filter == null
978                        || filter.contains(relation) && (filter.contains(port)
979                                || filter.contains(port.getContainer()))) {
980                    // If the relation is not persistent, then do not export the link.
981                    if (relation != null && !relation.isPersistent()) {
982                        continue;
983                    }
984                    // In order to support level-crossing links, consider the
985                    // possibility that the relation is not contained by this.
986                    String relationName;
987
988                    if (relation.getContainer() == this) {
989                        relationName = relation.getName();
990                    } else {
991                        if (deepContains(relation)) {
992                            // NOTE: This used to export the full name, but the
993                            // relative name is sufficient.
994                            relationName = relation.getName(this);
995                        } else {
996                            // Can't export the link here since when the
997                            // MoML file is re-read there is no assurance that
998                            // the relation exists when the link is to be
999                            // created.  Need to delegate to the least common
1000                            // container.
1001                            _recordLevelCrossingLink(port, relation, null,
1002                                    index);
1003                            continue;
1004                        }
1005                    }
1006
1007                    String escapedPortName = StringUtilities
1008                            .escapeForXML(port.getName());
1009                    String escapedRelationName = StringUtilities
1010                            .escapeForXML(relationName);
1011                    if (useIndex) {
1012                        useIndex = false;
1013                        result.append(_getIndentPrefix(depth) + "<link port=\""
1014                                + escapedPortName + "\" insertAt=\"" + index
1015                                + "\" relation=\"" + escapedRelationName
1016                                + "\"/>\n");
1017                    } else {
1018                        result.append(_getIndentPrefix(depth) + "<link port=\""
1019                                + escapedPortName + "\" relation=\""
1020                                + escapedRelationName + "\"/>\n");
1021                    }
1022                }
1023            }
1024        }
1025
1026        // Next, produce the links on ports contained by contained entities.
1027        Iterator entities = entityList().iterator();
1028
1029        while (entities.hasNext()) {
1030            ComponentEntity entity = (ComponentEntity) entities.next();
1031
1032            // Skip the entity if it is not persistent.
1033            if (entity == null || !entity.isPersistent()) {
1034                continue;
1035            }
1036
1037            ports = entity.portList().iterator();
1038
1039            while (ports.hasNext()) {
1040                ComponentPort port = (ComponentPort) ports.next();
1041                // Skip the port if it is not persistent.
1042                if (port == null || !port.isPersistent()) {
1043                    continue;
1044                }
1045                Iterator relations = port.linkedRelationList().iterator();
1046
1047                // The following variables are used to determine whether to
1048                // specify the index of the link explicitly, or to leave
1049                // it implicit.
1050                int index = -1;
1051                boolean useIndex = false;
1052
1053                while (relations.hasNext()) {
1054                    index++;
1055
1056                    ComponentRelation relation = (ComponentRelation) relations
1057                            .next();
1058
1059                    // Skip the relation if it is not persistent.
1060                    if (relation != null && !relation.isPersistent()) {
1061                        continue;
1062                    }
1063
1064                    if (relation == null) {
1065                        // Gap in the links.  The next link has to use an
1066                        // explicit index.
1067                        useIndex = true;
1068                        continue;
1069                    }
1070
1071                    // If both ends of the link are inherited objects, then
1072                    // suppress the export.  This depends on the level of export
1073                    // because if both ends of the link are implied, then the
1074                    // link is implied. Note that we need for both the port
1075                    // to be implied and the port's container to share a
1076                    // common implier with the relation. We know that the port
1077                    // is contained within its container, so we don't have to
1078                    // check it separately for a common implier.
1079                    if (port.getDerivedLevel() <= depth + 1 && _commonImplier(
1080                            relation, depth, port.getContainer(), depth)) {
1081                        continue;
1082                    }
1083                    // Used to have the previous logic here, skipping the link export,
1084                    // instead of the above.
1085                    // But careful!  It may be that the both the relation and
1086                    // the port are derived, but not from the same object.
1087                    // This can happen with level-crossing links.
1088                    // Check that the container above at which these two objects
1089                    // are implied is the same container.
1090                    // EAL 6/6/09
1091                    /*
1092                    int relationLevel = relation.getDerivedLevel();
1093                    int portLevel = port.getDerivedLevel();
1094                    if ((relationLevel <= depth)
1095                            && (portLevel <= (depth + 1))
1096                            && ((port.getContainer()).getDerivedLevel() <= depth)) {
1097                        continue;
1098                    }
1099                     */
1100
1101                    // Apply filter.
1102                    if (filter == null || filter.contains(relation)
1103                            && (filter.contains(port)
1104                                    || filter.contains(port.getContainer()))) {
1105                        // If the relation is not persistent, then do
1106                        // not export the link.
1107                        if (relation == null || !relation.isPersistent()) {
1108                            continue;
1109                        }
1110                        // In order to support level-crossing links,
1111                        // consider the possibility that the relation
1112                        // is not contained by this.
1113                        String relationName;
1114
1115                        if (relation.getContainer() == this) {
1116                            relationName = relation.getName();
1117                        } else {
1118                            if (deepContains(relation)) {
1119                                // NOTE: This used to export the full name, but the
1120                                // relative name is sufficient.
1121                                relationName = relation.getName(this);
1122                            } else {
1123                                // Can't export the link here since when the
1124                                // MoML file is re-read there is no assurance that
1125                                // the relation exists when the link is to be
1126                                // created.  Need to delegate to the least common
1127                                // container.
1128                                _recordLevelCrossingLink(port, relation, null,
1129                                        index);
1130                                continue;
1131                            }
1132                        }
1133
1134                        // Escape any < character that occurs in name.
1135                        // setName(String).
1136                        String escapedName = StringUtilities
1137                                .escapeForXML(entity.getName());
1138                        String escapedPortName = StringUtilities
1139                                .escapeForXML(port.getName());
1140                        String escapedRelationName = StringUtilities
1141                                .escapeForXML(relationName);
1142                        if (useIndex) {
1143                            useIndex = false;
1144                            result.append(_getIndentPrefix(depth)
1145                                    + "<link port=\"" + escapedName + "."
1146                                    + escapedPortName + "\" insertAt=\"" + index
1147                                    + "\" relation=\"" + escapedRelationName
1148                                    + "\"/>\n");
1149                        } else {
1150                            result.append(_getIndentPrefix(depth)
1151                                    + "<link port=\"" + escapedName + "."
1152                                    + escapedPortName + "\" relation=\""
1153                                    + escapedRelationName + "\"/>\n");
1154                        }
1155                    }
1156                }
1157            }
1158        }
1159
1160        // Finally, produce the links that are between contained
1161        // relations only. Slight trickiness here: Both relations
1162        // on either side of a link have links to each other,
1163        // but we only want to represent one of the links.
1164        // It doesn't matter which one. We do this by accumulating
1165        // a set of visited relations.
1166        Set visitedRelations = new HashSet();
1167        Iterator relations = relationList().iterator();
1168
1169        while (relations.hasNext()) {
1170            ComponentRelation relation = (ComponentRelation) relations.next();
1171            visitedRelations.add(relation);
1172
1173            // Skip the relation if it is not persistent.
1174            if (relation == null || !relation.isPersistent()) {
1175                continue;
1176            }
1177
1178            Iterator portsAndRelations = relation.linkedObjectsList()
1179                    .iterator();
1180
1181            while (portsAndRelations.hasNext()) {
1182                Object portOrRelation = portsAndRelations.next();
1183
1184                if (portOrRelation instanceof Relation) {
1185                    Relation otherRelation = (Relation) portOrRelation;
1186
1187                    // Skip the relation if it is not persistent.
1188                    if (otherRelation == null
1189                            || !otherRelation.isPersistent()) {
1190                        continue;
1191                    }
1192
1193                    // If we have visited the other relation already, then
1194                    // we have already represented the link. Skip this.
1195                    if (visitedRelations.contains(otherRelation)) {
1196                        continue;
1197                    }
1198
1199                    // If both ends of the link are inherited objects, then
1200                    // suppress the export. This depends on the level of export
1201                    // because if both ends of the link are implied, then the
1202                    // link is implied.
1203                    if (_commonImplier(relation, depth, otherRelation, depth)) {
1204                        continue;
1205                    }
1206
1207                    // Apply filter.
1208                    if (filter == null || filter.contains(relation)
1209                            && filter.contains(otherRelation)) {
1210                        // In order to support level-crossing links, consider the
1211                        // possibility that the relation is not contained by this.
1212                        String relationName;
1213
1214                        if (relation.getContainer() == this) {
1215                            relationName = relation.getName();
1216                        } else {
1217                            if (deepContains(relation)) {
1218                                // NOTE: This used to export the full name, but the
1219                                // relative name is sufficient.
1220                                relationName = relation.getName(this);
1221                            } else {
1222                                // Can't export the link here since when the
1223                                // MoML file is re-read there is no assurance that
1224                                // the relation exists when the link is to be
1225                                // created.  Need to delegate to the least common
1226                                // container.
1227                                _recordLevelCrossingLink(null, relation,
1228                                        otherRelation, 0);
1229                                continue;
1230                            }
1231                        }
1232
1233                        String otherRelationName;
1234
1235                        if (otherRelation.getContainer() == this) {
1236                            otherRelationName = otherRelation.getName();
1237                        } else {
1238                            // Can't export the link here since when the
1239                            // MoML file is re-read there is no assurance that
1240                            // the relation exists when the link is to be
1241                            // created.  Need to delegate to the least common
1242                            // container.
1243                            _recordLevelCrossingLink(null, relation,
1244                                    otherRelation, 0);
1245                            continue;
1246                        }
1247
1248                        result.append(
1249                                _getIndentPrefix(depth) + "<link relation1=\""
1250                                        + relationName + "\" relation2=\""
1251                                        + otherRelationName + "\"/>\n");
1252                    }
1253                }
1254            }
1255        }
1256
1257        return result.toString();
1258    }
1259
1260    /** Override the base class to initialize a data structure that can
1261     *  capture and then export level-crossing links deeply contained
1262     *  structure within. Otherwise, this delegates to the base
1263     *  class to do all the work.
1264     *  @param output The output stream to write to.
1265     *  @param depth The depth in the hierarchy, to determine indenting.
1266     *  @param name The name to use in the exported MoML.
1267     *  @exception IOException If an I/O error occurs.
1268     *  @see ptolemy.kernel.util.MoMLExportable
1269     */
1270    @Override
1271    public void exportMoML(Writer output, int depth, String name)
1272            throws IOException {
1273        try {
1274            _levelCrossingLinks = new LinkedList<LinkRecord>();
1275            super.exportMoML(output, depth, name);
1276        } finally {
1277            _levelCrossingLinks = null;
1278        }
1279    }
1280
1281    /** Get the attribute with the given name. The name may be compound,
1282     *  with fields separated by periods, in which case the attribute
1283     *  returned is contained by a (deeply) contained attribute, port,
1284     *  relation, or entity.
1285     *  If the name contains one or more periods, then it is assumed
1286     *  to be the relative name of an attribute contained by one of
1287     *  the contained attributes, ports, entities or relations.
1288     *  This method is read-synchronized on the workspace.
1289     *  @param name The name of the desired attribute.
1290     *  @return The requested attribute if it is found, null otherwise.
1291     */
1292    @Override
1293    public Attribute getAttribute(String name) {
1294        try {
1295            _workspace.getReadAccess();
1296
1297            // Check attributes and ports first.
1298            Attribute result = super.getAttribute(name);
1299
1300            if (result == null) {
1301                // Check entities first.
1302                String[] subnames = _splitName(name);
1303
1304                if (subnames[1] != null) {
1305                    ComponentEntity entity = getEntity(subnames[0]);
1306
1307                    if (entity != null) {
1308                        result = entity.getAttribute(subnames[1]);
1309                    }
1310
1311                    if (result == null) {
1312                        // Check relations.
1313                        ComponentRelation relation = getRelation(subnames[0]);
1314
1315                        if (relation != null) {
1316                            result = relation.getAttribute(subnames[1]);
1317                        }
1318                    }
1319                }
1320            }
1321
1322            return result;
1323        } finally {
1324            _workspace.doneReading();
1325        }
1326    }
1327
1328    /** Enumerate the contained entities in the order they were added
1329     *  (using their setContainer() method).
1330     *  The returned enumeration is static in the sense
1331     *  that it is not affected by any subsequent additions or removals
1332     *  of entities.
1333     *  This method is read-synchronized on the workspace.
1334     *  @deprecated Use entityList() instead.
1335     *  @return An enumeration of ComponentEntity objects.
1336     */
1337    @Deprecated
1338    public Enumeration getEntities() {
1339        return Collections.enumeration(entityList());
1340    }
1341
1342    /** Get a contained entity by name. The name may be compound,
1343     *  with fields separated by periods, in which case the entity
1344     *  returned is contained by a (deeply) contained entity.
1345     *  This method will return class definitions
1346     *  and ordinary entities.
1347     *  This method is read-synchronized on the workspace.
1348     *  @param name The name of the desired entity.
1349     *  @return An entity with the specified name, or null if none exists.
1350     */
1351    public ComponentEntity getEntity(String name) {
1352        try {
1353            _workspace.getReadAccess();
1354
1355            // This might be called from within a superclass constructor,
1356            // in which case there are no contained entities yet.
1357            if (_containedEntities == null) {
1358                return null;
1359            }
1360
1361            String[] subnames = _splitName(name);
1362
1363            if (subnames[1] == null) {
1364                return (ComponentEntity) _containedEntities.get(name);
1365            } else {
1366                Object match = _containedEntities.get(subnames[0]);
1367
1368                if (match == null) {
1369                    return null;
1370                } else {
1371                    if (match instanceof CompositeEntity) {
1372                        return ((CompositeEntity) match).getEntity(subnames[1]);
1373                    } else {
1374                        return null;
1375                    }
1376                }
1377            }
1378        } finally {
1379            _workspace.doneReading();
1380        }
1381    }
1382
1383    /** Get a contained port by name. The name may be compound,
1384     *  with fields separated by periods, in which case the port returned is
1385     *  contained by a (deeply) contained entity.
1386     *  This method is read-synchronized on the workspace.
1387     *  @param name The name of the desired port.
1388     *  @return A port with the specified name, or null if none exists.
1389     */
1390    @Override
1391    public Port getPort(String name) {
1392        try {
1393            _workspace.getReadAccess();
1394
1395            String[] subnames = _splitName(name);
1396
1397            if (subnames[1] == null) {
1398                return super.getPort(name);
1399            } else {
1400                ComponentEntity match = getEntity(subnames[0]);
1401
1402                if (match == null) {
1403                    return null;
1404                } else {
1405                    return match.getPort(subnames[1]);
1406                }
1407            }
1408        } finally {
1409            _workspace.doneReading();
1410        }
1411    }
1412
1413    /** Get a contained relation by name. The name may be compound,
1414     *  with fields separated by periods, in which case the relation
1415     *  returned is contained by a (deeply) contained entity.
1416     *  This method is read-synchronized on the workspace.
1417     *  @param name The name of the desired relation.
1418     *  @return A relation with the specified name, or null if none exists.
1419     */
1420    public ComponentRelation getRelation(String name) {
1421        try {
1422            _workspace.getReadAccess();
1423
1424            String[] subnames = _splitName(name);
1425
1426            if (subnames[1] == null) {
1427                return (ComponentRelation) _containedRelations.get(name);
1428            } else {
1429                ComponentEntity match = getEntity(subnames[0]);
1430
1431                if (match == null) {
1432                    return null;
1433                } else {
1434                    if (match instanceof CompositeEntity) {
1435                        return ((CompositeEntity) match)
1436                                .getRelation(subnames[1]);
1437                    } else {
1438                        return null;
1439                    }
1440                }
1441            }
1442        } finally {
1443            _workspace.doneReading();
1444        }
1445    }
1446
1447    /** Enumerate the relations contained by this entity.
1448     *  The returned enumeration is static in the sense
1449     *  that it is not affected by any subsequent additions or removals
1450     *  of relations.
1451     *  This method is read-synchronized on the workspace.
1452     *  @deprecated Use relationList() instead.
1453     *  @return An enumeration of ComponentRelation objects.
1454     */
1455    @Deprecated
1456    public Enumeration getRelations() {
1457        return Collections.enumeration(relationList());
1458    }
1459
1460    /** Return false since CompositeEntities are not atomic.
1461     *  Note that this will return false even if there are no contained
1462     *  entities or relations.  Derived classes may not override this.
1463     *  To hide the contents of the entity, they should override isOpaque().
1464     *  @return False.
1465     */
1466    @Override
1467    public final boolean isAtomic() {
1468        return false;
1469    }
1470
1471    /** Return false.  Derived classes may return true in order to hide
1472     *  their components behind opaque ports.
1473     *  @return True if the entity is opaque.
1474     *  @see ptolemy.kernel.CompositeEntity
1475     */
1476    @Override
1477    public boolean isOpaque() {
1478        return false;
1479    }
1480
1481    /** Lazy version of {@link #allAtomicEntityList()}.
1482     *  In this base class, this is identical to allAtomicEntityList(),
1483     *  except that if any inside entities are lazy, their contents
1484     *  are listed lazily.  Derived classes may omit from the returned list any
1485     *  entities whose instantiation is deferred.
1486     *  @return A list of ComponentEntity objects.
1487     */
1488    public List lazyAllAtomicEntityList() {
1489        // We don't use an Iterator here so that we can modify the list
1490        // rather than having both an Iterator and a result list.
1491        //
1492        // Note:
1493        // deepEntityList() should be renamed to deepOpaqueEntityList()
1494        // allAtomicEntityList() to deepAtomicEntityList()
1495        // However, the change would require a fair amount of work.
1496        //LinkedList entities = (LinkedList) deepEntityList();
1497        List entities = lazyDeepEntityList();
1498
1499        for (int i = 0; i < entities.size(); i++) {
1500            Object entity = entities.get(i);
1501
1502            if (entity instanceof CompositeEntity) {
1503                // Remove the composite actor and add its containees.
1504                entities.remove(i);
1505
1506                // Note that removing an element from the list causes
1507                // the indices of later elements to shift forward by 1.
1508                // We reduce the index i by one to match the index in
1509                // the list.
1510                i--;
1511                entities.addAll(
1512                        ((CompositeEntity) entity).lazyAllAtomicEntityList());
1513            }
1514        }
1515
1516        return entities;
1517    }
1518
1519    /** Lazy version of {#link #allCompositeEntityList()}.
1520     *  In this base class, this is identical to allCompositeEntityList(),
1521     *  except that if any contained composite is lazy, its contents
1522     *  are listed lazily.
1523     *  Derived classes may omit from the returned list any class
1524     *  definitions whose instantiation is deferred.
1525     *  @return A list of transparent ComponentEntity objects.
1526     */
1527    public List lazyAllCompositeEntityList() {
1528        try {
1529            _workspace.getReadAccess();
1530
1531            LinkedList result = new LinkedList();
1532
1533            // This might be called from within a superclass constructor,
1534            // in which case there are no contained entities yet.
1535            if (_containedEntities != null) {
1536                // Note that by directly using _containedEntities rather than
1537                // entityList() we are automatically lazy.
1538                Iterator entities = _containedEntities.elementList().iterator();
1539
1540                while (entities.hasNext()) {
1541                    ComponentEntity entity = (ComponentEntity) entities.next();
1542                    if (/*!entity.isClassDefinition()&& */!entity
1543                            .isOpaque() /*entity instanceof CompositeEntity*/) {
1544                        result.add(entity);
1545                        result.addAll(((CompositeEntity) entity)
1546                                .lazyAllCompositeEntityList());
1547
1548                    }
1549                }
1550            }
1551
1552            return result;
1553        } finally {
1554            _workspace.doneReading();
1555        }
1556    }
1557
1558    /** Return all the transparent and opaque composites.
1559     *  In this base class, if any contained composite is lazy, its contents
1560     *  are listed lazily.
1561     *  Derived classes may omit from the returned list any class
1562     *  definitions whose instantiation is deferred.
1563     *  @return A list of transparent ComponentEntity objects.
1564     */
1565    public List lazyAllCompositeTransparentAndOpaqueEntityList() {
1566        try {
1567            _workspace.getReadAccess();
1568
1569            LinkedList result = new LinkedList();
1570
1571            // This might be called from within a superclass constructor,
1572            // in which case there are no contained entities yet.
1573            if (_containedEntities != null) {
1574                // Note that by directly using _containedEntities rather than
1575                // entityList() we are automatically lazy.
1576                Iterator entities = _containedEntities.elementList().iterator();
1577                while (entities.hasNext()) {
1578                    ComponentEntity entity = (ComponentEntity) entities.next();
1579                    if (/*!entity.isClassDefinition()&& !entity.isOpaque()*/
1580                    entity instanceof CompositeEntity) {
1581                        result.add(entity);
1582                        result.addAll(((CompositeEntity) entity)
1583                                .lazyAllCompositeTransparentAndOpaqueEntityList());
1584
1585                    }
1586                }
1587            }
1588
1589            return result;
1590        } finally {
1591            _workspace.doneReading();
1592        }
1593    }
1594
1595    /** Lazy version of {@link #classDefinitionList()}.
1596     *  In this base class, this is identical to classDefinitionList(),
1597     *  but derived classes may omit from the returned list any class
1598     *  definitions whose instantiation is deferred.
1599     *  @return A list of ComponentEntity objects.
1600     */
1601    public List lazyClassDefinitionList() {
1602        return classDefinitionList();
1603    }
1604
1605    /** Lazy version of {@link #deepEntityList()}.
1606     *  In this base class, this is identical to deepEntityList(),
1607     *  except that if any contained composite is lazy, its contents
1608     *  are listed lazily.
1609     *  Derived classes may omit from the returned list any entities
1610     *  whose instantiation is deferred.
1611     *  @return A list of ComponentEntity objects.
1612     */
1613    public List lazyDeepEntityList() {
1614        try {
1615            _workspace.getReadAccess();
1616
1617            LinkedList result = new LinkedList();
1618
1619            // This might be called from within a superclass constructor,
1620            // in which case there are no contained entities yet.
1621            if (_containedEntities != null) {
1622                // Note that by directly using _containedEntities rather than
1623                // entityList() we are automatically lazy.
1624                Iterator entities = _containedEntities.elementList().iterator();
1625                while (entities.hasNext()) {
1626                    ComponentEntity entity = (ComponentEntity) entities.next();
1627
1628                    if (!entity.isClassDefinition()) {
1629                        if (entity.isOpaque()) {
1630                            result.add(entity);
1631                        } else {
1632                            result.addAll(((CompositeEntity) entity)
1633                                    .lazyDeepEntityList());
1634                        }
1635                    }
1636                }
1637            }
1638
1639            return result;
1640        } finally {
1641            _workspace.doneReading();
1642        }
1643    }
1644
1645    /** Lazy version of {@link #entityList()}.
1646     *  In this base class, this is identical to entityList(),
1647     *  but derived classes may omit from the returned list any
1648     *  entities whose instantiation is deferred.
1649     *  @return A list of ComponentEntity objects.
1650     */
1651    public List lazyEntityList() {
1652        return entityList();
1653    }
1654
1655    /** Lazy version of {@link #relationList()}.
1656     *  In this base class, this is identical to relationList(),
1657     *  but derived classes may omit from the returned list any
1658     *  relations whose instantiation is deferred.
1659     *  @return A list of Relation objects.
1660     */
1661    public List lazyRelationList() {
1662        return relationList();
1663    }
1664
1665    /** Create a new relation with the specified name, add it to the
1666     *  relation list, and return it. Derived classes can override
1667     *  this to create domain-specific subclasses of ComponentRelation.
1668     *  This method is write-synchronized on the workspace and increments
1669     *  its version number.
1670     *  @param name The name of the new relation.
1671     *  @return The new relation.
1672     *  @exception IllegalActionException If name argument is null.
1673     *  @exception NameDuplicationException If name collides with a name
1674     *   already in the container.
1675     */
1676    public ComponentRelation newRelation(String name)
1677            throws IllegalActionException, NameDuplicationException {
1678        try {
1679            _workspace.getWriteAccess();
1680
1681            ComponentRelation rel = new ComponentRelation(this, name);
1682            return rel;
1683        } finally {
1684            _workspace.doneWriting();
1685        }
1686    }
1687
1688    /** Return the number of contained entities, not including
1689     *  class definitions.
1690     *  This method is read-synchronized on the workspace.
1691     *  @return The number of entities.
1692     *  @deprecated Use numberOfEntities
1693     *  @see #numberOfEntities()
1694     */
1695    @Deprecated
1696    public int numEntities() {
1697        return numberOfEntities();
1698    }
1699
1700    /** Return the number of contained relations.
1701     *  This method is read-synchronized on the workspace.
1702     *  @return The number of relations.
1703     *  @deprecated Use numberOfRelations.
1704     */
1705    @Deprecated
1706    public int numRelations() {
1707        return numberOfRelations();
1708    }
1709
1710    /** Return the number of contained entities, not including
1711     *  class definitions.
1712     *  This method is read-synchronized on the workspace.
1713     *  @return The number of entities.
1714     */
1715    public int numberOfEntities() {
1716        try {
1717            _workspace.getReadAccess();
1718            return entityList().size();
1719        } finally {
1720            _workspace.doneReading();
1721        }
1722    }
1723
1724    /** Return the number of contained relations.
1725     *  This method is read-synchronized on the workspace.
1726     *  @return The number of relations.
1727     */
1728    public int numberOfRelations() {
1729        try {
1730            _workspace.getReadAccess();
1731            return _containedRelations.size();
1732        } finally {
1733            _workspace.doneReading();
1734        }
1735    }
1736
1737    /** List the relations contained by this entity.
1738     *  The returned list is static in the sense
1739     *  that it is not affected by any subsequent additions or removals
1740     *  of relations.
1741     *  This method is read-synchronized on the workspace.
1742     *  @return An unmodifiable list of ComponentRelation objects.
1743     */
1744    public List relationList() {
1745        try {
1746            _workspace.getReadAccess();
1747
1748            // Copy the list so we can create a static enumeration.
1749            NamedList relationsCopy = new NamedList(_containedRelations);
1750            return relationsCopy.elementList();
1751        } finally {
1752            _workspace.doneReading();
1753        }
1754    }
1755
1756    /** Remove all contained entities and unlink them from all relations.
1757     *  This is done by setting their containers to null.
1758     *  This method is read-synchronized on the workspace
1759     *  and increments its version number.
1760     */
1761    public void removeAllEntities() {
1762        try {
1763            _workspace.getReadAccess();
1764
1765            Iterator entities = entityList().iterator();
1766
1767            while (entities.hasNext()) {
1768                ComponentEntity entity = (ComponentEntity) entities.next();
1769
1770                try {
1771                    entity.setContainer(null);
1772                } catch (KernelException ex) {
1773                    // This exception should not be thrown.
1774                    throw new InternalErrorException(this, ex,
1775                            "Internal error in CompositeEntity."
1776                                    + "removeAllEntities() method!");
1777                }
1778            }
1779        } finally {
1780            _workspace.doneReading();
1781        }
1782    }
1783
1784    /** Remove all contained relations and unlink them from everything.
1785     *  This is done by setting their containers to null.
1786     *  This method is write-synchronized on the workspace
1787     *  and increments its version number.
1788     */
1789    public void removeAllRelations() {
1790        try {
1791            _workspace.getWriteAccess();
1792
1793            Iterator relations = relationList().iterator();
1794
1795            while (relations.hasNext()) {
1796                ComponentRelation relation = (ComponentRelation) relations
1797                        .next();
1798
1799                try {
1800                    relation.setContainer(null);
1801                } catch (KernelException ex) {
1802                    // This exception should not be thrown.
1803                    throw new InternalErrorException(this, ex,
1804                            "Internal error in CompositeEntity."
1805                                    + "removeAllRelations() method!");
1806                }
1807            }
1808        } finally {
1809            _workspace.doneWriting();
1810        }
1811    }
1812
1813    /** Specify whether this object is a class definition.
1814     *  If the argument is true and this entity is not a class
1815     *  definition, then all level crossing relations that
1816     *  This method overrides the base class to check that if the
1817     *  argument is true, then this entity contains no ports with links.
1818     *  This method is write synchronized on the workspace.
1819     *  @param isClass True to make this object a class definition.
1820     *  @exception IllegalActionException If the argument is true and
1821     *   this entity contains ports with links.
1822     */
1823    @Override
1824    public void setClassDefinition(boolean isClass)
1825            throws IllegalActionException {
1826        // The situation is that an AO class definition is not allowed to have
1827        // any connections to other things.  Thus, if an instance is converted
1828        // to a class, it should be first disconnected. However, a Subscriber
1829        // has a "hidden" connection.  This connection may or may not cross
1830        // the boundary of the class definition. It should only be disconnected
1831        // if it does.
1832
1833        // We need to disconnect upon invocation of
1834        // setClassDefinition().
1835        // It does have to traverse the whole tree below the actor being
1836        // converted to a class and disconnect any level-crossing link that
1837        // traverses to outside the class definition.
1838
1839        // We also need to worry about the converse: When a class is converted
1840        // to an instance, we need to find all inside Publisher/Subscriber actors
1841        // and call _updateLinks().  (FIXME: this is not done)
1842        if (isClass && !isClassDefinition()) {
1843            try {
1844                workspace().getWriteAccess();
1845                // Converting from an instance to a class.
1846                super.setClassDefinition(isClass);
1847                _unlinkLevelCrossingLinksToOutside(this);
1848            } finally {
1849                workspace().doneWriting();
1850            }
1851        } else {
1852            super.setClassDefinition(isClass);
1853        }
1854    }
1855
1856    /** Override the base class so that if the argument is null, all
1857     *  level-crossing links from inside this composite to outside this
1858     *  composite are removed.
1859     *  @param container The proposed container.
1860     *  @exception IllegalActionException If the action would result in a
1861     *   recursive containment structure, or if
1862     *   this entity and container are not in the same workspace, or
1863     *   if the protected method _checkContainer() throws it, or if
1864     *   a contained Settable becomes invalid and the error handler
1865     *   throws it.
1866     *  @exception NameDuplicationException If the name of this entity
1867     *   collides with a name already in the container.
1868     *  @see #getContainer()
1869     */
1870    @Override
1871    public void setContainer(CompositeEntity container)
1872            throws IllegalActionException, NameDuplicationException {
1873        if (container == null) {
1874            // This composite is being removed from the model.
1875            // Remove level-crossing links.
1876            try {
1877                _workspace.getWriteAccess();
1878                _unlinkLevelCrossingLinksToOutside(this);
1879                // Findbugs reports "load of known null value" so we use null
1880                super.setContainer(null);
1881            } finally {
1882                _workspace.doneWriting();
1883            }
1884        } else {
1885            super.setContainer(container);
1886        }
1887    }
1888
1889    /** Return a string describing how many actors, parameters,
1890     * ports, and relations are in this CompositeEntity.
1891     * Entities whose instantiation is deferred are not
1892     * included.
1893     * @param className If non-null and non-empty, then also
1894     * include the number of objects with the give name.
1895     * @return a string describing the number of components.
1896     * @exception IllegalActionException If the class named by
1897     * actorClassName cannot be found.
1898     */
1899    public String statistics(String className) throws IllegalActionException {
1900        // FIXME: The right way to do this is to have each class
1901        // in the hierarchy have a statistics method.
1902        try {
1903            _workspace.getReadAccess();
1904
1905            Class clazz = null;
1906            try {
1907                if (className != null && className.length() > 0) {
1908                    clazz = Class.forName(className);
1909                }
1910            } catch (Exception ex) {
1911                throw new IllegalActionException(null, ex,
1912                        "Failed to instantiate \"" + className + "\"");
1913            }
1914
1915            // Use the lazy version to avoid triggering a populate of LazyTypedCompositeActor.
1916            List atomicEntities = lazyAllAtomicEntityList();
1917            int entityCount = atomicEntities.size();
1918
1919            Map<String, Integer> actorMap = new HashMap<String, Integer>();
1920            Integer one = Integer.valueOf(1);
1921
1922            int attributeCount = 0, entityClassCount = 0;
1923            Iterator entities = atomicEntities.iterator();
1924            while (entities.hasNext()) {
1925                ComponentEntity entity = (ComponentEntity) entities.next();
1926                List attributeList = entity.attributeList();
1927                attributeCount += attributeList.size();
1928
1929                Class entityClass = entity.getClass();
1930
1931                // Create a map with the count of actors
1932                String entityClassName = entityClass.getName();
1933                if (!actorMap.containsKey(entityClassName)) {
1934                    actorMap.put(entityClassName, one);
1935                } else {
1936                    actorMap.put(entityClassName,
1937                            Integer.valueOf(actorMap.get(entityClassName) + 1));
1938                }
1939
1940                if (clazz != null) {
1941                    if (clazz.isAssignableFrom(entityClass)) {
1942                        entityClassCount++;
1943                    } else {
1944                        // Search the attributes
1945                        Iterator attributes = attributeList.iterator();
1946                        while (attributes.hasNext()) {
1947                            Attribute attribute = (Attribute) attributes.next();
1948                            if (clazz.isAssignableFrom(attribute.getClass())) {
1949                                entityClassCount++;
1950                            }
1951                        }
1952                    }
1953                }
1954            }
1955
1956            ArrayList actorArrayList = new ArrayList(actorMap.entrySet());
1957            //Sort the values based on values first and then keys.
1958            Collections.sort(actorArrayList, new CountComparator());
1959
1960            StringBuffer actorNames = new StringBuffer();
1961            Iterator actors = actorArrayList.iterator();
1962            while (actors.hasNext()) {
1963                Map.Entry<String, Integer> actor = (Map.Entry) actors.next();
1964                actorNames
1965                        .append(actor.getKey() + " " + actor.getValue() + "\n");
1966            }
1967
1968            int compositeEntityCount = 0;
1969            int opaqueCompositeEntityCount = 0;
1970            List relationList = lazyRelationList();
1971            int relationCount = relationList.size();
1972            if (clazz != null) {
1973                // Search the relations
1974                Iterator relations = relationList.iterator();
1975                while (relations.hasNext()) {
1976                    Relation relation = (Relation) relations.next();
1977                    if (clazz.isAssignableFrom(relation.getClass())) {
1978                        entityClassCount++;
1979                    }
1980                }
1981            }
1982
1983            Map<Integer, Integer> compositeEntityDepthMap = new TreeMap<Integer, Integer>();
1984            entities = lazyAllCompositeTransparentAndOpaqueEntityList()
1985                    .iterator();
1986
1987            while (entities.hasNext()) {
1988                Entity entity = (Entity) entities.next();
1989                if (entity instanceof CompositeEntity) {
1990                    compositeEntityCount++;
1991                    if (((CompositeEntity) entity).isOpaque()) {
1992                        opaqueCompositeEntityCount++;
1993                    }
1994                    // Find the depth and add it to the list
1995                    Integer depth = Integer.valueOf(entity.depthInHierarchy());
1996                    if (!compositeEntityDepthMap.containsKey(depth)) {
1997                        compositeEntityDepthMap.put(depth, one);
1998                    } else {
1999                        compositeEntityDepthMap.put(depth, Integer.valueOf(
2000                                compositeEntityDepthMap.get(depth) + 1));
2001                    }
2002
2003                    relationList = ((CompositeEntity) entity)
2004                            .lazyRelationList();
2005                    relationCount += relationList.size();
2006                    if (clazz != null) {
2007                        if (clazz.isAssignableFrom(entity.getClass())) {
2008                            entityClassCount++;
2009                        } else {
2010                            // Search the relations
2011                            Iterator relations = relationList.iterator();
2012                            while (relations.hasNext()) {
2013                                Relation relation = (Relation) relations.next();
2014                                if (clazz.isAssignableFrom(
2015                                        relation.getClass())) {
2016                                    entityClassCount++;
2017                                }
2018                            }
2019                        }
2020                    }
2021                }
2022            }
2023
2024            // Generate a string with the depths
2025            StringBuffer compositeEntityDepths = new StringBuffer();
2026            for (Map.Entry<Integer, Integer> depth : compositeEntityDepthMap
2027                    .entrySet()) {
2028                compositeEntityDepths.append("Depth: " + depth.getKey()
2029                        + " # of Composites at that depth: " + depth.getValue()
2030                        + "\n");
2031            }
2032
2033            return "Size Statistics for " + getFullName() + "\nAtomicEntities: "
2034                    + entityCount + "\nCompositeEntities: "
2035                    + compositeEntityCount + "\nOpaqueCompositeEntities: "
2036                    + opaqueCompositeEntityCount + "\nRelations: "
2037                    + relationCount + "\nAttributes: " + attributeCount
2038                    + (clazz == null ? ""
2039                            : "\nEntities of type \"" + clazz.getName() + "\": "
2040                                    + entityClassCount)
2041                    + "\nAtomic Actor Names and Counts:\n" + actorNames
2042                    + "\nComposite Entity Depths and Counts:\n"
2043                    + compositeEntityDepths;
2044
2045        } finally {
2046            _workspace.doneReading();
2047        }
2048    }
2049
2050    /** Return a name that is guaranteed to not be the name of
2051     *  any contained attribute, port, class, entity, or relation.
2052     *  In this implementation, the argument
2053     *  is stripped of any numeric suffix, and then a numeric suffix
2054     *  is appended and incremented until a name is found that does not
2055     *  conflict with a contained attribute, port, class, entity, or relation.
2056     *  If this composite entity or any composite entity that it contains
2057     *  defers its MoML definition (i.e., it is an instance of a class or
2058     *  a subclass), then the prefix gets appended with "_<i>n</i>_",
2059     *  where <i>n</i> is the depth of this deferral. That is, if the object
2060     *  deferred to also defers, then <i>n</i> is incremented.
2061     *  <p>Note that this method should be called judiciously from when
2062     *  the CompositeEntity is large.  The reason is that this method
2063     *  searches for matching attributes, ports, classes, entities
2064     *  and relations, which can result in slow performance.
2065     *
2066     *  @param prefix A prefix for the name.
2067     *  @return A unique name.
2068     */
2069    @Override
2070    public String uniqueName(String prefix) {
2071        if (prefix == null) {
2072            prefix = "null";
2073        }
2074
2075        prefix = _stripNumericSuffix(prefix);
2076
2077        String candidate = prefix;
2078
2079        // NOTE: The list returned by getPrototypeList() has
2080        // length equal to the number of containers of this object
2081        // that return non-null to getParent(). That number is
2082        // assured to be at least one greater than the corresponding
2083        // number for any of the parents returned by getParent().
2084        // Hence, we can use that number to minimize the likelyhood
2085        // of inadvertent capture.
2086        try {
2087            int depth = getPrototypeList().size();
2088
2089            if (depth > 0) {
2090                prefix = prefix + "_" + depth + "_";
2091            }
2092        } catch (IllegalActionException e) {
2093            // Derivation invariant is not satisified.
2094            throw new InternalErrorException(e);
2095        }
2096
2097        // FIXME: because we start with 2 each time, then if
2098        // we are calling this method many times we will need
2099        // to search the CompositeEntity for matching
2100        // attributes,  ports, entities and releations.
2101        // This will have poor behaviour for large CompositeEntities.
2102        // However, if we cached the uniqueNameIndex, then
2103        // it would tend to increase over time, which would be
2104        // unusual if we created a relation (_R2), deleted it
2105        // and created another relation, which would get the name
2106        // _R3, instead of _R2.
2107
2108        int uniqueNameIndex = 2;
2109
2110        while (getAttribute(candidate) != null || getPort(candidate) != null
2111                || getEntity(candidate) != null
2112                || getRelation(candidate) != null) {
2113            candidate = prefix + uniqueNameIndex++;
2114        }
2115
2116        return candidate;
2117    }
2118
2119    ///////////////////////////////////////////////////////////////////
2120    ////                         protected methods                 ////
2121
2122    /** Add an entity or class definition to this container. This method
2123     *  should not be used directly.  Call the setContainer() method of
2124     *  the entity instead. This method does not set
2125     *  the container of the entity to point to this composite entity.
2126     *  It assumes that the entity is in the same workspace as this
2127     *  container, but does not check.  The caller should check.
2128     *  Derived classes may override this method to constrain the
2129     *  the entity to a subclass of ComponentEntity.
2130     *  This method is <i>not</i> synchronized on the workspace, so the
2131     *  caller should be.
2132     *  @param entity Entity to contain.
2133     *  @exception IllegalActionException If the entity has no name, or the
2134     *   action would result in a recursive containment structure.
2135     *  @exception NameDuplicationException If the name collides with a name
2136     *  already in the entity.
2137     */
2138    protected void _addEntity(ComponentEntity entity)
2139            throws IllegalActionException, NameDuplicationException {
2140        if (entity.deepContains(this)) {
2141            throw new IllegalActionException(entity, this,
2142                    "Attempt to construct recursive containment");
2143        }
2144
2145        _containedEntities.append(entity);
2146    }
2147
2148    /** Add a relation to this container. This method should not be used
2149     *  directly.  Call the setContainer() method of the relation instead.
2150     *  This method does not set
2151     *  the container of the relation to refer to this container.
2152     *  This method is <i>not</i> synchronized on the workspace, so the
2153     *  caller should be.
2154     *  @param relation Relation to contain.
2155     *  @exception IllegalActionException If the relation has no name.
2156     *  @exception NameDuplicationException If the name collides with a name
2157     *   already on the contained relations list.
2158     */
2159    protected void _addRelation(ComponentRelation relation)
2160            throws IllegalActionException, NameDuplicationException {
2161        _containedRelations.append(relation);
2162    }
2163
2164    /** Adjust the deferrals in this object. This method should
2165     *  be called on any newly created object that is created by
2166     *  cloning. While cloning, parent relations are set to null.
2167     *  That is, no object in the clone has a parent. This method
2168     *  identifies the correct parent for any object in the clone.
2169     *  To do this, it uses the class name. Specifically, if this
2170     *  object has a class name that refers to a class in scope,
2171     *  then it replaces the current parent with that object.
2172     *  To look for a class in scope, we go up the hierarchy, but
2173     *  no more times than the return value of getDerivedLevel().
2174     *  The reason for this is that if the class from which this
2175     *  object is defined is above that level, then we do not want
2176     *  to establish a parent relationship with that class. This
2177     *  object is implied, and the parent relationship of the object
2178     *  from which it is implied is sufficient.
2179     *  <p>
2180     *  Derived classes that contain other objects should recursively
2181     *  call this method on contained objects.
2182     *  @exception IllegalActionException If the class found in scope
2183     *   cannot be set.
2184     */
2185    @Override
2186    protected void _adjustDeferrals() throws IllegalActionException {
2187        super._adjustDeferrals();
2188
2189        Iterator containedClasses = lazyClassDefinitionList().iterator();
2190
2191        while (containedClasses.hasNext()) {
2192            NamedObj containedObject = (NamedObj) containedClasses.next();
2193
2194            if (containedObject instanceof ComponentEntity) {
2195                ((ComponentEntity) containedObject)._adjustDeferrals();
2196            }
2197        }
2198
2199        Iterator containedEntities = lazyEntityList().iterator();
2200
2201        while (containedEntities.hasNext()) {
2202            NamedObj containedObject = (NamedObj) containedEntities.next();
2203
2204            if (containedObject instanceof ComponentEntity) {
2205                ((ComponentEntity) containedObject)._adjustDeferrals();
2206            }
2207        }
2208    }
2209
2210    /** Return a list of decorators contained by this object.
2211     *  This overrides the base class to include not only attributes that
2212     *  implement the {@link Decorator} interface, but also entities.
2213     *  @return A list of contained decorators.
2214     */
2215    @Override
2216    protected List<Decorator> _containedDecorators() {
2217        List<Decorator> result = super._containedDecorators();
2218        result.addAll(entityList(Decorator.class));
2219        return result;
2220    }
2221
2222    /** List the opaque entities that are directly or indirectly
2223     *  contained by this entity.  The list will be empty if there
2224     *  are no such contained entities. This list does not include
2225     *  class definitions nor anything contained by them.
2226     *  This method is <b>not</b> read-synchronized on the workspace,
2227     *  its caller should be read-synchronized.
2228     *  @param result The list of opaque ComponentEntity objects.
2229     *  @see #classDefinitionList()
2230     *  @see #allAtomicEntityList()
2231     *  @see #deepEntityList()
2232     */
2233    protected void _deepOpaqueEntityList(List result) {
2234
2235        // This might be called from within a superclass constructor,
2236        // in which case there are no contained entities yet.
2237        if (_containedEntities != null) {
2238            Iterator entities = _containedEntities.elementList().iterator();
2239
2240            while (entities.hasNext()) {
2241                ComponentEntity entity = (ComponentEntity) entities.next();
2242                if (!entity.isClassDefinition()) {
2243                    if (entity.isOpaque()) {
2244                        result.add(entity);
2245                    } else {
2246                        ((CompositeEntity) entity)
2247                                ._deepOpaqueEntityList(result);
2248                    }
2249                }
2250            }
2251        }
2252    }
2253
2254    /** Return a description of the object.  The level of detail depends
2255     *  on the argument, which is an or-ing of the static final constants
2256     *  defined in the NamedObj class.  Lines are indented according to
2257     *  to the level argument using the protected method _getIndentPrefix().
2258     *  Zero, one or two brackets can be specified to surround the returned
2259     *  description.  If one is specified it is the the leading bracket.
2260     *  This is used by derived classes that will append to the description.
2261     *  Those derived classes are responsible for the closing bracket.
2262     *  An argument other than 0, 1, or 2 is taken to be equivalent to 0.
2263     *  This method is read-synchronized on the workspace.
2264     *  @param detail The level of detail.
2265     *  @param indent The amount of indenting.
2266     *  @param bracket The number of surrounding brackets (0, 1, or 2).
2267     *  @return A description of the object.
2268     *  @exception IllegalActionException If thrown while getting the
2269     *  description of subcomponents.
2270     */
2271    @Override
2272    protected String _description(int detail, int indent, int bracket)
2273            throws IllegalActionException {
2274        try {
2275            _workspace.getReadAccess();
2276
2277            StringBuffer result = new StringBuffer();
2278
2279            if (bracket == 1 || bracket == 2) {
2280                result.append(super._description(detail, indent, 1));
2281            } else {
2282                result.append(super._description(detail, indent, 0));
2283            }
2284
2285            if ((detail & CONTENTS) != 0) {
2286                if (result.toString().trim().length() > 0) {
2287                    result.append(" ");
2288                }
2289
2290                result.append("classes {\n");
2291
2292                Iterator classes = classDefinitionList().iterator();
2293
2294                while (classes.hasNext()) {
2295                    ComponentEntity entity = (ComponentEntity) classes.next();
2296                    result.append(
2297                            entity._description(detail, indent + 1, 2) + "\n");
2298                }
2299
2300                result.append(_getIndentPrefix(indent) + "} entities {\n");
2301
2302                Iterator entities = entityList().iterator();
2303
2304                while (entities.hasNext()) {
2305                    ComponentEntity entity = (ComponentEntity) entities.next();
2306                    result.append(
2307                            entity._description(detail, indent + 1, 2) + "\n");
2308                }
2309
2310                result.append(_getIndentPrefix(indent) + "} relations {\n");
2311
2312                Iterator relations = relationList().iterator();
2313
2314                while (relations.hasNext()) {
2315                    Relation relation = (Relation) relations.next();
2316                    result.append(relation._description(detail, indent + 1, 2)
2317                            + "\n");
2318                }
2319
2320                result.append(_getIndentPrefix(indent) + "}");
2321            }
2322
2323            if (bracket == 2) {
2324                result.append("}");
2325            }
2326
2327            return result.toString();
2328        } finally {
2329            _workspace.doneReading();
2330        }
2331    }
2332
2333    /** Write a MoML description of the contents of this object, which
2334     *  in this class are the attributes, ports, contained relations,
2335     *  and contained entities, plus all links.  The links are written
2336     *  in an order that respects the ordering in ports, but not necessarily
2337     *  the ordering in relations.  This method is called
2338     *  by exportMoML().  Each description is indented according to the
2339     *  specified depth and terminated with a newline character.
2340     *  @param output The output to write to.
2341     *  @param depth The depth in the hierarchy, to determine indenting.
2342     *  @exception IOException If an I/O error occurs.
2343     */
2344    @Override
2345    protected void _exportMoMLContents(Writer output, int depth)
2346            throws IOException {
2347        if (depth == 1 && getContainer() == null) {
2348            if (getAttribute("_createdBy") == null) {
2349                // If there is no _createdBy attribute, then add one.
2350                output.write(_getIndentPrefix(depth)
2351                        + "<property name=\"_createdBy\" " + "class=\""
2352                        + VersionAttribute.CURRENT_VERSION.getClass().getName()
2353                        + "\" value=\""
2354                        + VersionAttribute.CURRENT_VERSION.getExpression()
2355                        + "\">\n");
2356                output.write(_getIndentPrefix(depth) + "</property>\n");
2357            } else if (getAttribute("_createdBy") != null) {
2358                try {
2359                    ((VersionAttribute) getAttribute("_createdBy"))
2360                            .setExpression(VersionAttribute.CURRENT_VERSION
2361                                    .getExpression());
2362                } catch (IllegalActionException ex) {
2363                    throw new InternalErrorException(this, ex,
2364                            "Failed to update _createdBy");
2365                }
2366            }
2367        }
2368
2369        super._exportMoMLContents(output, depth);
2370
2371        Iterator classes = classDefinitionList().iterator();
2372
2373        while (classes.hasNext()) {
2374            ComponentEntity entity = (ComponentEntity) classes.next();
2375            entity.exportMoML(output, depth);
2376        }
2377
2378        Iterator entities = entityList().iterator();
2379
2380        while (entities.hasNext()) {
2381            ComponentEntity entity = (ComponentEntity) entities.next();
2382            entity.exportMoML(output, depth);
2383        }
2384
2385        Iterator relations = relationList().iterator();
2386
2387        while (relations.hasNext()) {
2388            ComponentRelation relation = (ComponentRelation) relations.next();
2389            relation.exportMoML(output, depth);
2390        }
2391
2392        // NOTE: We used to write the links only if
2393        // this object did not defer to another
2394        // (getMoMLInfo().deferTo was null), and
2395        // would instead record links in a MoMLAttribute.
2396        // That mechanism was far too fragile.
2397        // EAL 3/10/04
2398        output.write(exportLinks(depth, null));
2399
2400        // Export level crossing links, if there are any.
2401        if (_levelCrossingLinks != null) {
2402            for (LinkRecord record : _levelCrossingLinks) {
2403                if (record.port != null) {
2404                    // Do not export if the relation and port are derived and
2405                    // share a common container that has the parent-child
2406                    // relation that implies them.
2407                    if (!_commonImplier(record.relation1,
2408                            depth + _depthInside(record.relation1), record.port,
2409                            depth + _depthInside(record.port))) {
2410
2411                        // Escape any < character in name. unescapeForXML occurs in
2412                        // setName(String).
2413                        String escapedRecordPortName = StringUtilities
2414                                .escapeForXML(record.port.getName(this));
2415                        String escapedRecordRelation1Name = StringUtilities
2416                                .escapeForXML(record.relation1.getName(this));
2417                        output.write(_getIndentPrefix(depth) + "<link port=\""
2418                                + escapedRecordPortName + "\" insertAt=\""
2419                                + record.index + "\" relation=\""
2420                                + escapedRecordRelation1Name + "\"/>\n");
2421                    }
2422                } else {
2423                    // Do not export if both relations are derived and
2424                    // share a common container that has the parent-child
2425                    // relation that implies them.
2426                    if (!_commonImplier(record.relation1,
2427                            depth + _depthInside(record.relation1),
2428                            record.relation2,
2429                            depth + _depthInside(record.relation2))) {
2430
2431                        String escapedRecordRelation1Name = StringUtilities
2432                                .escapeForXML(record.relation1.getName(this));
2433                        String escapedRecordRelation2Name = StringUtilities
2434                                .escapeForXML(record.relation2.getName(this));
2435
2436                        output.write(_getIndentPrefix(depth)
2437                                + "<link relation1=\""
2438                                + escapedRecordRelation1Name + "\" relation2=\""
2439                                + escapedRecordRelation2Name + "\"/>\n");
2440                    }
2441                }
2442            }
2443        }
2444    }
2445
2446    /** Notify this entity that the given entity has been added inside it.
2447     *  This base class does nothing.   Derived classes may override it to
2448     *  do something useful in responds to the notification.
2449     *  It is <i>not</i> synchronized on the workspace, so the
2450     *  caller should be.
2451     *
2452     *  @param entity The contained entity.
2453     */
2454    protected void _finishedAddEntity(ComponentEntity entity) {
2455    }
2456
2457    /** Remove the specified entity. This method should not be used
2458     *  directly.  Call the setContainer() method of the entity instead with
2459     *  a null argument.
2460     *  The entity is assumed to be contained by this composite (otherwise,
2461     *  nothing happens). This does not alter the entity in any way.
2462     *  This method is <i>not</i> synchronized on the workspace, so the
2463     *  caller should be.
2464     *  @param entity The entity to remove.
2465     */
2466    protected void _removeEntity(ComponentEntity entity) {
2467        _containedEntities.remove(entity);
2468    }
2469
2470    /** Remove the specified relation. This method should not be used
2471     *  directly.  Call the setContainer() method of the relation instead with
2472     *  a null argument.
2473     *  The relation is assumed to be contained by this composite (otherwise,
2474     *  nothing happens). This does not alter the relation in any way.
2475     *  This method is <i>not</i> synchronized on the workspace, so the
2476     *  caller should be.
2477     *  @param relation The relation to remove.
2478     */
2479    protected void _removeRelation(ComponentRelation relation) {
2480        _containedRelations.remove(relation);
2481    }
2482
2483    /** Validate attributes deeply contained by this object if they
2484     *  implement the Settable interface by calling their validate() method.
2485     *  This method overrides the base class to check attributes contained
2486     *  by the contained entities and relations.
2487     *  Errors that are triggered by this validation are handled by calling
2488     *  handleModelError().
2489     *  @param attributesValidated A HashSet of Attributes that have
2490     *  already been validated.  For example, Settables that implement
2491     *  the SharedSettable interface are validated only once.
2492     *  @see ptolemy.kernel.util.NamedObj#handleModelError(NamedObj, IllegalActionException)
2493     *  @exception IllegalActionException If the superclass throws it
2494     *  or if handleModelError() throws it.
2495     */
2496    @Override
2497    protected void _validateSettables(Collection attributesValidated)
2498            throws IllegalActionException {
2499        super._validateSettables(attributesValidated);
2500        Iterator classes = classDefinitionList().iterator();
2501        while (classes.hasNext()) {
2502            Entity entity = (Entity) classes.next();
2503            if (entity instanceof Settable) {
2504                try {
2505                    Collection validated = ((Settable) entity).validate();
2506                    if (validated != null) {
2507                        attributesValidated.addAll(validated);
2508                    }
2509                    attributesValidated.add(entity);
2510                } catch (IllegalActionException ex) {
2511                    if (!handleModelError(this, ex)) {
2512                        throw ex;
2513                    }
2514                }
2515            }
2516            entity._validateSettables(attributesValidated);
2517        }
2518
2519        Iterator entities = entityList().iterator();
2520        while (entities.hasNext()) {
2521            Entity entity = (Entity) entities.next();
2522            if (entity instanceof Settable) {
2523                try {
2524                    Collection validated = ((Settable) entity).validate();
2525                    if (validated != null) {
2526                        attributesValidated.addAll(validated);
2527                    }
2528                    attributesValidated.add(entity);
2529                } catch (IllegalActionException ex) {
2530                    if (!handleModelError(this, ex)) {
2531                        throw ex;
2532                    }
2533                }
2534            }
2535            entity._validateSettables(attributesValidated);
2536        }
2537
2538        Iterator relations = relationList().iterator();
2539        while (relations.hasNext()) {
2540            Relation relation = (Relation) relations.next();
2541            if (relation instanceof Settable) {
2542                try {
2543                    Collection validated = ((Settable) relation).validate();
2544                    if (validated != null) {
2545                        attributesValidated.addAll(validated);
2546                    }
2547                    attributesValidated.add(relation);
2548                } catch (IllegalActionException ex) {
2549                    if (!handleModelError(this, ex)) {
2550                        throw ex;
2551                    }
2552                }
2553            }
2554            relation.validateSettables();
2555        }
2556    }
2557
2558    ///////////////////////////////////////////////////////////////////
2559    ////                         protected variables               ////
2560
2561    /** Level-crossing links within this composite for which this composite
2562     *  is responsible.  This data structure is populated when exportMoML()
2563     *  is called.
2564     */
2565    protected List<LinkRecord> _levelCrossingLinks;
2566
2567    ///////////////////////////////////////////////////////////////////
2568    ////                         private methods                   ////
2569
2570    /**
2571     * Add all elements from the sourceList into the targetList.
2572     */
2573    @SuppressWarnings("unchecked")
2574    private static <T> void _addAll(Set<T> result, Collection<?> sourceList) {
2575        for (Object object : sourceList) {
2576            result.add((T) object);
2577        }
2578    }
2579
2580    /** Add a default icon description. */
2581    private void _addIcon() {
2582        _attachText("_iconDescription", _defaultIcon);
2583    }
2584
2585    /** Find the least common container of the two objects.
2586     *  @param object1 The first object.
2587     *  @param object2 The second object.
2588     *  @return The least common container, or null if there
2589     *   isn't one.
2590     */
2591    private NamedObj _commonContainer(NamedObj object1, NamedObj object2) {
2592        NamedObj container = object1.getContainer();
2593        while (container != null) {
2594            if (container.deepContains(object2)) {
2595                return container;
2596            }
2597            container = container.getContainer();
2598        }
2599        return null;
2600    }
2601
2602    /** Return true if the two specified objects are both derived,
2603     *  it is the same container above them whose parent-child
2604     *  relationship makes them derived, or if one of the containers
2605     *  contains the other, and those containers are
2606     *  no more than <i>depth1</i> and <i>depth2</i> above them,
2607     *  respectively.
2608     *  @param object1 The first object.
2609     *  @param depth1 The depth of the first object.
2610     *  @param object2 The second object.
2611     *  @param depth2 The depth of the second object.
2612     *  @return True if the two specified objects are both derived
2613     *   at a common level no more than depth above them.
2614     */
2615    private boolean _commonImplier(NamedObj object1, int depth1,
2616            NamedObj object2, int depth2) {
2617        if (object1 == null || object2 == null) {
2618            return false;
2619        }
2620        int object1Level = object1.getDerivedLevel();
2621        int object2Level = object2.getDerivedLevel();
2622        if (object1Level <= depth1 && object2Level <= depth2) {
2623            NamedObj object1Container = object1;
2624            while (object1Level > 0) {
2625                object1Container = object1Container.getContainer();
2626                object1Level--;
2627                // It's not clear to me how this occur, but if relationCaontiner
2628                // is null, then clearly there is no common container that
2629                // implies the two objects.
2630                if (object1Container == null) {
2631                    return false;
2632                }
2633            }
2634            NamedObj object2Container = object2;
2635            while (object2Level > 0) {
2636                object2Container = object2Container.getContainer();
2637                object2Level--;
2638                // It's not clear to me how this occur, but if relationCaontiner
2639                // is null, then clearly there is no common container that
2640                // implies the two objects.
2641                if (object2Container == null) {
2642                    return false;
2643                }
2644            }
2645            if (object1Container == object2Container
2646                    || object1Container.deepContains(object2Container)
2647                    || object2Container.deepContains(object1Container)) {
2648                return true;
2649            }
2650        }
2651        return false;
2652    }
2653
2654    /** Return the depth of specified object inside this.
2655     *  That is, return 0 if the specified object is this, 1 if this
2656     *  contains it, 2 if this contains the container
2657     *  of it, etc.
2658     *  @param containee The object contained.
2659     *  @return The depth of the containment, or -1 if the specified object
2660     *   is not deeply contained by this.
2661     */
2662    private int _depthInside(NamedObj containee) {
2663        int result = 0;
2664        NamedObj candidate = containee;
2665        while (candidate != null) {
2666            if (candidate == this) {
2667                return result;
2668            }
2669            result++;
2670            candidate = candidate.getContainer();
2671        }
2672        return -1;
2673    }
2674
2675    /** Record a level-crossing link with the least common container if there
2676     *  is such a least common container and if that least common container is
2677     *  currently exporting MoML. Otherwise, do nothing.
2678     *  @param port The port, or null for a link between relations.
2679     *  @param relation1 The first relation.
2680     *  @param relation2 The second relation.
2681     *  @param index The index of the link.
2682     */
2683    private void _recordLevelCrossingLink(Port port, Relation relation1,
2684            Relation relation2, int index) {
2685        // Find the least common container.
2686        NamedObj container;
2687        if (port != null) {
2688            container = _commonContainer(port, relation1);
2689        } else {
2690            // This is a link between relations.
2691            // Find the common container.
2692            container = _commonContainer(relation1, relation2);
2693            // We have to make sure
2694            // that the link only appears once in the export MoML.
2695            // To do this, check to see whether the common container
2696            // already contains a link record with relation2 in the
2697            // position of relation1.
2698            if (container instanceof CompositeEntity) {
2699                List<LinkRecord> linkRecords = ((CompositeEntity) container)._levelCrossingLinks;
2700                if (linkRecords != null) {
2701                    for (LinkRecord record : linkRecords) {
2702                        if (record.relation1 == relation2) {
2703                            return;
2704                        }
2705                    }
2706                }
2707            }
2708        }
2709        if (container instanceof CompositeEntity) {
2710            List<LinkRecord> linkRecords = ((CompositeEntity) container)._levelCrossingLinks;
2711            // If the common container is outside the scope of what is being exported,
2712            // then linkRecords will be null.
2713            if (linkRecords != null) {
2714                LinkRecord record = new LinkRecord();
2715                record.port = port;
2716                record.relation1 = relation1;
2717                record.relation2 = relation2;
2718                record.index = index;
2719                linkRecords.add(record);
2720            }
2721        }
2722    }
2723
2724    /** Remove all level-crossing links from relations contained by
2725     *  the specified entity to ports or relations outside this
2726     *  composite entity, and from ports contained by entities
2727     *  contained by the specified entity to relations outside this
2728     *  composite entity.
2729     *  @param entity The entity in which to look for relations or
2730     *   (if it is an instance of CompositeEntity), entities with ports.
2731     */
2732    private void _unlinkLevelCrossingLinksToOutside(CompositeEntity entity) {
2733        // Look for relations with level crossing links first.
2734        // Here we use the lazy version so as to not trigger evaluation
2735        // of lazy contents. We assume that if and when those contents
2736        // are evaluated, if there is a level-crossing list, it will
2737        // trigger an error.
2738        Iterator relations = entity.lazyRelationList().iterator();
2739        while (relations.hasNext()) {
2740            ComponentRelation relation = (ComponentRelation) relations.next();
2741            Iterator linkedObjects = relation.linkedObjectsList().iterator();
2742            while (linkedObjects.hasNext()) {
2743                Object linkedObject = linkedObjects.next();
2744
2745                Nameable relationContainer = relation.getContainer();
2746                if (linkedObject instanceof Relation) {
2747
2748                    Relation linkedRelation = (Relation) linkedObject;
2749                    Nameable linkedObjectContainer = linkedRelation
2750                            .getContainer();
2751                    if (relationContainer != linkedObjectContainer
2752                            && linkedObjectContainer
2753                                    .getContainer() != relationContainer) {
2754                        relation.unlink(linkedRelation);
2755                    }
2756                } else {
2757                    // Must be a port.
2758                    Port linkedPort = (Port) linkedObject;
2759                    Nameable linkedObjectContainer = linkedPort.getContainer();
2760                    if (relationContainer != linkedObjectContainer
2761                            && linkedObjectContainer
2762                                    .getContainer() != relationContainer) {
2763                        linkedPort.unlink(relation);
2764                    }
2765                }
2766            }
2767        }
2768        // Next look for ports with level-crossing links.
2769        Iterator entities = entity.entityList().iterator();
2770        while (entities.hasNext()) {
2771            ComponentEntity containedEntity = (ComponentEntity) entities.next();
2772            // If the contained entity is a composite entity, then unlink
2773            // anything inside it as well.
2774            if (containedEntity instanceof CompositeEntity) {
2775                _unlinkLevelCrossingLinksToOutside(
2776                        (CompositeEntity) containedEntity);
2777            }
2778            // Now unlink its ports.
2779            Iterator ports = containedEntity.portList().iterator();
2780            while (ports.hasNext()) {
2781                ComponentPort port = (ComponentPort) ports.next();
2782                Iterator linkedRelations = port.linkedRelationList().iterator();
2783                while (linkedRelations.hasNext()) {
2784                    ComponentRelation relation = (ComponentRelation) linkedRelations
2785                            .next();
2786                    if (relation != null && !deepContains(relation)) {
2787                        port.unlink(relation);
2788                    }
2789                }
2790            }
2791        }
2792    }
2793
2794    ///////////////////////////////////////////////////////////////////
2795    ////                         friendly variables                 ////
2796    // The following are friendly to support the move* methods of
2797    // Relation and ComponentEntity.
2798
2799    /** List of contained entities. */
2800    NamedList _containedEntities = new NamedList(this);
2801
2802    /** @serial List of contained ports. */
2803    NamedList _containedRelations = new NamedList(this);
2804
2805    ///////////////////////////////////////////////////////////////////
2806    ////                         private variables                 ////
2807
2808    /** Cache of class definition list. */
2809    private transient List _classDefinitionListCache;
2810
2811    /** Workspace version for cache. */
2812    private transient long _classDefinitionListVersion = -1L;
2813
2814    /** The default value icon.  This is static so that we avoid doing
2815     *  string concatenation each time we construct this object.
2816     */
2817    private static String _defaultIcon = "<svg>\n"
2818            + "<rect x=\"-30\" y=\"-20\" width=\"60\" "
2819            + "height=\"40\" style=\"fill:red\"/>\n"
2820            + "<rect x=\"-28\" y=\"-18\" width=\"56\" "
2821            + "height=\"36\" style=\"fill:lightgrey\"/>\n"
2822            + "<rect x=\"-15\" y=\"-10\" width=\"10\" height=\"8\" "
2823            + "style=\"fill:white\"/>\n"
2824            + "<rect x=\"-15\" y=\"2\" width=\"10\" height=\"8\" "
2825            + "style=\"fill:white\"/>\n"
2826            + "<rect x=\"5\" y=\"-4\" width=\"10\" height=\"8\" "
2827            + "style=\"fill:white\"/>\n"
2828            + "<line x1=\"-5\" y1=\"-6\" x2=\"0\" y2=\"-6\"/>"
2829            + "<line x1=\"-5\" y1=\"6\" x2=\"0\" y2=\"6\"/>"
2830            + "<line x1=\"0\" y1=\"-6\" x2=\"0\" y2=\"6\"/>"
2831            + "<line x1=\"0\" y1=\"0\" x2=\"5\" y2=\"0\"/>" + "</svg>\n";
2832
2833    /** Cache of entity list. */
2834    private transient WeakReference<List> _entityListCache;
2835
2836    /** Workspace version for cache. */
2837    private transient long _entityListVersion = -1L;
2838
2839    /** @serial Flag indicating whether level-crossing connect is permitted. */
2840    private boolean _levelCrossingConnectAllowed = false;
2841
2842    ///////////////////////////////////////////////////////////////////
2843    ////                         inner classes                     ////
2844
2845    /** This class is an iterator over all the contained objects
2846     *  (all instances of NamedObj). In this class, the contained
2847     *  objects are attributes first, then ports, then entities,
2848     *  then relations.
2849     */
2850    protected class ContainedObjectsIterator
2851            extends Entity.ContainedObjectsIterator {
2852        /** Create an iterator over all the contained objects, which
2853         *  for CompositeEntities are attributes, ports, classes
2854         *  entities, and relations.
2855         */
2856        public ContainedObjectsIterator() {
2857            super();
2858            _classListIterator = classDefinitionList().iterator();
2859            _entityListIterator = entityList().iterator();
2860            _relationListIterator = relationList().iterator();
2861        }
2862
2863        /** Return true if the iteration has more elements.
2864         *  In this class, this returns true if there are more
2865         *  attributes, ports, classes, entities, or relations.
2866         *  @return True if there are more elements.
2867         */
2868        @Override
2869        public boolean hasNext() {
2870            if (super.hasNext()) {
2871                return true;
2872            }
2873            if (_classListIterator.hasNext()) {
2874                return true;
2875            }
2876            if (_entityListIterator.hasNext()) {
2877                return true;
2878            }
2879            return _relationListIterator.hasNext();
2880        }
2881
2882        /** Return the next element in the iteration.
2883         *  In this base class, this is the next attribute or port.
2884         *  @return The next attribute or port.
2885         */
2886        @Override
2887        public Object next() {
2888            if (super.hasNext()) {
2889                return super.next();
2890            }
2891
2892            if (_classListIterator.hasNext()) {
2893                return _classListIterator.next();
2894            }
2895
2896            if (_entityListIterator.hasNext()) {
2897                return _entityListIterator.next();
2898            }
2899
2900            return _relationListIterator.next();
2901        }
2902
2903        /** The remove() method is not supported because is is not
2904         *  supported in NamedObj.ContainedObjectsIterator.remove().
2905         */
2906        @Override
2907        public void remove() {
2908            super.remove();
2909        }
2910
2911        private Iterator _classListIterator = null;
2912
2913        private Iterator _entityListIterator = null;
2914
2915        private Iterator _relationListIterator = null;
2916    }
2917
2918    /** A comparator for a &lt;String&gt;&lt;Integer&gt; Map. */
2919    private static class CountComparator implements Comparator {
2920        @Override
2921        public int compare(Object object1, Object object2) {
2922            int result = 0;
2923            Map.Entry entry1 = (Map.Entry) object1;
2924            Map.Entry entry2 = (Map.Entry) object2;
2925
2926            Integer value1 = (Integer) entry1.getValue();
2927            Integer value2 = (Integer) entry2.getValue();
2928
2929            if (value1.compareTo(value2) == 0) {
2930                String className1 = (String) entry1.getKey();
2931                String className2 = (String) entry2.getKey();
2932                result = className1.compareTo(className2);
2933            } else {
2934                result = value2.compareTo(value1);
2935            }
2936
2937            return result;
2938        }
2939    }
2940
2941    /** A data structure for level-crossing links. */
2942    private static class LinkRecord {
2943        public Port port;
2944        public Relation relation1;
2945        public Relation relation2;
2946        public int index;
2947    }
2948}