001/* A variable is an attribute that contains a token and can be referenced 002 in expressions. 003 004 Copyright (c) 1998-2016 The Regents of the University of California. 005 All rights reserved. 006 Permission is hereby granted, without written agreement and without 007 license or royalty fees, to use, copy, modify, and distribute this 008 software and its documentation for any purpose, provided that the above 009 copyright notice and the following two paragraphs appear in all copies 010 of this software. 011 012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 016 SUCH DAMAGE. 017 018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 023 ENHANCEMENTS, OR MODIFICATIONS. 024 025 PT_COPYRIGHT_VERSION_2 026 COPYRIGHTENDKEY 027 028 029 */ 030package ptolemy.data.expr; 031 032import java.util.Collection; 033import java.util.Collections; 034import java.util.HashMap; 035import java.util.HashSet; 036import java.util.Iterator; 037import java.util.LinkedList; 038import java.util.List; 039import java.util.Map; 040import java.util.Set; 041import java.util.concurrent.CopyOnWriteArrayList; 042 043import ptolemy.actor.TypeAttribute; 044import ptolemy.data.ObjectToken; 045import ptolemy.data.StringToken; 046import ptolemy.data.Token; 047import ptolemy.data.type.BaseType; 048import ptolemy.data.type.ObjectType; 049import ptolemy.data.type.StructuredType; 050import ptolemy.data.type.Type; 051import ptolemy.data.type.TypeConstant; 052import ptolemy.data.type.TypeLattice; 053import ptolemy.data.type.Typeable; 054import ptolemy.graph.CPO; 055import ptolemy.graph.Inequality; 056import ptolemy.graph.InequalityTerm; 057import ptolemy.kernel.InstantiableNamedObj; 058import ptolemy.kernel.util.AbstractSettableAttribute; 059import ptolemy.kernel.util.Attribute; 060import ptolemy.kernel.util.IllegalActionException; 061import ptolemy.kernel.util.Instantiable; 062import ptolemy.kernel.util.InternalErrorException; 063import ptolemy.kernel.util.NameDuplicationException; 064import ptolemy.kernel.util.Nameable; 065import ptolemy.kernel.util.NamedList; 066import ptolemy.kernel.util.NamedObj; 067import ptolemy.kernel.util.ScopeExtender; 068import ptolemy.kernel.util.Settable; 069import ptolemy.kernel.util.ValueListener; 070import ptolemy.kernel.util.Workspace; 071import ptolemy.util.MessageHandler; 072import ptolemy.util.StringUtilities; 073 074/////////////////////////////////////////////////////////////////// 075//// Variable 076 077/** 078 A Variable is an Attribute that contains a token, and can be set by an 079 expression that can refer to other variables. 080 <p> 081 A variable can be given a token or an expression as its value. To create 082 a variable with a token, either call the appropriate constructor, or create 083 the variable with the appropriate container and name, and then call 084 setToken(). To set the value from an expression, call setExpression(). 085 The expression is not actually evaluated until you call getToken(), 086 getType(). By default, it is also evaluated when you call validate(), 087 unless you have called setLazy(true), in which case it will only 088 be evaluated if there are other variables that depend on it and those 089 have not had setLazy(true) called. 090 <p> 091 Consider for example the sequence: 092 <pre> 093 Variable v3 = new Variable(container,"v3"); 094 Variable v2 = new Variable(container,"v2"); 095 Variable v1 = new Variable(container,"v1"); 096 v3.setExpression("v1 + v2"); 097 v2.setExpression("1.0"); 098 v1.setExpression("2.0"); 099 v3.getToken(); 100 </pre> 101 Notice that the expression for <code>v3</code> cannot be evaluated 102 when it is set because <code>v2</code> and <code>v1</code> do not 103 yet have values. But there is no problem because the expression 104 is not evaluated until getToken() is called. Equivalently, we 105 could have called, for example, 106 <pre> 107 v3.validate(); 108 </pre> 109 This will force <code>v3</code> to be evaluated, 110 and also <code>v1</code> and <code>v2</code> 111 to be evaluated. 112 <p> 113 There is a potentially confusing subtlety. In the above code, 114 before the last line is executed, the expression for <code>v3</code> 115 has not been evaluated, so the dependence that <code>v3</code> has 116 on <code>v1</code> and <code>v2</code> has not been recorded. 117 Thus, if we call 118 <pre> 119 v1.validate(); 120 </pre> 121 before <code>v3</code> has ever been evaluated, then it will <i>not</i> 122 trigger an evaluation of <code>v3</code>. Because of this, we recommend 123 that user code call validate() immediately after calling 124 setExpression(). 125 <p> 126 If the expression string is null or empty, 127 or if no value has been specified, then getToken() will return null. 128 <p> 129 The expression can reference variables that are in scope before the 130 expression is evaluated (i.e., before getToken() or validate() is called). 131 Otherwise, getToken() will throw an exception. All variables 132 contained by the same container, and those contained by the container's 133 container, are in the scope of this variable. Thus, in the above, 134 all three variables are in each other's scope because they belong 135 to the same container. If there are variables in the scope with the 136 same name, then those lower in the hierarchy shadow those that are higher. 137 An instance of ScopeExtendingAttribute can also be used to 138 aggregate a set of variables and add them to the scope. 139 <p> 140 If a variable is referred 141 to by expressions of other variables, then the name of the variable must be a 142 valid identifier as defined by the Ptolemy II expression language syntax. 143 A valid identifier starts with a letter or underscore, and contains 144 letters, underscores, numerals, dollar signs ($), 145 at signs (@), or pound signs (#). 146 <p> 147 A variable is a Typeable object. Constraints on its type can be 148 specified relative to other Typeable objects (as inequalities on the types), 149 or relative to specific types. The former are called <i>dynamic type 150 constraints</i>, and the latter are called <i>static type constraints</i>. 151 Static type constraints are specified by the methods: 152 <ul> 153 <li> setTypeEquals() 154 <li> setTypeAtMost() 155 </ul> 156 whereas dynamic type constraints are given by 157 <ul> 158 <li> setTypeAtLeast() 159 <li> setTypeSameAs() 160 </ul> 161 Static type constraints are enforced in this class, meaning that: 162 <ul> 163 <li> If the variable already has a value (set by setToken() or 164 setExpression()) when you set the static type constraint, then 165 the value must satisfy the type constraint; and 166 <li> If after setting a static type constraint you give the token 167 a value, then the value must satisfy the constraints. 168 </ul> 169 A violation will cause an exception (either when setToken() is called 170 or when the expression is evaluated). 171 <p> 172 The dynamic type constraints are not enforced in this class, but merely 173 reported by the typeConstraints() method. They must be enforced at a 174 higher level (by a type system) since they involve a network of variables 175 and other typeable objects. In fact, if the variable does not yet have 176 a value, then a type system may use these constraints to infer what the 177 type of the variable needs to be, and then call setTypeEquals(). 178 <p> 179 The token returned by getToken() is always an instance of the class given 180 by the getType() method. This is not necessarily the same as the class 181 of the token that was inserted via setToken(). It might be a distinct 182 type if the token given by setToken() can be converted losslessly into one 183 of the type given by setTypeEquals(). 184 <p> 185 A variable by default has no MoML description (MoML is an XML modeling markup 186 language). Thus, a variable contained by a named object is not 187 persistent, in that if the object is exported to a MoML file, the 188 variable will not be represented. If you prefer that the variable 189 be represented, then you should use the derived class Parameter instead. 190 <p> 191 A variable is also normally not settable by casual users from the user 192 interface. This is because, by default, getVisibility() returns EXPERT. 193 The derived class Parameter is fully visible by default. 194 <p> 195 In addition, this class provides as a convenience a "string mode." 196 If the variable is in string mode, then when setting the value of 197 this variable, the string that you pass to setExpression(String) 198 is taken to be literally the value of the instance of StringToken 199 that represents the value of this parameter. It is not necessary 200 to enclose it in quotation marks (and indeed, if you do, the quotation 201 marks will become part of the value of the string). In addition, 202 the type of this parameter will be set to string. In addition, 203 getToken() will never return null; if the value of the string 204 has never been set, then an instance of StringToken is returned 205 that has an empty string as its value. A parameter is 206 in string mode if either setStringMode(true) has been called or 207 it contains an attribute named "_stringMode". 208 <p> 209 In string mode, the value passed to setExpression(String) may contain 210 references to other variables in scope using the syntax $id, 211 ${id} or $(id). The first case only works if the id consists 212 only of alphanumeric characters and/or underscore, and if the 213 character immediately following the id is not one of these. 214 To get a simple dollar sign, use $$. In string mode, to set the 215 value to be the empty string, create a Parameter in the container 216 that has the value <code>""</code> and then set the string mode 217 parameter to the <code>$<i>nameOfTheParameter</i></code>. For example, 218 the parameter might be named <code>myEmptyParameter</code> and have 219 a value <code>""</code>; the value for the string mode parameter would 220 be <code>$myEmptyParameter</code>. 221 222 @author Neil Smyth, Xiaojun Liu, Edward A. Lee, Yuhong Xiong, contributor: Daniel Crawl. 223 @version $Id$ 224 @since Ptolemy II 0.2 225 @Pt.ProposedRating Red (neuendor) 226 @Pt.AcceptedRating Red (cxh) 227 228 @see ptolemy.data.Token 229 @see ptolemy.data.expr.PtParser 230 @see ptolemy.data.expr.Parameter 231 @see ScopeExtendingAttribute 232 @see #setPersistent(boolean) 233 */ 234public class Variable extends AbstractSettableAttribute 235 implements Typeable, ValueListener { 236 /** Construct a variable in the default workspace with an empty string 237 * as its name. The variable is added to the list of objects in the 238 * workspace. Increment the version number of the workspace. 239 */ 240 public Variable() { 241 super(); 242 setPersistent(false); 243 } 244 245 /** Construct a variable with the given name as an attribute of the 246 * given container. The container argument must not be null, otherwise 247 * a NullPointerException will be thrown. This variable will use the 248 * workspace of the container for synchronization and version counts. 249 * If the name argument is null, then the name is set to the empty 250 * string. Increment the version number of the workspace. 251 * @param container The container. 252 * @param name The name of the variable. 253 * @exception IllegalActionException If the container does not accept 254 * a variable as its attribute. 255 * @exception NameDuplicationException If the name coincides with a 256 * variable already in the container. 257 */ 258 public Variable(NamedObj container, String name) 259 throws IllegalActionException, NameDuplicationException { 260 super(container, name); 261 setPersistent(false); 262 } 263 264 /** Construct a variable with the given container, name, and token. 265 * The container argument must not be null, or a 266 * NullPointerException will be thrown. This variable will use the 267 * workspace of the container for synchronization and version counts. 268 * If the name argument is null, then the name is set to the empty 269 * string. Increment the version of the workspace. 270 * @param container The container. 271 * @param name The name. 272 * @param token The token contained by this variable. 273 * @exception IllegalActionException If the container does not accept 274 * a variable as its attribute. 275 * @exception NameDuplicationException If the name coincides with a 276 * variable already in the container. 277 */ 278 public Variable(NamedObj container, String name, ptolemy.data.Token token) 279 throws IllegalActionException, NameDuplicationException { 280 this(container, name, token, true); 281 } 282 283 /** Construct a variable in the specified workspace with an empty 284 * string as its name. The name can be later changed with setName(). 285 * If the workspace argument is null, then use the default workspace. 286 * The variable is added to the list of objects in the workspace. 287 * Increment the version number of the workspace. 288 * @param workspace The workspace that will list the variable. 289 */ 290 public Variable(Workspace workspace) { 291 super(workspace); 292 setPersistent(false); 293 } 294 295 /////////////////////////////////////////////////////////////////// 296 //// public methods //// 297 298 /** Add a listener to be notified when the value of this variable changes. 299 * @param listener The listener to add. 300 * @see #removeValueListener(ValueListener) 301 */ 302 @Override 303 public synchronized void addValueListener(ValueListener listener) { 304 if (_valueListeners == null) { 305 // Use CopyOnWriteArrayList because this has a thread-safe iterator. 306 // When asking for an iterator, you get an iterator over a frozen 307 // version of the list. When a modification is made to the underlying 308 // list (additions or deletions), this makes a copy of the list. 309 // This is efficient if we assume that modifications to the list are 310 // much more rare than iterations over the list. 311 _valueListeners = new CopyOnWriteArrayList<ValueListener>(); 312 } 313 314 if (!_valueListeners.contains(listener)) { 315 _valueListeners.add(listener); 316 } 317 } 318 319 /** React to a change in an attribute. This method is called by 320 * a contained attribute when its value changes. This overrides 321 * the base class so that if the attribute is an instance of 322 * TypeAttribute, then it sets the type of the port. 323 * @param attribute The attribute that changed. 324 * @exception IllegalActionException If the change is not acceptable 325 * to this container. 326 */ 327 @Override 328 public void attributeChanged(Attribute attribute) 329 throws IllegalActionException { 330 if (attribute instanceof TypeAttribute) { 331 Type type = ((TypeAttribute) attribute).getType(); 332 333 if (type != null) { 334 // Avoid incrementing the workspace version if the type has 335 // not changed. 336 if (!type.equals(_declaredType)) { 337 setTypeEquals(type); 338 } 339 } 340 } else { 341 super.attributeChanged(attribute); 342 } 343 } 344 345 /** Clone the variable. This creates a new variable containing the 346 * same token (if the value was set with setToken()) or the same 347 * (unevaluated) expression, if the expression was set with 348 * setExpression(). The list of variables added to the scope 349 * is not cloned; i.e., the clone has an empty scope. 350 * The clone has the same static type constraints (those given by 351 * setTypeEquals() and setTypeAtMost()), but none of the dynamic 352 * type constraints (those relative to other variables). 353 * @param workspace The workspace in which to place the cloned variable. 354 * @exception CloneNotSupportedException Not thrown in this base class. 355 * @see java.lang.Object#clone() 356 * @return The cloned variable. 357 */ 358 @Override 359 public Object clone(Workspace workspace) throws CloneNotSupportedException { 360 Variable newObject = (Variable) super.clone(workspace); 361 362 // _currentExpression and _initialExpression are preserved in clone 363 if (_currentExpression != null) { 364 newObject._needsEvaluation = true; 365 } 366 367 newObject._threadEvaluating = null; 368 369 // _noTokenYet and _initialToken are preserved in clone 370 newObject._parserScope = null; 371 372 newObject._parseTreeEvaluator = null; 373 374 // Very subtle bug from missing this. 375 // This bug only showed up when using MoML classes (e.g. 376 // SmoothedPeriodogram actors, which are composite actors 377 // in the library), because these are cloned when copied. 378 newObject._variablesDependentOn = null; 379 380 // The clone has new value listeners. 381 newObject._valueListeners = null; 382 383 // set _declaredType and _varType 384 if (_declaredType instanceof StructuredType 385 && !_declaredType.isConstant()) { 386 newObject._declaredType = (Type) ((StructuredType) _declaredType) 387 .clone(); 388 newObject._varType = newObject._declaredType; 389 } 390 391 // _typeAtMost is preserved 392 newObject._parseTree = null; 393 newObject._parseTreeValid = false; 394 395 newObject._constraints = new HashSet<Inequality>(); 396 newObject._typeTerm = null; 397 return newObject; 398 } 399 400 /** If setTypeEquals() has been called, then return the type specified 401 * there. Otherwise, return BaseType.UNKNOWN. 402 * @return The declared type of this variable. 403 * @see #setTypeEquals(Type) 404 * @see BaseType 405 */ 406 public Type getDeclaredType() { 407 return _declaredType; 408 } 409 410 /** Get the expression currently used by this variable. The expression 411 * is either the value set by setExpression(), or a string representation 412 * of the value set by setToken(), or an empty string if no value 413 * has been set. 414 * @return The expression used by this variable. 415 * @see #setExpression(String) 416 */ 417 @Override 418 public String getExpression() { 419 String value = _currentExpression; 420 421 if (value == null) { 422 ptolemy.data.Token token = null; 423 424 try { 425 token = getToken(); 426 } catch (IllegalActionException ex) { 427 } 428 429 if (token != null) { 430 if (isStringMode()) { 431 value = ((StringToken) token).stringValue(); 432 } else { 433 value = token.toString(); 434 } 435 } 436 } 437 438 if (value == null) { 439 value = ""; 440 } 441 442 return value; 443 } 444 445 /** Return the list of identifiers referenced by the current expression. 446 * @return A set of Strings. 447 * @exception IllegalActionException If the expression cannot be parsed. 448 */ 449 public Set getFreeIdentifiers() throws IllegalActionException { 450 if (_currentExpression == null) { 451 return Collections.EMPTY_SET; 452 } 453 454 try { 455 workspace().getReadAccess(); 456 _parseIfNecessary(); 457 458 ParseTreeFreeVariableCollector collector = new ParseTreeFreeVariableCollector(); 459 // hoc/test/auto/RunCompositeActor4.xml was throwing a NPE here. 460 if (_parseTree == null) { 461 throw new InternalErrorException(this, null, 462 "getFreeIdentifiers(): failed to set _parseTree? _parseTree is null, " 463 + "_parseTreeValid is " + _parseTreeValid); 464 } 465 return collector.collectFreeVariables(_parseTree); 466 } finally { 467 workspace().doneReading(); 468 } 469 } 470 471 /** Return the parser scope for this variable. 472 * @return The parser scope. 473 */ 474 public ParserScope getParserScope() { 475 if (_parserScope == null) { 476 _parserScope = new VariableScope(); 477 } 478 return _parserScope; 479 } 480 481 /** Return a NamedList of the variables that the value of this 482 * variable can depend on. These include other variables contained 483 * by the same container or any container that deeply contains 484 * this variable, as well as any variables in a ScopeExtendingAttribute 485 * contained by any of these containers. 486 * If there are variables with the same name in these various 487 * places, then they are shadowed as follows. A variable contained 488 * by the container of this variable has priority, followed 489 * by variables in a ScopeExtendingAttribute, followed by 490 * by a variable contained by the container of the container, etc. 491 * <p> 492 * Note that this method is an extremely inefficient to refer 493 * to the scope of a variable because it constructs a list containing 494 * every variable in the scope. It is best to avoid calling it 495 * and instead just use the get() method of the VariableScope 496 * inner class. 497 * <p> 498 * This method is read-synchronized on the workspace. 499 * @return The variables on which this variable can depend. 500 */ 501 public NamedList getScope() { 502 return getScope(this); 503 } 504 505 /** Return a NamedList of the variables that the value of the specified 506 * variable can depend on. These include other variables contained 507 * by the same container or any container that deeply contains 508 * the specified variable, as well as any variables in a 509 * ScopeExtendingAttribute contained by any of these containers. 510 * If there are variables with the same name in these various 511 * places, then they are shadowed as follows. A variable contained 512 * by the container of this variable has priority, followed 513 * by variables in a ScopeExtendingAttribute, followed by 514 * by a variable contained by the container of the container, etc. 515 * <p> 516 * Note that this method is an extremely inefficient way to refer 517 * to the scope of a variable because it constructs a list containing 518 * every variable in the scope. It is best to avoid calling it 519 * and instead just use the get() method of the VariableScope 520 * inner class. 521 * <p> 522 * This method is read-synchronized on the workspace. 523 * @param object The NamedObj variable 524 * @return The variables on which this variable can depend. 525 */ 526 public static NamedList getScope(NamedObj object) { 527 try { 528 object.workspace().getReadAccess(); 529 530 NamedList scope = new NamedList(); 531 NamedObj container = object.getContainer(); 532 533 while (container != null) { 534 Iterator level1 = container.attributeList().iterator(); 535 Attribute var = null; 536 537 while (level1.hasNext()) { 538 // add the variables in the same NamedObj to scope, 539 // excluding this 540 var = (Attribute) level1.next(); 541 542 if (var instanceof Variable && var != object) { 543 if (var.workspace() != object.workspace()) { 544 continue; 545 } 546 547 try { 548 scope.append(var); 549 } catch (NameDuplicationException ex) { 550 // This occurs when a variable is shadowed by one 551 // that has been previously entered in the scope. 552 } catch (IllegalActionException ex) { 553 // This should not happen since we are dealing with 554 // variables which are Nameable. 555 } 556 } 557 } 558 559 level1 = container.attributeList(ScopeExtender.class) 560 .iterator(); 561 562 while (level1.hasNext()) { 563 ScopeExtender extender = (ScopeExtender) level1.next(); 564 Iterator level2 = extender.attributeList().iterator(); 565 566 while (level2.hasNext()) { 567 // add the variables in the scope extender to scope, 568 // excluding this 569 var = (Attribute) level2.next(); 570 571 if (var instanceof Variable && var != object) { 572 if (var.workspace() != object.workspace()) { 573 continue; 574 } 575 576 try { 577 scope.append(var); 578 } catch (NameDuplicationException ex) { 579 // This occurs when a variable is shadowed by 580 // one that has been previously entered in the 581 // scope. 582 } catch (IllegalActionException ex) { 583 // This should not happen since we are dealing 584 // with variables which are Nameable. 585 } 586 } 587 } 588 } 589 590 container = container.getContainer(); 591 } 592 593 return scope; 594 } finally { 595 object.workspace().doneReading(); 596 } 597 } 598 599 /** Get the token contained by this variable. The type of the returned 600 * token is always that returned by getType(). Calling this method 601 * will trigger evaluation of the expression, if the value has been 602 * given by setExpression(). Notice the evaluation of the expression 603 * can trigger an exception if the expression is not valid, or if the 604 * result of the expression violates type constraints specified by 605 * setTypeEquals() or setTypeAtMost(), or if the result of the expression 606 * is null and there are other variables that depend on this one. 607 * The returned value will be null if neither an expression nor a 608 * token has been set, or either has been set to null. 609 * @return The token contained by this variable converted to the 610 * type of this variable, or null if there is none. 611 * @exception IllegalActionException If the expression cannot 612 * be parsed or cannot be evaluated, or if the result of evaluation 613 * violates type constraints, or if the result of evaluation is null 614 * and there are variables that depend on this one. 615 * @see #setToken(String) 616 * @see #setToken(ptolemy.data.Token) 617 */ 618 public ptolemy.data.Token getToken() throws IllegalActionException { 619 if (_isTokenUnknown) { 620 throw new UnknownResultException(this); 621 } 622 623 // If the value has been set with an expression, then 624 // reevaluate the token. 625 if (_needsEvaluation) { 626 _evaluate(); 627 } 628 629 if (_token == null && isStringMode()) { 630 _token = _EMPTY_STRING_TOKEN; 631 } 632 633 return _token; 634 } 635 636 /** Get the type of this variable. If a token has been set by setToken(), 637 * the returned type is the type of that token; If an expression has 638 * been set by setExpression(), and the expression can be evaluated, the 639 * returned type is the type the evaluation result. If the expression 640 * cannot be evaluated at this time, the returned type is the declared 641 * type of this Variable, which is either set by setTypeEquals(), or 642 * the default BaseType.UNKNOWN; If no token has been set by setToken(), 643 * no expression has been set by setExpression(), and setTypeEquals() 644 * has not been called, the returned type is BaseType.UNKNOWN. 645 * @return The type of this variable. 646 */ 647 @Override 648 public Type getType() { 649 try { 650 if (_needsEvaluation) { 651 _evaluate(); 652 } 653 654 return _varType; 655 } catch (IllegalActionException iae) { 656 // iae.printStackTrace(); 657 return _declaredType; 658 } 659 } 660 661 /** Return an InequalityTerm whose value is the type of this variable. 662 * @return An InequalityTerm. 663 */ 664 @Override 665 public InequalityTerm getTypeTerm() { 666 if (_typeTerm == null) { 667 _typeTerm = new TypeTerm(); 668 } 669 670 return _typeTerm; 671 } 672 673 /** Get the value of the attribute, which is the evaluated expression. 674 * If the value is null, this returns the string "null" 675 * @see #getExpression() 676 */ 677 @Override 678 public String getValueAsString() { 679 ptolemy.data.Token value = null; 680 try { 681 value = getToken(); 682 } catch (IllegalActionException ex) { 683 // The value of this variable is undefined. 684 } 685 String tokenString; 686 if (value == null) { 687 tokenString = "null"; 688 } else if (isStringMode()) { 689 tokenString = ((StringToken) value).stringValue(); 690 } else { 691 tokenString = value.toString(); 692 } 693 return tokenString; 694 } 695 696 /** Look up and return the attribute with the specified name in the 697 * scope. Return null if such an attribute does not exist. 698 * @param name The name of the variable to be looked up. 699 * @return The attribute with the specified name in the scope. 700 * @exception IllegalActionException If a value in the scope 701 * exists with the given name, but cannot be evaluated. 702 */ 703 public Variable getVariable(String name) throws IllegalActionException { 704 // FIXME: this is not a safe cast because we have setParserScope() 705 return ((VariableScope) getParserScope()).getVariable(name); 706 } 707 708 /** Get the visibility of this variable, as set by setVisibility(). 709 * The visibility is set by default to EXPERT. 710 * @return The visibility of this variable. 711 * @see #setVisibility(Settable.Visibility) 712 */ 713 @Override 714 public Settable.Visibility getVisibility() { 715 return _visibility; 716 } 717 718 /** Mark this variable, and all variables that depend on it, as 719 * needing to be evaluated. Remove this variable from being 720 * notified by the variables it used to depend on. Then notify 721 * other variables that depend on this one that its value has 722 * changed. That notification is done by calling their valueChanged() 723 * method, which flags them as needing to be evaluated. This might be 724 * called when something in the scope of this variable changes. 725 */ 726 public void invalidate() { 727 // Synchronize to prevent concurrent modification 728 // of the _variablesDependentOn collection, and to 729 // prevent setting _needsEvaluation in the middle of 730 // another evaluation. 731 synchronized (this) { 732 if (_currentExpression != null) { 733 _needsEvaluation = true; 734 _parseTreeValid = false; 735 } 736 737 if (_variablesDependentOn != null) { 738 Iterator entries = _variablesDependentOn.entrySet().iterator(); 739 740 while (entries.hasNext()) { 741 Map.Entry entry = (Map.Entry) entries.next(); 742 Variable variable = (Variable) entry.getValue(); 743 variable.removeValueListener(this); 744 } 745 746 _variablesDependentOn.clear(); 747 } 748 } 749 // Do not hold a synchronization lock while notifying 750 // value listeners, because that calls arbitrary code, and 751 // a deadlock could result. 752 // Note that this could result in listeners being notified 753 // in the opposite order in which the updates occur! See 754 // Lee, The Problem with Threads, IEEE Computer, 2006. 755 _notifyValueListeners(); 756 } 757 758 /** Return <i>true</i> if the value of this variable is known, and 759 * false otherwise. In domains with fixed-point semantics, such 760 * as SR, a variable that depends on a port value may be unknown 761 * at various points during the execution. 762 * @see #setUnknown(boolean) 763 * @return True if the value is known. 764 * @exception IllegalActionException If the expression cannot 765 * be parsed or cannot be evaluated, or if the result of evaluation 766 * violates type constraints, or if the result of evaluation is null 767 * and there are variables that depend on this one. 768 */ 769 public boolean isKnown() throws IllegalActionException { 770 try { 771 getToken(); 772 } catch (UnknownResultException ex) { 773 return false; 774 } 775 776 return true; 777 } 778 779 /** Return true if this variable is lazy. By default, a variable 780 * is not lazy. 781 * @return True if this variable is lazy. 782 * @see #setLazy(boolean) 783 */ 784 public boolean isLazy() { 785 return _isLazy; 786 } 787 788 /** Return true if this parameter is in string mode. 789 * @return True if this parameter is in string mode. 790 * @see #setStringMode(boolean) 791 */ 792 public boolean isStringMode() { 793 if (_isStringMode) { 794 return true; 795 } else { 796 return getAttribute("_stringMode") != null; 797 } 798 } 799 800 /** Return true if this variable is suppressing 801 * variable substitution. That is, it is ignoring dollar signs 802 * in the string and does not evaluate substrings such as $name 803 * and ${name}. By default, this returns false. 804 * @see #isStringMode() 805 * @see #setSuppressVariableSubstitution(boolean) 806 * @return True if suppressing variable substitution. 807 */ 808 public boolean isSuppressVariableSubstitution() { 809 return _suppressVariableSubstitution; 810 } 811 812 /** Check whether the current type of this variable is acceptable. 813 * A type is acceptable if it represents an instantiable object. 814 * @return True if the current type is acceptable. 815 */ 816 @Override 817 public boolean isTypeAcceptable() { 818 if (getType().isInstantiable()) { 819 return true; 820 } 821 822 return false; 823 } 824 825 /** Remove a listener from the list of listeners that is 826 * notified when the value of this variable changes. If no such listener 827 * exists, do nothing. 828 * @param listener The listener to remove. 829 * @see #addValueListener(ValueListener) 830 */ 831 @Override 832 public synchronized void removeValueListener(ValueListener listener) { 833 // This does not need to be synchronized because the listener 834 // list is a CopyOnWriteArrayList. 835 if (_valueListeners != null) { 836 _valueListeners.remove(listener); 837 } 838 if (_weakValueListeners != null) { 839 _weakValueListeners.remove(listener); 840 } 841 } 842 843 /** Reset the variable to its initial value. If the variable was 844 * originally set from a token, then this token is placed again 845 * in the variable, and the type of the variable is set to equal 846 * that of the token. If the variable was originally given an 847 * expression, then this expression is placed again in the variable 848 * (but not evaluated), and the type is reset to BaseType.UNKNOWN. 849 * The type will be determined when the expression is evaluated or 850 * when type resolution is done. Note that if this variable is 851 * cloned, then reset on the clone behaves exactly as reset on 852 * the original. 853 * @deprecated This capability may be removed to simplify this class. 854 * It is not currently used in Ptolemy II, as of version 2.0. 855 */ 856 @Deprecated 857 public void reset() { 858 if (_noTokenYet) { 859 return; 860 } 861 862 if (_initialToken != null) { 863 try { 864 setToken(_initialToken); 865 } catch (IllegalActionException ex) { 866 // should not occur 867 throw new InternalErrorException(ex.getMessage()); 868 } 869 } else { 870 // must have an initial expression 871 setExpression(_initialExpression); 872 } 873 } 874 875 /** Specify the container, and add this variable to the list 876 * of attributes in the container. If this variable already has a 877 * container, remove this variable from the attribute list of the 878 * current container first. Otherwise, remove it from the directory 879 * of the workspace, if it is there. If the specified container is 880 * null, remove this variable from the list of attributes of the 881 * current container. If the specified container already contains 882 * an attribute with the same name, then throw an exception and do 883 * not make any changes. Similarly, if the container is not in the 884 * same workspace as this variable, throw an exception. If this 885 * variable is already contained by the specified container, do 886 * nothing. 887 * <p> 888 * If this method results in a change of container (which it usually 889 * does), then remove this variable from the scope of any 890 * scope dependent of this variable. 891 * <p> 892 * This method is write-synchronized on the workspace and increments 893 * its version number. 894 * @param container The proposed container of this variable. 895 * @exception IllegalActionException If the container will not accept 896 * a variable as its attribute, or this variable and the container 897 * are not in the same workspace, or the proposed container would 898 * result in recursive containment. 899 * @exception NameDuplicationException If the container already has 900 * an attribute with the name of this variable. 901 */ 902 @Override 903 public void setContainer(NamedObj container) 904 throws IllegalActionException, NameDuplicationException { 905 Nameable previousContainer = getContainer(); 906 907 // Warn if there are variables that depend on this one. 908 if (container != previousContainer && previousContainer != null) { 909 Set<ValueListener> listeners = new HashSet<ValueListener>(); 910 if (_valueListeners != null && !_valueListeners.isEmpty()) { 911 listeners.addAll(_valueListeners); 912 } 913 // See https://projects.ecoinformatics.org/ecoinfo/issues/6681 914 if (_weakValueListeners != null && !_weakValueListeners.isEmpty()) { 915 listeners.removeAll(_weakValueListeners); 916 } 917 918 if (!listeners.isEmpty()) { 919 if (!MessageHandler.yesNoQuestion( 920 "WARNING: There are variables depending on " + getName() 921 + ". Continue?")) { 922 // Cancel. 923 throw new IllegalActionException(this, 924 "Cancelled change of container."); 925 } 926 } 927 } 928 929 super.setContainer(container); 930 931 if (container != previousContainer) { 932 // Every variable that this may shadow in its new location 933 // must invalidate all their dependents. 934 _invalidateShadowedSettables(container); 935 936 // This variable must still be valid. 937 // NOTE: This has the side effect of validating everything 938 // that depends on this variable. If the container is being 939 // set to null, this may result in errors in variables 940 // for which this is no longer in scope. The error handling 941 // mechanism has to handle this. 942 // NOTE: This is too early for attributeChanged() to be called 943 // since typically the public variable referring to an attribute 944 // has not been set yet. 945 // Optimization: During construction, the previous 946 // container will be null. It doesn't make sense 947 // to validate at this point, since there shouldn't 948 // actually be any contained settables. 949 // If the container is being set to null, then we 950 // do have to validate as there might be variables 951 // that depend on this one that are no longer valid. EAL 9/6/06 952 if (previousContainer != null) { 953 // Do not use validate here if the new container is null 954 // because there is no need to validate, and anyway the variable 955 // may not validate because it depends on variables that are no 956 // longer in scope. 957 if (container != null) { 958 validate(); 959 } else { 960 // The following will mark the listeners to this variable as 961 // needing evaluation. When that evaluation occurs, an exception 962 // will be thrown. NOTE: The error will only be detected later, 963 // but this seems better than the alternatives. Note the warning 964 // issued above. 965 _notifyValueListeners(); 966 } 967 } 968 } 969 } 970 971 /** Set the expression of this variable. Evaluation is deferred until 972 * the value of the variable is accessed by getToken(). The 973 * container is not notified of the change until then. If you need 974 * to notify the container right away, then call getToken(). If the 975 * argument is null, then getToken() will return null. However, if 976 * there are other variables that depend on its value, then upon 977 * evaluation to null, an exception will be thrown (by getToken()). 978 * If the type of this variable has been set with 979 * setTypeEquals(), then upon evaluation, the token will be 980 * converted into that type, if possible, or an exception will 981 * be thrown, if not. If setTypeAtMost() has been called, then 982 * upon evaluation, it will be verified that the type 983 * constraint is satisfied, and if not, an exception will be thrown. 984 * @param expr The expression for this variable. 985 * @see #getExpression() 986 */ 987 @Override 988 public void setExpression(String expr) { 989 try { 990 super.setExpression(expr); 991 } catch (IllegalActionException e) { 992 throw new InternalErrorException(e); 993 } 994 995 if (_debugging) { 996 _debug("setExpression: " + expr); 997 } 998 999 if (expr == null || expr.trim().equals("")) { 1000 _token = null; 1001 _needsEvaluation = false; 1002 1003 // set _varType 1004 if (_declaredType instanceof StructuredType) { 1005 ((StructuredType) _varType).initialize(BaseType.UNKNOWN); 1006 } else { 1007 _varType = _declaredType; 1008 } 1009 } else { 1010 // Evaluation may be expensive. Do not do it 1011 // unless the expression has actually changed. 1012 // EAL 060808 1013 if (!expr.equals(_currentExpression)) { 1014 _needsEvaluation = true; 1015 } 1016 } 1017 1018 _currentExpression = expr; 1019 _parseTree = null; 1020 _parseTreeValid = false; 1021 1022 _notifyValueListeners(); 1023 } 1024 1025 /** Specify whether this variable is to be lazy. By default, it is not. 1026 * A lazy variable is a variable that is not evaluated until its 1027 * value is needed. Its value is needed when getToken() or 1028 * getType() is called, but not necessarily when validate() 1029 * is called. In particular, validate() has the effect 1030 * only of setting a flag indicating that the variable needs to be 1031 * evaluated, but the evaluation is not performed. Thus, although 1032 * validate() returns, there is no assurance that the expression 1033 * giving the value of the variable can be evaluated without error. 1034 * The validate() method, however, will validate value dependents. 1035 * If those are also lazy, then they will not be evaluated either. 1036 * If they are not lazy however (they are eager), then evaluating them 1037 * may cause this variable to be evaluated. 1038 * <p> 1039 * A lazy variable may be used whenever its value will be actively 1040 * accessed via getToken() when it is needed, and its type will be 1041 * actively accessed via getType(). In particular, the container 1042 * does not rely on a call to attributeChanged() or 1043 * attributeTypeChanged() to notify it that the variable value has 1044 * changed. Those methods will not be called when the value of the 1045 * variable changes due to some other variable value that it 1046 * depends on changing because the new value will not be 1047 * immediately evaluated. 1048 * @param lazy True to make the variable lazy. 1049 * @see #validate() 1050 * @see NamedObj#attributeChanged(Attribute) 1051 * @see NamedObj#attributeTypeChanged(Attribute) 1052 */ 1053 public void setLazy(boolean lazy) { 1054 if (_debugging) { 1055 _debug("setLazy: " + lazy); 1056 } 1057 1058 _isLazy = lazy; 1059 } 1060 1061 /** Override the base class to throw an exception if renaming this 1062 * variable results in an error evaluating some variable that depends 1063 * on it. In this case, the name remains unchanged. 1064 * @exception IllegalActionException If the name contains a period 1065 * or if this variable is referenced in some other expression. 1066 * @exception NameDuplicationException If there is already an 1067 * attribute with the same name in the container. 1068 */ 1069 @Override 1070 public void setName(String name) 1071 throws IllegalActionException, NameDuplicationException { 1072 String previousName = getName(); 1073 // If the name is changing from a previous name, then 1074 // make sure to update the variables that depend on this. 1075 // Record which variables get changed so they can be 1076 // reversed if the change fails at any point. 1077 LinkedList<ValueListener> changed = new LinkedList<ValueListener>(); 1078 if (previousName != null && !previousName.equals(name)) { 1079 try { 1080 if (_valueListeners != null) { 1081 // Note that the listener list is a CopyOnWriteArrayList, 1082 // so it need not be cloned here. 1083 // Note that this could result in listeners being notified 1084 // of renaming of this variable in the opposite order in which 1085 // the updates occur! See Lee, The Problem with Threads, 1086 // IEEE Computer, 2006. Here, we assume that only one thread 1087 // ever does the renaming. 1088 Iterator listeners = _valueListeners.iterator(); 1089 while (listeners.hasNext()) { 1090 ValueListener listener = (ValueListener) listeners 1091 .next(); 1092 if (listener instanceof Variable) { 1093 // The listener could be referencing this variable. 1094 ParseTreeFreeVariableRenamer renamer = new ParseTreeFreeVariableRenamer(); 1095 ((Variable) listener)._parseIfNecessary(); 1096 renamer.renameVariables( 1097 ((Variable) listener)._parseTree, 1098 (Variable) listener, this, name); 1099 ParseTreeWriter writer = new ParseTreeWriter(); 1100 // Set the ParseTreeWriter to write expressions in 1101 // string mode if the listener is in string mode so 1102 // referenced variables are correctly renamed. See: 1103 // https://projects.ecoinformatics.org/ecoinfo/issues/5723 1104 writer.setStringMode( 1105 ((Variable) listener).isStringMode()); 1106 ((Variable) listener) 1107 .setExpression(writer.parseTreeToExpression( 1108 ((Variable) listener)._parseTree)); 1109 changed.add(listener); 1110 } 1111 } 1112 } 1113 super.setName(name); 1114 // With the new name, we may now shadow variables that 1115 // were not previously shadowed. Invalidate those. 1116 _invalidateShadowedSettables(getContainer()); 1117 validate(); 1118 } catch (IllegalActionException ex) { 1119 // Reverse the changes above. 1120 super.setName(previousName); 1121 Iterator listeners = changed.iterator(); 1122 while (listeners.hasNext()) { 1123 Variable listener = (Variable) listeners.next(); 1124 // The listener could be referencing this variable. 1125 ParseTreeFreeVariableRenamer renamer = new ParseTreeFreeVariableRenamer(); 1126 renamer.renameVariables(listener._parseTree, listener, this, 1127 previousName); 1128 ParseTreeWriter writer = new ParseTreeWriter(); 1129 listener.setExpression( 1130 writer.parseTreeToExpression(listener._parseTree)); 1131 } 1132 // Make sure to re-evaluate dependent variables. 1133 validate(); 1134 throw ex; 1135 } 1136 } else { 1137 super.setName(name); 1138 } 1139 } 1140 1141 /** Set a new parseTreeEvaluator. 1142 * @param parseTreeEvaluator The new parseTreeEvaluator used by 1143 * this variable. 1144 */ 1145 public void setParseTreeEvaluator(ParseTreeEvaluator parseTreeEvaluator) { 1146 _parseTreeEvaluator = parseTreeEvaluator; 1147 } 1148 1149 /** Specify whether this parameter should be in string mode. 1150 * If the argument is true, then specify that the type of this 1151 * parameter is string. Otherwise, specify that the type is 1152 * unknown. Note that it probably does not make sense to 1153 * switch between string mode and not string mode after the 1154 * variable has a value. Note that this has the side effect 1155 * of causing any $name or ${name} references in the string 1156 * value to be replaced with value of a parameter named "name" 1157 * in scope. To suppress this behavior, invoke 1158 * {@link #setSuppressVariableSubstitution(boolean)}. 1159 * @param stringMode True to put the parameter in string mode. 1160 * @exception IllegalActionException If the current value of this 1161 * parameter is incompatible with the resulting type. 1162 * @see #isStringMode() 1163 */ 1164 public void setStringMode(boolean stringMode) 1165 throws IllegalActionException { 1166 _isStringMode = stringMode; 1167 1168 if (_isStringMode) { 1169 setTypeEquals(BaseType.STRING); 1170 } else { 1171 setTypeEquals(BaseType.UNKNOWN); 1172 } 1173 } 1174 1175 /** If the argument is true, then for a string mode parameter, 1176 * suppress variable substitution. That is, ignore dollar signs 1177 * in the string and do not evaluate substrings such as $name 1178 * and ${name}. By default, this is false. 1179 * @see #setStringMode(boolean) 1180 * @see #isSuppressVariableSubstitution() 1181 * @param suppress True to suppress variable substitution. 1182 */ 1183 public void setSuppressVariableSubstitution(boolean suppress) { 1184 _suppressVariableSubstitution = suppress; 1185 } 1186 1187 /** Set the expression for this variable by calling 1188 * setExpression(), and then evaluate it by calling 1189 * validate(). This will cause any other variables 1190 * that are dependent on it to be evaluated, and will 1191 * also cause the container to be notified of the change, 1192 * unlike setExpression(). 1193 * @param expression The expression. 1194 * @exception IllegalActionException If this variable or a 1195 * variable dependent on this variable cannot be evaluated (and is 1196 * not lazy) and the model error handler throws an exception. 1197 * Also thrown if the change is not acceptable to the container. 1198 * @see #getToken() 1199 * @see #setExpression(String) 1200 * @see #validate() 1201 */ 1202 public void setToken(String expression) throws IllegalActionException { 1203 setExpression(expression); 1204 validate(); 1205 } 1206 1207 /** Put a new token in this variable and notify the container and 1208 * and value listeners. If an expression had been 1209 * previously given using setExpression(), then that expression 1210 * is forgotten. If the type of this variable has been set with 1211 * setTypeEquals(), then convert the specified token into that 1212 * type, if possible, or throw an exception, if not. If 1213 * setTypeAtMost() has been called, then verify that its type 1214 * constraint is satisfied, and if not, throw an exception. 1215 * <br>Note that you can call this with a null argument regardless 1216 * of type constraints, unless there are other variables that 1217 * depend on its value. 1218 * @param token The new token to be stored in this variable. 1219 * @exception IllegalActionException If the token type is not 1220 * compatible with specified constraints, or if you are attempting 1221 * to set to null a variable that has value dependents, or if the 1222 * container rejects the change. 1223 * @see #getToken() 1224 */ 1225 public void setToken(Token token) throws IllegalActionException { 1226 if (_debugging) { 1227 _debug("setToken: " + token); 1228 } 1229 if (_token != null && _token.equals(token)) { 1230 return; // Nothing changed 1231 } 1232 1233 _setTokenAndNotify(token); 1234 1235 // Override any expression that may have been previously given. 1236 if (_currentExpression != null) { 1237 _currentExpression = null; 1238 1239 _parseTree = null; 1240 _parseTreeValid = false; 1241 } 1242 1243 setUnknown(false); 1244 // We were failing to do this, creating all sorts of subtle 1245 // bugs. E.g., parameters that are set directly in actors would 1246 // not be persistent. EAL 8/5/12. 1247 propagateValue(); 1248 } 1249 1250 /** Constrain the type of this variable to be equal to or 1251 * greater than the type represented by the specified InequalityTerm. 1252 * This constraint is not enforced here, but is returned by the 1253 * typeConstraints() method for use by a type system. 1254 * @param typeTerm An InequalityTerm object. 1255 */ 1256 @Override 1257 public void setTypeAtLeast(InequalityTerm typeTerm) { 1258 if (_debugging) { 1259 String name = "not named"; 1260 1261 if (typeTerm.getAssociatedObject() instanceof Nameable) { 1262 name = ((Nameable) typeTerm.getAssociatedObject()) 1263 .getFullName(); 1264 } 1265 1266 _debug("setTypeAtLeast: " + name); 1267 } 1268 1269 Inequality ineq = new Inequality(typeTerm, getTypeTerm()); 1270 _constraints.add(ineq); 1271 } 1272 1273 /** Constrain the type of this variable to be equal to or 1274 * greater than the type of the specified object. 1275 * This constraint is not enforced 1276 * here, but is returned by the typeConstraints() method for use 1277 * by a type system. 1278 * @param lesser A Typeable object. 1279 */ 1280 @Override 1281 public void setTypeAtLeast(Typeable lesser) { 1282 if (_debugging) { 1283 String name = "not named"; 1284 1285 if (lesser instanceof Nameable) { 1286 name = ((Nameable) lesser).getFullName(); 1287 } 1288 1289 _debug("setTypeAtLeast: " + name); 1290 } 1291 1292 Inequality ineq = new Inequality(lesser.getTypeTerm(), getTypeTerm()); 1293 _constraints.add(ineq); 1294 } 1295 1296 /** Set a type constraint that the type of this object be less than 1297 * or equal to the specified class in the type lattice. 1298 * This replaces any constraint specified 1299 * by an earlier call to this same method (note that there is no 1300 * point in having two separate specifications like this because 1301 * it would be equivalent to a single specification using the 1302 * greatest lower bound of the two). This is an absolute type 1303 * constraint (not relative to another Typeable object), so it 1304 * is checked every time the value of the variable is set by 1305 * setToken() or by evaluating an expression. This type constraint 1306 * is also returned by the typeConstraints() methods. 1307 * To remove the type constraint, call this method with a 1308 * BaseType.UNKNOWN argument. 1309 * @exception IllegalActionException If the type of this object 1310 * already violates this constraint, or if the argument is not 1311 * an instantiable type in the type lattice. 1312 */ 1313 @Override 1314 public void setTypeAtMost(Type type) throws IllegalActionException { 1315 if (_debugging) { 1316 _debug("setTypeAtMost: " + type); 1317 } 1318 1319 if (type == BaseType.UNKNOWN) { 1320 _typeAtMost = BaseType.UNKNOWN; 1321 return; 1322 } 1323 1324 if (!type.isInstantiable()) { 1325 throw new IllegalActionException(this, "setTypeAtMost(): " 1326 + "the argument " + type 1327 + " is not an instantiable type in the type lattice."); 1328 } 1329 1330 Type currentType = getType(); 1331 int typeInfo = TypeLattice.compare(currentType, type); 1332 1333 if (typeInfo == CPO.HIGHER || typeInfo == CPO.INCOMPARABLE) { 1334 throw new IllegalActionException(this, 1335 "setTypeAtMost(): " + "the current type " 1336 + currentType.toString() 1337 + " is not less than the desired bounding type " 1338 + type.toString()); 1339 } 1340 1341 _typeAtMost = type; 1342 } 1343 1344 /** Set a type constraint that the type of this object equal 1345 * the specified value. This is an absolute type constraint (not 1346 * relative to another Typeable object), so it is checked every time 1347 * the value of the variable is set by setToken() or by evaluating 1348 * an expression. If the variable already has a value, then that 1349 * value is converted to the specified type, if possible, or an 1350 * exception is thrown. 1351 * To remove the type constraint, call this method with the argument 1352 * BaseType.UNKNOWN. 1353 * @param type A Type. 1354 * @exception IllegalActionException If the type of this object 1355 * already violates this constraint, in that the currently contained 1356 * token cannot be converted losslessly to the specified type. 1357 */ 1358 @Override 1359 public void setTypeEquals(Type type) throws IllegalActionException { 1360 if (_debugging) { 1361 _debug("setTypeEquals: " + type); 1362 } 1363 1364 if (_token != null) { 1365 if (type.isCompatible(_token.getType())) { 1366 _token = type.convert(_token); 1367 } else { 1368 throw new IllegalActionException(this, 1369 "The currently contained token " 1370 + _token.getClass().getName() + "(" 1371 + _token.toString() 1372 + ") is not compatible with the desired type " 1373 + type.toString()); 1374 } 1375 } 1376 1377 // set _declaredType to a clone of the argument since the argument 1378 // may be a structured type and may change later. 1379 try { 1380 _declaredType = (Type) type.clone(); 1381 } catch (CloneNotSupportedException cnse) { 1382 throw new InternalErrorException("Variable.setTypeEquals: " 1383 + "The specified type cannot be cloned."); 1384 } 1385 1386 // set _varType. It is _token.getType() if _token is not null, or 1387 // _declaredType if _token is null. 1388 _varType = _declaredType; 1389 1390 if (_token != null && _declaredType instanceof StructuredType) { 1391 ((StructuredType) _varType) 1392 .updateType((StructuredType) _token.getType()); 1393 } 1394 } 1395 1396 /** Constrain the type of this variable to be the same as the 1397 * type of the specified object. This constraint is not enforced 1398 * here, but is returned by the typeConstraints() method for use 1399 * by a type system. 1400 * @param equal A Typeable object. 1401 */ 1402 @Override 1403 public void setTypeSameAs(Typeable equal) { 1404 if (_debugging) { 1405 String name = "not named"; 1406 1407 if (equal instanceof Nameable) { 1408 name = ((Nameable) equal).getFullName(); 1409 } 1410 1411 _debug("setTypeSameAs: " + name); 1412 } 1413 1414 Inequality ineq = new Inequality(getTypeTerm(), equal.getTypeTerm()); 1415 _constraints.add(ineq); 1416 ineq = new Inequality(equal.getTypeTerm(), getTypeTerm()); 1417 _constraints.add(ineq); 1418 } 1419 1420 /** Mark the value of this variable to be unknown if the argument is 1421 * <i>true</i>, or known if the argument is <i>false</i>. In domains 1422 * with fixed-point semantics, such as SR, a variable that depends on 1423 * a port value may be unknown at various points during the execution. 1424 * @see #isKnown() 1425 * @param value True to change mark this variable unknown. 1426 */ 1427 public void setUnknown(boolean value) { 1428 if (_debugging) { 1429 _debug("setUnknown: " + value); 1430 } 1431 1432 _isTokenUnknown = value; 1433 } 1434 1435 /** Set a value listener as a weak dependency. When this Variable changes 1436 * containers, the value listener is not considered a dependency. 1437 * @see #setContainer(NamedObj) 1438 */ 1439 public void setValueListenerAsWeakDependency(ValueListener listener) { 1440 // See https://projects.ecoinformatics.org/ecoinfo/issues/6681 1441 if (_weakValueListeners == null) { 1442 _weakValueListeners = new HashSet<ValueListener>(); 1443 } 1444 _weakValueListeners.add(listener); 1445 } 1446 1447 /** Set the visibility of this variable. The argument should be one 1448 * of the public static instances in Settable. 1449 * @param visibility The visibility of this variable. 1450 * @see #getVisibility() 1451 */ 1452 @Override 1453 public void setVisibility(Settable.Visibility visibility) { 1454 if (_debugging) { 1455 _debug("setVisibility: " + visibility); 1456 } 1457 1458 _visibility = visibility; 1459 } 1460 1461 /** Same as getExpression(). 1462 * @return A string representation of this variable. 1463 * @deprecated 1464 */ 1465 @Deprecated 1466 public String stringRepresentation() { 1467 return getExpression(); 1468 } 1469 1470 /** Return a string representation of the current evaluated variable value. 1471 * @return A string representing the class and the current token. 1472 */ 1473 @Override 1474 public String toString() { 1475 ptolemy.data.Token value = null; 1476 1477 try { 1478 value = getToken(); 1479 } catch (IllegalActionException ex) { 1480 // The value of this variable is undefined. 1481 } 1482 1483 String tokenString; 1484 1485 if (value == null) { 1486 tokenString = "value undefined"; 1487 } else { 1488 tokenString = value.toString(); 1489 } 1490 1491 if (tokenString.length() > 50) { 1492 tokenString = "value elided"; 1493 } 1494 return super.toString() + " " + tokenString; 1495 } 1496 1497 /** Return the type constraints of this variable. 1498 * The constraints include the ones explicitly set to this variable, 1499 * plus the constraint that the type of this variable must be no less 1500 * than the type of its current value, if it has one. 1501 * The constraints are a list of inequalities. 1502 * @return a list of Inequality objects. 1503 * @see ptolemy.graph.Inequality 1504 */ 1505 @Override 1506 public Set<Inequality> typeConstraints() { 1507 // Include all relative types that have been specified. 1508 Set<Inequality> result = new HashSet<Inequality>(); 1509 result.addAll(_constraints); 1510 1511 // If the variable has a value known at this time, add a constraint. 1512 // If the variable cannot be evaluated at this time (an exception is 1513 // thrown in _evaluate(), do nothing. 1514 // Add the inequality to the result list directly to add the 1515 // constraint only for this round of type resolution. If using 1516 // setTypeAtLeast(), the constraint will be permanent for this 1517 // Variable. 1518 try { 1519 Token currentToken = getToken(); 1520 1521 if (currentToken != null) { 1522 Type currentType = currentToken.getType(); 1523 1524 TypeConstant current = new TypeConstant(currentType); 1525 Inequality ineq = new Inequality(current, getTypeTerm()); 1526 result.add(ineq); 1527 } 1528 } catch (Throwable throwable) { 1529 // Ignored: expression cannot be evaluated at this time. 1530 // do nothing. 1531 } 1532 1533 // If the variable has a type, add a constraint. 1534 // Type currentType = getType(); 1535 // if (currentType != BaseType.UNKNOWN) { 1536 // TypeConstant current = new TypeConstant(currentType); 1537 // Inequality ineq = new Inequality(current, getTypeTerm()); 1538 // result.add(ineq); 1539 // } 1540 // If an upper bound has been specified, add a constraint. 1541 if (_typeAtMost != BaseType.UNKNOWN) { 1542 TypeConstant atMost = new TypeConstant(_typeAtMost); 1543 Inequality ineq = new Inequality(getTypeTerm(), atMost); 1544 result.add(ineq); 1545 } 1546 1547 return result; 1548 } 1549 1550 /** Return the type constraints of this variable. 1551 * The constraints include the ones explicitly set to this variable, 1552 * plus the constraint that the type of this variable must be no less 1553 * than its current type, if it has one. 1554 * The constraints are a list of inequalities. 1555 * @return a list of Inequality objects. 1556 * @see ptolemy.graph.Inequality 1557 * @deprecated Use typeConstraints(). 1558 */ 1559 @Deprecated 1560 public List typeConstraintList() { 1561 LinkedList<Inequality> result = new LinkedList<Inequality>(); 1562 result.addAll(typeConstraints()); 1563 return result; 1564 } 1565 1566 /** If this variable is not lazy (the default) then evaluate 1567 * the expression contained in this variable, and notify any 1568 * value dependents. If those are not lazy, then they too will 1569 * be evaluated. Also, if the variable is not lazy, then 1570 * notify its container, if there is one, by calling its 1571 * attributeChanged() method. 1572 * <p> 1573 * If this variable is lazy, then mark this variable and any 1574 * of its value dependents as needing evaluation and for any 1575 * value dependents that are not lazy, evaluate them. 1576 * Note that if there are no value dependents, 1577 * or if they are all lazy, then this will not 1578 * result in evaluation of this variable, and hence will not ensure 1579 * that the expression giving its value is valid. Call getToken() 1580 * or getType() to accomplish that. 1581 * @return The current list of value listeners, which are evaluated 1582 * as a consequence of this call to validate(). 1583 * @exception IllegalActionException If this variable or a 1584 * variable dependent on this variable cannot be evaluated (and is 1585 * not lazy) and the model error handler throws an exception. 1586 * Also thrown if the change is not acceptable to the container. 1587 */ 1588 @Override 1589 public Collection validate() throws IllegalActionException { 1590 if (_debugging) { 1591 _debug("validate"); 1592 } 1593 1594 invalidate(); 1595 1596 List errors = _propagate(); 1597 1598 if (errors != null && errors.size() > 0) { 1599 Iterator errorsIterator = errors.iterator(); 1600 StringBuffer message = new StringBuffer(); 1601 1602 Exception error = null; 1603 while (errorsIterator.hasNext()) { 1604 error = (Exception) errorsIterator.next(); 1605 message.append(error.getMessage()); 1606 1607 if (errorsIterator.hasNext()) { 1608 message.append("\n-------------- and --------------\n"); 1609 } 1610 } 1611 1612 // NOTE: We could use exception chaining here to report 1613 // the cause, but this leads to very verbose error 1614 // error messages that are not very friendly. 1615 // NOTE: For copy and paste to work, it is essential that the first 1616 // argument be this, not null. Copy and paste relies on being able 1617 // to identify the variable for which there is an exception evaluating it. 1618 // Why was this changed by someone to have a first argument be null? 1619 throw new IllegalActionException(this, error, message.toString()); 1620 } 1621 1622 // NOTE: The call to _propagate() above has already done 1623 // notification, but only if _needsEvaluation was true. 1624 // Note that this will not happen unless the expression is also null. 1625 // Thus, we do the call here only if _needsEvaluation was false. 1626 // Generally, this only happens on construction of parameters (?). 1627 // EAL 6/11/03 1628 // NOTE: Regrettably, this also happens when changing the value 1629 // of a parameter from non-null to null. This erroneously prevents 1630 // notification of this change. So this optimization is invalid. 1631 // I believe its intent was to prevent double invocation of this 1632 // method for each parameter, once when it is being constructed 1633 // and once when it's value is being set. 1634 // EAL 9/16/03 1635 // if (!_isLazy && !neededEvaluation) { 1636 if (!_isLazy) { 1637 NamedObj container = getContainer(); 1638 1639 if (container != null) { 1640 container.attributeChanged(this); 1641 } 1642 } 1643 1644 // The propagate call has evaluated all the value 1645 // listeners that are instances of Variable, 1646 // so we can assume they are validated as well. 1647 // EAL 9/14/06. 1648 Collection<Variable> result = null; 1649 if (_valueListeners != null) { 1650 result = new HashSet<Variable>(); 1651 Iterator listeners = _valueListeners.iterator(); 1652 while (listeners.hasNext()) { 1653 Object listener = listeners.next(); 1654 if (listener instanceof Variable) { 1655 result.add((Variable) listener); 1656 } 1657 } 1658 } 1659 return result; 1660 } 1661 1662 /** React to the change in the specified instance of Settable. 1663 * Mark this variable as needing reevaluation when next accessed. 1664 * Notify the value listeners of this variable. 1665 * @param settable The object that has changed value. 1666 */ 1667 @Override 1668 public void valueChanged(Settable settable) { 1669 if (!_needsEvaluation) { 1670 // If the value was set via an expression, then mark this 1671 // variable as needing evaluation. 1672 // NOTE: For some reason, until 12/24/02, there was no "if" 1673 // here, which means _needsEvaluation was set to true even 1674 // if this variable's value had been set by setToken(). Why? EAL 1675 if (_currentExpression != null) { 1676 _needsEvaluation = true; 1677 } 1678 1679 _notifyValueListeners(); 1680 } 1681 } 1682 1683 /** Construct a variable with the given container, name, and token. 1684 * The container argument must not be null, or a 1685 * NullPointerException will be thrown. This variable will use the 1686 * workspace of the container for synchronization and version counts. 1687 * If the name argument is null, then the name is set to the empty 1688 * string. Increment the version of the workspace. 1689 * @param container The container. 1690 * @param name The name. 1691 * @param token The token contained by this variable. 1692 * @param incrementWorkspaceVersion False to not add this to the workspace 1693 * or do anything else that might change the workspace version number. 1694 * @exception IllegalActionException If the container does not accept 1695 * a variable as its attribute. 1696 * @exception NameDuplicationException If the name coincides with a 1697 * variable already in the container. 1698 */ 1699 protected Variable(NamedObj container, String name, 1700 ptolemy.data.Token token, boolean incrementWorkspaceVersion) 1701 throws IllegalActionException, NameDuplicationException { 1702 super(container, name, incrementWorkspaceVersion); 1703 if (token != null) { 1704 // Notification is important here so that the attributeChanged() 1705 // method of the container is called. 1706 _setToken(token); 1707 1708 // Record the initial value so "Defaults" button works. 1709 // Note that we call the superclass only to avoid getting the 1710 // other effects of setting the expression. 1711 super.setExpression(token.toString()); 1712 } 1713 setPersistent(false); 1714 } 1715 1716 /////////////////////////////////////////////////////////////////// 1717 //// protected methods //// 1718 1719 /** Return a description of this variable. This returns the same 1720 * information returned by toString(), but with optional indenting 1721 * and brackets. 1722 * @param detail The level of detail. 1723 * @param indent The amount of indenting. 1724 * @param bracket The number of surrounding brackets (0, 1, or 2). 1725 * @return A string describing this variable. 1726 */ 1727 @Override 1728 protected String _description(int detail, int indent, int bracket) { 1729 // This method intentionally does not call super._description() 1730 // because the output here consists of the value returned 1731 // by toString() wrapped in curly brackets. The super._description() 1732 // method returns the name wrapped in curly brackets, which is 1733 // not what we want. 1734 try { 1735 workspace().getReadAccess(); 1736 1737 String result = _getIndentPrefix(indent); 1738 1739 if (bracket == 1 || bracket == 2) { 1740 result += "{"; 1741 } 1742 1743 result += toString(); 1744 1745 if (bracket == 2) { 1746 result += "}"; 1747 } 1748 1749 return result; 1750 } finally { 1751 workspace().doneReading(); 1752 } 1753 } 1754 1755 /** Evaluate the current expression to a token. If this variable 1756 * was last set directly with a token, then do nothing. In other words, 1757 * the expression is evaluated only if the value of the token was most 1758 * recently given by an expression. The expression is also evaluated 1759 * if any of the variables it refers to have changed since the last 1760 * evaluation. If the value of this variable 1761 * changes due to this evaluation, then notify all 1762 * value dependents and notify the container (if there is one) by 1763 * calling its attributeChanged() and attributeTypeChanged() methods, 1764 * as appropriate. An exception is thrown 1765 * if the expression is illegal, for example if a parse error occurs 1766 * or if there is a dependency loop. 1767 * <p> 1768 * If evaluation results in a token that is not of the same type 1769 * as the current type of the variable, then the type of the variable 1770 * is changed, unless the new type is incompatible with statically 1771 * specified types (setTypeEquals() and setTypeAtMost()). 1772 * If the type is changed, the attributeTypeChanged() method of 1773 * the container is called. The container can reject the change 1774 * by throwing an exception. 1775 * <p> 1776 * This method may trigger a model error, which is delegated up 1777 * the container hierarchy until an error handler is found, and 1778 * is ignored if no error handler is found. A model error occurs 1779 * if the expression cannot be parsed or cannot be evaluated. 1780 * <p> 1781 * Part of this method is read-synchronized on the workspace. 1782 * 1783 * @exception IllegalActionException If the expression cannot 1784 * be parsed or cannot be evaluated, or if a dependency loop is found. 1785 */ 1786 protected void _evaluate() throws IllegalActionException { 1787 1788 // NOTE: This method is vulnerable to the horrific threading 1789 // problems of the listener pattern as documented in Lee (2006), 1790 // The Problem with Threads. Previous implementations were 1791 // vulnerable in that if multiple threads were evaluating 1792 // variables simultaneously, where one dependended on the other, 1793 // an exception would be reported about a dependency loop, 1794 // even though none exists. It will not work to acquire 1795 // synchronization locks, because the evaluation of variables 1796 // triggers notification of the container and any other 1797 // "value dependents," which are arbitrary code that could 1798 // be evaluating other variables or acquiring locks. 1799 // Hence, a deadlock could occur. 1800 1801 // The solution here is to allow only one thread at a time 1802 // to proceed with the evaluation by explicitly waiting 1803 // for the thread to complete, releasing the lock on this 1804 // variable while waiting. 1805 1806 // NOTE: It is absolutely imperative that the lock on this 1807 // object not be held while evaluating and notifying. 1808 // That could (and will!) result in deadlock. 1809 synchronized (this) { 1810 // If this thread is already evaluating the token, and the value of the token has not yet 1811 // been set (_needsEvaluation is true), then this call to evaluate() must 1812 // have been triggered by evaluating the expression of this variable, 1813 // which means that the expression directly or indirectly refers 1814 // to itself. 1815 if (_needsEvaluation 1816 && _threadEvaluating == Thread.currentThread()) { 1817 _threadEvaluating = null; 1818 throw new CircularDependencyError(this, 1819 "There is a dependency loop" + " where " + getFullName() 1820 + " directly or indirectly" 1821 + " refers to itself in its expression: " 1822 + _currentExpression); 1823 } 1824 1825 // If another thread is currently evaluating this variable, then 1826 // we need to wait until finishes. We put a timeout here so as to 1827 // not lock up the system. Currently, we won't wait more than 1828 // 30 seconds. 1829 int count = 0; 1830 while (_threadEvaluating != null) { 1831 if (count > 30) { 1832 throw new IllegalActionException(this, 1833 "Timeout waiting to evaluate variable."); 1834 } 1835 try { 1836 wait(1000L); 1837 } catch (InterruptedException e) { 1838 throw new IllegalActionException(this, 1839 "Thread interrupted while evaluating variable."); 1840 } 1841 } 1842 // If the other thread has successfully evaluated this variable, we are done. 1843 if (!_needsEvaluation) { 1844 return; 1845 } 1846 _threadEvaluating = Thread.currentThread(); 1847 } 1848 1849 try { 1850 workspace().getReadAccess(); 1851 1852 // Simple case: no expression. Just set the token to null, 1853 // notify value dependents, and return. 1854 if (_currentExpression == null 1855 || (isStringMode() ? _currentExpression.equals("") 1856 : _currentExpression.trim().equals(""))) { 1857 _setTokenAndNotify(null); 1858 return; 1859 } 1860 1861 _parseIfNecessary(); 1862 1863 if (_parseTreeEvaluator == null) { 1864 _parseTreeEvaluator = new ParseTreeEvaluator(); 1865 } 1866 1867 if (_parserScope == null) { 1868 _parserScope = new VariableScope(); 1869 } 1870 1871 Token result = _parseTreeEvaluator.evaluateParseTree(_parseTree, 1872 _parserScope); 1873 _setTokenAndNotify(result); 1874 } catch (IllegalActionException ex) { 1875 synchronized (this) { 1876 _needsEvaluation = true; 1877 } 1878 // Ignore the error if we are inside a class definition 1879 // and the error is an undefined identifier. 1880 // This is because one may want to define a class that 1881 // contains default expressions that can only be evaluated 1882 // in the context of the instances. 1883 // The same is true of a dependency loop error, since the circular 1884 // dependency could be due to referencing a variable with the same 1885 // name that does not yet exist. 1886 if (!_isWithinClassDefinition() 1887 || (!(ex instanceof UndefinedConstantOrIdentifierException)) 1888 && !(ex instanceof CircularDependencyError)) { 1889 throw new IllegalActionException(this, ex, 1890 "Error evaluating expression:\n" + StringUtilities 1891 .truncateString(_currentExpression, 80, 1)); 1892 } 1893 } finally { 1894 workspace().doneReading(); 1895 synchronized (this) { 1896 _threadEvaluating = null; 1897 notifyAll(); 1898 } 1899 } 1900 } 1901 1902 /** Get the current expression as a string, to be used to export to MoML. 1903 * @return The current expression as a string. 1904 */ 1905 protected String _getCurrentExpression() { 1906 return _currentExpression; 1907 } 1908 1909 /** Notify the value listeners of this variable that this variable 1910 * changed. 1911 */ 1912 protected void _notifyValueListeners() { 1913 if (_valueListeners != null) { 1914 // Note that the listener list is a CopyOnWriteArrayList, 1915 // so this iterates over a snapshot of the list. 1916 Iterator listeners = _valueListeners.iterator(); 1917 1918 // Note that this could result in listeners being notified 1919 // in the opposite order in which the updates occur! See 1920 // Lee, The Problem with Threads, IEEE Computer, 2006. 1921 while (listeners.hasNext()) { 1922 ValueListener listener = (ValueListener) listeners.next(); 1923 listener.valueChanged(this); 1924 } 1925 } 1926 } 1927 1928 /** Parse the expression, if the current parse tree is not valid. 1929 * This method should only be called if the expression is valid. 1930 * @exception IllegalActionException If the exception cannot be parsed. 1931 */ 1932 protected final void _parseIfNecessary() throws IllegalActionException { 1933 if (!_parseTreeValid) { 1934 if (_currentExpression == null) { 1935 throw new IllegalActionException(this, 1936 "Empty expression cannot be parsed!"); 1937 } 1938 1939 PtParser parser = new PtParser(); 1940 1941 if (isStringMode()) { 1942 // Different parse rules for String mode parameters. 1943 if (isSuppressVariableSubstitution()) { 1944 // Suppressing parsing. Create an empty parse tree. 1945 // This is astonishingly difficult to do!!!! 1946 _parseTree = new ASTPtLeafNode(0); 1947 _parseTree.setConstant(true); 1948 _parseTree.setToken(new StringToken(_currentExpression)); 1949 _parseTree.setType(BaseType.STRING); 1950 } else { 1951 _parseTree = parser 1952 .generateStringParseTree(_currentExpression); 1953 } 1954 } else { 1955 // Normal parse rules for expressions. 1956 _parseTree = parser.generateParseTree(_currentExpression); 1957 } 1958 1959 _parseTreeValid = _parseTree != null; 1960 } 1961 } 1962 1963 /** Force evaluation of this variable, unless it is lazy, 1964 * and call _propagate() on its value dependents. 1965 * @return A list of instances of IllegalActionException, one 1966 * for each exception triggered by a failure to evaluate a 1967 * value dependent, or null if there were no failures. 1968 */ 1969 protected List<IllegalActionException> _propagate() { 1970 if (_propagating) { 1971 return null; 1972 } 1973 1974 _propagating = true; 1975 1976 try { 1977 List<IllegalActionException> result = null; 1978 1979 // Force evaluation. 1980 if (_needsEvaluation && !_isLazy) { 1981 try { 1982 // The following will not evaluate if _needsEvaluation has become false 1983 // in some other thread. 1984 _evaluate(); 1985 } catch (IllegalActionException ex) { 1986 // This is very confusing code. 1987 // Don't mess with it if it works. 1988 try { 1989 // Report the error. 1990 // NOTE: When first opening a model, no ModelErrorHandler 1991 // has yet been registered with the model, so the following 1992 // method will simply return false. This is probably reasonable 1993 // since it allows opening models even if they have error 1994 // conditions. 1995 if (!handleModelError(this, ex)) { 1996 1997 // FIXME: The following should throw the exception. 1998 // This requires retraining many tests in the MoML test directory, 1999 // or modifying them to have a change listener for the change requests. 2000 throw ex; 2001 // Warn about errors opening models. 2002 // There are a bunch of things that need to be fixed, but there are also 2003 // legitimate models such as ptolemy/actor/parameters/test/auto/ParameterSetTest.xml 2004 // that refer to a parameter not present when the model is parsed. 2005 /* 2006 System.out 2007 .println("The message below is a Warning, and can be ignored."); 2008 System.out 2009 .println("See https://projects.ecoinformatics.org/ecoinfo/issues/6000"); 2010 System.out.println("#### Start of Warning ####"); 2011 new IllegalActionException(this, ex, 2012 "Warning:, there was a problem propagating \"" 2013 + getName() + "\".") 2014 .printStackTrace(); 2015 System.out.println("#### End of Warning ####"); 2016 */ 2017 } 2018 } catch (IllegalActionException ex2) { 2019 // The handler handled the error by throwing an exception. 2020 // Return the exception in a list. 2021 result = new LinkedList<IllegalActionException>(); 2022 result.add(ex2); 2023 } 2024 } 2025 } 2026 2027 // All the value dependents now need evaluation also. 2028 List<IllegalActionException> additionalErrors = _propagateToValueListeners(); 2029 2030 if (result == null) { 2031 result = additionalErrors; 2032 } else { 2033 if (additionalErrors != null) { 2034 result.addAll(additionalErrors); 2035 } 2036 } 2037 2038 return result; 2039 } finally { 2040 _propagating = false; 2041 } 2042 } 2043 2044 /** Call propagate() on all value listeners. 2045 * @return A list of instances of IllegalActionException, one 2046 * for each exception triggered by a failure to evaluate a 2047 * value dependent, or null if there were no failures. 2048 */ 2049 protected List<IllegalActionException> _propagateToValueListeners() { 2050 List<IllegalActionException> result = null; 2051 2052 if (_valueListeners != null) { 2053 // Note that the listener list is a CopyOnWriteArrayList, 2054 // so this iterates over a snapshot of the list. 2055 Iterator listeners = _valueListeners.iterator(); 2056 while (listeners.hasNext()) { 2057 ValueListener listener = (ValueListener) listeners.next(); 2058 2059 // Propagate to value listeners. 2060 // Also, remove the listener from the _valueListeners list 2061 // if it is no longer in scope. 2062 if (listener instanceof Variable) { 2063 try { 2064 // Check that listener is still referencing this variable. 2065 if (((Variable) listener) 2066 .getVariable(getName()) != this) { 2067 // This variable is no longer in the scope of the listener. 2068 // Note that the listener list is a CopyOnWriteArrayList, 2069 // so this does not cause a concurrent modification exception. 2070 _valueListeners.remove(listener); 2071 continue; 2072 } 2073 } catch (IllegalActionException e) { 2074 // The listener has a reference to something with the name 2075 // of this variable, but that something cannot be evaluated. 2076 // It must not be this variable. 2077 // Note that the listener list is a CopyOnWriteArrayList, 2078 // so this does not cause a concurrent modification exception. 2079 _valueListeners.remove(listener); 2080 continue; 2081 } 2082 // Call propagate on the value listener. By checking _needsEvaluation, 2083 // we avoid doing this more than once if the the value 2084 // listener appears more than once. This also has 2085 // the advantage of stopping circular reference looping. 2086 if (((Variable) listener)._needsEvaluation) { 2087 List<IllegalActionException> additionalErrors = ((Variable) listener) 2088 ._propagate(); 2089 2090 if (additionalErrors != null) { 2091 if (result == null) { 2092 result = new LinkedList<IllegalActionException>(); 2093 } 2094 2095 result.addAll(additionalErrors); 2096 } 2097 } 2098 } 2099 } 2100 } 2101 2102 return result; 2103 } 2104 2105 /** Propagate the value of this object to the 2106 * specified object. The specified object is required 2107 * to be an instance of the same class as this one, or 2108 * a ClassCastException will be thrown. 2109 * @param destination Object to which to propagate the 2110 * value. 2111 * @exception IllegalActionException If the value cannot 2112 * be propagated. 2113 */ 2114 @Override 2115 protected void _propagateValue(NamedObj destination) 2116 throws IllegalActionException { 2117 ((Settable) destination).setExpression(getExpression()); 2118 } 2119 2120 /** Set the token value and type of the variable. 2121 * If the type of the specified token is incompatible with specified 2122 * absolute type constraints (i.e. those that can be checked), then 2123 * throw an exception. It is converted to the type given by 2124 * setTypeEquals() if necessary and possible. If the argument is null, 2125 * then no type checks are done, and the contents of the variable is set 2126 * to null. 2127 * @param newToken The new value of the variable. 2128 * @exception IllegalActionException If the token type is not 2129 * compatible with specified constraints, or if you are attempting 2130 * to set to null a variable that has value dependents. 2131 */ 2132 protected void _setToken(Token newToken) throws IllegalActionException { 2133 if (newToken == null) { 2134 _token = null; 2135 _needsEvaluation = false; 2136 2137 // set _varType 2138 if (_declaredType instanceof StructuredType) { 2139 ((StructuredType) _varType).initialize(BaseType.UNKNOWN); 2140 } else { 2141 _varType = _declaredType; 2142 } 2143 } else { 2144 // newToken is not null, check if it is compatible with 2145 // _declaredType. For structured types, _declaredType and _varType 2146 // are the same reference, need to initialize this type 2147 // before checking compatibility. But if the new token is not 2148 // compatible with the declared type, the current resolved type 2149 // need to be preserved, so make a clone. 2150 Type declaredType; 2151 2152 try { 2153 declaredType = (Type) _declaredType.clone(); 2154 } catch (CloneNotSupportedException cnse) { 2155 throw new InternalErrorException("Variable._setToken: " 2156 + "Cannot clone the declared type of this Variable."); 2157 } // FIXME: clone seems unnecessary 2158 2159 // [marten 05/28/13] 2160 // This doesn't seem to do anything, it substitutes 2161 // unknowns for unknowns 2162 /*if (declaredType instanceof StructuredType) { 2163 ((StructuredType) declaredType).initialize(BaseType.UNKNOWN); 2164 }*/ 2165 2166 // declared type must be >= proposed type, convert new token (upcast) 2167 if (declaredType.isCompatible(newToken.getType())) { 2168 newToken = declaredType.convert(newToken); 2169 } else { 2170 throw new IllegalActionException(this, 2171 "Variable._setToken: Cannot store a token of type " 2172 + newToken.getType().toString() 2173 + ", which is incompatible with type " 2174 + declaredType.toString()); 2175 } 2176 2177 // update _varType to the type of the new token, if 2178 // 1) a type was declared that has elements of type unknown 2179 // 2) no type was declared 2180 // otherwise, set _varType to _declaredType 2181 if (_declaredType instanceof StructuredType 2182 && !_declaredType.isConstant()) { 2183 ((StructuredType) _varType) 2184 .updateType((StructuredType) newToken.getType()); 2185 } else if (_declaredType.equals(BaseType.UNKNOWN)) { 2186 // this could be either a structured or basic type 2187 _varType = newToken.getType(); 2188 } else { 2189 // this can only be a basic type 2190 _varType = _declaredType; 2191 } 2192 2193 // Check setTypeAtMost constraint. 2194 if (_typeAtMost != BaseType.UNKNOWN) { 2195 // Recalculate this in case the type has changed. 2196 Type tokenType = newToken.getType(); 2197 int comparison = TypeLattice.compare(tokenType, _typeAtMost); 2198 2199 if (comparison == CPO.HIGHER 2200 || comparison == CPO.INCOMPARABLE) { 2201 // Incompatible type! 2202 throw new IllegalActionException(this, 2203 "Cannot store a token of type " 2204 + tokenType.toString() 2205 + ", which is not less than or equal to " 2206 + _typeAtMost.toString()); 2207 } 2208 } 2209 2210 if (_noTokenYet) { 2211 // This is the first token stored in this variable. 2212 _initialExpression = _currentExpression; 2213 2214 if (_currentExpression == null) { 2215 // The token is being set directly. 2216 _initialToken = newToken; 2217 } 2218 2219 _noTokenYet = false; 2220 } 2221 2222 _token = newToken; 2223 2224 _needsEvaluation = false; 2225 } 2226 } 2227 2228 /** Set the token value and type of the variable, and notify the 2229 * container that the value (and type, if appropriate) has changed. 2230 * Also notify value dependents that they need to be re-evaluated, 2231 * and notify any listeners that have been registered with 2232 * addValueListener(). 2233 * If setTypeEquals() has been called, then attempt to convert 2234 * the specified token into one of the appropriate type, if needed, 2235 * rather than changing the type. 2236 * @param newToken The new value of the variable. 2237 * @exception IllegalActionException If the token type is not 2238 * compatible with specified constraints, or if you are attempting 2239 * to set to null a variable that has value dependents. 2240 */ 2241 protected void _setTokenAndNotify(Token newToken) 2242 throws IllegalActionException { 2243 // Save to restore in case the change is rejected. 2244 Token oldToken = _token; 2245 Type oldVarType = _varType; 2246 2247 if (_varType instanceof StructuredType) { 2248 try { 2249 oldVarType = (Type) ((StructuredType) _varType).clone(); 2250 } catch (CloneNotSupportedException ex2) { 2251 throw new InternalErrorException("Variable._setTokenAndNotify: " 2252 + " Cannot clone _varType" + ex2.getMessage()); 2253 } 2254 } 2255 2256 boolean oldNoTokenYet = _noTokenYet; 2257 String oldInitialExpression = _initialExpression; 2258 Token oldInitialToken = _initialToken; 2259 2260 try { 2261 _setToken(newToken); 2262 2263 NamedObj container = getContainer(); 2264 2265 if (container != null) { 2266 if (!oldVarType.equals(_varType) 2267 && oldVarType != BaseType.UNKNOWN) { 2268 container.attributeTypeChanged(this); 2269 } 2270 2271 container.attributeChanged(this); 2272 } 2273 2274 _notifyValueListeners(); 2275 } catch (IllegalActionException ex) { 2276 // reverse the changes 2277 _token = oldToken; 2278 2279 if (_varType instanceof StructuredType 2280 && oldVarType instanceof StructuredType) { 2281 ((StructuredType) _varType) 2282 .updateType((StructuredType) oldVarType); 2283 } else { 2284 _varType = oldVarType; 2285 } 2286 2287 _noTokenYet = oldNoTokenYet; 2288 _initialExpression = oldInitialExpression; 2289 _initialToken = oldInitialToken; 2290 throw ex; 2291 } 2292 } 2293 2294 /////////////////////////////////////////////////////////////////// 2295 //// protected variables //// 2296 2297 /** Stores the expression used to set this variable. It is null if 2298 * the variable was set from a token. 2299 */ 2300 protected String _currentExpression = null; 2301 2302 /** Flags that the expression needs to be evaluated when the value of this 2303 * variable is queried. 2304 */ 2305 protected boolean _needsEvaluation = false; 2306 2307 /** Indicator that the parse tree is valid. */ 2308 protected boolean _parseTreeValid = false; 2309 2310 /** The instance of VariableScope. */ 2311 protected ParserScope _parserScope = null; 2312 2313 /** True to suppress variable substitution in string mode. */ 2314 protected boolean _suppressVariableSubstitution = false; 2315 2316 /** Listeners for changes in value. */ 2317 protected List<ValueListener> _valueListeners; 2318 2319 /////////////////////////////////////////////////////////////////// 2320 //// private methods //// 2321 2322 /** Scope implementation with local caching. */ 2323 protected class VariableScope extends ModelScope { 2324 /** Construct a scope consisting of the variables 2325 * of the container of the the enclosing instance of 2326 * Variable and its containers and their scope-extending 2327 * attributes. 2328 */ 2329 public VariableScope() { 2330 this(null); 2331 } 2332 2333 /** Construct a scope consisting of the variables 2334 * of the specified container its containers and their 2335 * scope-extending attributes. If the argument is null, 2336 * then use the container of the enclosing instance of 2337 * Variable as the reference for the scope. 2338 * @param reference The reference for the scope. 2339 */ 2340 public VariableScope(NamedObj reference) { 2341 _reference = reference; 2342 } 2343 2344 /** Look up and return the attribute with the specified name in the 2345 * scope. Return null if such an attribute does not exist. 2346 * @param name The name of the attribute. 2347 * @return The attribute with the specified name in the scope. 2348 * @exception IllegalActionException If a value in the scope 2349 * exists with the given name, but cannot be evaluated. 2350 */ 2351 @Override 2352 public ptolemy.data.Token get(String name) 2353 throws IllegalActionException { 2354 Variable results = getVariable(name); 2355 if (results != null && results.getToken() != null) { 2356 return results.getToken(); 2357 } else { 2358 NamedObj reference; 2359 if (_reference == null) { 2360 reference = getContainer(); 2361 } else { 2362 reference = _reference; 2363 } 2364 NamedObj object = getScopedObject(reference, name); 2365 if (object instanceof Variable) { 2366 return ((Variable) object).getToken(); 2367 } else if (object != null) { 2368 return new ObjectToken(object, object.getClass()); 2369 } else { 2370 return null; 2371 } 2372 } 2373 } 2374 2375 /** Look up and return the type of the attribute with the 2376 * specified name in the scope. Return null if such an 2377 * attribute does not exist. 2378 * @param name The name of the attribute. 2379 * @return The attribute with the specified name in the scope. 2380 * @exception IllegalActionException If a value in the scope 2381 * exists with the given name, but cannot be evaluated. 2382 */ 2383 @Override 2384 public Type getType(String name) throws IllegalActionException { 2385 NamedObj reference; 2386 if (_reference == null) { 2387 reference = getContainer(); 2388 } else { 2389 reference = _reference; 2390 } 2391 NamedObj object = getScopedObject(reference, name); 2392 if (object instanceof Variable) { 2393 return ((Variable) object).getType(); 2394 } else if (object != null) { 2395 return new ObjectType(object, object.getClass()); 2396 } else { 2397 return null; 2398 } 2399 } 2400 2401 /** Look up and return the type term for the specified name 2402 * in the scope. Return null if the name is not defined in this 2403 * scope, or is a constant type. 2404 * @param name The name of the attribute. 2405 * @return The InequalityTerm associated with the given name in 2406 * the scope. 2407 * @exception IllegalActionException If a value in the scope 2408 * exists with the given name, but cannot be evaluated. 2409 */ 2410 @Override 2411 public InequalityTerm getTypeTerm(String name) 2412 throws IllegalActionException { 2413 NamedObj reference = _reference; 2414 2415 if (_reference == null) { 2416 reference = getContainer(); 2417 } 2418 2419 Variable result = getScopedVariable(Variable.this, reference, name); 2420 2421 if (result != null) { 2422 return result.getTypeTerm(); 2423 } else { 2424 return null; 2425 } 2426 } 2427 2428 /** Look up and return the attribute with the specified name in the 2429 * scope. Return null if such an attribute does not exist. 2430 * This method acquires read permission on the workspace. 2431 * @param name The name of the attribute. 2432 * @return The attribute with the specified name in the scope. 2433 * @exception IllegalActionException If a value in the scope 2434 * exists with the given name, but cannot be evaluated. 2435 */ 2436 public Variable getVariable(String name) throws IllegalActionException { 2437 workspace().getReadAccess(); 2438 try { 2439 NamedObj reference = _reference; 2440 if (_reference == null) { 2441 reference = getContainer(); 2442 } 2443 Variable result = getScopedVariable(Variable.this, reference, 2444 name); 2445 2446 if (result != null) { 2447 // NOTE: The following acquires a lock on result. 2448 result.addValueListener(Variable.this); 2449 // Add the result to the list of variables that we depend on so that 2450 // the dependence can later be broken by invalidate. 2451 synchronized (Variable.this) { 2452 if (_variablesDependentOn == null) { 2453 _variablesDependentOn = new HashMap<String, Variable>(); 2454 } 2455 _variablesDependentOn.put(name, result); 2456 } 2457 } 2458 return result; 2459 } finally { 2460 workspace().doneReading(); 2461 } 2462 } 2463 2464 /** Return the set of identifiers within the scope. 2465 * @return The set of variable names within the scope. 2466 */ 2467 @Override 2468 public Set<String> identifierSet() { 2469 NamedObj reference = _reference; 2470 2471 if (_reference == null) { 2472 reference = getContainer(); 2473 } 2474 2475 Set<String> identifiers = new HashSet<String>( 2476 getAllScopedVariableNames(Variable.this, reference)); 2477 identifiers.addAll(getAllScopedObjectNames(reference)); 2478 2479 return identifiers; 2480 } 2481 2482 // Reference object for the scope. 2483 private NamedObj _reference; 2484 } 2485 2486 /** Invalidate any variables contained by the specified object 2487 * or by instances of ScopeExtendingAttribute that it contains 2488 * whose name matches that of this variable. Then do the same 2489 * for the container of the specified object. 2490 * @param object The containers in which to invalidate variables. 2491 */ 2492 private void _invalidateShadowedSettables(NamedObj object) 2493 throws IllegalActionException { 2494 if (object == null) { 2495 // Nothing to do. 2496 return; 2497 } 2498 2499 for (Object element : object.attributeList(Variable.class)) { 2500 Variable variable = (Variable) element; 2501 2502 if (variable.getName().equals(getName())) { 2503 variable.invalidate(); 2504 } 2505 } 2506 2507 // Also invalidate the variables inside any 2508 // scopeExtendingAttributes. 2509 Iterator scopeAttributes = object 2510 .attributeList(ScopeExtendingAttribute.class).iterator(); 2511 2512 while (scopeAttributes.hasNext()) { 2513 ScopeExtendingAttribute attribute = (ScopeExtendingAttribute) scopeAttributes 2514 .next(); 2515 Iterator variables = attribute.attributeList(Variable.class) 2516 .iterator(); 2517 2518 while (variables.hasNext()) { 2519 Variable variable = (Variable) variables.next(); 2520 2521 if (variable.getName().equals(getName())) { 2522 variable.invalidate(); 2523 } 2524 } 2525 } 2526 2527 NamedObj container = object.getContainer(); 2528 2529 if (container != null) { 2530 _invalidateShadowedSettables(container); 2531 } 2532 } 2533 2534 /** Return true if this object is within 2535 * a class definition, which means that 2536 * any container above it in the hierarchy is 2537 * a class definition. 2538 * @return True if this object is within a class definition. 2539 * @see ptolemy.kernel.InstantiableNamedObj#setClassDefinition(boolean) 2540 * @see Instantiable 2541 */ 2542 private boolean _isWithinClassDefinition() { 2543 NamedObj container = getContainer(); 2544 while (container != null) { 2545 if (container instanceof InstantiableNamedObj) { 2546 if (((InstantiableNamedObj) container) 2547 .isWithinClassDefinition()) { 2548 return true; 2549 } 2550 // No need to continue since isWithinClassDefinition() 2551 // works its way further up the hierarchy. 2552 return false; 2553 } 2554 container = container.getContainer(); 2555 } 2556 return false; 2557 } 2558 2559 /////////////////////////////////////////////////////////////////// 2560 //// private variables //// 2561 2562 // Empty string token. 2563 private static StringToken _EMPTY_STRING_TOKEN = new StringToken(""); 2564 2565 // Type constraints. 2566 private Set<Inequality> _constraints = new HashSet<Inequality>(); 2567 2568 // The type set by setTypeEquals(). If _declaredType is not 2569 // BaseType.UNKNOWN, the type of this Variable is fixed to that type. 2570 private Type _declaredType = BaseType.UNKNOWN; 2571 2572 // The thread that is currently evaluating this variable, if any. 2573 private transient Thread _threadEvaluating = null; 2574 2575 // Stores the expression used to initialize this variable. It is null if 2576 // the first token placed in the variable is not the result of evaluating 2577 // an expression. 2578 private String _initialExpression; 2579 2580 // Stores the first token placed in this variable. It is null if the 2581 // first token contained by this variable was the result of evaluating 2582 // an expression. 2583 private ptolemy.data.Token _initialToken; 2584 2585 // Indicator that this variable is lazy. 2586 private boolean _isLazy; 2587 2588 // Indicates if string mode is on. 2589 private boolean _isStringMode = false; 2590 2591 // Indicates whether this variable has been flagged as unknown. 2592 private boolean _isTokenUnknown = false; 2593 2594 // Flags whether the variable has not yet contained a token. 2595 private boolean _noTokenYet = true; 2596 2597 // If the variable was last set from an expression, this stores 2598 // the parse tree for that expression. 2599 private ASTPtRootNode _parseTree; 2600 2601 // the parse tree evaluator used by this variable. 2602 private ParseTreeEvaluator _parseTreeEvaluator; 2603 2604 // Flag indicating that _propagate() is in progress. 2605 private boolean _propagating; 2606 2607 // The token contained by this variable. 2608 private ptolemy.data.Token _token; 2609 2610 // If setTypeAtMost() has been called, then the type bound is stored here. 2611 private Type _typeAtMost = BaseType.UNKNOWN; 2612 2613 // Reference to the inner class that implements InequalityTerm. 2614 private TypeTerm _typeTerm = null; 2615 2616 // Stores the Class object which represents the type of this variable. 2617 private Type _varType = BaseType.UNKNOWN; 2618 2619 /** Stores the variables that are referenced by this variable. */ 2620 private HashMap<String, Variable> _variablesDependentOn = null; 2621 2622 // The visibility of this variable. 2623 private Settable.Visibility _visibility = Settable.EXPERT; 2624 2625 /** Value listeners that should not be treated as true dependencies. */ 2626 private Set<ValueListener> _weakValueListeners; 2627 2628 /////////////////////////////////////////////////////////////////// 2629 //// inner classes //// 2630 2631 /** Subclass of IllegalActionException for use in reporting 2632 * circular dependency errors. 2633 */ 2634 @SuppressWarnings("serial") 2635 public static class CircularDependencyError extends IllegalActionException { 2636 public CircularDependencyError(Nameable object, String detail) { 2637 super(object, detail); 2638 } 2639 } 2640 2641 private class TypeTerm implements InequalityTerm { 2642 /////////////////////////////////////////////////////////////// 2643 //// public inner methods //// 2644 2645 /** Return this Variable. 2646 * @return A Variable. 2647 */ 2648 @Override 2649 public Object getAssociatedObject() { 2650 return Variable.this; 2651 } 2652 2653 /** Return the type of this Variable. 2654 */ 2655 @Override 2656 public Object getValue() { 2657 return getType(); 2658 } 2659 2660 /** Return this TypeTerm in an array if this term represent 2661 * a type variable. This term represents a type variable 2662 * if the type of this variable is not set through setTypeEquals(). 2663 * If the type of this variable is set, return an array of size zero. 2664 * @return An array of InequalityTerm. 2665 */ 2666 @Override 2667 public InequalityTerm[] getVariables() { 2668 if (isSettable()) { 2669 InequalityTerm[] result = new InequalityTerm[1]; 2670 result[0] = this; 2671 return result; 2672 } 2673 2674 return new InequalityTerm[0]; 2675 } 2676 2677 /** Reset the variable part of this type to the specified type. 2678 * @param e A Type. 2679 * @exception IllegalActionException If the type is not settable, 2680 * or the argument is not a Type. 2681 */ 2682 @Override 2683 public void initialize(Object e) throws IllegalActionException { 2684 if (!isSettable()) { 2685 throw new IllegalActionException( 2686 "TypeTerm.initialize: " + "The type is not settable."); 2687 } 2688 2689 if (!(e instanceof Type)) { 2690 throw new IllegalActionException("TypeTerm.initialize: " 2691 + "The argument is not a Type."); 2692 } 2693 2694 if (_declaredType == BaseType.UNKNOWN) { 2695 _varType = (Type) e; 2696 } else { 2697 // _declaredType is a StructuredType 2698 ((StructuredType) _varType).initialize((Type) e); 2699 } 2700 } 2701 2702 /** Test if the type of this variable is fixed. The type is fixed if 2703 * setTypeEquals() is called with an argument that is not 2704 * BaseType.UNKNOWN, or the user has set a non-null expression or 2705 * token into this variable. 2706 * @return True if the type of this variable can be set; 2707 * false otherwise. 2708 */ 2709 @Override 2710 public boolean isSettable() { 2711 return !_declaredType.isConstant(); 2712 } 2713 2714 /** Check whether the current value of this term is acceptable. 2715 * This method delegates the check to the isTypeAcceptable() 2716 * method of the outer class. 2717 * @return True if the current value is acceptable. 2718 */ 2719 @Override 2720 public boolean isValueAcceptable() { 2721 return isTypeAcceptable(); 2722 } 2723 2724 /** Set the type of this variable. 2725 * @param e a Type. 2726 * @exception IllegalActionException If this type is not settable, 2727 * or this type cannot be updated to the new type. 2728 */ 2729 @Override 2730 public void setValue(Object e) throws IllegalActionException { 2731 if (!isSettable()) { 2732 throw new IllegalActionException( 2733 "TypeTerm.setValue: The " + "type is not settable."); 2734 } 2735 2736 if (!_declaredType.isSubstitutionInstance((Type) e)) { 2737 throw new IllegalActionException("Variable$TypeTerm" 2738 + ".setValue: " 2739 + "Cannot update the type of this variable to the " 2740 + "new type." + " Variable: " + getFullName() 2741 + ", Variable type: " + _declaredType.toString() 2742 + ", New type: " + e.toString()); 2743 } 2744 2745 if (_declaredType == BaseType.UNKNOWN) { 2746 _varType = (Type) e; 2747 } else { 2748 // _declaredType is a StructuredType 2749 ((StructuredType) _varType).updateType((StructuredType) e); 2750 } 2751 } 2752 2753 /** Override the base class to give a description of the variable 2754 * and its type. 2755 * @return A description of the variable and its type. 2756 */ 2757 @Override 2758 public String toString() { 2759 return "(variable " + getFullName() + ": " + getType() + ")"; 2760 } 2761 } 2762}