package com.tinkerpop.pipes.sideeffect; import com.tinkerpop.pipes.AbstractPipe; import com.tinkerpop.pipes.Pipe; import com.tinkerpop.pipes.PipeFunction; import com.tinkerpop.pipes.util.FastNoSuchElementException; import com.tinkerpop.pipes.util.structures.ArrayQueue; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.NoSuchElementException; import java.util.Queue; /** * The AggregatePipe produces a side effect that is the provided Collection filled with the contents of all the objects that have passed through it. * Before the first object is emitted from the AggregatePipe, all of its incoming objects have been aggregated into the collection. * Finally, note that different Collections have different behaviors and write/read times. * * @author Marko A. Rodriguez (http://markorodriguez.com) */ public class AggregatePipe<S> extends AbstractPipe<S, S> implements SideEffectPipe.GreedySideEffectPipe<S, Collection> { private Collection aggregate; private Queue<S> currentObjectQueue = new ArrayQueue<S>(); private Queue<List> currentPathQueue = new ArrayQueue<List>(); private List currentPath; private PipeFunction<S, ?> preAggregateFunction = null; public AggregatePipe(final Collection aggregate) { this.aggregate = aggregate; } /** * The provided PipeFunction will process the object prior to inserting it into the aggregate collection. * * @param aggregate the aggregate collection * @param preAggregateFunction a function to process an object with prior to insertion into the collection */ public AggregatePipe(final Collection aggregate, final PipeFunction<S, ?> preAggregateFunction) { this(aggregate); this.preAggregateFunction = preAggregateFunction; } public List getCurrentPath() { if (this.pathEnabled) { final List pathElements = new ArrayList(this.currentPath); final int size = pathElements.size(); // do not repeat filters as they dup the object if (size == 0 || pathElements.get(size - 1) != this.currentEnd) { pathElements.add(this.currentEnd); } return pathElements; } else { throw new RuntimeException(Pipe.NO_PATH_MESSAGE); } } protected S processNextStart() { while (true) { if (this.currentObjectQueue.isEmpty()) { if (!this.starts.hasNext()) throw FastNoSuchElementException.instance(); else { this.currentObjectQueue.clear(); this.currentPathQueue.clear(); try { while (true) { final S s = this.starts.next(); if (this.preAggregateFunction != null) this.aggregate.add(this.preAggregateFunction.compute(s)); else this.aggregate.add(s); this.currentObjectQueue.add(s); if (this.pathEnabled) this.currentPathQueue.add(this.getPathToHere()); } } catch (final NoSuchElementException e) { } } } else { if (this.pathEnabled) this.currentPath = currentPathQueue.remove(); return this.currentObjectQueue.remove(); } } } public Collection getSideEffect() { return this.aggregate; } public void reset() { try { this.aggregate = this.aggregate.getClass().getConstructor().newInstance(); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } this.currentPath = null; this.currentObjectQueue.clear(); this.currentPathQueue.clear(); super.reset(); } }