Created
April 16, 2012 17:50
-
-
Save pettermahlen/2400295 to your computer and use it in GitHub Desktop.
AbstractThrowingIterator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public abstract class AbstractThrowingIterator<T> extends UnmodifiableIterator<T> { | |
private State state = State.NOT_READY; | |
/** Constructor for use by subclasses. */ | |
protected AbstractThrowingIterator() {} | |
private enum State { | |
/** We have computed the next element and haven't returned it yet. */ | |
READY, | |
/** We haven't yet computed or have already returned the element. */ | |
NOT_READY, | |
/** The current entry is 'bad', but there's a chance that future entries may be fine */ | |
BAD_ENTRY, | |
/** We have reached the end of the data and are finished. */ | |
DONE, | |
/** We've suffered an exception and are kaput. */ | |
FAILED, | |
} | |
private T next; | |
private BadEntryException nextException; | |
/** | |
* Returns the next element. <b>Note:</b> the implementation must call {@link | |
* #endOfData()} when there are no elements left in the iteration. Failure to | |
* do so could result in an infinite loop. | |
* | |
* <p>The initial invocation of {@link #hasNext()} or {@link #next()} calls | |
* this method, as does the first invocation of {@code hasNext} or {@code | |
* next} following each successful call to {@code next}. Once the | |
* implementation either invokes {@code endOfData} or throws an exception, | |
* {@code computeNext} is guaranteed to never be called again. | |
* | |
* <p>If this method throws an exception, it will propagate outward to the | |
* {@code hasNext} or {@code next} invocation that invoked this method. Any | |
* further attempts to use the iterator will result in an {@link | |
* IllegalStateException}. | |
* | |
* <p>The implementation of this method may not invoke the {@code hasNext} or | |
* {@code next} methods on this instance; if it does, an | |
* {@code IllegalStateException} will result. | |
* | |
* @return the next element if there was one. If {@code endOfData} was called | |
* during execution, the return value will be ignored. | |
* @throws RuntimeException if any unrecoverable error happens. This exception | |
* will propagate outward to the {@code hasNext()}, {@code next()}, or | |
* {@code peek()} invocation that invoked this method. Any further | |
* attempts to use the iterator will result in an | |
* {@link IllegalStateException}. | |
* @throws BadEntryException if the implementation detects a situation where the | |
* current entry is 'bad' in some sense, but further iteration may still succeed. | |
*/ | |
protected abstract T computeNext() throws BadEntryException; | |
/** | |
* Implementations of {@link #computeNext} <b>must</b> invoke this method when | |
* there are no elements left in the iteration. | |
* | |
* @return {@code null}; a convenience so your {@code computeNext} | |
* implementation can use the simple statement {@code return endOfData();} | |
*/ | |
protected final T endOfData() { | |
state = State.DONE; | |
return null; | |
} | |
@Override | |
public final boolean hasNext() { | |
checkState(state != State.FAILED); | |
switch (state) { | |
case DONE: | |
return false; | |
case READY: | |
case BAD_ENTRY: | |
return true; | |
default: | |
} | |
return tryToComputeNext(); | |
} | |
private boolean tryToComputeNext() { | |
state = State.FAILED; // temporary pessimism | |
try { | |
next = computeNext(); | |
} | |
catch (BadEntryException e) { | |
state = State.BAD_ENTRY; | |
nextException = e; | |
return true; | |
} | |
if (state != State.DONE) { | |
state = State.READY; | |
return true; | |
} | |
return false; | |
} | |
@Override | |
public final T next() { | |
if (!hasNext()) { | |
throw new NoSuchElementException(); | |
} | |
State oldState = state; | |
state = State.NOT_READY; | |
switch (oldState) { | |
case READY: | |
return next; | |
case BAD_ENTRY: | |
throw new IteratorException(nextException); | |
default: | |
throw new IllegalStateException("should never get here"); | |
} | |
} | |
} | |
public class BadEntryException | |
extends Exception { | |
public BadEntryException(String message, Throwable cause) { | |
super(message, cause); | |
} | |
} | |
public class IteratorException extends RuntimeException { | |
public IteratorException(Throwable cause) { | |
super(cause); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment