001: /*
002: * Copyright 1999-2005 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.collections.iterators;
017:
018: import java.util.ArrayList;
019: import java.util.Collection;
020: import java.util.Iterator;
021: import java.util.List;
022:
023: import org.apache.commons.collections.list.UnmodifiableList;
024:
025: /**
026: * An IteratorChain is an Iterator that wraps a number of Iterators.
027: * <p>
028: * This class makes multiple iterators look like one to the caller
029: * When any method from the Iterator interface is called, the IteratorChain
030: * will delegate to a single underlying Iterator. The IteratorChain will
031: * invoke the Iterators in sequence until all Iterators are exhausted.
032: * <p>
033: * Under many circumstances, linking Iterators together in this manner is
034: * more efficient (and convenient) than reading out the contents of each
035: * Iterator into a List and creating a new Iterator.
036: * <p>
037: * Calling a method that adds new Iterator<i>after a method in the Iterator
038: * interface has been called</i> will result in an UnsupportedOperationException.
039: * Subclasses should <i>take care</i> to not alter the underlying List of Iterators.
040: * <p>
041: * NOTE: As from version 3.0, the IteratorChain may contain no
042: * iterators. In this case the class will function as an empty iterator.
043: *
044: * @since Commons Collections 2.1
045: * @version $Revision: 171347 $ $Date: 2005-05-22 18:27:34 +0100 (Sun, 22 May 2005) $
046: *
047: * @author Morgan Delagrange
048: * @author Stephen Colebourne
049: */
050: public class IteratorChain implements Iterator {
051:
052: /** The chain of iterators */
053: protected final List iteratorChain = new ArrayList();
054: /** The index of the current iterator */
055: protected int currentIteratorIndex = 0;
056: /** The current iterator */
057: protected Iterator currentIterator = null;
058: /**
059: * The "last used" Iterator is the Iterator upon which
060: * next() or hasNext() was most recently called
061: * used for the remove() operation only
062: */
063: protected Iterator lastUsedIterator = null;
064: /**
065: * ComparatorChain is "locked" after the first time
066: * compare(Object,Object) is called
067: */
068: protected boolean isLocked = false;
069:
070: //-----------------------------------------------------------------------
071: /**
072: * Construct an IteratorChain with no Iterators.
073: * <p>
074: * You will normally use {@link #addIterator(Iterator)} to add
075: * some iterators after using this constructor.
076: */
077: public IteratorChain() {
078: super ();
079: }
080:
081: /**
082: * Construct an IteratorChain with a single Iterator.
083: *
084: * @param iterator first Iterator in the IteratorChain
085: * @throws NullPointerException if the iterator is null
086: */
087: public IteratorChain(Iterator iterator) {
088: super ();
089: addIterator(iterator);
090: }
091:
092: /**
093: * Constructs a new <code>IteratorChain</code> over the two
094: * given iterators.
095: *
096: * @param a the first child iterator
097: * @param b the second child iterator
098: * @throws NullPointerException if either iterator is null
099: */
100: public IteratorChain(Iterator a, Iterator b) {
101: super ();
102: addIterator(a);
103: addIterator(b);
104: }
105:
106: /**
107: * Constructs a new <code>IteratorChain</code> over the array
108: * of iterators.
109: *
110: * @param iterators the array of iterators
111: * @throws NullPointerException if iterators array is or contains null
112: */
113: public IteratorChain(Iterator[] iterators) {
114: super ();
115: for (int i = 0; i < iterators.length; i++) {
116: addIterator(iterators[i]);
117: }
118: }
119:
120: /**
121: * Constructs a new <code>IteratorChain</code> over the collection
122: * of iterators.
123: *
124: * @param iterators the collection of iterators
125: * @throws NullPointerException if iterators collection is or contains null
126: * @throws ClassCastException if iterators collection doesn't contain an iterator
127: */
128: public IteratorChain(Collection iterators) {
129: super ();
130: for (Iterator it = iterators.iterator(); it.hasNext();) {
131: Iterator item = (Iterator) it.next();
132: addIterator(item);
133: }
134: }
135:
136: //-----------------------------------------------------------------------
137: /**
138: * Add an Iterator to the end of the chain
139: *
140: * @param iterator Iterator to add
141: * @throws IllegalStateException if I've already started iterating
142: * @throws NullPointerException if the iterator is null
143: */
144: public void addIterator(Iterator iterator) {
145: checkLocked();
146: if (iterator == null) {
147: throw new NullPointerException("Iterator must not be null");
148: }
149: iteratorChain.add(iterator);
150: }
151:
152: /**
153: * Set the Iterator at the given index
154: *
155: * @param index index of the Iterator to replace
156: * @param iterator Iterator to place at the given index
157: * @throws IndexOutOfBoundsException if index < 0 or index > size()
158: * @throws IllegalStateException if I've already started iterating
159: * @throws NullPointerException if the iterator is null
160: */
161: public void setIterator(int index, Iterator iterator)
162: throws IndexOutOfBoundsException {
163: checkLocked();
164: if (iterator == null) {
165: throw new NullPointerException("Iterator must not be null");
166: }
167: iteratorChain.set(index, iterator);
168: }
169:
170: /**
171: * Get the list of Iterators (unmodifiable)
172: *
173: * @return the unmodifiable list of iterators added
174: */
175: public List getIterators() {
176: return UnmodifiableList.decorate(iteratorChain);
177: }
178:
179: /**
180: * Number of Iterators in the current IteratorChain.
181: *
182: * @return Iterator count
183: */
184: public int size() {
185: return iteratorChain.size();
186: }
187:
188: /**
189: * Determine if modifications can still be made to the IteratorChain.
190: * IteratorChains cannot be modified once they have executed a method
191: * from the Iterator interface.
192: *
193: * @return true if IteratorChain cannot be modified, false if it can
194: */
195: public boolean isLocked() {
196: return isLocked;
197: }
198:
199: /**
200: * Checks whether the iterator chain is now locked and in use.
201: */
202: private void checkLocked() {
203: if (isLocked == true) {
204: throw new UnsupportedOperationException(
205: "IteratorChain cannot be changed after the first use of a method from the Iterator interface");
206: }
207: }
208:
209: /**
210: * Lock the chain so no more iterators can be added.
211: * This must be called from all Iterator interface methods.
212: */
213: private void lockChain() {
214: if (isLocked == false) {
215: isLocked = true;
216: }
217: }
218:
219: /**
220: * Updates the current iterator field to ensure that the current Iterator
221: * is not exhausted
222: */
223: protected void updateCurrentIterator() {
224: if (currentIterator == null) {
225: if (iteratorChain.isEmpty()) {
226: currentIterator = EmptyIterator.INSTANCE;
227: } else {
228: currentIterator = (Iterator) iteratorChain.get(0);
229: }
230: // set last used iterator here, in case the user calls remove
231: // before calling hasNext() or next() (although they shouldn't)
232: lastUsedIterator = currentIterator;
233: }
234:
235: while (currentIterator.hasNext() == false
236: && currentIteratorIndex < iteratorChain.size() - 1) {
237: currentIteratorIndex++;
238: currentIterator = (Iterator) iteratorChain
239: .get(currentIteratorIndex);
240: }
241: }
242:
243: //-----------------------------------------------------------------------
244: /**
245: * Return true if any Iterator in the IteratorChain has a remaining element.
246: *
247: * @return true if elements remain
248: */
249: public boolean hasNext() {
250: lockChain();
251: updateCurrentIterator();
252: lastUsedIterator = currentIterator;
253:
254: return currentIterator.hasNext();
255: }
256:
257: /**
258: * Returns the next Object of the current Iterator
259: *
260: * @return Object from the current Iterator
261: * @throws java.util.NoSuchElementException if all the Iterators are exhausted
262: */
263: public Object next() {
264: lockChain();
265: updateCurrentIterator();
266: lastUsedIterator = currentIterator;
267:
268: return currentIterator.next();
269: }
270:
271: /**
272: * Removes from the underlying collection the last element
273: * returned by the Iterator. As with next() and hasNext(),
274: * this method calls remove() on the underlying Iterator.
275: * Therefore, this method may throw an
276: * UnsupportedOperationException if the underlying
277: * Iterator does not support this method.
278: *
279: * @throws UnsupportedOperationException
280: * if the remove operator is not supported by the underlying Iterator
281: * @throws IllegalStateException
282: * if the next method has not yet been called, or the remove method has
283: * already been called after the last call to the next method.
284: */
285: public void remove() {
286: lockChain();
287: if (currentIterator == null) {
288: updateCurrentIterator();
289: }
290: lastUsedIterator.remove();
291: }
292:
293: }
|