001/* A ComponentEntity is a vertex 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.lang.ref.WeakReference;
031import java.util.Iterator;
032import java.util.List;
033
034import ptolemy.kernel.util.ChangeRequest;
035import ptolemy.kernel.util.IllegalActionException;
036import ptolemy.kernel.util.Instantiable;
037import ptolemy.kernel.util.InternalErrorException;
038import ptolemy.kernel.util.NameDuplicationException;
039import ptolemy.kernel.util.NamedObj;
040import ptolemy.kernel.util.Workspace;
041
042///////////////////////////////////////////////////////////////////
043//// ComponentEntity
044
045/**
046 A ComponentEntity is a component in a CompositeEntity.
047 It might itself be composite, but in this base class it is assumed to
048 be atomic (meaning that it cannot contain components).
049 <p>
050 Derived classes may further constrain the container to be
051 a subclass of CompositeEntity.  To do this, they should override
052 the protected method _checkContainer() to throw an exception.
053 <p>
054 A ComponentEntity can contain instances of ComponentPort.  Derived
055 classes may further constrain to a subclass of ComponentPort.
056 To do this, they should override the public method newPort() to create
057 a port of the appropriate subclass, and the protected method _addPort()
058 to throw an exception if its argument is a port that is not of the
059 appropriate subclass.
060
061 @author John S. Davis II, Edward A. Lee
062 @version $Id$
063 @since Ptolemy II 0.2
064 @Pt.ProposedRating Green (eal)
065 @Pt.AcceptedRating Green (cxh)
066 */
067public class ComponentEntity<T extends ComponentPort> extends Entity<T> {
068    /** Construct an entity in the default workspace with an empty string
069     *  The object is added to the workspace directory.
070     *  as its name. Increment the version number of the workspace.
071     */
072    public ComponentEntity() {
073        super();
074        _addIcon();
075    }
076
077    /** Construct an entity in the specified workspace with an empty
078     *  string as a name. You can then change the name with setName().
079     *  If the workspace argument is null, then use the default workspace.
080     *  The object is added to the workspace directory.
081     *  Increment the version number of the workspace.
082     *  @param workspace The workspace that will list the entity.
083     */
084    public ComponentEntity(Workspace workspace) {
085        super(workspace);
086        _addIcon();
087    }
088
089    /** Construct an entity with the given name contained by the specified
090     *  entity. The container argument must not be null, or a
091     *  NullPointerException will be thrown.  This entity will use the
092     *  workspace of the container for synchronization and version counts.
093     *  If the name argument is null, then the name is set to the empty string.
094     *  Increment the version of the workspace.
095     *  This constructor write-synchronizes on the workspace.
096     *  @param container The container entity.
097     *  @param name The name of the entity.
098     *  @exception IllegalActionException If the entity cannot be contained
099     *   by the proposed container.
100     *  @exception NameDuplicationException If the name coincides with
101     *   an entity already in the container.
102     */
103    public ComponentEntity(CompositeEntity container, String name)
104            throws IllegalActionException, NameDuplicationException {
105        super(container.workspace(), name);
106        setContainer(container);
107        _addIcon();
108    }
109
110    ///////////////////////////////////////////////////////////////////
111    ////                         public methods                    ////
112
113    /** Clone the object into the specified workspace. The new object is
114     *  <i>not</i> added to the directory of that workspace (you must do this
115     *  yourself if you want it there).
116     *  The result is a new component entity that defers its definition to the
117     *  same object as this one (or to none) that has no container.
118     *  @param workspace The workspace for the cloned object.
119     *  @exception CloneNotSupportedException If one of the attributes
120     *   cannot be cloned.
121     *  @return A new instance of ComponentEntity.
122     */
123    @Override
124    public Object clone(Workspace workspace) throws CloneNotSupportedException {
125        ComponentEntity newObject = (ComponentEntity) super.clone(workspace);
126        newObject._container = null;
127        return newObject;
128    }
129
130    /** Get the container entity.
131     *  @return The container, which is an instance of CompositeEntity.
132     *  @see #setContainer(CompositeEntity)
133     */
134    @Override
135    public NamedObj getContainer() {
136        return _container;
137    }
138
139    /** Create an instance by cloning this object and then adjusting
140     *  the parent-child relationships between the clone and its parent.
141     *  Specifically, the clone defers its definition to this object,
142     *  which becomes its "parent." It inherits all the objects contained
143     *  by this object. If this object is a composite, then this method
144     *  adjusts any deferral relationships that are entirely contained
145     *  within the clone. That is, for any parent-child relationship that
146     *  is entirely contained within this object (i.e., both the parent
147     *  and the child are deeply contained by this object), a corresponding
148     *  parent-child relationship is created within the clone such that
149     *  both the parent and the child are entirely contained within
150     *  the clone.
151     *  <p>
152     *  The new object is not a class definition (it is by default an
153     *  "instance" rather than a "class").  To make it a class
154     *  definition (a "subclass"), call setClassDefinition(true).
155     *  <p>
156     *  This method overrides the base class to use setContainer() to
157     *  specify the container.
158     *  @see #setClassDefinition(boolean)
159     *  @param container The container for the instance, or null
160     *   to instantiate it at the top level.
161     *  @param name The name for the clone.
162     *  @return A new instance that is a clone of this object
163     *   with adjusted deferral relationships.
164     *  @exception CloneNotSupportedException If this object
165     *   cannot be cloned.
166     *  @exception IllegalActionException If this object is not a
167     *   class definition
168     *   or the proposed container is not acceptable.
169     *  @exception NameDuplicationException If the name collides with
170     *   an object already in the container.
171     */
172    @Override
173    public Instantiable instantiate(NamedObj container, String name)
174            throws CloneNotSupportedException, IllegalActionException,
175            NameDuplicationException {
176        if (container != null && !(container instanceof CompositeEntity)) {
177            throw new IllegalActionException(this,
178                    "Cannot instantiate into a container that is not an "
179                            + "instance of CompositeEntity: "
180                            + container.getFullName());
181        }
182
183        ComponentEntity clone = (ComponentEntity) super.instantiate(container,
184                name);
185        // Derived classes like this one are responsible for setting the container.
186        clone.setContainer((CompositeEntity) container);
187
188        clone._adjustDeferrals();
189
190        // Now that there is a new parent-child relationship,
191        // we need to propagate values from the parent to the child.
192        // Note that this isn't needed to get the
193        // right values, since the child has been cloned
194        // from the parent. However, this will set the override
195        // levels appropriately in the child.
196        // NOTE: This could be done by calling propagateValues(),
197        // but that would be extremely inefficient since it would
198        // propagate to all previously instantiated instances as
199        // well as to the one new clone.
200        clone._adjustOverride(0);
201
202        return clone;
203    }
204
205    /** Return true if the entity is atomic.
206     *  An atomic entity is one that cannot have components.
207     *  Instances of this base class are always atomic.
208     *  Derived classes that return false are assumed to be instances of
209     *  CompositeEntity or a class derived from that.
210     *  @return True if the entity is atomic.
211     *  @see ptolemy.kernel.CompositeEntity
212     */
213    public boolean isAtomic() {
214        return true;
215    }
216
217    /** Return true if the entity is opaque.
218     *  An opaque entity is one that either is atomic or hides
219     *  its components behind opaque ports.
220     *  Instances of this base class are always opaque.
221     *  Derived classes may be transparent, in which case they return false
222     *  to this method and to isAtomic().
223     *  @return True if the entity is opaque.
224     *  @see ptolemy.kernel.CompositeEntity
225     */
226    public boolean isOpaque() {
227        return true;
228    }
229
230    /** Move this object down by one in the list of entities of
231     *  its container. If this object is already last, do nothing.
232     *  Increment the version of the workspace.
233     *  @return The index of the specified object prior to moving it,
234     *   or -1 if it is not moved.
235     *  @exception IllegalActionException If this object has
236     *   no container.
237     */
238    @Override
239    public int moveDown() throws IllegalActionException {
240        CompositeEntity container = (CompositeEntity) getContainer();
241
242        if (container == null) {
243            throw new IllegalActionException(this, "Has no container.");
244        }
245
246        try {
247            _workspace.getWriteAccess();
248
249            int result = container._containedEntities.moveDown(this);
250
251            // Propagate.
252            Iterator derivedObjects = getDerivedList().iterator();
253
254            while (derivedObjects.hasNext()) {
255                NamedObj derived = (NamedObj) derivedObjects.next();
256                container = (CompositeEntity) derived.getContainer();
257                container._containedEntities.moveDown(derived);
258            }
259
260            return result;
261        } finally {
262            _workspace.doneWriting();
263        }
264    }
265
266    /** Move this object to the first position in the list
267     *  of entities of the container. If this object is already first,
268     *  do nothing. Increment the version of the workspace.
269     *  @return The index of the specified object prior to moving it,
270     *   or -1 if it is not moved.
271     *  @exception IllegalActionException If this object has
272     *   no container.
273     */
274    @Override
275    public int moveToFirst() throws IllegalActionException {
276        CompositeEntity container = (CompositeEntity) getContainer();
277
278        if (container == null) {
279            throw new IllegalActionException(this, "Has no container.");
280        }
281
282        try {
283            _workspace.getWriteAccess();
284
285            int result = container._containedEntities.moveToFirst(this);
286
287            // Propagate.
288            Iterator derivedObjects = getDerivedList().iterator();
289
290            while (derivedObjects.hasNext()) {
291                NamedObj derived = (NamedObj) derivedObjects.next();
292                container = (CompositeEntity) derived.getContainer();
293                container._containedEntities.moveToFirst(derived);
294            }
295
296            return result;
297        } finally {
298            _workspace.doneWriting();
299        }
300    }
301
302    /** Move this object to the specified position in the list
303     *  of entities of the container. If this object is already at
304     *  the specified position, do nothing. Increment the version
305     *  of the workspace.
306     *  @param index The position to move this object to.
307     *  @return The index of the specified object prior to moving it,
308     *   or -1 if it is not moved.
309     *  @exception IllegalActionException If this object has
310     *   no container or if the index is out of bounds.
311     */
312    @Override
313    public int moveToIndex(int index) throws IllegalActionException {
314        CompositeEntity container = (CompositeEntity) getContainer();
315
316        if (container == null) {
317            throw new IllegalActionException(this, "Has no container.");
318        }
319
320        try {
321            _workspace.getWriteAccess();
322
323            int result = container._containedEntities.moveToIndex(this, index);
324
325            // Propagate.
326            Iterator derivedObjects = getDerivedList().iterator();
327
328            while (derivedObjects.hasNext()) {
329                NamedObj derived = (NamedObj) derivedObjects.next();
330                container = (CompositeEntity) derived.getContainer();
331                container._containedEntities.moveToIndex(derived, index);
332            }
333
334            return result;
335        } finally {
336            _workspace.doneWriting();
337        }
338    }
339
340    /** Move this object to the last position in the list
341     *  of entities of the container.  If this object is already last,
342     *  do nothing.
343     *  Increment the version of the workspace.
344     *  @return The index of the specified object prior to moving it,
345     *   or -1 if it is not moved.
346     *  @exception IllegalActionException If this object has
347     *   no container.
348     */
349    @Override
350    public int moveToLast() throws IllegalActionException {
351        CompositeEntity container = (CompositeEntity) getContainer();
352
353        if (container == null) {
354            throw new IllegalActionException(this, "Has no container.");
355        }
356
357        try {
358            _workspace.getWriteAccess();
359
360            int result = container._containedEntities.moveToLast(this);
361
362            // Propagate.
363            Iterator derivedObjects = getDerivedList().iterator();
364
365            while (derivedObjects.hasNext()) {
366                NamedObj derived = (NamedObj) derivedObjects.next();
367                container = (CompositeEntity) derived.getContainer();
368                container._containedEntities.moveToLast(derived);
369            }
370
371            return result;
372        } finally {
373            _workspace.doneWriting();
374        }
375    }
376
377    /** Move this object up by one in the list of
378     *  entities of the container. If this object is already first, do
379     *  nothing. Increment the version of the workspace.
380     *  @return The index of the specified object prior to moving it,
381     *   or -1 if it is not moved.
382     *  @exception IllegalActionException If this object has
383     *   no container.
384     */
385    @Override
386    public int moveUp() throws IllegalActionException {
387        CompositeEntity container = (CompositeEntity) getContainer();
388
389        if (container == null) {
390            throw new IllegalActionException(this, "Has no container.");
391        }
392
393        try {
394            _workspace.getWriteAccess();
395
396            int result = container._containedEntities.moveUp(this);
397
398            // Propagate.
399            Iterator derivedObjects = getDerivedList().iterator();
400
401            while (derivedObjects.hasNext()) {
402                NamedObj derived = (NamedObj) derivedObjects.next();
403                container = (CompositeEntity) derived.getContainer();
404                container._containedEntities.moveUp(derived);
405            }
406
407            return result;
408        } finally {
409            _workspace.doneWriting();
410        }
411    }
412
413    /** Create a new port with the specified name.
414     *  The container of the port is set to this entity.
415     *  This overrides the base class to create an instance of ComponentPort.
416     *  Derived classes may override this to further constrain the ports.
417     *  This method is write-synchronized on the workspace and increments
418     *  its version number.
419     *  @param name The new port name.
420     *  @return The new port
421     *  @exception IllegalActionException If the argument is null.
422     *  @exception NameDuplicationException If this entity already has a
423     *   port with the specified name.
424     */
425    @Override
426    public Port newPort(String name)
427            throws IllegalActionException, NameDuplicationException {
428        try {
429            _workspace.getWriteAccess();
430
431            Port port = new ComponentPort(this, name);
432            return port;
433        } finally {
434            _workspace.doneWriting();
435        }
436    }
437
438    /** Propagate the existence of this object.
439     *  If this object has a container, then ensure that all
440     *  objects derived from the container contain an object
441     *  with the same class and name as this one. Create that
442     *  object when needed. Return the list of objects that are created.
443     *  This overrides the base class to adjust deferrals within
444     *  the objects that are created by cloning this one.
445     *  @return A list of derived objects of the same class
446     *   as this object that are created.
447     *  @exception IllegalActionException If the object does
448     *   not exists and cannot be created.
449     */
450    @Override
451    public List propagateExistence() throws IllegalActionException {
452        // Otherwise, _override probably doesn't get set in the
453        // derived object that is created.
454        List result = super.propagateExistence();
455
456        // Adjust deferrals in all the newly created objects.
457        Iterator clones = result.iterator();
458
459        while (clones.hasNext()) {
460            ComponentEntity clone = (ComponentEntity) clones.next();
461            clone._adjustDeferrals();
462        }
463
464        return result;
465    }
466
467    /** Specify the container, adding the entity to the list
468     *  of entities in the container.  If the container already contains
469     *  an entity with the same name, then throw an exception and do not make
470     *  any changes.  Similarly, if the container is not in the same
471     *  workspace as this entity, throw an exception.  If this entity is
472     *  a class element and the proposed container does not match
473     *  the current container, then also throw an exception.
474     *  If the entity is already contained by the container, do nothing.
475     *  If this entity already has a container, remove it
476     *  from that container first.  Otherwise, remove it from
477     *  the directory of the workspace, if it is present.
478     *  If the argument is null, then unlink the ports of the entity
479     *  from any relations and remove it from its container.
480     *  It is not added to the workspace directory, so this could result in
481     *  this entity being garbage collected.
482     *  <p>Note that for entities that are created by parsing MoML,
483     *  it may be necessary to call MoMLParser.purgeModelRecord().
484     *  This is especially critical when the MoML file imports
485     *  MoML classes.  See Effigy.setContainer() for details.
486     *
487     *  <p>Derived classes may further constrain the container
488     *  to subclasses of CompositeEntity by overriding the protected
489     *  method _checkContainer(). This method validates all
490     *  deeply contained instances of Settable, since they may no longer
491     *  be valid in the new context.  This method is write-synchronized
492     *  to the workspace and increments its version number.
493     *  @param container The proposed container.
494     *  @exception IllegalActionException If the action would result in a
495     *   recursive containment structure, or if
496     *   this entity and container are not in the same workspace, or
497     *   if the protected method _checkContainer() throws it, or if
498     *   a contained Settable becomes invalid and the error handler
499     *   throws it.
500     *  @exception NameDuplicationException If the name of this entity
501     *   collides with a name already in the container.
502     *  @see #getContainer()
503     */
504    public void setContainer(CompositeEntity container)
505            throws IllegalActionException, NameDuplicationException {
506        if (container != null && _workspace != container.workspace()) {
507            throw new IllegalActionException(this, container,
508                    "Cannot set container because workspaces are different.");
509        }
510
511        CompositeEntity previousContainer = (CompositeEntity) getContainer();
512
513        if (previousContainer == container) {
514            return;
515        }
516
517        _notifyHierarchyListenersBeforeChange();
518
519        try {
520            _workspace.getWriteAccess();
521            _checkContainer(container);
522
523            // NOTE: The following code is quite tricky.  It is very careful
524            // to leave a consistent state even in the face of unexpected
525            // exceptions.  Be very careful if modifying it.
526
527            // Do this first, because it may throw an exception, and we have
528            // not yet changed any state.
529            if (container != null) {
530                // checkContainer() above ensures that this cast is valid.
531                container._addEntity(this);
532
533                if (previousContainer == null) {
534                    _workspace.remove(this);
535                }
536            }
537
538            _container = container;
539
540            if (previousContainer != null) {
541                // This is safe now because it does not throw an exception.
542                previousContainer._removeEntity(this);
543            }
544
545            if (container == null) {
546                Iterator ports = portList().iterator();
547
548                while (ports.hasNext()) {
549                    Port port = (Port) ports.next();
550                    port.unlinkAll();
551                }
552
553                // Since the new container is null, this object is being
554                // deleted. Break deferral references that it may have.
555                _setParent(null);
556            } else {
557                // checkContainer() above ensures that this cast is valid.
558                container._finishedAddEntity(this);
559
560                // Transfer any queued change requests to the
561                // new container.  There could be queued change
562                // requests if this component is deferring change
563                // requests.
564                if (_changeRequests != null) {
565                    Iterator requests = _changeRequests.iterator();
566
567                    while (requests.hasNext()) {
568                        ChangeRequest request = (ChangeRequest) requests.next();
569                        container.requestChange(request);
570                    }
571
572                    _changeRequests = null;
573                }
574            }
575
576            // Validate all deeply contained settables, since
577            // they may no longer be valid in the new context.
578            // Optimization: During construction, the previous
579            // container will be null. It doesn't make sense
580            // to validate at this point, since there shouldn't
581            // actually be any contained settables. Moreover,
582            // if the container is being set to null, then the
583            // component is being discarded. It shouldn't be
584            // necessary to validate settables at this point
585            // since all dependents should be within this
586            // scope. EAL 9/6/06
587            if (previousContainer != null && container != null) {
588                // NOTE: This somehow prevents CompositeEntity.validateSettables
589                // from ever being called when a model is loaded.
590                validateSettables();
591            }
592        } finally {
593            try {
594                // Since we definitely notified the listeners
595                // before the change, we must definitely notify
596                // them after the change, even if the change caused
597                // some exceptions. Note that this too may trigger
598                // exceptions.
599                _notifyHierarchyListenersAfterChange();
600            } finally {
601                _workspace.doneWriting();
602            }
603        }
604    }
605
606    /** Set the name of the ComponentEntity. If there is already
607     *  a ComponentEntity of the container with the same name, throw an
608     *  exception.
609     *  @exception IllegalActionException If the name has a period.
610     *  @exception NameDuplicationException If there already is an entity
611     *   in the container with the same name.
612     */
613    @Override
614    public void setName(String name)
615            throws IllegalActionException, NameDuplicationException {
616        if (name == null) {
617            name = "";
618        }
619
620        CompositeEntity container = (CompositeEntity) getContainer();
621
622        if (container != null) {
623            ComponentEntity another = container.getEntity(name);
624
625            if (another != null && another != this) {
626                throw new NameDuplicationException(container,
627                        "Name duplication: " + name);
628            }
629        }
630
631        super.setName(name);
632    }
633
634    ///////////////////////////////////////////////////////////////////
635    ////                         protected methods                 ////
636
637    /** Adjust the deferrals in this object. This method should
638     *  be called on any newly created object that is created by
639     *  cloning. While cloning, parent relations are set to null.
640     *  That is, no object in the clone has a parent. This method
641     *  identifies the correct parent for any object in the clone.
642     *  To do this, it uses the class name. Specifically, if this
643     *  object has a class name that refers to a class in scope,
644     *  then it replaces the current parent with that object.
645     *  To look for a class in scope, we go up the hierarchy, but
646     *  no more times than the return value of getDerivedLevel().
647     *  The reason for this is that if the class from which this
648     *  object is defined is above that level, then we do not want
649     *  to establish a parent relationship with that class. This
650     *  object is implied, and the parent relationship of the object
651     *  from which it is implied is sufficient.
652     *  <p>
653     *  Derived classes that contain other objects should recursively
654     *  call this method on contained objects.
655     *  @exception IllegalActionException If the class found in scope
656     *   cannot be set.
657     */
658    protected void _adjustDeferrals() throws IllegalActionException {
659        // Use the class name.
660        String className = getClassName();
661
662        // Search upwards in the hierarchy.
663        NamedObj context = this;
664        int levelsToSearch = getDerivedLevel();
665        int aboveLevel = 0;
666        ComponentEntity candidate = null;
667
668        // Make sure we get a real candidate, which is a
669        // class definition. The second term in the if will
670        // cause the search to continue up the hierarchy.
671        // NOTE: There is still an oddness, in that
672        // the class scoping results in a subtle (and
673        // maybe incomprehensible) identification of
674        // the base class, particularly when pasting
675        // an instance or subclass into a new context.
676        while (aboveLevel < levelsToSearch
677                && (candidate == null || !candidate.isClassDefinition())
678                && context != null) {
679            context = context.getContainer();
680
681            if (context instanceof CompositeEntity) {
682                candidate = ((CompositeEntity) context).getEntity(className);
683            }
684
685            aboveLevel += 1;
686        }
687
688        if (candidate != null) {
689            _setParent(candidate);
690            _markContentsDerived(0);
691
692            // For every object contained by the new parent,
693            // we need to make sure its value is propagated
694            // to this new child and that the override field
695            // gets set to reflect that.
696            candidate.propagateValues();
697        }
698    }
699
700    /** Check the specified container.
701     *  @param container The proposed container.
702     *  @exception IllegalActionException If the container is not an
703     *   instance of CompositeEntity, or if the proposed container is
704     *   null and there are other objects that defer their definitions
705     *   to this one.
706     */
707    protected void _checkContainer(InstantiableNamedObj container)
708            throws IllegalActionException {
709        if (container != null && !(container instanceof CompositeEntity)) {
710            throw new IllegalActionException(this, container,
711                    "Component entity can only be contained by "
712                            + "a CompositeEntity");
713        }
714
715        // NOTE: If we don't enforce this, then undo could fail,
716        // since deletions occur in the opposite order of the re-additions
717        // in undo.  So being silent about this error will not do.
718        if (container == null) {
719            // If the class has objects that defer to it, then
720            // refuse to delete.
721            boolean hasDeferrals = false;
722            List deferred = getChildren();
723            StringBuffer names = new StringBuffer();
724
725            if (deferred != null) {
726                // List contains weak references, so it's not
727                // sufficient to just check the length.
728                Iterator deferrers = deferred.iterator();
729
730                while (deferrers.hasNext()) {
731                    WeakReference deferrer = (WeakReference) deferrers.next();
732                    NamedObj deferrerObject = (NamedObj) deferrer.get();
733
734                    if (deferrerObject != null) {
735                        hasDeferrals = true;
736
737                        if (names.length() > 0) {
738                            names.append(", ");
739                        }
740
741                        names.append(deferrerObject.getFullName());
742                    }
743                }
744            }
745
746            if (hasDeferrals) {
747                throw new IllegalActionException(this,
748                        "Cannot delete because "
749                                + "there are instances and/or subclasses:\n"
750                                + names.toString());
751            }
752        }
753    }
754
755    /** Get an entity with the specified name in the specified container.
756     *  The returned object is assured of being an
757     *  instance of the same class as this object.
758     *  @param relativeName The name relative to the container.
759     *  @param container The container expected to contain the object, which
760     *   must be an instance of CompositeEntity.
761     *  @return An object of the same class as this object, or null if there
762     *   is none.
763     *  @exception IllegalActionException If the object exists
764     *   and has the wrong class, or if the specified container is not
765     *   an instance of CompositeEntity.
766     */
767    @Override
768    protected NamedObj _getContainedObject(NamedObj container,
769            String relativeName) throws IllegalActionException {
770        if (!(container instanceof CompositeEntity)) {
771            throw new IllegalActionException(this, "Expected "
772                    + container.getFullName()
773                    + " to be an instance of ptolemy.kernel.CompositeEntity,"
774                    + " but it is " + container.getClass().getName());
775        }
776
777        ComponentEntity candidate = ((CompositeEntity) container)
778                .getEntity(relativeName);
779
780        if (candidate != null && !getClass().isInstance(candidate)) {
781            throw new IllegalActionException(this,
782                    "Expected " + candidate.getFullName()
783                            + " to be an instance of " + getClass().getName()
784                            + ", but it is " + candidate.getClass().getName());
785        }
786
787        return candidate;
788    }
789
790    /** Propagate existence of this object to the
791     *  specified object. This overrides the base class
792     *  to set the container.
793     *  @param container Object to contain the new object.
794     *  @exception IllegalActionException If the object
795     *   cannot be cloned.
796     *  @return A new object of the same class and name
797     *   as this one.
798     */
799    @Override
800    protected NamedObj _propagateExistence(NamedObj container)
801            throws IllegalActionException {
802        try {
803            ComponentEntity newObject = (ComponentEntity) super._propagateExistence(
804                    container);
805            // FindBugs warns that the cast of container is
806            // unchecked.
807            if (!(container instanceof CompositeEntity)) {
808                throw new InternalErrorException(
809                        container + " is not a CompositeEntity.");
810            } else {
811                newObject.setContainer((CompositeEntity) container);
812            }
813            return newObject;
814        } catch (NameDuplicationException e) {
815            throw new InternalErrorException(e);
816        }
817    }
818
819    ///////////////////////////////////////////////////////////////////
820    ////                         private methods                   ////
821
822    /** Define the icon. */
823    private void _addIcon() {
824        _attachText("_iconDescription", _defaultIcon);
825    }
826
827    ///////////////////////////////////////////////////////////////////
828    ////                         private variables                 ////
829
830    /** The container. */
831    private CompositeEntity _container;
832
833    /** The default value icon.  This is static so that we avoid doing
834     *  string concatenation each time we construct this object.
835     */
836    private static String _defaultIcon = "<svg>\n"
837            + "<rect x=\"-30\" y=\"-20\" width=\"60\" "
838            + "height=\"40\" style=\"fill:white\"/>\n"
839            + "<polygon points=\"-20,-10 20,0 -20,10\" "
840            + "style=\"fill:blue\"/>\n" + "</svg>\n";
841}