package com.tinkerpop.pipes.util;
import com.tinkerpop.pipes.Pipe;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* A Pipeline is a linear composite of Pipes.
* Pipeline takes a List of Pipes and joins them according to their order as specified by their location in the List.
* It is important to ensure that the provided ordered Pipes can connect together.
* That is, that the output type of the n-1 Pipe is the same as the input type of the n Pipe.
* Once all provided Pipes are composed, a Pipeline can be treated like any other Pipe.
*
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
public class Pipeline<S, E> implements Pipe<S, E>, MetaPipe {
protected Pipe<S, ?> startPipe;
protected Pipe<?, E> endPipe;
protected List<Pipe> pipes;
protected Iterator<S> starts;
protected boolean pathEnabled = false;
public Pipeline() {
this.pipes = new ArrayList<Pipe>();
}
/**
* Constructs a pipeline from the provided pipes. The ordered list determines how the pipes will be chained together.
* When the pipes are chained together, the start of pipe n is the end of pipe n-1.
*
* @param pipes the ordered list of pipes to chain together into a pipeline
*/
public Pipeline(final List<Pipe> pipes) {
this.pipes = pipes;
this.setPipes(pipes);
}
/**
* Constructs a pipeline from the provided pipes. The ordered array determines how the pipes will be chained together.
* When the pipes are chained together, the start of pipe n is the end of pipe n-1.
*
* @param pipes the ordered array of pipes to chain together into a pipeline
*/
public Pipeline(final Pipe... pipes) {
this(new ArrayList<Pipe>(Arrays.asList(pipes)));
}
/**
* Useful for constructing the pipeline chain without making use of the constructor.
*
* @param pipes the ordered list of pipes to chain together into a pipeline
*/
protected void setPipes(final List<Pipe> pipes) {
final int pipelineLength = pipes.size();
this.startPipe = (Pipe<S, ?>) pipes.get(0);
this.endPipe = (Pipe<?, E>) pipes.get(pipelineLength - 1);
for (int i = 1; i < pipelineLength; i++) {
pipes.get(i).setStarts((Iterator) pipes.get(i - 1));
}
}
/**
* Adds a new pipe to the end of the pipeline and then reconstructs the pipeline chain.
*
* @param pipe the new pipe to add to the pipeline
*/
public void addPipe(final Pipe pipe) {
this.pipes.add(pipe);
this.setPipes(this.pipes);
}
public void addPipe(final int location, final Pipe pipe) {
this.pipes.add(location, pipe);
this.setPipes(this.pipes);
}
public void setStarts(final Iterator<S> starts) {
this.starts = starts;
this.startPipe.setStarts(starts);
}
public void setStarts(final Iterable<S> starts) {
this.setStarts(starts.iterator());
}
/**
* An unsupported operation that throws an UnsupportedOperationException.
*/
public void remove() {
throw new UnsupportedOperationException();
}
/**
* Determines if there is another object that can be emitted from the pipeline.
*
* @return true if an object can be next()'d out of the pipeline
*/
public boolean hasNext() {
return this.endPipe.hasNext();
}
/**
* Get the next object emitted from the pipeline.
* If no such object exists, then a NoSuchElementException is thrown.
*
* @return the next emitted object
*/
public E next() {
return this.endPipe.next();
}
public List getCurrentPath() {
if (this.pathEnabled)
return this.endPipe.getCurrentPath();
else
throw new RuntimeException(Pipe.NO_PATH_MESSAGE);
}
public void enablePath(final boolean enable) {
this.pathEnabled = enable;
this.endPipe.enablePath(enable);
}
/**
* Get the number of pipes in the pipeline.
*
* @return the pipeline length
*/
public int size() {
return this.pipes.size();
}
public void reset() {
this.endPipe.reset();
}
/**
* Simply returns this as as a pipeline (more specifically, pipe) implements Iterator.
*
* @return returns the iterator representation of this pipeline
*/
public Iterator<E> iterator() {
return this;
}
/**
* Returns the current pipeline with a new end type.
* Useful if the end type of the pipeline cannot be implicitly derived.
*
* @return returns the current pipeline with the new end type.
*/
public <E> Pipeline<S, E> cast(Class<E> end) {
return (Pipeline<S, E>) this;
}
public String toString() {
return this.pipes.toString();
}
public List<Pipe> getPipes() {
return this.pipes;
}
public Iterator<S> getStarts() {
return this.starts;
}
public Pipe remove(final int index) {
return this.pipes.remove(index);
}
public Pipe get(final int index) {
return this.pipes.get(index);
}
public boolean equals(final Object object) {
return (object instanceof Pipeline) && PipeHelper.areEqual(this, (Pipeline) object);
}
public long count() {
return PipeHelper.counter(this);
}
public void iterate() {
PipeHelper.iterate(this);
}
public List<E> next(final int number) {
final List<E> list = new ArrayList<E>(number);
PipeHelper.fillCollection(this, list, number);
return list;
}
public List<E> toList() {
final List<E> list = new ArrayList<E>();
PipeHelper.fillCollection(this, list);
return list;
}
public Collection<E> fill(final Collection<E> collection) {
PipeHelper.fillCollection(this, collection);
return collection;
}
}