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 * <<i>element</i> name="<i>name</i>" class="<i>classname</i>" source="<i>source</i>">> 878 * <i>body, determined by _exportMoMLContents()</i> 879 * </<i>element</i>> 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 * <?xml version="1.0" standalone="no"?> 889 * <!DOCTYPE entity PUBLIC "-//UC Berkeley//DTD MoML 1//EN" 890 * "http://ptolemy.eecs.berkeley.edu/xml/dtd/MoML_1.dtd"> 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}