001/* A state in an FSMActor. 002 003 Copyright (c) 1999-2018 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 */ 027package ptolemy.domains.modal.kernel; 028 029import java.io.IOException; 030import java.io.StringReader; 031import java.io.Writer; 032import java.net.URL; 033import java.util.Iterator; 034import java.util.LinkedList; 035import java.util.List; 036import java.util.StringTokenizer; 037 038import ptolemy.actor.Actor; 039import ptolemy.actor.DesignPatternGetMoMLAction; 040import ptolemy.actor.TypedActor; 041import ptolemy.actor.TypedCompositeActor; 042import ptolemy.data.BooleanToken; 043import ptolemy.data.expr.Parameter; 044import ptolemy.data.expr.SingletonParameter; 045import ptolemy.data.type.BaseType; 046import ptolemy.domains.modal.modal.ModalModel; 047import ptolemy.domains.modal.modal.ModalRefinement; 048import ptolemy.kernel.ComponentEntity; 049import ptolemy.kernel.ComponentPort; 050import ptolemy.kernel.CompositeEntity; 051import ptolemy.kernel.Entity; 052import ptolemy.kernel.Port; 053import ptolemy.kernel.Relation; 054import ptolemy.kernel.util.Attribute; 055import ptolemy.kernel.util.ChangeRequest; 056import ptolemy.kernel.util.DropTargetHandler; 057import ptolemy.kernel.util.Flowable; 058import ptolemy.kernel.util.IllegalActionException; 059import ptolemy.kernel.util.InternalErrorException; 060import ptolemy.kernel.util.NameDuplicationException; 061import ptolemy.kernel.util.Nameable; 062import ptolemy.kernel.util.NamedObj; 063import ptolemy.kernel.util.Settable; 064import ptolemy.kernel.util.SingletonAttribute; 065import ptolemy.kernel.util.StringAttribute; 066import ptolemy.kernel.util.Workspace; 067import ptolemy.moml.MoMLChangeRequest; 068import ptolemy.moml.MoMLParser; 069 070/////////////////////////////////////////////////////////////////// 071//// State 072 073/** 074 A State has two ports: one for linking incoming transitions, the other for 075 outgoing transitions. When the FSMActor containing a state is the mode 076 controller of a modal model, the state can be refined by one or more 077 instances of TypedActor. The refinements must have the same container 078 as the FSMActor. During execution of a modal model, only the mode 079 controller and the refinements of the current state of the mode 080 controller react to input to the modal model and produce 081 output. The outgoing transitions from a state are either preemptive or 082 non-preemptive. When a modal model is fired, if a preemptive transition 083 from the current state of the mode controller is chosen, the refinements of 084 the current state are not fired. Otherwise the refinements are fired before 085 choosing a non-preemptive transition. 086 087 @author Xiaojun Liu 088 @version $Id$ 089 @since Ptolemy II 8.0 090 @Pt.ProposedRating Yellow (liuxj) 091 @Pt.AcceptedRating Yellow (kienhuis) 092 @see Transition 093 @see FSMActor 094 @see FSMDirector 095 */ 096public class State extends ComponentEntity 097 implements ConfigurableEntity, DropTargetHandler, Flowable { 098 099 /** Construct a state with the given name contained by the specified 100 * composite entity. The container argument must not be null, or a 101 * NullPointerException will be thrown. This state will use the 102 * workspace of the container for synchronization and version counts. 103 * If the name argument is null, then the name is set to the empty 104 * string. 105 * Increment the version of the workspace. 106 * This constructor write-synchronizes on the workspace. 107 * @param container The container. 108 * @param name The name of the state. 109 * @exception IllegalActionException If the state cannot be contained 110 * by the proposed container. 111 * @exception NameDuplicationException If the name coincides with 112 * that of an entity already in the container. 113 */ 114 public State(CompositeEntity container, String name) 115 throws IllegalActionException, NameDuplicationException { 116 super(container, name); 117 incomingPort = new ComponentPort(this, "incomingPort"); 118 outgoingPort = new ComponentPort(this, "outgoingPort"); 119 refinementName = new StringAttribute(this, "refinementName"); 120 121 _attachText("_iconDescription", "<svg>\n" 122 + "<circle cx=\"0\" cy=\"0\" r=\"20\" style=\"fill:white\"/>\n" 123 + "</svg>\n"); 124 125 // Specify that the name should be centered in graphical displays. 126 SingletonParameter center = new SingletonParameter(this, "_centerName"); 127 center.setExpression("true"); 128 center.setVisibility(Settable.EXPERT); 129 130 isInitialState = new Parameter(this, "isInitialState"); 131 isInitialState.setTypeEquals(BaseType.BOOLEAN); 132 isInitialState.setExpression("false"); 133 // If this is the only state in the container, then make 134 // it the initial state. For backward compatibility, 135 // we do not do this if the container has a non-empty 136 // value for initialStateName. In that case, we 137 // make this the initial state if its name matches that 138 // name. 139 String initialStateName = ""; 140 if (container instanceof FSMActor) { 141 initialStateName = ((FSMActor) container).initialStateName 142 .getExpression().trim(); 143 // If the container is an FSMActor, and this is the only 144 // state in it, then make the state an initial state. 145 // Also, for backward compatibility, if the container 146 // has an initialStateName value, and that value matches 147 // the name of this state, set the isInitialState parameter. 148 if (initialStateName.equals("")) { 149 if (container.entityList(State.class).size() == 1) { 150 isInitialState.setExpression("true"); 151 // Have to force this to export to MoML, since 152 // the true value will otherwise be seen as the default. 153 isInitialState.setPersistent(true); 154 } 155 } else { 156 // Backward compatibility scenario. The initial state 157 // was given by a name in the container. 158 if (initialStateName.equals(name)) { 159 isInitialState.setExpression("true"); 160 // Have to force this to export to MoML, since 161 // the true value will otherwise be seen as the default. 162 isInitialState.setPersistent(true); 163 } 164 } 165 } 166 isFinalState = new Parameter(this, "isFinalState"); 167 isFinalState.setTypeEquals(BaseType.BOOLEAN); 168 isFinalState.setExpression("false"); 169 170 saveRefinementsInConfigurer = new Parameter(this, 171 "saveRefinementsInConfigurer"); 172 saveRefinementsInConfigurer.setTypeEquals(BaseType.BOOLEAN); 173 saveRefinementsInConfigurer.setVisibility(Settable.EXPERT); 174 saveRefinementsInConfigurer.setExpression("false"); 175 saveRefinementsInConfigurer.setPersistent(false); 176 177 ContainmentExtender containmentExtender = new ContainmentExtender(this, 178 "_containmentExtender"); 179 containmentExtender.setPersistent(false); 180 181 _configurer = new Configurer(workspace()); 182 } 183 184 /////////////////////////////////////////////////////////////////// 185 //// ports and parameters //// 186 187 /** The port linking incoming transitions. 188 */ 189 public ComponentPort incomingPort = null; 190 191 /** An indicator of whether this state is a final state. 192 * This is a boolean that defaults to false. Setting it to true 193 * will cause the containing FSMActor to return false from its 194 * postfire() method, which indicates to the director that the 195 * FSMActor should not be fired again. 196 */ 197 public Parameter isFinalState; 198 199 /** An indicator of whether this state is the initial state. 200 * This is a boolean that defaults to false, unless this state 201 * is the only one in the container, in which case it defaults 202 * to true. Setting it to true 203 * will cause this parameter to become false for whatever 204 * other state is currently the initial state in the same 205 * container. 206 */ 207 public Parameter isInitialState; 208 209 /** The port linking outgoing transitions. 210 */ 211 public ComponentPort outgoingPort = null; 212 213 /** Attribute specifying one or more names of refinements. The 214 * refinements must be instances of TypedActor and have the same 215 * container as the FSMActor containing this state, otherwise 216 * an exception will be thrown when getRefinement() is called. 217 * Usually, the refinement is a single name. However, if a 218 * comma-separated list of names is provided, then all the specified 219 * refinements will be executed. 220 * This attribute has a null expression or a null string as 221 * expression when the state is not refined. 222 */ 223 public StringAttribute refinementName = null; 224 225 /** A boolean attribute to decide refinements of this state should be 226 * exported as configurations of this state or not. 227 */ 228 public Parameter saveRefinementsInConfigurer; 229 230 /////////////////////////////////////////////////////////////////// 231 //// public methods //// 232 233 /** React to a change in an attribute. If the changed attribute is 234 * the <i>refinementName</i> attribute, record the change but do 235 * not check whether there is a TypedActor with the specified name 236 * and having the same container as the FSMActor containing this 237 * state. 238 * @param attribute The attribute that changed. 239 * @exception IllegalActionException If thrown by the superclass 240 * attributeChanged() method. 241 */ 242 @Override 243 public void attributeChanged(Attribute attribute) 244 throws IllegalActionException { 245 super.attributeChanged(attribute); 246 247 if (attribute == refinementName) { 248 _refinementVersion = -1; 249 } else if (attribute == isInitialState) { 250 NamedObj container = getContainer(); 251 // Container might not be an FSMActor if, for example, 252 // the state is in a library. 253 if (container instanceof FSMActor) { 254 if (((BooleanToken) isInitialState.getToken()).booleanValue()) { 255 // If there is a previous initial state, unset its 256 // isInitialState parameter. 257 if (((FSMActor) container)._initialState != null 258 && ((FSMActor) container)._initialState != this) { 259 ((FSMActor) container)._initialState.isInitialState 260 .setToken("false"); 261 } 262 ((FSMActor) container)._initialState = this; 263 // If the initial state name of the container is set, 264 // unset it. 265 String name = ((FSMActor) container).initialStateName 266 .getExpression(); 267 if (!name.equals("")) { 268 ((FSMActor) container).initialStateName 269 .setExpression(""); 270 } 271 } 272 } 273 } 274 } 275 276 /** Clone the state into the specified workspace. This calls the 277 * base class and then sets the attribute and port public members 278 * to refer to the attributes and ports of the new state. 279 * @param workspace The workspace for the new state. 280 * @return A new state. 281 * @exception CloneNotSupportedException If a derived class contains 282 * an attribute that cannot be cloned. 283 */ 284 @Override 285 public Object clone(Workspace workspace) throws CloneNotSupportedException { 286 State newObject = (State) super.clone(workspace); 287 newObject.incomingPort = (ComponentPort) newObject 288 .getPort("incomingPort"); 289 newObject.outgoingPort = (ComponentPort) newObject 290 .getPort("outgoingPort"); 291 newObject.refinementName = (StringAttribute) newObject 292 .getAttribute("refinementName"); 293 newObject._configurer = new Configurer(newObject.workspace()); 294 newObject._nonpreemptiveTransitionList = new LinkedList(); 295 newObject._preemptiveTransitionList = new LinkedList(); 296 newObject._refinementVersion = -1; 297 newObject._transitionListVersion = -1; 298 newObject._nonErrorNonTerminationTransitionList = new LinkedList(); 299 return newObject; 300 } 301 302 /** Configure the object with data from the specified input source 303 * (a URL) and/or textual data. The object should interpret the 304 * source first, if it is specified, followed by the literal text, 305 * if that is specified. The new configuration should usually 306 * override any old configuration wherever possible, in order to 307 * ensure that the current state can be successfully retrieved. 308 * <p> 309 * This method is defined to throw a very general exception to allow 310 * classes that implement the interface to use whatever exceptions 311 * are appropriate. 312 * @param base The base relative to which references within the input 313 * are found, or null if this is not known, or there is none. 314 * @param source The input source, which specifies a URL, or null 315 * if none. 316 * @param text Configuration information given as text, or null if 317 * none. 318 * @exception Exception If something goes wrong. 319 */ 320 @Override 321 public void configure(URL base, String source, String text) 322 throws Exception { 323 refinementName.setExpression(""); 324 _configureSource = source; 325 // Coverity: Avoid a call to configure() in MoMLParser 326 // throwing a NPE if text is null. 327 if (text != null) { 328 text = text.trim(); 329 if (!text.equals("")) { 330 MoMLParser parser = new MoMLParser(workspace()); 331 _configurer.removeAllEntities(); 332 parser.setContext(_configurer); 333 parser.parse(base, source, new StringReader(text)); 334 _populateRefinements(); 335 } 336 } 337 } 338 339 /** React to a list of objects being dropped onto a target. 340 * 341 * @param target The target on which the objects are dropped. 342 * @param dropObjects The list of objects dropped onto the target. 343 * @param moml The moml string generated for the dropped objects. 344 * @exception IllegalActionException If the handling is unsuccessful. 345 */ 346 @Override 347 public void dropObject(NamedObj target, List dropObjects, String moml) 348 throws IllegalActionException { 349 NamedObj container = getContainer(); 350 if (container instanceof DropTargetHandler) { 351 ((DropTargetHandler) container).dropObject(target, dropObjects, 352 moml); 353 } 354 } 355 356 /** Return the list of outgoing error transitions from 357 * this state. 358 * @return The list of outgoing error transitions from 359 * this state. 360 * @exception IllegalActionException If the parameters giving transition 361 * properties cannot be evaluated. 362 */ 363 public List errorTransitionList() throws IllegalActionException { 364 if (_transitionListVersion != workspace().getVersion()) { 365 _updateTransitionLists(); 366 } 367 return _errorTransitionList; 368 } 369 370 /** Return the input source that was specified the last time the configure 371 * method was called. 372 * @return The string representation of the input URL, or null if the 373 * no source has been used to configure this object, or null if no 374 * external source need be used to configure this object. 375 */ 376 @Override 377 public String getConfigureSource() { 378 return _configureSource; 379 } 380 381 /** Return the text string that represents the current configuration of 382 * this object. Note that any configuration that was previously 383 * specified using the source attribute need not be represented here 384 * as well. 385 * @return A configuration string, or null if no configuration 386 * has been used to configure this object, or null if no 387 * configuration string need be used to configure this object. 388 */ 389 @Override 390 public String getConfigureText() { 391 return null; 392 } 393 394 /** Get the {@link Configurer} object for this entity. 395 */ 396 @Override 397 public Configurer getConfigurer() { 398 return _configurer; 399 } 400 401 /** Return the incoming port. 402 * @return The incoming port. 403 */ 404 @Override 405 public ComponentPort getIncomingPort() { 406 return incomingPort; 407 } 408 409 /** Get a NamedObj with the given name in the refinement of this state, if 410 * any. 411 * 412 * @param name The name of the NamedObj. 413 * @return The NamedObj in the refinement, or null if not found. 414 * @exception IllegalActionException If the refinement cannot be found, or 415 * if a comma-separated list is malformed. 416 */ 417 public NamedObj getObjectInRefinement(String name) 418 throws IllegalActionException { 419 TypedActor[] refinements = getRefinement(); 420 if (refinements == null) { 421 return null; 422 } 423 424 for (TypedActor refinement : refinements) { 425 if (refinement instanceof NamedObj) { 426 Attribute attribute = ((NamedObj) refinement) 427 .getAttribute(name); 428 if (attribute != null) { 429 return attribute; 430 } else if (refinement instanceof Entity) { 431 Port port = ((Entity) refinement).getPort(name); 432 if (port != null) { 433 return port; 434 } else if (refinement instanceof CompositeEntity) { 435 ComponentEntity entity = ((CompositeEntity) refinement) 436 .getEntity(name); 437 if (entity != null) { 438 return entity; 439 } 440 Relation relation = ((CompositeEntity) refinement) 441 .getRelation(name); 442 if (relation != null) { 443 return relation; 444 } 445 } 446 } 447 } 448 } 449 return null; 450 } 451 452 /** Return the outgoing port. 453 * @return The outgoing port. 454 */ 455 @Override 456 public ComponentPort getOutgoingPort() { 457 return outgoingPort; 458 } 459 460 /** Return the refinements of this state. The names of the refinements 461 * are specified by the <i>refinementName</i> attribute. The refinements 462 * must be instances of TypedActor and have the same container as 463 * the FSMActor containing this state, otherwise an exception is thrown. 464 * This method can also return null if there is no refinement. 465 * This method is read-synchronized on the workspace. 466 * @return The refinements of this state, or null if there are none. 467 * @exception IllegalActionException If the specified refinement 468 * cannot be found, or if a comma-separated list is malformed. 469 */ 470 public TypedActor[] getRefinement() throws IllegalActionException { 471 if (_refinementVersion == workspace().getVersion()) { 472 return _refinement; 473 } 474 475 try { 476 workspace().getReadAccess(); 477 478 String names = refinementName.getExpression(); 479 480 if (names == null || names.trim().equals("")) { 481 _refinementVersion = workspace().getVersion(); 482 _refinement = null; 483 return null; 484 } 485 486 StringTokenizer tokenizer = new StringTokenizer(names, ","); 487 int size = tokenizer.countTokens(); 488 489 if (size <= 0) { 490 _refinementVersion = workspace().getVersion(); 491 _refinement = null; 492 return null; 493 } 494 495 _refinement = new TypedActor[size]; 496 497 Nameable container = getContainer(); 498 TypedCompositeActor containerContainer = (TypedCompositeActor) container 499 .getContainer(); 500 int index = 0; 501 502 while (tokenizer.hasMoreTokens()) { 503 String name = tokenizer.nextToken().trim(); 504 505 if (name.equals("")) { 506 throw new IllegalActionException(this, 507 "Malformed list of refinements: " + names); 508 } 509 510 if (containerContainer == null) { 511 // If we are doing saveAs of ModalBSC and select 512 // submodel only, then some of the refinements might 513 // not yet have a container (containercontainer == null). 514 // ptolemy.vergil.modal.StateIcon._getFill() will call 515 // this and properly handles an IllegalActionException 516 throw new IllegalActionException(this, "Container of \"" 517 + getFullName() 518 + "\" is null? This is not always a problem."); 519 } 520 521 TypedActor element = (TypedActor) containerContainer 522 .getEntity(name); 523 524 if (element == null) { 525 throw new IllegalActionException(this, 526 "Cannot find " + "refinement with name \"" + name 527 + "\" in " 528 + containerContainer.getFullName()); 529 } 530 531 _refinement[index++] = element; 532 } 533 534 _refinementVersion = workspace().getVersion(); 535 return _refinement; 536 } finally { 537 workspace().doneReading(); 538 } 539 } 540 541 /** Return the list of outgoing transitions from 542 * this state that are neither error nor termination transitions. 543 * @return A list of outgoing transitions from this state. 544 * @exception IllegalActionException If the parameters giving transition 545 * properties cannot be evaluated. 546 */ 547 public List nonErrorNonTerminationTransitionList() 548 throws IllegalActionException { 549 if (_transitionListVersion != workspace().getVersion()) { 550 _updateTransitionLists(); 551 } 552 return _nonErrorNonTerminationTransitionList; 553 } 554 555 /** Return the list of non-preemptive outgoing transitions from 556 * this state. This list does not include error transitions 557 * and does include termination transitions. 558 * @return The list of non-preemptive outgoing transitions from 559 * this state. 560 * @exception IllegalActionException If the parameters giving transition 561 * properties cannot be evaluated. 562 */ 563 public List nonpreemptiveTransitionList() throws IllegalActionException { 564 if (_transitionListVersion != workspace().getVersion()) { 565 _updateTransitionLists(); 566 } 567 568 return _nonpreemptiveTransitionList; 569 } 570 571 /** Return the list of preemptive outgoing transitions from 572 * this state. 573 * @return The list of preemptive outgoing transitions from 574 * this state. This will be an empty list if there aren't any. 575 * @exception IllegalActionException If the parameters giving transition 576 * properties cannot be evaluated. 577 */ 578 public List preemptiveTransitionList() throws IllegalActionException { 579 if (_transitionListVersion != workspace().getVersion()) { 580 _updateTransitionLists(); 581 } 582 return _preemptiveTransitionList; 583 } 584 585 /** Return the list of termination transitions from 586 * this state. 587 * @return The list of termination transitions from 588 * this state. This will be an empty list if there aren't any. 589 * @exception IllegalActionException If the parameters giving transition 590 * properties cannot be evaluated. 591 */ 592 public List terminationTransitionList() throws IllegalActionException { 593 if (_transitionListVersion != workspace().getVersion()) { 594 _updateTransitionLists(); 595 } 596 return _terminationTransitionList; 597 } 598 599 /////////////////////////////////////////////////////////////////// 600 //// protected methods //// 601 602 /** Write a MoML description of the contents of this object, which 603 * in this class are the attributes plus the ports. This method is called 604 * by exportMoML(). Each description is indented according to the 605 * specified depth and terminated with a newline character. 606 * @param output The output to write to. 607 * @param depth The depth in the hierarchy, to determine indenting. 608 * @exception IOException If an I/O error occurs. 609 */ 610 @Override 611 protected void _exportMoMLContents(Writer output, int depth) 612 throws IOException { 613 super._exportMoMLContents(output, depth); 614 boolean createConfigurer = false; 615 try { 616 createConfigurer = ((BooleanToken) saveRefinementsInConfigurer 617 .getToken()).booleanValue(); 618 } catch (IllegalActionException e) { 619 // Ignore. Use false. 620 } 621 boolean configurePrinted = false; 622 if (createConfigurer) { 623 try { 624 TypedActor[] actors = getRefinement(); 625 if (actors != null) { 626 for (TypedActor actor : actors) { 627 if (!configurePrinted) { 628 output.write( 629 _getIndentPrefix(depth) + "<configure>\n"); 630 configurePrinted = true; 631 } 632 if (actor instanceof FSMActor) { 633 ((FSMActor) actor).exportSubmodel(output, depth + 1, 634 actor.getName()); 635 } else { 636 ((NamedObj) actor).exportMoML(output, depth + 1); 637 } 638 } 639 } 640 } catch (IllegalActionException e) { 641 throw new InternalErrorException(this, e, 642 "Unable to save refinements."); 643 } 644 } 645 List<ComponentEntity> actors = _configurer.entityList(); 646 for (ComponentEntity actor : actors) { 647 if (!configurePrinted) { 648 output.write(_getIndentPrefix(depth) + "<configure>\n"); 649 configurePrinted = true; 650 } 651 if (actor instanceof FSMActor) { 652 ((FSMActor) actor).exportSubmodel(output, depth + 1, 653 actor.getName()); 654 } else { 655 ((NamedObj) actor).exportMoML(output, depth + 1); 656 } 657 } 658 if (configurePrinted) { 659 output.write(_getIndentPrefix(depth) + "</configure>\n"); 660 } 661 } 662 663 /////////////////////////////////////////////////////////////////// 664 //// private methods //// 665 666 /** Move the refinements in the configurer of this state to the closest 667 * modal model above this state in the model hierarchy. 668 */ 669 private void _populateRefinements() throws IllegalActionException { 670 CompositeEntity container = (CompositeEntity) getContainer(); 671 CompositeEntity modalModel = (CompositeEntity) container.getContainer(); 672 boolean isModalModelInvisible = modalModel != null && !modalModel 673 .attributeList(InvisibleModalModel.class).isEmpty(); 674 if (!(modalModel instanceof TypedCompositeActor) 675 || isModalModelInvisible) { 676 if (modalModel == null || isModalModelInvisible) { 677 try { 678 if (modalModel == null) { 679 modalModel = new ModalModel(workspace()); 680 new InvisibleModalModel(modalModel, 681 modalModel.uniqueName("_invisibleModalModel")); 682 container.setContainer(modalModel); 683 } 684 } catch (NameDuplicationException e) { 685 // This should not happen. 686 } 687 saveRefinementsInConfigurer.setToken(BooleanToken.TRUE); 688 } else { 689 return; 690 } 691 } 692 List<ComponentEntity> entities = new LinkedList<ComponentEntity>( 693 _configurer.entityList()); 694 if (container instanceof RefinementActor) { 695 RefinementActor actor = (RefinementActor) container; 696 for (ComponentEntity entity : entities) { 697 String oldName = entity.getName(); 698 String newName = modalModel.uniqueName(oldName); 699 700 String refinements = refinementName.getExpression(); 701 String[] names = refinements.split("\\s*,\\s*"); 702 boolean changed = false; 703 StringBuffer newRefinements = new StringBuffer(); 704 for (String part : names) { 705 if (newRefinements.length() > 0) { 706 newRefinements.append(", "); 707 } 708 if (part.equals(oldName)) { 709 changed = true; 710 } else { 711 newRefinements.append(part); 712 } 713 } 714 if (changed) { 715 refinementName.setExpression(newRefinements.toString()); 716 } 717 718 actor.addRefinement(this, newName, null, entity.getClassName(), 719 null); 720 721 String moml = new DesignPatternGetMoMLAction().getMoml(entity, 722 newName); 723 try { 724 entity.setContainer(null); 725 } catch (NameDuplicationException e) { 726 // Ignore. 727 } 728 729 UpdateContentsRequest request = new UpdateContentsRequest(this, 730 modalModel, newName, moml); 731 modalModel.requestChange(request); 732 } 733 } 734 } 735 736 /** Update the cached transition lists. This method is read-synchronized on 737 * the workspace. 738 * @exception IllegalActionException If the parameters giving transition 739 * properties cannot be evaluated. 740 */ 741 private void _updateTransitionLists() throws IllegalActionException { 742 try { 743 workspace().getReadAccess(); 744 _nonpreemptiveTransitionList.clear(); 745 _preemptiveTransitionList.clear(); 746 _errorTransitionList.clear(); 747 _terminationTransitionList.clear(); 748 _nonErrorNonTerminationTransitionList.clear(); 749 750 // If this state is final, it should not have any outgoing 751 // transitions. 752 if (((BooleanToken) isFinalState.getToken()).booleanValue()) { 753 if (outgoingPort.linkedRelationList().size() > 0) { 754 throw new IllegalActionException(this, 755 "Final state cannot have outgoing transitions"); 756 } 757 } 758 759 Iterator transitions = outgoingPort.linkedRelationList().iterator(); 760 761 while (transitions.hasNext()) { 762 Transition transition = (Transition) transitions.next(); 763 764 if (transition.isErrorTransition()) { 765 // An error transition is required to not be preemptive 766 // or termination. Check that here. 767 if (transition.isPreemptive()) { 768 throw new IllegalActionException(transition, 769 "An error transition cannot also be preemptive."); 770 } 771 if (transition.isTermination()) { 772 throw new IllegalActionException(transition, 773 "An error transition cannot also be a termination transition."); 774 } 775 _errorTransitionList.add(transition); 776 } else if (transition.isPreemptive()) { 777 // A preemptive transition is not allowed to be a termination transition. 778 if (transition.isTermination()) { 779 throw new IllegalActionException(transition, 780 "A preemptive transition cannot also be a termination transition."); 781 } 782 _preemptiveTransitionList.add(transition); 783 _nonErrorNonTerminationTransitionList.add(transition); 784 } else if (transition.isTermination()) { 785 // Termination transitions are allowed to have output actions only 786 // all refinements of this state are state machine refinements. 787 TypedActor[] refinements = getRefinement(); 788 if (refinements == null || refinements.length == 0) { 789 throw new IllegalActionException(transition, 790 "Termination transitions must come from states with refinements"); 791 } 792 // There are refinements. 793 // Check that if there are output actions on the termination transition 794 // then all refinements are FSM refinements. This is because non-FSM 795 // refinements can only be known to have terminated in postfire, and that 796 // is too late to produce outputs for some domains (SR and Continuous, at least). 797 if (!transition.outputActions.getExpression().trim() 798 .equals("")) { 799 for (Actor refinementActor : refinements) { 800 if (!(refinementActor instanceof ModalRefinement)) { 801 throw new IllegalActionException(transition, 802 "Termination transition cannot have output actions because " 803 + "such a transition is taken in the postfire phase of execution."); 804 } 805 } 806 } 807 // Note that a transition does not appear on this list unless it is 808 // NOT a preemptive or error transition. 809 _terminationTransitionList.add(transition); 810 _nonpreemptiveTransitionList.add(transition); 811 } else { 812 _nonpreemptiveTransitionList.add(transition); 813 _nonErrorNonTerminationTransitionList.add(transition); 814 } 815 } 816 817 _transitionListVersion = workspace().getVersion(); 818 } finally { 819 workspace().doneReading(); 820 } 821 } 822 823 // The source of the configuration, which is not used. 824 private String _configureSource; 825 826 // The Configurer object for this state. 827 private Configurer _configurer; 828 829 // Cached list of error transitions from this state 830 private List _errorTransitionList = new LinkedList(); 831 832 // Cached list of outgoing transitions from this state that are 833 // neither error nor termination transitions. 834 private List _nonErrorNonTerminationTransitionList = new LinkedList(); 835 836 // Cached list of non-preemptive outgoing transitions from this state. 837 private List _nonpreemptiveTransitionList = new LinkedList(); 838 839 // Cached list of preemptive outgoing transitions from this state. 840 private List _preemptiveTransitionList = new LinkedList(); 841 842 // Cached reference to the refinement of this state. 843 private TypedActor[] _refinement = null; 844 845 // Version of the cached reference to the refinement. 846 private long _refinementVersion = -1; 847 848 // Cached list of termination transitions from this state. 849 private List _terminationTransitionList = new LinkedList(); 850 851 // Version of cached transition lists. 852 private long _transitionListVersion = -1; 853 854 /////////////////////////////////////////////////////////////////// 855 //// InvisibleModalModel 856 857 /** 858 An attribute that marks a modal model is created because the designer opens 859 a design pattern whose top-level is an FSMActor. In that case, a modal 860 model is automatically created to contain the FSMActor, and this attribute 861 is associated with the created (invisible) modal model. 862 863 @author Thomas Huining Feng 864 @version $Id$ 865 @since Ptolemy II 8.0 866 @Pt.ProposedRating Red (tfeng) 867 @Pt.AcceptedRating Red (tfeng) 868 */ 869 private static class InvisibleModalModel extends SingletonAttribute { 870 871 /** Construct an attribute with the given container and name. 872 * If an attribute already exists with the same name as the one 873 * specified here, that is an instance of class 874 * SingletonAttribute (or a derived class), then that 875 * attribute is removed before this one is inserted in the container. 876 * @param container The container. 877 * @param name The name of this attribute. 878 * @exception IllegalActionException If the attribute cannot be contained 879 * by the proposed container. 880 * @exception NameDuplicationException If the container already has an 881 * attribute with this name, and the class of that container is not 882 * SingletonAttribute. 883 */ 884 InvisibleModalModel(CompositeEntity container, String name) 885 throws NameDuplicationException, IllegalActionException { 886 super(container, name); 887 } 888 } 889 890 /////////////////////////////////////////////////////////////////// 891 //// UpdateContentsRequest 892 893 /** 894 A change request the updates the refinements of a state if it contains a 895 configure element. 896 897 @author Thomas Huining Feng 898 @version $Id$ 899 @since Ptolemy II 8.0 900 @Pt.ProposedRating Red (tfeng) 901 @Pt.AcceptedRating Red (tfeng) 902 */ 903 private static class UpdateContentsRequest extends ChangeRequest { 904 905 /** Construct a request. 906 * 907 * @param source The state that originates the request. 908 * @param modalModel The closest modal model that the source state is 909 * contained in. 910 * @param name The name of the refinement. 911 * @param moml The moml of the refinement. 912 */ 913 public UpdateContentsRequest(State source, CompositeEntity modalModel, 914 String name, String moml) { 915 super(source, "Update contents of refinement " + name + "."); 916 _modalModel = modalModel; 917 _name = name; 918 _moml = moml; 919 } 920 921 /** Execute the change. 922 * @exception Exception If the change fails. 923 */ 924 @Override 925 protected void _execute() throws Exception { 926 ComponentEntity entity = _modalModel.getEntity(_name); 927 MoMLChangeRequest request = new MoMLChangeRequest(this, entity, 928 _moml); 929 request.execute(); 930 } 931 932 // The name of the refinement. 933 private String _name; 934 935 // The closest modal model that the source state is ontained in. 936 private CompositeEntity _modalModel; 937 938 // The moml of the refinement. 939 private String _moml; 940 } 941}