View Javadoc

1   package com.sri.emo.dbobj.model_tree;
2   
3   import java.util.ArrayList;
4   import java.util.Iterator;
5   import java.util.List;
6   import java.util.Stack;
7   
8   
9   /***
10   * Implementation of Iterator that allows depth first iteratation over
11   * all the nodes in a model.  This iterator does not allow for removal
12   * of individual nodes: <em>it is read only</em>.
13   *
14   * @author Michael Rimov
15   */
16  public class ModelDepthFirstIterator implements Iterator {
17  
18      /***
19       * Stack for allowing backtrace of nodes
20       */
21      private final Stack backtrace;
22  
23      /***
24       * Current index in the stack
25       */
26      private int currentIndex;
27  
28  
29      /***
30       * The children of the node we're iterating through.
31       */
32      private List currentSiblings;
33  
34  
35      /***
36       * Instance of the listener.
37       */
38      private final TreeTraversalListener listener;
39  
40      /***
41       * Constructor that takes the root of what it is suppsoed to iterate.
42       *
43       * @param root ModelCompositeNode
44       */
45      public ModelDepthFirstIterator(final ModelNode root) {
46          backtrace = new Stack();
47          currentIndex = -1;
48          currentSiblings = new ArrayList(1);
49          currentSiblings.add(root);
50          listener = TreeTraversalListener.NULL_LISTENER;
51      }
52  
53  
54      /***
55       * Constructor that takes a listener as well as a root of what it is supposed
56       * to iterate.
57       *
58       * @param root     ModelNode the root composite node that we're iterating
59       *                 in a depth first fashion.
60       * @param listener TreeTraversalListener the notification sink that receives
61       *                 notifications of traversal down or up the tree.  This iterator is
62       *                 guaranteed to only traverse down one level at a time, although it may
63       *                 traverse up several levels.
64       */
65      public ModelDepthFirstIterator(final ModelNode root, final TreeTraversalListener listener) {
66          assert root != null;
67          assert listener != null;
68  
69          backtrace = new Stack();
70          currentIndex = -1;
71          currentSiblings = new ArrayList(1);
72          currentSiblings.add(root);
73          this.listener = listener;
74      }
75  
76      /***
77       * Returns <tt>true</tt> if the iteration has more elements.
78       *
79       * @return <tt>true</tt> if the iterator has more elements.
80       */
81      public boolean hasNext() {
82  
83          //If there are more children on this level the we're set.
84          if ((currentIndex + 1) < currentSiblings.size()) {
85              return true;
86          }
87  
88          //Otherwise we have to trace
89          boolean foundFurtherNodes = false;
90  
91          // It doesn't matter what order we search the stack (first in our last in)
92          // because all we need to know is 'are there more nodes somewhere to
93          // iterate?'
94          for (Iterator i = backtrace.iterator(); (i.hasNext() && !foundFurtherNodes);) {
95              StackStatus oneStatus = (StackStatus) i.next();
96              if ((oneStatus.getChildIndex() + 1) < oneStatus.getNode().size()) {
97                  foundFurtherNodes = true;
98              }
99          }
100 
101         return foundFurtherNodes;
102     }
103 
104     /***
105      * Returns the next element in the iteration.
106      *
107      * @return the next element in the iteration.
108      */
109     public Object next() {
110         currentIndex++;
111 
112         //If we are at the end of children array, then we need to pop
113         //up the hierarchy and resume where we left off.
114         while (currentIndex >= currentSiblings.size()) {
115             boolean hasParents = popState();
116             if (!hasParents) {
117                 throw new ArrayIndexOutOfBoundsException("Iterated past end of model");
118             }
119         }
120 
121         //Get the current value to print, and then push down to its children
122         //to iterate them.
123         Object returnValue = (Object) currentSiblings.get(currentIndex);
124 
125         //Now, depth first traverse the tree.
126         pushState();
127 
128         return returnValue;
129     }
130 
131     /***
132      * Removes from the underlying collection the last element returned by
133      * the iterator (optional operation).
134      */
135     public void remove() {
136         throw new java.lang.UnsupportedOperationException("This iterator: " + getClass()
137                 + " is read only and does not support removal");
138     }
139 
140     /***
141      * Pops the stack and resets state to the previous 'level' in the tree
142      * hierarchy.
143      *
144      * @return boolean if the stack was not empty.  If popStack() returns false,
145      *         then hasNext() should return false.
146      */
147     private boolean popState() {
148         if (backtrace.isEmpty()) {
149             return false;
150         } else {
151             StackStatus previousState = (StackStatus) backtrace.pop();
152             listener.ascendModelTree((ModelNode) previousState.getNode().get(previousState.getChildIndex()));
153             currentIndex = (previousState.getChildIndex() + 1);
154             currentSiblings = previousState.getNode();
155             return true;
156         }
157     }
158 
159     /***
160      * Pushes the current state onto the 'call stack' for later
161      * return and reiteration.
162      */
163     private void pushState() {
164         backtrace.push(new StackStatus(currentSiblings, currentIndex));
165         listener.descendModelTree((ModelNode) currentSiblings.get(currentIndex));
166         currentSiblings = ((ModelNode) currentSiblings.get(currentIndex)).getChildren();
167         currentIndex = -1;
168     }
169 
170     /***
171      * Class for allow state to be saved when traversing the various nodes.  It
172      * saves the current node as well as what child we were iterating through
173      * before we 'recursively' descended into the children of the tree.
174      *
175      * @author Michael Rimov
176      */
177     private static class StackStatus {
178         private final List node;
179         private final int child;
180 
181         StackStatus(final List currentList, final int currentChild) {
182             node = currentList;
183             child = currentChild;
184         }
185 
186         List getNode() {
187             return node;
188         }
189 
190         int getChildIndex() {
191             return child;
192         }
193     }
194 }