diff -r 6b3dc8e20c04 -r 6a5e9b4f27d2 jdk/src/share/classes/java/util/stream/MatchOps.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/util/stream/MatchOps.java Tue Apr 16 22:50:48 2013 -0400 @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2012, 2013, 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.Objects; +import java.util.Spliterator; +import java.util.function.DoublePredicate; +import java.util.function.IntPredicate; +import java.util.function.LongPredicate; +import java.util.function.Predicate; +import java.util.function.Supplier; + +/** + * Factory for instances of a short-circuiting {@code TerminalOp} that implement + * quantified predicate matching on the elements of a stream. Supported variants + * include match-all, match-any, and match-none. + * + * @since 1.8 + */ +final class MatchOps { + + private MatchOps() { } + + /** + * Enum describing quantified match options -- all match, any match, none + * match. + */ + enum MatchKind { + /** Do all elements match the predicate? */ + ANY(true, true), + + /** Do any elements match the predicate? */ + ALL(false, false), + + /** Do no elements match the predicate? */ + NONE(true, false); + + private final boolean stopOnPredicateMatches; + private final boolean shortCircuitResult; + + private MatchKind(boolean stopOnPredicateMatches, + boolean shortCircuitResult) { + this.stopOnPredicateMatches = stopOnPredicateMatches; + this.shortCircuitResult = shortCircuitResult; + } + } + + /** + * Constructs a quantified predicate matcher for a Stream. + * + * @param the type of stream elements + * @param predicate the {@code Predicate} to apply to stream elements + * @param matchKind the kind of quantified match (all, any, none) + * @return a {@code TerminalOp} implementing the desired quantified match + * criteria + */ + public static TerminalOp makeRef(Predicate predicate, + MatchKind matchKind) { + Objects.requireNonNull(predicate); + Objects.requireNonNull(matchKind); + class MatchSink extends BooleanTerminalSink { + MatchSink() { + super(matchKind); + } + + @Override + public void accept(T t) { + if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { + stop = true; + value = matchKind.shortCircuitResult; + } + } + } + + // @@@ Workaround for JDK-8011591 -- when fixed, replace s with constructor ref + Supplier> s = new Supplier>() { + @Override + public BooleanTerminalSink get() {return new MatchSink();} + }; + return new MatchOp<>(StreamShape.REFERENCE, matchKind, s); + } + + /** + * Constructs a quantified predicate matcher for an {@code IntStream}. + * + * @param predicate the {@code Predicate} to apply to stream elements + * @param matchKind the kind of quantified match (all, any, none) + * @return a {@code TerminalOp} implementing the desired quantified match + * criteria + */ + public static TerminalOp makeInt(IntPredicate predicate, + MatchKind matchKind) { + Objects.requireNonNull(predicate); + Objects.requireNonNull(matchKind); + class MatchSink extends BooleanTerminalSink implements Sink.OfInt { + MatchSink() { + super(matchKind); + } + + @Override + public void accept(int t) { + if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { + stop = true; + value = matchKind.shortCircuitResult; + } + } + } + + // @@@ Workaround for JDK-8011591 -- when fixed, replace s with constructor ref + Supplier> s = new Supplier>() { + @Override + public BooleanTerminalSink get() {return new MatchSink();} + }; + return new MatchOp<>(StreamShape.INT_VALUE, matchKind, s); + } + + /** + * Constructs a quantified predicate matcher for a {@code LongStream}. + * + * @param predicate the {@code Predicate} to apply to stream elements + * @param matchKind the kind of quantified match (all, any, none) + * @return a {@code TerminalOp} implementing the desired quantified match + * criteria + */ + public static TerminalOp makeLong(LongPredicate predicate, + MatchKind matchKind) { + Objects.requireNonNull(predicate); + Objects.requireNonNull(matchKind); + class MatchSink extends BooleanTerminalSink implements Sink.OfLong { + + MatchSink() { + super(matchKind); + } + + @Override + public void accept(long t) { + if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { + stop = true; + value = matchKind.shortCircuitResult; + } + } + } + + // @@@ Workaround for JDK-8011591 -- when fixed, replace s with constructor ref + Supplier> s = new Supplier>() { + @Override + public BooleanTerminalSink get() {return new MatchSink();} + }; + return new MatchOp<>(StreamShape.LONG_VALUE, matchKind, s); + } + + /** + * Constructs a quantified predicate matcher for a {@code DoubleStream}. + * + * @param predicate the {@code Predicate} to apply to stream elements + * @param matchKind the kind of quantified match (all, any, none) + * @return a {@code TerminalOp} implementing the desired quantified match + * criteria + */ + public static TerminalOp makeDouble(DoublePredicate predicate, + MatchKind matchKind) { + Objects.requireNonNull(predicate); + Objects.requireNonNull(matchKind); + class MatchSink extends BooleanTerminalSink implements Sink.OfDouble { + + MatchSink() { + super(matchKind); + } + + @Override + public void accept(double t) { + if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { + stop = true; + value = matchKind.shortCircuitResult; + } + } + } + + // @@@ Workaround for JDK-8011591 -- when fixed, replace s with constructor ref + Supplier> s = new Supplier>() { + @Override + public BooleanTerminalSink get() {return new MatchSink();} + }; + return new MatchOp<>(StreamShape.DOUBLE_VALUE, matchKind, s); + } + + /** + * A short-circuiting {@code TerminalOp} that evaluates a predicate on the + * elements of a stream and determines whether all, any or none of those + * elements match the predicate. + * + * @param the output type of the stream pipeline + */ + private static final class MatchOp implements TerminalOp { + private final StreamShape inputShape; + final MatchKind matchKind; + final Supplier> sinkSupplier; + + /** + * Constructs a {@code MatchOp}. + * + * @param shape the output shape of the stream pipeline + * @param matchKind the kind of quantified match (all, any, none) + * @param sinkSupplier {@code Supplier} for a {@code Sink} of the + * appropriate shape which implements the matching operation + */ + MatchOp(StreamShape shape, + MatchKind matchKind, + Supplier> sinkSupplier) { + this.inputShape = shape; + this.matchKind = matchKind; + this.sinkSupplier = sinkSupplier; + } + + @Override + public int getOpFlags() { + return StreamOpFlag.IS_SHORT_CIRCUIT | StreamOpFlag.NOT_ORDERED; + } + + @Override + public StreamShape inputShape() { + return inputShape; + } + + @Override + public Boolean evaluateSequential(PipelineHelper helper, + Spliterator spliterator) { + return helper.wrapAndCopyInto(sinkSupplier.get(), spliterator).getAndClearState(); + } + + @Override + public Boolean evaluateParallel(PipelineHelper helper, + Spliterator spliterator) { + // Approach for parallel implementation: + // - Decompose as per usual + // - run match on leaf chunks, call result "b" + // - if b == matchKind.shortCircuitOn, complete early and return b + // - else if we complete normally, return !shortCircuitOn + + return new MatchTask<>(this, helper, spliterator).invoke(); + } + } + + /** + * Boolean specific terminal sink to avoid the boxing costs when returning + * results. Subclasses implement the shape-specific functionality. + * + * @param The output type of the stream pipeline + */ + private static abstract class BooleanTerminalSink implements Sink { + boolean stop; + boolean value; + + BooleanTerminalSink(MatchKind matchKind) { + value = !matchKind.shortCircuitResult; + } + + public boolean getAndClearState() { + return value; + } + + @Override + public boolean cancellationRequested() { + return stop; + } + } + + /** + * ForkJoinTask implementation to implement a parallel short-circuiting + * quantified match + * + * @param the type of source elements for the pipeline + * @param the type of output elements for the pipeline + */ + private static final class MatchTask + extends AbstractShortCircuitTask> { + private final MatchOp op; + + /** + * Constructor for root node + */ + MatchTask(MatchOp op, PipelineHelper helper, + Spliterator spliterator) { + super(helper, spliterator); + this.op = op; + } + + /** + * Constructor for non-root node + */ + MatchTask(MatchTask parent, Spliterator spliterator) { + super(parent, spliterator); + this.op = parent.op; + } + + @Override + protected MatchTask makeChild(Spliterator spliterator) { + return new MatchTask<>(this, spliterator); + } + + @Override + protected Boolean doLeaf() { + boolean b = helper.wrapAndCopyInto(op.sinkSupplier.get(), spliterator).getAndClearState(); + if (b == op.matchKind.shortCircuitResult) + shortCircuit(b); + return null; + } + + @Override + protected Boolean getEmptyResult() { + return !op.matchKind.shortCircuitResult; + } + } +} +