public class IdentityPipe<S> extends AbstractPipe<S, S> {  // does not implement TransformPipe

    protected S processNextStart() {
        return this.starts.next();
    }
} public interface Pipe<S, E> extends Iterator<E>, Iterable<E> {

    public static final String NO_PATH_MESSAGE = "Path calculations are not enabled";

    /**
     * Set an iterator of S objects to the head (start) of the pipe.
     *
     * @param starts the iterator of incoming objects
     */
    public void setStarts(Iterator<S> starts);

    /**
     * Set an iterable of S objects to the head (start) of the pipe.
     *
     * @param starts the iterable of incoming objects
     */
    public void setStarts(Iterable<S> starts);

    /**
     * Returns the transformation path to arrive at the current object of the pipe.
     *
     * @return a List of all of the objects traversed for the current iterator position of the pipe.
     */
    public List getCurrentPath();

    /**
     * Calculating paths can be an expensive operation for some pipes.
     * This method is used to activate or deactivate the calculation of paths.
     * The default state of a newly constructed pipe should have its path calculations disabled.
     * An implementation of this method should be recursive whereby the starts (if a Pipe) should have this method called on it.
     *
     * @param enable enable path calculations
     */
    public void enablePath(boolean enable);

    /**
     * A pipe may maintain state. Reset is used to remove state.
     * The general use case for reset() is to reuse a pipe in another computation without having to create a new Pipe object.
     * An implementation of this method should be recursive whereby the starts (if a Pipe) should have this method called on it.
     */
    public void reset();
} public interface PipeFunction<A, B> {

    /**
     * A function that takes an argument of type A and returns a result of type B.
     *
     * @param argument An argument of type A
     * @return the result of computing the function on the argument
     */
    public B compute(A argument);
} public class ExceptFilterPipe<S> extends CollectionFilterPipe<S> {

    public ExceptFilterPipe(final Collection<S> storedCollection) {
        super(storedCollection, Contains.NOT_IN);
    }

    public ExceptFilterPipe(final AsMap asMap, final String... namedSteps) {
        super(Contains.NOT_IN, asMap, namedSteps);
    }
} public class FilterFunctionPipe<S> extends AbstractPipe<S, S> implements FilterPipe<S> {

    private final PipeFunction<S, Boolean> filterFunction;

    public FilterFunctionPipe(final PipeFunction<S, Boolean> filterFunction) {
        this.filterFunction = filterFunction;
    }

    public S processNextStart() {
        while (true) {
            final S s = this.starts.next();
            if (this.filterFunction.compute(s))
                return s;
        }
    }
} public class OrFilterPipe<S> extends AbstractMetaPipe<S, S> implements FilterPipe<S>, MetaPipe {

    private final List<Pipe<S, Boolean>> pipes = new ArrayList<Pipe<S, Boolean>>();

    public OrFilterPipe(final Pipe<S, ?>... pipes) {
        for (final Pipe<S, ?> pipe : pipes) {
            this.pipes.add(new HasNextPipe<S>(pipe));
        }
    }

    public S processNextStart() {
        while (true) {
            final S s = this.starts.next();
            for (Pipe<S, Boolean> pipe : this.pipes) {
                pipe.setStarts(new SingleIterator<S>(s));
                if (pipe.next()) {
                    return s;
                }
            }
        }
    }

    public List<Pipe> getPipes() {
        return (List) this.pipes;
    }

    public String toString() {
        return PipeHelper.makePipeString(this, this.pipes);
    }
} public class SideEffectFunctionPipe<S> extends AbstractPipe<S, S> {

    private final PipeFunction<S, ?> sideEffectFunction;

    public SideEffectFunctionPipe(final PipeFunction<S, ?> sideEffectFunction) {
        this.sideEffectFunction = sideEffectFunction;
    }

    public S processNextStart() {
        final S s = this.starts.next();
        this.sideEffectFunction.compute(s);
        return s;
    }
} public class TablePipe<S> extends AbstractPipe<S, S> implements SideEffectPipe.GreedySideEffectPipe<S, Table> {

    private Table table;
    private final PipeFunction[] columnFunctions;
    private int currentFunction;
    private final List<AsPipe> asPipes = new ArrayList<AsPipe>();
    private final Collection<String> stepNames;
    private final boolean doFunctions;

    public TablePipe(final Table table, final Collection<String> stepNames, final List<AsPipe> allPreviousAsPipes, final PipeFunction... columnFunctions) {
        this.table = table;
        this.columnFunctions = columnFunctions;
        this.stepNames = stepNames;

        if (this.doFunctions = this.columnFunctions.length > 0)
            currentFunction = 0;

        final List<String> tempNames = new ArrayList<String>();
        for (final AsPipe asPipe : allPreviousAsPipes) {
            final String columnName = asPipe.getName();
            if (null == this.stepNames || this.stepNames.contains(columnName)) {
                tempNames.add(columnName);
                this.asPipes.add(asPipe);
            }
        }

        if (tempNames.size() > 0)
            table.setColumnNames(tempNames.toArray(new String[tempNames.size()]));

    }

    public Table getSideEffect() {
        return this.table;
    }

    public S processNextStart() {
        final S s = this.starts.next();
        final List row = new ArrayList();
        for (final AsPipe asPipe : this.asPipes) {
            if (doFunctions) {
                row.add(this.columnFunctions[currentFunction++ % columnFunctions.length].compute(asPipe.getCurrentEnd()));
            } else {
                row.add(asPipe.getCurrentEnd());
            }
        }
        this.table.addRow(row);
        return s;
    }

    public void reset() {
        this.table = new Table();
        this.currentFunction = 0;
        super.reset();
    }

    public String toString() {
        if (null == this.stepNames)
            return PipeHelper.makePipeString(this);
        else
            return PipeHelper.makePipeString(this, this.stepNames.toArray());
    }

} public class GatherFunctionPipe<S, E> extends AbstractPipe<S, E> implements TransformPipe<S, E> {

    private List<List> listPaths = new ArrayList<List>();
    private final PipeFunction<List<S>, E> postFilterFunction;

    public GatherFunctionPipe(final PipeFunction<List<S>, E> postFilterFunction) {
        this.postFilterFunction = postFilterFunction;
    }

    public List getCurrentPath() {
        if (this.pathEnabled)
            return new ArrayList(this.listPaths);
        else
            throw new RuntimeException(Pipe.NO_PATH_MESSAGE);
    }

    protected E processNextStart() {
        final List<S> list = new ArrayList<S>();
        this.listPaths = new ArrayList<List>();
        if (!this.starts.hasNext()) {
            throw FastNoSuchElementException.instance();
        } else {
            while (this.starts.hasNext()) {
                final S s = this.starts.next();
                list.add(s);
                if (this.pathEnabled)
                    this.listPaths.add(super.getPathToHere());
            }
        }

        if (this.pathEnabled) {
            E e = this.postFilterFunction.compute(list);
            if (e instanceof List)
                addList((List) e);
            else
                addList(Arrays.asList(e));
            return e;
            //return (E) addList((List) this.postFilterFunction.compute(list));
        } else {

            return this.postFilterFunction.compute(list);

        }
    }

    public void reset() {
        this.listPaths = new ArrayList<List>();
        super.reset();
    }

    private List addList(final List list) {
        for (final List l : this.listPaths) {
            l.add(list);
        }
        return list;
    }
} public class GatherPipe<S> extends AbstractPipe<S, List<S>> implements TransformPipe<S, List<S>> {

    private List<List> listPaths = new ArrayList<List>();


    public List getCurrentPath() {
        if (pathEnabled)
            return new ArrayList(this.listPaths);
        else
            throw new RuntimeException(Pipe.NO_PATH_MESSAGE);
    }

    protected List<S> processNextStart() {
        final List<S> list = new ArrayList<S>();
        this.listPaths = new ArrayList<List>();
        if (!this.starts.hasNext()) {
            throw FastNoSuchElementException.instance();
        } else {
            while (this.starts.hasNext()) {
                final S s = this.starts.next();
                list.add(s);
                if (this.pathEnabled)
                    this.listPaths.add(super.getPathToHere());
            }
        }

        if (this.pathEnabled) {
            return addList(list);
        } else {
            return list;
        }
    }

    public void reset() {
        this.listPaths = new ArrayList<List>();
        super.reset();
    }

    private List addList(final List list) {
        for (final List l : this.listPaths) {
            l.add(list);
        }
        return list;
    }


} public class PathPipe<S> extends AbstractPipe<S, List> implements TransformPipe<S, List> {

    private final PipeFunction[] pathFunctions;

    public PathPipe(final PipeFunction... pathFunctions) {
        if (pathFunctions.length == 0) {
            this.pathFunctions = null;
        } else {
            this.pathFunctions = pathFunctions;
        }
    }

    public void setStarts(final Iterator<S> starts) {
        super.setStarts(starts);
        this.enablePath(true);
    }

    public List processNextStart() {
        if (this.starts instanceof Pipe) {
            this.starts.next();
            final List path = ((Pipe) this.starts).getCurrentPath();
            if (null == this.pathFunctions) {
                return path;
            } else {
                final List closedPath = new ArrayList();
                int nextFunction = 0;
                for (final Object object : path) {
                    closedPath.add(this.pathFunctions[nextFunction].compute(object));
                    nextFunction = (nextFunction + 1) % this.pathFunctions.length;
                }
                return closedPath;
            }
        } else {
            throw new NoSuchElementException("The start of this pipe was not a pipe");
        }
    }


} public class SelectPipe<S> extends AbstractPipe<S, Row> implements TransformPipe<S, Row> {

    private final PipeFunction[] stepFunctions;
    private final boolean doFunctions;
    private int currentFunction;
    private final List<AsPipe> asPipes = new ArrayList<AsPipe>();
    private final List<String> columnNames;
    private final Collection<String> stepNames;

    public SelectPipe(final Collection<String> stepNames, final List<AsPipe> allPreviousAsPipes, final PipeFunction... stepFunctions) {
        this.stepFunctions = stepFunctions;
        this.stepNames = stepNames;

        if (this.doFunctions = this.stepFunctions.length > 0)
            currentFunction = 0;

        final List<String> tempNames = new ArrayList<String>();
        for (final AsPipe asPipe : allPreviousAsPipes) {
            final String columnName = asPipe.getName();
            if (null == this.stepNames || this.stepNames.contains(columnName)) {
                tempNames.add(columnName);
                this.asPipes.add(asPipe);
            }
        }

        if (tempNames.size() > 0)
            this.columnNames = tempNames;
        else
            this.columnNames = null;

    }

    public Row processNextStart() {
        this.starts.next();
        final Row row;
        if (null == columnNames)
            row = new Row();
        else
            row = new Row(columnNames);

        for (final AsPipe asPipe : this.asPipes) {
            if (doFunctions) {
                row.add(this.stepFunctions[currentFunction++ % stepFunctions.length].compute(asPipe.getCurrentEnd()));
            } else {
                row.add(asPipe.getCurrentEnd());
            }
        }
        return row;
    }

    public void reset() {
        this.currentFunction = 0;
        super.reset();
    }

    public String toString() {
        if (null == this.stepNames)
            return PipeHelper.makePipeString(this);
        else
            return PipeHelper.makePipeString(this, this.stepNames.toArray());
    }

} public class TransformFunctionPipe<S, E> extends AbstractPipe<S, E> implements TransformPipe<S, E> {

    private final PipeFunction<S, E> transformFunction;

    public TransformFunctionPipe(final PipeFunction<S, E> transformFunction) {
        this.transformFunction = transformFunction;
    }

    public E processNextStart() {
        return this.transformFunction.compute(this.starts.next());
    }
} public interface TransformPipe<S, E> extends Pipe<S, E> {

    public enum Order {
        DECR, INCR
    }
} public class Tree<T> extends HashMap<T, Tree<T>> {

    public Tree() {
        super();
    }

    public Tree(final T... children) {
        this();
        for (final T t : children) {
            this.put(t, new Tree<T>());
        }
    }

    public Tree(final Map.Entry<T, Tree<T>>... children) {
        this();
        for (final Map.Entry<T, Tree<T>> entry : children) {
            this.put(entry.getKey(), entry.getValue());
        }
    }


    public List<Tree<T>> getTreesAtDepth(final int depth) {
        final List<Tree<T>> branches = new ArrayList<Tree<T>>();
        List<Tree<T>> currentDepth = Arrays.asList(this);
        for (int i = 0; i < depth; i++) {
            if (i == depth - 1) {
                return currentDepth;
            } else {
                final List<Tree<T>> temp = new ArrayList<Tree<T>>();
                for (final Tree<T> t : currentDepth) {
                    temp.addAll(t.values());
                }
                currentDepth = temp;
            }
        }
        return branches;
    }

    public List<T> getObjectsAtDepth(final int depth) {
        final List<T> list = new ArrayList<T>();
        for (final Tree<T> t : this.getTreesAtDepth(depth)) {
            list.addAll(t.keySet());
        }
        return list;
    }

    public List<Tree<T>> getLeafTrees() {
        final List<Tree<T>> leaves = new ArrayList<Tree<T>>();
        List<Tree<T>> currentDepth = Arrays.asList(this);
        boolean allLeaves = false;
        while (!allLeaves) {
            allLeaves = true;
            final List<Tree<T>> temp = new ArrayList<Tree<T>>();
            for (final Tree<T> t : currentDepth) {
                if (t.isLeaf()) {
                    for (Map.Entry<T, Tree<T>> t2 : t.entrySet()) {
                        leaves.add(new Tree<T>(t2));
                    }
                } else {
                    allLeaves = false;
                    temp.addAll(t.values());
                }
            }
            currentDepth = temp;

        }
        return leaves;
    }

    public List<T> getLeafObjects() {
        final List<T> leaves = new ArrayList<T>();
        for (final Tree<T> t : this.getLeafTrees()) {
            leaves.addAll(t.keySet());
        }
        return leaves;
    }

    public boolean isLeaf() {
        Collection<Tree<T>> values = this.values();
        return values.iterator().next().isEmpty();

    }

    public static <T> Map.Entry<T, Tree<T>> createTree(T key, Tree<T> tree) {
        return new SimpleEntry<T, Tree<T>>(key, tree);
    }
}
import com.tinkerpop.pipes.FunctionPipe;
import com.tinkerpop.pipes.IdentityPipe;
import com.tinkerpop.pipes.Pipe;
import com.tinkerpop.pipes.PipeFunction;
import com.tinkerpop.pipes.branch.CopySplitPipe;
import com.tinkerpop.pipes.branch.ExhaustMergePipe;
import com.tinkerpop.pipes.branch.FairMergePipe;
import com.tinkerpop.pipes.branch.IfThenElsePipe;
import com.tinkerpop.pipes.branch.LoopPipe;
import com.tinkerpop.pipes.filter.AndFilterPipe;
import com.tinkerpop.pipes.filter.BackFilterPipe;
import com.tinkerpop.pipes.filter.CyclicPathFilterPipe;
import com.tinkerpop.pipes.filter.DuplicateFilterPipe;
import com.tinkerpop.pipes.filter.ExceptFilterPipe;
import com.tinkerpop.pipes.filter.FilterFunctionPipe;
import com.tinkerpop.pipes.filter.OrFilterPipe;
import com.tinkerpop.pipes.filter.RandomFilterPipe;
import com.tinkerpop.pipes.filter.RangeFilterPipe;
import com.tinkerpop.pipes.filter.RetainFilterPipe;
import com.tinkerpop.pipes.sideeffect.AggregatePipe;
import com.tinkerpop.pipes.sideeffect.GroupByPipe;
import com.tinkerpop.pipes.sideeffect.GroupByReducePipe;
import com.tinkerpop.pipes.sideeffect.GroupCountFunctionPipe;
import com.tinkerpop.pipes.sideeffect.GroupCountPipe;
import com.tinkerpop.pipes.sideeffect.OptionalPipe;
import com.tinkerpop.pipes.sideeffect.SideEffectFunctionPipe;
import com.tinkerpop.pipes.sideeffect.SideEffectPipe;
import com.tinkerpop.pipes.sideeffect.StorePipe;
import com.tinkerpop.pipes.sideeffect.TablePipe;
import com.tinkerpop.pipes.sideeffect.TreePipe;
import com.tinkerpop.pipes.transform.GatherFunctionPipe;
import com.tinkerpop.pipes.transform.GatherPipe;
import com.tinkerpop.pipes.transform.MemoizePipe;
import com.tinkerpop.pipes.transform.OrderMapPipe;
import com.tinkerpop.pipes.transform.OrderPipe;
import com.tinkerpop.pipes.transform.PathPipe;
import com.tinkerpop.pipes.transform.ScatterPipe;
import com.tinkerpop.pipes.transform.SelectPipe;
import com.tinkerpop.pipes.transform.ShufflePipe;
import com.tinkerpop.pipes.transform.SideEffectCapPipe;
import com.tinkerpop.pipes.transform.TransformFunctionPipe;
import com.tinkerpop.pipes.transform.TransformPipe;
import com.tinkerpop.pipes.util.structures.AsMap;
import com.tinkerpop.pipes.util.structures.Pair;
import com.tinkerpop.pipes.util.structures.Row;
import com.tinkerpop.pipes.util.structures.Table;
import com.tinkerpop.pipes.util.structures.Tree;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
    }

    public PipesPipeline<S, E> groupCount(final PipeFunction keyFunction, final PipeFunction<Pair<?, Number>, Number> valueFunction) {
        return this.add(new GroupCountFunctionPipe(FluentUtility.prepareFunction(this.asMap, keyFunction), FluentUtility.prepareFunction(this.asMap, valueFunction)));
    }

    public PipesPipeline<S, E> groupCount(final Map<?, Number> map, final PipeFunction keyFunction) {
        return this.add(new GroupCountFunctionPipe(map, FluentUtility.prepareFunction(this.asMap, keyFunction), null));
    }

    public PipesPipeline<S, E> groupCount(final PipeFunction keyFunction) {
        return this.add(new GroupCountFunctionPipe(FluentUtility.prepareFunction(this.asMap, keyFunction), null));
    }

    public PipesPipeline<S, E> groupCount(final Map<?, Number> map) {
        return this.add(new GroupCountPipe(map));
    }

    public PipesPipeline<S, E> groupCount() {
        return this.add(new GroupCountPipe<E>());
    }

    public PipesPipeline<S, E> sideEffect(final PipeFunction<E, ?> sideEffectFunction) {
        return this.add(new SideEffectFunctionPipe<E>(FluentUtility.prepareFunction(this.asMap, sideEffectFunction)));
    }

    public PipesPipeline<S, E> store(final Collection<E> storage) {
        return this.add(new StorePipe<E>(storage));
    }

    public PipesPipeline<S, E> store(final Collection storage, final PipeFunction<E, ?> storageFunction) {
        return this.add(new StorePipe<E>(storage, FluentUtility.prepareFunction(this.asMap, storageFunction)));
    }

    public PipesFluentPipeline<S, E> store(final PipeFunction<E, ?> storageFunction) {
        return this.store(new ArrayList(), FluentUtility.prepareFunction(this.asMap, storageFunction));
    }

    public PipesPipeline<S, E> store() {
        return this.store(new ArrayList<E>());
    }

    public PipesPipeline<S, E> table(final Table table, final Collection<String> stepNames, final PipeFunction... columnFunctions) {
        return this.add(new TablePipe<E>(table, stepNames, FluentUtility.getAsPipes(this), FluentUtility.prepareFunctions(this.asMap, columnFunctions)));
    }

    public PipesPipeline<S, E> table(final Table table, final PipeFunction... columnFunctions) {
        return this.add(new TablePipe<E>(table, null, FluentUtility.getAsPipes(this), FluentUtility.prepareFunctions(this.asMap, columnFunctions)));
    }

    public PipesPipeline<S, E> table(final PipeFunction... columnFunctions) {
        return this.add(new TablePipe<E>(new Table(), null, FluentUtility.getAsPipes(this), FluentUtility.prepareFunctions(this.asMap, columnFunctions)));
    }

    public PipesPipeline<S, E> table(final Table table) {
        return this.add(new TablePipe<E>(table, null, FluentUtility.getAsPipes(this)));
    }

    public PipesPipeline<S, E> table() {
        return this.add(new TablePipe<E>(new Table(), null, FluentUtility.getAsPipes(this)));
    }

    public PipesPipeline<S, E> tree(final Tree tree, final PipeFunction... branchFunctions) {
        return this.add(new TreePipe<E>(tree, FluentUtility.prepareFunctions(this.asMap, branchFunctions)));
    }

    public PipesPipeline<S, E> tree(final PipeFunction... branchFunctions) {
        return this.add(new TreePipe<E>(FluentUtility.prepareFunctions(this.asMap, branchFunctions)));
    }

    ///////////////////////
    /// TRANSFORM PIPES ///
    ///////////////////////


    public PipesPipeline<S, List> gather() {
        return this.add(new GatherPipe());
    }

    public PipesPipeline<S, ?> gather(PipeFunction<List, ?> function) {
        return this.add(new GatherFunctionPipe(FluentUtility.prepareFunction(this.asMap, function)));
    }

    public PipesPipeline<S, E> _() {
        return this.add(new IdentityPipe<E>());
    }

    public PipesPipeline<S, E> memoize(final String namedStep) {
        return this.add(new MemoizePipe(new Pipeline(FluentUtility.removePreviousPipes(this, namedStep))));
    }

    public PipesPipeline<S, E> memoize(final int numberedStep) {
        return this.add(new MemoizePipe(new Pipeline(FluentUtility.removePreviousPipes(this, numberedStep))));
    }

    public PipesPipeline<S, E> memoize(final String namedStep, final Map map) {
        return this.add(new MemoizePipe(new Pipeline(FluentUtility.removePreviousPipes(this, namedStep)), map));
    }

    public PipesPipeline<S, E> memoize(final int numberedStep, final Map map) {
        return this.add(new MemoizePipe(new Pipeline(FluentUtility.removePreviousPipes(this, numberedStep)), map));
    }

    public PipesPipeline<S, E> order() {
        return this.add(new OrderPipe());
    }

    public PipesPipeline<S, E> order(TransformPipe.Order order) {
        return this.add(new OrderPipe(order));
    }

    public PipesPipeline<S, E> order(final PipeFunction<Pair<E, E>, Integer> compareFunction) {
        return this.add(new OrderPipe(FluentUtility.prepareFunction(this.asMap, compareFunction)));
    }

    public PipesPipeline<S, List> path(final PipeFunction... pathFunctions) {
        return this.add(new PathPipe<Object>(FluentUtility.prepareFunctions(this.asMap, pathFunctions)));
    }

    public PipesPipeline<S, Row> select(final Collection<String> stepNames, final PipeFunction... columnFunctions) {
        return this.add(new SelectPipe(stepNames, FluentUtility.getAsPipes(this), FluentUtility.prepareFunctions(this.asMap, columnFunctions)));