/** * */ package fr.cedrik.util; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import org.apache.commons.io.LineIterator; // Very heavily adapted from commons-collections 3.2.1 /** * An IteratorChain is an Iterator that wraps a number of Iterators. * <p> * This class makes multiple iterators look like one to the caller * When any method from the Iterator interface is called, the IteratorChain * will delegate to a single underlying Iterator. The IteratorChain will * invoke the Iterators in sequence until all Iterators are exhausted. * <p> * Under many circumstances, linking Iterators together in this manner is * more efficient (and convenient) than reading out the contents of each * Iterator into a List and creating a new Iterator. * <p> * NOTE: IteratorChain may contain no * iterators. In this case the class will function as an empty iterator. * * @author Morgan Delagrange * @author Stephen Colebourne * @author Cédrik LIME * @see LineIterator */ public class IteratorChain<E> implements Iterator<E>, Closeable { /** The chain of iterators */ protected final List<Iterator<E>> delegates = new ArrayList<Iterator<E>>(); /** The index of the current iterator */ protected int currentIteratorIndex = 0; /** The current iterator */ protected Iterator<E> currentIterator = null; /** * The "last used" Iterator is the Iterator upon which * next() or hasNext() was most recently called * used for the remove() operation only */ protected Iterator<E> lastUsedIterator = null; /** * Maximum number of elements to return (cap) */ protected long maxElementsCap = Long.MAX_VALUE; /** * Current number of elements served */ protected long count = 0; //----------------------------------------------------------------------- /** * Construct an IteratorChain with no Iterators. */ public IteratorChain() { super(); } /** * Construct an IteratorChain with a first {@code E} and Iterators. * * @param firstElement first {@code E} in the IteratorChain */ public IteratorChain(E firstElement) { super(); List<E> firstElementList = new ArrayList<E>(1); firstElementList.add(firstElement); addIterator(firstElementList.iterator()); } /** * Construct an IteratorChain with a list of {@code E}. * * @param elements first Iterator in the IteratorChain */ public IteratorChain(E... elements) { super(); List<E> elementsList = new ArrayList<E>(elements.length); for (E e : elements) { elementsList.add(e); } addIterator(elementsList.iterator()); } /** * Construct an IteratorChain with a first {@code E} and Iterators. * * @param firstElement first {@code E} in the IteratorChain * @param delegates the array of iterators */ public IteratorChain(E firstElement, Iterator<E>... delegates) { super(); List<E> firstElementList = new ArrayList<E>(1); firstElementList.add(firstElement); addIterator(firstElementList.iterator()); for (Iterator<E> delegate : delegates) { addIterator(delegate); } } /** * Constructs a new <code>IteratorChain</code> over the array * of iterators. * * @param delegates the array of iterators * @throws NullPointerException if iterators array is {@code null} */ public IteratorChain(Iterator<E>... delegates) { super(); for (Iterator<E> delegate : delegates) { addIterator(delegate); } } //----------------------------------------------------------------------- /** * Add an Iterator to the end of the chain * * @param iterator Iterator to add * @throws IllegalStateException if I've already started iterating */ protected void addIterator(Iterator<E> iterator) { if (iterator != null) { delegates.add(iterator); } } /** * Updates the current iterator field to ensure that the current Iterator * is not exhausted */ protected void updateCurrentIterator() { if (currentIterator == null) { if (delegates.isEmpty()) { currentIterator = EmptyIterator.INSTANCE; } else { currentIterator = delegates.get(0); } // set last used iterator here, in case the user calls remove // before calling hasNext() or next() (although they shouldn't) lastUsedIterator = currentIterator; } while (currentIterator.hasNext() == false && currentIteratorIndex < delegates.size() - 1) { ++currentIteratorIndex; currentIterator = delegates.get(currentIteratorIndex); } } public void setMaxElementsCap(long maxElementsCap) { this.maxElementsCap = maxElementsCap; } //----------------------------------------------------------------------- /** * Return true if any Iterator in the IteratorChain has a remaining element. * * @return true if elements remain */ @Override public boolean hasNext() { updateCurrentIterator(); lastUsedIterator = currentIterator; return count < maxElementsCap && currentIterator.hasNext(); } /** * Returns the next Object of the current Iterator * * @return Object from the current Iterator * @throws java.util.NoSuchElementException if all the Iterators are exhausted */ @Override public E next() { updateCurrentIterator(); lastUsedIterator = currentIterator; if (count >= maxElementsCap) { throw new NoSuchElementException("Served " + count + " elements already"); } ++count; return currentIterator.next(); } /** * Removes from the underlying collection the last element * returned by the Iterator. As with next() and hasNext(), * this method calls remove() on the underlying Iterator. * Therefore, this method may throw an * UnsupportedOperationException if the underlying * Iterator does not support this method. * * @throws UnsupportedOperationException * if the remove operator is not supported by the underlying Iterator * @throws IllegalStateException * if the next method has not yet been called, or the remove method has * already been called after the last call to the next method. */ @Override public void remove() { if (currentIterator == null) { updateCurrentIterator(); } lastUsedIterator.remove(); } @Override public void close() throws IOException { for (Iterator<E> delegate : delegates) { if (delegate instanceof Closeable) { ((Closeable)delegate).close(); } else if (delegate instanceof LineIterator) { ((LineIterator)delegate).close(); } } } //----------------------------------------------------------------------- /** * Provides an implementation of an empty iterator. * <p> * This class provides an implementation of an empty iterator. * This class provides for binary compatability between Commons Collections * 2.1.1 and 3.1 due to issues with <code>IteratorUtils</code>. * * @since Commons Collections 2.1.1 and 3.1 * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ * * @author Stephen Colebourne */ public static class EmptyIterator<E> implements Iterator<E> { /** * Singleton instance of the iterator. * @since Commons Collections 2.1.1 and 3.1 */ public static final Iterator INSTANCE = new EmptyIterator(); /** * Constructor. */ protected EmptyIterator() { super(); } @Override public boolean hasNext() { return false; } @Override public E next() { throw new NoSuchElementException("Iterator contains no elements"); } @Override public void remove() { throw new IllegalStateException("Iterator contains no elements"); } } }