001/* A base class for attributes to be attached to instances of NamedObj. 002 003 Copyright (c) 1998-2014 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.kernel.util; 029 030import java.util.Iterator; 031 032/////////////////////////////////////////////////////////////////// 033//// Attribute 034 035/** 036 Attribute is a base class for attributes to be attached to instances 037 of NamedObj. This base class is itself a NamedObj, with the only 038 extension being that it can have a container. The setContainer() 039 method puts this object on the list of attributes of the container. 040 041 @author Edward A. Lee, Neil Smyth 042 @version $Id$ 043 @since Ptolemy II 0.2 044 @Pt.ProposedRating Green (eal) 045 @Pt.AcceptedRating Green (johnr) 046 */ 047public class Attribute extends NamedObj { 048 /** Construct an attribute in the default workspace with an empty string 049 * as its name. 050 * The object is added to the directory of the workspace. 051 * Increment the version number of the workspace. 052 */ 053 public Attribute() { 054 super(); 055 _elementName = "property"; 056 } 057 058 /** Construct an attribute in the specified workspace with an empty 059 * string as a name. You can then change the name with setName(). 060 * If the workspace argument 061 * is null, then use the default workspace. 062 * The object is added to the directory of the workspace. 063 * Increment the version number of the workspace. 064 * @param workspace The workspace that will list the attribute. 065 */ 066 public Attribute(Workspace workspace) { 067 super(workspace); 068 _elementName = "property"; 069 } 070 071 /** Construct an attribute with the given name contained by the specified 072 * entity. The container argument must not be null, or a 073 * NullPointerException will be thrown. This attribute will use the 074 * workspace of the container for synchronization and version counts. 075 * If the name argument is null, then the name is set to the empty string. 076 * Increment the version of the workspace. 077 * @param container The container. 078 * @param name The name of this attribute. 079 * @exception IllegalActionException If the attribute is not of an 080 * acceptable class for the container, or if the name contains a period. 081 * @exception NameDuplicationException If the name coincides with 082 * an attribute already in the container. 083 */ 084 public Attribute(NamedObj container, String name) 085 throws IllegalActionException, NameDuplicationException { 086 this(container, name, true); 087 } 088 089 /** Construct an attribute with the given name contained by the specified 090 * entity. The container argument must not be null, or a 091 * NullPointerException will be thrown. This attribute will use the 092 * workspace of the container for synchronization and version counts. 093 * If the name argument is null, then the name is set to the empty string. 094 * Increment the version of the workspace. 095 * @param container The container. 096 * @param name The name of this attribute. 097 * @param incrementWorkspaceVersion False to not add this to the workspace 098 * or do anything else that might change the workspace version number. 099 * @exception IllegalActionException If the attribute is not of an 100 * acceptable class for the container, or if the name contains a period. 101 * @exception NameDuplicationException If the name coincides with 102 * an attribute already in the container. 103 */ 104 protected Attribute(NamedObj container, String name, 105 boolean incrementWorkspaceVersion) 106 throws IllegalActionException, NameDuplicationException { 107 super(container.workspace(), name, incrementWorkspaceVersion); 108 if (incrementWorkspaceVersion) { 109 setContainer(container); 110 } else { 111 // Get writeAccess to the workspace because we are updating 112 // the _attributes field of the container. 113 // See http://bugzilla.ecoinformatics.org/show_bug.cgi?id=3255 114 try { 115 _workspace.getWriteAccess(); 116 117 // Avoid methods that increment the workspace version. 118 if (container._attributes == null) { 119 container._attributes = new NamedList(); 120 } 121 container._attributes.append(this); 122 123 _container = container; 124 // Make sure even the debugging messages are unchanged. 125 if (container._debugging) { 126 container._debug("Added attribute", getName(), "to", 127 container.getFullName()); 128 } 129 } finally { 130 // Note that _doneTemporaryWriting 131 // does not increment the workspace version. 132 _workspace.doneTemporaryWriting(); 133 } 134 } 135 _elementName = "property"; 136 } 137 138 /////////////////////////////////////////////////////////////////// 139 //// public methods //// 140 141 /** Clone the object into the specified workspace. The new object is 142 * <i>not</i> added to the directory of that workspace (you must do this 143 * yourself if you want it there). 144 * The result is an attribute with no container. 145 * @param workspace The workspace for the cloned object. 146 * @exception CloneNotSupportedException Not thrown in this base class 147 * @return The new Attribute. 148 */ 149 @Override 150 public Object clone(Workspace workspace) throws CloneNotSupportedException { 151 Attribute newObject = (Attribute) super.clone(workspace); 152 newObject._container = null; 153 return newObject; 154 } 155 156 /** Move this object down by one in the list of attributes of 157 * its container. If this object is already last, do nothing. 158 * This method gets write access on workspace 159 * and increments the version. 160 * @return The index of the specified object prior to moving it, 161 * or -1 if it is not moved. 162 * @exception IllegalActionException If this object has 163 * no container. 164 */ 165 @Override 166 public int moveDown() throws IllegalActionException { 167 NamedObj container = getContainer(); 168 169 if (container == null) { 170 throw new IllegalActionException(this, "Has no container."); 171 } 172 173 try { 174 _workspace.getWriteAccess(); 175 176 int result = container._attributes.moveDown(this); 177 178 // Propagate. 179 Iterator derivedObjects = getDerivedList().iterator(); 180 181 while (derivedObjects.hasNext()) { 182 NamedObj derived = (NamedObj) derivedObjects.next(); 183 container = derived.getContainer(); 184 container._attributes.moveDown(derived); 185 } 186 187 return result; 188 } finally { 189 _workspace.doneWriting(); 190 } 191 } 192 193 /** Move this object to the first position in the list 194 * of attributes of the container. If this object is already first, 195 * do nothing. This method gets write access on workspace 196 * and increments the version. 197 * @return The index of the specified object prior to moving it, 198 * or -1 if it is not moved. 199 * @exception IllegalActionException If this object has 200 * no container. 201 */ 202 @Override 203 public int moveToFirst() throws IllegalActionException { 204 NamedObj container = getContainer(); 205 206 if (container == null) { 207 throw new IllegalActionException(this, "Has no container."); 208 } 209 210 try { 211 _workspace.getWriteAccess(); 212 213 int result = container._attributes.moveToFirst(this); 214 215 // Propagate. 216 Iterator derivedObjects = getDerivedList().iterator(); 217 218 while (derivedObjects.hasNext()) { 219 NamedObj derived = (NamedObj) derivedObjects.next(); 220 container = derived.getContainer(); 221 container._attributes.moveToFirst(derived); 222 } 223 224 return result; 225 } finally { 226 _workspace.doneWriting(); 227 } 228 } 229 230 /** Move this object to the specified position in the list 231 * of attributes of the container. If this object is already at 232 * the specified position, do nothing. This method gets write 233 * access on workspace and increments the version. 234 * @param index The zero based position to which this object is moved. 235 * 0 means the first position, 1 means the second position. 236 * @return The index of the specified object prior to moving it, 237 * or -1 if it is not moved. 238 * @exception IllegalActionException If this object has 239 * no container or if the index is out of bounds. 240 */ 241 @Override 242 public int moveToIndex(int index) throws IllegalActionException { 243 NamedObj container = getContainer(); 244 245 if (container == null) { 246 throw new IllegalActionException(this, "Has no container."); 247 } 248 249 try { 250 _workspace.getWriteAccess(); 251 252 int result = container._attributes.moveToIndex(this, index); 253 254 // Propagate. 255 Iterator derivedObjects = getDerivedList().iterator(); 256 257 while (derivedObjects.hasNext()) { 258 NamedObj derived = (NamedObj) derivedObjects.next(); 259 container = derived.getContainer(); 260 container._attributes.moveToIndex(derived, index); 261 } 262 263 return result; 264 } finally { 265 _workspace.doneWriting(); 266 } 267 } 268 269 /** Move this object to the last position in the list 270 * of attributes of the container. If this object is already last, 271 * do nothing. This method gets write access on workspace 272 * and increments the version. 273 * @return The index of the specified object prior to moving it, 274 * or -1 if it is not moved. 275 * @exception IllegalActionException If this object has 276 * no container. 277 */ 278 @Override 279 public int moveToLast() throws IllegalActionException { 280 NamedObj container = getContainer(); 281 282 if (container == null) { 283 throw new IllegalActionException(this, "Has no container."); 284 } 285 286 try { 287 _workspace.getWriteAccess(); 288 289 int result = container._attributes.moveToLast(this); 290 291 // Propagate. 292 Iterator derivedObjects = getDerivedList().iterator(); 293 294 while (derivedObjects.hasNext()) { 295 NamedObj derived = (NamedObj) derivedObjects.next(); 296 container = derived.getContainer(); 297 container._attributes.moveToLast(derived); 298 } 299 300 return result; 301 } finally { 302 _workspace.doneWriting(); 303 } 304 } 305 306 /** Move this object up by one in the list of 307 * attributes of the container. If this object is already first, do 308 * nothing. This method gets write access on workspace 309 * and increments the version. 310 * @return The index of the specified object prior to moving it, 311 * or -1 if it is not moved. 312 * @exception IllegalActionException If this object has 313 * no container. 314 */ 315 @Override 316 public int moveUp() throws IllegalActionException { 317 NamedObj container = getContainer(); 318 319 if (container == null) { 320 throw new IllegalActionException(this, "Has no container."); 321 } 322 323 try { 324 _workspace.getWriteAccess(); 325 326 int result = container._attributes.moveUp(this); 327 328 // Propagate. 329 Iterator derivedObjects = getDerivedList().iterator(); 330 331 while (derivedObjects.hasNext()) { 332 NamedObj derived = (NamedObj) derivedObjects.next(); 333 container = derived.getContainer(); 334 container._attributes.moveUp(derived); 335 } 336 337 return result; 338 } finally { 339 _workspace.doneWriting(); 340 } 341 } 342 343 /** Get the NamedObj that this Attribute is attached to. 344 * @return The container, an instance of NamedObj. 345 * @see #setContainer(NamedObj) 346 */ 347 @Override 348 public NamedObj getContainer() { 349 return _container; 350 } 351 352 /** Specify the container NamedObj, adding this attribute to the 353 * list of attributes in the container. If the container already 354 * contains an attribute with the same name, then throw an exception 355 * and do not make any changes. Similarly, if the container is 356 * not in the same workspace as this attribute, throw an exception. 357 * If this attribute is already contained by the NamedObj, do nothing. 358 * If the attribute already has a container, remove 359 * this attribute from its attribute list first. Otherwise, remove 360 * it from the directory of the workspace, if it is there. 361 * If the argument is null, then remove it from its container. 362 * It is not added to the workspace directory, so this could result in 363 * this object being garbage collected. 364 * Note that since an Attribute is a NamedObj, it can itself have 365 * attributes. However, recursive containment is not allowed, where 366 * an attribute is an attribute of itself, or indirectly of any attribute 367 * it contains. This method is write-synchronized on the 368 * workspace and increments its version number. 369 * <p> 370 * Subclasses may constrain the type of container by overriding 371 * {@link #setContainer(NamedObj)}. 372 * @param container The container to attach this attribute to.. 373 * @exception IllegalActionException If this attribute is not of the 374 * expected class for the container, or it has no name, 375 * or the attribute and container are not in the same workspace, or 376 * the proposed container would result in recursive containment. 377 * @exception NameDuplicationException If the container already has 378 * an attribute with the name of this attribute. 379 * @see #getContainer() 380 */ 381 public void setContainer(NamedObj container) 382 throws IllegalActionException, NameDuplicationException { 383 if (container != null && _workspace != container.workspace()) { 384 throw new IllegalActionException(this, container, 385 "Cannot set container because workspaces are different."); 386 } 387 _checkContainer(container); 388 if (deepContains(container)) { 389 throw new IllegalActionException(this, container, 390 "Attempt to construct recursive containment " 391 + "of attributes"); 392 } 393 394 NamedObj previousContainer = getContainer(); 395 396 if (previousContainer == container) { 397 return; 398 } 399 400 _notifyHierarchyListenersBeforeChange(); 401 402 try { 403 _workspace.getWriteAccess(); 404 405 // Do this first, because it may throw an exception. 406 if (container != null) { 407 container._addAttribute(this); 408 409 if (previousContainer == null) { 410 _workspace.remove(this); 411 } 412 413 // We have successfully set a new container for this 414 // object. Mark it modified to ensure MoML export. 415 // FIXME: Inappropriate? 416 // setOverrideDepth(0); 417 } 418 419 _container = container; 420 421 if (previousContainer != null) { 422 previousContainer._removeAttribute(this); 423 } 424 425 if (container != null) { 426 // Transfer any queued change requests to the 427 // new container. There could be queued change 428 // requests if this component is deferring change 429 // requests. 430 if (_changeRequests != null) { 431 Iterator requests = _changeRequests.iterator(); 432 433 while (requests.hasNext()) { 434 ChangeRequest request = (ChangeRequest) requests.next(); 435 container.requestChange(request); 436 } 437 438 _changeRequests = null; 439 } 440 } 441 } finally { 442 try { 443 // Since we definitely notified the listeners 444 // before the change, we must definitely notify 445 // them after the change, even if the change caused 446 // some exceptions. Note that this too may trigger 447 // exceptions. 448 _notifyHierarchyListenersAfterChange(); 449 } finally { 450 _workspace.doneWriting(); 451 } 452 } 453 } 454 455 /** Set the name of the attribute. If there is already an attribute 456 * of the container with the same name, then throw a 457 * NameDuplicationException. 458 * @exception IllegalActionException If the name contains a period. 459 * @exception NameDuplicationException If there is already an 460 * attribute with the same name in the container. 461 */ 462 @Override 463 public void setName(String name) 464 throws IllegalActionException, NameDuplicationException { 465 if (name == null) { 466 name = ""; 467 } 468 469 NamedObj container = getContainer(); 470 471 if (container != null) { 472 Attribute another = container.getAttribute(name); 473 474 if (another != null && another != this) { 475 throw new NameDuplicationException(container, 476 "Name duplication: " + name); 477 } 478 } 479 480 super.setName(name); 481 } 482 483 /** Update the content of this attribute. 484 * In this base class, nothing is performed. 485 * Subclasses need to override this class to update the attribute. 486 * @exception InternalErrorException Not thrown in this base class. 487 */ 488 public void updateContent() throws InternalErrorException { 489 } 490 491 /////////////////////////////////////////////////////////////////// 492 //// protected methods //// 493 494 /** Check that the specified container is of a suitable class for 495 * this attribute. In this base class, this method returns immediately 496 * without doing anything. 497 * @param container The proposed container. 498 * @exception IllegalActionException If the container is not of 499 * an acceptable class. Not thrown in this base class. 500 */ 501 protected void _checkContainer(NamedObj container) 502 throws IllegalActionException { 503 } 504 505 /** Get an attribute with the specified name in the specified container. 506 * The type of object sought is an instance of the same class as 507 * this object. The returned object is assured of being an 508 * instance of the same class as this object. 509 * @param relativeName The name relative to the container. 510 * @param container The container expected to contain the object. 511 * @return An object of the same class as this object, or null 512 * if there is none. 513 * @exception IllegalActionException If the object exists 514 * and has the wrong class. 515 */ 516 @Override 517 protected NamedObj _getContainedObject(NamedObj container, 518 String relativeName) throws IllegalActionException { 519 Attribute candidate = container.getAttribute(relativeName); 520 521 if (candidate != null && !getClass().isInstance(candidate)) { 522 throw new IllegalActionException(this, 523 "Expected " + candidate.getFullName() 524 + " to be an instance of " + getClass().getName() 525 + ", but it is " + candidate.getClass().getName()); 526 } 527 528 return candidate; 529 } 530 531 /** Propagate existence of this object to the 532 * specified object. This overrides the base class 533 * to set the container. 534 * @param container Object to contain the new object. 535 * @exception IllegalActionException If the object 536 * cannot be cloned. 537 * @return A new object of the same class and name 538 * as this one. 539 */ 540 @Override 541 protected NamedObj _propagateExistence(NamedObj container) 542 throws IllegalActionException { 543 try { 544 Attribute newObject = (Attribute) super._propagateExistence( 545 container); 546 newObject.setContainer(container); 547 return newObject; 548 } catch (NameDuplicationException e) { 549 throw new InternalErrorException(e); 550 } 551 } 552 553 /////////////////////////////////////////////////////////////////// 554 //// private variables //// 555 556 /** @serial Container of this attribute. */ 557 private NamedObj _container; 558}