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 any elements match the predicate? */ 52 ANY(true, true), 53 54 /** Do all 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 abstract static 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