001/* A transition in an FSMActor.
002
003   Copyright (c) 1999-2014 The Regents of the University of California.
004   All rights reserved.
005   Permission is hereby granted, without written agreement and without
006   license or royalty fees, to use, copy, modify, and distribute this
007   software and its documentation for any purpose, provided that the above
008   copyright notice and the following two paragraphs appear in all copies
009   of this software.
010
011   IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012   FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013   ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014   THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015   SUCH DAMAGE.
016
017   THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020   PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021   CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022   ENHANCEMENTS, OR MODIFICATIONS.
023
024   PT_COPYRIGHT_VERSION_2
025   COPYRIGHTENDKEY
026 */
027package ptolemy.domains.modal.kernel;
028
029import java.util.Iterator;
030import java.util.LinkedList;
031import java.util.List;
032import java.util.StringTokenizer;
033
034import ptolemy.actor.CompositeActor;
035import ptolemy.actor.Director;
036import ptolemy.actor.TypedActor;
037import ptolemy.actor.TypedCompositeActor;
038import ptolemy.data.BooleanToken;
039import ptolemy.data.StringToken;
040import ptolemy.data.Token;
041import ptolemy.data.expr.ASTPtRootNode;
042import ptolemy.data.expr.Parameter;
043import ptolemy.data.expr.ParseTreeEvaluator;
044import ptolemy.data.expr.ParserScope;
045import ptolemy.data.expr.PtParser;
046import ptolemy.data.expr.StringParameter;
047import ptolemy.data.expr.Variable;
048import ptolemy.data.type.BaseType;
049import ptolemy.kernel.ComponentRelation;
050import ptolemy.kernel.CompositeEntity;
051import ptolemy.kernel.Port;
052import ptolemy.kernel.attributes.VersionAttribute;
053import ptolemy.kernel.util.Attribute;
054import ptolemy.kernel.util.IllegalActionException;
055import ptolemy.kernel.util.InternalErrorException;
056import ptolemy.kernel.util.Location;
057import ptolemy.kernel.util.NameDuplicationException;
058import ptolemy.kernel.util.Nameable;
059import ptolemy.kernel.util.NamedObj;
060import ptolemy.kernel.util.Settable;
061import ptolemy.kernel.util.StreamListener;
062import ptolemy.kernel.util.StringAttribute;
063import ptolemy.kernel.util.Workspace;
064import ptolemy.moml.MoMLChangeRequest;
065
066///////////////////////////////////////////////////////////////////
067//// Transition
068
069/**
070   A Transition has a source state and a destination state. A
071   transition has a guard expression, which is evaluated to a boolean value.
072   Whenever a transition out of the current state
073   is enabled, it must be taken in the current firing.
074   That is, unlike some state machines formalisms, our guard is not just
075   an enabler for the transition but rather a trigger for the transition.
076
077   <p> A transition can contain actions. The way to specify actions is
078   to give value to the <i>outputActions</i> parameter and the
079   <i>setActions</i> parameter.
080
081   The value of these parameters is a string of the form:
082   <pre>
083   <i>command</i>; <i>command</i>; ...
084   </pre>
085   where each <i>command</i> has the form:
086   <pre>
087   <i>destination</i> = <i>expression</i>
088   </pre>
089   For the <i>outputActions</i> parameter, <i>destination</i> is either
090   <pre>
091   <i>portName</i>
092   </pre>
093   or
094   <pre>
095   <i>portName</i>(<i>channelNumber</i>)
096   </pre>
097   Here, <i>portName</i> is the name of a port of the FSM actor,
098   If no <i>channelNumber</i> is given, then the value
099   is broadcast to all channels of the port.
100   <p>
101   For the <i>setActions</i> parameter, <i>destination</i> is
102   <pre>
103   <i>variableName</i>
104   </pre>
105   <i>variableName</i> identifies either a variable or parameter of
106   the FSM actor, or a variable or parameter of the refinement of the
107   destination state of the transition. To give a variable of the
108   refinement, use a dotted name, as follows:
109   <pre>
110   <i>refinementName</i>.<i>variableName</i>
111   </pre>
112   The <i>expression</i> is a string giving an expression in the usual
113   Ptolemy II expression language. The expression may include references
114   to variables and parameters contained by the FSM actor.
115   <p>
116   The <i>outputActions</i> and <i>setActions</i> parameters are not the only
117   ways to specify actions. In fact, you can add action attributes that are
118   instances of anything that inherits from Action.
119   (Use the Add button in the Edit Parameters dialog).
120   <p>
121   An action is either a ChoiceAction or a CommitAction. The <i>setActions</i>
122   parameter is a CommitAction, whereas the <i>outputActions</i> parameter is a
123   ChoiceAction. A commit action is executed when the transition is taken to
124   change the state of the FSM, in the postfire() method of FSMActor.
125   A choice action, by contrast, is executed in the fire() method
126   of the FSMActor when the transition is chosen, but not yet taken.
127   The difference is subtle, and for most domains, irrelevant.
128   A few domains, however, such as CT, which have fixed point semantics,
129   where the fire() method may be invoked several times before the
130   transition is taken (committed). For such domains, it is useful
131   to have actions that fulfill the ChoiceAction interface.
132   Such actions participate in the search for a fixed point, but
133   do not change the state of the FSM.
134   <p>
135   A transition can be preemptive or non-preemptive. When a preemptive transition
136   is chosen, the refinement of its source state is not fired. A non-preemptive
137   transition is only chosen after the refinement of its source state is fired.
138   <p>
139   The <i>history</i> parameter specifies whether the refinement of the destination
140   state refinement is initialized when the transition is taken. By default, this
141   is false, which means that the destination refinement is initialized.
142   If you change this to true, then the destination refinement will not be
143   initialized, so when the state is re-entered, the refinement will
144   continue executing where it left off.
145   <p>
146   The <i>nondeterministic</i> parameter specifies whether this transition is
147   nondeterministic. Here nondeterministic means that this transition may not
148   be the only enabled transition at a time. The default value is a boolean
149   token with value as false, meaning that if this transition is enabled, it
150   must be the only enabled transition.
151   <p>
152   The <i>immediateTransition</i> parameter, if given a value true, specifies
153   that this transition is may be taken as soon as its source state is entered,
154   in the same iteration. This may lead to transient states, where a state is
155   passed through without ever becoming the current state.
156   <p>
157   The <i>defaultTransition</i> parameter, if given a value true, specifies
158   that this transition is enabled if no other non-default
159   transition is enabled and if its guard evaluates to true.
160   <p>
161   The <i>error</i> parameter, if given a value true, specifies
162   that this transition is enabled if the refinement of the source state of
163   the transition throws a model error or an exception
164   while executing. The default value is a boolean
165   token with value false. When such an exception or model error
166   occurs, two variables are set that may be used in the guard
167   or the output or set actions of this transition:
168   <ul>
169   <li> <i>errorMessage</i>: The error message (a string).
170   <li> <i>errorClass</i>: The class of the exception thrown.
171   </ul>
172   In addition, if the exception is an instance of KernelException
173   or a subclass (such as IllegalActionException), then a third
174   variable is set:
175   <ul>
176   <li> <i>errorCause</i>: The Ptolemy object that caused the exception.
177   </ul>
178   The <i>errorCause</i> is made available as an ObjectToken on which
179   you can invoke methods such as getName() in the guard or output
180   or set actions of this transition.
181
182   @author Xiaojun Liu, Edward A. Lee, Haiyang Zheng, Christian Motika
183   @version $Id$
184   @since Ptolemy II 8.0
185   @Pt.ProposedRating Yellow (hyzheng)
186   @Pt.AcceptedRating Red (hyzheng)
187   @see State
188   @see Action
189   @see ChoiceAction
190   @see CommitAction
191   @see CommitActionsAttribute
192   @see FSMActor
193   @see OutputActionsAttribute
194 */
195public class Transition extends ComponentRelation {
196    /** Construct a transition with the given name contained by the specified
197     *  entity. The container argument must not be null, or a
198     *  NullPointerException will be thrown. This transition will use the
199     *  workspace of the container for synchronization and version counts.
200     *  If the name argument is null, then the name is set to the empty string.
201     *  @param container The container.
202     *  @param name The name of the transition.
203     *  @exception IllegalActionException If the container is incompatible
204     *   with this transition.
205     *  @exception NameDuplicationException If the name coincides with
206     *   any relation already in the container.
207     */
208    public Transition(FSMActor container, String name)
209            throws IllegalActionException, NameDuplicationException {
210        super(container, name);
211        _init();
212    }
213
214    /** Construct a transition in the given workspace with an empty string
215     *  as a name.
216     *  If the workspace argument is null, use the default workspace.
217     *  The object is added to the workspace directory.
218     *  Increment the version of the workspace.
219     *  @param workspace The workspace for synchronization and version
220     *  tracking.
221     *  @exception IllegalActionException If the container is incompatible
222     *   with this transition.
223     *  @exception NameDuplicationException If the name coincides with
224     *   any relation already in the container.
225     */
226    public Transition(Workspace workspace)
227            throws IllegalActionException, NameDuplicationException {
228        super(workspace);
229        _init();
230    }
231
232    ///////////////////////////////////////////////////////////////////
233    ////                         public methods                    ////
234
235    /** React to a change in an attribute. If the changed attribute is
236     *  the <i>preemptive</i> parameter, evaluate the parameter. If the
237     *  parameter is given an expression that does not evaluate to a
238     *  boolean value, throw an exception; otherwise increment the
239     *  version number of the workspace.
240     *  @param attribute The attribute that changed.
241     *  @exception IllegalActionException If thrown by the superclass
242     *   attributeChanged() method, or the changed attribute is the
243     *   <i>preemptive</i> parameter and is given an expression that
244     *   does not evaluate to a boolean value.
245     */
246    @Override
247    public void attributeChanged(Attribute attribute)
248            throws IllegalActionException {
249        if (attribute == preemptive) {
250            // evaluate the parameter to make sure it is given a valid
251            // expression
252            preemptive.getToken();
253            workspace().incrVersion();
254        } else if (attribute == immediate) {
255            _immediate = ((BooleanToken) immediate.getToken()).booleanValue();
256        } else if (attribute == nondeterministic) {
257            _nondeterministic = ((BooleanToken) nondeterministic.getToken())
258                    .booleanValue();
259        } else if (attribute == guardExpression) {
260            // The guard expression can only be evaluated at run
261            // time, because the input variables it can reference are created
262            // at run time. The guardExpression is a string
263            // attribute used to convey expressions without being evaluated.
264            // _guard is the variable that does the evaluation.
265            _guardParseTree = null;
266            _guardParseTreeVersion = -1;
267            _parseTreeEvaluatorVersion = -1;
268        } else if (attribute == refinementName) {
269            _refinementVersion = -1;
270        } else if (attribute == outputActions || attribute == setActions) {
271            _actionListsVersion = -1;
272        } else {
273            super.attributeChanged(attribute);
274        }
275
276        if (attribute == outputActions && _debugging) {
277            outputActions.addDebugListener(new StreamListener());
278        } else if (attribute == setActions && _debugging) {
279            setActions.addDebugListener(new StreamListener());
280        } else if (attribute == fsmTransitionParameterName) {
281            if (((BooleanToken) showFSMTransitionParameter.getToken())
282                    .booleanValue()) {
283                _getFSMTransitionParameter();
284                try {
285                    _fsmTransitionParameter
286                            .setName(((StringToken) fsmTransitionParameterName
287                                    .getToken()).stringValue());
288                } catch (NameDuplicationException e) {
289                    throw new IllegalActionException(this, e.getCause(),
290                            e.getLocalizedMessage());
291                }
292            }
293        } else if (attribute == showFSMTransitionParameter) {
294            if (((BooleanToken) showFSMTransitionParameter.getToken())
295                    .booleanValue()) {
296                _getFSMTransitionParameter();
297                if (_fsmTransitionParameter != null) {
298                    _fsmTransitionParameter.hide(false);
299                    _fsmTransitionParameter.setPersistent(true);
300                }
301                showFSMTransitionParameter.setPersistent(true);
302                fsmTransitionParameterName.setPersistent(true);
303            } else {
304                if (_fsmTransitionParameter != null) {
305                    _fsmTransitionParameter.setPersistent(false);
306                }
307                showFSMTransitionParameter.setPersistent(false);
308                fsmTransitionParameterName.setPersistent(false);
309            }
310        }
311    }
312
313    /** Return the list of choice actions contained by this transition.
314     *  @return The list of choice actions contained by this transition.
315     */
316    public List choiceActionList() {
317        if (_actionListsVersion != workspace().getVersion()) {
318            _updateActionLists();
319        }
320
321        return _choiceActionList;
322    }
323
324    /** Clone the transition into the specified workspace. This calls the
325     *  base class and then sets the attribute public members to refer to
326     *  the attributes of the new transition.
327     *  @param workspace The workspace for the new transition.
328     *  @return A new transition.
329     *  @exception CloneNotSupportedException If a derived class contains
330     *   an attribute that cannot be cloned.
331     */
332    @Override
333    public Object clone(Workspace workspace) throws CloneNotSupportedException {
334        Transition newObject = (Transition) super.clone(workspace);
335        /*
336          newObject.guardExpression = (StringAttribute) newObject
337          .getAttribute("guardExpression");
338          newObject.preemptive = (Parameter) newObject.getAttribute("preemptive");
339          newObject.immediate = (Parameter) newObject.getAttribute("immediate");
340          newObject.refinementName = (StringAttribute) newObject.getAttribute("refinementName");
341         */
342        newObject._actionListsVersion = -1;
343        newObject._choiceActionList = new LinkedList();
344        newObject._commitActionList = new LinkedList();
345        newObject._destinationState = null;
346        newObject._fsmTransitionParameter = null;
347        newObject._guardParseTree = null;
348        newObject._guardParseTreeVersion = -1;
349        // newObject._historySet = false;
350        newObject._parseTreeEvaluatorVersion = -1;
351        newObject._parseTreeEvaluator = null;
352        newObject._refinementVersion = -1;
353        newObject._refinement = null;
354        newObject._sourceState = null;
355        newObject._stateVersion = -1;
356        return newObject;
357    }
358
359    /** Return the list of commit actions contained by this transition.
360     *  @return The list of commit actions contained by this transition.
361     */
362    public List commitActionList() {
363        if (_actionListsVersion != workspace().getVersion()) {
364            _updateActionLists();
365        }
366
367        return _commitActionList;
368    }
369
370    /** Return the destination state of this transition.
371     *  @return The destination state of this transition.
372     */
373    public State destinationState() {
374        if (_stateVersion != workspace().getVersion()) {
375            _checkConnectedStates();
376        }
377
378        return _destinationState;
379    }
380
381    /** Return the full label, which may include the guard expression,
382     *  the output expression and the set actions.
383     *  @return The full label
384     */
385    public String getFullLabel() {
386        StringBuffer buffer = new StringBuffer("");
387        boolean hasAnnotation = false;
388        String text;
389        try {
390            text = annotation.stringValue();
391        } catch (IllegalActionException e) {
392            text = "Exception evaluating annotation: " + e.getMessage();
393        }
394        if (!text.trim().equals("")) {
395            hasAnnotation = true;
396            buffer.append(text);
397        }
398
399        String guard = getGuardExpression();
400        if (guard != null && !guard.trim().equals("")) {
401            if (hasAnnotation) {
402                buffer.append("\n");
403            }
404            buffer.append("guard: ");
405            buffer.append(guard);
406        }
407
408        String expression = outputActions.getExpression();
409        if (expression != null && !expression.trim().equals("")) {
410            buffer.append("\n");
411            buffer.append("output: ");
412            buffer.append(expression);
413        }
414
415        expression = setActions.getExpression();
416        if (expression != null && !expression.trim().equals("")) {
417            buffer.append("\n");
418            buffer.append("set: ");
419            buffer.append(expression);
420        }
421        return buffer.toString();
422    }
423
424    /** Return the guard expression. The guard expression should evaluate
425     *  to a boolean value.
426     *  @return The guard expression.
427     *  @see #setGuardExpression
428     */
429    public String getGuardExpression() {
430        return guardExpression.getExpression();
431    }
432
433    /** Return a string describing this transition. The string has up to
434     *  three lines. The first line is the guard expression, preceded
435     *  by "guard: ".  The second line is the <i>outputActions</i> preceded
436     *  by the string "output: ". The third line is the
437     *  <i>setActions</i> preceded by the string "set: ". If any of these
438     *  is missing, then the corresponding line is omitted.
439     *  @return A string describing this transition.
440     */
441    public String getLabel() {
442        try {
443            if (((BooleanToken) showFSMTransitionParameter.getToken())
444                    .booleanValue()) {
445                return ((StringToken) fsmTransitionParameterName.getToken())
446                        .stringValue();
447            } else {
448                return getFullLabel();
449            }
450        } catch (IllegalActionException e) {
451            return "Exception evaluating annotation: " + e.getMessage();
452        }
453    }
454
455    /** Return the parse tree evaluator used by this transition to evaluate
456     *  the guard expression.
457     *  @return ParseTreeEvaluator for evaluating the guard expression.
458     */
459    public ParseTreeEvaluator getParseTreeEvaluator() {
460        if (_parseTreeEvaluatorVersion != workspace().getVersion()) {
461            // If there is no current parse tree evaluator,
462            // then create one. If this transition is under the control
463            // of an FSMDirector, then delegate creation to that director.
464            // Otherwise, create a default instance of ParseTreeEvaluator.
465            FSMDirector director = _getDirector();
466            if (director != null) {
467                _parseTreeEvaluator = director.getParseTreeEvaluator();
468            } else {
469                // When this transition is used inside an FSMActor.
470                if (_parseTreeEvaluator == null) {
471                    _parseTreeEvaluator = new ParseTreeEvaluator();
472                }
473            }
474            _parseTreeEvaluatorVersion = workspace().getVersion();
475        }
476        return _parseTreeEvaluator;
477    }
478
479    /** Return the refinements of this transition. The names of the refinements
480     *  are specified by the <i>refinementName</i> attribute. The refinements
481     *  must be instances of TypedActor and have the same container as
482     *  the FSMActor containing this state, otherwise an exception is thrown.
483     *  This method can also return null if there is no refinement.
484     *  This method is read-synchronized on the workspace.
485     *  @return The refinements of this state, or null if there are none.
486     *  @exception IllegalActionException If the specified refinement
487     *   cannot be found, or if a comma-separated list is malformed.
488     */
489    public TypedActor[] getRefinement() throws IllegalActionException {
490        if (_refinementVersion == workspace().getVersion()) {
491            return _refinement;
492        }
493
494        try {
495            workspace().getReadAccess();
496
497            String names = refinementName.getExpression();
498
499            if (names == null || names.trim().equals("")) {
500                _refinementVersion = workspace().getVersion();
501                _refinement = null;
502                return null;
503            }
504
505            StringTokenizer tokenizer = new StringTokenizer(names, ",");
506            int size = tokenizer.countTokens();
507
508            if (size <= 0) {
509                _refinementVersion = workspace().getVersion();
510                _refinement = null;
511                return null;
512            }
513
514            _refinement = new TypedActor[size];
515
516            Nameable container = getContainer();
517            TypedCompositeActor containerContainer = (TypedCompositeActor) container
518                    .getContainer();
519            int index = 0;
520
521            while (tokenizer.hasMoreTokens()) {
522                String name = tokenizer.nextToken().trim();
523
524                if (name.equals("")) {
525                    throw new IllegalActionException(this,
526                            "Malformed list of refinements: " + names);
527                }
528
529                TypedActor element = (TypedActor) containerContainer
530                        .getEntity(name);
531
532                if (element == null) {
533                    throw new IllegalActionException(this,
534                            "Cannot find " + "refinement with name \"" + name
535                                    + "\" in "
536                                    + containerContainer.getFullName());
537                }
538
539                _refinement[index++] = element;
540            }
541
542            _refinementVersion = workspace().getVersion();
543            return _refinement;
544        } finally {
545            workspace().doneReading();
546        }
547    }
548
549    /** Return true if this transition is a default transition. Return false
550     *  otherwise.
551     *  @return True if this transition is a default transition.
552     *  @exception IllegalActionException If the default parameter
553     *   cannot be evaluated.
554     */
555    public boolean isDefault() throws IllegalActionException {
556        return ((BooleanToken) defaultTransition.getToken()).booleanValue();
557    }
558
559    /** Return true if the transition is enabled, that is the guard is true,
560     *  and false if the guard evaluates to false.
561     *  @return True If the transition is enabled and some event is detected.
562     *  @exception IllegalActionException If the guard cannot be evaluated.
563     */
564    public boolean isEnabled() throws IllegalActionException {
565        NamedObj container = getContainer();
566        if (container instanceof FSMActor) {
567            return isEnabled(((FSMActor) container).getPortScope());
568        } else {
569            return false;
570        }
571    }
572
573    /** Return true if the transition is enabled, that is the guard is true,
574     *  and false if the guard evaluates to false.
575     *  @param scope The parser scope in which the guard is to be evaluated.
576     *  @return True If the transition is enabled and some event is detected.
577     *  @exception IllegalActionException If the guard cannot be evaluated.
578     */
579    public boolean isEnabled(ParserScope scope) throws IllegalActionException {
580        ParseTreeEvaluator parseTreeEvaluator = getParseTreeEvaluator();
581        if (_guardParseTree == null
582                || _guardParseTreeVersion != _workspace.getVersion()) {
583            String expr = getGuardExpression();
584            // If the expression is empty, interpret this as true.
585            if (expr.trim().equals("")) {
586                return true;
587            }
588            // Parse the guard expression.
589            PtParser parser = new PtParser();
590            try {
591                _guardParseTree = parser.generateParseTree(expr);
592                _guardParseTreeVersion = _workspace.getVersion();
593            } catch (IllegalActionException ex) {
594                throw new IllegalActionException(this, ex,
595                        "Failed to parse guard expression \"" + expr + "\"");
596            }
597        }
598        Token token = parseTreeEvaluator.evaluateParseTree(_guardParseTree,
599                scope);
600        if (!(token instanceof BooleanToken)) {
601            throw new IllegalActionException(this,
602                    "Guard expression does not evaluate to a boolean!"
603                            + " The gaurd expression is: \""
604                            + guardExpression.getExpression()
605                            + "\", which evaluates to " + token);
606        }
607        boolean result = ((BooleanToken) token).booleanValue();
608        return result;
609    }
610
611    /** Return true if this transition is an error transition. Whether this
612     *  transition an error transition is specified by the <i>error</i> parameter.
613     *  @return True if this transition is an error transition.
614     *  @exception IllegalActionException If the parameter cannot be evaluated.
615     */
616    public boolean isErrorTransition() throws IllegalActionException {
617        return ((BooleanToken) error.getToken()).booleanValue();
618    }
619
620    /** Return true if this transition is a history transition.
621     *  If the <i>history</i> parameter has been set to true, then return true.
622     *  Otherwise, check to see whether the model was created by a Ptolemy version
623     *  earlier than 9.1.devel. If it was not, return false, the new default.
624     *  If it was, then look for a parameter named
625     *  "reset", and set the history parameter to the complement of it
626     *  and return that value.
627     *  If there is no such parameter, then we have to assume the default
628     *  behavior that prevailed before 9.1.devel, and set history to true.
629     *  @return true if this transition is a history transition.
630     *  @exception IllegalActionException If the value of the history parameter
631     *   cannot be read.
632     */
633    public boolean isHistory() throws IllegalActionException {
634        // Ensure that the corrections below for older version compatibility
635        // are performed only once.
636        if (_historySet) {
637            return ((BooleanToken) history.getToken()).booleanValue();
638        }
639        _historySet = true;
640        // History has not been explicitly set true. Should use either new
641        // or old default depending on the version of Ptolemy that created the model.
642        try {
643            VersionAttribute version = (VersionAttribute) toplevel()
644                    .getAttribute("_createdBy", VersionAttribute.class);
645            if (version == null) {
646                // No version attribute. Return whatever the value
647                // of the history parameter is.
648                return ((BooleanToken) history.getToken()).booleanValue();
649            }
650            if (_REFERENCE_VERSION == null) {
651                _REFERENCE_VERSION = new VersionAttribute("9.0.devel");
652            }
653            if (version.compareTo(_REFERENCE_VERSION) <= 0) {
654                // Model was created under old defaults. Look for a reset parameter.
655                final Parameter reset = (Parameter) getAttribute("reset",
656                        Parameter.class);
657                if (reset != null) {
658                    Token resetValue = reset.getToken();
659                    // Remove the reset parameter.
660                    // If the model is subsequently saved, then the history
661                    // parameter will take over, and it will be confusing to
662                    // also have a reset parameter that doesn't do anything.
663                    try {
664                        reset.setContainer(null);
665                    } catch (NameDuplicationException e) {
666                        // Should not happen. Ignore.
667                    }
668                    // If the value of the reset parameter is false,
669                    // and the destination states have refinements,
670                    // then set the history parameter to true.
671                    // Otherwise, leave the history parameter at its default.
672                    if (resetValue instanceof BooleanToken) {
673                        boolean resetValueBoolean = ((BooleanToken) resetValue)
674                                .booleanValue();
675                        if (!resetValueBoolean) {
676                            // reset parameter exists and has value false, but if the destination
677                            // state has no refinement, we nonetheless change this to make
678                            // the history parameter false.
679                            State destinationState = destinationState();
680                            if (destinationState != null) {
681                                TypedActor[] refinements = destinationState
682                                        .getRefinement();
683                                if (refinements == null
684                                        || refinements.length == 0) {
685                                    // No need to make history true. Stick with the default.
686                                    return false;
687                                }
688                            }
689                            // Set the new parameter to its non-default value.
690                            history.setExpression("true");
691                            // Force this to be exported because this might be getting set
692                            // during construction of the model, in which case, true will be
693                            // assumed to be the default value.
694                            history.setPersistent(true);
695                            return true;
696                        } else {
697                            // No need to set the new parameter, since this is the default.
698                            return false;
699                        }
700                    } else {
701                        // Parameter reset exists, but is not a boolean. Old default is
702                        // that history is true.
703                        history.setExpression("true");
704                        // Force this to be exported because this might be getting set
705                        // during construction of the model, in which case, true will be
706                        // assumed to be the default value.
707                        history.setPersistent(true);
708                        return true;
709                    }
710                } else {
711                    // Parameter reset does not exist. Old default is
712                    // that history is true. But this is only really required
713                    // if the destination state has a refinement.
714                    State destinationState = destinationState();
715                    if (destinationState != null) {
716                        TypedActor[] refinements = destinationState
717                                .getRefinement();
718                        if (refinements == null || refinements.length == 0) {
719                            // No need to make history true. Stick with the default.
720                            return false;
721                        }
722                    }
723                    history.setExpression("true");
724                    // Force this to be exported because this might be getting set
725                    // during construction of the model, in which case, true will be
726                    // assumed to be the default value.
727                    history.setPersistent(true);
728                    return true;
729                }
730            } else {
731                // Version is recent. Return the current value of the history parameter.
732                return ((BooleanToken) history.getToken()).booleanValue();
733            }
734        } catch (IllegalActionException e) {
735            // Can't access version attribute. Return default.
736            return ((BooleanToken) history.getToken()).booleanValue();
737        }
738    }
739
740    /** Return true if this transition is immediate. Whether this transition
741     *  is immediate is specified by the <i>immediateTransition</i> parameter.
742     *  @return True if this transition is immediate.
743     */
744    public boolean isImmediate() {
745        return _immediate;
746    }
747
748    /** Return true if this transition is nondeterministic. Return false
749     *  otherwise.
750     *  @return True if this transition is nondeterministic.
751     */
752    public boolean isNondeterministic() {
753        return _nondeterministic;
754    }
755
756    /** Return true if this transition is a termination transition. Whether this
757     *  transition a termination transition is specified by the <i>termination</i> parameter.
758     *  @return True if this transition is a termination transition.
759     *  @exception IllegalActionException If the parameter cannot be evaluated.
760     */
761    public boolean isTermination() throws IllegalActionException {
762        return ((BooleanToken) termination.getToken()).booleanValue();
763    }
764
765    /** Return true if this transition is preemptive. Whether this transition
766     *  is preemptive is specified by the <i>preemptive</i> parameter.
767     *  @return True if this transition is preemptive.
768     *  @exception IllegalActionException If the parameter cannot be evaluated.
769     */
770    public boolean isPreemptive() throws IllegalActionException {
771        return ((BooleanToken) preemptive.getToken()).booleanValue();
772    }
773
774    /** Override the base class to ensure that the proposed container
775     *  is an instance of FSMActor or null; if it is null, then
776     *  remove it from the container, and also remove any refinement(s)
777     *  that it references that are not referenced by some other
778     *  transition or state.
779     *
780     *  @param container The proposed container.
781     *  @exception IllegalActionException If the transition would result
782     *   in a recursive containment structure, or if
783     *   this transition and container are not in the same workspace, or
784     *   if the argument is not a FSMActor or null.
785     *  @exception NameDuplicationException If the container already has
786     *   an relation with the name of this transition.
787     */
788    @Override
789    public void setContainer(CompositeEntity container)
790            throws IllegalActionException, NameDuplicationException {
791        if (container != null) {
792            if (!(container instanceof FSMActor)) {
793                throw new IllegalActionException(container, this,
794                        "Transition can only be contained by instances of "
795                                + "FSMActor.");
796            }
797        } else {
798            if (_fsmTransitionParameter != null) {
799                _fsmTransitionParameter.setContainer(null);
800            }
801        }
802
803        super.setContainer(container);
804    }
805
806    /** Set the guard expression. The guard expression should evaluate
807     *  to a boolean value.
808     *  @param expression The guard expression.
809     *  @see #getGuardExpression
810     */
811    public void setGuardExpression(String expression) {
812        try {
813            guardExpression.setExpression(expression);
814            guardExpression.validate();
815        } catch (IllegalActionException ex) {
816            throw new InternalErrorException("Error in setting the "
817                    + "guard expression of a transition.");
818        }
819    }
820
821    /** Set the FSMTransitionParameter.
822     * @param parameter The parameter.
823     */
824    public void setFsmTransitionParameter(FSMTransitionParameter parameter) {
825        _fsmTransitionParameter = parameter;
826    }
827
828    /** Return the source state of this transition.
829     *  @return The source state of this transition.
830     */
831    public State sourceState() {
832        if (_stateVersion != workspace().getVersion()) {
833            _checkConnectedStates();
834        }
835
836        return _sourceState;
837    }
838
839    ///////////////////////////////////////////////////////////////////
840    ////                         public variables                  ////
841
842    /** An annotation that describes the transition. If this is non-empty,
843     *  then a visual editor will be expected to put this annotation on
844     *  or near the transition to document its function. This is a string
845     *  that defaults to the empty string. Note that it can reference
846     *  variables in scope using the notation $name.
847     */
848    public StringParameter annotation;
849
850    /** Indicator that this transition is a default transition. A
851     *  default transition is enabled only if no other non-default
852     *  transition is enabled.  This is a boolean with default value
853     *  false. If the value is true, then the guard expression is
854     *  ignored.
855     */
856    public Parameter defaultTransition = null;
857
858    /** Parameter specifying whether this transition should be treated
859     *  as an error transition.  The default value is a boolean with
860     *  the value false, which indicates that this transition is not
861     *  an error transition.  If the value is true, that this transition
862     *  is enabled if and only if the refinement of the source state of
863     *  the transition throws a model error while executing.
864     */
865    public Parameter error = null;
866
867    /** Attribute the exit angle of a visual rendition.
868     *  This parameter contains a DoubleToken, initially with value PI/5.
869     *  It must lie between -PI and PI.  Otherwise, it will be truncated
870     *  to lie within this range.
871     */
872    public Parameter exitAngle;
873
874    /** The name of the transition, which defaults to the name of
875     *  the transition followed by the string "Parameter".
876     */
877    public Parameter fsmTransitionParameterName;
878
879    /** Attribute giving the orientation of a self-loop. This is equal to
880     * the tangent at the midpoint (more or less).
881     *  This parameter contains a DoubleToken, initially with value 0.0.
882     */
883    public Parameter gamma;
884
885    /** Attribute specifying the guard expression.
886     */
887    public StringAttribute guardExpression = null;
888
889    /** Parameter specifying whether the refinements of the destination
890     *  state are initialized when the transition is taken.
891     *  This is a boolean that defaults to false.
892     */
893    public Parameter history;
894
895    /** Parameter specifying whether this transition is immediate.
896     */
897    public Parameter immediate = null;
898
899    /** Parameter specifying whether this transition is nondeterministic.
900     *  Here nondeterministic means that this transition may not be the only
901     *  enabled transition at a time. The default value is a boolean token
902     *  with value as false, meaning that if this transition is enabled, it
903     *  must be the only enabled transition.
904     */
905    public Parameter nondeterministic = null;
906
907    /** The action commands that produce outputs when the transition is taken.
908     */
909    public OutputActionsAttribute outputActions;
910
911    /** Parameter specifying whether this transition is preemptive.
912     */
913    public Parameter preemptive = null;
914
915    /** Attribute specifying one or more names of refinements. The
916     *  refinements must be instances of TypedActor and have the same
917     *  container as the FSMActor containing this state, otherwise
918     *  an exception will be thrown when getRefinement() is called.
919     *  Usually, the refinement is a single name. However, if a
920     *  comma-separated list of names is provided, then all the specified
921     *  refinements will be executed.
922     *  This attribute has a null expression or a null string as
923     *  expression when the state is not refined.
924     *  @deprecated Use immediate transitions.
925     */
926    @Deprecated
927    public StringAttribute refinementName;
928
929    /** The action commands that set parameters when the transition is taken.
930     *  By default, this is empty.
931     */
932    public CommitActionsAttribute setActions;
933
934    /** True of the the value of the {@link #fsmTransitionParameterName} parameter
935     *  should be returned by {@link #getLabel()}.
936     */
937    public Parameter showFSMTransitionParameter;
938
939    /** Parameter specifying whether the refinements of the origin
940     *  state must have terminated (postfire has returned false)
941     *  for the transition to be enabled.
942     */
943    public Parameter termination;
944
945    ///////////////////////////////////////////////////////////////////
946    ////                         protected methods                 ////
947
948    /** Throw an IllegalActionException if the port cannot be linked
949     *  to this transition. A transition has a source state and a
950     *  destination state. A transition is only linked to the outgoing
951     *  port of its source state and the incoming port of its destination
952     *  state.
953     *  @exception IllegalActionException If the port cannot be linked
954     *   to this transition.
955     */
956    @Override
957    protected void _checkPort(Port port) throws IllegalActionException {
958        super._checkPort(port);
959
960        if (!(port.getContainer() instanceof State)) {
961            throw new IllegalActionException(this, port.getContainer(),
962                    "Transition can only connect to instances of State.");
963        }
964
965        State st = (State) port.getContainer();
966
967        if (port != st.incomingPort && port != st.outgoingPort) {
968            throw new IllegalActionException(this, port.getContainer(),
969                    "Transition can only be linked to incoming or outgoing "
970                            + "port of State.");
971        }
972
973        if (numLinks() == 0) {
974            return;
975        }
976
977        if (numLinks() >= 2) {
978            throw new IllegalActionException(this,
979                    "Transition can only connect two States.");
980        }
981
982        Iterator ports = linkedPortList().iterator();
983        Port pt = (Port) ports.next();
984        State s = (State) pt.getContainer();
985
986        if (pt == s.incomingPort && port == st.incomingPort
987                || pt == s.outgoingPort && port == st.outgoingPort) {
988            throw new IllegalActionException(this,
989                    "Transition can only have one source and one destination.");
990        }
991
992        return;
993    }
994
995    ///////////////////////////////////////////////////////////////////
996    ////                         private methods                   ////
997
998    // Check the states connected by this transition, cache the result.
999    // This method is read-synchronized on the workspace.
1000    private void _checkConnectedStates() {
1001        try {
1002            workspace().getReadAccess();
1003
1004            Iterator ports = linkedPortList().iterator();
1005            _sourceState = null;
1006            _destinationState = null;
1007
1008            while (ports.hasNext()) {
1009                Port p = (Port) ports.next();
1010                State s = (State) p.getContainer();
1011
1012                if (p == s.incomingPort) {
1013                    _destinationState = s;
1014                } else {
1015                    _sourceState = s;
1016                }
1017            }
1018
1019            _stateVersion = workspace().getVersion();
1020        } finally {
1021            workspace().doneReading();
1022        }
1023    }
1024
1025    /** Return the FSMDirector in charge of this transition,
1026     *  or null if there is none.
1027     *  @return The director in charge of this transition.
1028     */
1029    private FSMDirector _getDirector() {
1030        // Get the containing FSMActor.
1031        NamedObj container = getContainer();
1032        if (container != null) {
1033            // Get the containing modal model.
1034            CompositeActor modalModel = (CompositeActor) container
1035                    .getContainer();
1036            if (modalModel != null) {
1037                // Get the director for the modal model.
1038                Director director = modalModel.getDirector();
1039                if (director instanceof FSMDirector) {
1040                    return (FSMDirector) director;
1041                }
1042            }
1043        }
1044        return null;
1045    }
1046
1047    private void _getFSMTransitionParameter() throws IllegalActionException {
1048        if (getContainer() != null) {
1049            if (_fsmTransitionParameter == null) {
1050                _fsmTransitionParameter = (FSMTransitionParameter) getContainer()
1051                        .getAttribute(((StringToken) fsmTransitionParameterName
1052                                .getToken()).stringValue());
1053                if (_fsmTransitionParameter != null) {
1054                    try {
1055                        _fsmTransitionParameter.setTransition(this);
1056                    } catch (NameDuplicationException e) {
1057                        throw new IllegalActionException(this, e.getCause(),
1058                                e.getLocalizedMessage());
1059                    }
1060                }
1061            }
1062            if (_fsmTransitionParameter == null) {
1063                Location sourceStateLocation = (Location) sourceState()
1064                        .getAttribute("_location");
1065                Location destinationStateLocation = (Location) destinationState()
1066                        .getAttribute("_location");
1067                String moml = "<property name=\""
1068                        + ((StringToken) fsmTransitionParameterName.getToken())
1069                                .stringValue()
1070                        + "\" class=\"ptolemy.domains.modal.kernel.FSMTransitionParameter\">\n"
1071                        + "    <property name=\"_hideName\" class=\"ptolemy.kernel.util.SingletonAttribute\"/>\n"
1072                        + "    <property name=\"_icon\" class=\"ptolemy.vergil.icon.ValueIcon\">\n"
1073                        + "        <property name=\"_color\" class=\"ptolemy.actor.gui.ColorAttribute\" value=\"{0.0, 0.0, 1.0, 1.0}\"/>\n"
1074                        + "        <property name=\"displayWidth\" value=\"1000\"/>\n"
1075                        + "        <property name=\"numberOfLines\" value=\"100\"/>\n"
1076                        + "    </property>\n"
1077                        + "    <property name=\"_location\" class=\"ptolemy.kernel.util.Location\" value=\"["
1078                        + (int) (destinationStateLocation.getLocation()[0]
1079                                + (sourceStateLocation.getLocation()[0]
1080                                        - destinationStateLocation
1081                                                .getLocation()[0])
1082                                        / 2)
1083                        + ", "
1084                        + (int) (destinationStateLocation.getLocation()[1]
1085                                + (sourceStateLocation.getLocation()[1]
1086                                        - destinationStateLocation
1087                                                .getLocation()[1])
1088                                        / 2)
1089                        + "]\"/>\n"
1090                        + "    <property name=\"_smallIconDescription\" class=\"ptolemy.kernel.util.SingletonConfigurableAttribute\">\n"
1091                        + "        <configure><svg><text x=\"20\" style=\"font-size:14; font-family:SansSerif; fill:blue\" y=\"20\">-P-</text></svg></configure>\n"
1092                        + "    </property>\n"
1093                        + "    <property name=\"_editorFactory\" class=\"ptolemy.vergil.toolbox.VisibleParameterEditorFactory\"/>\n"
1094                        + "    <property name=\"_configurer\" class=\"ptolemy.actor.gui.TransitionEditorPaneFactory\"/>\n"
1095                        + "</property>";
1096
1097                MoMLChangeRequest request = new MoMLChangeRequest(this,
1098                        getContainer(), moml);
1099                getContainer().requestChange(request);
1100            }
1101        }
1102    }
1103
1104    // Initialize the variables of this transition.
1105    private void _init()
1106            throws IllegalActionException, NameDuplicationException {
1107        fsmTransitionParameterName = new StringParameter(this,
1108                "fsmTransitionParameterName");
1109        fsmTransitionParameterName.setExpression(this.getName() + "Parameter");
1110        fsmTransitionParameterName.setVisibility(Settable.FULL);
1111
1112        showFSMTransitionParameter = new Parameter(this,
1113                "showFSMTransitionParameter");
1114        showFSMTransitionParameter.setTypeEquals(BaseType.BOOLEAN);
1115        showFSMTransitionParameter.setToken(BooleanToken.FALSE);
1116
1117        annotation = new StringParameter(this, "annotation");
1118        annotation.setExpression("");
1119        // Add a hint to indicate to the PtolemyQuery class to open with a text style.
1120        Variable variable = new Variable(annotation, "_textHeightHint");
1121        variable.setExpression(_TEXT_HEIGHT);
1122        variable.setPersistent(false);
1123
1124        guardExpression = new StringAttribute(this, "guardExpression");
1125        // Add a hint to indicate to the PtolemyQuery class to open with a text style.
1126        variable = new Variable(guardExpression, "_textHeightHint");
1127        variable.setExpression(_TEXT_HEIGHT);
1128        variable.setPersistent(false);
1129
1130        outputActions = new OutputActionsAttribute(this, "outputActions");
1131        // Add a hint to indicate to the PtolemyQuery class to open with a text style.
1132        variable = new Variable(outputActions, "_textHeightHint");
1133        variable.setExpression(_TEXT_HEIGHT);
1134        variable.setPersistent(false);
1135
1136        setActions = new CommitActionsAttribute(this, "setActions");
1137        // Add a hint to indicate to the PtolemyQuery class to open with a text style.
1138        variable = new Variable(setActions, "_textHeightHint");
1139        variable.setExpression(_TEXT_HEIGHT);
1140        variable.setPersistent(false);
1141
1142        exitAngle = new Parameter(this, "exitAngle");
1143        exitAngle.setVisibility(Settable.NONE);
1144        exitAngle.setExpression("PI/5.0");
1145        exitAngle.setTypeEquals(BaseType.DOUBLE);
1146
1147        gamma = new Parameter(this, "gamma");
1148        gamma.setVisibility(Settable.NONE);
1149        gamma.setExpression("0.0");
1150        gamma.setTypeEquals(BaseType.DOUBLE);
1151
1152        // default attributes.
1153        defaultTransition = new Parameter(this, "defaultTransition");
1154        defaultTransition.setTypeEquals(BaseType.BOOLEAN);
1155        defaultTransition.setToken(BooleanToken.FALSE);
1156        // We would like to call this parameter "default" but
1157        // can't because this is a Java keyword.
1158        defaultTransition.setDisplayName("default");
1159
1160        // Nondeterministic attributes.
1161        nondeterministic = new Parameter(this, "nondeterministic");
1162        nondeterministic.setTypeEquals(BaseType.BOOLEAN);
1163        nondeterministic.setToken(BooleanToken.FALSE);
1164
1165        immediate = new Parameter(this, "immediate");
1166        immediate.setTypeEquals(BaseType.BOOLEAN);
1167        immediate.setToken(BooleanToken.FALSE);
1168
1169        preemptive = new Parameter(this, "preemptive");
1170        preemptive.setTypeEquals(BaseType.BOOLEAN);
1171        preemptive.setToken(BooleanToken.FALSE);
1172
1173        // From version 9.0.devel to 9.1.devel, the parameter
1174        // reset was replaced by history and the default behavior
1175        // was changed so that by default transitions are not
1176        // history transitions (are reset transitions).
1177        history = new Parameter(this, "history");
1178        history.setTypeEquals(BaseType.BOOLEAN);
1179        history.setToken(BooleanToken.FALSE);
1180
1181        error = new Parameter(this, "error");
1182        error.setTypeEquals(BaseType.BOOLEAN);
1183        error.setToken(BooleanToken.FALSE);
1184
1185        termination = new Parameter(this, "termination");
1186        termination.setTypeEquals(BaseType.BOOLEAN);
1187        termination.setToken(BooleanToken.FALSE);
1188
1189        // Add refinement name parameter
1190        refinementName = new StringAttribute(this, "refinementName");
1191        refinementName.setVisibility(Settable.EXPERT);
1192
1193    }
1194
1195    // Update the cached lists of actions.
1196    // This method is read-synchronized on the workspace.
1197    private void _updateActionLists() {
1198        try {
1199            workspace().getReadAccess();
1200            _choiceActionList.clear();
1201            _commitActionList.clear();
1202
1203            Iterator actions = attributeList(Action.class).iterator();
1204
1205            while (actions.hasNext()) {
1206                Action action = (Action) actions.next();
1207
1208                if (action instanceof ChoiceAction) {
1209                    _choiceActionList.add(action);
1210                }
1211
1212                if (action instanceof CommitAction) {
1213                    _commitActionList.add(action);
1214                }
1215            }
1216
1217            _actionListsVersion = workspace().getVersion();
1218        } finally {
1219            workspace().doneReading();
1220        }
1221    }
1222
1223    ///////////////////////////////////////////////////////////////////
1224    ////                         private variables                 ////
1225
1226    // Version of cached lists of actions.
1227    private long _actionListsVersion = -1;
1228
1229    // Cached list of choice actions contained by this transition.
1230    private List _choiceActionList = new LinkedList();
1231
1232    // Cached list of commit actions contained by this Transition.
1233    private List _commitActionList = new LinkedList();
1234
1235    // Cached destination state of this transition.
1236    private State _destinationState = null;
1237
1238    private FSMTransitionParameter _fsmTransitionParameter;
1239
1240    // The parse tree for the guard expression.
1241    private ASTPtRootNode _guardParseTree;
1242
1243    // Version of the cached guard parse tree
1244    private long _guardParseTreeVersion = -1;
1245
1246    // Flag to ensure that the corrections below for older version compatibility
1247    // are performed only once.
1248    private boolean _historySet = false;
1249
1250    // Set to true if the transition should be checked
1251    // as soon as the source state is entered. This may lead
1252    // to transient states.
1253    private boolean _immediate = false;
1254
1255    private boolean _nondeterministic = false;
1256
1257    // The parse tree evaluator for the transition.
1258    // Note that this variable should not be accessed directly even inside
1259    // this class. Instead, always use the getParseTreeEvaluator() method.
1260    private ParseTreeEvaluator _parseTreeEvaluator;
1261
1262    // Version of the cached parse tree evaluator
1263    private long _parseTreeEvaluatorVersion = -1;
1264
1265    // Latest version before default history behavior changed.
1266    private static VersionAttribute _REFERENCE_VERSION;
1267
1268    // Cached reference to the refinement of this state.
1269    private TypedActor[] _refinement = null;
1270
1271    // Version of the cached reference to the refinement.
1272    private long _refinementVersion = -1;
1273
1274    // Cached source state of this transition.
1275    private State _sourceState = null;
1276
1277    // Version of cached source/destination state.
1278    private long _stateVersion = -1;
1279
1280    // Default text height in the dialog box.
1281    private static String _TEXT_HEIGHT = "4";
1282}