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
84 if ((currentIndex + 1) < currentSiblings.size()) {
85 return true;
86 }
87
88
89 boolean foundFurtherNodes = false;
90
91
92
93
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
113
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
122
123 Object returnValue = (Object) currentSiblings.get(currentIndex);
124
125
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 }