001/* An actor that iterates a contained actor over input arrays. 002 003 Copyright (c) 2004-2015 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 027 */ 028package ptolemy.actor.lib.hoc; 029 030import java.io.Writer; 031import java.util.ArrayList; 032import java.util.HashSet; 033import java.util.Iterator; 034import java.util.LinkedList; 035import java.util.List; 036import java.util.Set; 037 038import ptolemy.actor.Actor; 039import ptolemy.actor.CompositeActor; 040import ptolemy.actor.Director; 041import ptolemy.actor.Executable; 042import ptolemy.actor.FiringEvent; 043import ptolemy.actor.IOPort; 044import ptolemy.actor.IOPortEvent; 045import ptolemy.actor.NoRoomException; 046import ptolemy.actor.NoTokenException; 047import ptolemy.actor.QueueReceiver; 048import ptolemy.actor.Receiver; 049import ptolemy.actor.TypedCompositeActor; 050import ptolemy.actor.TypedIOPort; 051import ptolemy.actor.util.ArrayElementTypeFunction; 052import ptolemy.actor.util.GLBFunction; 053import ptolemy.data.ArrayToken; 054import ptolemy.data.IntToken; 055import ptolemy.data.Token; 056import ptolemy.data.expr.Variable; 057import ptolemy.data.type.ArrayType; 058import ptolemy.data.type.BaseType; 059import ptolemy.data.type.Type; 060import ptolemy.data.type.TypeLattice; 061import ptolemy.graph.CPO; 062import ptolemy.graph.Inequality; 063import ptolemy.graph.InequalityTerm; 064import ptolemy.kernel.ComponentEntity; 065import ptolemy.kernel.CompositeEntity; 066import ptolemy.kernel.Port; 067import ptolemy.kernel.util.IllegalActionException; 068import ptolemy.kernel.util.InternalErrorException; 069import ptolemy.kernel.util.NameDuplicationException; 070import ptolemy.kernel.util.Nameable; 071import ptolemy.kernel.util.Workspace; 072 073/////////////////////////////////////////////////////////////////// 074//// IterateOverArray 075 076/** 077 This actor iterates the contained actor or model over input arrays. 078 To use it, either drop an actor on it and provide arrays to the inputs, 079 or use a default configuration where the actor is effectively a 080 composite actor. In the latter case, 081 you can simply look inside and 082 populate that actor with a submodel that will be applied to the 083 array elements. The submodel is required to have a director. 084 An SDF director will 085 often be sufficient for operations taken on array elements, 086 but other directors can be used as well. 087 Note that this inside director should not impose a limit 088 on the number of iterations of the inside model. If it does, 089 then that limit will be respected, which may result in a failure 090 to iterate over all the input data. 091 <p> 092 Each input port expects an array. When this actor fires, 093 an array is read on each input port that has one, and its 094 contents are provided sequentially to the contained actor or model. 095 This actor then iterates the contained actor or model until either 096 there are no more input data for the actor or the prefire() 097 method of the actor or model 098 returns false. If postfire() of the actor returns false, 099 then postfire() of this actor will return false, requesting 100 a halt to execution of the model. The outputs from the 101 contained actor are collected into arrays that are 102 produced on the outputs of this actor.</p> 103 <p> 104 A special variable named "iterationCount" can be used in 105 any expression setting the value of a parameter of this actor 106 or its contents. This variable has an integer value that 107 starts at 1 during the first iteration of the contained 108 actor(s) and is incremented by 1 on each firing. If the 109 inside actors consume one token on each firing, then 110 its final value will be the size of the input array(s).</p> 111 <p> 112 This actor is properly viewed as a "higher-order component" in 113 that its contained actor is a parameter that specifies how to 114 operate on input arrays. It is inspired by the higher-order 115 functions of functional languages, but unlike those, the 116 contained actor need not be functional. That is, it can have 117 state.</p> 118 <p> 119 Note that you cannot place class definitions inside this 120 actor. There should be no need to because class instances 121 inside it can be instances of classes defined outside of it.</p> 122 <p> 123 This actor (and many of the other higher-order components) 124 has its intellectual roots in the higher-order functions 125 of functional languages, which have been in use since 126 the 1970s. Similar actors were implemented in Ptolemy 127 Classic, and are described in Lee & Parks, "Dataflow 128 Process Networks," <i>Proceedings of the IEEE</i>, 1995. 129 Those were inspired by [2]. 130 Alternative approaches are found dataflow visual programming 131 since the beginning (Sutherland in the 1960s, Prograph and 132 Labview in the 1980s), and in time-based visual languages 133 (Simulink in the 1990s).</p> 134 <p> 135 There are a number of known bugs or limitations in this 136 implementation:</p> 137 <ul> 138 <li> FIXME: When you drop in an actor, and then another actor, 139 and then select "undo," the second actor is deleted without 140 the first one being re-created. Thus, undo is only a partial 141 undo. The fix to this is extremely complicated. Probably the 142 only viable mechanism is to use UndoStackAttribute.getUndoInfo() 143 to get the undo stack and then to manipulate the contents 144 of that stack directly.</li> 145 <li> FIXME: There should be an option to reset between 146 firings of the inside actor.</li> 147 <li> FIXME: If you drop a new actor onto an 148 IterateOverArray in a subclass, it will replace the 149 version inherited from the prototype. This is not right, 150 since it violates the derivation invariant. Any attempt 151 to modify the contained object in the prototype will trigger 152 an exception. There are two possible fixes. One is to 153 relax the derivation invariant and allow derived objects 154 to not perfectly mirror the hierarchy of the prototype. 155 Another is for this class to somehow refuse to accept 156 the new object in a subclass. But it is not obvious how 157 to do this.</li> 158 <li> 159 FIXME: If an instance of IterateOverArray in a derived class has 160 overridden values of parameters, those are lost if contained 161 entity of the instance in the base class is replaced and 162 then an undo is requested.</li> 163 </ul> 164 <b>References</b> 165 <ol> 166 <li> E. A. Lee and T. M. Parks, "Dataflow Process Networks," 167 Proceedings of the IEEE, 83(5): 773-801, May, 1995.</li> 168 <li> H. J. Reekie, 169<a href="http://ptolemy.eecs.berkeley.edu/~johnr/papers/thesis.html#in_browser">Realtime Signal Processing: Dataflow, Visual, 170 and Functional Programming</a>," Ph.D. Thesis, 171 University of Technology, Sydney, Sydney, Australia, 1995.</li> 172 </ol> 173 174 @author Edward A. Lee, Steve Neuendorffer 175 @version $Id$ 176 @since Ptolemy II 4.1 177 @Pt.ProposedRating Yellow (eal) 178 @Pt.AcceptedRating Red (neuendor) 179 */ 180public class IterateOverArray extends MirrorComposite { 181 /** Create an actor with a name and a container. 182 * The container argument must not be null, or a 183 * NullPointerException will be thrown. This actor will use the 184 * workspace of the container for synchronization and version counts. 185 * If the name argument is null, then the name is set to the empty string. 186 * Increment the version of the workspace. 187 * This actor will have no 188 * local director initially, and its executive director will be simply 189 * the director of the container. 190 * You should set a director before attempting to execute it. 191 * @param container The container actor. 192 * @param name The name of this actor. 193 * @exception IllegalActionException If the container is incompatible 194 * with this actor. 195 * @exception NameDuplicationException If the name coincides with 196 * an actor already in the container. 197 */ 198 public IterateOverArray(CompositeEntity container, String name) 199 throws IllegalActionException, NameDuplicationException { 200 super(container, name); 201 _init(); 202 } 203 204 /** Construct an IterateOverArray in the specified workspace with 205 * no container and an empty string as a name. You can then change 206 * the name with setName(). If the workspace argument is null, then 207 * use the default workspace. You should set the local director or 208 * executive director before attempting to send data to the actor 209 * or to execute it. Add the actor to the workspace directory. 210 * Increment the version number of the workspace. 211 * @param workspace The workspace that will list the actor. 212 * @exception IllegalActionException If the container is incompatible 213 * with this actor. 214 * @exception NameDuplicationException If the name coincides with 215 * an actor already in the container. 216 */ 217 public IterateOverArray(Workspace workspace) 218 throws IllegalActionException, NameDuplicationException { 219 super(workspace); 220 _init(); 221 } 222 223 /////////////////////////////////////////////////////////////////// 224 //// public methods //// 225 226 /** Clone the object into the specified workspace. This overrides 227 * the base class to instantiate a new IterateDirector and to set 228 * the association with iterationCount. 229 * @param workspace The workspace for the new object. 230 * @return A new NamedObj. 231 * @exception CloneNotSupportedException If any of the attributes 232 * cannot be cloned. 233 * @see #exportMoML(Writer, int, String) 234 */ 235 @Override 236 public Object clone(Workspace workspace) throws CloneNotSupportedException { 237 IterateOverArray result = (IterateOverArray) super.clone(workspace); 238 try { 239 // Remove the old inner IterateDirector(s) that is(are) in the wrong workspace. 240 String iterateDirectorName = null; 241 Iterator iterateDirectors = result 242 .attributeList(IterateDirector.class).iterator(); 243 while (iterateDirectors.hasNext()) { 244 IterateDirector oldIterateDirector = (IterateDirector) iterateDirectors 245 .next(); 246 if (iterateDirectorName == null) { 247 iterateDirectorName = oldIterateDirector.getName(); 248 } 249 oldIterateDirector.setContainer(null); 250 } 251 252 // Create a new IterateDirector that is in the right workspace. 253 IterateDirector iterateDirector = result.new IterateDirector( 254 workspace); 255 iterateDirector.setContainer(result); 256 iterateDirector.setName(iterateDirectorName); 257 } catch (Throwable throwable) { 258 throw new CloneNotSupportedException( 259 "Could not clone: " + throwable); 260 } 261 result._iterationCount = (Variable) result 262 .getAttribute("iterationCount"); 263 return result; 264 } 265 266 /** Override the base class to return a specialized port. 267 * @param name The name of the port to create. 268 * @return A new instance of IteratePort, an inner class. 269 * @exception NameDuplicationException If the container already has a port 270 * with this name. 271 */ 272 @Override 273 public Port newPort(String name) throws NameDuplicationException { 274 try { 275 IteratePort result = new IteratePort(this, name); 276 277 // NOTE: We would like prevent deletion via MoML 278 // (or name changes, for that matter), but the following 279 // also prevents making it an input, which makes 280 // adding ports via the port dialog fail. 281 // result.setDerivedLevel(1); 282 // Force the port to be persistent despite being derived. 283 // result.setPersistent(true); 284 return result; 285 } catch (IllegalActionException ex) { 286 // This exception should not occur, so we throw a runtime 287 // exception. 288 throw new InternalErrorException(this, ex, null); 289 } 290 } 291 292 /** Override the base class to ensure that the input ports of this 293 * actor all have array types. 294 * @return A list of instances of Inequality. 295 * @exception IllegalActionException If the typeConstraints 296 * of one of the deeply contained objects throws it. 297 * @see ptolemy.graph.Inequality 298 */ 299 @Override 300 public Set<Inequality> typeConstraints() throws IllegalActionException { 301 Iterator ports = inputPortList().iterator(); 302 303 while (ports.hasNext()) { 304 TypedIOPort port = (TypedIOPort) ports.next(); 305 port.setTypeAtLeast(ArrayType.ARRAY_BOTTOM); 306 // With backward type resolution, we also need the following 307 // to ensure that the type does not resolve to GENERAL. 308 port.setTypeAtMost(new ArrayType(BaseType.GENERAL)); 309 } 310 311 return super.typeConstraints(); 312 } 313 314 /////////////////////////////////////////////////////////////////// 315 //// protected methods //// 316 317 /** Check types from a source port to a group of destination ports, 318 * assuming the source port is connected to all the ports in the 319 * group of destination ports. Return a list of instances of 320 * Inequality that have type conflicts. This overrides the base 321 * class so that if one of the ports belongs to this IterateOverArray 322 * actor, then its element type is compared against the inside port. 323 * @param sourcePort The source port. 324 * @param destinationPortList A list of destination ports. 325 * @return A list of instances of Inequality indicating the 326 * type constraints that are not satisfied. 327 */ 328 @Override 329 protected List _checkTypesFromTo(TypedIOPort sourcePort, 330 List destinationPortList) { 331 List result = new LinkedList(); 332 333 boolean isUndeclared = sourcePort.getTypeTerm().isSettable(); 334 335 if (!isUndeclared) { 336 // sourcePort has a declared type. 337 Type srcDeclared = sourcePort.getType(); 338 Iterator destinationPorts = destinationPortList.iterator(); 339 340 while (destinationPorts.hasNext()) { 341 TypedIOPort destinationPort = (TypedIOPort) destinationPorts 342 .next(); 343 isUndeclared = destinationPort.getTypeTerm().isSettable(); 344 345 if (!isUndeclared) { 346 // both source/destination ports are declared, 347 // check type 348 Type destinationDeclared = destinationPort.getType(); 349 350 int compare; 351 352 // If the source port belongs to me, then we want to 353 // compare its array element type to the type of the 354 // destination. 355 if (sourcePort.getContainer() == this 356 && destinationPort.getContainer() != this) { 357 // The source port belongs to me, but not the 358 // destination. 359 Type srcElementType = ((ArrayType) srcDeclared) 360 .getElementType(); 361 compare = TypeLattice.compare(srcElementType, 362 destinationDeclared); 363 } else if (sourcePort.getContainer() != this 364 && destinationPort.getContainer() == this) { 365 // The destination port belongs to me, but not 366 // the source. 367 Type destinationElementType = ((ArrayType) destinationDeclared) 368 .getElementType(); 369 compare = TypeLattice.compare(srcDeclared, 370 destinationElementType); 371 } else { 372 compare = TypeLattice.compare(srcDeclared, 373 destinationDeclared); 374 } 375 376 if (compare == CPO.HIGHER || compare == CPO.INCOMPARABLE) { 377 Inequality inequality = new Inequality( 378 sourcePort.getTypeTerm(), 379 destinationPort.getTypeTerm()); 380 result.add(inequality); 381 } 382 } 383 } 384 } 385 386 return result; 387 } 388 389 /** Return the type constraints on all connections starting from the 390 * specified source port to all the ports in a group of destination 391 * ports. This overrides the base class to ensure that if the source 392 * port or the destination port is a port of this composite, then 393 * the port is forced to be an array type and the proper constraint 394 * on the element type of the array is made. If the source port 395 * has no possible sources of data, then no type constraints are 396 * added for it. 397 * @param sourcePort The source port. 398 * @return A list of instances of Inequality. 399 */ 400 @Override 401 protected List _destinationTypeConstraints(TypedIOPort sourcePort) { 402 Iterator<IOPort> destinationPorts; 403 List<Inequality> result = new LinkedList<Inequality>(); 404 boolean srcUndeclared = sourcePort.getTypeTerm().isSettable(); 405 406 if (sourcePort.isInput()) { 407 destinationPorts = sourcePort.insideSinkPortList().iterator(); 408 } else { 409 destinationPorts = sourcePort.sinkPortList().iterator(); 410 } 411 412 while (destinationPorts.hasNext()) { 413 TypedIOPort destinationPort = (TypedIOPort) destinationPorts.next(); 414 boolean destUndeclared = destinationPort.getTypeTerm().isSettable(); 415 416 if (srcUndeclared || destUndeclared) { 417 // At least one of the source/destination ports does 418 // not have declared type, form type constraint. 419 if (sourcePort.getContainer() == this 420 && destinationPort.getContainer() == this) { 421 // Both ports belong to this. 422 // Require the output to be at least the input. 423 Inequality ineq1 = new Inequality(sourcePort.getTypeTerm(), 424 destinationPort.getTypeTerm()); 425 result.add(ineq1); 426 427 // Finally, if backward type inference is enabled, 428 // require that the source array element type be greater 429 // than or equal to the GLB of all the destination ports. 430 if (isBackwardTypeInferenceEnabled()) { 431 InequalityTerm typeTerm = sourcePort.getTypeTerm(); 432 if (typeTerm.isSettable()) { 433 result.add(new Inequality( 434 new GLBArrayFunction(sourcePort), 435 typeTerm)); 436 } 437 } 438 } else if (sourcePort.getContainer().equals(this)) { 439 // The source port belongs to this, so its array element 440 // type must be less than or equal to the type of the destination 441 // port. 442 if (sourcePort.sourcePortList().size() == 0) { 443 // Skip this port. It is not connected on the outside. 444 continue; 445 } 446 447 // Require the source port to be an array. 448 Inequality arrayInequality = new Inequality( 449 ArrayType.ARRAY_BOTTOM, sourcePort.getTypeTerm()); 450 result.add(arrayInequality); 451 452 // Next require that the element type of the 453 // source port array be compatible with the 454 // destination port. 455 try { 456 Inequality ineq = new Inequality( 457 ArrayType.elementType(sourcePort), 458 destinationPort.getTypeTerm()); 459 result.add(ineq); 460 461 // Finally, if backward type inference is enabled, 462 // require that the source array element type be greater 463 // than or equal to the GLB of all the destination ports. 464 if (isBackwardTypeInferenceEnabled()) { 465 InequalityTerm typeTerm = sourcePort.getTypeTerm(); 466 if (typeTerm.isSettable()) { 467 result.add(new Inequality( 468 new GLBArrayFunction(sourcePort), 469 typeTerm)); 470 } 471 } 472 } catch (IllegalActionException e) { 473 throw new InternalErrorException(e); 474 } 475 476 } else if (destinationPort.getContainer().equals(this)) { 477 // Require that the destination port type be an array 478 // with elements compatible with the source port. 479 try { 480 Inequality ineq = new Inequality( 481 ArrayType.arrayOf(sourcePort), 482 destinationPort.getTypeTerm()); 483 result.add(ineq); 484 485 // Also require that the source port type 486 // be greater than or equal to the GLB of all 487 // its destination ports (or array element types 488 // of destination ports that are ports of this 489 // IterateOverArray actor). 490 // This ensures that backward type inference occurs. 491 // NOTE: We used to do this only if backward type 492 // inference was enabled globally. But the cost of 493 // doing this locally is small, and there is no 494 // mechanism for coercing the type of the inside 495 // actor, so if we want to be able to coerce the 496 // type without backward type inference being 497 // enabled globally, then we need to do this here. 498 // if (isBackwardTypeInferenceEnabled()) { 499 InequalityTerm typeTerm = sourcePort.getTypeTerm(); 500 if (typeTerm.isSettable()) { 501 result.add( 502 new Inequality( 503 new ArrayElementTypeFunction( 504 destinationPort), 505 typeTerm)); 506 } 507 // } 508 } catch (IllegalActionException e) { 509 throw new InternalErrorException(e); 510 } 511 } 512 } 513 } 514 515 return result; 516 } 517 518 /////////////////////////////////////////////////////////////////// 519 //// private variables //// 520 521 // Variable that reflects the current iteration count on the 522 // inside. 523 private Variable _iterationCount; 524 525 /////////////////////////////////////////////////////////////////// 526 //// private methods //// 527 528 /** Initialize the class. */ 529 private void _init() 530 throws IllegalActionException, NameDuplicationException { 531 setClassName("ptolemy.actor.lib.hoc.IterateOverArray"); 532 533 // Create the IterateDirector in the proper workspace. 534 IterateDirector iterateDirector = new IterateDirector(workspace()); 535 iterateDirector.setContainer(this); 536 iterateDirector.setName(uniqueName("IterateDirector")); 537 538 _iterationCount = new Variable(this, "iterationCount", new IntToken(0)); 539 _iterationCount.setTypeEquals(BaseType.INT); 540 } 541 542 /////////////////////////////////////////////////////////////////// 543 //// inner classes //// 544 545 /////////////////////////////////////////////////////////////////// 546 //// GLBArrayFunction 547 548 /** This class implements a monotonic function that returns an array 549 * type with element type equal to the greatest 550 * lower bound (GLB) of its arguments, or if any 551 * of its arguments is itself a port belonging to 552 * this IterateOverArray actor, then its array element type. 553 */ 554 private class GLBArrayFunction extends GLBArrayElementFunction { 555 556 public GLBArrayFunction(TypedIOPort sourcePort) { 557 super(sourcePort); 558 } 559 560 /** Return the current value of this monotonic function. 561 * @return A Type. 562 */ 563 @Override 564 public Object getValue() throws IllegalActionException { 565 Type elementType = (Type) super.getValue(); 566 return new ArrayType(elementType); 567 } 568 } 569 570 /////////////////////////////////////////////////////////////////// 571 //// GLBArrayElementFunction 572 573 /** This class implements a monotonic function that returns the greatest 574 * lower bound (GLB) of its arguments or the array element type 575 * of its arguments. Specifically, if one of the arguments is a port 576 * belonging to the enclosing task, then it references the array element 577 * type of that port rather than the port type. 578 */ 579 private class GLBArrayElementFunction extends GLBFunction { 580 581 public GLBArrayElementFunction(TypedIOPort sourcePort) { 582 super(sourcePort); 583 } 584 585 /** Return the current value of this monotonic function. 586 * @return A Type. 587 */ 588 @Override 589 public Object getValue() throws IllegalActionException { 590 _updateArguments(); 591 592 Set<Type> types = new HashSet<Type>(); 593 types.addAll(_cachedTypes); 594 for (InequalityTerm _cachedTerm : _cachedTerms) { 595 Object termObject = _cachedTerm.getAssociatedObject(); 596 if (termObject instanceof IOPort && ((IOPort) termObject) 597 .getContainer() == IterateOverArray.this) { 598 // The type term belongs to a port of this IterateOverArray actor. 599 // Use its element type rather than its type. 600 Object value = _cachedTerm.getValue(); 601 if (value instanceof ArrayType) { 602 types.add(((ArrayType) value).getElementType()); 603 } else if (value.equals(BaseType.GENERAL)) { 604 // To ensure that this function is monotonic, we have to 605 // handle the case where the value is greater than ArrayType. 606 // The only thing greater than ArrayType is GENERAL. 607 types.add(BaseType.GENERAL); 608 } 609 // If the value is not an array type, then it must be unknown, 610 // so we don't need to add it to the collection. Adding unknown 611 // to the arguments to GLB does nothing. 612 } else { 613 types.add((Type) _cachedTerm.getValue()); 614 } 615 } 616 // If there are no destination outputs at all, then set 617 // the output type to unknown. 618 if (types.size() == 0) { 619 return BaseType.UNKNOWN; 620 } 621 // If there is only one destination, the GLB is equal to the 622 // type of that port. 623 if (types.size() == 1) { 624 return types.toArray()[0]; 625 } 626 627 return TypeLattice.lattice().greatestLowerBound(types); 628 } 629 } 630 631 /////////////////////////////////////////////////////////////////// 632 //// IterateComposite 633 634 /** This is a specialized composite actor for use in IterateOverArray. 635 * In particular, it ensures that if ports are added or deleted 636 * locally, then corresponding ports will be added or deleted 637 * in the container. That addition will result in appropriate 638 * connections being made. 639 */ 640 public static class IterateComposite 641 extends MirrorComposite.MirrorCompositeContents { 642 // NOTE: This has to be a static class so that MoML can 643 // instantiate it. 644 645 /** Construct an actor with a name and a container. 646 * @param container The container. 647 * @param name The name of this actor. 648 * @exception IllegalActionException If the container is incompatible 649 * with this actor. 650 * @exception NameDuplicationException If the name coincides with 651 * an actor already in the container. 652 */ 653 public IterateComposite(CompositeEntity container, String name) 654 throws IllegalActionException, NameDuplicationException { 655 super(container, name); 656 } 657 658 /** Override the base class to return a specialized port. 659 * @param name The name of the port to create. 660 * @return A new instance of IteratePort, an inner class. 661 * @exception NameDuplicationException If the container already has 662 * a port with this name. 663 */ 664 @Override 665 public Port newPort(String name) throws NameDuplicationException { 666 try { 667 return new IteratePort(this, name); 668 } catch (IllegalActionException ex) { 669 // This exception should not occur, so we throw a runtime 670 // exception. 671 throw new InternalErrorException(this, ex, null); 672 } 673 } 674 } 675 676 /////////////////////////////////////////////////////////////////// 677 //// IterateDirector 678 679 /** This is a specialized director that fires contained actors 680 * in the order in which they appear in the actor list repeatedly 681 * until either there is no more input data for the actor or 682 * the prefire() method of the actor returns false. If postfire() 683 * of any actor returns false, then postfire() of this director 684 * will return false, requesting a halt to execution of the model. 685 */ 686 private class IterateDirector extends Director { 687 /** Construct an IterateDirector in the specified workspace with 688 * no container and an empty string as a name. You can then change 689 * the name with setName(). If the workspace argument is null, then 690 * use the default workspace. You should set the local director or 691 * executive director before attempting to send data to the actor 692 * or to execute it. Add the actor to the workspace directory. 693 * Increment the version number of the workspace. 694 * @param workspace The workspace that will list the actor. 695 * @exception IllegalActionException If the container is incompatible 696 * with this actor. 697 * @exception NameDuplicationException If the name coincides with 698 * an actor already in the container. 699 */ 700 public IterateDirector(Workspace workspace) 701 throws IllegalActionException, NameDuplicationException { 702 super(workspace); 703 setPersistent(false); 704 } 705 706 /** Invoke iterations on the contained actor of the 707 * container of this director repeatedly until either it runs out 708 * of input data or prefire() returns false. If postfire() of the 709 * actor returns false, then set a flag indicating to postfire() of 710 * this director to return false. 711 * @exception IllegalActionException If any called method of 712 * of the contained actor throws it, or if the contained 713 * actor is not opaque. 714 */ 715 @Override 716 public void fire() throws IllegalActionException { 717 // Don't call "super.fire();" here, this actor contains its 718 // own director. 719 CompositeActor container = (CompositeActor) getContainer(); 720 Iterator actors = container.entityList().iterator(); 721 _postfireReturns = true; 722 723 while (actors.hasNext() && !_stopRequested) { 724 Actor actor = (Actor) actors.next(); 725 726 if (!((ComponentEntity) actor).isOpaque()) { 727 throw new IllegalActionException(container, 728 "Inside actor is not opaque " 729 + "(perhaps it needs a director)."); 730 } 731 732 int result = Executable.COMPLETED; 733 int iterationCount = 0; 734 735 while (result != Executable.NOT_READY) { 736 iterationCount++; 737 _iterationCount.setToken(new IntToken(iterationCount)); 738 739 if (_debugging) { 740 _debug(new FiringEvent(this, actor, 741 FiringEvent.BEFORE_ITERATE, iterationCount)); 742 } 743 744 result = actor.iterate(1); 745 746 if (_debugging) { 747 _debug(new FiringEvent(this, actor, 748 FiringEvent.AFTER_ITERATE, iterationCount)); 749 } 750 751 // Should return if there is no more input data, 752 // irrespective of return value of prefire() of 753 // the actor, which is not reliable. 754 boolean outOfData = true; 755 Iterator inPorts = actor.inputPortList().iterator(); 756 757 while (inPorts.hasNext()) { 758 IOPort port = (IOPort) inPorts.next(); 759 760 for (int i = 0; i < port.getWidth(); i++) { 761 if (port.hasToken(i)) { 762 outOfData = false; 763 break; 764 } 765 } 766 } 767 768 if (outOfData) { 769 if (_debugging) { 770 _debug("No more input data for: " 771 + ((Nameable) actor).getFullName()); 772 } 773 774 break; 775 } 776 777 if (result == Executable.STOP_ITERATING) { 778 if (_debugging) { 779 _debug("Actor requests halt: " 780 + ((Nameable) actor).getFullName()); 781 } 782 783 _postfireReturns = false; 784 break; 785 } 786 } 787 } 788 } 789 790 /** Return a new instance of QueueReceiver. 791 * @return A new instance of QueueReceiver. 792 * @see QueueReceiver 793 */ 794 @Override 795 public Receiver newReceiver() { 796 return new QueueReceiver(); 797 } 798 799 /** Override the base class to return the logical AND of 800 * what the base class postfire() method returns and the 801 * flag set in fire(). As a result, this will return 802 * false if any contained actor returned false in its 803 * postfire() method. 804 */ 805 @Override 806 public boolean postfire() throws IllegalActionException { 807 boolean superReturns = super.postfire(); 808 return superReturns && _postfireReturns; 809 } 810 811 /** Transfer data from an input port of the 812 * container to the ports it is connected to on the inside. 813 * This method extracts tokens from the input array and 814 * provides them sequentially to the corresponding ports 815 * of the contained actor. 816 * @param port The port to transfer tokens from. 817 * @return True if at least one data token is transferred. 818 * @exception IllegalActionException Not thrown in this base class. 819 */ 820 @Override 821 public boolean transferInputs(IOPort port) 822 throws IllegalActionException { 823 boolean result = false; 824 825 for (int i = 0; i < port.getWidth(); i++) { 826 // NOTE: This is not compatible with certain cases 827 // in PN, where we don't want to block on a port 828 // if nothing is connected to the port on the 829 // inside. 830 try { 831 if (port.isKnown(i)) { 832 if (port.hasToken(i)) { 833 Token t = port.get(i); 834 835 if (_debugging) { 836 _debug(getName(), "transferring input from " 837 + port.getName()); 838 } 839 840 ArrayToken arrayToken = (ArrayToken) t; 841 842 for (int j = 0; j < arrayToken.length(); j++) { 843 port.sendInside(i, arrayToken.getElement(j)); 844 } 845 846 result = true; 847 } 848 } 849 } catch (NoTokenException ex) { 850 // this shouldn't happen. 851 throw new InternalErrorException(this, ex, null); 852 } 853 } 854 855 return result; 856 } 857 858 /** Transfer data from the inside receivers of an output port of the 859 * container to the ports it is connected to on the outside. 860 * This method packages the available tokens into a single array. 861 * @param port The port to transfer tokens from. 862 * @return True if at least one data token is transferred. 863 * @exception IllegalActionException Not thrown in this base class. 864 * @see IOPort#transferOutputs 865 */ 866 @Override 867 public boolean transferOutputs(IOPort port) 868 throws IllegalActionException { 869 boolean result = false; 870 871 // Output type might be GENERAL, in which case, we 872 // let the element type be GENERAL. 873 Type elementType = BaseType.GENERAL; 874 Type portType = ((TypedIOPort) port).getType(); 875 if (portType instanceof ArrayType) { 876 elementType = ((ArrayType) portType).getElementType(); 877 } 878 879 for (int i = 0; i < port.getWidthInside(); i++) { 880 try { 881 ArrayList list = new ArrayList(); 882 883 while (port.isKnownInside(i) && port.hasNewTokenInside(i)) { 884 Token t = port.getInside(i); 885 list.add(t); 886 } 887 888 if (list.size() != 0) { 889 Token[] tokens = (Token[]) list 890 .toArray(new Token[list.size()]); 891 892 if (_debugging) { 893 _debug(getName(), 894 "transferring output to " + port.getName()); 895 } 896 897 port.send(i, new ArrayToken(elementType, tokens)); 898 } else { 899 // Send an empty list 900 port.send(i, new ArrayToken(elementType)); 901 } 902 903 result = true; 904 } catch (NoTokenException ex) { 905 throw new InternalErrorException(this, ex, null); 906 } 907 } 908 909 return result; 910 } 911 912 ////////////////////////////////////////////////////////////// 913 //// private variables //// 914 // Indicator that at least one actor returned false in postfire. 915 private boolean _postfireReturns = true; 916 } 917 918 /////////////////////////////////////////////////////////////////// 919 //// IteratePort 920 921 /** This is a specialized port for IterateOverArray. 922 * If the container is an instance of IterateOverArray, 923 * then it handles type conversions between 924 * the array types of the ports of the enclosing IterateOverArray 925 * actor and the scalar types (or arrays with one less dimension) 926 * of the actor that are contained. It has a notion of an 927 * "associated port," and ensures that changes to the status 928 * of one port (whether it is input, output, or multiport) 929 * are reflected in the associated port. 930 */ 931 public static class IteratePort extends MirrorPort { 932 /** Construct a port in the specified workspace with an empty 933 * string as a name. You can then change the name with setName(). 934 * If the workspace argument 935 * is null, then use the default workspace. 936 * The object is added to the workspace directory. 937 * Increment the version number of the workspace. 938 * @param workspace The workspace that will list the port. 939 * @exception IllegalActionException If port parameters cannot be initialized. 940 */ 941 public IteratePort(Workspace workspace) throws IllegalActionException { 942 // This constructor is needed for Shallow codgen. 943 super(workspace); 944 } 945 946 // NOTE: This class has to be static because otherwise the 947 // constructor has an extra argument (the first argument, 948 // actually) that is an instance of the enclosing class. 949 // The MoML parser cannot know what the instance of the 950 // enclosing class is, so it would not be able to instantiate 951 // these ports. 952 953 /** Create a new instance of a port for IterateOverArray. 954 * @param container The container for the port. 955 * @param name The name of the port. 956 * @exception IllegalActionException Not thrown in this base class. 957 * @exception NameDuplicationException Not thrown in this base class. 958 */ 959 public IteratePort(TypedCompositeActor container, String name) 960 throws IllegalActionException, NameDuplicationException { 961 super(container, name); 962 963 // NOTE: Ideally, Port are created when an entity is added. 964 // However, there appears to be no clean way to do this. 965 // Instead, ports are added when an entity is added via a 966 // change request registered with this IterateOverArray actor. 967 // Consequently, these ports have to be persistent, and this 968 // constructor and class have to be public. 969 // setPersistent(false); 970 } 971 972 /** Override the base class to convert the token to the element 973 * type rather than to the type of the port, unless the port 974 * is of type GENERAL, in which case, no conversion is necessary. 975 * @param token The token to convert. 976 * @return The converted token. 977 * @exception IllegalActionException If the conversion is 978 * invalid. 979 */ 980 @Override 981 public Token convert(Token token) throws IllegalActionException { 982 if (!(getContainer() instanceof IterateOverArray) || !isOutput()) { 983 return super.convert(token); 984 } 985 if (getType().equals(BaseType.GENERAL)) { 986 return token; 987 } 988 Type type = ((ArrayType) getType()).getElementType(); 989 990 if (type.equals(token.getType())) { 991 return token; 992 } else { 993 Token newToken = type.convert(token); 994 return newToken; 995 } 996 } 997 998 /** Override the base class to convert the token to the element 999 * type rather than to the type of the port. 1000 * @param channelIndex The index of the channel, from 0 to width-1 1001 * @param token The token to send 1002 * @exception NoRoomException If there is no room in the receiver. 1003 * @exception IllegalActionException Not thrown in this base class. 1004 */ 1005 @Override 1006 public void sendInside(int channelIndex, Token token) 1007 throws IllegalActionException, NoRoomException { 1008 if (!(getContainer() instanceof IterateOverArray)) { 1009 super.sendInside(channelIndex, token); 1010 return; 1011 } 1012 1013 Receiver[][] farReceivers; 1014 1015 if (_debugging) { 1016 _debug("send inside to channel " + channelIndex + ": " + token); 1017 } 1018 1019 if (_hasPortEventListeners) { 1020 _notifyPortEventListeners(new IOPortEvent(this, 1021 IOPortEvent.SEND_BEGIN, channelIndex, true, token)); 1022 } 1023 1024 try { 1025 try { 1026 _workspace.getReadAccess(); 1027 1028 ArrayType type = (ArrayType) getType(); 1029 int compare = TypeLattice.compare(token.getType(), 1030 type.getElementType()); 1031 1032 if (compare == CPO.HIGHER || compare == CPO.INCOMPARABLE) { 1033 throw new IllegalActionException( 1034 "Run-time type checking failed. Token type: " 1035 + token.getType().toString() 1036 + ", port: " + getFullName() 1037 + ", port type: " 1038 + getType().toString()); 1039 } 1040 1041 // Note that the getRemoteReceivers() method doesn't throw 1042 // any non-runtime exception. 1043 farReceivers = deepGetReceivers(); 1044 1045 if (farReceivers == null 1046 || farReceivers[channelIndex] == null) { 1047 return; 1048 } 1049 } finally { 1050 _workspace.doneReading(); 1051 } 1052 1053 for (int j = 0; j < farReceivers[channelIndex].length; j++) { 1054 TypedIOPort port = (TypedIOPort) farReceivers[channelIndex][j] 1055 .getContainer(); 1056 Token newToken = port.convert(token); 1057 farReceivers[channelIndex][j].put(newToken); 1058 } 1059 } catch (ArrayIndexOutOfBoundsException ex) { 1060 // NOTE: This may occur if the channel index is out of range. 1061 // This is allowed, just do nothing. 1062 } finally { 1063 if (_hasPortEventListeners) { 1064 _notifyPortEventListeners(new IOPortEvent(this, 1065 IOPortEvent.SEND_END, channelIndex, true, token)); 1066 } 1067 } 1068 } 1069 } 1070}