package com.tinkerpop.pipes;
import com.tinkerpop.pipes.filter.FilterPipe;
import com.tinkerpop.pipes.sideeffect.SideEffectPipe;
import com.tinkerpop.pipes.transform.TransformPipe;
import com.tinkerpop.pipes.util.PipeHelper;
import com.tinkerpop.pipes.util.iterators.HistoryIterator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
* An AbstractPipe provides most of the functionality that is repeated in every instance of a Pipe.
* Any subclass of AbstractPipe should simply implement processNextStart(). The standard model is
* <pre>
* protected E processNextStart() throws NoSuchElementException {
* S s = this.starts.next();
* E e = // do something with the S to yield an E
* return e;
* }
* </pre>
* If the current incoming S is not to be emitted and there are no other S objects to process and emit, then throw a NoSuchElementException.
*
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
public abstract class AbstractPipe<S, E> implements Pipe<S, E> {
protected Iterator<S> starts;
private E nextEnd;
protected E currentEnd;
private boolean available = false;
protected boolean pathEnabled = false;
public void setStarts(final Iterator<S> starts) {
if (starts instanceof Pipe) {
this.starts = starts;
} else {
this.starts = new HistoryIterator<S>(starts);
}
}
public void setStarts(final Iterable<S> starts) {
this.setStarts(starts.iterator());
}
public void setStarts(final Pipe<?, S> starts) {
this.setStarts(starts.iterator());
}
public void reset() {
if (this.starts instanceof Pipe)
((Pipe) this.starts).reset();
this.nextEnd = null;
this.currentEnd = null;
this.available = false;
}
public List getCurrentPath() {
if (this.pathEnabled) {
final List pathElements = getPathToHere();
if (this instanceof TransformPipe) {
pathElements.add(this.currentEnd);
} else if (!(this instanceof SideEffectPipe) && !(this instanceof FilterPipe)) {
final int size = pathElements.size();
if (size == 0 || pathElements.get(size - 1) != this.currentEnd) {
// do not repeat filters or side-effects as they dup the object
// this is for backwards compatibility to before TransformPipe interface
pathElements.add(this.currentEnd);
}
}
return pathElements;
} else {
throw new RuntimeException(Pipe.NO_PATH_MESSAGE);
}
}
public void remove() {
throw new UnsupportedOperationException();
}
public E next() {
if (this.available) {
this.available = false;
return (this.currentEnd = this.nextEnd);
} else {
return (this.currentEnd = this.processNextStart());
}
}
public boolean hasNext() {
if (this.available)
return true;
else {
try {
this.nextEnd = this.processNextStart();
return (this.available = true);
} catch (final NoSuchElementException e) {
return (this.available = false);
}
}
}
public void enablePath(final boolean enable) {
this.pathEnabled = enable;
if (this.starts instanceof Pipe)
((Pipe) this.starts).enablePath(enable);
}
/**
* The iterator method of Iterable is not faithful to the Java semantics of iterator().
* This method simply returns the pipe itself (which is an iterator) and thus, is useful only for foreach iteration.
*
* @return the pipe from the perspective of an iterator
*/
public Iterator<E> iterator() {
return this;
}
public String toString() {
return PipeHelper.makePipeString(this);
}
protected abstract E processNextStart() throws NoSuchElementException;
protected List getPathToHere() {
if (this.starts instanceof Pipe) {
return ((Pipe) this.starts).getCurrentPath();
} else if (this.starts instanceof HistoryIterator) {
final List list = new ArrayList();
list.add(((HistoryIterator) this.starts).getLast());
return list;
} else {
return new ArrayList();
}
}
}