001/* A count and a list of schedule elements. 002 003 Copyright (c) 2003-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 027 */ 028package ptolemy.graph.sched; 029 030import java.util.ArrayList; 031import java.util.Collections; 032import java.util.ConcurrentModificationException; 033import java.util.HashMap; 034import java.util.Iterator; 035import java.util.LinkedList; 036import java.util.List; 037import java.util.Map; 038import java.util.NoSuchElementException; 039 040import ptolemy.kernel.util.InternalErrorException; 041import ptolemy.kernel.util.InvalidStateException; 042 043/////////////////////////////////////////////////////////////////// 044//// Schedule 045 046/** 047 This class represents a static schedule of firing elements invocation. An 048 instance of this class is returned by the scheduler of a model to 049 represent order of firing element firings in the model. A schedule consists of 050 a list of schedule elements and the number of times the schedule 051 should repeat (called the <i>iteration count</i>). <p> 052 053 Each element of 054 the schedule is represented by an instance of the ScheduleElement 055 class. Each element may correspond to a number of firings of a single 056 firing element (represented by the Firing class) or an entire sub-schedule 057 (represented by a hierarchically contained instance of this class). 058 This nesting allows this concise representation of looped schedules. 059 The nesting can be arbitrarily deep, but must be a tree where the 060 leaf nodes represent firings. It is up to the scheduler to 061 enforce this requirement. <p> 062 063 The add() and remove() methods are used to add or 064 remove schedule elements. Only elements of type ScheduleElement (Schedule 065 or Firing) may be added to the schedule list. 066 067 The iteration count is set by the 068 setIterationCount() method. If this method is not invoked, a default value 069 of one will be used. <p> 070 071 As an example, suppose that we have an SDF graph containing actors 072 A, B, C, and D, with the firing order ABCBCBCDD. 073 This firing order can be represented by a simple looped schedule. 074 The code to create this schedule appears below. 075 <p> 076 <pre> 077 * Schedule S = new Schedule(); 078 * Firing S1 = new Firing(); 079 * Schedule S2 = new Schedule(); 080 * Firing S3 = new Firing(); 081 * S.add(S1); 082 * S.add(S2); 083 * S.add(S3); 084 * S1.setFiringElement(A); 085 * S2.setIterationCount(3); 086 * Firing S2_1 = new Firing(); 087 * Firing S2_2 = new Firing(); 088 * S2_1.setFiringElement(B); 089 * S2_2.setFiringElement(C); 090 * S2.add(S2_1); 091 * S2.add(S2_2); 092 * S3.setIterationCount(2); 093 * S3.setFiringElement(D); 094 </pre> 095 <p> 096 097 Note that this implementation is not synchronized. It is therefore not safe 098 for a thread to make modifications to the schedule structure while 099 multiple threads are concurrently accessing the schedule. 100 <h1>References</h1> 101 S. S. Bhattacharyya, P K. Murthy, and E. A. Lee, 102 Software Syntheses from Dataflow Graphs, Kluwer Academic Publishers, 1996. 103 104 @since Ptolemy II 4.0 105 @Pt.ProposedRating red (shahrooz) 106 @Pt.AcceptedRating red (ssb) 107 @author Shahrooz Shahparnia, Mingyung Ko, 108 University of Maryland at College Park, based on a file by 109 Brian K. Vogel, Steve Neuendorffer 110 @version $Id$ 111 @see ptolemy.graph.sched.Firing 112 @see ptolemy.graph.sched.Schedule 113 @see ptolemy.graph.sched.ScheduleElement 114 */ 115public class Schedule extends ScheduleElement { 116 /** Construct a schedule with iteration count of one and an 117 * empty schedule list. This constructor should be used when 118 * creating a root schedule. 119 */ 120 public Schedule() { 121 super(); 122 123 // This list will contain the schedule elements. 124 _schedule = new LinkedList(); 125 126 //_firingIteratorVersion = 0; 127 // Default tree depth to use for allocation state arrays 128 // for the firingIterator() method. The arrays will be 129 // dynamically resized as needed. 3 was an arbitrary 130 // choice. Any positive integer will do. The arrays are 131 // only resized while iterating through the first iterator 132 // that was created since the schedule was modified. 133 _treeDepth = 3; 134 } 135 136 /** Construct a schedule with iteration count of one and an 137 * empty schedule list. This constructor should be used when 138 * creating a root schedule. If this constructor is used, 139 * {@link ScheduleElement}s containing firing elements of the given class 140 * would only be accepted as next elements for this schedule. 141 * 142 * @param firingElementClass The given class type. 143 */ 144 public Schedule(Class firingElementClass) { 145 super(firingElementClass); 146 147 // This list will contain the schedule elements. 148 _schedule = new LinkedList(); 149 150 //_firingIteratorVersion = 0; 151 // Default tree depth to use for allocation state arrays 152 // for the firingIterator() method. The arrays will be 153 // dynamically resized as needed. 3 was an arbitrary 154 // choice. Any positive integer will do. The arrays are 155 // only resized while iterating through the first iterator 156 // that was created since the schedule was modified. 157 _treeDepth = 3; 158 } 159 160 /////////////////////////////////////////////////////////////////// 161 //// public methods //// 162 163 /** Append the specified schedule element to the end of the schedule 164 * list. This element must be an instance of ScheduleElement. 165 * 166 * @param element The schedule element to add. 167 */ 168 public void add(ScheduleElement element) { 169 add(_schedule.size(), element); 170 } 171 172 /** Insert the specified schedule element at the specified position in 173 * the schedule list. This element must be an instance of 174 * ScheduleElement. An exception is thrown if the index is out of 175 * range. 176 * 177 * @param index The index at which the specified element is to be 178 * inserted. 179 * @param element The schedule element to add. 180 * @exception IndexOutOfBoundsException If the specified index is out of 181 * range (index < 0 || index > size()). 182 */ 183 public void add(int index, ScheduleElement element) { 184 // Give element a reference to this schedule so that it can 185 // notify this schedule (via _incrementVersions()) when 186 // element is modified. 187 if (this.firingElementClass() != null) { 188 Class firingElementClass = this.firingElementClass(); 189 Class elementClass = element.firingElementClass(); 190 191 if (elementClass != null) { 192 if (firingElementClass.isAssignableFrom(elementClass)) { 193 element.setParent(this); 194 _incrementVersion(); 195 _schedule.add(index, element); 196 } else { 197 throw new RuntimeException("Attempt to add a non " 198 + "authorized firing element"); 199 } 200 } else { 201 throw new RuntimeException( 202 "Attempt to add a non " + "authorized firing element"); 203 } 204 } else { 205 element.setParent(this); 206 _incrementVersion(); 207 _schedule.add(index, element); 208 } 209 } 210 211 /** The number of times the given firing element appears in the 212 * schedule. Generally, firing elements represent program blocks. 213 * Appearances of elements indicate how many copies of codes are 214 * in the schedule. The more appearances, the larger program 215 * space is required. 216 * 217 * @param firingElement The given firing element. 218 * @return The number of appearances. 219 */ 220 public int appearanceCount(Object firingElement) { 221 return firings(firingElement).size(); 222 } 223 224 /** Return the firing element invocation sequence of the schedule in the 225 * form of a sequence of firing elements. For a valid schedule, all of the 226 * lowest-level nodes should be an instance of Firing. If the 227 * schedule is not valid, then the returned iterator will contain 228 * null elements. 229 * <p> 230 * A runtime exception is thrown if the 231 * underlying schedule structure is modified while the iterator 232 * is active. 233 * 234 * @return An iterator over a sequence of firing elements. 235 * @exception ConcurrentModificationException If the 236 * underlying schedule structure is modified while the iterator 237 * is active. 238 */ 239 @Override 240 public Iterator firingElementIterator() { 241 return new FiringElementIterator(); 242 } 243 244 /** Return the Firing invocation sequence of this schedule in the form 245 * of a sequence of firings. All of the lowest-level nodes of the 246 * schedule should be an instance of Firing. Otherwise an exception will 247 * occur at some point in the iteration. 248 * <p> 249 * A runtime exception is thrown if the 250 * underlying schedule structure is modified while the iterator 251 * is active. 252 * <p> 253 * Implementation note: This method is optimized to be memory 254 * efficient. It walks the schedule tree structure as the 255 * iterator methods are invoked. 256 * 257 * @return An iterator over a sequence of firings. 258 * @exception ConcurrentModificationException If the 259 * underlying schedule structure is modified while the iterator 260 * is active. 261 */ 262 @Override 263 public Iterator firingIterator() { 264 return new FiringIterator(this); 265 } 266 267 /** Get firings for the firing element. This is used in determining 268 * element appearances. 269 * 270 * @param firingElement The given firing element. 271 * @return A list of firings for the firing element. 272 */ 273 274 //FIXME: It's probably suitable for 'protected' or 'private'. 275 //FIXME: The returned type can be replaced with un-ordered 276 // collection, eg. Set, if the sequence of firings 277 // doesn't matter. 278 public List firings(Object firingElement) { 279 Map firingElementFiringsMap = _getFiringElementFiringsMap(); 280 return Collections.unmodifiableList( 281 (List) firingElementFiringsMap.get(firingElement)); 282 } 283 284 /** Return the element at the specified position in the list. 285 * 286 * @param index The index of the element to return. 287 * @return The element at the specified position in the list. 288 */ 289 public ScheduleElement get(int index) { 290 return (ScheduleElement) _schedule.get(index); 291 } 292 293 /** Return an iterator over the schedule elements of this schedule. 294 * The ordering of elements in the iterator sequence is the order 295 * of the schedule list. The elements of the iterator sequence are 296 * instances of Firing or Schedule. 297 * <p> 298 * A runtime exception is thrown if the 299 * underlying schedule structure is modified while the iterator 300 * is active. 301 * 302 * @return An iterator over the schedule elements of this schedule. 303 * @exception ConcurrentModificationException If the 304 * underlying schedule structure is modified while the iterator 305 * is active. 306 */ 307 public Iterator iterator() { 308 return _schedule.iterator(); 309 } 310 311 /** Get the lexical order of firing elements. Only single appearance 312 * schedules are qualified for this operation. It's meaningless to 313 * have the order for multiple appearance schedules. 314 * 315 * @return The lexical order of firing elements. 316 * @exception IllegalStateException If this is not a single 317 * appearance schedule. 318 */ 319 public List lexicalOrder() { 320 List lexicalList = new ArrayList(); 321 Iterator firingInstances = _getFiringInstancesList().iterator(); 322 323 while (firingInstances.hasNext()) { 324 Firing firing = (Firing) firingInstances.next(); 325 Object firingElement = firing.getFiringElement(); 326 327 if (lexicalList.contains(firingElement)) { 328 throw new IllegalStateException( 329 "Not a single appearance schedule to compute " 330 + "the lexical order of nodes."); 331 } else { 332 lexicalList.add(firingElement); 333 } 334 } 335 336 return Collections.unmodifiableList(lexicalList); 337 } 338 339 /** Get the maximum appearance counts of all firing elements in 340 * the schedule. 341 * 342 * @return The maximum appearance counts. 343 */ 344 public int maxAppearanceCount() { 345 int maxCount = 0; 346 Iterator firingLists = _getFiringElementFiringsMap().values() 347 .iterator(); 348 349 while (firingLists.hasNext()) { 350 int firingListSize = ((List) firingLists.next()).size(); 351 352 if (maxCount < firingListSize) { 353 maxCount = firingListSize; 354 } 355 } 356 357 return maxCount; 358 } 359 360 /** Remove the schedule element at the specified position in the 361 * schedule list. 362 * 363 * @param index The index of the schedule element to be removed. 364 * @return The schedule element that was removed. 365 */ 366 public ScheduleElement remove(int index) { 367 _incrementVersion(); 368 return (ScheduleElement) _schedule.remove(index); 369 } 370 371 /** Return the number of elements in this list. 372 * 373 * @return The number of elements in this list. 374 */ 375 public int size() { 376 return _schedule.size(); 377 } 378 379 /** Print the schedule in a nested parenthesis style. Please see 380 * {@link ScheduleElement#toParenthesisString(Map, String)}. 381 * 382 * @param nameMap The map from firing elements to their short names. 383 * @param delimiter The delimiter between iterands. 384 * @return A nested parenthesis expression of the schedule. 385 */ 386 @Override 387 public String toParenthesisString(Map nameMap, String delimiter) { 388 StringBuffer result = new StringBuffer("("); 389 int iterations = getIterationCount(); 390 391 if (iterations > 1) { 392 result.append(iterations); 393 } 394 395 Iterator schedules = iterator(); 396 397 while (schedules.hasNext()) { 398 ScheduleElement schedule = (ScheduleElement) schedules.next(); 399 result.append(delimiter 400 + schedule.toParenthesisString(nameMap, delimiter)); 401 } 402 403 result.append(")"); 404 return result.toString(); 405 } 406 407 /** Output a string representation of this Schedule. 408 * 409 * @return Return a string representation of this Schedule. 410 */ 411 @Override 412 public String toString() { 413 StringBuffer result = new StringBuffer("Execute Schedule{\n"); 414 Iterator i = iterator(); 415 416 while (i.hasNext()) { 417 ScheduleElement e = (ScheduleElement) i.next(); 418 result.append(e + "\n"); 419 } 420 421 result.append("}"); 422 423 if (getIterationCount() > 1) { 424 result.append(" " + getIterationCount() + " times"); 425 } 426 427 return result.toString(); 428 } 429 430 /////////////////////////////////////////////////////////////////// 431 //// inner classes //// 432 433 /** An adapter class for iterating over the firing elements of this 434 * schedule. An exception is thrown if the schedule structure 435 * changes while this iterator is active. 436 */ 437 private class FiringElementIterator implements Iterator { 438 /** Construct a ScheduleIterator. 439 */ 440 public FiringElementIterator() { 441 _advance = true; 442 _firingIterator = firingIterator(); 443 _currentVersion = _getVersion(); 444 _currentIteration = 0; 445 } 446 447 /** Return true if the iteration has more elements. 448 * 449 * @exception ConcurrentModificationException If the schedule 450 * data structure has changed since this iterator was created. 451 * @return True if the iterator has more elements. 452 */ 453 @Override 454 public boolean hasNext() { 455 if (_currentVersion != _getVersion()) { 456 throw new ConcurrentModificationException( 457 "Schedule structure changed while iterator is active."); 458 } else if (_advance == true) { 459 boolean returnValue; 460 461 if (_currentFiring == null) { 462 returnValue = _firingIterator.hasNext(); 463 464 if (returnValue == true) { 465 _currentFiring = (Firing) _firingIterator.next(); 466 _currentFiringElement = _currentFiring 467 .getFiringElement(); 468 _currentIteration++; 469 _advance = false; 470 _lastHasNext = true; 471 return _lastHasNext; 472 } else { 473 _advance = false; 474 _lastHasNext = false; 475 return _lastHasNext; 476 } 477 } else { 478 if (_currentIteration < _currentFiring 479 .getIterationCount()) { 480 _currentIteration++; 481 _advance = false; 482 _lastHasNext = true; 483 return _lastHasNext; 484 } else { 485 _currentIteration = 0; 486 returnValue = _firingIterator.hasNext(); 487 488 if (returnValue == true) { 489 _currentFiring = (Firing) _firingIterator.next(); 490 _currentFiringElement = _currentFiring 491 .getFiringElement(); 492 _currentIteration++; 493 _advance = false; 494 _lastHasNext = true; 495 return _lastHasNext; 496 } else { 497 _advance = false; 498 _lastHasNext = false; 499 return _lastHasNext; 500 } 501 } 502 } 503 } else { 504 return _lastHasNext; 505 } 506 } 507 508 /** Return the next object in the iteration. 509 * 510 * @exception InvalidStateException If the schedule data structure 511 * has changed since this iterator was created. 512 * @return The next object in the iteration. 513 */ 514 @Override 515 public Object next() throws NoSuchElementException { 516 if (!hasNext()) { 517 throw new NoSuchElementException("No element to return."); 518 } else if (_currentVersion != _getVersion()) { 519 throw new ConcurrentModificationException( 520 "Schedule structure changed while iterator is active."); 521 } else { 522 _advance = true; 523 return _currentFiringElement; 524 } 525 } 526 527 /** Throw an exception, since removal is not allowed. It really 528 * doesn't make sense to remove an actor from an actor invocation 529 * sequence anyway. 530 */ 531 @Override 532 public void remove() { 533 throw new UnsupportedOperationException(); 534 } 535 536 private Iterator _firingIterator; 537 538 private Firing _currentFiring; 539 540 private Object _currentFiringElement; 541 542 private long _currentVersion; 543 544 private int _currentIteration; 545 546 private boolean _advance; 547 548 private boolean _lastHasNext; 549 } 550 551 /** An adapter class for iterating over the firings of this 552 * schedule. An exception is thrown if the schedule structure 553 * changes while this iterator is active. The iterator walks 554 * the schedule tree as the hasNext() and next() methods are 555 * invoked, using a small number of state variables. 556 */ 557 private class FiringIterator implements Iterator { 558 /** Construct a ScheduleIterator. 559 */ 560 public FiringIterator(Schedule schedule) { 561 // If _advance is true, then hasNext() can move 562 // to the next node when invoked. 563 _advance = true; 564 _startingVersion = _getVersion(); 565 566 // state. This is the current position as we walk 567 // the schedule tree. 568 _currentNode = schedule; 569 570 // The depth of _currentNode in the schedule tree. 571 // Depth 0 corresponds to the level of the root node 572 // of the tree. 573 _currentDepth = 0; 574 575 // These state arrays are dynamically increased 576 // in size as we deeper into the tree. Their 577 // length is equal to the number of nesting levels 578 // in the schedule. 579 _iterationCounts = new int[_treeDepth]; 580 _horizontalNodePosition = new int[_treeDepth]; 581 } 582 583 /** Return true if the iteration has more elements. 584 * 585 * @exception ConcurrentModificationException If the schedule 586 * data structure has changed since this iterator was created. 587 * @exception InternalErrorException If the schedule contains 588 * any leaf nodes that are not an instance of Firing. 589 * @return True if the iterator has more elements. 590 */ 591 @Override 592 public boolean hasNext() { 593 // This code may look messy, but it simply walks the 594 // schedule tree. 595 if (_startingVersion != _getVersion()) { 596 throw new ConcurrentModificationException( 597 "Schedule structure changed while iterator is active."); 598 } else if (_advance == true) { 599 // Try to go to the next firing node in the tree. Return 600 // false if we fail. 601 if (_currentNode instanceof Firing) { 602 Schedule scheduleNode = _backTrack(_currentNode); 603 _currentNode = _findLeafNode(scheduleNode); 604 605 if (_currentNode == null) { 606 // There are no more Firings in the tree. 607 _advance = false; 608 _lastHasNext = false; 609 return _lastHasNext; 610 } 611 } else if (_currentNode instanceof Schedule) { 612 // This condition should only happen for the first element 613 // in the iteration. 614 _currentNode = _findLeafNode((Schedule) _currentNode); 615 616 if (_currentNode == null) { 617 // There are no more Firings in the tree at all. 618 _advance = false; 619 _lastHasNext = false; 620 return _lastHasNext; 621 } 622 } else { 623 // Throw runtime exception. 624 throw new RuntimeException( 625 "Encountered a ScheduleElement that " 626 + "is not an instance " 627 + "of Schedule or Firing."); 628 } 629 630 _advance = false; 631 _lastHasNext = true; 632 return _lastHasNext; 633 } else { 634 return _lastHasNext; 635 } 636 } 637 638 /** Return the next object in the iteration. 639 * 640 * @exception InvalidStateException If the schedule 641 * data structure has changed since this iterator was created. 642 * @return The next object in the iteration. 643 */ 644 @Override 645 public Object next() throws NoSuchElementException { 646 if (!hasNext()) { 647 throw new NoSuchElementException("No element to return."); 648 } else if (_startingVersion != _getVersion()) { 649 throw new ConcurrentModificationException( 650 "Schedule structure changed while iterator is active."); 651 } else { 652 _advance = true; 653 return _currentNode; 654 } 655 } 656 657 /** Throw an exception, since removal is not allowed. It really 658 * doesn't make sense to remove a firing from the firing 659 * sequence anyway, since there is not a 1-1 correspondence 660 * between the firing in a firing iterator and a firing in the 661 * schedule. 662 */ 663 @Override 664 public void remove() { 665 throw new UnsupportedOperationException(); 666 } 667 668 /////////////////////////////////////////////////////////////////// 669 //// private methods //// 670 671 /** Start at the specified node in the schedule tree and 672 * move up the tree (towards the root node) until we 673 * find a node the has children we have not iterated through 674 * yet or children that we have not iterated through enough 675 * times (not reached the maximum iteration count). 676 * 677 * @param firingNode The starting node to backtrack from. 678 */ 679 private Schedule _backTrack(ScheduleElement firingNode) { 680 if (_currentDepth == 0) { 681 // Don't backtrack past the root node. 682 return null; 683 } 684 685 _currentDepth--; 686 687 Schedule node = (Schedule) firingNode.getParent(); 688 689 if (node == null) { 690 return null; 691 } else if (node 692 .size() > ++_horizontalNodePosition[_currentDepth + 1]) { 693 return node; 694 } else if (++_iterationCounts[_currentDepth] < node 695 .getIterationCount()) { 696 _horizontalNodePosition[_currentDepth + 1] = 0; 697 return node; 698 } 699 700 _horizontalNodePosition[_currentDepth + 1] = 0; 701 _iterationCounts[_currentDepth] = 0; 702 return _backTrack(node); 703 } 704 705 /** Start at the specified node and move down the tree 706 * (away from the root node) until we find the next Firing 707 * in the iteration order. Through a runtime exception 708 * if we encounter a leaf node that is not an instance of 709 * Firing. 710 * 711 * @param node The schedule node to start from. 712 */ 713 private ScheduleElement _findLeafNode(Schedule node) { 714 // Check if we need to resize the arrays. 715 if (_iterationCounts.length - 1 < _currentDepth + 2) { 716 // Need to resize. 717 int[] temp = new int[_currentDepth + 2]; 718 719 for (int i = 0; i < _iterationCounts.length; i++) { 720 temp[i] = _iterationCounts[i]; 721 } 722 723 _iterationCounts = temp; 724 725 int[] temp2 = new int[_currentDepth + 2]; 726 727 for (int i = 0; i < _horizontalNodePosition.length; i++) { 728 temp2[i] = _horizontalNodePosition[i]; 729 } 730 731 _horizontalNodePosition = temp2; 732 733 // Set new max tree depth. Any new iterators will 734 // create state arrays with this length to avoid 735 // needing to resize in the future (provide the 736 // schedule structure is not modified). 737 _treeDepth = _currentDepth + 2; 738 } 739 740 if (node == null) { 741 return null; 742 } else if (node 743 .size() > _horizontalNodePosition[_currentDepth + 1]) { 744 _currentDepth++; 745 746 ScheduleElement nodeElement = node 747 .get(_horizontalNodePosition[_currentDepth]); 748 749 if (nodeElement instanceof Firing) { 750 return nodeElement; 751 } else { 752 return _findLeafNode((Schedule) nodeElement); 753 } 754 } else if (node.size() == 0) { 755 Schedule scheduleNode = _backTrack(_currentNode); 756 return _findLeafNode(scheduleNode); 757 } else if (_iterationCounts[_currentDepth] < node 758 .getIterationCount()) { 759 ScheduleElement nodeElement = node.get(0); 760 _currentDepth++; 761 762 if (nodeElement instanceof Firing) { 763 return nodeElement; 764 } else { 765 return _findLeafNode((Schedule) nodeElement); 766 } 767 } else { 768 return null; 769 } 770 } 771 772 /////////////////////////////////////////////////////////////////// 773 //// private variables //// 774 private boolean _advance; 775 776 private ScheduleElement _currentNode; 777 778 private boolean _lastHasNext; 779 780 // The current depth in the schedule tree. 781 private int _currentDepth; 782 783 private long _startingVersion; 784 785 // This array contains the iteration counts of schedule elements 786 // indexed by tree depth. 787 // This array will grow to the depth of the schedule tree. 788 private int[] _iterationCounts; 789 790 private int[] _horizontalNodePosition; 791 } 792 793 /////////////////////////////////////////////////////////////////// 794 //// private methods //// 795 796 /* Get unique firing instances. Repeated firings generally 797 * occur in the iterator returned by 798 * {@link ptolemy.graph.sched.ScheduleElement#firingIterator()}. 799 * This method removes duplications and keeps a list of 800 * unique firings. Though not sure whether it matters for the 801 * sequence, firings returned are in the form of <code>List</code> 802 * rather than un-ordered <code>Collection</code>. 803 * 804 * @return A list of distinct firings. 805 */ 806 private List _getFiringInstancesList() { 807 /* Build _firingInstanceList if it does not exist. This cache 808 * style implementation ensures execution efficiency. 809 */ 810 if (_firingInstancesList == null) { 811 _firingInstancesList = new ArrayList(); 812 813 Iterator originalFirings = firingIterator(); 814 815 while (originalFirings.hasNext()) { 816 Object firing = originalFirings.next(); 817 818 if (!_firingInstancesList.contains(firing)) { 819 _firingInstancesList.add(firing); 820 } 821 } 822 } 823 824 return _firingInstancesList; 825 } 826 827 /* Get the map of firing elements to firings. Multiple firings 828 * can be created for any single element and the firings are 829 * obtained in a <code>List</code>. Therefore, the map returned 830 * is actually a mapping from elements to <code>List</code>s 831 * of firings. 832 * 833 * @return The map from elements to firings. 834 */ 835 private Map _getFiringElementFiringsMap() { 836 /* Build _nodeFiringsMap if it does not exist. This cache 837 * style implementation ensures execution efficiency. 838 */ 839 if (_firingElementFiringsMap == null) { 840 _firingElementFiringsMap = new HashMap(); 841 842 Iterator firingInstances = _getFiringInstancesList().iterator(); 843 844 while (firingInstances.hasNext()) { 845 Firing firing = (Firing) firingInstances.next(); 846 Object node = firing.getFiringElement(); 847 List firingList = null; 848 849 if (!_firingElementFiringsMap.containsKey(node)) { 850 firingList = new ArrayList(); 851 _firingElementFiringsMap.put(node, firingList); 852 } else { 853 firingList = (List) _firingElementFiringsMap.get(node); 854 } 855 856 firingList.add(firing); 857 } 858 } 859 860 return Collections.unmodifiableMap(_firingElementFiringsMap); 861 } 862 863 /////////////////////////////////////////////////////////////////// 864 //// protected variables //// 865 866 /** The list of schedule elements contained by this schedule. */ 867 protected List _schedule; 868 869 /////////////////////////////////////////////////////////////////// 870 //// private variables //// 871 872 // The list of Firings for this schedule. 873 //private List _firingList; 874 // The current version of the firing iterator list. 875 //private long _firingIteratorVersion; 876 // The depth of this schedule tree. This may grow. 877 private int _treeDepth; 878 879 /* A list of all distinct firings. */ 880 private List _firingInstancesList; 881 882 /* A map from firing elements to firings. */ 883 private Map _firingElementFiringsMap; 884}