1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.collections15.iterators;
17
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.Iterator;
21 import java.util.List;
22
23 import net.sf.collections15.list.UnmodifiableList;
24
25
26 /***
27 * An <code>IteratorChain</code> is an <code>Iterator</code> that wraps a number
28 * of <code>Iterator</code>s.
29 * <p>
30 * This class makes multiple iterators look like one to the caller
31 * When any method from the <code>Iterator</code> interface is called, the
32 * <code>IteratorChain</code> will delegate to a single underlying
33 * <code>Iterator</code>. The <code>IteratorChain</code> will invoke the
34 * <code>Iterator</code>s in sequence until all <code>Iterator</code>s are
35 * exhausted.
36 * <p>
37 * Under many circumstances, linking <code>Iterator</code>s together in this
38 * manner is more efficient (and convenient) than reading out the contents of
39 * each <code>Iterator</code> into a <code>List</code> and creating a new
40 * <code>Iterator</code>.
41 * <p>
42 * Calling a method that adds new <code>Iterator</code> <i>after a method in the
43 * <code>Iterator<code> interface has been called</i> will result in an
44 * <code>UnsupportedOperationException</code>. Subclasses should <i>take
45 * care</i> to not alter the underlying <code>List</code> of
46 * <code>Iterator</code>s.
47 * <p>
48 * NOTE: As from version 3.0, the IteratorChain may contain no
49 * iterators. In this case the class will function as an empty iterator.
50 *
51 * @since Commons Collections 2.1
52 * @version $Revision: 1.1 $ $Date: 2005/02/27 18:48:37 $
53 *
54 * @author Morgan Delagrange
55 * @author Stephen Colebourne
56 * @author Mauro Franceschini (port to 5.0)
57 */
58 public class IteratorChain<E> implements Iterator<E> {
59
60
61
62 /*** The chain of iterators */
63 protected final List<Iterator<E>> iteratorChain =
64 new ArrayList<Iterator<E>>();
65
66 /*** The index of the current iterator */
67 protected int currentIteratorIndex = 0;
68
69 /*** The current iterator */
70 protected Iterator<E> currentIterator = null;
71
72 /***
73 * The "last used" <code>Iterator</code> is the <code>Iterator</code> upon
74 * which <code>next()</code> or <code>hasNext()</code> was most recently
75 * called used for the <code>remove()</code> operation only.
76 */
77 protected Iterator<E> lastUsedIterator = null;
78
79 /***
80 * <code>IteratorChain</code> is "locked" after the first time
81 * <code>next()</code> is called.
82 */
83 protected boolean isLocked = false;
84
85
86
87
88 /***
89 * Construct an <code>IteratorChain</code> with no <code>Iterator</code>s.
90 * <p>
91 * You will normally use {@link #addIterator(Iterator)} to add some
92 * iterators after using this constructor.
93 */
94 public IteratorChain() {
95 super();
96 }
97
98 /***
99 * Construct an <code>IteratorChain</code> with a list of
100 * <code>Iterator</code>s.
101 *
102 * @param iterators the iterators to add to the <code>IteratorChain</code>
103 *
104 * @throws NullPointerException if one of the provided iterator is
105 * <code>null</code>
106 */
107 public IteratorChain(final Iterator<E>... iterators) {
108 super();
109 for (Iterator<E> iterator : iterators) {
110 addIterator(iterator);
111 }
112 }
113
114 /***
115 * Constructs a new <code>IteratorChain</code> over the collection of
116 * iterators.
117 *
118 * @param iterators the collection of iterators
119 *
120 * @throws NullPointerException if iterators collection is or contains
121 * <code>null</code>
122 */
123 public IteratorChain(final Collection<Iterator<E>> iterators) {
124 for (Iterator<E> iterator : iterators) {
125 addIterator(iterator);
126 }
127 }
128
129
130
131
132 /***
133 * Add an <code>Iterator</code> to the end of the chain.
134 *
135 * @param iterator the <code>Iterator</code> to add
136 *
137 * @throws IllegalStateException if I've already started iterating
138 * @throws NullPointerException if the iterator is <code>null</code>
139 */
140 public void addIterator(final Iterator<E> iterator) {
141 checkLocked();
142 if (iterator == null) {
143 throw new NullPointerException("Iterator must not be null");
144 }
145 iteratorChain.add(iterator);
146 }
147
148 /***
149 * Set the <code>Iterator</code> at the given index
150 *
151 * @param index the index of the <code>Iterator</code> to replace
152 * @param iterator <code>Iterator</code> to place at the given index
153 *
154 * @throws IndexOutOfBoundsException if index < 0 or index > size()
155 * @throws IllegalStateException if I've already started iterating
156 * @throws NullPointerException if the iterator is <code>null</code>
157 */
158 public void setIterator(int index, Iterator<E> iterator)
159 throws IndexOutOfBoundsException {
160 checkLocked();
161 if (iterator == null) {
162 throw new NullPointerException("Iterator must not be null");
163 }
164 iteratorChain.set(index, iterator);
165 }
166
167 /***
168 * Get the list of <code>Iterator</code>s (unmodifiable)
169 *
170 * @return the unmodifiable list of iterators added
171 */
172 public List<Iterator<E>> getIterators() {
173 return UnmodifiableList.decorate(iteratorChain);
174 }
175
176 /***
177 * Number of <code>Iterator</code>s in the current
178 * <code>IteratorChain</code>.
179 *
180 * @return the <code>Iterator</code> count
181 */
182 public int size() {
183 return iteratorChain.size();
184 }
185
186 /***
187 * Determine if modifications can still be made to the
188 * <code>IteratorChain</code>.
189 * <p>
190 * <code>IteratorChain</code>s cannot be modified once they have executed a
191 * method from the <code>Iterator</code> interface.
192 *
193 * @return <code>true</code> if the <code>IteratorChain</code> cannot be
194 * modified, <code>false</code> if it can
195 */
196 public boolean isLocked() {
197 return isLocked;
198 }
199
200 /***
201 * Checks whether the iterator chain is now locked and in use.
202 */
203 private void checkLocked() {
204 if (isLocked == true) {
205 throw new UnsupportedOperationException("IteratorChain cannot be " +
206 "changed after the first use of a method from the " +
207 "Iterator interface");
208 }
209 }
210
211 /***
212 * Lock the chain so no more iterators can be added.
213 * <p>
214 * This must be called from all <code>Iterator</code> interface methods.
215 */
216 private void lockChain() {
217 if (isLocked == false) {
218 isLocked = true;
219 }
220 }
221
222 /***
223 * Updates the current iterator field to ensure that the current
224 * <code>Iterator</code> is not exhausted
225 */
226 protected void updateCurrentIterator() {
227 if (currentIterator == null) {
228 if (iteratorChain.isEmpty()) {
229
230 currentIterator = EmptyIterator.INSTANCE;
231 } else {
232 currentIterator = iteratorChain.get(0);
233 }
234
235
236 lastUsedIterator = currentIterator;
237 }
238
239 while (currentIterator.hasNext() == false &&
240 currentIteratorIndex < iteratorChain.size() - 1) {
241 currentIteratorIndex++;
242 currentIterator = iteratorChain.get(currentIteratorIndex);
243 }
244 }
245
246
247
248
249 /***
250 * Return <code>true</code> if any <code>Iterator</code> in the
251 * <code>IteratorChain</code> has a remaining element.
252 *
253 * @return <code>true</code> if elements remain
254 */
255 public boolean hasNext() {
256 lockChain();
257 updateCurrentIterator();
258 lastUsedIterator = currentIterator;
259
260 return currentIterator.hasNext();
261 }
262
263 /***
264 * Returns the next object of the current <code>Iterator</code>.
265 *
266 * @return object from the current <code>Iterator</code>
267 *
268 * @throws java.util.NoSuchElementException if all the
269 * <code>Iterator</code>s are exhausted
270 */
271 public E next() {
272 lockChain();
273 updateCurrentIterator();
274 lastUsedIterator = currentIterator;
275
276 return currentIterator.next();
277 }
278
279 /***
280 * Removes from the underlying collection the last element
281 * returned by the <code>Iterator</code>.
282 * <p>
283 * As with <code>next()</code> and <code>hasNext()</code>, this method calls
284 * <code>remove()</code> on the underlying <code>Iterator</code>. Therefore,
285 * this method may throw an <code>UnsupportedOperationException</code> if
286 * the underlying <code>Iterator</code> does not support this method.
287 *
288 * @throws UnsupportedOperationException if the remove operator is not
289 * supported by the underlying <code>Iterator</code>
290 * @throws IllegalStateException if the next method has not yet been called,
291 * or the remove method has already been called after the last call to the
292 * next method
293 */
294 public void remove() {
295 lockChain();
296 updateCurrentIterator();
297
298 lastUsedIterator.remove();
299 }
300
301 }