142 lines
3.0 KiB
Java
142 lines
3.0 KiB
Java
package org.gcube.data.streams;
|
|
|
|
import java.util.NoSuchElementException;
|
|
|
|
import org.gcube.data.streams.exceptions.StreamSkipSignal;
|
|
import org.gcube.data.streams.exceptions.StreamStopSignal;
|
|
import org.gcube.data.streams.handlers.FaultHandler;
|
|
import org.gcube.data.streams.handlers.RethrowHandler;
|
|
|
|
/**
|
|
* Partial {@link Stream} implementation based on look-ahead operations over an underlying stream.
|
|
* <p>
|
|
* The implementation attempts to prefetch the output of {@link #next()} in {@link #hasNext()}. If a failure occurs,
|
|
* {@link #hasNext()}:
|
|
*
|
|
* <li>keeps consuming the underlying stream as long as the failure is a {@link StreamSkipSignal};
|
|
* <li>consults a {@link FaultHandler} for all the other failures. If the {@link FaultHandler} re-throws the same or a
|
|
* different exception, the implementation throws it at the following {@link #next()}.
|
|
*
|
|
* @author Fabio Simeoni
|
|
*
|
|
* @param <E> the type of stream elements
|
|
*/
|
|
public abstract class LookAheadStream<E> implements Stream<E> {
|
|
|
|
private FaultHandler handler = new RethrowHandler();
|
|
|
|
// iteration state
|
|
protected Boolean hasNext;
|
|
protected E element;
|
|
private RuntimeException failure;
|
|
|
|
/**
|
|
* Sets the {@link FaultHandler} for the iteration
|
|
*
|
|
* @param handler the handler
|
|
* @throws IllegalArgumentException if the handler is <code>null</code>
|
|
*/
|
|
public void setHandler(FaultHandler handler) throws IllegalArgumentException {
|
|
|
|
if (handler == null)
|
|
throw new IllegalArgumentException("invalid null handler");
|
|
|
|
this.handler = handler;
|
|
}
|
|
|
|
@Override
|
|
public final boolean hasNext() {
|
|
|
|
if (hasNext==null)
|
|
hasNext = lookAhead();
|
|
|
|
//auto-close
|
|
if (!hasNext)
|
|
close();
|
|
|
|
return hasNext;
|
|
}
|
|
|
|
private boolean lookAhead() {
|
|
|
|
if (!delegateHasNext())
|
|
return false;
|
|
|
|
try {
|
|
this.element = delegateNext();
|
|
return true;
|
|
}
|
|
catch (RuntimeException failure) {
|
|
|
|
try {
|
|
handler.handle(failure);
|
|
return lookAhead();
|
|
}
|
|
catch(StreamSkipSignal skip) {
|
|
return lookAhead();
|
|
}
|
|
catch(StreamStopSignal stop) {
|
|
return false;
|
|
}
|
|
catch(RuntimeException rethrownUnchecked) {
|
|
this.failure=rethrownUnchecked;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
@Override
|
|
public final E next() {
|
|
|
|
try {
|
|
throwLookAheadFailureIfAny();
|
|
return lookedAheadElementOrGetItNow();
|
|
}
|
|
finally {
|
|
cleanIterationState();
|
|
}
|
|
}
|
|
|
|
private void throwLookAheadFailureIfAny() {
|
|
if (failure != null)
|
|
throw failure;
|
|
}
|
|
|
|
private E lookedAheadElementOrGetItNow() {
|
|
|
|
if (element == null)
|
|
if (hasNext())
|
|
return next();
|
|
else
|
|
throw new NoSuchElementException();
|
|
|
|
return element;
|
|
|
|
}
|
|
|
|
private void cleanIterationState() {
|
|
failure=null;
|
|
element=null;
|
|
hasNext = null;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Returns an element of the underlying stream
|
|
*
|
|
* @return the element
|
|
*/
|
|
protected abstract E delegateNext();
|
|
|
|
/**
|
|
* Returns {@code true} if the underlying stream has more elements.
|
|
*
|
|
* @return {@code true} if the underlying stream has more elements
|
|
*/
|
|
protected abstract boolean delegateHasNext();
|
|
}
|