001/* Base class for objects with a name and a container.
002
003 Copyright (c) 1997-2018 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.util;
029
030import java.io.IOException;
031import java.io.Serializable;
032import java.io.StringWriter;
033import java.io.Writer;
034import java.lang.ref.WeakReference;
035import java.lang.reflect.Field;
036import java.lang.reflect.Modifier;
037import java.util.ArrayList;
038import java.util.Collection;
039import java.util.Collections;
040import java.util.Enumeration;
041import java.util.HashMap;
042import java.util.HashSet;
043import java.util.Iterator;
044import java.util.LinkedList;
045import java.util.List;
046import java.util.ListIterator;
047import java.util.Map;
048import java.util.Set;
049
050import ptolemy.kernel.CompositeEntity;
051import ptolemy.util.StringUtilities;
052
053///////////////////////////////////////////////////////////////////
054//// NamedObj
055
056/**
057 This is a base class for almost all Ptolemy II objects.
058 <p>
059 This class supports a naming scheme, change requests, a persistent
060 file format (MoML), a mutual exclusion mechanism for models (the
061 workspace), an error handler, and a hierarchical class mechanism
062 with inheritance.
063 <p>
064 An instance of this class can also be parameterized by attaching
065 instances of the Attribute class.
066 Instances of Attribute can be attached by calling their setContainer()
067 method and passing this object as an argument. Those instances will
068 then be reported by the {@link #getAttribute(String)},
069 {@link #getAttribute(String, Class)}, {@link #attributeList()}
070 and {@link #attributeList(Class)} methods.
071 Classes derived from NamedObj may constrain attributes to be a
072 subclass of Attribute.  To do that, they should override the protected
073 _addAttribute(Attribute) method to throw an exception if
074 the object provided is not of the right class.
075 <p>
076 An instance of this class has a name.
077 A name is an arbitrary string with no periods.  If no
078 name is provided, the name is taken to be an empty string (not a null
079 reference). An instance also has a full name, which is a concatenation
080 of the container's full name and the simple name, separated by a
081 period. If there is no container, then the full name begins with a
082 period. The full name is used for error reporting throughout Ptolemy
083 II.
084 <p>
085 Instances of this class are associated with a workspace, specified as
086 a constructor argument.  The reference to the workspace is immutable.
087 It cannot be changed during the lifetime of this object.  It is used for
088 synchronization of methods that depend on or modify the state of
089 objects within it. If no workspace is specified, then the default
090 workspace is used.  Note that the workspace should not be confused
091 with the container.  The workspace never serves as a container.
092 <p>
093 In this base class, the container is null by default, and no
094 method is provided to change it. Derived classes that support
095 hierarchy provide one or more methods that set the container.
096 By convention, if the container is set,
097 then the instance should be removed from the workspace directory, if
098 it is present.  The workspace directory is expected to list only
099 top-level objects in a hierarchy.  The NamedObj can still use the
100 workspace for synchronization.  Any object contained by another uses
101 the workspace of its container as its own workspace by default.
102 <p>
103 This class supports <i>change requests</i> or <i>mutations</i>,
104 which are changes to a model that are performed in a disciplined
105 fashion.  In particular, a mutation can be requested via the
106 {@link #requestChange(ChangeRequest)} method. By default, when
107 a change is requested, the change is executed immediately.
108 However, by calling {@link #setDeferringChangeRequests(boolean)},
109 you can ensure that change requests are queued to be executed
110 only when it is safe to execute them.
111 <p>
112 This class supports the notion of a <i>model error</i>, which is
113 an exception that is handled by a registered model error handler, or
114 passed up the container hierarchy if there is no registered model
115 error handler.  This mechanism complements the exception mechanism in
116 Java. Instead of unraveling the calling stack to handle exceptions,
117 this mechanism passes control up the Ptolemy II hierarchy.
118 <p>
119 Derived classes should override the _description() method to
120 append new fields if there is new information that should be included
121 in the description.
122 <p>
123 A NamedObj can contain instances of {@link DecoratorAttributes}. These are attributes that are
124 added by another NamedObj that implements the {@link Decorator} interface.
125 These attributes are stored separately and can be retrieved by using
126 {@link #getDecoratorAttributes(Decorator)} or
127 {@link #getDecoratorAttribute(Decorator, String)}.
128
129 @author Mudit Goel, Edward A. Lee, Neil Smyth, Contributor: Bert Rodiers
130 @version $Id$
131 @since Ptolemy II 0.2
132 @Pt.ProposedRating Green (eal)
133 @Pt.AcceptedRating Green (cxh)
134
135 @see Attribute
136 @see Workspace
137 */
138public class NamedObj implements Changeable, Cloneable, Debuggable,
139        DebugListener, Derivable, MoMLExportable, ModelErrorHandler, Moveable {
140    // This class used to implement Serializable, but the implementation was never
141    // complete and thus cause many warnings.
142
143    // Note that Nameable extends ModelErrorHandler, so this class
144    // need not declare that it directly implements ModelErrorHandler.
145
146    /** Construct an object in the default workspace with an empty string
147     *  as its name. The object is added to the list of objects in
148     *  the workspace. Increment the version number of the workspace.
149     */
150    public NamedObj() {
151        this((Workspace) null);
152    }
153
154    /** Construct an object in the default workspace with the given name.
155     *  If the name argument is null, then the name is set to the empty
156     *  string. The object is added to the list of objects in the workspace.
157     *  Increment the version number of the workspace.
158     *  @param name Name of this object.
159     *  @exception IllegalActionException If the name has a period.
160     */
161    public NamedObj(String name) throws IllegalActionException {
162        this(_DEFAULT_WORKSPACE, name);
163    }
164
165    /** Construct an object in the specified workspace with an empty string
166     *  as its name. The object is added to the list of objects in
167     *  the workspace. Increment the version number of the workspace.
168     *  @param workspace Object for synchronization and version tracking
169     */
170    public NamedObj(Workspace workspace) {
171        // NOTE: Can't call the constructor below, which has essentially
172        // the same code, without also spuriously throwing
173        // IllegalActionException.
174        if (workspace == null) {
175            workspace = _DEFAULT_WORKSPACE;
176        }
177
178        _workspace = workspace;
179
180        // Exception cannot occur, so we ignore. The object does not
181        // have a container, and is not already on the workspace list.
182        // NOTE: This does not need to be write-synchronized on the workspace
183        // because the only side effect is adding to the directory,
184        // and methods for adding and reading from the directory are
185        // synchronized.
186        try {
187            workspace.add(this);
188        } catch (IllegalActionException ex) {
189            // This exception should not be thrown.
190            throw new InternalErrorException(null, ex,
191                    "Internal error in NamedObj constructor!");
192        }
193
194        try {
195            setName("");
196        } catch (KernelException ex) {
197            // This exception should not be thrown.
198            throw new InternalErrorException(null, ex,
199                    "Internal error in NamedObj constructor!");
200        }
201    }
202
203    /** Construct an object in the given workspace with the given name.
204     *  If the workspace argument is null, use the default workspace.
205     *  The object is added to the list of objects in the workspace.
206     *  If the name argument is null, then the name is set to the
207     *  empty string. Increment the version number of the workspace.
208     *  @param workspace Object for synchronization and version tracking
209     *  @param name Name of this object.
210     *  @exception IllegalActionException If the name has a period.
211     */
212    public NamedObj(Workspace workspace, String name)
213            throws IllegalActionException {
214        this(workspace, name, true);
215    }
216
217    /** Construct an object in the given workspace with the given name.
218     *  If the workspace argument is null, use the default workspace.
219     *  The object is added to the list of objects in the workspace.
220     *  If the name argument is null, then the name is set to the
221     *  empty string. Increment the version number of the workspace.
222     *  @param workspace Object for synchronization and version tracking
223     *  @param name Name of this object.
224     *  @param incrementWorkspaceVersion False to not add this to the workspace
225     *   or do anything else that might change the workspace version number.
226     *  @exception IllegalActionException If the name has a period.
227     */
228    protected NamedObj(Workspace workspace, String name,
229            boolean incrementWorkspaceVersion) throws IllegalActionException {
230        if (workspace == null) {
231            workspace = _DEFAULT_WORKSPACE;
232        }
233
234        _workspace = workspace;
235
236        // Exception cannot occur, so we ignore. The object does not
237        // have a container, and is not already on the workspace list.
238        // NOTE: This does not need to be write-synchronized on the workspace
239        // because the only side effect is adding to the directory,
240        // and methods for adding and reading from the directory are
241        // synchronized.
242        if (incrementWorkspaceVersion) {
243            try {
244                workspace.add(this);
245                setName(name);
246            } catch (NameDuplicationException ex) {
247                // This exception should not be thrown.
248                throw new InternalErrorException(null, ex,
249                        "Internal error in NamedObj constructor!");
250            }
251        } else {
252            _name = name;
253        }
254    }
255
256    ///////////////////////////////////////////////////////////////////
257    ////                         public methods                    ////
258
259    /** Add a change listener.  If there is a container, then
260     *  delegate to the container.  Otherwise, add the listener
261     *  to the list of change listeners in this object. Each listener
262     *  will be notified of the execution (or failure) of each
263     *  change request that is executed via the requestChange() method.
264     *  Note that in this implementation, only the top level of a
265     *  hierarchy executes changes, which is why this method delegates
266     *  to the container if there is one.
267     *  <p>
268     *  If the listener is already in the list, remove the previous
269     *  instance and add it again in the first position.
270     *  This listener is also notified before
271     *  other listeners that have been previously registered with the
272     *  top-level object.
273     *  @param listener The listener to add.
274     *  @see #removeChangeListener(ChangeListener)
275     *  @see #requestChange(ChangeRequest)
276     *  @see Changeable
277     */
278    @Override
279    public void addChangeListener(ChangeListener listener) {
280        NamedObj container = getContainer();
281
282        if (container != null) {
283            container.addChangeListener(listener);
284        } else {
285            synchronized (_changeLock) {
286                if (_changeListeners == null) {
287                    _changeListeners = new LinkedList<WeakReference<ChangeListener>>();
288                } else {
289                    // In case there is a previous instance, remove it.
290                    removeChangeListener(listener);
291                }
292
293                _changeListeners.add(0, new WeakReference(listener));
294            }
295        }
296    }
297
298    /** Append a listener to the current set of debug listeners.
299     *  If the listener is already in the set, do not add it again.
300     *  @param listener The listener to which to send debug messages.
301     *  @see #removeDebugListener(DebugListener)
302     */
303    @Override
304    public void addDebugListener(DebugListener listener) {
305        // NOTE: This method needs to be synchronized to prevent two
306        // threads from each creating a new _debugListeners list.
307        synchronized (this) {
308            if (_debugListeners == null) {
309                _debugListeners = new LinkedList();
310            }
311        }
312
313        // NOTE: This has to be synchronized to prevent
314        // concurrent modification exceptions.
315        synchronized (_debugListeners) {
316            if (_debugListeners.contains(listener)) {
317                return;
318            } else {
319                _debugListeners.add(listener);
320            }
321
322            _debugging = true;
323        }
324    }
325
326    /** Add a hierarchy listener. If the listener is already
327     *  added, do nothing. This will cause the object to also
328     *  be added as a hierarchy listener in the container of
329     *  this object, if there is one, and in its container,
330     *  up to the top of the hierarchy.
331     *  @param listener The listener to add.
332     *  @see #removeHierarchyListener(HierarchyListener)
333     */
334    public void addHierarchyListener(HierarchyListener listener) {
335        if (_hierarchyListeners == null) {
336            _hierarchyListeners = new HashSet<WeakReference<HierarchyListener>>();
337        }
338        _hierarchyListeners.add(new WeakReference(listener));
339
340        // Add to the container.
341        NamedObj container = getContainer();
342        if (container != null) {
343            container.addHierarchyListener(listener);
344        }
345    }
346
347    /** React to a change in an attribute.  This method is called by
348     *  a contained attribute when its value changes.  In this base class,
349     *  the method does nothing.  In derived classes, this method may
350     *  throw an exception, indicating that the new attribute value
351     *  is invalid.  It is up to the caller to restore the attribute
352     *  to a valid value if an exception is thrown.
353     *  @param attribute The attribute that changed.
354     *  @exception IllegalActionException If the change is not acceptable
355     *   to this container (not thrown in this base class).
356     */
357    public void attributeChanged(Attribute attribute)
358            throws IllegalActionException {
359    }
360
361    /** React to the deletion of an attribute. This method is called
362     *  by a contained attributed when it is deleted. In this base class,
363     *  the method does nothing. In derived classes, this method may deal
364     *  with consequences of deletion, for instance, update local variables.
365     *  @param attribute The attribute that was deleted.
366     *  @exception IllegalActionException If the deletion is not acceptable
367     *    to this container (not thrown in this base class).
368     */
369    public void attributeDeleted(Attribute attribute)
370            throws IllegalActionException {
371    }
372
373    /** Return a list of the attributes contained by this object.
374     *  If there are no attributes, return an empty list.
375     *  This method is read-synchronized on the workspace.
376     *  @return An unmodifiable list of instances of Attribute.
377     */
378    public List attributeList() {
379        try {
380            _workspace.getReadAccess();
381
382            if (_attributes == null) {
383                _attributes = new NamedList();
384            }
385
386            return _attributes.elementList();
387        } finally {
388            _workspace.doneReading();
389        }
390    }
391
392    /** Return a list of the attributes contained by this object that
393     *  are instances of the specified class.  If there are no such
394     *  instances, then return an empty list.
395     *  This method is read-synchronized on the workspace.
396     *  @param filter The class of attribute of interest.
397     *  @param <T> The type of that class.
398     *  @return A list of instances of specified class.
399     */
400    public <T> List<T> attributeList(Class<T> filter) {
401        try {
402            _workspace.getReadAccess();
403
404            if (_attributes == null) {
405                _attributes = new NamedList();
406            }
407
408            List<T> result = new LinkedList<T>();
409            Iterator<?> attributes = _attributes.elementList().iterator();
410
411            while (attributes.hasNext()) {
412                Object attribute = attributes.next();
413
414                if (filter.isInstance(attribute)) {
415                    @SuppressWarnings("unchecked")
416                    T tAttribute = (T) attribute;
417                    result.add(tAttribute);
418                }
419            }
420
421            return result;
422        } finally {
423            _workspace.doneReading();
424        }
425    }
426
427    /** React to a change in the type of an attribute.  This method is
428     *  called by a contained attribute when its type changes.
429     *  In this base class, the method does nothing.
430     *  @param attribute The attribute whose type changed.
431     *  @exception IllegalActionException If the change is not acceptable
432     *   to this container (not thrown in this base class).
433     */
434    public void attributeTypeChanged(Attribute attribute)
435            throws IllegalActionException {
436    }
437
438    /** Clone the object into the current workspace by calling the clone()
439     *  method that takes a Workspace argument.
440     *  This method read-synchronizes on the workspace.
441     *  @return A new NamedObj.
442     *  @exception CloneNotSupportedException If any of the attributes
443     *   cannot be cloned.
444     */
445    @Override
446    public Object clone() throws CloneNotSupportedException {
447        // FindBugs warns that "clone method does not call super.clone()",
448        // but that can be ignored because clone(Workspace) calls
449        // super.clone().
450        return clone(_workspace);
451    }
452
453    /** Clone the object into the specified workspace. The new object is
454     *  <i>not</i> added to the directory of that workspace (you must do this
455     *  yourself if you want it there). This uses the clone() method of
456     *  java.lang.Object, which makes a field-by-field copy.
457     *  It then adjusts the workspace reference and clones the
458     *  attributes on the attribute list, if there is one.  The attributes
459     *  are set to the attributes of the new object.
460     *  The new object will be set to defer change requests, so change
461     *  requests can be safely issued during cloning. However, it is
462     *  up to the caller of this clone() method to then execute the
463     *  the change requests, or to call setDeferringChangeRequests(false).
464     *  This method read-synchronizes on the workspace.
465     *  @param workspace The workspace for the new object.
466     *  @return A new NamedObj.
467     *  @exception CloneNotSupportedException If any of the attributes
468     *   cannot be cloned.
469     *  @see #exportMoML(Writer, int, String)
470     *  @see #setDeferringChangeRequests(boolean)
471     */
472    public Object clone(Workspace workspace) throws CloneNotSupportedException {
473        // NOTE: It is safe to clone an object into a different
474        // workspace. It is not safe to move an object to a new
475        // workspace, by contrast. The reason this is safe is that
476        // after the this method has been run, there
477        // are no references in the clone to objects in the old
478        // workspace. Moreover, no object in the old workspace can have
479        // a reference to the cloned object because we have only just
480        // created it and have not returned the reference.
481        try {
482            _workspace.getReadAccess();
483
484            NamedObj newObject = (NamedObj) super.clone();
485
486            newObject._hierarchyListeners = null;
487
488            newObject._changeLock = new SerializableObject();
489            newObject._changeRequests = null;
490
491            // The clone should have its own listeners, otherwise
492            // debug messages from the clone will go to the master.
493            // See 8.1.0 in NamedObj.tcl. Credit: Colin Endicott
494            newObject._debugListeners = null;
495
496            // Since _debugListeners is null, _debugging should be
497            // false to avoid error message in _debug()
498            newObject._debugging = false;
499
500            // During the cloning process, change requests might
501            // be issued (e.g. in an actor's _addEntity() method).
502            // Execution of these change requests need to be deferred
503            // until after cloning is complete.  To ensure that,
504            // we set the following.  Note that when the container
505            // of an object being cloned is set, any queued change
506            // requests will be delegated to the new container, and
507            // the value of this private variable will no longer
508            // have any effect.
509            newObject._deferChangeRequests = true;
510
511            // NOTE: It is not necessary to write-synchronize on the other
512            // workspace because this only affects its directory, and methods
513            // to access the directory are synchronized.
514            newObject._attributes = null;
515
516            newObject._decoratorAttributes = new HashMap<Decorator, DecoratorAttributes>();
517            newObject._decoratorAttributesVersion = -1L;
518
519            // NOTE: As of version 5.0, clones inherit the derived
520            // level of the object from which they are cloned.
521            // This is somewhat risky, but since cloning is usually
522            // used for instantiation, and instantiation fixes up
523            // the derived level, this creates no problems there.
524            // In the rare cases when clone is actually used directly
525            // (mainly in tests), it is appropriate for the clone
526            // to be indentical in this regard to the object from
527            // which it is cloned.  EAL 3/3/05
528            // newObject._derivedLevel = Integer.MAX_VALUE;
529            if (workspace == null) {
530                newObject._workspace = _DEFAULT_WORKSPACE;
531            } else {
532                newObject._workspace = workspace;
533            }
534
535            newObject._fullNameVersion = -1;
536
537            if (_attributes != null) {
538                Iterator<?> parameters = _attributes.elementList().iterator();
539
540                while (parameters.hasNext()) {
541                    Attribute parameter = (Attribute) parameters.next();
542                    Attribute newParameter = (Attribute) parameter
543                            .clone(workspace);
544
545                    try {
546                        newParameter.setContainer(newObject);
547                        // PortParameters are
548                        // AbstractInitializableParameters that end up
549                        // adding HierarchyListeners to the
550                        // initializable container of the actor.  This
551                        // results in a memory leak, so we remove
552                        // parameters that are HierarchyListeners.
553                        // See https://projects.ecoinformatics.org/ecoinfo/issues/7190
554                        if (newParameter instanceof HierarchyListener) {
555                            if (newParameter.getContainer() != null) {
556                                NamedObj newContainerContainer = newParameter
557                                        .getContainer().getContainer();
558                                NamedObj oldContainer = getContainer();
559                                // FIXME: This is probably a serious
560                                // problem that the container of the
561                                // container of the parameter in the
562                                // clone is the same as the container
563                                // of original master NamedObj.  Part
564                                // of the issue here is that NamedObj
565                                // does not have setContainer().  What
566                                // we would really like to do is to
567                                // set the container to null during
568                                // cloning.
569                                if (newContainerContainer != null
570                                        && newContainerContainer == oldContainer) {
571                                    newContainerContainer
572                                            .removeHierarchyListener(
573                                                    (HierarchyListener) newParameter);
574                                }
575                            }
576                        }
577                    } catch (KernelException exception) {
578                        throw new CloneNotSupportedException(
579                                "Failed to clone attribute "
580                                        + parameter.getFullName() + ": "
581                                        + exception);
582                    }
583                }
584            }
585
586            if (_debugging) {
587                if (workspace == null) {
588                    _debug("Cloned", getFullName(), "into default workspace.");
589                } else {
590                    _debug("Cloned", getFullName(), "into workspace:",
591                            workspace.getFullName());
592                }
593            }
594
595            newObject._elementName = _elementName;
596            newObject._source = _source;
597
598            // NOTE: It's not clear that this is the right thing to do
599            // here, having the same override properties as the original
600            // seems reasonable, so we leave this be. However, it is essential
601            // to clone the list in case it is later modified in the source
602            // of the clone.
603            if (_override != null) {
604                newObject._override = new LinkedList<Integer>(_override);
605            }
606
607            // NOTE: The value for the classname and superclass isn't
608            // correct if this cloning operation is meant to create
609            // an extension rather than a clone.  A clone has exactly
610            // the same className and superclass as the master.
611            // It is up to the caller to correct these fields if
612            // that is the case.  It cannot be done here because
613            // we don't know the name of the new class.
614            newObject.setClassName(getClassName());
615
616            _cloneFixAttributeFields(newObject);
617
618            return newObject;
619        } finally {
620            _workspace.doneReading();
621        }
622    }
623
624    /** Return an iterator over contained objects. In this base class,
625     *  this is simply an iterator over attributes.  In derived classes,
626     *  the iterator will also traverse ports, entities, classes,
627     *  and relations.
628     *  @return An iterator over instances of NamedObj contained by this
629     *   object.
630     */
631    public Iterator containedObjectsIterator() {
632        return new ContainedObjectsIterator();
633    }
634
635    /** Return the set of decorators that decorate this object.
636     *  @see Decorator
637     *  @return The decorators that decorate this object (which may
638     *   be an empty set).
639     * @exception IllegalActionException If a decorator referenced
640     *  by a DecoratorAttributes cannot be found.
641     */
642    public Set<Decorator> decorators() throws IllegalActionException {
643        synchronized (_decoratorAttributes) {
644            _updateDecoratorAttributes();
645            return _decoratorAttributes.keySet();
646        }
647    }
648
649    /** Return true if this object contains the specified object,
650     *  directly or indirectly.  That is, return true if the specified
651     *  object is contained by an object that this contains, or by an
652     *  object contained by an object contained by this, etc.
653     *  This method ignores whether the entities report that they are
654     *  atomic (see CompositeEntity), and always returns false if the entities
655     *  are not in the same workspace.
656     *  This method is read-synchronized on the workspace.
657     *  @param inside The object to check for inside this object.
658     *  @return True if this contains the argument, directly or indirectly.
659     */
660    public boolean deepContains(NamedObj inside) {
661        try {
662            _workspace.getReadAccess();
663
664            // Start with the inside and check its containers in sequence.
665            if (inside != null) {
666                if (_workspace != inside._workspace) {
667                    return false;
668                }
669
670                Nameable container = inside.getContainer();
671
672                while (container != null) {
673                    if (container == this) {
674                        return true;
675                    }
676
677                    container = container.getContainer();
678                }
679            }
680
681            return false;
682        } finally {
683            _workspace.doneReading();
684        }
685    }
686
687    /** Return the depth in the hierarchy of this object. If this object
688     *  has no container, then return 0.  If its container has no container,
689     *  then return 1.  Etc.
690     *  @return The depth in the hierarchy of this object.
691     */
692    public int depthInHierarchy() {
693        int result = 0;
694        Nameable container = getContainer();
695
696        while (container != null) {
697            result++;
698            container = container.getContainer();
699        }
700
701        return result;
702    }
703
704    /** Return a full description of the object. This is accomplished
705     *  by calling the description method with an argument for full detail.
706     *  This method read-synchronizes on the workspace.
707     *  @return A description of the object.
708     *  @exception IllegalActionException Not thrown in this base class,
709     *  but derived classes could throw an exception if there is a problem
710     *  accessing subcomponents of this object.
711     *  @see #exportMoML(Writer, int, String)
712     */
713    @Override
714    public String description() throws IllegalActionException {
715        return description(COMPLETE);
716    }
717
718    /** Return a description of the object.  The level of detail depends
719     *  on the argument, which is an or-ing of the static final constants
720     *  defined in this class (NamedObj).  This method returns an empty
721     *  string (not null) if there is nothing to report.
722     *  It read-synchronizes on the workspace.
723     *  @param detail The level of detail.
724     *  @return A description of the object.
725     *  @exception IllegalActionException Not thrown in this base class,
726     *  but derived classes could throw an exception if there is a problem
727     *  accessing subcomponents of this object.
728     *  @see #exportMoML(Writer, int, String)
729     */
730    public String description(int detail) throws IllegalActionException {
731        return _description(detail, 0, 0);
732    }
733
734    /** React to the given debug event by relaying to any registered
735     *  debug listeners.
736     *  @param event The event.
737     *  @since Ptolemy II 2.3
738     */
739    @Override
740    public void event(DebugEvent event) {
741        if (_debugging) {
742            _debug(event);
743        }
744    }
745
746    /** Execute previously requested changes. If there is a container, then
747     *  delegate the request to the container.  Otherwise, this method will
748     *  execute all pending changes (even if
749     *  {@link #isDeferringChangeRequests()} returns true.
750     *  Listeners will be notified of success or failure.
751     *  @see #addChangeListener(ChangeListener)
752     *  @see #requestChange(ChangeRequest)
753     *  @see #isDeferringChangeRequests()
754     *  @see Changeable
755     */
756    @Override
757    public void executeChangeRequests() {
758        NamedObj container = getContainer();
759
760        if (container != null) {
761            container.executeChangeRequests();
762            return;
763        }
764
765        // Have to execute a copy of the change request list
766        // because the list may be modified during execution.
767        List<ChangeRequest> copy = _copyChangeRequestList();
768
769        if (copy != null) {
770            _executeChangeRequests(copy);
771            // Change requests may have been queued during the execute.
772            // Execute those by a recursive call.
773            executeChangeRequests();
774        }
775    }
776
777    /** Get a MoML description of this object.  This might be an empty string
778     *  if there is no MoML description of this object or if this object is
779     *  not persistent or if this object is a derived object.  This uses the
780     *  three-argument version of this method.  It is final to ensure that
781     *  derived classes only need to override that method to change
782     *  the MoML description.
783     *  @return A MoML description, or an empty string if there is none.
784     *  @see MoMLExportable
785     *  @see #exportMoML(Writer, int, String)
786     *  @see #isPersistent()
787     *  @see #getDerivedLevel()
788     */
789    @Override
790    public final String exportMoML() {
791        try {
792            StringWriter buffer = new StringWriter();
793            exportMoML(buffer, 0);
794            return buffer.toString();
795        } catch (IOException ex) {
796            // This should not occur.
797            throw new InternalErrorException(this, ex, null);
798        }
799    }
800
801    /** Get a MoML description of this object with its name replaced by
802     *  the specified name.  The description might be an empty string
803     *  if there is no MoML description of this object or if this object
804     *  is not persistent, or this object a derived object.  This uses the
805     *  three-argument version of this method.  It is final to ensure that
806     *  derived classes only override that method to change
807     *  the MoML description.
808     *  @param name The name of we use when exporting the description.
809     *  @return A MoML description, or the empty string if there is none.
810     *  @see MoMLExportable
811     *  @see #exportMoML(Writer, int, String)
812     *  @see #isPersistent()
813     *  @see #getDerivedLevel()
814     */
815    @Override
816    public final String exportMoML(String name) {
817        try {
818            StringWriter buffer = new StringWriter();
819            exportMoML(buffer, 0, name);
820            return buffer.toString();
821        } catch (IOException ex) {
822            // This should not occur.
823            throw new InternalErrorException(this, ex, null);
824        }
825    }
826
827    /** Write a MoML description of this object using the specified
828     *  Writer.  If there is no MoML description, or if the object
829     *  is not persistent, or if this object is a derived object,
830     *  then nothing is written. To write to standard out, do
831     *  <pre>
832     *      exportMoML(new OutputStreamWriter(System.out))
833     *  </pre>
834     *  This method uses the three-argument
835     *  version of this method.  It is final to ensure that
836     *  derived classes only need to override that method to change
837     *  the MoML description.
838     *  @param output The stream to write to.
839     *  @exception IOException If an I/O error occurs.
840     *  @see MoMLExportable
841     *  @see #exportMoML(Writer, int, String)
842     *  @see #isPersistent()
843     *  @see #getDerivedLevel()
844     */
845    @Override
846    public final void exportMoML(Writer output) throws IOException {
847        exportMoML(output, 0);
848    }
849
850    /** Write a MoML description of this entity with the specified
851     *  indentation depth.  This calls the three-argument version of
852     *  this method with getName() as the third argument.
853     *  This method is final to ensure that
854     *  derived classes only override the three-argument method to change
855     *  the MoML description.
856     *  If the object is not persistent, or if there is no MoML description,
857     *  or if this object is a class instance, then write nothing.
858     *  @param output The output stream to write to.
859     *  @param depth The depth in the hierarchy, to determine indenting.
860     *  @exception IOException If an I/O error occurs.
861     *  @see MoMLExportable
862     *  @see #exportMoML(Writer, int, String)
863     *  @see #isPersistent()
864     *  @see #getDerivedLevel()
865     */
866    @Override
867    public final void exportMoML(Writer output, int depth) throws IOException {
868        exportMoML(output, depth, getName());
869    }
870
871    /** Write a MoML description of this object with the specified
872     *  indentation depth and with the specified name substituting
873     *  for the name of this object.  The class name is determined
874     *  by {@link #getClassName()}, the source is determined by
875     *  {@link #getSource()}. The description has the form:
876     *  <pre>
877     *      &lt;<i>element</i> name="<i>name</i>" class="<i>classname</i>" source="<i>source</i>"&gt;&gt;
878     *          <i>body, determined by _exportMoMLContents()</i>
879     *      &lt;/<i>element</i>&gt;
880     *  </pre>
881     *  By default, the element name is "entity."  The default class name
882     *  is the Java classname of this instance.
883     *  The source attribute is by default left off altogether.
884     *  <p>
885     *  If this object has no container and the depth argument is zero,
886     *  then this method prepends XML file header information, which is:
887     *  <pre>
888     *  &lt;?xml version="1.0" standalone="no"?&gt;
889     *  &lt;!DOCTYPE entity PUBLIC "-//UC Berkeley//DTD MoML 1//EN"
890     *      "http://ptolemy.eecs.berkeley.edu/xml/dtd/MoML_1.dtd"&gt;
891     *  </pre>
892     *  In the above, "entity" may be replaced by "property" or "port"
893     *  if somehow a top-level property or port is exported.
894     *  <p>
895     *  The text that is written is indented according to the specified
896     *  depth, with each line (including the last one)
897     *  terminated with a newline.
898     *  Derived classes can override this method to change the MoML
899     *  description of an object.  They can override the protected
900     *  method _exportMoMLContents() if they need to only change which
901     *  contents are described.
902     *  <p>
903     *  If this object is not persistent, or if there is no MoML
904     *  description of this object, or if this object is a class
905     *  instance, then write nothing.
906     *  @param output The output stream to write to.
907     *  @param depth The depth in the hierarchy, to determine indenting.
908     *  @param name The name to use in the exported MoML.
909     *  @exception IOException If an I/O error occurs.
910     *  @see MoMLExportable
911     *  @see #clone(Workspace)
912     *  @see #isPersistent()
913     *  @see #getDerivedLevel()
914     */
915    @Override
916    public void exportMoML(Writer output, int depth, String name)
917            throws IOException {
918
919        // Escape any < character in name. unescapeForXML occurs in
920        // setName(String).
921        name = StringUtilities.escapeForXML(name);
922
923        try {
924            _workspace.getReadAccess();
925
926            // If the object is not persistent, or the MoML is
927            // redundant with what would be propagated, then do
928            // not generate any MoML.
929            if (_isMoMLSuppressed(depth)) {
930                return;
931            }
932
933            String className = getClassName();
934
935            if (depth == 0 && getContainer() == null) {
936                // No container, and this is a top level moml element.
937                // Generate header information.
938                // NOTE: Used to generate this only if the top-level
939                // was an entity, with the following test:
940                // if (_elementName.equals("entity")) {}
941                // However, this meant that when saving icons,
942                // they would not have the header information,
943                // and when opened, would open as a text file
944                // instead of in the icon editor.
945                output.write("<?xml version=\"1.0\" standalone=\"no\"?>\n"
946                        + "<!DOCTYPE " + _elementName + " PUBLIC "
947                        + "\"-//UC Berkeley//DTD MoML 1//EN\"\n"
948                        + "    \"http://ptolemy.eecs.berkeley.edu"
949                        + "/xml/dtd/MoML_1.dtd\">\n");
950            }
951
952            output.write(_getIndentPrefix(depth) + "<" + _elementName
953                    + " name=\"" + name + "\" class=\"" + className + "\"");
954
955            if (getSource() != null) {
956                output.write(" source=\"" + getSource() + "\">\n");
957            } else {
958                output.write(">\n");
959            }
960
961            // Callers of _exportMoMLContents() should hold a read lock
962            // so as to avoid ConcurrentModificationExceptions
963            _exportMoMLContents(output, depth + 1);
964
965            // Write the close of the element.
966            output.write(_getIndentPrefix(depth) + "</" + _elementName + ">\n");
967        } finally {
968            _workspace.doneReading();
969        }
970    }
971
972    /** Get a MoML description of this object without any XML headers.
973     *  This differs significantly from exportMoML() only if this
974     *  object has no container, because if it has a container, then
975     *  it will not export MoML headers anyway.
976     *  @return A MoML description, or the empty string if there is none.
977     *  @see #exportMoML()
978     */
979    public final String exportMoMLPlain() {
980        try {
981            StringWriter buffer = new StringWriter();
982            // Using a depth of 1 suppresses the XML header.
983            // It also, unfortunately, indents the result.
984            // But I guess this is harmless.
985            exportMoML(buffer, 1, getName());
986            return buffer.toString();
987        } catch (IOException ex) {
988            // This should not occur.
989            throw new InternalErrorException(this, ex, null);
990        }
991    }
992
993    /** Get the attribute with the given name. The name may be compound,
994     *  with fields separated by periods, in which case the attribute
995     *  returned is contained by a (deeply) contained attribute.
996     *  If the given name is null, then an InternalErrorException is thrown.
997     *  This method is read-synchronized on the workspace.
998     *  @param name The name of the desired attribute.
999     *  @return The requested attribute if it is found, null otherwise.
1000     */
1001    public Attribute getAttribute(String name) {
1002        try {
1003            _workspace.getReadAccess();
1004
1005            if (_attributes == null) {
1006                // No attribute has been added to this NamedObj yet.
1007                return null;
1008            } else {
1009                if (name == null) {
1010                    // If MoMLParser has problems, we may end up here,
1011                    // so rather than having _splitName() throw a
1012                    // NullPointerException, we do the check here and
1013                    // include 'this' so that we know where the problem
1014                    // is occurring.
1015                    throw new InternalErrorException(this, null,
1016                            "This should not be happening: getAttribute() "
1017                                    + "was called with a null name");
1018                }
1019
1020                // This method gets called often,
1021                // so avoid the call to _splitName().
1022                // This change is good for a 2-3% speed up
1023                // in ptesdf mini-model-aggregator.
1024                // Below is the old code:
1025
1026                // String[] subnames = _splitName(name);
1027                // if (subnames[1] == null) {
1028                //   return (Attribute) _attributes.get(name);
1029                // else {
1030                //   Attribute match = (Attribute) _attributes.get(subnames[0]);
1031
1032                //   if (match == null) {
1033                //       return null;
1034                //   } else {
1035                //       return match.getAttribute(subnames[1]);
1036                //   }
1037                // }
1038
1039                final int period = name.indexOf(".");
1040
1041                if (period < 0) {
1042                    return (Attribute) _attributes.get(name);
1043                } else {
1044                    final Attribute match = (Attribute) _attributes
1045                            .get(name.substring(0, period));
1046                    if (match == null) {
1047                        return null;
1048                    } else {
1049                        return match.getAttribute(name.substring(period + 1));
1050                    }
1051                }
1052            }
1053        } finally {
1054            _workspace.doneReading();
1055        }
1056    }
1057
1058    /** Get the attribute with the given name and class. If an attribute
1059     *  is found that has the specified name, but the class does not match,
1060     *  then throw an IllegalActionException.  The name may be compound,
1061     *  with fields separated by periods, in which case the attribute
1062     *  returned is contained by a (deeply) contained attribute.
1063     *  This method is read-synchronized on the workspace.
1064     *  @param name The name of the desired attribute.
1065     *  @param attributeClass The class of the desired attribute.
1066     *  @return The requested attribute if it is found, null otherwise.
1067     *  @exception IllegalActionException If an attribute is found with
1068     *   the specified name that is not an instance of the specified class.
1069     */
1070    public Attribute getAttribute(String name, Class attributeClass)
1071            throws IllegalActionException {
1072        Attribute attribute = getAttribute(name);
1073
1074        if (attribute != null) {
1075            if (!attributeClass.isInstance(attribute)) {
1076                throw new IllegalActionException(attribute,
1077                        "Expected attribute of class "
1078                                + attributeClass.getName()
1079                                + " but got attribute of class "
1080                                + attribute.getClass().getName());
1081            }
1082        }
1083
1084        return attribute;
1085    }
1086
1087    /** Return an enumeration of the attributes attached to this object.
1088     *  This method is read-synchronized on the workspace.
1089     *  @deprecated Use attributeList() instead.
1090     *  @return An enumeration of instances of Attribute.
1091     */
1092    @Deprecated
1093    public Enumeration getAttributes() {
1094        return Collections.enumeration(attributeList());
1095    }
1096
1097    /** Return a list of weak references to change listeners,
1098     *  or null if there is none.
1099     *  @return A list of weak references to change listeners,
1100     *   or null if there is none.
1101     */
1102    public List getChangeListeners() {
1103        return _changeListeners;
1104    }
1105
1106    /** Return the MoML class name.  This is either the
1107     *  class of which this object is an instance, or if this
1108     *  object is itself a class, then the class that it extends.
1109     *  By default, it will be the Java class name of this object.
1110     *  This method never returns null.
1111     *  @return The MoML class name.
1112     *  @see MoMLExportable
1113     *  @see #setClassName(String)
1114     */
1115    @Override
1116    public String getClassName() {
1117        if (_className == null) {
1118            _className = getClass().getName();
1119        }
1120
1121        return _className;
1122    }
1123
1124    /** Get the container.  Always return null in this base class.
1125     *  A null returned value should be interpreted as indicating
1126     *  that there is no container.
1127     *  @return null.
1128     */
1129    @Override
1130    public NamedObj getContainer() {
1131        return null;
1132    }
1133
1134    /** Return the decorator attribute with the specified name for the
1135     *  specified decorator, or null the specified decorator provides
1136     *  no attribute with the specified name or the decorator does not
1137     *  decorate this object. This method is normally called by the
1138     *  decorator itself to retrieve its decorated parameter values for
1139     *  this NamedObj.
1140     *  If this object has no decorator attributes, then calling
1141     *  this method will cause them to be created and assigned default values,
1142     *  if the specified decorator decorates this object.
1143     *  @see ptolemy.kernel.util.Decorator#createDecoratorAttributes(NamedObj)
1144     *  @see #getDecoratorAttributes(Decorator)
1145     *  @param decorator The decorator.
1146     *  @param name The name of the attribute.
1147     *  @return The attribute with the given name for the decorator, or null
1148     *   if the specified decorator does not provide an attribute with the specified
1149     *   name.
1150     *  @exception IllegalActionException If a decorator referenced
1151     *   by a DecoratorAttributes cannot be found.
1152     */
1153    public Attribute getDecoratorAttribute(Decorator decorator, String name)
1154            throws IllegalActionException {
1155        DecoratorAttributes attributes = getDecoratorAttributes(decorator);
1156        if (attributes != null) {
1157            return attributes.getAttribute(name);
1158        }
1159        return null;
1160    }
1161
1162    /** Return the decorated attributes of this NamedObj, as decorated by the
1163     *  specified decorator. If there are no such attributes, then calling
1164     *  this method will cause them to attempt to be created and assigned default values.
1165     *  If the specified decorator does not decorate this object, then this method will
1166     *  return null.
1167     *  @see ptolemy.kernel.util.Decorator#createDecoratorAttributes(NamedObj)
1168     *  @see #getDecoratorAttribute(Decorator, String)
1169     *  @param decorator The decorator.
1170     *  @return The decorated attributes, or null if the specified decorator does not
1171     *   decorate this object.
1172     *  @exception IllegalActionException If a decorator referenced
1173     *   by a DecoratorAttributes cannot be found.
1174     */
1175    public DecoratorAttributes getDecoratorAttributes(Decorator decorator)
1176            throws IllegalActionException {
1177        synchronized (_decoratorAttributes) {
1178            _updateDecoratorAttributes();
1179            return _decoratorAttributes.get(decorator);
1180        }
1181    }
1182
1183    /** Get the minimum level above this object in the hierarchy where a
1184     *  parent-child relationship implies the existence of this object.
1185     *  A value Integer.MAX_VALUE is used to indicate that this object is
1186     *  not a derived object. A value of 1 indicates that the container
1187     *  of the object is a child, and that the this object is derived
1188     *  from a prototype in the parent of the container. Etc.
1189     *  @return The level above this object in the containment
1190     *   hierarchy where a parent-child relationship implies this object.
1191     *  @see Derivable
1192     *  @see #setDerivedLevel(int)
1193     */
1194    @Override
1195    public int getDerivedLevel() {
1196        return _derivedLevel;
1197    }
1198
1199    /** Return a list of objects derived from this one.
1200     *  This is the list of objects that are "inherited" by their
1201     *  containers from a container of this object. The existence of
1202     *  these derived objects is "implied" by a parent-child relationship
1203     *  somewhere above this object in the containment hierarchy.
1204     *  This method returns a complete list, including objects that
1205     *  have been overridden.
1206     *  @return A list of objects of the same class as the object on
1207     *   which this is called, or an empty list if there are none.
1208     *  @see Derivable
1209     */
1210    @Override
1211    public List getDerivedList() {
1212        try {
1213            return _getDerivedList(null, false, false, this, 0, null, null);
1214        } catch (IllegalActionException ex) {
1215            throw new InternalErrorException(ex);
1216        }
1217    }
1218
1219    /** Return a name to present to the user. If setDisplayName(String)
1220     *  has been called, then return the name specified there, and
1221     *  otherwise return the name returned by getName().
1222     *  @return A name to present to the user.
1223     *  @see #setDisplayName(String)
1224     */
1225    @Override
1226    public String getDisplayName() {
1227        if (_displayName != null) {
1228            return _displayName;
1229        }
1230        return getName();
1231    }
1232
1233    /** Get the MoML element name. This defaults to "entity"
1234     *  but can be set to something else by subclasses.
1235     *  @return The MoML element name for this object.
1236     *  @see MoMLExportable
1237     */
1238    @Override
1239    public String getElementName() {
1240        return _elementName;
1241    }
1242
1243    /** Return a string of the form ".name1.name2...nameN". Here,
1244     *  "nameN" is the name of this object,
1245     *  and the intervening names are the names of the containers
1246     *  of this other name of this object, if there are containers.
1247     *  A recursive structure, where this object is directly or indirectly
1248     *  contained by itself, results in a runtime exception of class
1249     *  InvalidStateException.  Note that it is
1250     *  not possible to construct a recursive structure using this class alone,
1251     *  since there is no container.
1252     *  But derived classes might erroneously permit recursive structures,
1253     *  so this error is caught here.
1254     *  This method is read-synchronized on the workspace.
1255     *  @return The full name of the object.
1256     */
1257    @Override
1258    public String getFullName() {
1259        try {
1260            _workspace.getReadAccess();
1261
1262            if (_fullNameVersion == _workspace.getVersion()) {
1263                return _fullNameCache;
1264            }
1265
1266            // Cache is not valid. Recalculate full name.
1267            String fullName = getName();
1268
1269            // Use a hash set to keep track of what we've seen already.
1270            Set<Nameable> visited = new HashSet<Nameable>();
1271            visited.add(this);
1272
1273            Nameable container = getContainer();
1274
1275            while (container != null) {
1276                if (visited.contains(container)) {
1277                    // Cannot use "this" as a constructor argument to the
1278                    // exception or we'll get stuck infinitely
1279                    // calling this method, since this method is used to report
1280                    // exceptions.  InvalidStateException is a runtime
1281                    // exception, so it need not be declared.
1282                    throw new InvalidStateException(
1283                            "Container contains itself!");
1284                }
1285
1286                fullName = container.getName() + "." + fullName;
1287                visited.add(container);
1288                container = container.getContainer();
1289            }
1290
1291            _fullNameCache = "." + fullName;
1292            _fullNameVersion = _workspace.getVersion();
1293            return _fullNameCache;
1294        } finally {
1295            _workspace.doneReading();
1296        }
1297    }
1298
1299    /** Get the model error handler specified by setErrorHandler().
1300     *  @return The error handler, or null if none.
1301     *  @see #setModelErrorHandler(ModelErrorHandler handler)
1302     */
1303    public ModelErrorHandler getModelErrorHandler() {
1304        return _modelErrorHandler;
1305    }
1306
1307    /** Get the name. If no name has been given, or null has been given,
1308     *  then return an empty string, "".
1309     *  @return The name of the object.
1310     *  @see #setName(String)
1311     */
1312    @Override
1313    public String getName() {
1314        return _name;
1315    }
1316
1317    /** Get the name of this object relative to the specified container.
1318     *  If this object is contained directly by the specified container,
1319     *  this is just its name, as returned by getName().  If it is deeply
1320     *  contained by the specified container, then the relative name is
1321     *  <i>x1</i>.<i>x2</i>. ... .<i>name</i>, where <i>x1</i> is directly
1322     *  contained by the specified container, <i>x2</i> is contained by
1323     *  <i>x1</i>, etc.  If this object is not deeply contained by the
1324     *  specified container, then this method returns the full name of
1325     *  this object, as returned by getFullName().
1326     *  <p>
1327     *  A recursive structure, where this object is directly or indirectly
1328     *  contained by itself, may result in a runtime exception of class
1329     *  InvalidStateException if it is detected.  Note that it is
1330     *  not possible to construct a recursive structure using this class alone,
1331     *  since there is no container.
1332     *  But derived classes might erroneously permit recursive structures,
1333     *  so this error is caught here.
1334     *  <p>
1335     *  This method is read-synchronized on the workspace.
1336     *  @param parent The object relative to which you want the name.
1337     *  @return A string of the form "name2...nameN".
1338     *  @exception InvalidStateException If a recursive structure is
1339     *   encountered, where this object directly or indirectly contains
1340     *   itself. Note that this is a runtime exception so it need not
1341     *   be declared explicitly.
1342     *  @see #setName(String)
1343     */
1344    @Override
1345    public String getName(NamedObj parent) throws InvalidStateException {
1346        if (parent == null) {
1347            return getFullName();
1348        }
1349
1350        try {
1351            _workspace.getReadAccess();
1352
1353            StringBuffer name = new StringBuffer(getName());
1354
1355            // Use a hash set to keep track of what we've seen already.
1356            Set<Nameable> visited = new HashSet<Nameable>();
1357            visited.add(this);
1358
1359            Nameable container = getContainer();
1360
1361            while (container != null && container != parent) {
1362                if (visited.contains(container)) {
1363                    // Cannot use "this" as a constructor argument to the
1364                    // exception or we'll get stuck infinitely
1365                    // calling getFullName(),
1366                    // since that method is used to report
1367                    // exceptions.  InvalidStateException is a runtime
1368                    // exception, so it need not be declared.
1369                    throw new InvalidStateException(
1370                            "Container contains itself!");
1371                }
1372
1373                name.insert(0, ".");
1374                name.insert(0, container.getName());
1375                visited.add(container);
1376                container = container.getContainer();
1377            }
1378
1379            if (container == null) {
1380                return getFullName();
1381            }
1382
1383            return name.toString();
1384        } finally {
1385            _workspace.doneReading();
1386        }
1387    }
1388
1389    /** Return a list of prototypes for this object. The list is ordered
1390     *  so that more local prototypes are listed before more remote
1391     *  prototypes. Specifically, if the container has a parent, and
1392     *  that parent contains an object whose name matches the name
1393     *  of this object, then that object is the first prototype listed.
1394     *  If the container of the container has a parent, and that parent
1395     *  (deeply) contains a prototype, then that prototype is listed next.
1396     *  And so on up the hierarchy.
1397     *  @return A list of prototypes for this object, each of which is
1398     *   assured of being an instance of the same (Java) class as this
1399     *   object, or an empty list if there are no prototypes.
1400     *  @exception IllegalActionException If a prototype with the right
1401     *   name but the wrong class is found.
1402     *  @see Derivable
1403     */
1404    @Override
1405    public List getPrototypeList() throws IllegalActionException {
1406        List<NamedObj> result = new LinkedList<NamedObj>();
1407        NamedObj container = getContainer();
1408        String relativeName = getName();
1409
1410        while (container != null) {
1411            if (container instanceof Instantiable) {
1412                Instantiable parent = ((Instantiable) container).getParent();
1413
1414                if (parent != null) {
1415                    // Check whether the parent has it...
1416                    NamedObj prototype = _getContainedObject((NamedObj) parent,
1417                            relativeName);
1418
1419                    if (prototype != null) {
1420                        result.add(prototype);
1421                    }
1422                }
1423            }
1424
1425            relativeName = container.getName() + "." + relativeName;
1426            container = container.getContainer();
1427        }
1428
1429        return result;
1430    }
1431
1432    /** Get the source, which gives an external URL
1433     *  associated with an entity (presumably from which the entity
1434     *  was defined).  This becomes the value in the "source"
1435     *  attribute of exported MoML.
1436     *  @return The source, or null if there is none.
1437     *  @see #setSource(String)
1438     *  @see MoMLExportable
1439     */
1440    @Override
1441    public String getSource() {
1442        return _source;
1443    }
1444
1445    /** Handle a model error. If a model error handler has been registered
1446     *  with setModelErrorHandler(), then handling is delegated to that
1447     *  handler.  Otherwise, or if the registered error handler declines
1448     *  to handle the error by returning false, then if there is a
1449     *  container, handling is delegated to the container.
1450     *  If there is no container and no handler that agrees to
1451     *  handle the error, then return false.
1452     *  <p>
1453     *  A typical use of this facility is where a subclass of NamedObj
1454     *  does the following:
1455     *  <pre>
1456     *     handleModelError(this, new IllegalActionException(this, message));
1457     *  </pre>
1458     *  instead of this:
1459     *  <pre>
1460     *     throw new IllegalActionException(this, message);
1461     *  </pre>
1462     *  The former allows a container in the hierarchy to intercept the
1463     *  exception, whereas the latter simply throws the exception.
1464     *  @param context The object in which the error occurred.
1465     *  @param exception An exception that represents the error.
1466     *  @return True if the error is handled, false otherwise.
1467     *  @exception IllegalActionException If the handler handles the
1468     *   error by throwing an exception.
1469     *  @see #setModelErrorHandler(ModelErrorHandler handler)
1470     */
1471    @Override
1472    public boolean handleModelError(NamedObj context,
1473            IllegalActionException exception) throws IllegalActionException {
1474        // FIXME: This code fails horribly when one forgets to add a
1475        // BasicModelErrorHandler at the toplevel of the model.  In
1476        // reality, this code should do what BasicModelErrorHandler
1477        // does and throw an exception when anything falls off the top
1478        // of the model.
1479        if (_modelErrorHandler != null) {
1480            if (_modelErrorHandler.handleModelError(context, exception)) {
1481                return true;
1482            }
1483        }
1484
1485        ModelErrorHandler container = getContainer();
1486
1487        if (container != null) {
1488            return container.handleModelError(context, exception);
1489        }
1490
1491        return false;
1492    }
1493
1494    /** Return true if setDeferringChangeRequests(true) has been called
1495     *  to specify that change requests should be deferred. If there
1496     *  is a container, this delegates to the container.
1497     *  @return True if change requests are being deferred.
1498     *  @see #setDeferringChangeRequests(boolean)
1499     *  @see Changeable
1500     */
1501    @Override
1502    public boolean isDeferringChangeRequests() {
1503        NamedObj container = getContainer();
1504
1505        if (container != null) {
1506            return container.isDeferringChangeRequests();
1507        }
1508
1509        return _deferChangeRequests;
1510    }
1511
1512    /** Return true if propagateValue() has been called, which
1513     *  indicates that the value of this object (if any) has been
1514     *  overridden from the default defined by its class definition.
1515     *  Note that if setDerivedLevel() is called after propagateValue(),
1516     *  then this method will return false, since setDerivedLevel()
1517     *  resets the override property.
1518     *  @return True if propagateValues() has been called.
1519     *  @see #propagateValue()
1520     *  @see #setDerivedLevel(int)
1521     */
1522    public boolean isOverridden() {
1523        // Return true only if _override is a list of length 1
1524        // with the value 0.
1525        if (_override == null) {
1526            return false;
1527        }
1528
1529        if (_override.size() != 1) {
1530            return false;
1531        }
1532
1533        int override = _override.get(0).intValue();
1534        return override == 0;
1535    }
1536
1537    /** Return true if this object is persistent.
1538     *  A persistent object has a MoML description that can be stored
1539     *  in a file and used to re-create the object. A non-persistent
1540     *  object has an empty MoML description.
1541     *  @return True if the object is persistent.
1542     *  @see #setPersistent(boolean)
1543     *  @see MoMLExportable
1544     */
1545    @Override
1546    public boolean isPersistent() {
1547        return _isPersistent == null || _isPersistent.booleanValue();
1548    }
1549
1550    /** Return an iterator over contained object that currently exist,
1551     *  omitting any objects that have not yet been instantiated because
1552     *  they are "lazy". A lazy object is one that is instantiated when it
1553     *  is needed, but not before. In this base class, this method returns
1554     *  the same iterator returned by {@link #containedObjectsIterator()}.
1555     *  If derived classes override it, they must guarantee that any omitted
1556     *  objects are genuinely not needed in whatever uses this method.
1557     *  @return An iterator over instances of NamedObj contained by this
1558     *   object.
1559     */
1560    public Iterator lazyContainedObjectsIterator() {
1561        return containedObjectsIterator();
1562    }
1563
1564    /** React to a debug message by relaying it to any registered
1565     *  debug listeners.
1566     *  @param message The debug message.
1567     *  @since Ptolemy II 2.3
1568     */
1569    @Override
1570    public void message(String message) {
1571        if (_debugging) {
1572            _debug(message);
1573        }
1574    }
1575
1576    /** Move this object down by one in the list of objects in
1577     *  its container. If this object is already
1578     *  last, do nothing.  In this base class, this method throws
1579     *  an IllegalActionException because this base class does not
1580     *  have a setContainer() method, and hence cannot be contained.
1581     *  Any derived object that implements setContainer() should
1582     *  also implement this method.
1583     *  @return This base class does not return. In derived classes, it should
1584     *   return the index of the specified object prior to moving it,
1585     *   or -1 if it is not moved.
1586     *  @exception IllegalActionException Always thrown in this base class.
1587     */
1588    @Override
1589    public int moveDown() throws IllegalActionException {
1590        // NOTE: This method could be made abstract, but NamedObj
1591        // is not abstract to allow for more complete testing.
1592        throw new IllegalActionException(this, "Has no container.");
1593    }
1594
1595    /** Move this object to the first position in the list
1596     *  of attributes of the container. If this object is already
1597     *  first, do nothing.  In this base class, this method throws
1598     *  an IllegalActionException because this base class does not
1599     *  have a setContainer() method, and hence cannot be contained.
1600     *  Any derived object that implements setContainer() should
1601     *  also implement this method.
1602     *  @return This base class does not return. In derived classes, it should
1603     *   return the index of the specified object prior to moving it,
1604     *   or -1 if it is not moved.
1605     *  @exception IllegalActionException Always thrown in this base class.
1606     */
1607    @Override
1608    public int moveToFirst() throws IllegalActionException {
1609        // NOTE: This method could be made abstract, but NamedObj
1610        // is not abstract to allow for more complete testing.
1611        throw new IllegalActionException(this, "Has no container.");
1612    }
1613
1614    /** Move this object to the specified position in the list of
1615     *  attributes of the container. If this object is already at the
1616     *  specified position, do nothing.  In this base class, this
1617     *  method throws an IllegalActionException because this base
1618     *  class does not have a setContainer() method, and hence cannot
1619     *  be contained.
1620     *  Any derived object that implements setContainer() should
1621     *  also implement this method.
1622     *  @param index The position to move this object to.
1623     *  @return This base class does not return. In derived classes, it should
1624     *   return the index of the specified object prior to moving it,
1625     *   or -1 if it is not moved.
1626     *  @exception IllegalActionException Always thrown in this base class.
1627     */
1628    @Override
1629    public int moveToIndex(int index) throws IllegalActionException {
1630        // NOTE: This method could be made abstract, but NamedObj
1631        // is not abstract to allow for more complete testing.
1632        throw new IllegalActionException(this, "Has no container.");
1633    }
1634
1635    /** Move this object to the last position in the list
1636     *  of attributes of the container.  If this object is already last,
1637     *  do nothing. In this base class, this method throws
1638     *  an IllegalActionException because this base class does not
1639     *  have a setContainer() method, and hence cannot be contained.
1640     *  Any derived object that implements setContainer() should
1641     *  also implement this method.
1642     *  @return This base class does not return. In derived classes, it should
1643     *   return the index of the specified object prior to moving it,
1644     *   or -1 if it is not moved.
1645     *  @exception IllegalActionException Always thrown in this base class.
1646     */
1647    @Override
1648    public int moveToLast() throws IllegalActionException {
1649        // NOTE: This method could be made abstract, but NamedObj
1650        // is not abstract to allow for more complete testing.
1651        throw new IllegalActionException(this, "Has no container.");
1652    }
1653
1654    /** Move this object up by one in the list of
1655     *  attributes of the container. If this object is already first, do
1656     *  nothing. In this base class, this method throws
1657     *  an IllegalActionException because this base class does not
1658     *  have a setContainer() method, and hence cannot be contained.
1659     *  Any derived object that implements setContainer() should
1660     *  also implement this method.
1661     *  @return This base class does not return. In derived classes, it should
1662     *   return the index of the specified object prior to moving it,
1663     *   or -1 if it is not moved.
1664     *  @exception IllegalActionException Always thrown in this base class.
1665     */
1666    @Override
1667    public int moveUp() throws IllegalActionException {
1668        // NOTE: This method could be made abstract, but NamedObj
1669        // is not abstract to allow for more complete testing.
1670        throw new IllegalActionException(this, "Has no container.");
1671    }
1672
1673    /** React to a change in a contained named object. This method is
1674     *  called by a contained named object when its name or display name
1675     *  changes. In this base class, the method does nothing.
1676     *  @param object The object that changed.
1677     */
1678    public void notifyOfNameChange(NamedObj object) {
1679    }
1680
1681    /** Propagate the existence of this object.
1682     *  If this object has a container, then ensure that all
1683     *  objects derived from the container contain an object
1684     *  with the same class and name as this one. Create that
1685     *  object when needed. The contents of each so created
1686     *  object is marked as derived using setDerivedLevel().
1687     *  Return the list of objects that are created.
1688     *  @return A list of derived objects of the same class
1689     *   as this object that are created.
1690     *  @exception IllegalActionException If the object does
1691     *   not exist and cannot be created.
1692     *  @see Derivable
1693     *  @see #setDerivedLevel(int)
1694     */
1695    @Override
1696    public List propagateExistence() throws IllegalActionException {
1697        return _getDerivedList(null, false, true, this, 0, null, null);
1698    }
1699
1700    /** Propagate the value (if any) held by this
1701     *  object to derived objects that have not been overridden.
1702     *  This leaves all derived objects unchanged if any single
1703     *  derived object throws an exception
1704     *  when attempting to propagate the value to it.
1705     *  This also marks this object as overridden.
1706     *  @return The list of objects to which this propagated.
1707     *  @exception IllegalActionException If propagation fails.
1708     *  @see Derivable
1709     *  @see #isOverridden()
1710     */
1711    @Override
1712    public List propagateValue() throws IllegalActionException {
1713        // Mark this object as having been modified directly.
1714        _override = new LinkedList<Integer>();
1715        _override.add(Integer.valueOf(0));
1716
1717        return _getDerivedList(null, true, false, this, 0, _override, null);
1718    }
1719
1720    /** If this object has a value that has been set directly,
1721     *  or if it has a value that has propagated in, then
1722     *  propagate that value to all derived objects, and
1723     *  then repeat this for all objects this object contains.
1724     *  Unlike propagateValue(), this does not assume this
1725     *  object or any of its contained objects is having
1726     *  its value set directly. Instead, it uses the current
1727     *  state of override of this object as the starting point.
1728     *  @exception IllegalActionException If propagation fails.
1729     */
1730    public void propagateValues() throws IllegalActionException {
1731        // If this object has not had its value set directly or
1732        // by propagation into it, then there is no need to do
1733        // any propagation.
1734        if (_override != null) {
1735            _getDerivedList(null, true, false, this, 0, _override, null);
1736        }
1737
1738        Iterator<?> containedObjects = containedObjectsIterator();
1739
1740        while (containedObjects.hasNext()) {
1741            NamedObj containedObject = (NamedObj) containedObjects.next();
1742            containedObject.propagateValues();
1743        }
1744    }
1745
1746    /** Remove a change listener. If there is a container, delegate the
1747     *  request to the container.  If the specified listener is not
1748     *  on the list of listeners, do nothing.
1749     *  @param listener The listener to remove.
1750     *  @see #addChangeListener(ChangeListener)
1751     *  @see Changeable
1752     */
1753    @Override
1754    public synchronized void removeChangeListener(ChangeListener listener) {
1755        NamedObj container = getContainer();
1756
1757        if (container != null) {
1758            container.removeChangeListener(listener);
1759        } else {
1760            synchronized (_changeLock) {
1761                if (_changeListeners != null) {
1762                    ListIterator<WeakReference<ChangeListener>> listeners = _changeListeners
1763                            .listIterator();
1764
1765                    while (listeners.hasNext()) {
1766                        WeakReference<ChangeListener> reference = listeners
1767                                .next();
1768
1769                        if (reference.get() == listener) {
1770                            listeners.remove();
1771                        } else if (reference.get() == null) {
1772                            listeners.remove();
1773                        }
1774                    }
1775                }
1776            }
1777        }
1778    }
1779
1780    /** Unregister a debug listener.  If the specified listener has not
1781     *  been previously registered, then do nothing.
1782     *  @param listener The listener to remove from the list of listeners
1783     *   to which debug messages are sent.
1784     *  @see #addDebugListener(DebugListener)
1785     */
1786    @Override
1787    public void removeDebugListener(DebugListener listener) {
1788        if (_debugListeners == null) {
1789            return;
1790        }
1791
1792        // NOTE: This has to be synchronized to prevent
1793        // concurrent modification exceptions.
1794        synchronized (_debugListeners) {
1795            _debugListeners.remove(listener);
1796
1797            if (_debugListeners.size() == 0) {
1798                _debugging = false;
1799            }
1800
1801            return;
1802        }
1803    }
1804
1805    /** Remove a hierarchy listener. If the listener is already
1806     *  removed, do nothing. This will cause the object to also
1807     *  be removed as a hierarchy listener in the container of
1808     *  this object, if there is one, and in its container,
1809     *  up to the top of the hierarchy.
1810     *  @param listener The listener to remove.
1811     *  @see #addHierarchyListener(HierarchyListener)
1812     */
1813    public void removeHierarchyListener(HierarchyListener listener) {
1814        if (_hierarchyListeners != null) {
1815            Iterator<WeakReference<HierarchyListener>> listeners = _hierarchyListeners
1816                    .iterator();
1817            while (listeners.hasNext()) {
1818                WeakReference<HierarchyListener> reference = listeners.next();
1819                if (reference.get() == listener) {
1820                    listeners.remove();
1821                }
1822            }
1823
1824            // Remove from the container.
1825            NamedObj container = getContainer();
1826            if (container != null) {
1827                container.removeHierarchyListener(listener);
1828            }
1829        }
1830    }
1831
1832    /** Request that the given change be executed.   In this base class,
1833     *  delegate the change request to the container, if there is one.
1834     *  If there is no container, then execute the request immediately,
1835     *  unless this object is deferring change requests. If
1836     *  setDeferChangeRequests() has been called with a true argument,
1837     *  then simply queue the request until either setDeferChangeRequests()
1838     *  is called with a false argument or executeChangeRequests() is called.
1839     *  If this object is already in the middle of executing a change
1840     *  request, then that execution is finished before this one is performed.
1841     *  Change listeners will be notified of success (or failure) of the
1842     *  request when it is executed.
1843     *  @param change The requested change.
1844     *  @see #executeChangeRequests()
1845     *  @see #setDeferringChangeRequests(boolean)
1846     *  @see Changeable
1847     */
1848    @Override
1849    public void requestChange(ChangeRequest change) {
1850        NamedObj container = getContainer();
1851
1852        if (container != null) {
1853            container.requestChange(change);
1854        } else {
1855            // Synchronize to make sure we don't modify
1856            // the list of change requests while some other
1857            // thread is also trying to read or modify it.
1858            synchronized (_changeLock) {
1859                // Queue the request.
1860                // Create the list of requests if it doesn't already exist
1861                if (_changeRequests == null) {
1862                    _changeRequests = new LinkedList<ChangeRequest>();
1863                }
1864
1865                _changeRequests.add(change);
1866            }
1867            if (!_deferChangeRequests) {
1868                executeChangeRequests();
1869            }
1870        }
1871    }
1872
1873    /** Set the MoML class name.  This is either the
1874     *  class of which this object is an instance, or if this
1875     *  object is itself a class, then the class that it extends.
1876     *  @param name The MoML class name.
1877     *  @see #getClassName()
1878     */
1879    public void setClassName(String name) {
1880        _className = name;
1881    }
1882
1883    /** Specify whether change requests made by calls to requestChange()
1884     *  should be executed immediately. If there is a container, then
1885     *  this request is delegated to the container. Otherwise,
1886     *  if the argument is true, then requests
1887     *  are simply queued until either this method is called again
1888     *  with argument false, or until executeChangeRequests() is called.
1889     *  If the argument is false, then execute any pending change requests
1890     *  and set a flag requesting that future requests be executed
1891     *  immediately.
1892     *  @param isDeferring If true, defer change requests.
1893     *  @see #addChangeListener(ChangeListener)
1894     *  @see #executeChangeRequests()
1895     *  @see #isDeferringChangeRequests()
1896     *  @see #requestChange(ChangeRequest)
1897     *  @see Changeable
1898     */
1899    @Override
1900    public void setDeferringChangeRequests(boolean isDeferring) {
1901        NamedObj container = getContainer();
1902
1903        if (container != null) {
1904            container.setDeferringChangeRequests(isDeferring);
1905            return;
1906        }
1907
1908        // Make sure to avoid modification of this flag in the middle
1909        // of a change request or change execution.
1910        List<ChangeRequest> copy = null;
1911        synchronized (_changeLock) {
1912            _deferChangeRequests = isDeferring;
1913
1914            if (isDeferring == false) {
1915                // Must not hold _changeLock while executing change requests.
1916                copy = _copyChangeRequestList();
1917            }
1918        }
1919        if (copy != null) {
1920            _executeChangeRequests(copy);
1921        }
1922    }
1923
1924    /** Set the level above this object in the hierarchy where a
1925     *  parent-child relationship implies the existence of this object.
1926     *  When this object is originally created by a constructor or
1927     *  by the clone method, the level is set to the default Integer.MAX_VALUE,
1928     *  which indicates that the object is not implied. When this
1929     *  is called multiple times, the level will be the minimum of
1930     *  all the levels specified. Thus, a value of 1 indicates that the
1931     *  container of the object is a child, and that this object is
1932     *  implied by a like object in the parent of the container, for example.
1933     *  If an object is implied, then it normally has no persistent
1934     *  representation when it is exported to MoML (unless it
1935     *  is overridden), and normally it cannot have its name or
1936     *  container changed.  An exception, however, is that the object
1937     *  may appear in the MoML if the exported MoML does not include
1938     *  the level of the hierarchy above this with the parent-child
1939     *  relationship that implies this object.
1940     *  Calling this method also has the side effect of resetting the
1941     *  flag used to determine whether the value of this object overrides
1942     *  some inherited value. So this method should only be called when
1943     *  object is first being constructed.
1944     *  <p>
1945     *  NOTE: This method is tricky to use correctly. It is public because
1946     *  the MoML parser needs access to it. It should not be considered part
1947     *  of the public interface, however, in that only very sophisticated
1948     *  users should use it.
1949     *  @param level The minimum level above this object in the containment
1950     *   hierarchy where a parent-child relationship implies this object.
1951     *  @see #getDerivedLevel()
1952     *  @see #setPersistent(boolean)
1953     *  @see Derivable
1954     */
1955    public final void setDerivedLevel(int level) {
1956        if (level < _derivedLevel) {
1957            _derivedLevel = level;
1958        }
1959
1960        // Setting override to null indicates that no override has
1961        // occurred.
1962        // NOTE: This setting of _override to null was commented
1963        // out, justified by the following NOTE.  However,
1964        // the following NOTE is questionable, since this public
1965        // method is invoked by the MoMLParser and MoMLChangeRequest
1966        // in circumstances where they really want the objects to be
1967        // marked as not overridden. Thus, I have changed this so that
1968        // local uses of this method are replaced with direct actions
1969        // and public accesses of this method revert to the original
1970        // behavior.  11/07 EAL
1971        // OBSOLETE NOTE: This is no longer the right thing to do.
1972        // Upon instantiating, the clone method creates a copy
1973        // of the _override field.  Then the _adjustOverrides()
1974        // method adjusts the value of that field to reflect
1975        // that the new object gets its value from the object
1976        // from which it was cloned, or from whatever that
1977        // gets it from.
1978        _override = null;
1979    }
1980
1981    /** Set a name to present to the user.
1982     *  @param name A name to present to the user.
1983     *  @see #getDisplayName()
1984     */
1985    public void setDisplayName(String name) {
1986        _displayName = name;
1987        // Notify container of this change.
1988        NamedObj container = getContainer();
1989        if (container != null) {
1990            container.notifyOfNameChange(this);
1991        }
1992    }
1993
1994    /** Set the model error handler.
1995     *  @param handler The error handler, or null to specify no handler.
1996     *  @see #getModelErrorHandler()
1997     */
1998    public void setModelErrorHandler(ModelErrorHandler handler) {
1999        _modelErrorHandler = handler;
2000    }
2001
2002    /** Set or change the name.  If a null argument is given the
2003     *  name is set to an empty string.
2004     *  Increment the version of the workspace.
2005     *  This method is write-synchronized on the workspace.
2006     *  @param name The new name.
2007     *  @exception IllegalActionException If the name contains a period
2008     *   or if the object is a derived object and the name argument does
2009     *   not match the current name.
2010     *  @exception NameDuplicationException Not thrown in this base class.
2011     *   May be thrown by derived classes if the container already contains
2012     *   an object with this name.
2013     *  @see #getName()
2014     *  @see #getName(NamedObj)
2015     */
2016    @Override
2017    public void setName(String name)
2018            throws IllegalActionException, NameDuplicationException {
2019        String oldName = "";
2020
2021        if (_debugging) {
2022            oldName = getFullName();
2023        }
2024
2025        if (name == null) {
2026            name = "";
2027        }
2028
2029        if (name.equals(_name)) {
2030            // Nothing to do.
2031            return;
2032        }
2033
2034        int period = name.indexOf(".");
2035
2036        if (period >= 0) {
2037            throw new IllegalActionException(this,
2038                    "Cannot set a name with a period: " + name);
2039        }
2040
2041        // Unescape if necessary. escapeForXML occurs in
2042        // exportMoML(Writer output, int depth, String name)
2043        // See http://chess.eecs.berkeley.edu/ptolemy/listinfo/ptolemy/2010-April/011999.html
2044        name = StringUtilities.unescapeForXML(name);
2045
2046        try {
2047            _workspace.getWriteAccess();
2048            _name = name;
2049        } finally {
2050            _workspace.doneWriting();
2051        }
2052
2053        // Notify container of this change.
2054        NamedObj container = getContainer();
2055        if (container != null) {
2056            container.notifyOfNameChange(this);
2057        }
2058
2059        if (_debugging) {
2060            _debug("Changed name from " + oldName + " to " + getFullName());
2061        }
2062    }
2063
2064    /** Set the persistence of this object. If the persistence is not
2065     *  specified with this method, then by default the object will be
2066     *  persistent unless it is derivable by derivation from a class.
2067     *  A persistent object has a non-empty MoML description that can be used
2068     *  to re-create the object. To make an instance non-persistent,
2069     *  call this method with the argument <i>false</i>. To force
2070     *  it to always be persistent, irrespective of its relationship
2071     *  to a class, then call this with argument <i>true</i>. Note
2072     *  that this will have the additional effect that it no longer
2073     *  inherits properties from the class, so in effect, calling
2074     *  this with <i>true</i> overrides values given by the class.
2075     *  @param persistent False to make this object non-persistent.
2076     *  @see #isPersistent()
2077     *  @see MoMLExportable
2078     */
2079    @Override
2080    public void setPersistent(boolean persistent) {
2081        if (persistent) {
2082            _isPersistent = Boolean.TRUE;
2083        } else {
2084            _isPersistent = Boolean.FALSE;
2085        }
2086    }
2087
2088    /** Set the source, which gives an external URL
2089     *  associated with an entity (presumably from which the entity
2090     *  was defined).  This becomes the value in the "source"
2091     *  attribute of exported MoML. Call this with null to prevent
2092     *  any source attribute from being generated.
2093     *  @param source The source, or null if there is none.
2094     *  @see #getSource()
2095     *  @see MoMLExportable
2096     */
2097    @Override
2098    public void setSource(String source) {
2099        _source = source;
2100    }
2101
2102    /** Return an ordered list of contained objects filtered by the specified
2103     *  filter. The attributes are listed first, followed by ports,
2104     *  classes, entities, and relations, in that order. Within each
2105     *  category, objects are listed in the order they were created
2106     *  (or as later modified by methods like moveDown()). The filter
2107     *  gives a collection of objects to include. Only objects
2108     *  contained by the filter are included.
2109     *  @param filter A collection specifying which objects to include
2110     *   in the returned list.
2111     *  @return A list of contained instances of NamedObj that are
2112     *   in the specified filter, or an empty list if there are none.
2113     */
2114    public List sortContainedObjects(Collection filter) {
2115        LinkedList<NamedObj> result = new LinkedList<NamedObj>();
2116        Iterator<?> containedObjects = containedObjectsIterator();
2117
2118        while (containedObjects.hasNext()) {
2119            NamedObj object = (NamedObj) containedObjects.next();
2120
2121            if (filter.contains(object)) {
2122                result.add(object);
2123            }
2124        }
2125
2126        return result;
2127    }
2128
2129    /** Return the class name and the full name of the object,
2130     *  with syntax "className {fullName}".
2131     *  @return The class name and the full name. */
2132    @Override
2133    public String toString() {
2134        return getClass().getName() + " {" + getFullName() + "}";
2135    }
2136
2137    /** Return the top level of the containment hierarchy.
2138     *  @return The top level, or this if this has no container.
2139     */
2140    public NamedObj toplevel() {
2141        NamedObj result = this;
2142
2143        while (result.getContainer() != null) {
2144            result = result.getContainer();
2145        }
2146
2147        return result;
2148    }
2149
2150    /** Return a name that is guaranteed to not be the name of any
2151     *  contained attribute.  In derived classes, this should be overridden
2152     *  so that the returned name is guaranteed to not conflict with
2153     *  any contained object.  In this implementation, the argument
2154     *  is stripped of any numeric suffix, and then a numeric suffix
2155     *  is appended and incremented until a name is found that does not
2156     *  conflict with a contained attribute.
2157     *  @param prefix A prefix for the name.
2158     *  @return A unique name, which will be exactly the prefix if possible,
2159     *   or the prefix extended by a number.
2160     */
2161    public String uniqueName(String prefix) {
2162        if (prefix == null) {
2163            prefix = "null";
2164        }
2165
2166        prefix = _stripNumericSuffix(prefix);
2167
2168        String candidate = prefix;
2169        int uniqueNameIndex = 2;
2170
2171        while (getAttribute(candidate) != null) {
2172            candidate = prefix + uniqueNameIndex++;
2173        }
2174
2175        return candidate;
2176    }
2177
2178    /** Validate attributes deeply contained by this object if they
2179     *  implement the Settable interface by calling their validate() method.
2180     *  Errors that are triggered by this validation are handled by calling
2181     *  handleModelError().  Normally this should be called after constructing
2182     *  a model or after making changes to it.  It is called, for example,
2183     *  by the MoMLParser.
2184     *  @see #handleModelError(NamedObj context, IllegalActionException exception)
2185     *  @exception IllegalActionException If there is a problem validating
2186     *  the deeply contained attributes.
2187     */
2188    public void validateSettables() throws IllegalActionException {
2189        _validateSettables(new HashSet<Settable>());
2190    }
2191
2192    /** Get the workspace. This method never returns null, since there
2193     *  is always a workspace.
2194     *  @return The workspace responsible for this object.
2195     */
2196    public final Workspace workspace() {
2197        return _workspace;
2198    }
2199
2200    ///////////////////////////////////////////////////////////////////
2201    ////                         public variables                  ////
2202
2203    /** Indicate that the description(int) method should include everything.
2204     */
2205    public static final int COMPLETE = -1;
2206
2207    /** Indicate that the description(int) method should include the class name.
2208     */
2209    public static final int CLASSNAME = 1;
2210
2211    /** Indicate that the description(int) method should include the full name.
2212     *  The full name is surrounded by braces "{name}" in case it has spaces.
2213     */
2214    public static final int FULLNAME = 2;
2215
2216    /** Indicate that the description(int) method should include the links
2217     *  (if any) that the object has.  This has the form "links {...}"
2218     *  where the list is a list of descriptions of the linked objects.
2219     *  This may force some of the contents to be listed.  For example,
2220     *  a description of an entity will include the ports if this is set,
2221     *  irrespective of whether the CONTENTS bit is set.
2222     */
2223    public static final int LINKS = 4;
2224
2225    /** Indicate that the description(int) method should include the contained
2226     *  objects (if any) that the object has.  This has the form
2227     *  "keyword {{class {name}} {class {name}} ... }" where the keyword
2228     *  can be ports, entities, relations, or anything else that might
2229     *  indicate what the object contains.
2230     */
2231    public static final int CONTENTS = 8;
2232
2233    /** Indicate that the description(int) method should include the contained
2234     *  objects (if any) that the contained objects have.  This has no effect
2235     *  if CONTENTS is not also specified.  The returned string has the form
2236     *  "keyword {{class {name} keyword {...}} ... }".
2237     */
2238    public static final int DEEP = 16;
2239
2240    /** Indicate that the description(int) method should include attributes
2241     *  (if any).
2242     */
2243    public static final int ATTRIBUTES = 32;
2244
2245    ///////////////////////////////////////////////////////////////////
2246    ////                         protected methods                 ////
2247
2248    /** Add an attribute.  This method should not be used directly.
2249     *  Instead, call setContainer() on the attribute.
2250     *  Derived classes may further constrain the class of the attribute.
2251     *  To do this, they should override this method to throw an exception
2252     *  when the argument is not an instance of the expected class.
2253     *  <p>
2254     *  This method is write-synchronized on the workspace and increments its
2255     *  version number.</p>
2256     *
2257     *  @param attribute The attribute to be added.
2258     *  @exception NameDuplicationException If this object already
2259     *   has an attribute with the same name.
2260     *  @exception IllegalActionException If the attribute is not an
2261     *   an instance of the expect class (in derived classes).
2262     */
2263    protected void _addAttribute(Attribute attribute)
2264            throws NameDuplicationException, IllegalActionException {
2265        try {
2266            _workspace.getWriteAccess();
2267
2268            if (_attributes == null) {
2269                _attributes = new NamedList();
2270            }
2271
2272            _attributes.append(attribute);
2273
2274            if (_debugging) {
2275                _debug("Added attribute", attribute.getName(), "to",
2276                        getFullName());
2277            }
2278        } finally {
2279            _workspace.doneWriting();
2280        }
2281    }
2282
2283    /** Adjust the _override field of this object, if there is
2284     *  one, by incrementing the value at the specified depth
2285     *  by one, and do the same for all contained objects, with
2286     *  one larger depth.
2287     *  @param depth The depth.
2288     */
2289    protected void _adjustOverride(int depth) {
2290        // This method is called after cloning, so having a non-null
2291        // _override means that this object was cloned from and
2292        // is derived from an object whose value is set from some
2293        // other object according to the _override field.
2294        if (_override != null) {
2295            // If the _override field is not long enough,
2296            // then we need to make it long enough.
2297            while (_override.size() <= depth) {
2298                _override.add(Integer.valueOf(0));
2299            }
2300            int breadth = _override.get(depth).intValue();
2301            _override.set(depth, Integer.valueOf(breadth + 1));
2302        }
2303        Iterator<?> objects = lazyContainedObjectsIterator();
2304        while (objects.hasNext()) {
2305            NamedObj object = (NamedObj) objects.next();
2306            object._adjustOverride(depth + 1);
2307        }
2308    }
2309
2310    /** Attach the specified text as an attribute with the specified
2311     *  name.  This is a convenience method (syntactic sugar) that
2312     *  creates an instance of TransientSingletonConfigurableAttribute
2313     *  and configures it with the specified text.  This attribute
2314     *  is transient, meaning that it is not described by exported
2315     *  MoML.  Moreover, it is a singleton, meaning that it will
2316     *  replace any previously contained instance of SingletonAttribute
2317     *  that has the same name.
2318     *  <p>
2319     *  Note that attribute names beginning with an underscore "_"
2320     *  are reserved for system use.  This method is used in several
2321     *  places to set the value of such attributes.
2322     *  @param name The name of the attribute.
2323     *  @param text The text with which to configure the attribute.
2324     */
2325    protected void _attachText(String name, String text) {
2326        try {
2327            SingletonConfigurableAttribute icon = new SingletonConfigurableAttribute(
2328                    this, name);
2329            icon.setPersistent(false);
2330
2331            // The first argument below is the base w.r.t. which to open
2332            // relative references within the text, which doesn't make
2333            // sense in this case, so it's null. The second argument is
2334            // an external URL source for the text, which is again null.
2335            icon.configure(null, null, text);
2336        } catch (Exception ex) {
2337            throw new InternalErrorException(this, ex,
2338                    "Error creating singleton attribute named " + name + " for "
2339                            + getFullName());
2340        }
2341    }
2342
2343    /** Fix the fields of the given object which point to Attributes.
2344     *  The object is assumed to be a clone of this one.  The fields
2345     *  are fixed to point to the corresponding attribute of the clone,
2346     *  instead of pointing to attributes of this object.
2347     *  @param newObject The object in which we fix the fields.
2348     *  @exception CloneNotSupportedException If there is a problem
2349     *   getting the attribute
2350     */
2351    protected void _cloneFixAttributeFields(NamedObj newObject)
2352            throws CloneNotSupportedException {
2353        // If the new object has any public fields whose name
2354        // matches that of an attribute, then set the public field
2355        // equal to the attribute.
2356        Class<? extends NamedObj> myClass = getClass();
2357        Field[] fields = myClass.getFields();
2358
2359        for (int i = 0; i < fields.length; i++) {
2360            try {
2361                // VersionAttribute has a final field
2362                if (!Modifier.isFinal(fields[i].getModifiers())) {
2363                    Object object = fields[i].get(this);
2364
2365                    if (object instanceof Attribute) {
2366                        String name = ((NamedObj) object).getName(this);
2367                        fields[i].set(newObject, newObject.getAttribute(name));
2368                    }
2369                }
2370            } catch (IllegalAccessException ex) {
2371                // CloneNotSupportedException does not have a
2372                // constructor that takes a cause argument, so we call
2373                // initCause() and then throw.
2374                CloneNotSupportedException cloneException = new CloneNotSupportedException(
2375                        "The field associated with " + fields[i].getName()
2376                                + " could not be automatically cloned because "
2377                                + ex.getMessage() + ".  This can be caused if "
2378                                + "the field is not defined in a public class.");
2379
2380                cloneException.initCause(ex);
2381                throw cloneException;
2382            }
2383        }
2384    }
2385
2386    /** Return a list of decorators contained by this object.
2387     *  In this base class, this list consists of Attributes that implement
2388     *  the {@link Decorator} interface. In subclasses, it can contain other
2389     *  objects that implement the Decorator interface, such as Entities.
2390     *  @return A list of contained decorators.
2391     */
2392    protected List<Decorator> _containedDecorators() {
2393        return attributeList(Decorator.class);
2394    }
2395
2396    /** Return a copy of the current list of change requests, or return
2397     *  null if there aren't any pending change requests.
2398     *  @return A copy of the change request list, or null if there aren't any.
2399     */
2400    protected List<ChangeRequest> _copyChangeRequestList() {
2401        synchronized (_changeLock) {
2402            if (_changeRequests != null && _changeRequests.size() > 0) {
2403                // Copy the change requests lists because it may
2404                // be modified during execution.
2405                List<ChangeRequest> copy = new LinkedList(_changeRequests);
2406
2407                // Remove the changes to be executed.
2408                // We remove them even if there is a failure because
2409                // otherwise we could get stuck making changes that
2410                // will continue to fail.
2411                _changeRequests.clear();
2412
2413                return copy;
2414            }
2415        }
2416        return null;
2417    }
2418
2419    /** Send a debug event to all debug listeners that have registered.
2420     *  @param event The event.
2421     */
2422    protected final void _debug(DebugEvent event) {
2423        if (_debugging) {
2424            // We copy this list so that responding to the event may block.
2425            // while the execution thread is blocked, we want to be able to
2426            // add more debug listeners...
2427            // Yes, this is slow, but hey, it's debug code.
2428            List<DebugListener> list;
2429
2430            if (_debugListeners == null) {
2431                System.err.println("Warning, _debugListeners was null, "
2432                        + "which means that _debugging was set to true, but no "
2433                        + "listeners were added?");
2434                System.err.println(event);
2435            } else {
2436                // NOTE: This used to synchronize on this, which caused
2437                // deadlocks. We use a more specialized lock now.
2438                synchronized (_debugListeners) {
2439                    list = new ArrayList<DebugListener>(_debugListeners);
2440                }
2441
2442                for (DebugListener listener : list) {
2443                    listener.event(event);
2444                }
2445            }
2446        }
2447    }
2448
2449    /** Send a debug message to all debug listeners that have registered.
2450     *  By convention, messages should not include a newline at the end.
2451     *  The newline will be added by the listener, if appropriate.
2452     *  @param message The message.
2453     */
2454    protected final void _debug(String message) {
2455        if (_debugging) {
2456            // We copy this list so that responding to the event may block.
2457            // while the execution thread is blocked, we want to be able to
2458            // add more debug listeners...
2459            // Yes, this is slow, but hey, it's debug code.
2460            List<DebugListener> list;
2461
2462            if (_debugListeners == null) {
2463                System.err.println("Warning, _debugListeners was null, "
2464                        + "which means that _debugging was set to true, but no "
2465                        + "listeners were added?");
2466                System.err.println(message);
2467            } else {
2468                // NOTE: This used to synchronize on this, which caused
2469                // deadlocks.  We use a more specialized lock now.
2470                synchronized (_debugListeners) {
2471                    list = new ArrayList<DebugListener>(_debugListeners);
2472                }
2473
2474                for (DebugListener listener : list) {
2475                    listener.message(message);
2476                }
2477            }
2478        }
2479    }
2480
2481    /** Send a debug message to all debug listeners that have registered.
2482     *  The message is a concatenation of the two parts, with a space between
2483     *  them.
2484     *  By convention, messages should not include a newline at the end.
2485     *  The newline will be added by the listener, if appropriate.
2486     *  @param part1 The first part of the message.
2487     *  @param part2 The second part of the message.
2488     */
2489    protected final void _debug(String part1, String part2) {
2490        if (_debugging) {
2491            _debug(part1 + " " + part2);
2492        }
2493    }
2494
2495    /** Send a debug message to all debug listeners that have registered.
2496     *  The message is a concatenation of the three parts, with a space between
2497     *  them.
2498     *  By convention, messages should not include a newline at the end.
2499     *  The newline will be added by the listener, if appropriate.
2500     *  @param part1 The first part of the message.
2501     *  @param part2 The second part of the message.
2502     *  @param part3 The third part of the message.
2503     */
2504    protected final void _debug(String part1, String part2, String part3) {
2505        if (_debugging) {
2506            _debug(part1 + " " + part2 + " " + part3);
2507        }
2508    }
2509
2510    /** Send a debug message to all debug listeners that have registered.
2511     *  The message is a concatenation of the four parts, with a space between
2512     *  them.
2513     *  By convention, messages should not include a newline at the end.
2514     *  The newline will be added by the listener, if appropriate.
2515     *  @param part1 The first part of the message.
2516     *  @param part2 The second part of the message.
2517     *  @param part3 The third part of the message.
2518     *  @param part4 The fourth part of the message.
2519     */
2520    protected final void _debug(String part1, String part2, String part3,
2521            String part4) {
2522        if (_debugging) {
2523            _debug(part1 + " " + part2 + " " + part3 + " " + part4);
2524        }
2525    }
2526
2527    /** Return a description of the object.  The level of detail depends
2528     *  on the argument, which is an or-ing of the static final constants
2529     *  defined in this class (NamedObj).  Lines are indented according to
2530     *  to the level argument using the static method _getIndentPrefix().
2531     *  Zero, one or two brackets can be specified to surround the returned
2532     *  description.  If one is specified it is the the leading bracket.
2533     *  This is used by derived classes that will append to the description.
2534     *  Those derived classes are responsible for the closing bracket.
2535     *  An argument other than 0, 1, or 2 is taken to be equivalent to 0.
2536     *  This method is read-synchronized on the workspace.
2537     *  @param detail The level of detail.
2538     *  @param indent The amount of indenting.
2539     *  @param bracket The number of surrounding brackets (0, 1, or 2).
2540     *  @return A description of the object.
2541     *  @exception IllegalActionException Not thrown in this base class,
2542     *  but derived classes could throw an exception if there is a problem
2543     *  accessing subcomponents of this object.
2544     */
2545    protected String _description(int detail, int indent, int bracket)
2546            throws IllegalActionException {
2547        try {
2548            _workspace.getReadAccess();
2549
2550            StringBuffer result = new StringBuffer(_getIndentPrefix(indent));
2551
2552            if (bracket == 1 || bracket == 2) {
2553                result.append("{");
2554            }
2555
2556            if ((detail & CLASSNAME) != 0) {
2557                result.append(getClass().getName());
2558
2559                if ((detail & FULLNAME) != 0) {
2560                    result.append(" ");
2561                }
2562            }
2563
2564            if ((detail & FULLNAME) != 0) {
2565                result.append("{" + getFullName() + "}");
2566            }
2567
2568            if ((detail & ATTRIBUTES) != 0) {
2569                if ((detail & (CLASSNAME | FULLNAME)) != 0) {
2570                    result.append(" ");
2571                }
2572
2573                result.append("attributes {\n");
2574
2575                // Do not recursively list attributes unless the DEEP
2576                // bit is set.
2577                if ((detail & DEEP) == 0) {
2578                    detail &= ~ATTRIBUTES;
2579                }
2580
2581                if (_attributes != null) {
2582                    Iterator<?> parameters = _attributes.elementList()
2583                            .iterator();
2584
2585                    while (parameters.hasNext()) {
2586                        Attribute parameter = (Attribute) parameters.next();
2587                        result.append(
2588                                parameter._description(detail, indent + 1, 2)
2589                                        + "\n");
2590                    }
2591                }
2592
2593                result.append(_getIndentPrefix(indent) + "}");
2594            }
2595
2596            if (bracket == 2) {
2597                result.append("}");
2598            }
2599
2600            return result.toString();
2601        } finally {
2602            _workspace.doneReading();
2603        }
2604    }
2605
2606    /** Execute the specified list of change requests.
2607     *  @param changeRequests The list of change requests to execute.
2608     */
2609    protected void _executeChangeRequests(List<ChangeRequest> changeRequests) {
2610        Iterator requests = changeRequests.iterator();
2611        boolean previousDeferStatus = _deferChangeRequests;
2612
2613        try {
2614            // Get write access once on the outside, to make
2615            // getting write access on each individual
2616            // modification faster.
2617            // NOTE: This optimization, it turns out,
2618            // drastically slows down execution of models
2619            // that do graphical animation or that change
2620            // parameter values during execution. Changing
2621            // parameter values does not require write
2622            // access to the workspace.
2623            // _workspace.getWriteAccess();
2624
2625            // Defer change requests so that if changes are
2626            // requested during execution, they get queued.
2627            _deferChangeRequests = true;
2628            // FIXME: How do we ensure that _deferChangeRequests
2629            // does not get changed during the following loop?
2630            // It is not OK to change _changeLock, as this will
2631            // lead to deadlock. In particular, another thread
2632            // that holds read permission on the workspace
2633            // might try to acquire _changeLock and block,
2634            // and then this thread will try to get write
2635            // permission on the workspace, and it will block.
2636            while (requests.hasNext()) {
2637                ChangeRequest change = (ChangeRequest) requests.next();
2638
2639                // The following is a bad idea because there may be
2640                // many fine-grain change requests in the list, and
2641                // notification triggers expensive operations such
2642                // as repairing the graph model in diva and repainting.
2643                // Hence, we do the notification once after all the
2644                // change requests have executed.  Note that this may
2645                // make it harder to optimize Vergil so that it
2646                // repaints only damaged regions of the screen.
2647                change.setListeners(_changeListeners);
2648
2649                if (_debugging) {
2650                    _debug("-- Executing change request " + "with description: "
2651                            + change.getDescription());
2652                }
2653                change.execute();
2654            }
2655        } finally {
2656            // NOTE: See note above.
2657            // _workspace.doneWriting();
2658
2659            _deferChangeRequests = previousDeferStatus;
2660        }
2661    }
2662
2663    /** Write a MoML description of the contents of this object, which
2664     *  in this base class is the attributes.  This method is called
2665     *  by exportMoML().  If there are attributes, then
2666     *  each attribute description is indented according to the specified
2667     *  depth and terminated with a newline character.
2668     *  Callers of this method should hold read access before
2669     *  calling this method.  Note that exportMoML() does this for us.
2670     *
2671     *  @param output The output stream to write to.
2672     *  @param depth The depth in the hierarchy, to determine indenting.
2673     *  @exception IOException If an I/O error occurs.
2674     *  @see #exportMoML(Writer, int)
2675     */
2676    protected void _exportMoMLContents(Writer output, int depth)
2677            throws IOException {
2678        // If the display name has been set, then include a display element.
2679        // Note that copying parameters that have _displayName set need
2680        // to export _displayName.
2681        // See: http://bugzilla.ecoinformatics.org/show_bug.cgi?id=3361
2682        if (_displayName != null) {
2683            output.write(_getIndentPrefix(depth) + "<display name=\"");
2684            output.write(StringUtilities.escapeForXML(_displayName));
2685            output.write("\"/>\n");
2686        }
2687
2688        // Callers of this method should hold read access
2689        // so as to avoid ConcurrentModificationException.
2690        if (_attributes != null) {
2691            Iterator<?> attributes = _attributes.elementList().iterator();
2692
2693            while (attributes.hasNext()) {
2694                Attribute attribute = (Attribute) attributes.next();
2695                attribute.exportMoML(output, depth);
2696            }
2697        }
2698    }
2699
2700    /** Get an object with the specified name in the specified container.
2701     *  The type of object sought is an instance of the same class as
2702     *  this object.  In this base class, return null, as there
2703     *  is no containment mechanism. Derived classes should override this
2704     *  method to return an object of their same type.
2705     *  @param relativeName The name relative to the container.
2706     *  @param container The container expected to contain the object.
2707     *  @return null.
2708     *  @exception IllegalActionException If the object exists
2709     *   and has the wrong class. Not thrown in this base class.
2710     */
2711    protected NamedObj _getContainedObject(NamedObj container,
2712            String relativeName) throws IllegalActionException {
2713        return null;
2714    }
2715
2716    /** Return a number of spaces that is proportional to the argument.
2717     *  If the argument is negative or zero, return an empty string.
2718     *  @param level The level of indenting represented by the spaces.
2719     *  @return A string with zero or more spaces.
2720     */
2721    protected static String _getIndentPrefix(int level) {
2722        return StringUtilities.getIndentPrefix(level);
2723    }
2724
2725    /** Return true if describing this class in MoML is redundant.
2726     *  This will return true if setPersistent() has been called
2727     *  with argument false, irrespective of other conditions.
2728     *  If setPersistent() has not been called, or has been called
2729     *  with argument true, then things are more complicated.
2730     *  If the <i>depth</i> argument is 0 or if this object is
2731     *  not derived, then this method returns false, indicating
2732     *  that MoML should be exported. Otherwise, whether to export
2733     *  MoML depends on whether the MoML specifies information
2734     *  that should be created by propagation rather than explicitly
2735     *  represented in MoML.  If this is a derived object, then whether
2736     *  its information can be created by propagation depends on whether
2737     *  the object from which that propagation would occur is included
2738     *  in the MoML, which depends on the <i>depth</i> argument.
2739     *  This method uses the <i>depth</i> argument to determine whether
2740     *  the exported MoML both contains an object that implies the
2741     *  existence of this object and contains an object that implies
2742     *  the value of this object.  If both conditions are satisfied,
2743     *  then it returns false.  Finally, if we haven't already
2744     *  returned false, then check all the contained objects, and
2745     *  if any of them requires a MoML description, then return false.
2746     *  Otherwise, return true.
2747     *  @param depth The depth of the requested MoML.
2748     *  @return Return true to suppress MoML export.
2749     */
2750    protected boolean _isMoMLSuppressed(int depth) {
2751        // Check whether suppression of MoML has been explicitly
2752        // requested.
2753        if (_isPersistent != null) {
2754            return !_isPersistent.booleanValue();
2755        }
2756
2757        // Object is persistent, but export may still not
2758        // be required since the structure and values might
2759        // be implied by inheritance. However, if that
2760        // inheritance occurs above the level of the hierarchy
2761        // where we are doing the export, then we need to
2762        // give structure and values anyway. Otherwise, for
2763        // example, copy and paste won't work properly.
2764        if (_derivedLevel > depth) {
2765            // Object is either not derived or the derivation occurs
2766            // above in the hierarchy where we are exporting.
2767            return false;
2768        }
2769
2770        // At this point, we know the object is implied.
2771        // However, we may need to export anyway because it
2772        // may have an overridden value.
2773        // Export MoML if the value of the object is
2774        // propagated in but from outside the scope
2775        // of the export.
2776        if (_override != null) {
2777            // Export MoML if the value of the object is
2778            // propagated in but from outside the scope
2779            // of the export.
2780            if (_override.size() > depth + 1) {
2781                return false;
2782            }
2783
2784            // Export MoML if the value has been set directly.
2785            if (_override.size() == 1 && _override.get(0).intValue() == 0) {
2786                return false;
2787            }
2788        }
2789
2790        // If any contained object wishes to have
2791        // MoML exported, then this object will export MoML.
2792        Iterator<?> objects = containedObjectsIterator();
2793
2794        while (objects.hasNext()) {
2795            NamedObj object = (NamedObj) objects.next();
2796
2797            if (!object._isMoMLSuppressed(depth + 1)) {
2798                return false;
2799            }
2800        }
2801
2802        // If we get here, then this object is a derived object
2803        // whose value is defined somewhere within the scope
2804        // of the export, and all its contained
2805        // objects do not need to export MoML. In this case,
2806        // it is OK to suppress MoML.
2807        return true;
2808    }
2809
2810    /** Mark the contents of this object as being derived objects.
2811     *  Specifically, the derivation depth of the immediately contained
2812     *  objects is set to one greater than the <i>depth</i> argument,
2813     *  and then this method is called on that object with an argument
2814     *  one greater than the <i>depth</i> argument. For the contained
2815     *  objects, this will also cancel any previous call to
2816     *  setPersistent(true), since it's a derived object.
2817     *  @param depth The derivation depth for this object, which
2818     *   should be 0 except on recursive calls.
2819     *  @see #setDerivedLevel(int)
2820     */
2821    protected void _markContentsDerived(int depth) {
2822        depth = depth + 1;
2823
2824        Iterator<?> objects = lazyContainedObjectsIterator();
2825
2826        while (objects.hasNext()) {
2827            NamedObj containedObject = (NamedObj) objects.next();
2828            // NOTE: Do not invoke setDerivedLevel() because that
2829            // method nulls the _override field.
2830            // containedObject.setDerivedLevel(depth);
2831            if (depth < containedObject._derivedLevel) {
2832                containedObject._derivedLevel = depth;
2833            }
2834            containedObject._markContentsDerived(depth);
2835
2836            // If this object has previously had
2837            // persistence set to true (e.g., it was
2838            // cloned from an object that had persistence
2839            // set to true), then override that and
2840            // reset to where persistence is unspecified.
2841            if (containedObject._isPersistent != null
2842                    && containedObject._isPersistent.booleanValue()) {
2843                containedObject._isPersistent = null;
2844            }
2845        }
2846    }
2847
2848    /** If any hierarchy listeners are registered, notify them
2849     *  that a change has occurred in the hierarchy.
2850     *  @exception IllegalActionException If the change to the
2851     *   hierarchy is not acceptable to the listener.
2852     *  @see #addHierarchyListener(HierarchyListener)
2853     *  @see HierarchyListener
2854     */
2855    protected void _notifyHierarchyListenersAfterChange()
2856            throws IllegalActionException {
2857        if (_hierarchyListeners != null) {
2858            // The hierarchy has changed. Add all hierarchy listeners
2859            // up the new hierarchy. This should be done before notification
2860            // because notification may result in exceptions.
2861            NamedObj container = getContainer();
2862            if (container != null) {
2863                Iterator<WeakReference<HierarchyListener>> listeners = _hierarchyListeners
2864                        .iterator();
2865                while (listeners.hasNext()) {
2866                    WeakReference<HierarchyListener> reference = listeners
2867                            .next();
2868                    container.addHierarchyListener(reference.get());
2869                }
2870            }
2871
2872            Iterator<WeakReference<HierarchyListener>> listeners = _hierarchyListeners
2873                    .iterator();
2874            while (listeners.hasNext()) {
2875                WeakReference<HierarchyListener> reference = listeners.next();
2876                reference.get().hierarchyChanged();
2877                //for (HierarchyListener listener : _hierarchyListeners) {
2878                //    listener.hierarchyChanged();
2879            }
2880        }
2881    }
2882
2883    /** If any hierarchy listeners are registered, notify them
2884     *  that a change is about to occur in the hierarchy.
2885     *  @exception IllegalActionException If changing the
2886     *   hierarchy is not acceptable to the listener.
2887     *  @see #addHierarchyListener(HierarchyListener)
2888     *  @see HierarchyListener
2889     */
2890    protected void _notifyHierarchyListenersBeforeChange()
2891            throws IllegalActionException {
2892        if (_hierarchyListeners != null) {
2893            Iterator<WeakReference<HierarchyListener>> listeners = _hierarchyListeners
2894                    .iterator();
2895            while (listeners.hasNext()) {
2896                WeakReference<HierarchyListener> reference = listeners.next();
2897                reference.get().hierarchyWillChange();
2898            }
2899            // If changing the hierarchy is acceptable to all listeners,
2900            // then we get to here. At this point, we should remove all
2901            // listeners from containers above us in the hierarchy, to
2902            // re-add them after the change in the hierarchy is complete.
2903            NamedObj container = getContainer();
2904            if (container != null) {
2905                Iterator<WeakReference<HierarchyListener>> listeners2 = _hierarchyListeners
2906                        .iterator();
2907                while (listeners2.hasNext()) {
2908                    WeakReference<HierarchyListener> reference = listeners2
2909                            .next();
2910                    container.removeHierarchyListener(reference.get());
2911                }
2912            }
2913        }
2914    }
2915
2916    /** Propagate existence of this object to the
2917     *  specified object. The specified object is required
2918     *  to be an instance of the same class as the container
2919     *  of this one, or an exception will be thrown. In this
2920     *  base class, this object is cloned, and its name
2921     *  is set to the same as this object.
2922     *  Derived classes with a setContainer() method are
2923     *  responsible for ensuring that this returned object
2924     *  has its container set to the specified container.
2925     *  This base class ensures that the returned object
2926     *  is in the same workspace as the container.
2927     *  <p>
2928     *  NOTE: Any object that creates objects in its
2929     *  constructor that it does not contain must override
2930     *  this method and call propagateExistence() on those
2931     *  objects. Otherwise, those objects will not be
2932     *  propagated to subclasses or instances when this
2933     *  object is contained by a class definition.
2934     *  See PortParameter for an example.
2935     *  @param container Object to contain the new object.
2936     *  @exception IllegalActionException If the object
2937     *   cannot be cloned.
2938     *  @return A new object of the same class and name
2939     *   as this one.
2940     */
2941    protected NamedObj _propagateExistence(NamedObj container)
2942            throws IllegalActionException {
2943        try {
2944            // Look for error condition.
2945            if (container == null) {
2946                throw new IllegalActionException(this,
2947                        "Attempting to propagate into a null container");
2948            }
2949            return (NamedObj) clone(container.workspace());
2950        } catch (CloneNotSupportedException e) {
2951            throw new IllegalActionException(this, e,
2952                    "Failed to propagate instance.");
2953        }
2954    }
2955
2956    /** Propagate the value of this object (if any) to the
2957     *  specified object. The specified object is required
2958     *  to be an instance of the same class as this one, or
2959     *  an exception will be thrown. In this base class,
2960     *  there is no value, and so nothing needs to be done.
2961     *  Derived classes that have values should override
2962     *  this method.
2963     *  @param destination Object to which to propagate the
2964     *   value.
2965     *  @exception IllegalActionException If the value cannot
2966     *   be propagated.
2967     */
2968    protected void _propagateValue(NamedObj destination)
2969            throws IllegalActionException {
2970    }
2971
2972    /** Remove the given attribute.
2973     *  If there is no such attribute, do nothing.
2974     *  This method is write-synchronized on the workspace and increments its
2975     *  version. It should only be called by setContainer() in Attribute.
2976     *  @param param The attribute to be removed.
2977     */
2978    protected void _removeAttribute(Attribute param) {
2979        try {
2980            _workspace.getWriteAccess();
2981            _attributes.remove(param);
2982
2983            if (_debugging) {
2984                _debug("Removed attribute", param.getName(), "from",
2985                        getFullName());
2986            }
2987        } finally {
2988            _workspace.doneWriting();
2989        }
2990    }
2991
2992    /** Remove attribute from list of attributes.
2993     *  @param param Attribute to remove.
2994     */
2995    public void removeAttribute(Attribute param) {
2996        _removeAttribute(param);
2997    }
2998
2999    /** Split the specified name at the first period and return the
3000     *  two parts as a two-element array.  If there is no period, the second
3001     *  element is null.
3002     *  @param name The name to split.
3003     *  @return The name before and after the first period as a two-element
3004     *   array.
3005     */
3006    protected static final String[] _splitName(String name) {
3007        String[] result = new String[2];
3008        int period = name.indexOf(".");
3009
3010        if (period < 0) {
3011            result[0] = name;
3012        } else {
3013            result[0] = name.substring(0, period);
3014            result[1] = name.substring(period + 1);
3015        }
3016
3017        return result;
3018    }
3019
3020    /** Return a string that is identical to the specified string
3021     *  except any trailing digits are removed.
3022     *  @param string The string to strip of its numeric suffix.
3023     *  @return A string with no numeric suffix.
3024     */
3025    protected static String _stripNumericSuffix(String string) {
3026        int length = string.length();
3027        char[] chars = string.toCharArray();
3028
3029        for (int i = length - 1; i >= 0; i--) {
3030            char current = chars[i];
3031
3032            if (Character.isDigit(current)) {
3033                length--;
3034            } else {
3035                //if (current == '_') {
3036                //    length--;
3037                //}
3038                // Found a non-numeric, so we are done.
3039                break;
3040            }
3041        }
3042
3043        if (length < string.length()) {
3044            // Some stripping occurred.
3045            char[] result = new char[length];
3046            System.arraycopy(chars, 0, result, 0, length);
3047            return new String(result);
3048        } else {
3049            return string;
3050        }
3051    }
3052
3053    /** Validate attributes deeply contained by this object if they
3054     *  implement the Settable interface by calling their validate() method.
3055     *  Errors that are triggered by this validation are handled by calling
3056     *  handleModelError().
3057     *  @param attributesValidated A collection of Settables that have
3058     *  already been validated.  For example, Settables that implement
3059     *  the ShareableSettable interface are validated only once.
3060     *  @see #handleModelError(NamedObj context, IllegalActionException exception)
3061     *  @exception IllegalActionException If there is a problem validating
3062     *  the deeply contained attributes.
3063     */
3064    protected void _validateSettables(Collection attributesValidated)
3065            throws IllegalActionException {
3066        Iterator<Settable> attributes = attributeList(Settable.class)
3067                .iterator();
3068        while (attributes.hasNext()) {
3069            Settable attribute = attributes.next();
3070            if (attributesValidated.contains(attribute)) {
3071                continue;
3072            }
3073            try {
3074                Collection<Settable> validated = attribute.validate();
3075                if (validated != null) {
3076                    attributesValidated.addAll(validated);
3077                }
3078                attributesValidated.add(attribute);
3079            } catch (IllegalActionException ex) {
3080                if (!handleModelError(this, ex)) {
3081                    throw ex;
3082                }
3083            }
3084            ((NamedObj) attribute)._validateSettables(attributesValidated);
3085        }
3086    }
3087
3088    ///////////////////////////////////////////////////////////////////
3089    ////                         protected variables               ////
3090
3091    /** A list of weak references to change listeners. */
3092    protected List _changeListeners;
3093
3094    /** Object for locking accesses to change request list and status.
3095     *  NOTE: We could have used _changeRequests or _changeListeners,
3096     *  but those lists are only created when needed.  A simple
3097     *  Object here is presumably cheaper than a list, but it is
3098     *  truly unfortunate to have to carry this in every NamedObj.
3099     */
3100    protected Object _changeLock = new SerializableObject();
3101
3102    /** A list of pending change requests. */
3103    protected List _changeRequests;
3104
3105    /** Flag that is true if there are debug listeners. */
3106    protected boolean _debugging = false;
3107
3108    /** The list of DebugListeners registered with this object.
3109     *  NOTE: Because of the way we synchronize on this object, it should
3110     *  never be reset to null after the first list is created.
3111     */
3112    protected LinkedList _debugListeners = null;
3113
3114    /** Flag indicating that we should not immediately
3115     *  execute a change request.
3116     */
3117    protected transient boolean _deferChangeRequests = false;
3118
3119    /** The MoML element name. This defaults to "entity".
3120     *  Subclasses that wish this to be different should set it
3121     *  in their constructor, or override getElementName()
3122     *  to return the desired value.
3123     */
3124    protected String _elementName = "entity";
3125
3126    /** Boolean variable to indicate the persistence of the object.
3127     *  If this is null (the default), then instances of NamedObj are
3128     *  persistent unless they are inferrable through propagation.
3129     *  We use Boolean here rather than boolean because a null value
3130     *  is used to indicate that no persistence has been specified.
3131     */
3132    protected Boolean _isPersistent = null;
3133
3134    /** The workspace for this object.
3135     *  This should be set by the constructor and never changed.
3136     */
3137    protected Workspace _workspace;
3138
3139    /** Flag that is true if detailed debug information is necessary.
3140     */
3141    protected boolean _verbose = false;
3142
3143    ///////////////////////////////////////////////////////////////////
3144    ////                         private methods                   ////
3145
3146    /** Return a list of derived objects. If the <i>propagate</i>
3147     *  argument is true, then this list will contain only those derived
3148     *  objects whose values are not overridden and that are not
3149     *  shadowed by objects whose values are overridden. Also, if
3150     *  that argument is true, then the value of this object is
3151     *  propagated to those returned objects during the construction
3152     *  of the list. This method is read-synchronized on the workspace.
3153     *  If the <i>force</i> argument is true, then if an expected
3154     *  derived object does not exist, then it is created by calling
3155     *  the _propagateExistence() protected method.
3156     *  @param visited A set of objects that have previously been
3157     *   visited. This should be non-null only on the recursive calls
3158     *   to this method.
3159     *  @param propagate True to propagate the value of this object
3160     *   (if any) to derived objects that have not been overridden
3161     *   while the list is being constructed.
3162     *  @param force Force derived objects to exist where they should
3163     *   be if they do not already exist.
3164     *  @param context The context (this except in recursive calls).
3165     *  @param depth The depth (0 except in recursive calls).
3166     *  @param relativeName The name of the object relative to the
3167     *   context (null except in recursive calls).
3168     *  @param override The list of override breadths (one per depth).
3169     *   If propagate is true, then this should be a list with
3170     *   a single Integer 0 for outside callers, and otherwise it
3171     *   should be null.
3172     *  @return A list of instances of the same class as this object
3173     *   which are derived from
3174     *   this object. The list is empty in this base class, but
3175     *   subclasses that override _getContainedObject() can
3176     *   return non-empty lists.
3177     *  @exception IllegalActionException If propagate is true
3178     *   and propagation fails.
3179     */
3180    private List<NamedObj> _getDerivedList(Collection<NamedObj> visited,
3181            boolean propagate, boolean force, NamedObj context, int depth,
3182            List<Integer> override, String relativeName)
3183            throws IllegalActionException {
3184        try {
3185            workspace().getReadAccess();
3186
3187            LinkedList<NamedObj> result = new LinkedList<NamedObj>();
3188
3189            // We may have visited this container already, in
3190            // which case the propagation has occurred already.
3191            // It should not occur again because the first occurrence
3192            // would have been from an object that propagated to
3193            // this occurrence. A similar propagation will occur
3194            // in propagation from the container, and for propageting
3195            // to work in that context, we must not issue the
3196            // change from here.  There is also no need to
3197            // go any further up the containment tree, since
3198            // the previous occurrence would have taken care of that.
3199            if (visited == null) {
3200                visited = new HashSet<NamedObj>();
3201            } else {
3202                if (visited.contains(context)) {
3203                    return result;
3204                }
3205            }
3206
3207            visited.add(context);
3208
3209            // Need to do deepest propagations
3210            // (those closest to the root of the tree) first.
3211            NamedObj container = context.getContainer();
3212
3213            if (container != null) {
3214                String newRelativeName;
3215
3216                if (relativeName == null) {
3217                    newRelativeName = context.getName();
3218                } else {
3219                    newRelativeName = context.getName() + "." + relativeName;
3220                }
3221
3222                // Create a new override list to pass to the container.
3223                List<Integer> newOverride = null;
3224
3225                if (propagate) {
3226                    newOverride = new LinkedList<Integer>(override);
3227
3228                    // If the override list is not long enough for the
3229                    // new depth, make it long enough. It should at most
3230                    // be one element short.
3231                    if (newOverride.size() <= depth + 1) {
3232                        newOverride.add(Integer.valueOf(0));
3233                    }
3234                }
3235
3236                result.addAll(_getDerivedList(visited, propagate, force,
3237                        container, depth + 1, newOverride, newRelativeName));
3238            }
3239
3240            if (!(context instanceof Instantiable)) {
3241                // This level can't possibly defer, so it has
3242                // nothing to add.
3243                return result;
3244            }
3245
3246            // Extract the current breadth from the list.
3247            int myBreadth = 0;
3248
3249            if (propagate) {
3250                myBreadth = override.get(depth).intValue();
3251            }
3252
3253            // Iterate over the children.
3254            List<?> othersList = ((Instantiable) context).getChildren();
3255            if (othersList != null) {
3256                Iterator<?> others = othersList.iterator();
3257
3258                while (others.hasNext()) {
3259                    WeakReference<?> reference = (WeakReference<?>) others
3260                            .next();
3261                    NamedObj other = (NamedObj) reference.get();
3262                    if (other != null) {
3263                        // Found a deferral.
3264                        // Look for an object with the relative name.
3265                        NamedObj candidate = other;
3266
3267                        if (relativeName != null) {
3268                            candidate = _getContainedObject(other,
3269                                    relativeName);
3270                        }
3271
3272                        if (candidate == null) {
3273                            if (force) {
3274                                // Need to get the container.
3275                                // Is there a better way than parsing
3276                                // the relativeName?
3277                                NamedObj remoteContainer = other;
3278                                int lastPeriod = relativeName.lastIndexOf(".");
3279
3280                                if (lastPeriod > 0) {
3281                                    String containerName = relativeName
3282                                            .substring(0, lastPeriod);
3283                                    // NOTE: The following may return null
3284                                    // if the propagation hasn't occurred yet.
3285                                    // This happens when invoking createHierarchy
3286                                    // in classes. It is a bug if propagation of the
3287                                    // container hasn't happened.
3288                                    remoteContainer = getContainer()
3289                                            ._getContainedObject(other,
3290                                                    containerName);
3291                                }
3292
3293                                candidate = _propagateExistence(
3294                                        remoteContainer);
3295
3296                                // Indicate that the existence of the
3297                                // candidate is implied by a
3298                                // parent-child relationship at the
3299                                // current depth. NOTE: Do not use setDerivedLevel()
3300                                // because that method resets the _override field.
3301                                // candidate.setDerivedLevel(depth);
3302                                if (depth < candidate._derivedLevel) {
3303                                    candidate._derivedLevel = depth;
3304                                }
3305
3306                                candidate._markContentsDerived(depth);
3307                                candidate._adjustOverride(depth);
3308                            } else {
3309                                // No candidate and no error.
3310                                // We can reach this line if this method
3311                                // is called during construction of an object
3312                                // in a class definition, before propagation has
3313                                // occurred. For example, some constructors call
3314                                // moveToLast() or moveToFirst() on attributes.
3315                                // These methods normally propagate the change
3316                                // to instances and derived classes. But if
3317                                // those instances and derived classes have not
3318                                // yet had the object propagated to them, then
3319                                // this will fail.
3320                                continue;
3321                                /* NOTE: This used to throw the following exception,
3322                                 * but this exception was spurious.
3323                                 * To test, create a class an an instance, then
3324                                 * drop an SDFDirector into the class. This used
3325                                 * to result in this exception being thrown.
3326                                 throw new InternalErrorException("Expected "
3327                                 + other.getFullName()
3328                                 + " to contain an object named "
3329                                 + relativeName + " of type "
3330                                 + getClass().toString());
3331                                 */
3332                            }
3333                        }
3334
3335                        // We may have done this already.  Check this
3336                        // by finding the object that will be affected by
3337                        // this propagation.
3338                        if (visited.contains(candidate)) {
3339                            // Skip this candidate. We've done it already.
3340                            // Continue to the next deferral in the list.
3341                            continue;
3342                        }
3343
3344                        List<Integer> newOverride = null;
3345
3346                        // If the propagate argument is true, then
3347                        // determine whether the candidate object is
3348                        // shadowed, and if it is not, then apply the
3349                        // propagation change to it.
3350                        if (propagate) {
3351                            // Is it shadowed?  Create a new override
3352                            // list to pass to the candidate.
3353                            newOverride = new LinkedList<Integer>(override);
3354                            newOverride.set(depth,
3355                                    Integer.valueOf(myBreadth + 1));
3356
3357                            if (_isShadowed(candidate._override, newOverride)) {
3358                                // Yes it is.
3359                                continue;
3360                            }
3361
3362                            // FIXME: If the following throws an
3363                            // exception, we have to somehow restore
3364                            // values of previous propagations.
3365                            _propagateValue(candidate);
3366
3367                            // Set the override.
3368                            candidate._override = newOverride;
3369                        }
3370
3371                        result.add(candidate);
3372
3373                        // Add objects derived from this candidate.
3374                        // Note that depth goes back to zero, since the
3375                        // existence of objects derived from this candidate
3376                        // will be determined by the depth of propagation from
3377                        // this candidate.
3378                        result.addAll(candidate._getDerivedList(visited,
3379                                propagate, force, candidate, 0, newOverride,
3380                                null));
3381
3382                        // Note that the above recursive call will
3383                        // add the candidate to the HashSet, so we
3384                        // don't have to do that here.
3385                    }
3386                }
3387            }
3388
3389            return result;
3390        } finally {
3391            workspace().doneReading();
3392        }
3393    }
3394
3395    /** Return true if the first argument (an _override list)
3396     *  indicates that the object owning that override list should
3397     *  be shadowed relative to a change made via the path defined by
3398     *  the second override list.
3399     *  @param candidate The override list of the candidate for a change.
3400     *  @param changer The override list for a path for the change.
3401     *  @return True if the candidate is shadowed.
3402     */
3403    private boolean _isShadowed(List<Integer> candidate,
3404            List<Integer> changer) {
3405        if (candidate == null) {
3406            return false;
3407        }
3408
3409        if (changer == null) {
3410            // Probably it makes no sense for the second argument
3411            // to be null, but in case it is, we declare that there
3412            // is shadowing if it is.
3413            return true;
3414        }
3415
3416        // If the the candidate object has a value that has been
3417        // set more locally (involving fewer levels of the hierarchy)
3418        // than the proposed changer path, then it is shadowed.
3419        if (candidate.size() < changer.size()) {
3420            return true;
3421        }
3422
3423        // If the sizes are equal, then we need to compare the
3424        // elements of the list, starting with the last.
3425        if (candidate.size() == changer.size()) {
3426            int index = candidate.size() - 1;
3427
3428            while (index >= 0
3429                    && candidate.get(index).equals(changer.get(index))) {
3430                index--;
3431            }
3432
3433            if (index < 0) {
3434                // The two lists are identical, so there be no shadowing.
3435                return false;
3436            } else {
3437                int candidateBreadth = candidate.get(index).intValue();
3438                int changerBreadth = changer.get(index).intValue();
3439
3440                if (candidateBreadth < changerBreadth) {
3441                    return true;
3442                }
3443            }
3444        }
3445
3446        return false;
3447    }
3448
3449    /** Update the decorator attributes.
3450     *  This method finds all decorators in scope (above this object in the hierarchy),
3451     *  and for each decorator that can decorate this object, creates an entry in
3452     *  _decoratorAttributes.
3453     *  @see ptolemy.kernel.util.Decorator#createDecoratorAttributes(NamedObj)
3454     *  @see #getDecoratorAttributes(Decorator)
3455     *  @exception IllegalActionException If thrown while checking to
3456     *  see if the decorator is global, while getting the decorator or
3457     *  while creating a decorator.
3458     */
3459    private void _updateDecoratorAttributes() throws IllegalActionException {
3460        synchronized (_decoratorAttributes) {
3461            if (workspace().getVersion() != _decoratorAttributesVersion) {
3462                _decoratorAttributes.clear();
3463                // Find all the decorators in scope, and store them indexed by full name.
3464                Set<Decorator> decorators = new HashSet<Decorator>();
3465                NamedObj container = getContainer();
3466                boolean crossedOpaqueBoundary = false;
3467                while (container != null) {
3468                    List<Decorator> localDecorators = container
3469                            ._containedDecorators();
3470                    for (Decorator decorator : localDecorators) {
3471                        if (!crossedOpaqueBoundary
3472                                || decorator.isGlobalDecorator()) {
3473                            decorators.add(decorator);
3474                        }
3475                    }
3476                    if (container instanceof CompositeEntity
3477                            && ((CompositeEntity) container).isOpaque()) {
3478                        crossedOpaqueBoundary = true;
3479                    }
3480                    container = container.getContainer();
3481                }
3482
3483                // Find all the instances of DecoratorAttributes contained by this NamedObj,
3484                // and put these in the cache, associated with the right decorator.
3485                List<DecoratorAttributes> decoratorAttributes = attributeList(
3486                        DecoratorAttributes.class);
3487                for (DecoratorAttributes decoratorAttribute : decoratorAttributes) {
3488                    Decorator decorator = decoratorAttribute.getDecorator();
3489                    // If the decorator is not found, decorator will be null.
3490                    // In that case, we leave the decoratorAttributes for now (in case the
3491                    // decorator reappears, e.g. through an undo). When the model is saved,
3492                    // decoratorAttributes will disappear.
3493                    if (decorator != null) {
3494                        // Since this decorator is now associated with a decoratorAttribute, remove it.
3495                        boolean removed = decorators.remove(decorator);
3496                        // If the above returns null, then the decorator is no longer in scope, so
3497                        // we do not add it to the cache. We do not want to remove the decoratorAttributes,
3498                        // however, until the model is saved.
3499                        if (removed) {
3500                            // The decorator was found. Put in cache.
3501                            _decoratorAttributes.put(decorator,
3502                                    decoratorAttribute);
3503                        }
3504                    }
3505                }
3506
3507                // For each remaining decorator, if it decorates this NamedObj, create an entry.
3508                for (Decorator decorator : decorators) {
3509                    DecoratorAttributes attribute = decorator
3510                            .createDecoratorAttributes(this);
3511                    _decoratorAttributes.put(decorator, attribute);
3512                }
3513
3514                _decoratorAttributesVersion = workspace().getVersion();
3515            }
3516        }
3517    }
3518
3519    ///////////////////////////////////////////////////////////////////
3520    ////                         friendly variables                 ////
3521    // The following is friendly to support the move* methods of
3522    // Attribute.
3523
3524    /** The Attributes attached to this object. */
3525    NamedList _attributes;
3526
3527    ///////////////////////////////////////////////////////////////////
3528    ////                         private variables                 ////
3529
3530    /** The class name for MoML exports. */
3531    private String _className;
3532
3533    /** A map from decorators to the decorated attributes which which each decorator has decorated this NamedObj.
3534     *  This is a cache that may not be complete. To get a complete list of decorated attributes, get a list
3535     *  of attributes of type DecoratorAttributes.
3536     */
3537    private Map<Decorator, DecoratorAttributes> _decoratorAttributes = new HashMap<Decorator, DecoratorAttributes>();
3538
3539    /** Workspace version for decorated attributes. */
3540    private long _decoratorAttributesVersion = -1L;
3541
3542    /** Instance of a workspace that can be used if no other
3543     *  is specified.
3544     */
3545    private static Workspace _DEFAULT_WORKSPACE = new Workspace();
3546
3547    // Variable indicating at what level above this object is derived.
3548    // Integer.MAX_VALUE indicates that it is not derived.
3549    private int _derivedLevel = Integer.MAX_VALUE;
3550
3551    /** The display name, if set. */
3552    private String _displayName;
3553
3554    // Cached value of the full name.
3555    private String _fullNameCache;
3556
3557    // Version of the workspace when cache last updated.
3558    private long _fullNameVersion = -1;
3559
3560    /** List of hierarchy listeners, if any. */
3561    private HashSet<WeakReference<HierarchyListener>> _hierarchyListeners;
3562
3563    // The model error handler, if there is one.
3564    private ModelErrorHandler _modelErrorHandler = null;
3565
3566    /** The name */
3567    private String _name;
3568
3569    /** List indicating whether and how this derived
3570     *  object has been modified.
3571     */
3572    private List<Integer> _override = null;
3573
3574    /** The value for the source MoML attribute. */
3575    private String _source;
3576
3577    ///////////////////////////////////////////////////////////////////
3578    ////                         inner classes                     ////
3579
3580    /** This class is an iterator over all the contained objects
3581     *  (all instances of NamedObj). In this base class, the contained
3582     *  objects are attributes.  In derived classes, they include
3583     *  ports, relations, and entities as well.
3584     */
3585    protected class ContainedObjectsIterator implements Iterator {
3586
3587        /** Create an iterator over all the contained objects. */
3588        public ContainedObjectsIterator() {
3589            super();
3590            // This iterator gets called quite a bit, so at Kevin Ruland's
3591            // suggestion, we move instantiation of the iterator
3592            // into the constructor so that hasNext() and next() don't
3593            // have to check if _attributeListIterator is null each time.
3594            _attributeListIterator = attributeList().iterator();
3595        }
3596
3597        /** Return true if the iteration has more elements.
3598         *  In this base class, this returns true if there are more
3599         *  attributes.
3600         *  @return True if there are more attributes.
3601         */
3602        @Override
3603        public boolean hasNext() {
3604            return _attributeListIterator.hasNext();
3605        }
3606
3607        /** Return the next element in the iteration.
3608         *  In this base class, this is the next attribute.
3609         *  @return The next attribute.
3610         */
3611        @Override
3612        public Object next() {
3613            return _attributeListIterator.next();
3614        }
3615
3616        /** Throw a UnsupportedOperationException because remove() is not
3617         *  supported.  The reason is because this iterator calls
3618         *  attributeList().iterator(), which returns a NamedList that
3619         *  is unmodifiable.
3620         */
3621        @Override
3622        public void remove() {
3623            // Iterator requires a remove().
3624            throw new UnsupportedOperationException("remove() not supported "
3625                    + "because attributeList().iterator() returns a NamedList "
3626                    + "that is unmodifiable");
3627            //_attributeListIterator.remove();
3628        }
3629
3630        private Iterator<?> _attributeListIterator = null;
3631    }
3632
3633    /** Serializable version of the Java Object class. */
3634    @SuppressWarnings("serial")
3635    private static class SerializableObject extends Object
3636            implements Serializable {
3637        // FindBugs suggested making this class a static inner class:
3638        //
3639        // "This class is an inner class, but does not use its embedded
3640        // reference to the object which created it. This reference makes
3641        // the instances of the class larger, and may keep the reference
3642        // to the creator object alive longer than necessary. If
3643        // possible, the class should be made into a static inner class."
3644    }
3645}