| /* |
| * 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 any elements match the predicate? */ |
| ANY(true, true), |
| |
| /** Do all 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 <T> 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 <T> TerminalOp<T, Boolean> makeRef(Predicate<? super T> predicate, |
| MatchKind matchKind) { |
| Objects.requireNonNull(predicate); |
| Objects.requireNonNull(matchKind); |
| class MatchSink extends BooleanTerminalSink<T> { |
| MatchSink() { |
| super(matchKind); |
| } |
| |
| @Override |
| public void accept(T t) { |
| if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { |
| stop = true; |
| value = matchKind.shortCircuitResult; |
| } |
| } |
| } |
| |
| return new MatchOp<>(StreamShape.REFERENCE, matchKind, MatchSink::new); |
| } |
| |
| /** |
| * 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<Integer, Boolean> makeInt(IntPredicate predicate, |
| MatchKind matchKind) { |
| Objects.requireNonNull(predicate); |
| Objects.requireNonNull(matchKind); |
| class MatchSink extends BooleanTerminalSink<Integer> implements Sink.OfInt { |
| MatchSink() { |
| super(matchKind); |
| } |
| |
| @Override |
| public void accept(int t) { |
| if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { |
| stop = true; |
| value = matchKind.shortCircuitResult; |
| } |
| } |
| } |
| |
| return new MatchOp<>(StreamShape.INT_VALUE, matchKind, MatchSink::new); |
| } |
| |
| /** |
| * 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<Long, Boolean> makeLong(LongPredicate predicate, |
| MatchKind matchKind) { |
| Objects.requireNonNull(predicate); |
| Objects.requireNonNull(matchKind); |
| class MatchSink extends BooleanTerminalSink<Long> implements Sink.OfLong { |
| |
| MatchSink() { |
| super(matchKind); |
| } |
| |
| @Override |
| public void accept(long t) { |
| if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { |
| stop = true; |
| value = matchKind.shortCircuitResult; |
| } |
| } |
| } |
| |
| return new MatchOp<>(StreamShape.LONG_VALUE, matchKind, MatchSink::new); |
| } |
| |
| /** |
| * 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<Double, Boolean> makeDouble(DoublePredicate predicate, |
| MatchKind matchKind) { |
| Objects.requireNonNull(predicate); |
| Objects.requireNonNull(matchKind); |
| class MatchSink extends BooleanTerminalSink<Double> implements Sink.OfDouble { |
| |
| MatchSink() { |
| super(matchKind); |
| } |
| |
| @Override |
| public void accept(double t) { |
| if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { |
| stop = true; |
| value = matchKind.shortCircuitResult; |
| } |
| } |
| } |
| |
| return new MatchOp<>(StreamShape.DOUBLE_VALUE, matchKind, MatchSink::new); |
| } |
| |
| /** |
| * 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 <T> the output type of the stream pipeline |
| */ |
| private static final class MatchOp<T> implements TerminalOp<T, Boolean> { |
| private final StreamShape inputShape; |
| final MatchKind matchKind; |
| final Supplier<BooleanTerminalSink<T>> 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<BooleanTerminalSink<T>> 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 <S> Boolean evaluateSequential(PipelineHelper<T> helper, |
| Spliterator<S> spliterator) { |
| return helper.wrapAndCopyInto(sinkSupplier.get(), spliterator).getAndClearState(); |
| } |
| |
| @Override |
| public <S> Boolean evaluateParallel(PipelineHelper<T> helper, |
| Spliterator<S> 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 <T> The output type of the stream pipeline |
| */ |
| private abstract static class BooleanTerminalSink<T> implements Sink<T> { |
| 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 <P_IN> the type of source elements for the pipeline |
| * @param <P_OUT> the type of output elements for the pipeline |
| */ |
| @SuppressWarnings("serial") |
| private static final class MatchTask<P_IN, P_OUT> |
| extends AbstractShortCircuitTask<P_IN, P_OUT, Boolean, MatchTask<P_IN, P_OUT>> { |
| private final MatchOp<P_OUT> op; |
| |
| /** |
| * Constructor for root node |
| */ |
| MatchTask(MatchOp<P_OUT> op, PipelineHelper<P_OUT> helper, |
| Spliterator<P_IN> spliterator) { |
| super(helper, spliterator); |
| this.op = op; |
| } |
| |
| /** |
| * Constructor for non-root node |
| */ |
| MatchTask(MatchTask<P_IN, P_OUT> parent, Spliterator<P_IN> spliterator) { |
| super(parent, spliterator); |
| this.op = parent.op; |
| } |
| |
| @Override |
| protected MatchTask<P_IN, P_OUT> makeChild(Spliterator<P_IN> 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; |
| } |
| } |
| } |
| |