001/* An Interface Automaton.
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.ia;
028
029import java.util.HashMap;
030import java.util.HashSet;
031import java.util.Iterator;
032import java.util.List;
033import java.util.Map;
034import java.util.Set;
035
036import ptolemy.actor.IOPort;
037import ptolemy.actor.TypedIOPort;
038import ptolemy.data.expr.Parameter;
039import ptolemy.domains.modal.kernel.FSMActor;
040import ptolemy.domains.modal.kernel.State;
041import ptolemy.kernel.ComponentPort;
042import ptolemy.kernel.ComponentRelation;
043import ptolemy.kernel.CompositeEntity;
044import ptolemy.kernel.Port;
045import ptolemy.kernel.util.Attribute;
046import ptolemy.kernel.util.IllegalActionException;
047import ptolemy.kernel.util.InternalErrorException;
048import ptolemy.kernel.util.NameDuplicationException;
049import ptolemy.kernel.util.Workspace;
050
051///////////////////////////////////////////////////////////////////
052//// InterfaceAutomaton
053
054/**
055 This class models an Interface Automaton. Interface automata is an automata
056 model defined by de Alfaro and Henzinger in the paper
057"<a href="http://www.eecs.berkeley.edu/~tah/Publications/interface_automata.pdf">Interface Automata</a>".
058 An InterfaceAutomaton contains a set of states and
059 InterfaceAutomatonTransitions. There are three kinds transitions:
060 input transition, output transition, and internal transitions.
061 The input and output transitions correspond to input and output ports,
062 respectively. The internal transition correspond to a parameter in this
063 InterfaceAutomaton. The parameter is added automatically when the internal
064 transition is added.
065 <p>
066 When an InterfaceAutomaton is fired, the outgoing transitions of the current
067 state are examined. An IllegalActionException is thrown if there is more than
068 one enabled transition. If there is exactly one enabled transition then it is
069 taken.
070 <p>
071 An InterfaceAutomaton enters its initial state during initialization. The
072 name of the initial state is specified by the <i>initialStateName</i> string
073 attribute.
074 <p>
075
076 @author Yuhong Xiong, Xiaojun Liu and Edward A. Lee
077 @version $Id$
078 @since Ptolemy II 8.0
079 @Pt.ProposedRating Yellow (liuxj)
080 @Pt.AcceptedRating Yellow (kienhuis)
081 @see State
082 @see InterfaceAutomatonTransition
083 */
084
085// FIXME: Are interface automata that are fired required to be deterministic?
086// or just randomly choose a transition.
087public class InterfaceAutomaton extends FSMActor {
088    /** Construct an InterfaceAutomaton in the default workspace with an
089     *  empty string as its name. Add the actor to the workspace directory.
090     *  Increment the version number of the workspace.
091     */
092    public InterfaceAutomaton() {
093        super();
094    }
095
096    /** Construct an InterfaceAutomaton in the specified workspace with an
097     *  empty string as its name. The name can be changed later with
098     *  setName().
099     *  If the workspace argument is null, then use the default workspace.
100     *  Add the actor to the workspace directory.
101     *  Increment the version number of the workspace.
102     *  @param workspace The workspace that will list the actor.
103     */
104    public InterfaceAutomaton(Workspace workspace) {
105        super(workspace);
106    }
107
108    /** Create an InterfaceAutomaton in the specified container with the
109     *  specified name. The name must be unique within the container or an
110     *  exception is thrown. The container argument must not be null, or a
111     *  NullPointerException will be thrown.
112     *  @param container The container.
113     *  @param name The name of this automaton within the container.
114     *  @exception IllegalActionException If the entity cannot be contained
115     *   by the proposed container.
116     *  @exception NameDuplicationException If the name coincides with
117     *   an entity already in the container.
118     */
119    public InterfaceAutomaton(CompositeEntity container, String name)
120            throws IllegalActionException, NameDuplicationException {
121        super(container, name);
122    }
123
124    ///////////////////////////////////////////////////////////////////
125    ////                         public variables                  ////
126    ///////////////////////////////////////////////////////////////////
127    ////                         public methods                    ////
128
129    /** Add instances of TypedIOPort that correspond to input and output
130     *  transitions, if these port do not exist. If the ports
131     *  corresponding to some transitions already exist, do nothing with
132     *  respect to those transitions.
133     */
134    public void addPorts() {
135        try {
136            Iterator iterator = relationList().iterator();
137
138            while (iterator.hasNext()) {
139                InterfaceAutomatonTransition transition = (InterfaceAutomatonTransition) iterator
140                        .next();
141                String label = transition.getLabel();
142                String name = label.substring(0, label.length() - 1);
143
144                if (label.endsWith("?")) {
145                    TypedIOPort port = (TypedIOPort) getPort(name);
146
147                    if (port == null) {
148                        port = new TypedIOPort(this, name);
149                        port.setInput(true);
150                    }
151                } else if (label.endsWith("!")) {
152                    TypedIOPort port = (TypedIOPort) getPort(name);
153
154                    if (port == null) {
155                        port = new TypedIOPort(this, name);
156                        port.setOutput(true);
157                    }
158                }
159            }
160        } catch (IllegalActionException exception) {
161            // should not happen since TypedIOPort can be added to
162            // InterfaceAutomaton
163            throw new InternalErrorException("InterfaceAutomaton.addPorts: "
164                    + "Cannot add port. " + exception.getMessage());
165        } catch (NameDuplicationException exception) {
166            // should not happen since new port will not be created if there
167            // is already a port with the same name.
168            throw new InternalErrorException("InterfaceAutomaton.addPorts: "
169                    + "Cannot add port. " + exception.getMessage());
170        }
171    }
172
173    /** Combine each chain of internal transitions into one transition.
174     *  This method iterates through all the states. If a state has just
175     *  one incoming and one outgoing internal transitions, and it is
176     *  not the initial state, it is removed and the two transitions are
177     *  combined into one. The label on the new transition is formed by
178     *  &lt;incomingLabel&gt;&lt;NAME_CONNECTOR&gt;&lt;outgoingLabel&gt;.
179     */
180    public void combineInternalTransitions() {
181        State initialState = (State) getEntity(
182                initialStateName.getExpression());
183
184        try {
185            Iterator states = entityList().iterator();
186
187            while (states.hasNext()) {
188                State state = (State) states.next();
189
190                ComponentPort inPort = state.incomingPort;
191                List transitionList = inPort.linkedRelationList();
192                InterfaceAutomatonTransition incomingTransition = null;
193
194                if (transitionList.size() != 1) {
195                    continue;
196                }
197
198                // just one incoming transition, check if it's internal
199                incomingTransition = (InterfaceAutomatonTransition) transitionList
200                        .get(0);
201
202                if (incomingTransition
203                        .getType() != InterfaceAutomatonTransition._INTERNAL_TRANSITION) {
204                    continue;
205                }
206
207                ComponentPort outPort = state.outgoingPort;
208                transitionList = outPort.linkedRelationList();
209
210                InterfaceAutomatonTransition outgoingTransition = null;
211
212                if (transitionList.size() != 1) {
213                    continue;
214                }
215
216                // just one outgoing transition, check if it's internal
217                outgoingTransition = (InterfaceAutomatonTransition) transitionList
218                        .get(0);
219
220                if (outgoingTransition
221                        .getType() != InterfaceAutomatonTransition._INTERNAL_TRANSITION) {
222                    continue;
223                }
224
225                // only has one incoming and one outgoing internal transition,
226                // check if this state is initial.
227                if (state == initialState) {
228                    continue;
229                }
230
231                // combine transitions
232                State sourceState = incomingTransition.sourceState();
233                State destinationState = outgoingTransition.destinationState();
234                String incomingLabel = incomingTransition.getLabel();
235                String incomingName = incomingLabel.substring(0,
236                        incomingLabel.length() - 1);
237                String outgoingLabel = outgoingTransition.getLabel();
238                String newLabel = incomingName + NAME_CONNECTOR + outgoingLabel;
239
240                String relationNamePrefix = incomingTransition.getName()
241                        + NAME_CONNECTOR + outgoingTransition.getName();
242
243                // remove this state and add new transition
244                _removeStateAndTransitions(state);
245                _addTransition(this, relationNamePrefix, sourceState,
246                        destinationState, newLabel);
247            }
248        } catch (IllegalActionException exception) {
249            // should not happen
250            throw new InternalErrorException(
251                    "InterfaceAutomaton.combineInternalTransitions: "
252                            + exception.getMessage());
253        } catch (NameDuplicationException exception) {
254            // should not happen since _addTransition() uses unique name.
255            // Maybe that method should not throw this exception.
256            throw new InternalErrorException(
257                    "InterfaceAutomaton.combineInternalTransitions: "
258                            + exception.getMessage());
259        }
260    }
261
262    /** Return a new InterfaceAutomaton that is the composition of the
263     *  specified InterfaceAutomaton and this one.
264     *  @param automaton An InterfaceAutomaton to compose with this one.
265     *  @return An InterfaceAutomaton that is the composition.
266     *  @exception IllegalActionException If this automaton is not composable
267     *   with the argument.
268     */
269    public InterfaceAutomaton compose(InterfaceAutomaton automaton)
270            throws IllegalActionException {
271        return compose(automaton, false);
272    }
273
274    /** Return a new InterfaceAutomaton that is the composition of the
275     *  specified InterfaceAutomaton and this one.
276     *  @param automaton An InterfaceAutomaton to compose with this one.
277     *  @param considerTransient True to indicate that transient states
278     *   should be treated differently; false to indicate that transient
279     *   states are treated as regular ones.
280     *  @return An InterfaceAutomaton that is the composition.
281     *  @exception IllegalActionException If this automaton is not composable
282     *   with the argument.
283     */
284    public InterfaceAutomaton compose(InterfaceAutomaton automaton,
285            boolean considerTransient) throws IllegalActionException {
286        this._check();
287        automaton._check();
288
289        // check composability
290        _checkComposability(automaton);
291
292        // compute the input, output, and internal transitions of the
293        // composition
294        _computeTransitionNamesInComposition(automaton);
295
296        // compute the product automaton
297        InterfaceAutomaton composition = _computeProduct(automaton,
298                considerTransient);
299
300        // prune illegal states
301        _pruneIllegalStates();
302
303        // remove states unreacheable from the initial state.
304        composition._removeUnreacheableStates();
305
306        // Create ports for the composition.  Internal transition parameters
307        // were created automatically when the transition labels were set.
308        _createPorts(composition);
309
310        return composition;
311    }
312
313    /** Return the unique maximal alternating simulation from the specified
314     *  automaton to this automaton.
315     *  Alternating simulation is a binary relation defined in the interface
316     *  automata paper. If P and Q are two interface automata, and Vp and Vq
317     *  are their states, an alternating simulation is a subset of Vp x Vq
318     *  that satisfies the conditions described in the paper. This method
319     *  computes such a subset. If this subset is not empty, we say that
320     *  there is an alternating simulation from Q to P. In this class, we
321     *  call the automaton P the <i>super automaton</i>, and Q the
322     *  <i>sub automaton</i>.
323     *  <p>
324     *  This method returns a set of instances of StatePair. The first state
325     *  in each pair is in the super automaton, and the second state is in
326     *  the sub automaton.
327     *  @param subAutomaton An interface automaton.
328     *  @return A set representing the alternating simulation.
329     *  @exception IllegalActionException If this automaton or the specified
330     *   one is not consistent. For example, missing ports.
331     *  @see StatePair
332     */
333    public Set computeAlternatingSimulation(InterfaceAutomaton subAutomaton)
334            throws IllegalActionException {
335        this._check();
336        subAutomaton._check();
337
338        Set simulation = new HashSet();
339
340        // Initialize simulation. Use condition 1 in Definitino 14 to
341        // (significantly) reduce the size.
342        Iterator superStates = entityList().iterator();
343
344        while (superStates.hasNext()) {
345            State superState = (State) superStates.next();
346            Iterator subStates = subAutomaton.entityList().iterator();
347
348            while (subStates.hasNext()) {
349                State subState = (State) subStates.next();
350
351                if (_condition1Satisfied(this, superState, subAutomaton,
352                        subState)) {
353                    StatePair pair = new StatePair(superState, subState);
354                    simulation.add(pair);
355                }
356            }
357        }
358
359        // Repeatedly removing the pairs in simulation using the 2 conditions
360        // in Definition 14 of the paper, until no more pairs can be removed
361        // or until simulation is empty.
362        Set toBeRemoved = new HashSet();
363
364        do {
365            toBeRemoved.clear();
366
367            Iterator pairs = simulation.iterator();
368
369            while (pairs.hasNext()) {
370                StatePair pair = (StatePair) pairs.next();
371                State superState = pair.first();
372                State subState = pair.second();
373
374                if (_condition2Satisfied(this, superState, subAutomaton,
375                        subState, simulation) == false) {
376                    toBeRemoved.add(pair);
377                }
378            }
379
380            simulation.removeAll(toBeRemoved);
381        } while (!toBeRemoved.isEmpty());
382
383        return simulation;
384    }
385
386    /** Return the deadlock states in a Set. A state is a deadlock state if
387     *  it does not have any outgoing transitions.
388     *  @return A Set of deadlock states.
389     *  @exception IllegalActionException If this automaton is not closed.
390     */
391    public Set deadlockStates() throws IllegalActionException {
392        if (!isClosed()) {
393            throw new IllegalActionException(
394                    "InterfaceAutomaton.deadlockStates: "
395                            + "Deadlock can only be checked on closed interface "
396                            + "automaton.");
397        }
398
399        Set deadlockStates = new HashSet();
400        Iterator states = entityList().iterator();
401
402        while (states.hasNext()) {
403            State state = (State) states.next();
404            ComponentPort outPort = state.outgoingPort;
405
406            if (outPort.linkedRelationList().size() == 0) {
407                deadlockStates.add(state);
408            }
409        }
410
411        return deadlockStates;
412    }
413
414    /** Return the epsilon-closure of the specified state. Epsilon-closure
415     *  is defined in Definition 11 of the interface automaton paper. It
416     *  is the set of states that can be reached from the specified state
417     *  by taking only internal transitions.
418     *  @param state The state from which the epsilon-closure is computed.
419     *  @return A set of instances of State.
420     */
421    public Set epsilonClosure(State state) {
422        // Use frontier exploration. The Set frontier stores the frontier
423        // states, and the Set closure stores all reacheable states. frontier
424        // is always a subset of closure.
425        //
426        // init: closure = frontier = state
427        //
428        // iterate: Pick (remove) a state p from the frontier
429        //          for all states s reacheable from p through internal
430        //          transitions
431        //              if s is not in closure
432        //                  add s to both closure and frontier
433        //
434        //          end when frontier is empty
435        // init
436        Set closure = new HashSet();
437        Set frontier = new HashSet();
438        closure.add(state);
439        frontier.add(state);
440
441        // iterate
442        while (!frontier.isEmpty()) {
443            // there does not seem to be an easy way to remove an arbitrary
444            // element, except through Iterator
445            Iterator iterator = frontier.iterator();
446            State current = (State) iterator.next();
447            frontier.remove(current);
448
449            // put all states that are reacheable from current through
450            // internal transitions into closure and frontier
451            ComponentPort outPort = current.outgoingPort;
452            Iterator transitions = outPort.linkedRelationList().iterator();
453
454            while (transitions.hasNext()) {
455                InterfaceAutomatonTransition transition = (InterfaceAutomatonTransition) transitions
456                        .next();
457                int transitionType = transition.getType();
458
459                if (transitionType == InterfaceAutomatonTransition._INTERNAL_TRANSITION) {
460                    State destinationState = transition.destinationState();
461
462                    if (!closure.contains(destinationState)) {
463                        closure.add(destinationState);
464                        frontier.add(destinationState);
465                    }
466                }
467            }
468        }
469
470        return closure;
471    }
472
473    /** Return the set of externally enabled destination states. This set is
474     *  defined in Definition 13 of the interface automaton paper.
475     *  The caller should ensure that the specified transition label is for
476     *  an externally enabled input or output transition. This method assumes
477     *  this is true without checking.
478     *  @param sourceState The source state from which the externally
479     *   enabled destinations are computed.
480     *  @param transitionLabel The label for an externally enabled transition.
481     *  @return A set of instances of State.
482     */
483    public Set externallyEnabledDestinations(State sourceState,
484            String transitionLabel) {
485        Set destinations = new HashSet();
486        Set closure = epsilonClosure(sourceState);
487        Iterator sources = closure.iterator();
488
489        while (sources.hasNext()) {
490            State source = (State) sources.next();
491            ComponentPort outPort = source.outgoingPort;
492            Iterator transitions = outPort.linkedRelationList().iterator();
493
494            while (transitions.hasNext()) {
495                InterfaceAutomatonTransition transition = (InterfaceAutomatonTransition) transitions
496                        .next();
497
498                if (transition.getLabel().equals(transitionLabel)) {
499                    State destination = transition.destinationState();
500                    destinations.add(destination);
501                }
502            }
503        }
504
505        return destinations;
506    }
507
508    /** Return the labels for the set of externally enabled input transitions
509     *  for the specified state. This set is defined in Definition 12 of the
510     *  interface automaton paper.
511     *  @param state The state for which the externally enabled input
512     *   transitions are computed.
513     *  @return A set of string.
514     */
515    public Set externallyEnabledInputTransitionLabels(State state) {
516        Set transitionLabels = _transitionLabelsFrom(state,
517                InterfaceAutomatonTransition._INPUT_TRANSITION);
518
519        Set closure = epsilonClosure(state);
520        closure.remove(state);
521
522        Iterator states = closure.iterator();
523
524        while (states.hasNext()) {
525            State nextState = (State) states.next();
526            Set labels = _transitionLabelsFrom(nextState,
527                    InterfaceAutomatonTransition._INPUT_TRANSITION);
528            transitionLabels.retainAll(labels);
529        }
530
531        return transitionLabels;
532    }
533
534    /** Return the labels for the set of externally enabled output transitions
535     *  for the specified state. This set is defined in Definition 12 of the
536     *  interface automaton paper.
537     *  @param state The state for which the externally enabled output
538     *   transitions are computed.
539     *  @return A set of string.
540     */
541    public Set externallyEnabledOutputTransitionLabels(State state) {
542        Set transitionLabels = _transitionLabelsFrom(state,
543                InterfaceAutomatonTransition._OUTPUT_TRANSITION);
544
545        Set closure = epsilonClosure(state);
546        closure.remove(state);
547
548        Iterator states = closure.iterator();
549
550        while (states.hasNext()) {
551            State nextState = (State) states.next();
552            Set labels = _transitionLabelsFrom(nextState,
553                    InterfaceAutomatonTransition._OUTPUT_TRANSITION);
554            transitionLabels.addAll(labels);
555        }
556
557        return transitionLabels;
558    }
559
560    /** Choose the enabled transition among the outgoing transitions of
561     *  the current state. Throw an exception if there is more than one
562     *  transition enabled.
563     *  @exception IllegalActionException If there is more than one
564     *   transition enabled.
565     */
566    @Override
567    public void fire() throws IllegalActionException {
568        super.fire();
569    }
570
571    /** Return a high-level description of this automaton. The returned String
572     *  has the format:
573     *  <pre>
574     *  (full name of automaton):
575     *    (number of states) states
576     *    (number of transitions) transitions
577     *    (number of input names) input names
578     *    (number of output names) output names
579     *    (number of internal transition names) internal transition names
580     *    Input Names:
581     *      (list of input names)
582     *    Output Names:
583     *      (list of output names)
584     *    Internal Transition Names:
585     *      (list of internal transition names)
586     *  </pre>
587     *  @return A high-level description of this automaton.
588     */
589    public String getInfo() {
590        StringBuffer info = new StringBuffer(getFullName() + "\n");
591
592        Set inputNames = inputNameSet();
593        Set outputNames = outputNameSet();
594        Set internalNames = internalTransitionNameSet();
595
596        info.append("  " + entityList().size() + " states\n" + "  "
597                + relationList().size() + " transitions\n" + "  "
598                + inputNames.size() + " input names\n" + "  "
599                + outputNames.size() + " output names\n" + "  "
600                + internalNames.size() + " internal transition names\n"
601                + "  Input Names:\n");
602
603        Iterator iterator = inputNames.iterator();
604
605        while (iterator.hasNext()) {
606            info.append("    " + iterator.next().toString() + "\n");
607        }
608
609        info.append("  Output Names:\n");
610        iterator = outputNames.iterator();
611
612        while (iterator.hasNext()) {
613            info.append("    " + iterator.next().toString() + "\n");
614        }
615
616        info.append("  Internal Transition Names:\n");
617        iterator = internalNames.iterator();
618
619        while (iterator.hasNext()) {
620            info.append("    " + iterator.next().toString() + "\n");
621        }
622
623        return info.toString();
624    }
625
626    /** Return the names of the input ports as a Set.
627     *  @return A Set containing all the input port names.
628     */
629    public Set inputNameSet() {
630        Set set = new HashSet();
631        Iterator iterator = inputPortList().iterator();
632
633        while (iterator.hasNext()) {
634            Port port = (Port) iterator.next();
635            set.add(port.getName());
636        }
637
638        return set;
639    }
640
641    /** Return the names of the internal transitions as a Set.
642     *  @return A Set containing all the internal transition names.
643     */
644
645    // This method differs from inputNameSet() and outputNameSet() in that
646    // those methods return the names of the input or output ports, but this
647    // one does not get the names from the parameters corresponding to the
648    // internal transitions. As a result, all the returned names have one or
649    // more corresponding internal transition instances. The is because
650    // (1) Unlike the relation between input/output transitions and ports,
651    // where some ports may not have corresponding instances of transition,
652    // it does not make sense to have any "internal transition parameter"
653    // that does not have transition instances; (2) there is no way to tell
654    // which parameter is for internal transition, and which is for other
655    // purpose.
656    public Set internalTransitionNameSet() {
657        Set set = new HashSet();
658        Iterator iterator = relationList().iterator();
659
660        while (iterator.hasNext()) {
661            InterfaceAutomatonTransition transition = (InterfaceAutomatonTransition) iterator
662                    .next();
663            String label = transition.getLabel();
664
665            if (label.endsWith(";")) {
666                String name = label.substring(0, label.length() - 1);
667                set.add(name);
668            }
669        }
670
671        return set;
672    }
673
674    /** Return true if this automaton does not have any input and output;
675     *  false otherwise.
676     *  @return True if this automaton does not have any input and output.
677     */
678    public boolean isClosed() {
679        return portList().size() == 0;
680    }
681
682    /** Return true if this automaton is empty; false otherwise.
683     *  @return true if this automaton is empty; false otherwise.
684     */
685    public boolean isEmpty() {
686        List states = entityList();
687        return states.size() == 0;
688    }
689
690    /** Create a new instance of InterfaceAutomatonTransition with the
691     *  specified name in this actor, and return it.
692     *  This method is write-synchronized on the workspace.
693     *  @param name The name of the new transition.
694     *  @return An InterfaceAutomatonTransition with the given name.
695     *  @exception IllegalActionException If the name argument is null.
696     *  @exception NameDuplicationException If name collides with that
697     *   of a transition already in this actor.
698     */
699    @Override
700    public ComponentRelation newRelation(String name)
701            throws IllegalActionException, NameDuplicationException {
702        try {
703            workspace().getWriteAccess();
704
705            InterfaceAutomatonTransition transition = new InterfaceAutomatonTransition(
706                    this, name);
707            return transition;
708        } finally {
709            workspace().doneWriting();
710        }
711    }
712
713    /** Return the names of the output ports as a Set.
714     *  @return A Set containing all the output port names.
715     */
716    public Set outputNameSet() {
717        Set set = new HashSet();
718        Iterator iterator = outputPortList().iterator();
719
720        while (iterator.hasNext()) {
721            Port port = (Port) iterator.next();
722            set.add(port.getName());
723        }
724
725        return set;
726    }
727
728    /** Project this automaton into the specified one.
729     *  More specifically, this method converts the input and output
730     *  transitions of this automaton that do not overlap with the specified
731     *  one to internal transitions, and remove the corresponding ports.
732     *  @param automaton The interface automaton to which this automaton will
733     *   be projected.
734     *  @exception IllegalActionException If this or the specified automaton
735     *   is not consistent. For example, missing ports.
736     */
737    public void project(InterfaceAutomaton automaton)
738            throws IllegalActionException {
739        this._check();
740        automaton._check();
741
742        Set nameDifference = inputNameSet();
743        nameDifference.addAll(outputNameSet());
744
745        nameDifference.removeAll(automaton.inputNameSet());
746        nameDifference.removeAll(automaton.outputNameSet());
747
748        // convert transitions to internal
749        Iterator relations = relationList().iterator();
750
751        while (relations.hasNext()) {
752            InterfaceAutomatonTransition transition = (InterfaceAutomatonTransition) relations
753                    .next();
754            String label = transition.getLabel();
755            String name = label.substring(0, label.length() - 1);
756
757            if (nameDifference.contains(name)) {
758                int type = transition.getType();
759
760                if (type == InterfaceAutomatonTransition._INPUT_TRANSITION
761                        || type == InterfaceAutomatonTransition._OUTPUT_TRANSITION) {
762                    transition.label.setExpression(name + ";");
763                }
764            }
765        }
766
767        // remove ports
768        Iterator portNames = nameDifference.iterator();
769
770        while (portNames.hasNext()) {
771            String portName = (String) portNames.next();
772            Port port = getPort(portName);
773
774            try {
775                port.setContainer(null);
776            } catch (NameDuplicationException nameDuplication) {
777                // This cannot happen since the name is set to null.
778                throw new InternalErrorException(
779                        "Cannot set container to " + "null.");
780            }
781        }
782    }
783
784    /** Return the reacheable state pairs in the specified alternating
785     *  simulation. A state pair is reacheable if they can be reached
786     *  from the initial states of their corresponding automata through the
787     *  same input or output transitions, or internal transitions. The
788     *  internal transitions in the two automata that are taken to reach
789     *  a state pair do not have to be the same.
790     *  @param alternatingSimulation A set of instances of StatePair.
791     *  @param superAutomaton The automaton that contains the first state
792     *   in the state pairs in alternatingSimulation.
793     *  @param subAutomaton The automaton that contains the second state
794     *   in the state pairs in alternatingSimulation.
795     *  @return A set of instances of StatePair that only contain the
796     *   reacheable state pairs in alternatingSimulation.
797     *  @exception IllegalActionException If thrown by getInitialState().
798     */
799    public static Set reacheableAlternatingSimulation(Set alternatingSimulation,
800            InterfaceAutomaton superAutomaton, InterfaceAutomaton subAutomaton)
801            throws IllegalActionException {
802        // Use frontier exploration:
803        // Init:
804        //     if initial states not in alternatingSimulation
805        //         return a null set
806        //     else
807        //         reacheableSimulation = frontier = initial state pair
808        //
809        // Repeat:
810        //     take a state pair (p, q) from frontier
811        //     for each transition pTp'
812        //         if T is input or output
813        //             if the subAutomaton has transition(s) qTq'
814        //                 if (p', q') is in alternatingSimulation
815        //                     if (p', q') not already in reacheableSimulation
816        //                         put it in both reacheableSimulation and
817        //                         frontier
818        //         else (pTp' is internal transition)
819        //             if (p', q) is in alternatingSimulation
820        //                 if (p', q) is not already in reacheableSimulation
821        //                     put it in both reacheableSimulation and frontier
822        //
823        //     for each internal transition qTq'
824        //         if (p, q') is in alternatingSimulation
825        //             if (p, q') is not already in reacheableSimulation
826        //                 put it in both reacheableSimulation and frontier
827        //
828        //     stop until frontier is empty
829        State superInitial = superAutomaton.getInitialState();
830        State subInitial = subAutomaton.getInitialState();
831        StatePair pair = new StatePair(superInitial, subInitial);
832
833        if (!alternatingSimulation.contains(pair)) {
834            // initial states not in alternating simulation, return
835            // an empty set.
836            return new HashSet();
837        }
838
839        // initial states in alternating simulation
840        Set reacheableSimulation = new HashSet();
841        Set frontier = new HashSet();
842        reacheableSimulation.add(pair);
843        frontier.add(pair);
844
845        // repeat
846        while (!frontier.isEmpty()) {
847            // pick a state from frontier. It seems that there isn't an
848            // easy way to pick an arbitrary entry from a HashSet, except
849            // through Iterator
850            Iterator iterator = frontier.iterator();
851            StatePair currentPair = (StatePair) iterator.next();
852            frontier.remove(currentPair);
853
854            State superState = currentPair.first();
855            State subState = currentPair.second();
856
857            ComponentPort superPort = superState.outgoingPort;
858            Iterator superTransitions = superPort.linkedRelationList()
859                    .iterator();
860
861            while (superTransitions.hasNext()) {
862                InterfaceAutomatonTransition superTransition = (InterfaceAutomatonTransition) superTransitions
863                        .next();
864                State superDestination = superTransition.destinationState();
865                String superLabel = superTransition.getLabel();
866
867                int transitionType = superTransition.getType();
868
869                if (transitionType == InterfaceAutomatonTransition._INPUT_TRANSITION
870                        || transitionType == InterfaceAutomatonTransition._OUTPUT_TRANSITION) {
871                    // check whether sub automaton has same transition
872                    ComponentPort subPort = subState.outgoingPort;
873                    Iterator subTransitions = subPort.linkedRelationList()
874                            .iterator();
875
876                    while (subTransitions.hasNext()) {
877                        InterfaceAutomatonTransition subTransition = (InterfaceAutomatonTransition) subTransitions
878                                .next();
879                        String subLabel = subTransition.getLabel();
880
881                        if (superLabel.equals(subLabel)) {
882                            State subDestination = subTransition
883                                    .destinationState();
884                            StatePair newPair = new StatePair(superDestination,
885                                    subDestination);
886
887                            if (alternatingSimulation.contains(newPair)
888                                    && !reacheableSimulation
889                                            .contains(newPair)) {
890                                reacheableSimulation.add(newPair);
891                                frontier.add(newPair);
892                            }
893                        }
894                    }
895                } else {
896                    // internal transition in super automaton
897                    StatePair newPair = new StatePair(superDestination,
898                            subState);
899
900                    if (alternatingSimulation.contains(newPair)
901                            && !reacheableSimulation.contains(newPair)) {
902                        reacheableSimulation.add(newPair);
903                        frontier.add(newPair);
904                    }
905                }
906            }
907
908            // explore internal transitions from subState
909            ComponentPort subPort = subState.outgoingPort;
910            Iterator subTransitions = subPort.linkedRelationList().iterator();
911
912            while (subTransitions.hasNext()) {
913                InterfaceAutomatonTransition subTransition = (InterfaceAutomatonTransition) subTransitions
914                        .next();
915
916                int transitionType = subTransition.getType();
917
918                if (transitionType == InterfaceAutomatonTransition._INTERNAL_TRANSITION) {
919                    State subDestination = subTransition.destinationState();
920                    StatePair newPair = new StatePair(superState,
921                            subDestination);
922
923                    if (alternatingSimulation.contains(newPair)
924                            && !reacheableSimulation.contains(newPair)) {
925                        reacheableSimulation.add(newPair);
926                        frontier.add(newPair);
927                    }
928                }
929            }
930        }
931
932        return reacheableSimulation;
933    }
934
935    /** Rename the labels on some transitions. The argument is a Map
936     *  specifying which transition labels should be renamed. The keys of the
937     *  Map are the old label names, and the values are the new label names.
938     *  Neither the keys nor the values should include the ending character
939     *  "?", "!", or ";" that indicate the type of the transition. And this
940     *  method does not change the type.
941     *  <p>
942     *  For input and output transitions, this method also renames the ports
943     *  associated with the renamed transitions, if these ports are created
944     *  already. This is done regardless of whether there are instances of
945     *  transitions that correspond to the ports. For internal transitions,
946     *  this method renames the parameter associated with the renamed
947     *  transition.
948     *  @param nameMap A map between the old and the new label names.
949     *  @exception IllegalActionException If the new name is not legal.
950     *  @exception NameDuplicationException If the requested name change will
951     *   cause name collision.
952     */
953    public void renameTransitionLabels(Map nameMap)
954            throws IllegalActionException, NameDuplicationException {
955        Iterator iterator = relationList().iterator();
956
957        while (iterator.hasNext()) {
958            InterfaceAutomatonTransition transition = (InterfaceAutomatonTransition) iterator
959                    .next();
960            String oldLabel = transition.getLabel();
961            int length = oldLabel.length();
962            String oldLabelName = oldLabel.substring(0, length - 1);
963            String newLabelName = (String) nameMap.get(oldLabelName);
964
965            if (newLabelName != null) {
966                String ending = oldLabel.substring(length - 1, length);
967                String newLabel = newLabelName + ending;
968                transition.label.setExpression(newLabel);
969
970                // change port or parameter name
971                if (ending.equals("?") || ending.equals("!")) {
972                    Port port = getPort(oldLabelName);
973
974                    if (port != null) {
975                        port.setName(newLabelName);
976                    }
977                } else if (ending.equals(";")) {
978                    Parameter param = (Parameter) getAttribute(oldLabelName);
979                    param.setName(newLabelName);
980                } else {
981                    throw new InternalErrorException("Transition label "
982                            + "does not end with ?, !, or ;");
983                }
984            }
985        }
986
987        // for ports that do not have corresponding transitions, rename the
988        // ports
989        iterator = portList().iterator();
990
991        while (iterator.hasNext()) {
992            Port port = (Port) iterator.next();
993            String oldName = port.getName();
994            String newName = (String) nameMap.get(oldName);
995
996            if (newName != null) {
997                port.setName(newName);
998            }
999        }
1000    }
1001
1002    ///////////////////////////////////////////////////////////////////
1003    ////                         protected methods                 ////
1004
1005    /** Add an InterfaceAutomatonTransition to this InterfaceAutomaton.
1006     *  This method should not be used directly.  Call the setContainer()
1007     *  method of the transition instead. This method does not set the
1008     *  container of the transition to refer to this container. This method
1009     *  is <i>not</i> synchronized on the workspace, so the caller should be.
1010     *
1011     *  @param relation The InterfaceAutomatonTransition to contain.
1012     *  @exception IllegalActionException If the transition has no name, or
1013     *   is not an instance of Transition.
1014     *  @exception NameDuplicationException If the name collides with a name
1015     *   already on the contained transitions list.
1016     */
1017    @Override
1018    protected void _addRelation(ComponentRelation relation)
1019            throws IllegalActionException, NameDuplicationException {
1020        if (!(relation instanceof InterfaceAutomatonTransition)) {
1021            throw new IllegalActionException(this, relation,
1022                    "InterfaceAutomaton can only contain instances of "
1023                            + "InterfaceAutomatonTransition.");
1024        }
1025
1026        super._addRelation(relation);
1027    }
1028
1029    ///////////////////////////////////////////////////////////////////
1030    ////                         protected variables               ////
1031    ///////////////////////////////////////////////////////////////////
1032    ////                         private methods                   ////
1033    // Add a state to the product automaton and the frontier, if the
1034    // state is not added already. Return the state added or the one
1035    // already existed. This method is called from _computeProduct.
1036    private State _addState(InterfaceAutomaton product, State stateInThis,
1037            State stateInArgument, HashMap frontier)
1038            throws IllegalActionException, NameDuplicationException {
1039        String name = stateInThis.getName() + NAME_CONNECTOR
1040                + stateInArgument.getName();
1041        State state = (State) product.getEntity(name);
1042
1043        if (state == null) {
1044            // not in product
1045            state = new State(product, name);
1046
1047            Triple triple = new Triple(state, stateInThis, stateInArgument);
1048            frontier.put(name, triple);
1049        }
1050
1051        return state;
1052    }
1053
1054    // Add a transition to between two states in an automaton
1055    private void _addTransition(InterfaceAutomaton automaton,
1056            String relationNamePrefix, State sourceState,
1057            State destinationState, String label)
1058            throws IllegalActionException, NameDuplicationException {
1059        String name = automaton.uniqueName(relationNamePrefix);
1060        InterfaceAutomatonTransition transition = new InterfaceAutomatonTransition(
1061                automaton, name);
1062        sourceState.outgoingPort.link(transition);
1063        destinationState.incomingPort.link(transition);
1064        transition.label.setExpression(label);
1065    }
1066
1067    // Perform sanity check on this automaton. In particular, this method
1068    // checks: (1) all input transitions have a corresponding input port,
1069    // all output transitions have a corresponding output port, and all
1070    // internal transitions have a corresponding parameter; (2) The names
1071    // for input, output, and internal transitions do not overlap.
1072    // If one of the above check fails, an exception is thrown;
1073    // otherwise, this method just returns.
1074    //
1075    private void _check() throws IllegalActionException {
1076        // check all transitions have corresponding ports or parameter.
1077        Iterator iterator = relationList().iterator();
1078
1079        while (iterator.hasNext()) {
1080            InterfaceAutomatonTransition transition = (InterfaceAutomatonTransition) iterator
1081                    .next();
1082            String label = transition.getLabel();
1083            String name = label.substring(0, label.length() - 1);
1084
1085            if (label.endsWith("?")) {
1086                IOPort port = (IOPort) getPort(name);
1087
1088                if (port == null || port.isInput() == false) {
1089                    throw new IllegalActionException(
1090                            "InterfaceAutomaton._check: " + getFullName()
1091                                    + ": The transition " + label
1092                                    + " does not have a corresponding input port.");
1093                }
1094            } else if (label.endsWith("!")) {
1095                IOPort port = (IOPort) getPort(name);
1096
1097                if (port == null || port.isOutput() == false) {
1098                    throw new IllegalActionException(
1099                            "InterfaceAutomaton._check: " + getFullName()
1100                                    + ": The transition " + label
1101                                    + " does not have a corresponding output port.");
1102                }
1103            } else if (label.endsWith(";")) {
1104                Attribute attribute = getAttribute(name);
1105
1106                if (attribute == null || !(attribute instanceof Parameter)) {
1107                    throw new IllegalActionException(
1108                            "InterfaceAutomaton._check: " + getFullName()
1109                                    + ": The transition " + label
1110                                    + " does not have a corresponding Parameter.");
1111                }
1112            } else {
1113                throw new InternalErrorException(
1114                        "InterfaceAutomaton._check: The label " + label
1115                                + " does not end with ?, !, or ;.");
1116            }
1117        }
1118
1119        // check transition names do not overlap
1120        Set inputNames = inputNameSet();
1121        Set outputNames = outputNameSet();
1122        inputNames.retainAll(outputNames);
1123
1124        if (!inputNames.isEmpty()) {
1125            throw new IllegalActionException("InterfaceAutomaton._check: "
1126                    + getFullName() + ": The names for input and output "
1127                    + "transitions overlap.");
1128        }
1129
1130        inputNames = inputNameSet();
1131
1132        Set internalNames = internalTransitionNameSet();
1133        inputNames.retainAll(internalNames);
1134
1135        if (!inputNames.isEmpty()) {
1136            throw new IllegalActionException("InterfaceAutomaton._check: "
1137                    + getFullName() + ": The names for input and internal "
1138                    + "transitions overlap.");
1139        }
1140
1141        outputNames.retainAll(internalNames);
1142
1143        if (!outputNames.isEmpty()) {
1144            throw new IllegalActionException("InterfaceAutomaton._check: "
1145                    + getFullName() + ": The names for output and internal "
1146                    + "transitions overlap.");
1147        }
1148    }
1149
1150    // Throw an exception if this automaton and the specified one is not
1151    // composable.
1152    private void _checkComposability(InterfaceAutomaton automaton)
1153            throws IllegalActionException {
1154        String message = "InterfaceAutomaton._checkComposability: "
1155                + this.getFullName() + " is not composable with "
1156                + automaton.getFullName() + " because ";
1157
1158        // check the internal transitions of one do not overlap with the
1159        // transitions of the other
1160        Set thisInternals = this.internalTransitionNameSet();
1161
1162        Set thatInputs = automaton.inputNameSet();
1163        Set thatOutputs = automaton.outputNameSet();
1164        Set thatInternals = automaton.internalTransitionNameSet();
1165
1166        thatInputs.retainAll(thisInternals);
1167        thatOutputs.retainAll(thisInternals);
1168        thatInternals.retainAll(thisInternals);
1169
1170        if (!thatInputs.isEmpty() || !thatOutputs.isEmpty()
1171                || !thatInternals.isEmpty()) {
1172            throw new IllegalActionException(message + "the internal "
1173                    + "transitions of the former overlaps with the transitions "
1174                    + "of the latter.");
1175        }
1176
1177        thatInternals = automaton.internalTransitionNameSet();
1178
1179        Set thisInputs = this.inputNameSet();
1180        Set thisOutputs = this.outputNameSet();
1181        thisInternals = this.internalTransitionNameSet();
1182
1183        thisInputs.retainAll(thatInternals);
1184        thisOutputs.retainAll(thatInternals);
1185        thisInternals.retainAll(thatInternals);
1186
1187        if (!thisInputs.isEmpty() || !thisOutputs.isEmpty()
1188                || !thisInternals.isEmpty()) {
1189            throw new IllegalActionException(message + "the internal "
1190                    + "transitions of the latter overlaps with the transitions "
1191                    + "of the former.");
1192        }
1193
1194        // check the input transitions do not overlap
1195        thisInputs = this.inputNameSet();
1196        thatInputs = automaton.inputNameSet();
1197        thisInputs.retainAll(thatInputs);
1198
1199        if (!thisInputs.isEmpty()) {
1200            throw new IllegalActionException(
1201                    message + "the input " + "transitions of the two overlap.");
1202        }
1203
1204        // check the output transitions do not overlap
1205        thisOutputs = this.outputNameSet();
1206        thatOutputs = automaton.outputNameSet();
1207        thisOutputs.retainAll(thatOutputs);
1208
1209        if (!thisOutputs.isEmpty()) {
1210            throw new IllegalActionException(message + "the output "
1211                    + "transitions of the two overlap.");
1212        }
1213    }
1214
1215    // Compute the product of this automaton and the argument. Also store
1216    // the illegal states found in the Set _illegalStates.
1217    //
1218    // Use frontier exploration. The frontier is represented by a HashMap
1219    // frontier. The key is the name of the state in the product, the value
1220    // is a Triple: stateInProduct, stateInThis, stateInArgument. The keys
1221    // are used to easily check if a product state is in the frontier.
1222    //
1223    // init: product = (this.initialState x automaton.initialSate)
1224    //       frontier = (this.initialState x automaton.initialSate,
1225    //                   this.initialState, automaton.initialState)
1226    // iterate: pick (remove) a state p x q from frontier;
1227    //          pick a step pTr from p
1228    //            (case 1) T is input for p:
1229    //              (1A) T is input of product: add T to product
1230    //              (1B) T is shared:
1231    //                (1Ba) q has T output: add T to product as internal
1232    //                      transition
1233    //                (1Bb) q does not have T output: transition cannot happen
1234    //                      in product. ignore
1235    //            (case 2) T is output for p:
1236    //              (2A) T is output of product: add T to product
1237    //              (2B) T is shared:
1238    //                (2Ba) q has T input: add T to product as internal
1239    //                      transition
1240    //                (2Bb) q does not have T input: mark p x q as illegal.
1241    //                      stop exploring from p x q.
1242    //            (case 3) T is internal for p: add T to product
1243    //
1244    //          The cases for a transition from q is almost symmetric,
1245    //          but be careful not to add shared transition twice. In the code
1246    //          below, shared transitions are added in the code that explores
1247    //          the state with the input transition.
1248    //
1249    //          (after exploring all transitions from p and q), remove p x q
1250    //          from frontier.
1251    //
1252    //          end when frontier is empty
1253    //
1254    //private InterfaceAutomaton _computeProduct(InterfaceAutomaton automaton)
1255    //        throws IllegalActionException {
1256    //    return _computeProduct(automaton, false);
1257    //}
1258    // Compute the product of this automaton and the argument. Also store
1259    // the illegal states found in the Set _illegalStates.
1260    //
1261    // Use frontier exploration. The frontier is represented by a HashMap
1262    // frontier. The key is the name of the state in the product, the value
1263    // is a Triple: stateInProduct, stateInThis, stateInArgument. The keys
1264    // are used to easily check if a product state is in the frontier.
1265    //
1266    // init: product = (this.initialState x automaton.initialSate)
1267    //       frontier = (this.initialState x automaton.initialSate,
1268    //                   this.initialState, automaton.initialState)
1269    // iterate: pick (remove) a state p x q from frontier;
1270    //      if neither p nor q are transient {
1271    //          pick a step pTr from p
1272    //            (case 1) T is input for p:
1273    //              (1A) T is input of product: add T to product
1274    //              (1B) T is shared:
1275    //                (1Ba) q has T output: add T to product as internal
1276    //                      transition
1277    //                (1Bb) q does not have T output: transition cannot happen
1278    //                      in product. ignore
1279    //            (case 2) T is output for p:
1280    //              (2A) T is output of product: add T to product
1281    //              (2B) T is shared:
1282    //                (2Ba) q has T input: add T to product as internal
1283    //                      transition
1284    //                (2Bb) q does not have T input: mark p x q as illegal.
1285    //                      stop exploring from p x q.
1286    //            (case 3) T is internal for p: add T to product
1287    //
1288    //          The cases for a transition from q is almost symmetric,
1289    //          but be careful not to add shared transition twice. In the code
1290    //          below, shared transitions are added in the code that explores
1291    //          the state with the input transition.
1292    //      } else {
1293    //          // one or both of p and q are transient
1294    //          if (both p and q are transient)
1295    //              throw exception
1296    //          else if (p is transient)
1297    //              pick a step qTr from p
1298    //                (case 1) T is input for p:
1299    //                  throw exception
1300    //                (case 2) T is output for p:
1301    //                  (2A) T is output of product: add T to product
1302    //                  (2B) T is shared:
1303    //                    (2Ba) q has T input: add T to product as internal
1304    //                          transition
1305    //                    (2Bb) q does not have T input: mark q x q as illegal
1306    //                          stop exploring from p x q.
1307    //                (case 3) T is internal for p: add T to product
1308    //           } else {
1309    //               // q is transient.
1310    //               // This case is symmetric as above, omitted.
1311    //           }
1312    //      }
1313    //
1314    //      (after exploring all transitions from p and q), remove p x q
1315    //       from frontier.
1316    //
1317    //      end when frontier is empty
1318    //
1319    private InterfaceAutomaton _computeProduct(InterfaceAutomaton automaton,
1320            boolean considerTransient) throws IllegalActionException {
1321        try {
1322            // init
1323            _illegalStates = new HashSet();
1324
1325            InterfaceAutomaton product = new InterfaceAutomaton();
1326            product.setName(
1327                    this.getName() + NAME_CONNECTOR + automaton.getName());
1328
1329            HashMap frontier = new HashMap();
1330
1331            // create initial state
1332            State stateInThis = this.getInitialState();
1333            State stateInArgument = automaton.getInitialState();
1334            String name = stateInThis.getName() + NAME_CONNECTOR
1335                    + stateInArgument.getName();
1336            State stateInProduct = new State(product, name);
1337            product.initialStateName.setExpression(name);
1338
1339            Triple triple = new Triple(stateInProduct, stateInThis,
1340                    stateInArgument);
1341            frontier.put(name, triple);
1342
1343            // iterate
1344            while (!frontier.isEmpty()) {
1345                // pick a state from frontier. It seems that there isn't an
1346                // easy way to pick an arbitrary entry from a HashMap, except
1347                // through Iterator
1348                Iterator iterator = frontier.keySet().iterator();
1349                name = (String) iterator.next();
1350                triple = (Triple) frontier.remove(name);
1351                stateInProduct = triple._stateInProduct;
1352                stateInThis = triple._stateInThis;
1353                stateInArgument = triple._stateInArgument;
1354
1355                boolean isStateInProductIllegal = false;
1356
1357                if (!considerTransient || !_isTransient(stateInThis)
1358                        && !_isTransient(stateInArgument)) {
1359                    // extend frontier from state in this automaton
1360                    ComponentPort outPort = stateInThis.outgoingPort;
1361                    Iterator transitions = outPort.linkedRelationList()
1362                            .iterator();
1363
1364                    while (transitions.hasNext() && !isStateInProductIllegal) {
1365                        InterfaceAutomatonTransition transition = (InterfaceAutomatonTransition) transitions
1366                                .next();
1367
1368                        State destinationInThis = transition.destinationState();
1369
1370                        // get transitionLabel, transitionName and relation name
1371                        // for later use
1372                        String transitionLabel = transition.getLabel();
1373
1374                        // remove ending "?", "!", or ";"
1375                        String transitionName = transitionLabel.substring(0,
1376                                transitionLabel.length() - 1);
1377
1378                        // switch depending on type of transition
1379                        int transitionType = transition.getType();
1380
1381                        if (transitionType == InterfaceAutomatonTransition._INPUT_TRANSITION) {
1382                            // case 1
1383                            if (_inputNames.contains(transitionName)) {
1384                                // case 1A. Add transition to product as input
1385                                // transition
1386                                State destinationInProduct = _addState(product,
1387                                        destinationInThis, stateInArgument,
1388                                        frontier);
1389                                _addTransition(product, this.getName(),
1390                                        stateInProduct, destinationInProduct,
1391                                        transitionLabel);
1392                            } else {
1393                                // case 1B. transition is shared in product
1394                                String outName = transitionName + "!";
1395                                Set destinationsInArgument = _getDestinationStates(
1396                                        stateInArgument, outName);
1397
1398                                if (!destinationsInArgument.isEmpty()) {
1399                                    // case 1Ba. q has T output. Add T to
1400                                    // product as internal transition
1401                                    Iterator destinations = destinationsInArgument
1402                                            .iterator();
1403
1404                                    while (destinations.hasNext()) {
1405                                        State destinationInArgument = (State) destinations
1406                                                .next();
1407                                        State destinationInProduct = _addState(
1408                                                product, destinationInThis,
1409                                                destinationInArgument,
1410                                                frontier);
1411                                        _addTransition(product,
1412                                                this.getName() + NAME_CONNECTOR
1413                                                        + automaton.getName(),
1414                                                stateInProduct,
1415                                                destinationInProduct,
1416                                                transitionName + ";");
1417                                    }
1418                                } else {
1419                                    // case 1Bb. q does not have T output.
1420                                    // Transition cannot happen, ignore.
1421                                }
1422                            }
1423                        } else if (transitionType == InterfaceAutomatonTransition._OUTPUT_TRANSITION) {
1424                            // case 2. T is output for p.
1425                            if (_outputNames.contains(transitionName)) {
1426                                // case 2A. T is output of product. Add T to
1427                                // product as output transition
1428                                State destinationInProduct = _addState(product,
1429                                        destinationInThis, stateInArgument,
1430                                        frontier);
1431                                _addTransition(product, this.getName(),
1432                                        stateInProduct, destinationInProduct,
1433                                        transitionLabel);
1434                            } else {
1435                                // case 2B. transition is shared in product
1436                                String inName = transitionName + "?";
1437                                Set destinationsInArgument = _getDestinationStates(
1438                                        stateInArgument, inName);
1439
1440                                if (!destinationsInArgument.isEmpty()) {
1441                                    // case 2Ba. q has T input. Need to add T
1442                                    // to product as internal transition.
1443                                    // However, to avoid adding this transition
1444                                    // twice, leave the code that explores
1445                                    // state q to add the transition
1446                                } else {
1447                                    // case 2Bb. q does not have T input.
1448                                    // stateInProduct is illegal
1449                                    _illegalStates.add(stateInProduct);
1450                                    isStateInProductIllegal = true;
1451                                }
1452                            }
1453                        } else if (transitionType == InterfaceAutomatonTransition._INTERNAL_TRANSITION) {
1454                            // case 3. T is internal for p. Add T to product
1455                            State destinationInProduct = _addState(product,
1456                                    destinationInThis, stateInArgument,
1457                                    frontier);
1458                            _addTransition(product, this.getName(),
1459                                    stateInProduct, destinationInProduct,
1460                                    transitionLabel);
1461                        } else {
1462                            throw new InternalErrorException(
1463                                    "InterfaceAutomaton._computeProduct: "
1464                                            + "unrecognized transition type.");
1465                        }
1466                    } // end explore from state p
1467
1468                    // extend frontier from state in the argument automaton
1469                    outPort = stateInArgument.outgoingPort;
1470                    transitions = outPort.linkedRelationList().iterator();
1471
1472                    while (transitions.hasNext() && !isStateInProductIllegal) {
1473                        InterfaceAutomatonTransition transition = (InterfaceAutomatonTransition) transitions
1474                                .next();
1475
1476                        State destinationInArgument = transition
1477                                .destinationState();
1478
1479                        // get transitionLabel, transitionName and relation name
1480                        // for later use
1481                        String transitionLabel = transition.getLabel();
1482
1483                        // remove ending "?", "!", or ";"
1484                        String transitionName = transitionLabel.substring(0,
1485                                transitionLabel.length() - 1);
1486
1487                        // switch depending on type of transition
1488                        int transitionType = transition.getType();
1489
1490                        if (transitionType == InterfaceAutomatonTransition._INPUT_TRANSITION) {
1491                            // case 1
1492                            if (_inputNames.contains(transitionName)) {
1493                                // case 1A. Add transition to product as input
1494                                // transition
1495                                State destinationInProduct = _addState(product,
1496                                        stateInThis, destinationInArgument,
1497                                        frontier);
1498                                _addTransition(product, automaton.getName(),
1499                                        stateInProduct, destinationInProduct,
1500                                        transitionLabel);
1501                            } else {
1502                                // case 1B. transition is shared in product
1503                                String outName = transitionName + "!";
1504                                Set destinationsInThis = _getDestinationStates(
1505                                        stateInThis, outName);
1506
1507                                if (!destinationsInThis.isEmpty()) {
1508                                    // case 1Ba. p has T output. Add T to
1509                                    // product as internal transition
1510                                    Iterator destinations = destinationsInThis
1511                                            .iterator();
1512
1513                                    while (destinations.hasNext()) {
1514                                        State destinationInThis = (State) destinations
1515                                                .next();
1516                                        State destinationInProduct = _addState(
1517                                                product, destinationInThis,
1518                                                destinationInArgument,
1519                                                frontier);
1520                                        _addTransition(product,
1521                                                this.getName() + NAME_CONNECTOR
1522                                                        + automaton.getName(),
1523                                                stateInProduct,
1524                                                destinationInProduct,
1525                                                transitionName + ";");
1526                                    }
1527                                } else {
1528                                    // case 1Bb. p does not have T output.
1529                                    // Transition cannot happen, ignore.
1530                                }
1531                            }
1532                        } else if (transitionType == InterfaceAutomatonTransition._OUTPUT_TRANSITION) {
1533                            // case 2. T is output for q.
1534                            if (_outputNames.contains(transitionName)) {
1535                                // case 2A. T is output of product. Add T to
1536                                // product as output transition
1537                                State destinationInProduct = _addState(product,
1538                                        stateInThis, destinationInArgument,
1539                                        frontier);
1540                                _addTransition(product, automaton.getName(),
1541                                        stateInProduct, destinationInProduct,
1542                                        transitionLabel);
1543                            } else {
1544                                // case 2B. transition is shared in product
1545                                String inName = transitionName + "?";
1546                                Set destinationsInThis = _getDestinationStates(
1547                                        stateInThis, inName);
1548
1549                                if (!destinationsInThis.isEmpty()) {
1550                                    // case 2Ba. p has T input. Need to add T
1551                                    // to product as internal transition.
1552                                    // However, to avoid adding this transition
1553                                    // twice, leave the code that explores
1554                                    // state p to add the transition
1555                                } else {
1556                                    // case 2Bb. p does not have T input.
1557                                    // stateInProduct is illegal
1558                                    _illegalStates.add(stateInProduct);
1559                                    isStateInProductIllegal = true;
1560                                }
1561                            }
1562                        } else if (transitionType == InterfaceAutomatonTransition._INTERNAL_TRANSITION) {
1563                            // case 3. T is internal for q. Add T to product
1564                            State destinationInProduct = _addState(product,
1565                                    stateInThis, destinationInArgument,
1566                                    frontier);
1567                            _addTransition(product, automaton.getName(),
1568                                    stateInProduct, destinationInProduct,
1569                                    transitionLabel);
1570                        } else {
1571                            throw new InternalErrorException(
1572                                    "InterfaceAutomaton._computeProduct: "
1573                                            + "unrecognized transition type.");
1574                        }
1575                    } // end explore from state q
1576                } else {
1577                    // one or both of stateInThis and stateInArgument are
1578                    // transient
1579                    if (_isTransient(stateInThis)
1580                            && _isTransient(stateInArgument)) {
1581                        throw new IllegalActionException("Cannot compute "
1582                                + "product since both states are transient: "
1583                                + stateInThis.getName() + " and "
1584                                + stateInArgument.getName());
1585                    }
1586
1587                    if (_isTransient(stateInThis)) {
1588                        // extend frontier from transient state in this
1589                        // automaton
1590                        ComponentPort outPort = stateInThis.outgoingPort;
1591                        Iterator transitions = outPort.linkedRelationList()
1592                                .iterator();
1593
1594                        while (transitions.hasNext()
1595                                && !isStateInProductIllegal) {
1596                            InterfaceAutomatonTransition transition = (InterfaceAutomatonTransition) transitions
1597                                    .next();
1598
1599                            State destinationInThis = transition
1600                                    .destinationState();
1601
1602                            // get transitionLabel, transitionName and
1603                            // relation name for later use
1604                            String transitionLabel = transition.getLabel();
1605
1606                            // remove ending "?", "!", or ";"
1607                            String transitionName = transitionLabel.substring(0,
1608                                    transitionLabel.length() - 1);
1609
1610                            // switch depending on type of transition
1611                            int transitionType = transition.getType();
1612
1613                            if (transitionType == InterfaceAutomatonTransition._INPUT_TRANSITION) {
1614                                // case 1
1615                                throw new IllegalActionException("Transient "
1616                                        + "state " + stateInThis.getName()
1617                                        + " in " + getName() + " has input "
1618                                        + "transition " + transitionLabel);
1619                            } else if (transitionType == InterfaceAutomatonTransition._OUTPUT_TRANSITION) {
1620                                // case 2. T is output for p.
1621                                if (_outputNames.contains(transitionName)) {
1622                                    // case 2A. T is output of product. Add T
1623                                    // to product as output transition
1624                                    State destinationInProduct = _addState(
1625                                            product, destinationInThis,
1626                                            stateInArgument, frontier);
1627                                    _addTransition(product, this.getName(),
1628                                            stateInProduct,
1629                                            destinationInProduct,
1630                                            transitionLabel);
1631                                } else {
1632                                    // case 2B. transition is shared in product
1633                                    String inName = transitionName + "?";
1634                                    Set destinationsInArgument = _getDestinationStates(
1635                                            stateInArgument, inName);
1636
1637                                    if (!destinationsInArgument.isEmpty()) {
1638                                        // case 2Ba. q has T input. Add T to
1639                                        // product as internal transition.
1640                                        Iterator destinations = destinationsInArgument
1641                                                .iterator();
1642
1643                                        while (destinations.hasNext()) {
1644                                            State destinationInArgument = (State) destinations
1645                                                    .next();
1646                                            State destinationInProduct = _addState(
1647                                                    product, destinationInThis,
1648                                                    destinationInArgument,
1649                                                    frontier);
1650                                            _addTransition(product,
1651                                                    this.getName()
1652                                                            + NAME_CONNECTOR
1653                                                            + automaton
1654                                                                    .getName(),
1655                                                    stateInProduct,
1656                                                    destinationInProduct,
1657                                                    transitionName + ";");
1658                                        }
1659                                    } else {
1660                                        // case 2Bb. q does not have T input.
1661                                        // stateInProduct is illegal
1662                                        _illegalStates.add(stateInProduct);
1663                                        isStateInProductIllegal = true;
1664                                    }
1665                                }
1666                            } else if (transitionType == InterfaceAutomatonTransition._INTERNAL_TRANSITION) {
1667                                // case 3. T is internal for p. Add T to product
1668                                State destinationInProduct = _addState(product,
1669                                        destinationInThis, stateInArgument,
1670                                        frontier);
1671                                _addTransition(product, this.getName(),
1672                                        stateInProduct, destinationInProduct,
1673                                        transitionLabel);
1674                            } else {
1675                                throw new InternalErrorException(
1676                                        "InterfaceAutomaton._computeProduct: "
1677                                                + "unrecognized transition type.");
1678                            }
1679                        } // end explore from transient state p
1680                    } else {
1681                        // stateInArgument is transient.
1682                        // extend frontier from state in the argument automaton
1683                        ComponentPort outPort = stateInArgument.outgoingPort;
1684                        Iterator transitions = outPort.linkedRelationList()
1685                                .iterator();
1686
1687                        while (transitions.hasNext()
1688                                && !isStateInProductIllegal) {
1689                            InterfaceAutomatonTransition transition = (InterfaceAutomatonTransition) transitions
1690                                    .next();
1691
1692                            State destinationInArgument = transition
1693                                    .destinationState();
1694
1695                            // get transitionLabel, transitionName and
1696                            // relation name for later use
1697                            String transitionLabel = transition.getLabel();
1698
1699                            // remove ending "?", "!", or ";"
1700                            String transitionName = transitionLabel.substring(0,
1701                                    transitionLabel.length() - 1);
1702
1703                            // switch depending on type of transition
1704                            int transitionType = transition.getType();
1705
1706                            if (transitionType == InterfaceAutomatonTransition._INPUT_TRANSITION) {
1707                                throw new IllegalActionException("Transient "
1708                                        + "state " + stateInArgument.getName()
1709                                        + " in " + automaton.getName()
1710                                        + " has input transition "
1711                                        + transitionLabel);
1712                            } else if (transitionType == InterfaceAutomatonTransition._OUTPUT_TRANSITION) {
1713                                // case 2. T is output for q.
1714                                if (_outputNames.contains(transitionName)) {
1715                                    // case 2A. T is output of product. Add T to
1716                                    // product as output transition
1717                                    State destinationInProduct = _addState(
1718                                            product, stateInThis,
1719                                            destinationInArgument, frontier);
1720                                    _addTransition(product, automaton.getName(),
1721                                            stateInProduct,
1722                                            destinationInProduct,
1723                                            transitionLabel);
1724                                } else {
1725                                    // case 2B. transition is shared in product
1726                                    String inName = transitionName + "?";
1727                                    Set destinationsInThis = _getDestinationStates(
1728                                            stateInThis, inName);
1729
1730                                    if (!destinationsInThis.isEmpty()) {
1731                                        // case 2Ba. p has T input. Add T
1732                                        // to product as internal transition.
1733                                        Iterator destinations = destinationsInThis
1734                                                .iterator();
1735
1736                                        while (destinations.hasNext()) {
1737                                            State destinationInThis = (State) destinations
1738                                                    .next();
1739                                            State destinationInProduct = _addState(
1740                                                    product, destinationInThis,
1741                                                    destinationInArgument,
1742                                                    frontier);
1743                                            _addTransition(product,
1744                                                    this.getName()
1745                                                            + NAME_CONNECTOR
1746                                                            + automaton
1747                                                                    .getName(),
1748                                                    stateInProduct,
1749                                                    destinationInProduct,
1750                                                    transitionName + ";");
1751                                        }
1752                                    } else {
1753                                        // case 2Bb. p does not have T input.
1754                                        // stateInProduct is illegal
1755                                        _illegalStates.add(stateInProduct);
1756                                        isStateInProductIllegal = true;
1757                                    }
1758                                }
1759                            } else if (transitionType == InterfaceAutomatonTransition._INTERNAL_TRANSITION) {
1760                                // case 3. T is internal for q. Add T to product
1761                                State destinationInProduct = _addState(product,
1762                                        stateInThis, destinationInArgument,
1763                                        frontier);
1764                                _addTransition(product, automaton.getName(),
1765                                        stateInProduct, destinationInProduct,
1766                                        transitionLabel);
1767                            } else {
1768                                throw new InternalErrorException(
1769                                        "InterfaceAutomaton._computeProduct: "
1770                                                + "unrecognized transition type.");
1771                            }
1772                        } // end explore from transient state q
1773                    }
1774                } // finished processing one state in frontier
1775            } // finished processing all states in frontier
1776
1777            return product;
1778        } catch (NameDuplicationException exception) {
1779            // FIXME: this can actually happen, although extremely unlikely.
1780            // Eg. this automaton has states "X" and "Y_Z", the argument
1781            // has "X_Y" and "Z". Do we need to worry about this?
1782            throw new InternalErrorException(
1783                    "InterfaceAutomaton._computeProduct: name in product "
1784                            + "automaton clashes: " + exception.getMessage());
1785        }
1786    }
1787
1788    // Compute the names of the input, output, and internal transitions of
1789    // the composition.  Set the results to _inputNames, _outputNames, and
1790    // _internalNames;
1791    private void _computeTransitionNamesInComposition(
1792            InterfaceAutomaton automaton) {
1793        // compute shared transitions
1794        Set shared = this.inputNameSet();
1795        Set thatOutputs = automaton.outputNameSet();
1796        shared.retainAll(thatOutputs);
1797
1798        Set sharedOutputNameSet = this.outputNameSet();
1799        Set thatInputs = automaton.inputNameSet();
1800        sharedOutputNameSet.retainAll(thatInputs);
1801
1802        shared.addAll(sharedOutputNameSet);
1803
1804        // compute input, output, and internal transitions
1805        _inputNames = this.inputNameSet();
1806        _inputNames.addAll(automaton.inputNameSet());
1807        _inputNames.removeAll(shared);
1808
1809        _outputNames = this.outputNameSet();
1810        _outputNames.addAll(automaton.outputNameSet());
1811        _outputNames.removeAll(shared);
1812
1813        _internalNames = this.internalTransitionNameSet();
1814        _internalNames.addAll(automaton.internalTransitionNameSet());
1815        _internalNames.addAll(shared);
1816    }
1817
1818    // Return true if condition 1 in Definition 14 of the interface automaton
1819    // paper is satisfied. That is, return true if the externally enabled input
1820    // transitions for the state in the super automaton is a subset of the
1821    // externally enabled input transitions for the state in the sub automaton,
1822    // and the externally enabled output transitions of the sub automaton is
1823    // a subset of the externally enabled output transitions of the super
1824    // automaton.
1825    private static boolean _condition1Satisfied(
1826            InterfaceAutomaton superAutomaton, State superState,
1827            InterfaceAutomaton subAutomaton, State subState) {
1828        Set inputLabelsInSuper = superAutomaton
1829                .externallyEnabledInputTransitionLabels(superState);
1830        Set inputLabelsInSub = subAutomaton
1831                .externallyEnabledInputTransitionLabels(subState);
1832
1833        if (inputLabelsInSub.containsAll(inputLabelsInSuper) == false) {
1834            return false;
1835        }
1836
1837        Set outputLabelsInSuper = superAutomaton
1838                .externallyEnabledOutputTransitionLabels(superState);
1839        Set outputLabelsInSub = subAutomaton
1840                .externallyEnabledOutputTransitionLabels(subState);
1841        return outputLabelsInSuper.containsAll(outputLabelsInSub);
1842    }
1843
1844    // Return true if condition 2 in Definition 14 of the interface automaton
1845    // paper is satisfied. That is, return true if the super automaton can
1846    // match the move of the sub automaton at the specified states.
1847    private static boolean _condition2Satisfied(
1848            InterfaceAutomaton superAutomaton, State superState,
1849            InterfaceAutomaton subAutomaton, State subState,
1850            Set currentSimulation) {
1851        // consideredTransitions are the union of the externally enabled
1852        // input transitions at the super state and the externally enabled
1853        // output transitions at the sub state.
1854        Set consideredTransitionLabels = superAutomaton
1855                .externallyEnabledInputTransitionLabels(superState);
1856        Set consideredOutputTransitionLabels = subAutomaton
1857                .externallyEnabledOutputTransitionLabels(subState);
1858        consideredTransitionLabels.addAll(consideredOutputTransitionLabels);
1859
1860        Iterator transitionLabels = consideredTransitionLabels.iterator();
1861
1862        while (transitionLabels.hasNext()) {
1863            String label = (String) transitionLabels.next();
1864            Set destinationsInSub = subAutomaton
1865                    .externallyEnabledDestinations(subState, label);
1866            Set destinationsInSuper = superAutomaton
1867                    .externallyEnabledDestinations(superState, label);
1868
1869            // check that for each destination in the sub automaton, there is
1870            // an destination in the super automaton such that the two
1871            // destinations are in the current alternating simulation relation.
1872            Iterator subStates = destinationsInSub.iterator();
1873
1874            while (subStates.hasNext()) {
1875                State destinationInSub = (State) subStates.next();
1876                boolean inCurrentSimulation = false;
1877                Iterator superStates = destinationsInSuper.iterator();
1878
1879                while (superStates.hasNext()) {
1880                    State destinationInSuper = (State) superStates.next();
1881                    StatePair pair = new StatePair(destinationInSuper,
1882                            destinationInSub);
1883
1884                    if (currentSimulation.contains(pair)) {
1885                        inCurrentSimulation = true;
1886                        break;
1887                    }
1888                }
1889
1890                if (inCurrentSimulation == false) {
1891                    return false;
1892                }
1893            }
1894        }
1895
1896        return true;
1897    }
1898
1899    // Create ports on the composition automaton, based on _inputNames and
1900    // _outputNames.
1901    private void _createPorts(InterfaceAutomaton composition)
1902            throws IllegalActionException {
1903        try {
1904            Iterator iterator = _inputNames.iterator();
1905
1906            while (iterator.hasNext()) {
1907                String name = (String) iterator.next();
1908                TypedIOPort port = new TypedIOPort(composition, name);
1909                port.setInput(true);
1910            }
1911
1912            iterator = _outputNames.iterator();
1913
1914            while (iterator.hasNext()) {
1915                String name = (String) iterator.next();
1916                TypedIOPort port = new TypedIOPort(composition, name);
1917                port.setOutput(true);
1918            }
1919        } catch (NameDuplicationException exception) {
1920            // this should not happen. Composability check should ensure that
1921            // names do not clash.
1922            throw new InternalErrorException("InterfaceAutomaton._createPort: "
1923                    + "Cannot create ports due to name duplication: "
1924                    + exception.getMessage());
1925        }
1926    }
1927
1928    // Return the set of destination states of the transition from the
1929    // specified state with the specified label. This set may contain more
1930    // than one state if the automaton is non-deterministic.
1931    // Return an empty set if such a transition does not exist.
1932    private Set _getDestinationStates(State state, String label) {
1933        Set destinations = new HashSet();
1934        ComponentPort outPort = state.outgoingPort;
1935        Iterator iterator = outPort.linkedRelationList().iterator();
1936
1937        while (iterator.hasNext()) {
1938            InterfaceAutomatonTransition transition = (InterfaceAutomatonTransition) iterator
1939                    .next();
1940            String transitionLabel = transition.getLabel();
1941
1942            if (transitionLabel.equals(label)) {
1943                destinations.add(transition.destinationState());
1944            }
1945        }
1946
1947        return destinations;
1948    }
1949
1950    // Return true if the specified state is transient.
1951    // FIXME: currently, a letter "t" in state name indicates the state is
1952    // transient. This should probably be changed to an attribute in state.
1953    private static boolean _isTransient(State state) {
1954        String name = state.getName();
1955
1956        if (name.indexOf("t") == -1) {
1957            return false;
1958        }
1959
1960        return true;
1961    }
1962
1963    // prune illegal states from the argument. Use frontier exploration.
1964    // The Set frontier contains the references of illegal states in the
1965    // frontier; the Set _illegalStates contains references of all the
1966    // illegal states found so far. The Set frontier is always a subset
1967    // of _illegalStates. When this method is called, _illegalStates contains
1968    // an initial set of illegal states computed in _computeProduct().
1969    //
1970    // init: frontier = _illegalStates
1971    //
1972    // iterate: pick (remove) a state p from frontier
1973    //          for all states s that has an output or internal transition to p,
1974    //              if s is not in _illegalStates
1975    //                add s to both _illegalStates and frontier
1976    //
1977    //          end when frontier is empty
1978    //
1979    // remove all states in _illegalStates from automaton
1980    //
1981    // Note: this method does not operate the "this" automaton, it operates
1982    // on the composition automaton. This is implicit since _illegalStates
1983    // contains the states in the composition.
1984    private void _pruneIllegalStates() {
1985        // init
1986        Set frontier = new HashSet();
1987        Iterator iterator = _illegalStates.iterator();
1988
1989        while (iterator.hasNext()) {
1990            frontier.add(iterator.next());
1991        }
1992
1993        // iterate
1994        while (!frontier.isEmpty()) {
1995            // there does not seem to be an easy way to remove an arbitrary
1996            // element, except through Iterator
1997            iterator = frontier.iterator();
1998
1999            State current = (State) iterator.next();
2000            frontier.remove(current);
2001
2002            // make all states that can reach current by output or internal
2003            // transitions illegal
2004            ComponentPort inPort = current.incomingPort;
2005            Iterator transitions = inPort.linkedRelationList().iterator();
2006
2007            while (transitions.hasNext()) {
2008                InterfaceAutomatonTransition transition = (InterfaceAutomatonTransition) transitions
2009                        .next();
2010                int transitionType = transition.getType();
2011
2012                if (transitionType == InterfaceAutomatonTransition._OUTPUT_TRANSITION
2013                        || transitionType == InterfaceAutomatonTransition._INTERNAL_TRANSITION) {
2014                    State sourceState = transition.sourceState();
2015
2016                    if (!_illegalStates.contains(sourceState)) {
2017                        _illegalStates.add(sourceState);
2018                        frontier.add(sourceState);
2019                    }
2020                }
2021            }
2022        }
2023
2024        // remove all illegalStates from automaton
2025        iterator = _illegalStates.iterator();
2026
2027        while (iterator.hasNext()) {
2028            State state = (State) iterator.next();
2029            _removeStateAndTransitions(state);
2030        }
2031    }
2032
2033    // remove the specified state and transitions linked to it.
2034    private void _removeStateAndTransitions(State state) {
2035        try {
2036            // remove incoming transitions
2037            ComponentPort inPort = state.incomingPort;
2038            Iterator transitions = inPort.linkedRelationList().iterator();
2039
2040            while (transitions.hasNext()) {
2041                ComponentRelation transition = (ComponentRelation) transitions
2042                        .next();
2043                transition.setContainer(null);
2044            }
2045
2046            // remove outgoing transitions
2047            ComponentPort outPort = state.outgoingPort;
2048            transitions = outPort.linkedRelationList().iterator();
2049
2050            while (transitions.hasNext()) {
2051                ComponentRelation transition = (ComponentRelation) transitions
2052                        .next();
2053                transition.setContainer(null);
2054            }
2055
2056            // remove the state
2057            state.setContainer(null);
2058        } catch (IllegalActionException exception) {
2059            // Should not happen since the argument for setContainer() is null.
2060            throw new InternalErrorException(
2061                    "InterfaceAutomaton._removeStateAndTransitions: "
2062                            + "IllegalActionException thrown when calling "
2063                            + "setContainer() with null argument: "
2064                            + exception.getMessage());
2065        } catch (NameDuplicationException exception) {
2066            // Should not happen since the argument for setContainer() is null.
2067            throw new InternalErrorException(
2068                    "InterfaceAutomaton._removeStateAndTransitions: "
2069                            + "NameDuplicationException thrown when calling "
2070                            + "setContainer() with null argument: "
2071                            + exception.getMessage());
2072        }
2073    }
2074
2075    // Remove states unreacheable from the initial state. Also remove the
2076    // transition from and to these states. Note that these states may not
2077    // be disconnected from the initial state. For example, these states
2078    // may have transitions to the initial state, but the initial state does
2079    // not have transitions to these states.
2080    // Use frontier exploration. The Set frontier stores the frontier states,
2081    // and the Set reacheableStates stores all reacheable states. frontier
2082    // is always a subset of reacheableStates.
2083    //
2084    // init: if initial state does not exist (it was illegal and removed)
2085    //           remove all states
2086    //       else
2087    //           reacheableStates = frontier = initial state
2088    //
2089    // iterate: Pick (remove) a state p from the frontier
2090    //          for all states s reacheable from p
2091    //              if s is not in reacheableStates
2092    //                  add s to both reacheableStates and frontier
2093    //
2094    //          end when frontier is empty
2095    //
2096    // remove all states not in reacheableStates from this automaton
2097    private void _removeUnreacheableStates() {
2098        // init
2099        State initialState;
2100
2101        try {
2102            initialState = getInitialState();
2103        } catch (IllegalActionException exception) {
2104            // initial state was removed since it was illegal. remove all
2105            // states from this automaton to make it empty.
2106            this.removeAllRelations();
2107            this.removeAllEntities();
2108            return;
2109        }
2110
2111        Set reacheableStates = new HashSet();
2112        Set frontier = new HashSet();
2113        reacheableStates.add(initialState);
2114        frontier.add(initialState);
2115
2116        // iterate
2117        while (!frontier.isEmpty()) {
2118            // there does not seem to be an easy way to remove an arbitrary
2119            // element, except through Iterator
2120            Iterator iterator = frontier.iterator();
2121            State current = (State) iterator.next();
2122            frontier.remove(current);
2123
2124            // make all states that are reacheable from current reacheable
2125            ComponentPort outPort = current.outgoingPort;
2126            Iterator transitions = outPort.linkedRelationList().iterator();
2127
2128            while (transitions.hasNext()) {
2129                InterfaceAutomatonTransition transition = (InterfaceAutomatonTransition) transitions
2130                        .next();
2131                State destinationState = transition.destinationState();
2132
2133                if (!reacheableStates.contains(destinationState)) {
2134                    reacheableStates.add(destinationState);
2135                    frontier.add(destinationState);
2136                }
2137            }
2138        }
2139
2140        // remove all states not reacheable from initial state
2141        List states = entityList();
2142
2143        // the method removeAll() is not supported by this List (it throws
2144        // UnsupportedOperationException), so manually construct the set
2145        // of unreacheable states.
2146        // states.removeAll(reacheableStates);
2147        Set unreacheableStates = new HashSet();
2148        Iterator iterator = states.iterator();
2149
2150        while (iterator.hasNext()) {
2151            Object state = iterator.next();
2152
2153            if (!reacheableStates.contains(state)) {
2154                unreacheableStates.add(state);
2155            }
2156        }
2157
2158        iterator = unreacheableStates.iterator();
2159
2160        while (iterator.hasNext()) {
2161            State state = (State) iterator.next();
2162            _removeStateAndTransitions(state);
2163        }
2164    }
2165
2166    // Return all the transitions from the specified state with the specified
2167    // type.
2168    private Set _transitionLabelsFrom(State state, int transitionType) {
2169        Set labels = new HashSet();
2170        ComponentPort outPort = state.outgoingPort;
2171        Iterator transitions = outPort.linkedRelationList().iterator();
2172
2173        while (transitions.hasNext()) {
2174            InterfaceAutomatonTransition transition = (InterfaceAutomatonTransition) transitions
2175                    .next();
2176            int type = transition.getType();
2177
2178            if (type == transitionType) {
2179                String label = transition.getLabel();
2180                labels.add(label);
2181            }
2182        }
2183
2184        return labels;
2185    }
2186
2187    ///////////////////////////////////////////////////////////////////
2188    ////                         private variables                 ////
2189    // The state name in the composition automaton is formed by
2190    // <nameInThisAutomaton><NAME_CONNECTOR><nameInArgumentAutomaton>
2191    // The transition name prefix in the composition is formed by
2192    // (1) <nameOfAutomaton> for non-shared transitions;
2193    // (2) <nameOfAutomaton1><NAME_CONNECTOR><nameOfAutomaton2> for shared
2194    //     transitions.
2195    // The name of the product automaton is
2196    // <thisName><NAME_CONNECTOR><argumentName>
2197    private final static String NAME_CONNECTOR = "_";
2198
2199    // The following variables are used to store intermediate results
2200    // during the computation of compose().
2201    // Names of the transitions in the composition. Constructed by
2202    // _computeTransitionNamesInComposition().
2203    private Set _inputNames;
2204
2205    private Set _outputNames;
2206
2207    private Set _internalNames;
2208
2209    // Set of illegal states in the product automaton. The elements of
2210    // the Set are references to states. Constructed by _computeProduct().
2211    private Set _illegalStates;
2212
2213    ///////////////////////////////////////////////////////////////////
2214    ////                         inner class                       ////
2215    private static class Triple {
2216        private Triple(State stateInProduct, State stateInThis,
2217                State stateInArgument) {
2218            _stateInProduct = stateInProduct;
2219            _stateInThis = stateInThis;
2220            _stateInArgument = stateInArgument;
2221        }
2222
2223        private State _stateInProduct = null;
2224
2225        private State _stateInThis = null;
2226
2227        private State _stateInArgument = null;
2228    }
2229}