1 /*
2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 package java.util.stream;
26 
27 import java.util.Objects;
28 import java.util.Spliterator;
29 import java.util.function.DoublePredicate;
30 import java.util.function.IntPredicate;
31 import java.util.function.LongPredicate;
32 import java.util.function.Predicate;
33 import java.util.function.Supplier;
34 
35 /**
36  * Factory for instances of a short-circuiting {@code TerminalOp} that implement
37  * quantified predicate matching on the elements of a stream. Supported variants
38  * include match-all, match-any, and match-none.
39  *
40  * @since 1.8
41  */
42 final class MatchOps {
43 
MatchOps()44     private MatchOps() { }
45 
46     /**
47      * Enum describing quantified match options -- all match, any match, none
48      * match.
49      */
50     enum MatchKind {
51         /** Do all elements match the predicate? */
52         ANY(true, true),
53 
54         /** Do any elements match the predicate? */
55         ALL(false, false),
56 
57         /** Do no elements match the predicate? */
58         NONE(true, false);
59 
60         private final boolean stopOnPredicateMatches;
61         private final boolean shortCircuitResult;
62 
MatchKind(boolean stopOnPredicateMatches, boolean shortCircuitResult)63         private MatchKind(boolean stopOnPredicateMatches,
64                           boolean shortCircuitResult) {
65             this.stopOnPredicateMatches = stopOnPredicateMatches;
66             this.shortCircuitResult = shortCircuitResult;
67         }
68     }
69 
70     /**
71      * Constructs a quantified predicate matcher for a Stream.
72      *
73      * @param <T> the type of stream elements
74      * @param predicate the {@code Predicate} to apply to stream elements
75      * @param matchKind the kind of quantified match (all, any, none)
76      * @return a {@code TerminalOp} implementing the desired quantified match
77      *         criteria
78      */
makeRef(Predicate<? super T> predicate, MatchKind matchKind)79     public static <T> TerminalOp<T, Boolean> makeRef(Predicate<? super T> predicate,
80             MatchKind matchKind) {
81         Objects.requireNonNull(predicate);
82         Objects.requireNonNull(matchKind);
83         class MatchSink extends BooleanTerminalSink<T> {
84             MatchSink() {
85                 super(matchKind);
86             }
87 
88             @Override
89             public void accept(T t) {
90                 if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) {
91                     stop = true;
92                     value = matchKind.shortCircuitResult;
93                 }
94             }
95         }
96 
97         return new MatchOp<>(StreamShape.REFERENCE, matchKind, MatchSink::new);
98     }
99 
100     /**
101      * Constructs a quantified predicate matcher for an {@code IntStream}.
102      *
103      * @param predicate the {@code Predicate} to apply to stream elements
104      * @param matchKind the kind of quantified match (all, any, none)
105      * @return a {@code TerminalOp} implementing the desired quantified match
106      *         criteria
107      */
makeInt(IntPredicate predicate, MatchKind matchKind)108     public static TerminalOp<Integer, Boolean> makeInt(IntPredicate predicate,
109                                                        MatchKind matchKind) {
110         Objects.requireNonNull(predicate);
111         Objects.requireNonNull(matchKind);
112         class MatchSink extends BooleanTerminalSink<Integer> implements Sink.OfInt {
113             MatchSink() {
114                 super(matchKind);
115             }
116 
117             @Override
118             public void accept(int t) {
119                 if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) {
120                     stop = true;
121                     value = matchKind.shortCircuitResult;
122                 }
123             }
124         }
125 
126         return new MatchOp<>(StreamShape.INT_VALUE, matchKind, MatchSink::new);
127     }
128 
129     /**
130      * Constructs a quantified predicate matcher for a {@code LongStream}.
131      *
132      * @param predicate the {@code Predicate} to apply to stream elements
133      * @param matchKind the kind of quantified match (all, any, none)
134      * @return a {@code TerminalOp} implementing the desired quantified match
135      *         criteria
136      */
makeLong(LongPredicate predicate, MatchKind matchKind)137     public static TerminalOp<Long, Boolean> makeLong(LongPredicate predicate,
138                                                      MatchKind matchKind) {
139         Objects.requireNonNull(predicate);
140         Objects.requireNonNull(matchKind);
141         class MatchSink extends BooleanTerminalSink<Long> implements Sink.OfLong {
142 
143             MatchSink() {
144                 super(matchKind);
145             }
146 
147             @Override
148             public void accept(long t) {
149                 if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) {
150                     stop = true;
151                     value = matchKind.shortCircuitResult;
152                 }
153             }
154         }
155 
156         return new MatchOp<>(StreamShape.LONG_VALUE, matchKind, MatchSink::new);
157     }
158 
159     /**
160      * Constructs a quantified predicate matcher for a {@code DoubleStream}.
161      *
162      * @param predicate the {@code Predicate} to apply to stream elements
163      * @param matchKind the kind of quantified match (all, any, none)
164      * @return a {@code TerminalOp} implementing the desired quantified match
165      *         criteria
166      */
makeDouble(DoublePredicate predicate, MatchKind matchKind)167     public static TerminalOp<Double, Boolean> makeDouble(DoublePredicate predicate,
168                                                          MatchKind matchKind) {
169         Objects.requireNonNull(predicate);
170         Objects.requireNonNull(matchKind);
171         class MatchSink extends BooleanTerminalSink<Double> implements Sink.OfDouble {
172 
173             MatchSink() {
174                 super(matchKind);
175             }
176 
177             @Override
178             public void accept(double t) {
179                 if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) {
180                     stop = true;
181                     value = matchKind.shortCircuitResult;
182                 }
183             }
184         }
185 
186         return new MatchOp<>(StreamShape.DOUBLE_VALUE, matchKind, MatchSink::new);
187     }
188 
189     /**
190      * A short-circuiting {@code TerminalOp} that evaluates a predicate on the
191      * elements of a stream and determines whether all, any or none of those
192      * elements match the predicate.
193      *
194      * @param <T> the output type of the stream pipeline
195      */
196     private static final class MatchOp<T> implements TerminalOp<T, Boolean> {
197         private final StreamShape inputShape;
198         final MatchKind matchKind;
199         final Supplier<BooleanTerminalSink<T>> sinkSupplier;
200 
201         /**
202          * Constructs a {@code MatchOp}.
203          *
204          * @param shape the output shape of the stream pipeline
205          * @param matchKind the kind of quantified match (all, any, none)
206          * @param sinkSupplier {@code Supplier} for a {@code Sink} of the
207          *        appropriate shape which implements the matching operation
208          */
MatchOp(StreamShape shape, MatchKind matchKind, Supplier<BooleanTerminalSink<T>> sinkSupplier)209         MatchOp(StreamShape shape,
210                 MatchKind matchKind,
211                 Supplier<BooleanTerminalSink<T>> sinkSupplier) {
212             this.inputShape = shape;
213             this.matchKind = matchKind;
214             this.sinkSupplier = sinkSupplier;
215         }
216 
217         @Override
getOpFlags()218         public int getOpFlags() {
219             return StreamOpFlag.IS_SHORT_CIRCUIT | StreamOpFlag.NOT_ORDERED;
220         }
221 
222         @Override
inputShape()223         public StreamShape inputShape() {
224             return inputShape;
225         }
226 
227         @Override
evaluateSequential(PipelineHelper<T> helper, Spliterator<S> spliterator)228         public <S> Boolean evaluateSequential(PipelineHelper<T> helper,
229                                               Spliterator<S> spliterator) {
230             return helper.wrapAndCopyInto(sinkSupplier.get(), spliterator).getAndClearState();
231         }
232 
233         @Override
evaluateParallel(PipelineHelper<T> helper, Spliterator<S> spliterator)234         public <S> Boolean evaluateParallel(PipelineHelper<T> helper,
235                                             Spliterator<S> spliterator) {
236             // Approach for parallel implementation:
237             // - Decompose as per usual
238             // - run match on leaf chunks, call result "b"
239             // - if b == matchKind.shortCircuitOn, complete early and return b
240             // - else if we complete normally, return !shortCircuitOn
241 
242             return new MatchTask<>(this, helper, spliterator).invoke();
243         }
244     }
245 
246     /**
247      * Boolean specific terminal sink to avoid the boxing costs when returning
248      * results.  Subclasses implement the shape-specific functionality.
249      *
250      * @param <T> The output type of the stream pipeline
251      */
252     private static abstract class BooleanTerminalSink<T> implements Sink<T> {
253         boolean stop;
254         boolean value;
255 
BooleanTerminalSink(MatchKind matchKind)256         BooleanTerminalSink(MatchKind matchKind) {
257             value = !matchKind.shortCircuitResult;
258         }
259 
getAndClearState()260         public boolean getAndClearState() {
261             return value;
262         }
263 
264         @Override
cancellationRequested()265         public boolean cancellationRequested() {
266             return stop;
267         }
268     }
269 
270     /**
271      * ForkJoinTask implementation to implement a parallel short-circuiting
272      * quantified match
273      *
274      * @param <P_IN> the type of source elements for the pipeline
275      * @param <P_OUT> the type of output elements for the pipeline
276      */
277     @SuppressWarnings("serial")
278     private static final class MatchTask<P_IN, P_OUT>
279             extends AbstractShortCircuitTask<P_IN, P_OUT, Boolean, MatchTask<P_IN, P_OUT>> {
280         private final MatchOp<P_OUT> op;
281 
282         /**
283          * Constructor for root node
284          */
MatchTask(MatchOp<P_OUT> op, PipelineHelper<P_OUT> helper, Spliterator<P_IN> spliterator)285         MatchTask(MatchOp<P_OUT> op, PipelineHelper<P_OUT> helper,
286                   Spliterator<P_IN> spliterator) {
287             super(helper, spliterator);
288             this.op = op;
289         }
290 
291         /**
292          * Constructor for non-root node
293          */
MatchTask(MatchTask<P_IN, P_OUT> parent, Spliterator<P_IN> spliterator)294         MatchTask(MatchTask<P_IN, P_OUT> parent, Spliterator<P_IN> spliterator) {
295             super(parent, spliterator);
296             this.op = parent.op;
297         }
298 
299         @Override
makeChild(Spliterator<P_IN> spliterator)300         protected MatchTask<P_IN, P_OUT> makeChild(Spliterator<P_IN> spliterator) {
301             return new MatchTask<>(this, spliterator);
302         }
303 
304         @Override
doLeaf()305         protected Boolean doLeaf() {
306             boolean b = helper.wrapAndCopyInto(op.sinkSupplier.get(), spliterator).getAndClearState();
307             if (b == op.matchKind.shortCircuitResult)
308                 shortCircuit(b);
309             return null;
310         }
311 
312         @Override
getEmptyResult()313         protected Boolean getEmptyResult() {
314             return !op.matchKind.shortCircuitResult;
315         }
316     }
317 }
318 
319