--- /dev/null 2013-02-14 00:07:37.184020992 -0500 +++ new/src/share/classes/java/util/stream/FindOp.java 2013-02-21 14:11:04.000000000 -0500 @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.util.stream; + +import java.util.*; +import java.util.concurrent.CountedCompleter; +import java.util.function.Predicate; +import java.util.function.Supplier; + +/** + * A short-circuiting {@code TerminalOp} that searches for an element in a stream pipeline, and terminates when + * it finds one. Implements both find-first (find the first element in the encounter order) and find-any (find + * any element, may not be the first in encounter order.) + * + * @param The output type of the stream pipeline + * @param The result type of the find operation, typically an optional type + * @since 1.8 + */ +class FindOp implements TerminalOp { + + /** + * Construct a {@code FindOp} for streams of objects + * @param mustFindFirst Whether the {@code FindOp} must produce the first element in the encounter order + * @param The type of elements of the stream + * @return A {@code FindOp} implementing the find operation + */ + public static FindOp> makeRef(boolean mustFindFirst) { + return new FindOp<>(mustFindFirst, StreamShape.REFERENCE, Optional.empty(), Optional::isPresent, FindSink.OfRef::new); + } + + /** + * Construct a {@code FindOp} for streams of ints + * @param mustFindFirst Whether the {@code FindOp} must produce the first element in the encounter order + * @return A {@code FindOp} implementing the find operation + */ + public static FindOp makeInt(boolean mustFindFirst) { + return new FindOp<>(mustFindFirst, StreamShape.INT_VALUE, OptionalInt.empty(), OptionalInt::isPresent, FindSink.OfInt::new); + } + + /** + * Construct a {@code FindOp} for streams of longs + * @param mustFindFirst Whether the {@code FindOp} must produce the first element in the encounter order + * @return A {@code FindOp} implementing the find operation + */ + public static FindOp makeLong(boolean mustFindFirst) { + return new FindOp<>(mustFindFirst, StreamShape.LONG_VALUE, OptionalLong.empty(), OptionalLong::isPresent, FindSink.OfLong::new); + } + + /** + * Construct a {@code FindOp} for streams of doubles + * @param mustFindFirst Whether the {@code FindOp} must produce the first element in the encounter order + * @return A {@code FindOp} implementing the find operation + */ + public static FindOp makeDouble(boolean mustFindFirst) { + return new FindOp<>(mustFindFirst, StreamShape.DOUBLE_VALUE, OptionalDouble.empty(), OptionalDouble::isPresent, FindSink.OfDouble::new); + } + + /** + * Implementation of @{code TerminalSink} that implements the find functionality, requesting cancellation + * when something has been found + * + * @param The type of input element + * @param The result type, typically an optional type + */ + static abstract class FindSink implements TerminalSink { + boolean hasValue; + T value; + + @Override + public void accept(T value) { + if (!hasValue) { + hasValue = true; + this.value = value; + } + } + + @Override + public boolean cancellationRequested() { + return hasValue; + } + + /** Specialization of {@code FindSink} for reference streams */ + static class OfRef extends FindSink> { + @Override + public Optional getAndClearState() { + return hasValue ? Optional.of(value) : null; + } + } + + /** Specialization of {@code FindSink} for int streams */ + static class OfInt extends FindSink implements Sink.OfInt { + @Override + public void accept(int value) { + // Boxing is OK here, since few values will actually flow into the sink + accept((Integer) value); + } + + @Override + public OptionalInt getAndClearState() { + return hasValue ? OptionalInt.of(value) : null; + } + } + + /** Specialization of {@code FindSink} for long streams */ + static class OfLong extends FindSink implements Sink.OfLong { + @Override + public void accept(long value) { + // Boxing is OK here, since few values will actually flow into the sink + accept((Long) value); + } + + @Override + public OptionalLong getAndClearState() { + return hasValue ? OptionalLong.of(value) : null; + } + } + + /** Specialization of {@code FindSink} for double streams */ + static class OfDouble extends FindSink implements Sink.OfDouble { + @Override + public void accept(double value) { + // Boxing is OK here, since few values will actually flow into the sink + accept((Double) value); + } + + @Override + public OptionalDouble getAndClearState() { + return hasValue ? OptionalDouble.of(value) : null; + } + } + } + + private final boolean mustFindFirst; + private final StreamShape shape; + private final O emptyValue; + private final Predicate presentPredicate; + private final Supplier> sinkSupplier; + + /** + * Construct a {@code FindOp} + * @param mustFindFirst If true, must find the first element in encounter order, otherwise can find any element + * @param shape Stream shape of elements to search + * @param emptyValue Result value corresponding to "found nothing" + * @param presentPredicate {@code Predicate} on result value corresponding to "found something" + * @param sinkSupplier Factory for a {@code TerminalSink} implementing the matching functionality + */ + private FindOp(boolean mustFindFirst, + StreamShape shape, + O emptyValue, + Predicate presentPredicate, + Supplier> sinkSupplier) { + this.mustFindFirst = mustFindFirst; + this.shape = shape; + this.emptyValue = emptyValue; + this.presentPredicate = presentPredicate; + this.sinkSupplier = sinkSupplier; + } + + @Override + public int getOpFlags() { + return StreamOpFlag.IS_SHORT_CIRCUIT | (mustFindFirst ? 0 : StreamOpFlag.NOT_ORDERED); + } + + @Override + public StreamShape inputShape() { + return shape; + } + + @Override + public O evaluateSequential(PipelineHelper helper) { + O result = helper.into(sinkSupplier.get(), helper.sourceSpliterator()).getAndClearState(); + return result != null ? result : emptyValue; + } + + @Override + public O evaluateParallel(PipelineHelper helper) { + return new FindTask<>(helper, this).invoke(); + } + + /** + * {@code ForkJoinTask} implementing parallel short-circuiting search + * @param Input element type to the stream pipeline + * @param Output element type from the stream pipeline + * @param Result type from the find operation + */ + private static class FindTask extends AbstractShortCircuitTask> { + private final FindOp op; + + private FindTask(PipelineHelper helper, FindOp op) { + super(helper); + this.op = op; + } + + private FindTask(FindTask parent, Spliterator spliterator) { + super(parent, spliterator); + this.op = parent.op; + } + + @Override + protected FindTask makeChild(Spliterator spliterator) { + return new FindTask<>(this, spliterator); + } + + @Override + protected O getEmptyResult() { + return op.emptyValue; + } + + private void foundResult(O answer) { + if (isLeftmostNode()) + shortCircuit(answer); + else + cancelLaterNodes(); + } + + @Override + protected O doLeaf() { + O result = helper.into(op.sinkSupplier.get(), spliterator).getAndClearState(); + if (!op.mustFindFirst) { + if (result != null) + shortCircuit(result); + return null; + } + else { + if (result != null) { + foundResult(result); + return result; + } + else + return null; + } + } + + @Override + public void onCompletion(CountedCompleter caller) { + if (op.mustFindFirst) { + for (FindTask child = children; child != null; child = child.nextSibling) { + O result = child.getLocalResult(); + if (result != null && op.presentPredicate.test(result)) { + setLocalResult(result); + foundResult(result); + break; + } + } + } + } + } +}