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. *

* The implementation attempts to prefetch the output of {@link #next()} in {@link #hasNext()}. If a failure occurs, * {@link #hasNext()}: * *

  • keeps consuming the underlying stream as long as the failure is a {@link StreamSkipSignal}; *
  • 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 the type of stream elements */ public abstract class LookAheadStream implements Stream { 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 null */ 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(); }