001/* A parameter that has an associated port. 002 003 Copyright (c) 2002-2015 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.actor.parameters; 029 030import ptolemy.actor.Initializable; 031import ptolemy.actor.TypedActor; 032import ptolemy.data.StringToken; 033import ptolemy.data.Token; 034import ptolemy.data.expr.AbstractInitializableParameter; 035import ptolemy.kernel.ComponentEntity; 036import ptolemy.kernel.Entity; 037import ptolemy.kernel.Port; 038import ptolemy.kernel.util.Attribute; 039import ptolemy.kernel.util.IllegalActionException; 040import ptolemy.kernel.util.InternalErrorException; 041import ptolemy.kernel.util.KernelException; 042import ptolemy.kernel.util.Locatable; 043import ptolemy.kernel.util.Location; 044import ptolemy.kernel.util.NameDuplicationException; 045import ptolemy.kernel.util.NamedObj; 046import ptolemy.kernel.util.Workspace; 047 048/////////////////////////////////////////////////////////////////// 049//// PortParameter 050 051/** 052 <p>This parameter creates an associated port that can be used to update 053 the current value of the parameter. This parameter has two values, 054 which may not be equal, a <i>current value</i> and a <i>persistent value</i>. 055 The persistent value is returned by 056 getExpression() and is set by any of three different mechanisms:</p> 057 <ul> 058 <li> calling setExpression();</li> 059 <li> calling setToken(); and </li> 060 <li> specifying a value as a constructor argument.</li> 061 </ul> 062 <p> 063 All three of these will also set the current value, which is then 064 equal to the persistent value. 065 The current value is returned by get getToken() 066 and is set by any of two different mechanisms:</p> 067 <ul> 068 <li> calling setCurrentValue();</li> 069 <li> calling update() sets the current value if there is an associated 070 port, and that port has a token to consume; and</li> 071 </ul> 072 These two techniques do not change the persistent value, so after 073 these are used, the persistent value and current value may be different. 074 <p> 075 When the container for this parameter is initialized, the current 076 value of the parameter is reset to match the persistent value. 077 <p> 078 When using this parameter in an actor, care must be exercised 079 to call update() exactly once per firing prior to calling getToken(). 080 Each time update() is called, a new token will be consumed from 081 the associated port (if the port is connected and has a token). 082 If this is called multiple times in an iteration, it may result in 083 consuming tokens that were intended for subsequent iterations. 084 Thus, for example, update() should not be called in fire() and then 085 again in postfire(). Moreover, in some domains (such as DE), 086 it is essential that if a token is provided on a port, that it 087 is consumed. In DE, the actor will be repeatedly fired until 088 the token is consumed. Thus, it is an error to not call update() 089 once per iteration. For an example of an actor that uses this 090 mechanism, see Ramp.</p> 091 <p> 092 If this actor is placed in a container that does not implement 093 the TypedActor interface, then no associated port is created, 094 and it functions as an ordinary parameter. This is useful, 095 for example, if this is put in a library, where one would not 096 want the associated port to appear.</p> 097 098 <p>There are a few situations where PortParameter might not do what 099 you expect:</p> 100 101 <ol> 102 <li> If it is used in a transparent composite actor, then a token provided 103 to a PortParameter will never be read. A transparent composite actor 104 is one without a director. 105 <br>Workaround: Put a director in the composite.<br> 106 </li> 107 108 <li> Certain actors (such as the Integrator in CT) read parameter 109 values only during initialization. During initialization, a 110 PortParameter can only have a value set via the parameter (it 111 can't have yet received a token). So if the initial value of the 112 Integrator is set to the value of the PortParameter, then it will 113 see only the parameter value, never the value provided via the 114 port. 115 <br>Workaround: Use a RunCompositeActor to contain the model with the 116 Integrator. 117 </li> 118 119 </ol> 120 121 @see ptolemy.actor.lib.Ramp 122 @see ParameterPort 123 @author Edward A. Lee 124 @version $Id$ 125 @since Ptolemy II 3.0 126 @Pt.ProposedRating Green (eal) 127 @Pt.AcceptedRating Yellow (neuendor) 128 */ 129public class PortParameter extends AbstractInitializableParameter 130 implements Initializable { 131 132 /** Construct a parameter with the given name contained by the specified 133 * entity. The container argument must not be null, or a 134 * NullPointerException will be thrown. This parameter will create 135 * an associated port in the same container. 136 * @param container The container. 137 * @param name The name of the parameter. 138 * @exception IllegalActionException If the parameter is not of an 139 * acceptable class for the container. 140 * @exception NameDuplicationException If the name coincides with 141 * a parameter already in the container. 142 */ 143 public PortParameter(NamedObj container, String name) 144 throws IllegalActionException, NameDuplicationException { 145 this(container, name, (ParameterPort) null); 146 } 147 148 /** Construct a parameter with the given name contained by the specified 149 * entity. The container argument must not be null, or a 150 * NullPointerException will be thrown. This parameter will create 151 * an associated port in the same container. 152 * @param container The container. 153 * @param name The name of the parameter. 154 * @param port The associated ParameterPort, or null to create one. 155 * @exception IllegalActionException If the parameter is not of an 156 * acceptable class for the container. 157 * @exception NameDuplicationException If the name coincides with 158 * a parameter already in the container. 159 */ 160 protected PortParameter(NamedObj container, String name, ParameterPort port) 161 throws IllegalActionException, NameDuplicationException { 162 super(container, name); 163 _port = port; 164 if (port == null) { 165 // If we get to here, we know the container is a ComponentEntity, 166 // so the cast is safe. 167 Port existingPort = ((ComponentEntity) container).getPort(name); 168 if (existingPort instanceof ParameterPort) { 169 _port = (ParameterPort) existingPort; 170 ((ParameterPort) existingPort)._parameter = this; 171 } else { 172 _port = new ParameterPort((ComponentEntity) container, name, 173 this); 174 } 175 } 176 _setTypeConstraints(); 177 } 178 179 /** Construct a parameter with the given name contained by the specified 180 * entity. The container argument must not be null, or a 181 * NullPointerException will be thrown. This parameter will create 182 * an associated port in the same container. 183 * @param container The container. 184 * @param name The name of the parameter. 185 * @param initializeParameterPort True if the parameterPort should 186 * be initialized here. Some derived classes might want to initialize 187 * the port themselves (e.g. MirrorPortParameter). 188 * @exception IllegalActionException If the parameter is not of an 189 * acceptable class for the container. 190 * @exception NameDuplicationException If the name coincides with 191 * a parameter already in the container. 192 */ 193 public PortParameter(NamedObj container, String name, 194 boolean initializeParameterPort) 195 throws IllegalActionException, NameDuplicationException { 196 super(container, name); 197 // If we get to here, we know the container is a ComponentEntity, 198 // so the cast is safe. 199 if (initializeParameterPort && container instanceof TypedActor) { 200 _port = new ParameterPort((ComponentEntity) container, name, this); 201 _setTypeConstraints(); 202 } 203 } 204 205 /** Construct a Parameter with the given container, name, and Token. 206 * The token defines the initial persistent and current values. 207 * The container argument must not be null, or a 208 * NullPointerException will be thrown. This parameter will use the 209 * workspace of the container for synchronization and version counts. 210 * If the name argument is null, then the name is set to the empty string. 211 * The object is not added to the list of objects in the workspace 212 * unless the container is null. 213 * Increment the version of the workspace. 214 * If the name argument is null, then the name is set to the empty 215 * string. 216 * @param container The container. 217 * @param name The name. 218 * @param token The Token contained by this Parameter. 219 * @exception IllegalActionException If the parameter is not of an 220 * acceptable class for the container. 221 * @exception NameDuplicationException If the name coincides with 222 * an parameter already in the container. 223 */ 224 public PortParameter(NamedObj container, String name, 225 ptolemy.data.Token token) 226 throws IllegalActionException, NameDuplicationException { 227 this(container, name); 228 setToken(token); 229 if (token != null) { 230 if (isStringMode() && token instanceof StringToken) { 231 _persistentExpression = ((StringToken) token).stringValue(); 232 } else { 233 _persistentExpression = token.toString(); 234 } 235 } else { 236 _persistentExpression = ""; 237 } 238 } 239 240 /////////////////////////////////////////////////////////////////// 241 //// public methods //// 242 243 /** React to a change in an attribute. This method is called by 244 * a contained attribute when its value changes. In this class, 245 * if the attribute is an instance of Location, then the location 246 * of the associated port is set as well. 247 * @param attribute The attribute that changed. 248 * @exception IllegalActionException If the change is not acceptable 249 * to this container (not thrown in this base class). 250 */ 251 @Override 252 public void attributeChanged(Attribute attribute) 253 throws IllegalActionException { 254 if (attribute instanceof Locatable) { 255 Locatable location = (Locatable) attribute; 256 257 if (_port != null) { 258 Attribute portAttribute = _port.getAttribute("_location"); 259 Locatable portLocation = null; 260 261 if (portAttribute instanceof Locatable) { 262 portLocation = (Locatable) portAttribute; 263 } else { 264 try { 265 portLocation = new Location(_port, "_location"); 266 ((NamedObj) portLocation).propagateExistence(); 267 } catch (KernelException ex) { 268 throw new InternalErrorException(ex); 269 } 270 } 271 272 double[] locationValues = location.getLocation(); 273 double[] portLocationValues = new double[2]; 274 portLocationValues[0] = locationValues[0] - 20.0; 275 portLocationValues[1] = locationValues[1] - 5.0; 276 portLocation.setLocation(portLocationValues); 277 } 278 } else { 279 super.attributeChanged(attribute); 280 } 281 } 282 283 /** Clone the parameter. This overrides the base class to remove 284 * the current association with a port. It is assumed that the 285 * port will also be cloned, and when the containers are set of 286 * this parameter and that port, whichever one is set second 287 * will result in re-establishment of the association. 288 * @param workspace The workspace in which to place the cloned parameter. 289 * @exception CloneNotSupportedException Not thrown in this base class. 290 * @see java.lang.Object#clone() 291 * @return The cloned parameter. 292 */ 293 @Override 294 public Object clone(Workspace workspace) throws CloneNotSupportedException { 295 PortParameter newObject = (PortParameter) super.clone(workspace); 296 // Cannot establish an association with the cloned port until 297 // that port is cloned and the container of both is set. 298 newObject._port = null; 299 return newObject; 300 } 301 302 /** Get the persistent expression. 303 * @return The expression used by this variable. 304 * @see #setExpression(String) 305 */ 306 @Override 307 public String getExpression() { 308 if (_persistentExpression == null) { 309 return ""; 310 } 311 return _persistentExpression; 312 } 313 314 /** Return the associated port. Normally, there always is one, 315 * but if setContainer() is called to change the container, then 316 * this might return null. Also, during cloning, there is a 317 * transient during which this may return null. 318 * @return The associated port. 319 */ 320 public ParameterPort getPort() { 321 if (_port == null) { 322 // Attempt to find the port. 323 NamedObj container = getContainer(); 324 if (container instanceof Entity) { 325 Port candidate = ((Entity) container).getPort(getName()); 326 if (candidate instanceof ParameterPort) { 327 _port = (ParameterPort) candidate; 328 _setTypeConstraints(); 329 } 330 } 331 } 332 return _port; 333 } 334 335 /** Reset the current value to match the persistent value. 336 * @exception IllegalActionException If thrown by a subclass. 337 */ 338 @Override 339 public void initialize() throws IllegalActionException { 340 super.initialize(); 341 super.setExpression(_persistentExpression); 342 validate(); 343 } 344 345 /** Reset the current value to match the persistent value. 346 * @exception IllegalActionException If thrown by a subclass. 347 */ 348 @Override 349 public void preinitialize() throws IllegalActionException { 350 super.preinitialize(); 351 super.setExpression(_persistentExpression); 352 validate(); 353 } 354 355 /** Set the container of this parameter. If the container is different 356 * from what it was before and there is an associated port, then 357 * also change the container of the port. 358 * @see ParameterPort 359 * @param entity The new container. 360 * @exception IllegalActionException If the superclass throws it. 361 * @exception NameDuplicationException If the superclass throws it. 362 */ 363 @Override 364 public void setContainer(NamedObj entity) 365 throws IllegalActionException, NameDuplicationException { 366 if (_settingContainer) { 367 // Recursive call through the port. 368 return; 369 } 370 Entity previousContainer = (Entity) getContainer(); 371 if (entity == previousContainer) { 372 // No change. 373 return; 374 } 375 super.setContainer(entity); 376 377 // If there is an associated port, and the container has changed, 378 // updated the container of the port. 379 if (_port != null && (entity == null || entity instanceof Entity)) { 380 try { 381 _settingContainer = true; 382 _port.setContainer((Entity) entity); 383 } catch (KernelException ex) { 384 super.setContainer(previousContainer); 385 throw ex; 386 } finally { 387 _settingContainer = false; 388 } 389 } 390 } 391 392 /** Set the current value of this parameter and notify the container 393 * and value listeners. This does not change the persistent value 394 * (returned by getExpression()), but does change the current value 395 * (returned by getToken()). 396 * <p> 397 * If the type of this variable has been set with 398 * setTypeEquals(), then convert the specified token into that 399 * type, if possible, or throw an exception, if not. If 400 * setTypeAtMost() has been called, then verify that its type 401 * constraint is satisfied, and if not, throw an exception. 402 * Note that you can call this with a null argument regardless 403 * of type constraints, unless there are other variables that 404 * depend on its value.</p> 405 * @param token The new token to be stored in this variable. 406 * @exception IllegalActionException If the token type is not 407 * compatible with specified constraints, or if you are attempting 408 * to set to null a variable that has value dependents, or if the 409 * container rejects the change. 410 */ 411 public void setCurrentValue(ptolemy.data.Token token) 412 throws IllegalActionException { 413 if (_debugging) { 414 _debug("setCurrentValue: " + token); 415 } 416 417 super.setToken(token); 418 setUnknown(false); 419 } 420 421 /** Set the display name, and propagate the name change to the 422 * associated port. 423 * Increment the version of the workspace. 424 * This method is write-synchronized on the workspace. 425 * @param name The new display name. 426 */ 427 @Override 428 public void setDisplayName(String name) { 429 if (_settingName) { 430 return; 431 } 432 super.setDisplayName(name); 433 ParameterPort port = getPort(); 434 if (port != null) { 435 try { 436 _settingName = true; 437 port.setDisplayName(name); 438 } finally { 439 _settingName = false; 440 } 441 } 442 } 443 444 /** Override the base class to record the persistent expression. 445 * @param expression The expression for this variable. 446 * @see #getExpression() 447 */ 448 @Override 449 public void setExpression(String expression) { 450 _persistentExpression = expression; 451 super.setExpression(expression); 452 } 453 454 /** Set or change the name, and propagate the name change to the 455 * associated port. If a null argument is given, then the 456 * name is set to an empty string. 457 * Increment the version of the workspace. 458 * This method is write-synchronized on the workspace. 459 * @param name The new name. 460 * @exception IllegalActionException If the name contains a period. 461 * @exception NameDuplicationException If the container already 462 * contains an attribute with the proposed name. 463 */ 464 @Override 465 public void setName(String name) 466 throws IllegalActionException, NameDuplicationException { 467 if (_settingName) { 468 return; 469 } 470 super.setName(name); 471 ParameterPort port = getPort(); 472 if (port != null) { 473 String oldName = getName(); 474 try { 475 _settingName = true; 476 port.setName(name); 477 } catch (KernelException ex) { 478 super.setName(oldName); 479 throw ex; 480 } finally { 481 _settingName = false; 482 } 483 } 484 } 485 486 /** Override the base class to record the persistent expression 487 * to be the string representation of the specified token. 488 * @param newValue The new persistent value. 489 * @exception IllegalActionException If the token type is not 490 * compatible with specified constraints, or if you are attempting 491 * to set to null a variable that has value dependents, or if the 492 * container rejects the change. 493 */ 494 @Override 495 public void setToken(Token newValue) throws IllegalActionException { 496 if (newValue != null) { 497 if (isStringMode() && newValue instanceof StringToken) { 498 _persistentExpression = ((StringToken) newValue).stringValue(); 499 } else { 500 _persistentExpression = newValue.toString(); 501 } 502 } else { 503 _persistentExpression = ""; 504 } 505 super.setToken(newValue); 506 } 507 508 /** Check to see whether a token has arrived at the 509 * associated port, and if so, update the current value of 510 * parameter with that token. If there is no associated port, 511 * do nothing. 512 * @return True if an input token has been consumed, false if not. 513 * @exception IllegalActionException If reading from the associated 514 * port throws it. 515 */ 516 public boolean update() throws IllegalActionException { 517 ParameterPort port = getPort(); 518 519 if (port != null && port.isOutsideConnected() && port.hasToken(0)) { 520 Token token = port.get(0); 521 setCurrentValue(token); 522 // Have to validate so that containers of dependent 523 // variables get attributeChanged() called. 524 validate(); 525 526 if (_debugging) { 527 _debug("Updated parameter value to: " + token); 528 } 529 return true; 530 } 531 return false; 532 } 533 534 /////////////////////////////////////////////////////////////////// 535 //// protected methods //// 536 537 /** Get the persistent expression as a string, to be used to export to MoML. 538 * @return The persistent expression as a string. 539 */ 540 @Override 541 protected String _getCurrentExpression() { 542 return _persistentExpression; 543 } 544 545 /** Override the base class to also propagate the associated port. 546 * @param container Object to contain the new object. 547 * @exception IllegalActionException If the object 548 * cannot be cloned. 549 * @return A new object of the same class and name 550 * as this one. 551 */ 552 @Override 553 protected NamedObj _propagateExistence(NamedObj container) 554 throws IllegalActionException { 555 NamedObj result = super._propagateExistence(container); 556 557 // Since we have created an associated port in the 558 // constructor, and since that port is not contained by 559 // this parameter, it will not automatically be propagated. 560 // If this parameter is contained by class definition 561 // somewhere above in the hierarchy, then not propagating 562 // the associated port is an error. 563 ParameterPort port = getPort(); 564 if (port != null) { 565 port.propagateExistence(); 566 } 567 return result; 568 } 569 570 /** Set the type constraints between the protected member _port 571 * and this parameter. This is a protected method so that subclasses 572 * can define different type constraints. 573 */ 574 protected void _setTypeConstraints() { 575 ParameterPort port = getPort(); 576 if (port != null) { 577 port.setTypeSameAs(this); 578 } 579 } 580 581 /////////////////////////////////////////////////////////////////// 582 //// protected members //// 583 584 /** The associated port. */ 585 protected ParameterPort _port; 586 587 /////////////////////////////////////////////////////////////////// 588 //// private variables //// 589 590 /** The persistent expression. */ 591 private String _persistentExpression; 592 593 /** Indicator that we are in the midst of setting the container. */ 594 private boolean _settingContainer = false; 595 596 /** Indicator that we are in the midst of setting the name. */ 597 private boolean _settingName = false; 598}