package com.tinkerpop.pipes.transform; 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 com.tinkerpop.pipes.util.structures.Pair; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.NoSuchElementException; /** * OrderPipe supports in-stream sorting of objects. * If no provided sorting function is provided, then a default sort order is assumed. * Moreover, when no sorting function is provided, the object S is assumed to be comparable. * If a sorting function is provided, then sort is determined by the comparison computed by the function. * * @author Marko A. Rodriguez (http://markorodriguez.com) */ public class OrderPipe<S> extends AbstractPipe<S, S> implements TransformPipe<S, S> { private final ArrayQueue<ObjectBundle<S>> bundles = new ArrayQueue<ObjectBundle<S>>(); private final ArrayQueue<S> objects = new ArrayQueue<S>(); private Comparator<ObjectBundle<S>> bundledComparator; private Comparator<S> objectComparator; private List currentPath; public OrderPipe(final PipeFunction<Pair<S, S>, Integer> compareFunction) { this.objectComparator = new PipeFunctionComparator(compareFunction); this.bundledComparator = new PipeFunctionBundleComparator(compareFunction); } public OrderPipe() { this(Order.INCR); } public OrderPipe(final Order order) { if (order.equals(Order.INCR)) { this.objectComparator = new PipeFunctionComparator(new IncrementFunction()); this.bundledComparator = new PipeFunctionBundleComparator(new IncrementFunction()); } else { this.objectComparator = new PipeFunctionComparator(new DecrementFunction()); this.bundledComparator = new PipeFunctionBundleComparator(new DecrementFunction()); } } public void enablePath(boolean enablePath) { if (enablePath) this.objectComparator = null; else this.bundledComparator = null; super.enablePath(enablePath); } public S processNextStart() { while (true) { if (this.pathEnabled) { if (this.bundles.isEmpty()) { if (!this.starts.hasNext()) throw FastNoSuchElementException.instance(); else { this.bundles.clear(); try { while (true) { this.bundles.add(new ObjectBundle<S>(this.starts.next(), this.getPathToHere())); } } catch (final NoSuchElementException e) { } if (null != this.bundledComparator) Collections.sort(this.bundles, this.bundledComparator); else Collections.sort(this.bundles); } } else { final ObjectBundle<S> object = this.bundles.remove(); this.currentPath = object.path; return object.object; } } else { if (this.objects.isEmpty()) { if (!this.starts.hasNext()) throw FastNoSuchElementException.instance(); else { this.objects.clear(); try { while (true) { this.objects.add(this.starts.next()); } } catch (final NoSuchElementException e) { } if (null != this.objectComparator) Collections.sort(this.objects, this.objectComparator); else Collections.sort((List<Comparable>) this.objects); } } else { return this.objects.remove(); } } } } public List getCurrentPath() { if (this.pathEnabled) { final List pathElements = new ArrayList(this.currentPath); final int size = pathElements.size(); if (this instanceof TransformPipe) { pathElements.add(this.currentEnd); } else if (size == 0 || pathElements.get(size - 1) != this.currentEnd) { // do not repeat filters or side-effects as they dup the object pathElements.add(this.currentEnd); } return pathElements; } else { throw new RuntimeException(Pipe.NO_PATH_MESSAGE); } } private class ObjectBundle<S> implements Comparable<ObjectBundle<S>> { public final S object; public final List path; public ObjectBundle(final S object, final List path) { this.object = object; this.path = path; } public int compareTo(final ObjectBundle bundle) { return ((Comparable) this.object).compareTo(bundle.object); } } private class PipeFunctionBundleComparator implements Comparator<ObjectBundle<S>> { private final PipeFunction<Pair<S, S>, Integer> pipeFunction; public PipeFunctionBundleComparator(final PipeFunction<Pair<S, S>, Integer> pipeFunction) { this.pipeFunction = pipeFunction; } public int compare(final ObjectBundle<S> a, final ObjectBundle<S> b) { return this.pipeFunction.compute(new Pair<S, S>(a.object, b.object)); } } private class PipeFunctionComparator implements Comparator<S> { private final PipeFunction<Pair<S, S>, Integer> pipeFunction; public PipeFunctionComparator(final PipeFunction<Pair<S, S>, Integer> pipeFunction) { this.pipeFunction = pipeFunction; } public int compare(final S a, final S b) { return this.pipeFunction.compute(new Pair<S, S>(a, b)); } } private class IncrementFunction implements PipeFunction<Pair<S, S>, Integer> { public Integer compute(Pair<S, S> pair) { return ((Comparable) pair.getA()).compareTo(pair.getB()); } } private class DecrementFunction implements PipeFunction<Pair<S, S>, Integer> { public Integer compute(Pair<S, S> pair) { return ((Comparable) pair.getB()).compareTo(pair.getA()); } } }