001: /*
002: * Copyright 2005 JBoss Inc
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:
017: package org.drools.util;
018:
019: import java.util.ArrayList;
020: import java.util.Collection;
021: import java.util.Collections;
022: import java.util.Iterator;
023: import java.util.List;
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 When any
029: * method from the Iterator interface is called, the IteratorChain will delegate
030: * to a single underlying Iterator. The IteratorChain will invoke the Iterators
031: * in sequence until all Iterators are exhausted.
032: * <p>
033: * Under many circumstances, linking Iterators together in this manner is more
034: * efficient (and convenient) than reading out the contents of each Iterator
035: * 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
039: * UnsupportedOperationException. Subclasses should <i>take care </i> to not
040: * alter the underlying List of Iterators.
041: * <p>
042: * NOTE: As from version 3.0, the IteratorChain may contain no iterators. In
043: * this case the class will function as an empty iterator.
044: *
045: * @since Commons Collections 2.1
046: * @version $Revision: 1.1 $ $Date: 2005/07/26 01:06:32 $
047: *
048: * @author Morgan Delagrange
049: * @author Stephen Colebourne
050: */
051: public class IteratorChain implements Iterator {
052:
053: /** The chain of iterators */
054: protected final List iteratorChain = new ArrayList();
055: /** The index of the current iterator */
056: protected int currentIteratorIndex = 0;
057: /** The current iterator */
058: protected Iterator currentIterator = null;
059: /**
060: * The "last used" Iterator is the Iterator upon which next() or hasNext()
061: * was most recently called used for the remove() operation only
062: */
063: protected Iterator lastUsedIterator = null;
064: /**
065: * ComparatorChain is "locked" after the first time compare(Object,Object)
066: * 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 some
075: * 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
085: * first Iterator in the IteratorChain
086: * @throws NullPointerException
087: * if the iterator is null
088: */
089: public IteratorChain(final Iterator iterator) {
090: super ();
091: addIterator(iterator);
092: }
093:
094: /**
095: * Constructs a new <code>IteratorChain</code> over the two given
096: * iterators.
097: *
098: * @param a
099: * the first child iterator
100: * @param b
101: * the second child iterator
102: * @throws NullPointerException
103: * if either iterator is null
104: */
105: public IteratorChain(final Iterator a, final Iterator b) {
106: super ();
107: addIterator(a);
108: addIterator(b);
109: }
110:
111: /**
112: * Constructs a new <code>IteratorChain</code> over the array of
113: * iterators.
114: *
115: * @param iterators
116: * the array of iterators
117: * @throws NullPointerException
118: * if iterators array is or contains null
119: */
120: public IteratorChain(final Iterator[] iterators) {
121: super ();
122: for (int i = 0; i < iterators.length; i++) {
123: addIterator(iterators[i]);
124: }
125: }
126:
127: /**
128: * Constructs a new <code>IteratorChain</code> over the collection of
129: * iterators.
130: *
131: * @param iterators
132: * the collection of iterators
133: * @throws NullPointerException
134: * if iterators collection is or contains null
135: * @throws ClassCastException
136: * if iterators collection doesn't contain an iterator
137: */
138: public IteratorChain(final Collection iterators) {
139: super ();
140: for (final Iterator it = iterators.iterator(); it.hasNext();) {
141: final Iterator item = (Iterator) it.next();
142: addIterator(item);
143: }
144: }
145:
146: // -----------------------------------------------------------------------
147: /**
148: * Add an Iterator to the end of the chain
149: *
150: * @param iterator
151: * Iterator to add
152: * @throws IllegalStateException
153: * if I've already started iterating
154: * @throws NullPointerException
155: * if the iterator is null
156: */
157: public void addIterator(final Iterator iterator) {
158: checkLocked();
159: if (iterator == null) {
160: throw new NullPointerException("Iterator must not be null");
161: }
162: this .iteratorChain.add(iterator);
163: }
164:
165: /**
166: * Set the Iterator at the given index
167: *
168: * @param index
169: * index of the Iterator to replace
170: * @param iterator
171: * Iterator to place at the given index
172: * @throws IndexOutOfBoundsException
173: * if index < 0 or index > size()
174: * @throws IllegalStateException
175: * if I've already started iterating
176: * @throws NullPointerException
177: * if the iterator is null
178: */
179: public void setIterator(final int index, final Iterator iterator)
180: throws IndexOutOfBoundsException {
181: checkLocked();
182: if (iterator == null) {
183: throw new NullPointerException("Iterator must not be null");
184: }
185: this .iteratorChain.set(index, iterator);
186: }
187:
188: /**
189: * Get the list of Iterators (unmodifiable)
190: *
191: * @return the unmodifiable list of iterators added
192: */
193: public List getIterators() {
194: return Collections.unmodifiableList(this .iteratorChain);
195: }
196:
197: /**
198: * Number of Iterators in the current IteratorChain.
199: *
200: * @return Iterator count
201: */
202: public int size() {
203: return this .iteratorChain.size();
204: }
205:
206: /**
207: * Determine if modifications can still be made to the IteratorChain.
208: * IteratorChains cannot be modified once they have executed a method from
209: * the Iterator interface.
210: *
211: * @return true if IteratorChain cannot be modified, false if it can
212: */
213: public boolean isLocked() {
214: return this .isLocked;
215: }
216:
217: /**
218: * Checks whether the iterator chain is now locked and in use.
219: */
220: private void checkLocked() {
221: if (this .isLocked == true) {
222: throw new UnsupportedOperationException(
223: "IteratorChain cannot be changed after the first use of a method from the Iterator interface");
224: }
225: }
226:
227: /**
228: * Lock the chain so no more iterators can be added. This must be called
229: * from all Iterator interface methods.
230: */
231: private void lockChain() {
232: if (this .isLocked == false) {
233: this .isLocked = true;
234: }
235: }
236:
237: /**
238: * Updates the current iterator field to ensure that the current Iterator is
239: * not exhausted
240: */
241: protected void updateCurrentIterator() {
242: if (this .currentIterator == null) {
243: if (this .iteratorChain.isEmpty()) {
244: this .currentIterator = Collections.EMPTY_LIST
245: .iterator();
246: } else {
247: this .currentIterator = (Iterator) this .iteratorChain
248: .get(0);
249: }
250: // set last used iterator here, in case the user calls remove
251: // before calling hasNext() or next() (although they shouldn't)
252: this .lastUsedIterator = this .currentIterator;
253: }
254:
255: while (this .currentIterator.hasNext() == false
256: && this .currentIteratorIndex < this .iteratorChain
257: .size() - 1) {
258: this .currentIteratorIndex++;
259: this .currentIterator = (Iterator) this .iteratorChain
260: .get(this .currentIteratorIndex);
261: }
262: }
263:
264: // -----------------------------------------------------------------------
265: /**
266: * Return true if any Iterator in the IteratorChain has a remaining element.
267: *
268: * @return true if elements remain
269: */
270: public boolean hasNext() {
271: lockChain();
272: updateCurrentIterator();
273: this .lastUsedIterator = this .currentIterator;
274:
275: return this .currentIterator.hasNext();
276: }
277:
278: /**
279: * Returns the next Object of the current Iterator
280: *
281: * @return Object from the current Iterator
282: * @throws java.util.NoSuchElementException
283: * if all the Iterators are exhausted
284: */
285: public Object next() {
286: lockChain();
287: updateCurrentIterator();
288: this .lastUsedIterator = this .currentIterator;
289:
290: return this .currentIterator.next();
291: }
292:
293: /**
294: * Removes from the underlying collection the last element returned by the
295: * Iterator. As with next() and hasNext(), this method calls remove() on the
296: * underlying Iterator. Therefore, this method may throw an
297: * UnsupportedOperationException if the underlying Iterator does not support
298: * this method.
299: *
300: * @throws UnsupportedOperationException
301: * if the remove operator is not supported by the underlying
302: * Iterator
303: * @throws IllegalStateException
304: * if the next method has not yet been called, or the remove
305: * method has already been called after the last call to the
306: * next method.
307: */
308: public void remove() {
309: lockChain();
310: updateCurrentIterator();
311:
312: this.lastUsedIterator.remove();
313: }
314:
315: }
|