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 */
25package java.util.stream;
26
27import java.util.Objects;
28import java.util.Spliterator;
29import java.util.function.DoublePredicate;
30import java.util.function.IntPredicate;
31import java.util.function.LongPredicate;
32import java.util.function.Predicate;
33import 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 */
42final class MatchOps {
43
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
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     */
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     */
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     */
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     */
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         */
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
218        public int getOpFlags() {
219            return StreamOpFlag.IS_SHORT_CIRCUIT | StreamOpFlag.NOT_ORDERED;
220        }
221
222        @Override
223        public StreamShape inputShape() {
224            return inputShape;
225        }
226
227        @Override
228        public <S> Boolean evaluateSequential(PipelineHelper<T> helper,
229                                              Spliterator<S> spliterator) {
230            return helper.wrapAndCopyInto(sinkSupplier.get(), spliterator).getAndClearState();
231        }
232
233        @Override
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 abstract static class BooleanTerminalSink<T> implements Sink<T> {
253        boolean stop;
254        boolean value;
255
256        BooleanTerminalSink(MatchKind matchKind) {
257            value = !matchKind.shortCircuitResult;
258        }
259
260        public boolean getAndClearState() {
261            return value;
262        }
263
264        @Override
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  the type of source elements for the pipeline
275     * @param  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         */
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         */
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
300        protected MatchTask<P_IN, P_OUT> makeChild(Spliterator<P_IN> spliterator) {
301            return new MatchTask<>(this, spliterator);
302        }
303
304        @Override
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
313        protected Boolean getEmptyResult() {
314            return !op.matchKind.shortCircuitResult;
315        }
316    }
317}
318
319