blob: 2c5b7efc2052b271dc31e97c2a4c9ed91ba71321 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.collections4.iterators;
import java.util.Iterator;
/**
* An LazyIteratorChain is an Iterator that wraps a number of Iterators in a lazy manner.
* <p>
* This class makes multiple iterators look like one to the caller. When any
* method from the Iterator interface is called, the LazyIteratorChain will delegate
* to a single underlying Iterator. The LazyIteratorChain will invoke the Iterators
* in sequence until all Iterators are exhausted.
* <p>
* The Iterators are provided by {@link #nextIterator(int)} which has to be overridden by
* sub-classes and allows to lazily create the Iterators as they are accessed:
* <pre>
* return new LazyIteratorChain&lt;String&gt;() {
* protected Iterator&lt;String&gt; nextIterator(int count) {
* return count == 1 ? Arrays.asList("foo", "bar").iterator() : null;
* }
* };
* </pre>
* <p>
* Once the inner Iterator's {@link Iterator#hasNext()} method returns false,
* {@link #nextIterator(int)} will be called to obtain another iterator, and so on
* until {@link #nextIterator(int)} returns null, indicating that the chain is exhausted.
* <p>
* NOTE: The LazyIteratorChain may contain no iterators. In this case the class will
* function as an empty iterator.
*
* @since 4.0
*/
public abstract class LazyIteratorChain<E> implements Iterator<E> {
/** The number of times {@link #next()} was already called. */
private int callCounter;
/** Indicates that the Iterator chain has been exhausted. */
private boolean chainExhausted;
/** The current iterator. */
private Iterator<? extends E> currentIterator;
/**
* The "last used" Iterator is the Iterator upon which next() or hasNext()
* was most recently called used for the remove() operation only.
*/
private Iterator<? extends E> lastUsedIterator;
//-----------------------------------------------------------------------
/**
* Gets the next iterator after the previous one has been exhausted.
* <p>
* This method <b>MUST</b> return null when there are no more iterators.
*
* @param count the number of time this method has been called (starts with 1)
* @return the next iterator, or null if there are no more.
*/
protected abstract Iterator<? extends E> nextIterator(int count);
/**
* Updates the current iterator field to ensure that the current Iterator
* is not exhausted.
*/
private void updateCurrentIterator() {
if (callCounter == 0) {
currentIterator = nextIterator(++callCounter);
if (currentIterator == null) {
currentIterator = EmptyIterator.<E>emptyIterator();
chainExhausted = true;
}
// 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 && !chainExhausted) {
final Iterator<? extends E> nextIterator = nextIterator(++callCounter);
if (nextIterator != null) {
currentIterator = nextIterator;
} else {
chainExhausted = true;
}
}
}
//-----------------------------------------------------------------------
/**
* Return true if any Iterator in the chain has a remaining element.
*
* @return true if elements remain
*/
@Override
public boolean hasNext() {
updateCurrentIterator();
lastUsedIterator = currentIterator;
return currentIterator.hasNext();
}
/**
* Returns the next element of the current Iterator
*
* @return element from the current Iterator
* @throws java.util.NoSuchElementException if all the Iterators are exhausted
*/
@Override
public E next() {
updateCurrentIterator();
lastUsedIterator = currentIterator;
return currentIterator.next();
}
/**
* Removes from the underlying collection the last element returned by the Iterator.
* <p>
* 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();
}
}