1 /* 2 * Copyright (c) 2012, 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 * A factory for creating instances of a short-circuiting {@code TerminalOp} 37 * that evaluates a predicate on the elements of a stream and determines whether 38 * all, any or none of those elements match the predicate. 39 * 40 * @since 1.8 41 */ 42 final 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, boolean shortCircuitResult) { 64 this.stopOnPredicateMatches = stopOnPredicateMatches; 65 this.shortCircuitResult = shortCircuitResult; 66 } 67 } 68 69 /** 70 * Constructs a {@code TerminalOp} for the given predicate and quantified 71 * match criteria 72 * 73 * @param predicate The {@code Predicate} to apply to stream elements 74 * @param matchKind The kind of quantified match (all, any, none) 75 * @param <T> The type of stream elements 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, MatchKind matchKind) { 80 Objects.requireNonNull(predicate); 81 Objects.requireNonNull(matchKind); 82 class MatchSink extends BooleanTerminalSink<T> { 83 MatchSink() { 84 super(matchKind); 85 } 86 87 @Override 88 public void accept(T t) { 89 // @@@ assert !stop when SortedOp supports short-circuit on Sink.end 90 // for sequential operations 91 if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { 92 stop = true; 93 value = matchKind.shortCircuitResult; 94 } 95 } 96 } 97 98 // @@@ Change to return MatchSink::new when compiler and runtime bugs are fixed 99 Supplier<BooleanTerminalSink<T>> s = new Supplier<BooleanTerminalSink<T>>() { 100 @Override 101 public BooleanTerminalSink<T> get() {return new MatchSink();} 102 }; 103 return new MatchOp<>(StreamShape.REFERENCE, matchKind, s); 104 } 105 106 /** 107 * Constructs a {@code TerminalOp} for the given predicate and quantified 108 * match criteria for an {@code IntStream} 109 * 110 * @param predicate The {@code Predicate} to apply to stream elements 111 * @param matchKind The kind of quantified match (all, any, none) 112 * @return A {@code TerminalOp} implementing the desired quantified match 113 * criteria 114 */ 115 public static TerminalOp<Integer, Boolean> makeInt(IntPredicate predicate, MatchKind matchKind) { 116 Objects.requireNonNull(predicate); 117 Objects.requireNonNull(matchKind); 118 class MatchSink extends BooleanTerminalSink<Integer> implements Sink.OfInt { 119 MatchSink() { 120 super(matchKind); 121 } 122 123 @Override 124 public void accept(int t) { 125 if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { 126 stop = true; 127 value = matchKind.shortCircuitResult; 128 } 129 } 130 } 131 132 Supplier<BooleanTerminalSink<Integer>> s = new Supplier<BooleanTerminalSink<Integer>>() { 133 @Override 134 public BooleanTerminalSink<Integer> get() {return new MatchSink();} 135 }; 136 return new MatchOp<>(StreamShape.INT_VALUE, matchKind, s); 137 } 138 139 /** 140 * Constructs a {@code TerminalOp} for the given predicate and quantified 141 * match criteria for a {@code LongStream} 142 * 143 * @param predicate The {@code Predicate} to apply to stream elements 144 * @param matchKind The kind of quantified match (all, any, none) 145 * @return A {@code TerminalOp} implementing the desired quantified match 146 * criteria 147 */ 148 public static TerminalOp<Long, Boolean> makeLong(LongPredicate predicate, MatchKind matchKind) { 149 Objects.requireNonNull(predicate); 150 Objects.requireNonNull(matchKind); 151 class MatchSink extends BooleanTerminalSink<Long> implements Sink.OfLong { 152 153 MatchSink() { 154 super(matchKind); 155 } 156 157 @Override 158 public void accept(long t) { 159 if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { 160 stop = true; 161 value = matchKind.shortCircuitResult; 162 } 163 } 164 } 165 166 Supplier<BooleanTerminalSink<Long>> s = new Supplier<BooleanTerminalSink<Long>>() { 167 @Override 168 public BooleanTerminalSink<Long> get() {return new MatchSink();} 169 }; 170 return new MatchOp<>(StreamShape.LONG_VALUE, matchKind, s); 171 } 172 173 /** 174 * Constructs a {@code TerminalOp} for the given predicate and quantified 175 * match criteria for a {@code DoubleStream} 176 * 177 * @param predicate The {@code Predicate} to apply to stream elements 178 * @param matchKind The kind of quantified match (all, any, none) 179 * @return A {@code TerminalOp} implementing the desired quantified match 180 * criteria 181 */ 182 public static TerminalOp<Double, Boolean> makeDouble(DoublePredicate predicate, MatchKind matchKind) { 183 Objects.requireNonNull(predicate); 184 Objects.requireNonNull(matchKind); 185 class MatchSink extends BooleanTerminalSink<Double> implements Sink.OfDouble { 186 187 MatchSink() { 188 super(matchKind); 189 } 190 191 @Override 192 public void accept(double t) { 193 if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { 194 stop = true; 195 value = matchKind.shortCircuitResult; 196 } 197 } 198 } 199 200 Supplier<BooleanTerminalSink<Double>> s = new Supplier<BooleanTerminalSink<Double>>() { 201 @Override 202 public BooleanTerminalSink<Double> get() {return new MatchSink();} 203 }; 204 return new MatchOp<>(StreamShape.DOUBLE_VALUE, matchKind, s); 205 } 206 207 /** 208 * A short-circuiting {@code TerminalOp} that evaluates a predicate on the 209 * elements of a stream and determines whether all, any or none of those 210 * elements match the predicate. 211 * 212 * @param <T> The output type of the stream pipeline 213 */ 214 private static final class MatchOp<T> implements TerminalOp<T, Boolean> { 215 private final StreamShape inputShape; 216 final MatchKind matchKind; 217 final Supplier<BooleanTerminalSink<T>> sinkSupplier; 218 219 /** 220 * Constructs a {@code MatchOp} 221 * 222 * @param shape The output shape of the stream pipeline 223 * @param matchKind The kind of quantified match (all, any, none) 224 * @param sinkSupplier {@code Supplier} for a {@code Sink} of the 225 * appropriate shape which implements the matching operation 226 */ 227 MatchOp(StreamShape shape, MatchKind matchKind, Supplier<BooleanTerminalSink<T>> sinkSupplier) { 228 this.inputShape = shape; 229 this.matchKind = matchKind; 230 this.sinkSupplier = sinkSupplier; 231 } 232 233 @Override 234 public int getOpFlags() { 235 return StreamOpFlag.IS_SHORT_CIRCUIT | StreamOpFlag.NOT_ORDERED; 236 } 237 238 @Override 239 public StreamShape inputShape() { 240 return inputShape; 241 } 242 243 @Override 244 public <S> Boolean evaluateSequential(PipelineHelper<S, T> helper) { 245 return helper.into(sinkSupplier.get(), helper.sourceSpliterator()).getAndClearState(); 246 } 247 248 @Override 249 public <S> Boolean evaluateParallel(PipelineHelper<S, T> helper) { 250 // Approach for parallel implementation: 251 // - Decompose as per usual 252 // - run match on leaf chunks, call result "b" 253 // - if b == matchKind.shortCircuitOn, complete early and return b 254 // - else if we complete normally, return !shortCircuitOn 255 256 return new MatchTask<>(this, helper).invoke(); 257 } 258 } 259 260 /** 261 * Boolean specific terminal sink to avoid the boxing costs when returning 262 * results. Subclasses implement the shape-specific functionality. 263 * 264 * @param <T> The output type of the stream pipeline 265 */ 266 private static abstract class BooleanTerminalSink<T> implements Sink<T> { 267 boolean stop; 268 boolean value; 269 270 BooleanTerminalSink(MatchKind matchKind) { 271 value = !matchKind.shortCircuitResult; 272 } 273 274 public boolean getAndClearState() { 275 return value; 276 } 277 278 @Override 279 public boolean cancellationRequested() { 280 return stop; 281 } 282 } 283 284 /** 285 * ForkJoinTask implementation to implement a parallel short-circuiting 286 * quantified match 287 * 288 * @param <S> The type of source elements for the pipeline 289 * @param <T> The type of output elements for the pipeline 290 */ 291 private static final class MatchTask<S, T> extends AbstractShortCircuitTask<S, T, Boolean, MatchTask<S, T>> { 292 private final MatchOp<T> op; 293 294 MatchTask(MatchOp<T> op, PipelineHelper<S, T> helper) { 295 super(helper); 296 this.op = op; 297 } 298 299 MatchTask(MatchTask<S, T> parent, Spliterator<S> spliterator) { 300 super(parent, spliterator); 301 this.op = parent.op; 302 } 303 304 @Override 305 protected MatchTask<S, T> makeChild(Spliterator<S> spliterator) { 306 return new MatchTask<>(this, spliterator); 307 } 308 309 @Override 310 protected Boolean doLeaf() { 311 boolean b = helper.into(op.sinkSupplier.get(), spliterator).getAndClearState(); 312 if (b == op.matchKind.shortCircuitResult) 313 shortCircuit(b); 314 return null; 315 } 316 317 @Override 318 protected Boolean getEmptyResult() { 319 return !op.matchKind.shortCircuitResult; 320 } 321 } 322 } 323