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}