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 * <incomingLabel><NAME_CONNECTOR><outgoingLabel>. 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}