Emulate Java 8 Streams. Change-Id: Ifbe0c001f1a53f5cb9c4c1a77ee203b2660d4e23
diff --git a/user/super/com/google/gwt/emul/java/util/Arrays.java b/user/super/com/google/gwt/emul/java/util/Arrays.java index 72da442..4229fe6 100644 --- a/user/super/com/google/gwt/emul/java/util/Arrays.java +++ b/user/super/com/google/gwt/emul/java/util/Arrays.java
@@ -35,6 +35,11 @@ import java.util.function.IntUnaryOperator; import java.util.function.LongBinaryOperator; import java.util.function.UnaryOperator; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; import javaemul.internal.ArrayHelper; import javaemul.internal.LongCompareHolder; @@ -1452,6 +1457,38 @@ Spliterator.IMMUTABLE | Spliterator.ORDERED); } + public static DoubleStream stream(double[] array) { + return stream(array, 0, array.length); + } + + public static DoubleStream stream(double[] array, int startInclusive, int endExclusive) { + return StreamSupport.doubleStream(spliterator(array, startInclusive, endExclusive), false); + } + + public static IntStream stream(int[] array) { + return stream(array, 0, array.length); + } + + public static IntStream stream(int[] array, int startInclusive, int endExclusive) { + return StreamSupport.intStream(spliterator(array, startInclusive, endExclusive), false); + } + + public static LongStream stream(long[] array) { + return stream(array, 0, array.length); + } + + public static LongStream stream(long[] array, int startInclusive, int endExclusive) { + return StreamSupport.longStream(spliterator(array, startInclusive, endExclusive), false); + } + + public static <T> Stream<T> stream(T[] array) { + return stream(array, 0, array.length); + } + + public static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive) { + return StreamSupport.stream(spliterator(array, startInclusive, endExclusive), false); + } + public static String toString(boolean[] a) { if (a == null) { return "null";
diff --git a/user/super/com/google/gwt/emul/java/util/Collection.java b/user/super/com/google/gwt/emul/java/util/Collection.java index 379be6d..d678e05 100644 --- a/user/super/com/google/gwt/emul/java/util/Collection.java +++ b/user/super/com/google/gwt/emul/java/util/Collection.java
@@ -18,6 +18,8 @@ import static javaemul.internal.InternalPreconditions.checkNotNull; import java.util.function.Predicate; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * General-purpose interface for storing collections of objects. @@ -49,6 +51,11 @@ @Override Iterator<E> iterator(); + default Stream<E> parallelStream() { + // no parallelism in gwt + return stream(); + } + boolean remove(Object o); boolean removeAll(Collection<?> c); @@ -74,6 +81,10 @@ return Spliterators.spliterator(this, 0); } + default Stream<E> stream() { + return StreamSupport.stream(spliterator(), false); + } + Object[] toArray(); <T> T[] toArray(T[] a);
diff --git a/user/super/com/google/gwt/emul/java/util/stream/BaseStream.java b/user/super/com/google/gwt/emul/java/util/stream/BaseStream.java new file mode 100644 index 0000000..3d08e80 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/stream/BaseStream.java
@@ -0,0 +1,45 @@ +/* + * Copyright 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package java.util.stream; + +import java.util.Iterator; +import java.util.Spliterator; + +/** + * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/BaseStream.html"> + * the official Java API doc</a> for details. + * + * @param <T> the contents of the stream + * @param <S> the type of stream implementing this interface + */ +public interface BaseStream<T,S extends BaseStream<T,S>> extends AutoCloseable { + Iterator<T> iterator(); + + Spliterator<T> spliterator(); + + boolean isParallel(); + + S sequential(); + + S parallel(); + + S unordered(); + + S onClose(Runnable closeHandler); + + void close(); +}
diff --git a/user/super/com/google/gwt/emul/java/util/stream/DoubleStream.java b/user/super/com/google/gwt/emul/java/util/stream/DoubleStream.java new file mode 100644 index 0000000..492f621 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/stream/DoubleStream.java
@@ -0,0 +1,1028 @@ +/* + * Copyright 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package java.util.stream; + +import static javaemul.internal.InternalPreconditions.checkCriticalState; +import static javaemul.internal.InternalPreconditions.checkNotNull; +import static javaemul.internal.InternalPreconditions.checkState; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.DoubleSummaryStatistics; +import java.util.HashSet; +import java.util.OptionalDouble; +import java.util.PrimitiveIterator; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.DoubleBinaryOperator; +import java.util.function.DoubleConsumer; +import java.util.function.DoubleFunction; +import java.util.function.DoublePredicate; +import java.util.function.DoubleSupplier; +import java.util.function.DoubleToIntFunction; +import java.util.function.DoubleToLongFunction; +import java.util.function.DoubleUnaryOperator; +import java.util.function.IntConsumer; +import java.util.function.LongConsumer; +import java.util.function.ObjDoubleConsumer; +import java.util.function.Supplier; + +/** + * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/DoubleStream.html"> + * the official Java API doc</a> for details. + */ +public interface DoubleStream extends BaseStream<Double,DoubleStream> { + + /** + * Value holder for various stream operations. + */ + static final class ValueConsumer implements DoubleConsumer { + double value; + + @Override + public void accept(double value) { + this.value = value; + } + } + + static DoubleStream.Builder builder() { + return new Builder() { + private double[] items = new double[0]; + + @Override + public void accept(double t) { + checkState(items != null, "Builder already built"); + items[items.length] = t; + } + + @Override + public DoubleStream build() { + checkCriticalState(items != null, "Builder already built"); + DoubleStream stream = Arrays.stream(items); + items = null; + return stream; + } + }; + } + + static DoubleStream concat(DoubleStream a, DoubleStream b) { + // This is nearly the same as flatMap, but inlined, wrapped around a single spliterator of + // these two objects, and without close() called as the stream progresses. Instead, close is + // invoked as part of the resulting stream's own onClose, so that either can fail without + // affecting the other, and correctly collecting suppressed exceptions. + + // TODO replace this flatMap-ish spliterator with one that directly combines the two root + // streams + Spliterator<? extends DoubleStream> spliteratorOfStreams = + Arrays.asList(a, b).spliterator(); + DoubleStream result = new DoubleStreamSource(null, new Spliterators.AbstractDoubleSpliterator( + Long.MAX_VALUE, + 0 + ) { + DoubleStream nextStream; + Spliterator.OfDouble next; + + @Override + public boolean tryAdvance(DoubleConsumer action) { + // look for a new spliterator + while (advanceToNextSpliterator()) { + // if we have one, try to read and use it + if (next.tryAdvance(action)) { + return true; + } else { + nextStream = null; + // failed, null it out so we can find another + next = null; + } + } + return false; + } + + private boolean advanceToNextSpliterator() { + while (next == null) { + if (!spliteratorOfStreams.tryAdvance(n -> { + if (n != null) { + nextStream = n; + next = n.spliterator(); + } + })) { + return false; + } + } + return true; + } + }); + + result.onClose(a::close); + result.onClose(b::close); + + return result; + } + + static DoubleStream empty() { + return new EmptyDoubleStreamSource(null); + } + + static DoubleStream generate(DoubleSupplier s) { + return StreamSupport.doubleStream(new Spliterators.AbstractDoubleSpliterator( + Long.MAX_VALUE, + Spliterator.IMMUTABLE | Spliterator.ORDERED + ) { + @Override + public boolean tryAdvance(DoubleConsumer action) { + action.accept(s.getAsDouble()); + return true; + } + }, false); + } + + static DoubleStream iterate(double seed, DoubleUnaryOperator f) { + return StreamSupport.doubleStream(new Spliterators.AbstractDoubleSpliterator( + Long.MAX_VALUE, + Spliterator.IMMUTABLE | Spliterator.ORDERED + ) { + private double next = seed; + + @Override + public boolean tryAdvance(DoubleConsumer action) { + action.accept(next); + next = f.applyAsDouble(next); + return true; + } + }, false); + } + + static DoubleStream of(double... values) { + return Arrays.stream(values); + } + + static DoubleStream of(double t) { + // TODO consider a splittable that returns only a single value + return of(new double[]{t}); + } + + /** + * See + * <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/DoubleStream.Builder.html"> + * the official Java API doc</a> for details. + */ + public interface Builder extends DoubleConsumer { + @Override + void accept(double t); + + default DoubleStream.Builder add(double t) { + accept(t); + return this; + } + + DoubleStream build(); + } + + boolean allMatch(DoublePredicate predicate); + + boolean anyMatch(DoublePredicate predicate); + + OptionalDouble average(); + + Stream<Double> boxed(); + + <R> R collect(Supplier<R> supplier, ObjDoubleConsumer<R> accumulator, BiConsumer<R,R> combiner); + + long count(); + + DoubleStream distinct(); + + DoubleStream filter(DoublePredicate predicate); + + OptionalDouble findAny(); + + OptionalDouble findFirst(); + + DoubleStream flatMap(DoubleFunction<? extends DoubleStream> mapper); + + void forEach(DoubleConsumer action); + + void forEachOrdered(DoubleConsumer action); + + PrimitiveIterator.OfDouble iterator(); + + DoubleStream limit(long maxSize); + + DoubleStream map(DoubleUnaryOperator mapper); + + IntStream mapToInt(DoubleToIntFunction mapper); + + LongStream mapToLong(DoubleToLongFunction mapper); + + <U> Stream<U> mapToObj(DoubleFunction<? extends U> mapper); + + OptionalDouble max(); + + OptionalDouble min(); + + boolean noneMatch(DoublePredicate predicate); + + DoubleStream parallel(); + + DoubleStream peek(DoubleConsumer action); + + OptionalDouble reduce(DoubleBinaryOperator op); + + double reduce(double identity, DoubleBinaryOperator op); + + DoubleStream sequential(); + + DoubleStream skip(long n); + + DoubleStream sorted(); + + Spliterator.OfDouble spliterator(); + + double sum(); + + DoubleSummaryStatistics summaryStatistics(); + + double[] toArray(); + + /** + * Represents an empty stream, doing nothing for all methods. + */ + static class EmptyDoubleStreamSource extends TerminatableStream<EmptyDoubleStreamSource> + implements DoubleStream { + public EmptyDoubleStreamSource(TerminatableStream<?> previous) { + super(previous); + } + + @Override + public DoubleStream filter(DoublePredicate predicate) { + throwIfTerminated(); + return this; + } + + @Override + public DoubleStream map(DoubleUnaryOperator mapper) { + throwIfTerminated(); + return this; + } + + @Override + public <U> Stream<U> mapToObj(DoubleFunction<? extends U> mapper) { + throwIfTerminated(); + return new Stream.EmptyStreamSource<U>(this); + } + + @Override + public IntStream mapToInt(DoubleToIntFunction mapper) { + throwIfTerminated(); + return new IntStream.EmptyIntStreamSource(this); + } + + @Override + public LongStream mapToLong(DoubleToLongFunction mapper) { + throwIfTerminated(); + return new LongStream.EmptyLongStreamSource(this); + } + + @Override + public DoubleStream flatMap(DoubleFunction<? extends DoubleStream> mapper) { + throwIfTerminated(); + return this; + } + + @Override + public DoubleStream distinct() { + throwIfTerminated(); + return this; + } + + @Override + public DoubleStream sorted() { + throwIfTerminated(); + return this; + } + + @Override + public DoubleStream peek(DoubleConsumer action) { + throwIfTerminated(); + return this; + } + + @Override + public DoubleStream limit(long maxSize) { + throwIfTerminated(); + checkState(maxSize >= 0, "maxSize may not be negative"); + return this; + } + + @Override + public DoubleStream skip(long n) { + throwIfTerminated(); + checkState(n >= 0, "n may not be negative"); + return this; + } + + @Override + public void forEach(DoubleConsumer action) { + terminate(); + // do nothing + } + + @Override + public void forEachOrdered(DoubleConsumer action) { + terminate(); + // do nothing + } + + @Override + public double[] toArray() { + terminate(); + return new double[0]; + } + + @Override + public double reduce(double identity, DoubleBinaryOperator op) { + terminate(); + return identity; + } + + @Override + public OptionalDouble reduce(DoubleBinaryOperator op) { + terminate(); + return OptionalDouble.empty(); + } + + @Override + public <R> R collect(Supplier<R> supplier, + ObjDoubleConsumer<R> accumulator, + BiConsumer<R, R> combiner) { + terminate(); + return supplier.get(); + } + + @Override + public double sum() { + terminate(); + return 0; + } + + @Override + public OptionalDouble min() { + terminate(); + return OptionalDouble.empty(); + } + + @Override + public OptionalDouble max() { + terminate(); + return OptionalDouble.empty(); + } + + @Override + public long count() { + terminate(); + return 0; + } + + @Override + public OptionalDouble average() { + terminate(); + return OptionalDouble.empty(); + } + + @Override + public DoubleSummaryStatistics summaryStatistics() { + terminate(); + return new DoubleSummaryStatistics(); + } + + @Override + public boolean anyMatch(DoublePredicate predicate) { + terminate(); + return false; + } + + @Override + public boolean allMatch(DoublePredicate predicate) { + terminate(); + return true; + } + + @Override + public boolean noneMatch(DoublePredicate predicate) { + terminate(); + return true; + } + + @Override + public OptionalDouble findFirst() { + terminate(); + return OptionalDouble.empty(); + } + + @Override + public OptionalDouble findAny() { + terminate(); + return OptionalDouble.empty(); + } + + @Override + public Stream<Double> boxed() { + throwIfTerminated(); + return new Stream.EmptyStreamSource<Double>(this); + } + + @Override + public DoubleStream sequential() { + throwIfTerminated(); + return this; + } + + @Override + public DoubleStream parallel() { + throwIfTerminated(); + return this; + } + + @Override + public PrimitiveIterator.OfDouble iterator() { + return Spliterators.iterator(spliterator()); + } + + @Override + public Spliterator.OfDouble spliterator() { + terminate(); + return Spliterators.emptyDoubleSpliterator(); + } + + @Override + public boolean isParallel() { + throwIfTerminated(); + return false; + } + + @Override + public DoubleStream unordered() { + throwIfTerminated(); + return this; + } + } + + /** + * Double to Int map spliterator. + */ + static final class MapToIntSpliterator extends Spliterators.AbstractIntSpliterator { + private final DoubleToIntFunction map; + private final Spliterator.OfDouble original; + + public MapToIntSpliterator(DoubleToIntFunction map, Spliterator.OfDouble original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final IntConsumer action) { + return original.tryAdvance((double u) -> action.accept(map.applyAsInt(u))); + } + } + + /** + * Double to Object map spliterator. + * @param <T> the type of Object in the spliterator + */ + static final class MapToObjSpliterator<T> extends Spliterators.AbstractSpliterator<T> { + private final DoubleFunction<? extends T> map; + private final Spliterator.OfDouble original; + + public MapToObjSpliterator(DoubleFunction<? extends T> map, Spliterator.OfDouble original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final Consumer<? super T> action) { + return original.tryAdvance((double u) -> action.accept(map.apply(u))); + } + } + + /** + * Double to Long map spliterator. + */ + static final class MapToLongSpliterator extends Spliterators.AbstractLongSpliterator { + private final DoubleToLongFunction map; + private final Spliterator.OfDouble original; + + public MapToLongSpliterator(DoubleToLongFunction map, Spliterator.OfDouble original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final LongConsumer action) { + return original.tryAdvance((double u) -> action.accept(map.applyAsLong(u))); + } + } + + /** + * Double to Double map spliterator. + */ + static final class MapToDoubleSpliterator extends Spliterators.AbstractDoubleSpliterator { + private final DoubleUnaryOperator map; + private final Spliterator.OfDouble original; + + public MapToDoubleSpliterator(DoubleUnaryOperator map, Spliterator.OfDouble original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final DoubleConsumer action) { + return original.tryAdvance((double u) -> action.accept(map.applyAsDouble(u))); + } + } + + /** + * Double filter spliterator. + */ + static final class FilterSpliterator extends Spliterators.AbstractDoubleSpliterator { + private final DoublePredicate filter; + private final Spliterator.OfDouble original; + + private boolean found; + + public FilterSpliterator(DoublePredicate filter, Spliterator.OfDouble original) { + super(original.estimateSize(), original.characteristics() & ~Spliterator.SIZED); + checkNotNull(filter); + this.filter = filter; + this.original = original; + } + + @Override + public Comparator<? super Double> getComparator() { + return original.getComparator(); + } + + @Override + public boolean tryAdvance(final DoubleConsumer action) { + found = false; + while (!found && original.tryAdvance((double item) -> { + if (filter.test(item)) { + found = true; + action.accept(item); + } + })) { + // do nothing, work is done in tryAdvance + } + + return found; + } + } + + /** + * Double skip spliterator. + */ + static final class SkipSpliterator extends Spliterators.AbstractDoubleSpliterator { + private long skip; + private final Spliterator.OfDouble original; + + public SkipSpliterator(long skip, Spliterator.OfDouble original) { + super( + original.hasCharacteristics(Spliterator.SIZED) + ? Math.max(0, original.estimateSize() - skip) + : Long.MAX_VALUE, + original.characteristics() + ); + this.skip = skip; + this.original = original; + } + + @Override + public Comparator<? super Double> getComparator() { + return original.getComparator(); + } + + @Override + public boolean tryAdvance(DoubleConsumer action) { + while (skip > 0) { + if (!original.tryAdvance((double ignore) -> { })) { + return false; + } + skip--; + } + return original.tryAdvance(action); + } + } + + /** + * Double limit spliterator. + */ + static final class LimitSpliterator extends Spliterators.AbstractDoubleSpliterator { + private final long limit; + private final Spliterator.OfDouble original; + private int position = 0; + + public LimitSpliterator(long limit, Spliterator.OfDouble original) { + super( + original.hasCharacteristics(Spliterator.SIZED) + ? Math.min(original.estimateSize(), limit) + : Long.MAX_VALUE, + original.characteristics() + ); + this.limit = limit; + this.original = original; + } + + @Override + public Comparator<? super Double> getComparator() { + return original.getComparator(); + } + + @Override + public boolean tryAdvance(DoubleConsumer action) { + if (limit <= position) { + return false; + } + boolean result = original.tryAdvance(action); + position++; + return result; + } + } + + /** + * Main implementation of DoubleStream, wrapping a single spliterator, and an optional parent + * stream. + */ + static class DoubleStreamSource extends TerminatableStream<DoubleStreamSource> + implements DoubleStream { + private final Spliterator.OfDouble spliterator; + + public DoubleStreamSource(TerminatableStream<?> previous, Spliterator.OfDouble spliterator) { + super(previous); + this.spliterator = spliterator; + } + + // terminals + + @Override + public void forEach(DoubleConsumer action) { + forEachOrdered(action); + } + + @Override + public void forEachOrdered(DoubleConsumer action) { + terminate(); + spliterator.forEachRemaining(action); + } + + @Override + public double[] toArray() { + terminate(); + double[] entries = new double[0]; + // this is legal in js, since the array will be backed by a JS array + spliterator.forEachRemaining((double value) -> entries[entries.length] = value); + + return entries; + } + + @Override + public double reduce(double identity, DoubleBinaryOperator op) { + terminate(); + ValueConsumer holder = new ValueConsumer(); + holder.value = identity; + spliterator.forEachRemaining((double value) -> { + holder.accept(op.applyAsDouble(holder.value, value)); + }); + return holder.value; + } + + @Override + public OptionalDouble reduce(DoubleBinaryOperator op) { + ValueConsumer holder = new ValueConsumer(); + if (spliterator.tryAdvance(holder)) { + return OptionalDouble.of(reduce(holder.value, op)); + } + terminate(); + return OptionalDouble.empty(); + } + + @Override + public <R> R collect(Supplier<R> supplier, + ObjDoubleConsumer<R> accumulator, + BiConsumer<R, R> combiner) { + terminate(); + final R acc = supplier.get(); + spliterator.forEachRemaining((double value) -> accumulator.accept(acc, value)); + return acc; + } + + @Override + public double sum() { + return summaryStatistics().getSum(); + } + + @Override + public OptionalDouble min() { + terminate(); + final ValueConsumer holder = new ValueConsumer(); + if (spliterator.tryAdvance((double value) -> holder.value = value)) { + spliterator.forEachRemaining((double value) -> { + holder.value = Math.min(holder.value, value); + }); + return OptionalDouble.of(holder.value); + } + return OptionalDouble.empty(); + } + + @Override + public OptionalDouble max() { + terminate(); + final ValueConsumer holder = new ValueConsumer(); + if (spliterator.tryAdvance((double value) -> holder.value = value)) { + spliterator.forEachRemaining((double value) -> { + holder.value = Math.max(holder.value, value); + }); + return OptionalDouble.of(holder.value); + } + return OptionalDouble.empty(); + } + + @Override + public long count() { + terminate(); + long count = 0; + while (spliterator.tryAdvance((double value) -> { })) { + count++; + } + return count; + } + + @Override + public OptionalDouble average() { + DoubleSummaryStatistics stats = summaryStatistics(); + if (stats.getCount() == 0) { + return OptionalDouble.empty(); + } + return OptionalDouble.of(stats.getAverage()); + } + + @Override + public DoubleSummaryStatistics summaryStatistics() { + return collect( + DoubleSummaryStatistics::new, + // TODO switch to a lambda reference once #9340 is fixed + (doubleSummaryStatistics, value) -> doubleSummaryStatistics.accept(value), + DoubleSummaryStatistics::combine + ); + } + + @Override + public boolean anyMatch(DoublePredicate predicate) { + return filter(predicate).findFirst().isPresent(); + } + + @Override + public boolean allMatch(DoublePredicate predicate) { + return !anyMatch(predicate.negate()); + } + + @Override + public boolean noneMatch(DoublePredicate predicate) { + return !anyMatch(predicate); + } + + @Override + public OptionalDouble findFirst() { + terminate(); + ValueConsumer holder = new ValueConsumer(); + if (spliterator.tryAdvance(holder)) { + return OptionalDouble.of(holder.value); + } + return OptionalDouble.empty(); + } + + @Override + public OptionalDouble findAny() { + return findFirst(); + } + + @Override + public PrimitiveIterator.OfDouble iterator() { + return Spliterators.iterator(spliterator()); + } + + @Override + public Spliterator.OfDouble spliterator() { + terminate(); + return spliterator; + } + + // end terminals + + // intermediates + + @Override + public DoubleStream filter(DoublePredicate predicate) { + throwIfTerminated(); + return new DoubleStreamSource(this, new FilterSpliterator(predicate, spliterator)); + } + + @Override + public DoubleStream map(DoubleUnaryOperator mapper) { + throwIfTerminated(); + return new DoubleStreamSource(this, new MapToDoubleSpliterator(mapper, spliterator)); + } + + @Override + public <U> Stream<U> mapToObj(DoubleFunction<? extends U> mapper) { + throwIfTerminated(); + return new Stream.StreamSource(this, new MapToObjSpliterator<U>(mapper, spliterator)); + } + + @Override + public IntStream mapToInt(DoubleToIntFunction mapper) { + throwIfTerminated(); + return new IntStream.IntStreamSource(this, new MapToIntSpliterator(mapper, spliterator)); + } + + @Override + public LongStream mapToLong(DoubleToLongFunction mapper) { + throwIfTerminated(); + return new LongStream.LongStreamSource(this, new MapToLongSpliterator(mapper, spliterator)); + } + + @Override + public DoubleStream flatMap(DoubleFunction<? extends DoubleStream> mapper) { + throwIfTerminated(); + final Spliterator<? extends DoubleStream> spliteratorOfStreams = + new MapToObjSpliterator<DoubleStream>(mapper, spliterator); + return new DoubleStreamSource(this, new Spliterators.AbstractDoubleSpliterator( + Long.MAX_VALUE, + 0 + ) { + DoubleStream nextStream; + Spliterator.OfDouble next; + + @Override + public boolean tryAdvance(DoubleConsumer action) { + // look for a new spliterator + while (advanceToNextSpliterator()) { + // if we have one, try to read and use it + if (next.tryAdvance(action)) { + return true; + } else { + nextStream.close(); + nextStream = null; + // failed, null it out so we can find another + next = null; + } + } + return false; + } + + private boolean advanceToNextSpliterator() { + while (next == null) { + if (!spliteratorOfStreams.tryAdvance(n -> { + if (n != null) { + nextStream = n; + next = n.spliterator(); + } + })) { + return false; + } + } + return true; + } + }); + } + + @Override + public DoubleStream distinct() { + throwIfTerminated(); + HashSet<Double> seen = new HashSet<>(); + return filter(seen::add); + } + + @Override + public DoubleStream sorted() { + throwIfTerminated(); + return new DoubleStreamSource(this, new Spliterators.AbstractDoubleSpliterator( + spliterator.estimateSize(), + spliterator.characteristics() | Spliterator.SORTED + ) { + Spliterator.OfDouble ordered = null; + + @Override + public Comparator<? super Double> getComparator() { + return null; + } + + @Override + public boolean tryAdvance(DoubleConsumer action) { + if (ordered == null) { + double[] list = new double[0]; + spliterator.forEachRemaining((double item) -> list[list.length] = item); + Arrays.sort(list); + ordered = Spliterators.spliterator(list, characteristics()); + } + return ordered.tryAdvance(action); + } + }); + } + + @Override + public DoubleStream peek(DoubleConsumer action) { + checkNotNull(action); + throwIfTerminated(); + return new DoubleStreamSource(this, new Spliterators.AbstractDoubleSpliterator( + spliterator.estimateSize(), + spliterator.characteristics() + ) { + @Override + public boolean tryAdvance(final DoubleConsumer innerAction) { + return spliterator.tryAdvance(action.andThen(innerAction)); + } + }); + } + + @Override + public DoubleStream limit(long maxSize) { + throwIfTerminated(); + checkState(maxSize >= 0, "maxSize may not be negative"); + return new DoubleStreamSource(this, new LimitSpliterator(maxSize, spliterator)); + } + + @Override + public DoubleStream skip(long n) { + throwIfTerminated(); + checkState(n >= 0, "n may not be negative"); + if (n == 0) { + return this; + } + return new DoubleStreamSource(this, new SkipSpliterator(n, spliterator)); + } + + @Override + public Stream<Double> boxed() { + return mapToObj(Double::valueOf); + } + + @Override + public DoubleStream sequential() { + throwIfTerminated(); + return this; + } + + @Override + public DoubleStream parallel() { + throwIfTerminated(); + return this; + } + + @Override + public boolean isParallel() { + throwIfTerminated(); + return false; + } + + @Override + public DoubleStream unordered() { + throwIfTerminated(); + return this; + } + } +}
diff --git a/user/super/com/google/gwt/emul/java/util/stream/IntStream.java b/user/super/com/google/gwt/emul/java/util/stream/IntStream.java new file mode 100644 index 0000000..8a0816e --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/stream/IntStream.java
@@ -0,0 +1,1081 @@ +/* + * Copyright 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package java.util.stream; + +import static javaemul.internal.InternalPreconditions.checkCriticalState; +import static javaemul.internal.InternalPreconditions.checkNotNull; +import static javaemul.internal.InternalPreconditions.checkState; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashSet; +import java.util.IntSummaryStatistics; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.PrimitiveIterator; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.DoubleConsumer; +import java.util.function.IntBinaryOperator; +import java.util.function.IntConsumer; +import java.util.function.IntFunction; +import java.util.function.IntPredicate; +import java.util.function.IntSupplier; +import java.util.function.IntToDoubleFunction; +import java.util.function.IntToLongFunction; +import java.util.function.IntUnaryOperator; +import java.util.function.LongConsumer; +import java.util.function.ObjIntConsumer; +import java.util.function.Supplier; + +/** + * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/IntStream.html"> + * the official Java API doc</a> for details. + */ +public interface IntStream extends BaseStream<Integer,IntStream> { + + /** + * Value holder for various stream operations. + */ + static final class ValueConsumer implements IntConsumer { + int value; + + @Override + public void accept(int value) { + this.value = value; + } + } + + static IntStream.Builder builder() { + return new Builder() { + private int[] items = new int[0]; + + @Override + public void accept(int t) { + checkState(items != null, "Builder already built"); + items[items.length] = t; + } + + @Override + public IntStream build() { + checkCriticalState(items != null, "Builder already built"); + IntStream stream = Arrays.stream(items); + items = null; + return stream; + } + }; + } + + static IntStream concat(IntStream a, IntStream b) { + // This is nearly the same as flatMap, but inlined, wrapped around a single spliterator of + // these two objects, and without close() called as the stream progresses. Instead, close is + // invoked as part of the resulting stream's own onClose, so that either can fail without + // affecting the other, and correctly collecting suppressed exceptions. + + // TODO replace this flatMap-ish spliterator with one that directly combines the two root + // streams + Spliterator<? extends IntStream> spliteratorOfStreams = + Arrays.asList(a, b).spliterator(); + IntStream result = new IntStreamSource(null, new Spliterators.AbstractIntSpliterator(Long + .MAX_VALUE, 0) { + IntStream nextStream; + Spliterator.OfInt next; + + @Override + public boolean tryAdvance(IntConsumer action) { + // look for a new spliterator + while (advanceToNextSpliterator()) { + // if we have one, try to read and use it + if (next.tryAdvance(action)) { + return true; + } else { + nextStream = null; + // failed, null it out so we can find another + next = null; + } + } + return false; + } + + private boolean advanceToNextSpliterator() { + while (next == null) { + if (!spliteratorOfStreams.tryAdvance(n -> { + if (n != null) { + nextStream = n; + next = n.spliterator(); + } + })) { + return false; + } + } + return true; + } + }); + + result.onClose(a::close); + result.onClose(b::close); + + return result; + } + + static IntStream empty() { + return new EmptyIntStreamSource(null); + } + + static IntStream generate(final IntSupplier s) { + return StreamSupport.intStream(new Spliterators.AbstractIntSpliterator( + Long.MAX_VALUE, + Spliterator.IMMUTABLE | Spliterator.ORDERED + ) { + @Override + public boolean tryAdvance(IntConsumer action) { + action.accept(s.getAsInt()); + return true; + } + }, false); + } + + static IntStream iterate(int seed, IntUnaryOperator f) { + return StreamSupport.intStream(new Spliterators.AbstractIntSpliterator( + Long.MAX_VALUE, + Spliterator.IMMUTABLE | Spliterator.ORDERED + ) { + private int next = seed; + + @Override + public boolean tryAdvance(IntConsumer action) { + action.accept(next); + next = f.applyAsInt(next); + return true; + } + }, false); + } + + static IntStream of(int... values) { + return Arrays.stream(values); + } + + static IntStream of(int t) { + // TODO consider a splittable that returns only a single value + return of(new int[]{t}); + } + + static IntStream range(int startInclusive, int endExclusive) { + if (startInclusive >= endExclusive) { + return empty(); + } + return rangeClosed(startInclusive, endExclusive - 1); + } + + static IntStream rangeClosed(int startInclusive, int endInclusive) { + if (startInclusive > endInclusive) { + return empty(); + } + int count = endInclusive - startInclusive + 1; + + return StreamSupport.intStream(new Spliterators.AbstractIntSpliterator( + count, + Spliterator.IMMUTABLE | Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED + | Spliterator.SORTED | Spliterator.DISTINCT + ) { + private int next = startInclusive; + + @Override + public Comparator<? super Integer> getComparator() { + return null; + } + + @Override + public boolean tryAdvance(IntConsumer action) { + if (next <= endInclusive) { + action.accept(next++); + return true; + } + return false; + } + }, false); + } + + /** + * See + * <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/IntStream.Builder.html"> + * the official Java API doc</a> for details. + */ + public interface Builder extends IntConsumer { + @Override + void accept(int t); + + default IntStream.Builder add(int t) { + accept(t); + return this; + } + + IntStream build(); + } + + boolean allMatch(IntPredicate predicate); + + boolean anyMatch(IntPredicate predicate); + + DoubleStream asDoubleStream(); + + LongStream asLongStream(); + + OptionalDouble average(); + + Stream<Integer> boxed(); + + <R> R collect(Supplier<R> supplier, ObjIntConsumer<R> accumulator, BiConsumer<R,R> combiner); + + long count(); + + IntStream distinct(); + + IntStream filter(IntPredicate predicate); + + OptionalInt findAny(); + + OptionalInt findFirst(); + + IntStream flatMap(IntFunction<? extends IntStream> mapper); + + void forEach(IntConsumer action); + + void forEachOrdered(IntConsumer action); + + PrimitiveIterator.OfInt iterator(); + + IntStream limit(long maxSize); + + IntStream map(IntUnaryOperator mapper); + + DoubleStream mapToDouble(IntToDoubleFunction mapper); + + LongStream mapToLong(IntToLongFunction mapper); + + <U> Stream<U> mapToObj(IntFunction<? extends U> mapper); + + OptionalInt max(); + + OptionalInt min(); + + boolean noneMatch(IntPredicate predicate); + + IntStream parallel(); + + IntStream peek(IntConsumer action); + + OptionalInt reduce(IntBinaryOperator op); + + int reduce(int identity, IntBinaryOperator op); + + IntStream sequential(); + + IntStream skip(long n); + + IntStream sorted(); + + Spliterator.OfInt spliterator(); + + int sum(); + + IntSummaryStatistics summaryStatistics(); + + int[] toArray(); + + /** + * Represents an empty stream, doing nothing for all methods. + */ + static class EmptyIntStreamSource extends TerminatableStream<EmptyIntStreamSource> + implements IntStream { + public EmptyIntStreamSource(TerminatableStream<?> previous) { + super(previous); + } + + @Override + public IntStream filter(IntPredicate predicate) { + throwIfTerminated(); + return this; + } + + @Override + public IntStream map(IntUnaryOperator mapper) { + throwIfTerminated(); + return this; + } + + @Override + public <U> Stream<U> mapToObj(IntFunction<? extends U> mapper) { + throwIfTerminated(); + return new Stream.EmptyStreamSource<U>(this); + } + + @Override + public LongStream mapToLong(IntToLongFunction mapper) { + throwIfTerminated(); + return new LongStream.EmptyLongStreamSource(this); + } + + @Override + public DoubleStream mapToDouble(IntToDoubleFunction mapper) { + throwIfTerminated(); + return new DoubleStream.EmptyDoubleStreamSource(this); + } + + @Override + public IntStream flatMap(IntFunction<? extends IntStream> mapper) { + throwIfTerminated(); + return this; + } + + @Override + public IntStream distinct() { + throwIfTerminated(); + return this; + } + + @Override + public IntStream sorted() { + throwIfTerminated(); + return this; + } + + @Override + public IntStream peek(IntConsumer action) { + throwIfTerminated(); + return this; + } + + @Override + public IntStream limit(long maxSize) { + throwIfTerminated(); + checkState(maxSize >= 0, "maxSize may not be negative"); + return this; + } + + @Override + public IntStream skip(long n) { + throwIfTerminated(); + checkState(n >= 0, "n may not be negative"); + return this; + } + + @Override + public void forEach(IntConsumer action) { + terminate(); + // do nothing + } + + @Override + public void forEachOrdered(IntConsumer action) { + terminate(); + // do nothing + } + + @Override + public int[] toArray() { + terminate(); + return new int[0]; + } + + @Override + public int reduce(int identity, IntBinaryOperator op) { + terminate(); + return identity; + } + + @Override + public OptionalInt reduce(IntBinaryOperator op) { + terminate(); + return OptionalInt.empty(); + } + + @Override + public <R> R collect(Supplier<R> supplier, + ObjIntConsumer<R> accumulator, + BiConsumer<R, R> combiner) { + terminate(); + return supplier.get(); + } + + @Override + public int sum() { + terminate(); + return 0; + } + + @Override + public OptionalInt min() { + terminate(); + return OptionalInt.empty(); + } + + @Override + public OptionalInt max() { + terminate(); + return OptionalInt.empty(); + } + + @Override + public long count() { + terminate(); + return 0; + } + + @Override + public OptionalDouble average() { + terminate(); + return OptionalDouble.empty(); + } + + @Override + public IntSummaryStatistics summaryStatistics() { + terminate(); + return new IntSummaryStatistics(); + } + + @Override + public boolean anyMatch(IntPredicate predicate) { + terminate(); + return false; + } + + @Override + public boolean allMatch(IntPredicate predicate) { + terminate(); + return true; + } + + @Override + public boolean noneMatch(IntPredicate predicate) { + terminate(); + return true; + } + + @Override + public OptionalInt findFirst() { + terminate(); + return OptionalInt.empty(); + } + + @Override + public OptionalInt findAny() { + terminate(); + return OptionalInt.empty(); + } + + @Override + public LongStream asLongStream() { + throwIfTerminated(); + return new LongStream.EmptyLongStreamSource(this); + } + + @Override + public DoubleStream asDoubleStream() { + throwIfTerminated(); + return new DoubleStream.EmptyDoubleStreamSource(this); + } + + @Override + public Stream<Integer> boxed() { + throwIfTerminated(); + return new Stream.EmptyStreamSource<Integer>(this); + } + + @Override + public IntStream sequential() { + throwIfTerminated(); + return this; + } + + @Override + public IntStream parallel() { + throwIfTerminated(); + return this; + } + + @Override + public PrimitiveIterator.OfInt iterator() { + return Spliterators.iterator(spliterator()); + } + + @Override + public Spliterator.OfInt spliterator() { + terminate(); + return Spliterators.emptyIntSpliterator(); + } + + @Override + public boolean isParallel() { + throwIfTerminated(); + return false; + } + + @Override + public IntStream unordered() { + throwIfTerminated(); + return this; + } + } + + /** + * Int to Int map spliterator. + */ + static final class MapToIntSpliterator extends Spliterators.AbstractIntSpliterator { + private final IntUnaryOperator map; + private final Spliterator.OfInt original; + + public MapToIntSpliterator(IntUnaryOperator map, Spliterator.OfInt original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final IntConsumer action) { + return original.tryAdvance((int u) -> action.accept(map.applyAsInt(u))); + } + } + + /** + * Int to Object map spliterator. + * @param <T> the type of data in the object spliterator + */ + static final class MapToObjSpliterator<T> extends Spliterators.AbstractSpliterator<T> { + private final IntFunction<? extends T> map; + private final Spliterator.OfInt original; + + public MapToObjSpliterator(IntFunction<? extends T> map, Spliterator.OfInt original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final Consumer<? super T> action) { + return original.tryAdvance((int u) -> action.accept(map.apply(u))); + } + } + + /** + * Int to Long map spliterator. + */ + static final class MapToLongSpliterator extends Spliterators.AbstractLongSpliterator { + private final IntToLongFunction map; + private final Spliterator.OfInt original; + + public MapToLongSpliterator(IntToLongFunction map, Spliterator.OfInt original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final LongConsumer action) { + return original.tryAdvance((int u) -> action.accept(map.applyAsLong(u))); + } + } + + /** + * Int to Double map spliterator. + */ + static final class MapToDoubleSpliterator extends Spliterators.AbstractDoubleSpliterator { + private final IntToDoubleFunction map; + private final Spliterator.OfInt original; + + public MapToDoubleSpliterator(IntToDoubleFunction map, Spliterator.OfInt original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final DoubleConsumer action) { + return original.tryAdvance((int u) -> action.accept(map.applyAsDouble(u))); + } + } + + /** + * Int filter spliterator. + */ + static final class FilterSpliterator extends Spliterators.AbstractIntSpliterator { + private final IntPredicate filter; + private final Spliterator.OfInt original; + + private boolean found; + + public FilterSpliterator(IntPredicate filter, Spliterator.OfInt original) { + super(original.estimateSize(), original.characteristics() & ~Spliterator.SIZED); + checkNotNull(filter); + this.filter = filter; + this.original = original; + } + + @Override + public Comparator<? super Integer> getComparator() { + return original.getComparator(); + } + + @Override + public boolean tryAdvance(final IntConsumer action) { + found = false; + while (!found && original.tryAdvance((int item) -> { + if (filter.test(item)) { + found = true; + action.accept(item); + } + })) { + // do nothing, work is done in tryAdvance + } + + return found; + } + } + + /** + * Int skip spliterator. + */ + static final class SkipSpliterator extends Spliterators.AbstractIntSpliterator { + private long skip; + private final Spliterator.OfInt original; + + public SkipSpliterator(long skip, Spliterator.OfInt original) { + super( + original.hasCharacteristics(Spliterator.SIZED) + ? Math.max(0, original.estimateSize() - skip) + : Long.MAX_VALUE, + original.characteristics() + ); + this.skip = skip; + this.original = original; + } + + @Override + public Comparator<? super Integer> getComparator() { + return original.getComparator(); + } + + @Override + public boolean tryAdvance(IntConsumer action) { + while (skip > 0) { + if (!original.tryAdvance((int ignore) -> { })) { + return false; + } + skip--; + } + return original.tryAdvance(action); + } + } + + /** + * Int limit spliterator. + */ + static final class LimitSpliterator extends Spliterators.AbstractIntSpliterator { + private final long limit; + private final Spliterator.OfInt original; + private int position = 0; + + public LimitSpliterator(long limit, Spliterator.OfInt original) { + super( + original.hasCharacteristics(Spliterator.SIZED) + ? Math.min(original.estimateSize(), limit) + : Long.MAX_VALUE, + original.characteristics() + ); + this.limit = limit; + this.original = original; + } + + @Override + public Comparator<? super Integer> getComparator() { + return original.getComparator(); + } + + @Override + public boolean tryAdvance(IntConsumer action) { + if (limit <= position) { + return false; + } + boolean result = original.tryAdvance(action); + position++; + return result; + } + } + + /** + * Main implementation of IntStream, wrapping a single spliterator, and an optional parent stream. + */ + static class IntStreamSource extends TerminatableStream<IntStreamSource> implements IntStream { + private final Spliterator.OfInt spliterator; + + public IntStreamSource(TerminatableStream<?> previous, Spliterator.OfInt spliterator) { + super(previous); + this.spliterator = spliterator; + } + + // terminals + @Override + public Spliterator.OfInt spliterator() { + terminate(); + return spliterator; + } + + @Override + public PrimitiveIterator.OfInt iterator() { + return Spliterators.iterator(spliterator()); + } + + @Override + public OptionalInt findFirst() { + terminate(); + ValueConsumer holder = new ValueConsumer(); + if (spliterator.tryAdvance(holder)) { + return OptionalInt.of(holder.value); + } + return OptionalInt.empty(); + } + + @Override + public OptionalInt findAny() { + return findFirst(); + } + + @Override + public boolean noneMatch(IntPredicate predicate) { + return !anyMatch(predicate); + } + + @Override + public boolean allMatch(IntPredicate predicate) { + return !anyMatch(predicate.negate()); + } + + @Override + public boolean anyMatch(IntPredicate predicate) { + return filter(predicate).findFirst().isPresent(); + } + + @Override + public IntSummaryStatistics summaryStatistics() { + return collect( + IntSummaryStatistics::new, + // TODO switch to a lambda reference once #9340 is fixed + (intSummaryStatistics, value) -> intSummaryStatistics.accept(value), + IntSummaryStatistics::combine + ); + } + + @Override + public OptionalDouble average() { + IntSummaryStatistics stats = summaryStatistics(); + if (stats.getCount() == 0) { + return OptionalDouble.empty(); + } + return OptionalDouble.of(stats.getAverage()); + } + + @Override + public long count() { + terminate(); + long count = 0; + while (spliterator.tryAdvance((int value) -> { })) { + count++; + } + return count; + } + + @Override + public OptionalInt max() { + terminate(); + final ValueConsumer holder = new ValueConsumer(); + if (spliterator.tryAdvance((int value) -> holder.value = value)) { + spliterator.forEachRemaining((int value) -> holder.value = Math.max(holder.value, value)); + return OptionalInt.of(holder.value); + } + return OptionalInt.empty(); + } + + @Override + public OptionalInt min() { + terminate(); + final ValueConsumer holder = new ValueConsumer(); + if (spliterator.tryAdvance((int value) -> holder.value = value)) { + spliterator.forEachRemaining((int value) -> holder.value = Math.min(holder.value, value)); + return OptionalInt.of(holder.value); + } + return OptionalInt.empty(); + } + + @Override + public int sum() { + return (int) summaryStatistics().getSum(); + } + + @Override + public <R> R collect(Supplier<R> supplier, + final ObjIntConsumer<R> accumulator, + BiConsumer<R, R> combiner) { + terminate(); + final R acc = supplier.get(); + spliterator.forEachRemaining((int value) -> accumulator.accept(acc, value)); + return acc; + } + + @Override + public OptionalInt reduce(IntBinaryOperator op) { + ValueConsumer holder = new ValueConsumer(); + if (spliterator.tryAdvance(holder)) { + return OptionalInt.of(reduce(holder.value, op)); + } + terminate(); + return OptionalInt.empty(); + } + + @Override + public int reduce(int identity, IntBinaryOperator op) { + terminate(); + ValueConsumer holder = new ValueConsumer(); + holder.value = identity; + spliterator.forEachRemaining((int value) -> { + holder.accept(op.applyAsInt(holder.value, value)); + }); + return holder.value; + } + + @Override + public int[] toArray() { + terminate(); + int[] entries = new int[0]; + // this is legal in js, since the array will be backed by a JS array + spliterator.forEachRemaining((int value) -> entries[entries.length] = value); + + return entries; + } + + @Override + public void forEachOrdered(IntConsumer action) { + terminate(); + spliterator.forEachRemaining(action); + } + + @Override + public void forEach(IntConsumer action) { + forEachOrdered(action); + } + // end terminals + + // intermediates + + @Override + public IntStream filter(IntPredicate predicate) { + throwIfTerminated(); + return new IntStreamSource(this, new FilterSpliterator(predicate, spliterator)); + } + + @Override + public IntStream map(IntUnaryOperator mapper) { + throwIfTerminated(); + return new IntStreamSource(this, new MapToIntSpliterator(mapper, spliterator)); + } + + @Override + public <U> Stream<U> mapToObj(IntFunction<? extends U> mapper) { + throwIfTerminated(); + return new Stream.StreamSource(this, new MapToObjSpliterator<U>(mapper, spliterator)); + } + + @Override + public LongStream mapToLong(IntToLongFunction mapper) { + throwIfTerminated(); + return new LongStream.LongStreamSource(this, new MapToLongSpliterator(mapper, spliterator)); + } + + @Override + public DoubleStream mapToDouble(IntToDoubleFunction mapper) { + throwIfTerminated(); + return new DoubleStream.DoubleStreamSource( + this, + new MapToDoubleSpliterator(mapper, spliterator) + ); + } + + @Override + public IntStream flatMap(IntFunction<? extends IntStream> mapper) { + throwIfTerminated(); + final Spliterator<? extends IntStream> spliteratorOfStreams = + new MapToObjSpliterator<>(mapper, spliterator); + return new IntStreamSource(this, new Spliterators.AbstractIntSpliterator(Long.MAX_VALUE, 0) { + IntStream nextStream; + Spliterator.OfInt next; + + @Override + public boolean tryAdvance(IntConsumer action) { + // look for a new spliterator + while (advanceToNextSpliterator()) { + // if we have one, try to read and use it + if (next.tryAdvance(action)) { + return true; + } else { + nextStream.close(); + nextStream = null; + // failed, null it out so we can find another + next = null; + } + } + return false; + } + + private boolean advanceToNextSpliterator() { + while (next == null) { + if (!spliteratorOfStreams.tryAdvance(n -> { + if (n != null) { + nextStream = n; + next = n.spliterator(); + } + })) { + return false; + } + } + return true; + } + }); + } + + @Override + public IntStream distinct() { + throwIfTerminated(); + HashSet<Integer> seen = new HashSet<>(); + return filter(seen::add); + } + + @Override + public IntStream sorted() { + throwIfTerminated(); + return new IntStreamSource(this, new Spliterators.AbstractIntSpliterator( + spliterator.estimateSize(), + spliterator.characteristics() | Spliterator.SORTED + ) { + Spliterator.OfInt ordered = null; + + @Override + public Comparator<? super Integer> getComparator() { + return null; + } + + @Override + public boolean tryAdvance(IntConsumer action) { + if (ordered == null) { + int[] list = new int[0]; + spliterator.forEachRemaining((int item) -> list[list.length] = item); + Arrays.sort(list); + ordered = Spliterators.spliterator(list, characteristics()); + } + return ordered.tryAdvance(action); + } + }); + } + + @Override + public IntStream peek(IntConsumer action) { + checkNotNull(action); + throwIfTerminated(); + return new IntStreamSource(this, new Spliterators.AbstractIntSpliterator( + spliterator.estimateSize(), + spliterator.characteristics() + ) { + @Override + public boolean tryAdvance(final IntConsumer innerAction) { + return spliterator.tryAdvance(action.andThen(innerAction)); + } + }); + } + + @Override + public IntStream limit(long maxSize) { + throwIfTerminated(); + checkState(maxSize >= 0, "maxSize may not be negative"); + return new IntStreamSource(this, new LimitSpliterator(maxSize, spliterator)); + } + + @Override + public IntStream skip(long n) { + throwIfTerminated(); + checkState(n >= 0, "n may not be negative"); + if (n == 0) { + return this; + } + return new IntStreamSource(this, new SkipSpliterator(n, spliterator)); + } + + @Override + public LongStream asLongStream() { + return mapToLong(i -> (long) i); + } + + @Override + public DoubleStream asDoubleStream() { + return mapToDouble(i -> (double) i); + } + + @Override + public Stream<Integer> boxed() { + return mapToObj(Integer::valueOf); + } + + @Override + public IntStream sequential() { + throwIfTerminated(); + return this; + } + + @Override + public IntStream parallel() { + throwIfTerminated(); + return this; + } + + @Override + public boolean isParallel() { + throwIfTerminated(); + return false; + } + + @Override + public IntStream unordered() { + throwIfTerminated(); + return this; + } + } +}
diff --git a/user/super/com/google/gwt/emul/java/util/stream/LongStream.java b/user/super/com/google/gwt/emul/java/util/stream/LongStream.java new file mode 100644 index 0000000..f2b6ba1 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/stream/LongStream.java
@@ -0,0 +1,1072 @@ +/* + * Copyright 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package java.util.stream; + +import static javaemul.internal.InternalPreconditions.checkCriticalState; +import static javaemul.internal.InternalPreconditions.checkNotNull; +import static javaemul.internal.InternalPreconditions.checkState; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashSet; +import java.util.LongSummaryStatistics; +import java.util.OptionalDouble; +import java.util.OptionalLong; +import java.util.PrimitiveIterator; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.DoubleConsumer; +import java.util.function.IntConsumer; +import java.util.function.LongBinaryOperator; +import java.util.function.LongConsumer; +import java.util.function.LongFunction; +import java.util.function.LongPredicate; +import java.util.function.LongSupplier; +import java.util.function.LongToDoubleFunction; +import java.util.function.LongToIntFunction; +import java.util.function.LongUnaryOperator; +import java.util.function.ObjLongConsumer; +import java.util.function.Supplier; + +/** + * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/LongStream.html"> + * the official Java API doc</a> for details. + */ +public interface LongStream extends BaseStream<Long,LongStream> { + + /** + * Value holder for various stream operations. + */ + static final class ValueConsumer implements LongConsumer { + long value; + + @Override + public void accept(long value) { + this.value = value; + } + } + + static LongStream.Builder builder() { + return new Builder() { + private long[] items = new long[0]; + + @Override + public void accept(long t) { + checkState(items != null, "Builder already built"); + items[items.length] = t; + } + + @Override + public LongStream build() { + checkCriticalState(items != null, "Builder already built"); + LongStream stream = Arrays.stream(items); + items = null; + return stream; + } + }; + } + + static LongStream concat(LongStream a, LongStream b) { + // This is nearly the same as flatMap, but inlined, wrapped around a single spliterator of + // these two objects, and without close() called as the stream progresses. Instead, close is + // invoked as part of the resulting stream's own onClose, so that either can fail without + // affecting the other, and correctly collecting suppressed exceptions. + + // TODO replace this flatMap-ish spliterator with one that directly combines the two root + // streams + Spliterator<? extends LongStream> spliteratorOfStreams = + Arrays.asList(a, b).spliterator(); + LongStream result = new LongStreamSource(null, new Spliterators.AbstractLongSpliterator(Long + .MAX_VALUE, 0) { + LongStream nextStream; + Spliterator.OfLong next; + + @Override + public boolean tryAdvance(LongConsumer action) { + // look for a new spliterator + while (advanceToNextSpliterator()) { + // if we have one, try to read and use it + if (next.tryAdvance(action)) { + return true; + } else { + nextStream = null; + // failed, null it out so we can find another + next = null; + } + } + return false; + } + + private boolean advanceToNextSpliterator() { + while (next == null) { + if (!spliteratorOfStreams.tryAdvance(n -> { + if (n != null) { + nextStream = n; + next = n.spliterator(); + } + })) { + return false; + } + } + return true; + } + }); + + result.onClose(a::close); + result.onClose(b::close); + + return result; + } + + static LongStream empty() { + return new EmptyLongStreamSource(null); + } + + static LongStream generate(LongSupplier s) { + return StreamSupport.longStream(new Spliterators.AbstractLongSpliterator( + Long.MAX_VALUE, + Spliterator.IMMUTABLE | Spliterator.ORDERED + ) { + @Override + public boolean tryAdvance(LongConsumer action) { + action.accept(s.getAsLong()); + return true; + } + }, false); + } + + static LongStream iterate(long seed, LongUnaryOperator f) { + return StreamSupport.longStream(new Spliterators.AbstractLongSpliterator( + Long.MAX_VALUE, + Spliterator.IMMUTABLE | Spliterator.ORDERED + ) { + private long next = seed; + + @Override + public boolean tryAdvance(LongConsumer action) { + action.accept(next); + next = f.applyAsLong(next); + return true; + } + }, false); + } + + static LongStream of(long... values) { + return Arrays.stream(values); + } + + static LongStream of(long t) { + // TODO consider a splittable that returns only a single value + return of(new long[]{t}); + } + + static LongStream range(long startInclusive, long endExclusive) { + if (startInclusive >= endExclusive) { + return empty(); + } + return rangeClosed(startInclusive, endExclusive - 1); + } + + static LongStream rangeClosed(long startInclusive, long endInclusive) { + if (startInclusive > endInclusive) { + return empty(); + } + long count = endInclusive - startInclusive + 1; + + return StreamSupport.longStream(new Spliterators.AbstractLongSpliterator( + count, + Spliterator.IMMUTABLE | Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED + | Spliterator.SORTED | Spliterator.DISTINCT + ) { + private long next = startInclusive; + + @Override + public Comparator<? super Long> getComparator() { + return null; + } + + @Override + public boolean tryAdvance(LongConsumer action) { + if (next <= endInclusive) { + action.accept(next++); + return true; + } + return false; + } + }, false); + } + + /** + * See + * <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/LongStream.Builder.html"> + * the official Java API doc</a> for details. + */ + public interface Builder extends LongConsumer { + @Override + void accept(long t); + + default LongStream.Builder add(long t) { + accept(t); + return this; + } + + LongStream build(); + } + + boolean allMatch(LongPredicate predicate); + + boolean anyMatch(LongPredicate predicate); + + DoubleStream asDoubleStream(); + + OptionalDouble average(); + + Stream<Long> boxed(); + + <R> R collect(Supplier<R> supplier, ObjLongConsumer<R> accumulator, BiConsumer<R, R> combiner); + + long count(); + + LongStream distinct(); + + LongStream filter(LongPredicate predicate); + + OptionalLong findAny(); + + OptionalLong findFirst(); + + LongStream flatMap(LongFunction<? extends LongStream> mapper); + + void forEach(LongConsumer action); + + void forEachOrdered(LongConsumer action); + + PrimitiveIterator.OfLong iterator(); + + LongStream limit(long maxSize); + + LongStream map(LongUnaryOperator mapper); + + DoubleStream mapToDouble(LongToDoubleFunction mapper); + + IntStream mapToInt(LongToIntFunction mapper); + + <U> Stream<U> mapToObj(LongFunction<? extends U> mapper); + + OptionalLong max(); + + OptionalLong min(); + + boolean noneMatch(LongPredicate predicate); + + LongStream parallel(); + + LongStream peek(LongConsumer action); + + OptionalLong reduce(LongBinaryOperator op); + + long reduce(long identity, LongBinaryOperator op); + + LongStream sequential(); + + LongStream skip(long n); + + LongStream sorted(); + + Spliterator.OfLong spliterator(); + + long sum(); + + LongSummaryStatistics summaryStatistics(); + + long[] toArray(); + + /** + * Represents an empty stream, doing nothing for all methods. + */ + static class EmptyLongStreamSource extends TerminatableStream<EmptyLongStreamSource> + implements LongStream { + public EmptyLongStreamSource(TerminatableStream<?> previous) { + super(previous); + } + + @Override + public LongStream filter(LongPredicate predicate) { + throwIfTerminated(); + return this; + } + + @Override + public LongStream map(LongUnaryOperator mapper) { + throwIfTerminated(); + return this; + } + + @Override + public <U> Stream<U> mapToObj(LongFunction<? extends U> mapper) { + throwIfTerminated(); + return new Stream.EmptyStreamSource<U>(this); + } + + @Override + public IntStream mapToInt(LongToIntFunction mapper) { + throwIfTerminated(); + return new IntStream.EmptyIntStreamSource(this); + } + + @Override + public DoubleStream mapToDouble(LongToDoubleFunction mapper) { + throwIfTerminated(); + return new DoubleStream.EmptyDoubleStreamSource(this); + } + + @Override + public LongStream flatMap(LongFunction<? extends LongStream> mapper) { + throwIfTerminated(); + return this; + } + + @Override + public LongStream distinct() { + throwIfTerminated(); + return this; + } + + @Override + public LongStream sorted() { + throwIfTerminated(); + return this; + } + + @Override + public LongStream peek(LongConsumer action) { + throwIfTerminated(); + return this; + } + + @Override + public LongStream limit(long maxSize) { + throwIfTerminated(); + checkState(maxSize >= 0, "maxSize may not be negative"); + return this; + } + + @Override + public LongStream skip(long n) { + throwIfTerminated(); + checkState(n >= 0, "n may not be negative"); + return this; + } + + @Override + public void forEach(LongConsumer action) { + terminate(); + } + + @Override + public void forEachOrdered(LongConsumer action) { + terminate(); + } + + @Override + public long[] toArray() { + terminate(); + return new long[0]; + } + + @Override + public long reduce(long identity, LongBinaryOperator op) { + terminate(); + return identity; + } + + @Override + public OptionalLong reduce(LongBinaryOperator op) { + terminate(); + return OptionalLong.empty(); + } + + @Override + public <R> R collect(Supplier<R> supplier, + ObjLongConsumer<R> accumulator, + BiConsumer<R, R> combiner) { + terminate(); + return supplier.get(); + } + + @Override + public long sum() { + terminate(); + return 0; + } + + @Override + public OptionalLong min() { + terminate(); + return OptionalLong.empty(); + } + + @Override + public OptionalLong max() { + terminate(); + return OptionalLong.empty(); + } + + @Override + public long count() { + terminate(); + return 0; + } + + @Override + public OptionalDouble average() { + terminate(); + return OptionalDouble.empty(); + } + + @Override + public LongSummaryStatistics summaryStatistics() { + terminate(); + return new LongSummaryStatistics(); + } + + @Override + public boolean anyMatch(LongPredicate predicate) { + terminate(); + return false; + } + + @Override + public boolean allMatch(LongPredicate predicate) { + terminate(); + return true; + } + + @Override + public boolean noneMatch(LongPredicate predicate) { + terminate(); + return true; + } + + @Override + public OptionalLong findFirst() { + terminate(); + return OptionalLong.empty(); + } + + @Override + public OptionalLong findAny() { + terminate(); + return OptionalLong.empty(); + } + + @Override + public DoubleStream asDoubleStream() { + throwIfTerminated(); + return new DoubleStream.EmptyDoubleStreamSource(this); + } + + @Override + public Stream<Long> boxed() { + throwIfTerminated(); + return new Stream.EmptyStreamSource<Long>(this); + } + + @Override + public LongStream sequential() { + throwIfTerminated(); + return this; + } + + @Override + public LongStream parallel() { + throwIfTerminated(); + return this; + } + + @Override + public PrimitiveIterator.OfLong iterator() { + return Spliterators.iterator(spliterator()); + } + + @Override + public Spliterator.OfLong spliterator() { + terminate(); + return Spliterators.emptyLongSpliterator(); + } + + @Override + public boolean isParallel() { + throwIfTerminated(); + return false; + } + + @Override + public LongStream unordered() { + throwIfTerminated(); + return this; + } + } + + /** + * Long to Int map spliterator. + */ + static final class MapToIntSpliterator extends Spliterators.AbstractIntSpliterator { + private final LongToIntFunction map; + private final Spliterator.OfLong original; + + public MapToIntSpliterator(LongToIntFunction map, Spliterator.OfLong original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final IntConsumer action) { + return original.tryAdvance((long u) -> action.accept(map.applyAsInt(u))); + } + } + + /** + * Long to Object map spliterator. + * @param <T> the type of data in the object spliterator + */ + static final class MapToObjSpliterator<T> extends Spliterators.AbstractSpliterator<T> { + private final LongFunction<? extends T> map; + private final Spliterator.OfLong original; + + public MapToObjSpliterator(LongFunction<? extends T> map, Spliterator.OfLong original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final Consumer<? super T> action) { + return original.tryAdvance((long u) -> action.accept(map.apply(u))); + } + } + + /** + * Long to Long map spliterator. + */ + static final class MapToLongSpliterator extends Spliterators.AbstractLongSpliterator { + private final LongUnaryOperator map; + private final Spliterator.OfLong original; + + public MapToLongSpliterator(LongUnaryOperator map, Spliterator.OfLong original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final LongConsumer action) { + return original.tryAdvance((long u) -> action.accept(map.applyAsLong(u))); + } + } + + /** + * Long to Double map Spliterator. + */ + static final class MapToDoubleSpliterator extends Spliterators.AbstractDoubleSpliterator { + private final LongToDoubleFunction map; + private final Spliterator.OfLong original; + + public MapToDoubleSpliterator(LongToDoubleFunction map, Spliterator.OfLong original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final DoubleConsumer action) { + return original.tryAdvance((long u) -> action.accept(map.applyAsDouble(u))); + } + } + + /** + * Long filter spliterator. + */ + static final class FilterSpliterator extends Spliterators.AbstractLongSpliterator { + private final LongPredicate filter; + private final Spliterator.OfLong original; + + private boolean found; + + public FilterSpliterator(LongPredicate filter, Spliterator.OfLong original) { + super(original.estimateSize(), original.characteristics() & ~Spliterator.SIZED); + checkNotNull(filter); + this.filter = filter; + this.original = original; + } + + @Override + public Comparator<? super Long> getComparator() { + return original.getComparator(); + } + + @Override + public boolean tryAdvance(final LongConsumer action) { + found = false; + while (!found && original.tryAdvance((long item) -> { + if (filter.test(item)) { + found = true; + action.accept(item); + } + })) { + // do nothing, work is done in tryAdvance + } + + return found; + } + } + + /** + * Long skip spliterator. + */ + static final class SkipSpliterator extends Spliterators.AbstractLongSpliterator { + private long skip; + private final Spliterator.OfLong original; + + public SkipSpliterator(long skip, Spliterator.OfLong original) { + super( + original.hasCharacteristics(Spliterator.SIZED) + ? Math.max(0, original.estimateSize() - skip) + : Long.MAX_VALUE, + original.characteristics() + ); + this.skip = skip; + this.original = original; + } + + @Override + public Comparator<? super Long> getComparator() { + return original.getComparator(); + } + + @Override + public boolean tryAdvance(LongConsumer action) { + while (skip > 0) { + if (!original.tryAdvance((long ignore) -> { })) { + return false; + } + skip--; + } + return original.tryAdvance(action); + } + } + + /** + * Long limit spliterator. + */ + static final class LimitSpliterator extends Spliterators.AbstractLongSpliterator { + private final long limit; + private final Spliterator.OfLong original; + private int position = 0; + + public LimitSpliterator(long limit, Spliterator.OfLong original) { + super( + original.hasCharacteristics(Spliterator.SIZED) + ? Math.min(original.estimateSize(), limit) + : Long.MAX_VALUE, + original.characteristics() + ); + this.limit = limit; + this.original = original; + } + + @Override + public Comparator<? super Long> getComparator() { + return original.getComparator(); + } + + @Override + public boolean tryAdvance(LongConsumer action) { + if (limit <= position) { + return false; + } + boolean result = original.tryAdvance(action); + position++; + return result; + } + } + + /** + * Main implementation of LongStream, wrapping a single spliterator, and an optional parent + * stream. + */ + static class LongStreamSource extends TerminatableStream<LongStreamSource> implements LongStream { + private final Spliterator.OfLong spliterator; + + public LongStreamSource(TerminatableStream<?> previous, Spliterator.OfLong spliterator) { + super(previous); + this.spliterator = spliterator; + } + + // terminals + + @Override + public void forEach(LongConsumer action) { + forEachOrdered(action); + } + + @Override + public void forEachOrdered(LongConsumer action) { + terminate(); + spliterator.forEachRemaining(action); + } + + @Override + public long[] toArray() { + terminate(); + long[] entries = new long[0]; + // this is legal in js, since the array will be backed by a JS array + spliterator.forEachRemaining((long value) -> entries[entries.length] = value); + + return entries; + } + + @Override + public long reduce(long identity, LongBinaryOperator op) { + terminate(); + ValueConsumer holder = new ValueConsumer(); + holder.value = identity; + spliterator.forEachRemaining((long value) -> { + holder.accept(op.applyAsLong(holder.value, value)); + }); + return holder.value; + } + + @Override + public OptionalLong reduce(LongBinaryOperator op) { + ValueConsumer holder = new ValueConsumer(); + if (spliterator.tryAdvance(holder)) { + return OptionalLong.of(reduce(holder.value, op)); + } + terminate(); + return OptionalLong.empty(); + } + + @Override + public <R> R collect(Supplier<R> supplier, + ObjLongConsumer<R> accumulator, + BiConsumer<R, R> combiner) { + terminate(); + final R acc = supplier.get(); + spliterator.forEachRemaining((long value) -> accumulator.accept(acc, value)); + return acc; + } + + @Override + public long sum() { + return summaryStatistics().getSum(); + } + + @Override + public OptionalLong min() { + terminate(); + final ValueConsumer holder = new ValueConsumer(); + if (spliterator.tryAdvance((long value) -> holder.value = value)) { + spliterator.forEachRemaining((long value) -> holder.value = Math.min(holder.value, value)); + return OptionalLong.of(holder.value); + } + return OptionalLong.empty(); + } + + @Override + public OptionalLong max() { + terminate(); + final ValueConsumer holder = new ValueConsumer(); + if (spliterator.tryAdvance((long value) -> holder.value = value)) { + spliterator.forEachRemaining((long value) -> holder.value = Math.max(holder.value, value)); + return OptionalLong.of(holder.value); + } + return OptionalLong.empty(); + } + + @Override + public long count() { + terminate(); + long count = 0; + while (spliterator.tryAdvance((long value) -> { })) { + count++; + } + return count; + } + + @Override + public OptionalDouble average() { + LongSummaryStatistics stats = summaryStatistics(); + if (stats.getCount() == 0) { + return OptionalDouble.empty(); + } + return OptionalDouble.of(stats.getAverage()); + } + + @Override + public LongSummaryStatistics summaryStatistics() { + return collect( + LongSummaryStatistics::new, + // TODO switch to a lambda reference once #9340 is fixed + (longSummaryStatistics, value) -> longSummaryStatistics.accept(value), + LongSummaryStatistics::combine + ); + } + + @Override + public boolean anyMatch(LongPredicate predicate) { + return filter(predicate).findFirst().isPresent(); + } + + @Override + public boolean allMatch(LongPredicate predicate) { + return !anyMatch(predicate.negate()); + } + + @Override + public boolean noneMatch(LongPredicate predicate) { + return !anyMatch(predicate); + } + + @Override + public OptionalLong findFirst() { + terminate(); + ValueConsumer holder = new ValueConsumer(); + if (spliterator.tryAdvance(holder)) { + return OptionalLong.of(holder.value); + } + return OptionalLong.empty(); + } + + @Override + public OptionalLong findAny() { + return findFirst(); + } + + @Override + public PrimitiveIterator.OfLong iterator() { + return Spliterators.iterator(spliterator()); + } + + @Override + public Spliterator.OfLong spliterator() { + terminate(); + return spliterator; + } + // end terminals + + // intermediates + @Override + public LongStream filter(LongPredicate predicate) { + throwIfTerminated(); + return new LongStreamSource(this, new FilterSpliterator(predicate, spliterator)); + } + + @Override + public LongStream map(LongUnaryOperator mapper) { + throwIfTerminated(); + return new LongStream.LongStreamSource(this, new MapToLongSpliterator(mapper, spliterator)); + } + + @Override + public <U> Stream<U> mapToObj(LongFunction<? extends U> mapper) { + throwIfTerminated(); + return new Stream.StreamSource(this, new MapToObjSpliterator<U>(mapper, spliterator)); + } + + @Override + public IntStream mapToInt(LongToIntFunction mapper) { + throwIfTerminated(); + return new IntStream.IntStreamSource(this, new MapToIntSpliterator(mapper, spliterator)); + } + + @Override + public DoubleStream mapToDouble(LongToDoubleFunction mapper) { + throwIfTerminated(); + return new DoubleStream.DoubleStreamSource(this, new MapToDoubleSpliterator( + mapper, + spliterator) + ); + } + + @Override + public LongStream flatMap(LongFunction<? extends LongStream> mapper) { + throwIfTerminated(); + final Spliterator<? extends LongStream> spliteratorOfStreams = new MapToObjSpliterator<>( + mapper, + spliterator + ); + return new LongStreamSource( + this, + new Spliterators.AbstractLongSpliterator(Long.MAX_VALUE, 0 + ) { + LongStream nextStream; + Spliterator.OfLong next; + + @Override + public boolean tryAdvance(LongConsumer action) { + // look for a new spliterator + while (advanceToNextSpliterator()) { + // if we have one, try to read and use it + if (next.tryAdvance(action)) { + return true; + } else { + nextStream.close(); + nextStream = null; + // failed, null it out so we can find another + next = null; + } + } + return false; + } + + private boolean advanceToNextSpliterator() { + while (next == null) { + if (!spliteratorOfStreams.tryAdvance(n -> { + if (n != null) { + nextStream = n; + next = n.spliterator(); + } + })) { + return false; + } + } + return true; + } + }); + } + + @Override + public LongStream distinct() { + throwIfTerminated(); + HashSet<Long> seen = new HashSet<>(); + return filter(seen::add); + } + + @Override + public LongStream sorted() { + throwIfTerminated(); + return new LongStreamSource(this, new Spliterators.AbstractLongSpliterator( + spliterator.estimateSize(), + spliterator.characteristics() | Spliterator.SORTED + ) { + Spliterator.OfLong ordered = null; + + @Override + public Comparator<? super Long> getComparator() { + return null; + } + + @Override + public boolean tryAdvance(LongConsumer action) { + if (ordered == null) { + long[] list = new long[0]; + spliterator.forEachRemaining((long item) -> list[list.length] = item); + Arrays.sort(list); + ordered = Spliterators.spliterator(list, characteristics()); + } + return ordered.tryAdvance(action); + } + }); + } + + @Override + public LongStream peek(LongConsumer action) { + checkNotNull(action); + throwIfTerminated(); + return new LongStreamSource(this, new Spliterators.AbstractLongSpliterator( + spliterator.estimateSize(), + spliterator.characteristics() + ) { + @Override + public boolean tryAdvance(final LongConsumer innerAction) { + return spliterator.tryAdvance(action.andThen(innerAction)); + } + }); + } + + @Override + public LongStream limit(long maxSize) { + throwIfTerminated(); + checkState(maxSize >= 0, "maxSize may not be negative"); + return new LongStreamSource(this, new LimitSpliterator(maxSize, spliterator)); + } + + @Override + public LongStream skip(long n) { + throwIfTerminated(); + checkState(n >= 0, "n may not be negative"); + if (n == 0) { + return this; + } + return new LongStreamSource(this, new SkipSpliterator(n, spliterator)); + } + + @Override + public DoubleStream asDoubleStream() { + return mapToDouble(x -> (double) x); + } + + @Override + public Stream<Long> boxed() { + return mapToObj(Long::valueOf); + } + + @Override + public LongStream sequential() { + throwIfTerminated(); + return this; + } + + @Override + public LongStream parallel() { + throwIfTerminated(); + return this; + } + + @Override + public boolean isParallel() { + throwIfTerminated(); + return false; + } + + @Override + public LongStream unordered() { + throwIfTerminated(); + return this; + } + } +}
diff --git a/user/super/com/google/gwt/emul/java/util/stream/Stream.java b/user/super/com/google/gwt/emul/java/util/stream/Stream.java new file mode 100644 index 0000000..b61486d --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/stream/Stream.java
@@ -0,0 +1,1180 @@ +/* + * Copyright 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package java.util.stream; + +import static javaemul.internal.InternalPreconditions.checkCriticalState; +import static javaemul.internal.InternalPreconditions.checkNotNull; +import static javaemul.internal.InternalPreconditions.checkState; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Consumer; +import java.util.function.DoubleConsumer; +import java.util.function.Function; +import java.util.function.IntConsumer; +import java.util.function.IntFunction; +import java.util.function.LongConsumer; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.ToDoubleFunction; +import java.util.function.ToIntFunction; +import java.util.function.ToLongFunction; +import java.util.function.UnaryOperator; + +/** + * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html"> + * the official Java API doc</a> for details. + * + * @param <T> the type of data being streamed + */ +public interface Stream<T> extends BaseStream<T, Stream<T>> { + /** + * Value holder for various stream operations. + */ + static final class ValueConsumer<T> implements Consumer<T> { + T value; + + @Override + public void accept(T value) { + this.value = value; + } + } + + static <T> Stream.Builder<T> builder() { + return new Builder<T>() { + private Object[] items = new Object[0]; + + @Override + public void accept(T t) { + checkState(items != null, "Builder already built"); + items[items.length] = t; + } + + @Override + @SuppressWarnings("unchecked") + public Stream<T> build() { + checkCriticalState(items != null, "Builder already built"); + Stream<T> stream = (Stream<T>) Arrays.stream(items); + items = null; + return stream; + } + }; + } + + static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) { + // This is nearly the same as flatMap, but inlined, wrapped around a single spliterator of + // these two objects, and without close() called as the stream progresses. Instead, close is + // invoked as part of the resulting stream's own onClose, so that either can fail without + // affecting the other, and correctly collecting suppressed exceptions. + + // TODO replace this flatMap-ish spliterator with one that directly combines the two root + // streams + Spliterator<? extends Stream<? extends T>> spliteratorOfStreams = + Arrays.asList(a, b).spliterator(); + Stream<T> result = new StreamSource<T>(null, new Spliterators.AbstractSpliterator<T>(Long + .MAX_VALUE, 0) { + Stream<? extends T> nextStream; + Spliterator<? extends T> next; + + @Override + public boolean tryAdvance(Consumer<? super T> action) { + // look for a new spliterator + while (advanceToNextSpliterator()) { + // if we have one, try to read and use it + if (next.tryAdvance(action)) { + return true; + } else { + nextStream = null; + // failed, null it out so we can find another + next = null; + } + } + return false; + } + + private boolean advanceToNextSpliterator() { + while (next == null) { + if (!spliteratorOfStreams.tryAdvance(n -> { + if (n != null) { + nextStream = n; + next = n.spliterator(); + } + })) { + return false; + } + } + return true; + } + }); + + result.onClose(a::close); + result.onClose(b::close); + + return result; + } + + static <T> Stream<T> empty() { + return new EmptyStreamSource<T>(null); + } + + static <T> Stream<T> generate(Supplier<T> s) { + return StreamSupport.stream(new Spliterators.AbstractSpliterator<T>(Long.MAX_VALUE, + Spliterator.IMMUTABLE | Spliterator.ORDERED) { + @Override + public boolean tryAdvance(Consumer<? super T> action) { + action.accept(s.get()); + return true; + } + }, false); + } + + static <T> Stream<T> iterate(T seed, UnaryOperator<T> f) { + return StreamSupport.stream(new Spliterators.AbstractSpliterator<T>(Long.MAX_VALUE, + Spliterator.IMMUTABLE | Spliterator.ORDERED) { + private T next = seed; + + @Override + public boolean tryAdvance(Consumer<? super T> action) { + action.accept(next); + next = f.apply(next); + return true; + } + }, false); + } + + static <T> Stream<T> of(T t) { + // TODO consider a splittable that returns only a single value, either for use here or in the + // singleton collection types + return Collections.singleton(t).stream(); + } + + static <T> Stream<T> of(T... values) { + return Arrays.stream(values); + } + + /** + * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.Builder.html"> + * the official Java API doc</a> for details. + */ + public interface Builder<T> extends Consumer<T> { + @Override + void accept(T t); + + default Stream.Builder<T> add(T t) { + accept(t); + return this; + } + + Stream<T> build(); + } + + boolean allMatch(Predicate<? super T> predicate); + + boolean anyMatch(Predicate<? super T> predicate); + + <R,A> R collect(Collector<? super T,A,R> collector); + + <R> R collect(Supplier<R> supplier, + BiConsumer<R,? super T> accumulator, + BiConsumer<R,R> combiner); + + long count(); + + Stream<T> distinct(); + + Stream<T> filter(Predicate<? super T> predicate); + + Optional<T> findAny(); + + Optional<T> findFirst(); + + <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper); + + DoubleStream flatMapToDouble(Function<? super T,? extends DoubleStream> mapper); + + IntStream flatMapToInt(Function<? super T,? extends IntStream> mapper); + + LongStream flatMapToLong(Function<? super T,? extends LongStream> mapper); + + void forEach(Consumer<? super T> action); + + void forEachOrdered(Consumer<? super T> action); + + Stream<T> limit(long maxSize); + + <R> Stream<R> map(Function<? super T,? extends R> mapper); + + DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper); + + IntStream mapToInt(ToIntFunction<? super T> mapper); + + LongStream mapToLong(ToLongFunction<? super T> mapper); + + Optional<T> max(Comparator<? super T> comparator); + + Optional<T> min(Comparator<? super T> comparator); + + boolean noneMatch(Predicate<? super T> predicate); + + Stream<T> peek(Consumer<? super T> action); + + Optional<T> reduce(BinaryOperator<T> accumulator); + + T reduce(T identity, BinaryOperator<T> accumulator); + + <U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner); + + Stream<T> skip(long n); + + Stream<T> sorted(); + + Stream<T> sorted(Comparator<? super T> comparator); + + Object[] toArray(); + + <A> A[] toArray(IntFunction<A[]> generator); + + /** + * Represents an empty stream, doing nothing for all methods. + */ + static class EmptyStreamSource<T> extends TerminatableStream<EmptyStreamSource<T>> + implements Stream<T> { + + public EmptyStreamSource(TerminatableStream<?> previous) { + super(previous); + } + + @Override + public Stream<T> filter(Predicate<? super T> predicate) { + throwIfTerminated(); + return this; + } + + @Override + public <R> Stream<R> map(Function<? super T, ? extends R> mapper) { + throwIfTerminated(); + return (Stream) this; + } + + @Override + public IntStream mapToInt(ToIntFunction<? super T> mapper) { + throwIfTerminated(); + return new IntStream.EmptyIntStreamSource(this); + } + + @Override + public LongStream mapToLong(ToLongFunction<? super T> mapper) { + throwIfTerminated(); + return new LongStream.EmptyLongStreamSource(this); + } + + @Override + public DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper) { + throwIfTerminated(); + return new DoubleStream.EmptyDoubleStreamSource(this); + } + + @Override + public <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) { + throwIfTerminated(); + return (Stream) this; + } + + @Override + public IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) { + throwIfTerminated(); + return new IntStream.EmptyIntStreamSource(this); + } + + @Override + public LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper) { + throwIfTerminated(); + return new LongStream.EmptyLongStreamSource(this); + } + + @Override + public DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) { + throwIfTerminated(); + return new DoubleStream.EmptyDoubleStreamSource(this); + } + + @Override + public Stream<T> distinct() { + throwIfTerminated(); + return this; + } + + @Override + public Stream<T> sorted() { + throwIfTerminated(); + return this; + } + + @Override + public Stream<T> sorted(Comparator<? super T> comparator) { + throwIfTerminated(); + return this; + } + + @Override + public Stream<T> peek(Consumer<? super T> action) { + throwIfTerminated(); + return this; + } + + @Override + public Stream<T> limit(long maxSize) { + throwIfTerminated(); + checkState(maxSize >= 0, "maxSize may not be negative"); + return this; + } + + @Override + public Stream<T> skip(long n) { + throwIfTerminated(); + checkState(n >= 0, "n may not be negative"); + return this; + } + + @Override + public void forEach(Consumer<? super T> action) { + terminate(); + // nothing to do + } + + @Override + public void forEachOrdered(Consumer<? super T> action) { + terminate(); + // nothing to do + } + + @Override + public Object[] toArray() { + terminate(); + return new Object[0]; + } + + @Override + public <A> A[] toArray(IntFunction<A[]> generator) { + terminate(); + return generator.apply(0); + } + + @Override + public T reduce(T identity, BinaryOperator<T> accumulator) { + terminate(); + return identity; + } + + @Override + public Optional<T> reduce(BinaryOperator<T> accumulator) { + terminate(); + return Optional.empty(); + } + + @Override + public <U> U reduce(U identity, + BiFunction<U, ? super T, U> accumulator, + BinaryOperator<U> combiner) { + terminate(); + return identity; + } + + @Override + public <R> R collect(Supplier<R> supplier, + BiConsumer<R, ? super T> accumulator, + BiConsumer<R, R> combiner) { + terminate(); + return supplier.get(); + } + + @Override + public <R, A> R collect(Collector<? super T, A, R> collector) { + terminate(); + return collector.finisher().apply(collector.supplier().get()); + } + + @Override + public Optional<T> min(Comparator<? super T> comparator) { + terminate(); + return Optional.empty(); + } + + @Override + public Optional<T> max(Comparator<? super T> comparator) { + terminate(); + return Optional.empty(); + } + + @Override + public long count() { + terminate(); + return 0; + } + + @Override + public boolean anyMatch(Predicate<? super T> predicate) { + terminate(); + return false; + } + + @Override + public boolean allMatch(Predicate<? super T> predicate) { + terminate(); + return true; + } + + @Override + public boolean noneMatch(Predicate<? super T> predicate) { + terminate(); + return true; + } + + @Override + public Optional<T> findFirst() { + terminate(); + return Optional.empty(); + } + + @Override + public Optional<T> findAny() { + terminate(); + return Optional.empty(); + } + + @Override + public Iterator<T> iterator() { + terminate(); + return Collections.emptyIterator(); + } + + @Override + public Spliterator<T> spliterator() { + terminate(); + return Spliterators.emptySpliterator(); + } + + @Override + public boolean isParallel() { + throwIfTerminated(); + return false; + } + + @Override + public Stream<T> sequential() { + throwIfTerminated(); + return this; + } + + @Override + public Stream<T> parallel() { + throwIfTerminated(); + return this; + } + + @Override + public Stream<T> unordered() { + throwIfTerminated(); + return this; + } + } + + /** + * Object to Object map spliterator. + * @param <U> the input type + * @param <T> the output type + */ + static final class MapToObjSpliterator<U, T> extends Spliterators.AbstractSpliterator<T> { + private final Function<? super U, ? extends T> map; + private final Spliterator<U> original; + + public MapToObjSpliterator(Function<? super U, ? extends T> map, Spliterator<U> original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final Consumer<? super T> action) { + return original.tryAdvance(u -> action.accept(map.apply(u))); + } + } + + /** + * Object to Int map spliterator. + * @param <T> the input type + */ + static final class MapToIntSpliterator<T> extends Spliterators.AbstractIntSpliterator { + private final ToIntFunction<? super T> map; + private final Spliterator<T> original; + + public MapToIntSpliterator(ToIntFunction<? super T> map, Spliterator<T> original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final IntConsumer action) { + return original.tryAdvance(u -> action.accept(map.applyAsInt(u))); + } + } + + /** + * Object to Long map spliterator. + * @param <T> the input type + */ + static final class MapToLongSpliterator<T> extends Spliterators.AbstractLongSpliterator { + private final ToLongFunction<? super T> map; + private final Spliterator<T> original; + + public MapToLongSpliterator(ToLongFunction<? super T> map, Spliterator<T> original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final LongConsumer action) { + return original.tryAdvance(u -> action.accept(map.applyAsLong(u))); + } + } + + /** + * Object to Double map spliterator. + * @param <T> the input type + */ + static final class MapToDoubleSpliterator<T> extends Spliterators.AbstractDoubleSpliterator { + private final ToDoubleFunction<? super T> map; + private final Spliterator<T> original; + + public MapToDoubleSpliterator(ToDoubleFunction<? super T> map, Spliterator<T> original) { + super( + original.estimateSize(), + original.characteristics() & ~(Spliterator.SORTED | Spliterator.DISTINCT) + ); + checkNotNull(map); + this.map = map; + this.original = original; + } + + @Override + public boolean tryAdvance(final DoubleConsumer action) { + return original.tryAdvance(u -> action.accept(map.applyAsDouble(u))); + } + } + + /** + * Object filter spliterator. + * @param <T> the type of data to iterate over + */ + static final class FilterSpliterator<T> extends Spliterators.AbstractSpliterator<T> { + private final Predicate<? super T> filter; + private final Spliterator<T> original; + + private boolean found; + + public FilterSpliterator(Predicate<? super T> filter, Spliterator<T> original) { + super(original.estimateSize(), original.characteristics() & ~Spliterator.SIZED); + checkNotNull(filter); + this.filter = filter; + this.original = original; + } + + @Override + public Comparator<? super T> getComparator() { + return original.getComparator(); + } + + @Override + public boolean tryAdvance(final Consumer<? super T> action) { + found = false; + while (!found && original.tryAdvance(item -> { + if (filter.test(item)) { + found = true; + action.accept(item); + } + })) { + // do nothing, work is done in tryAdvance + } + + return found; + } + } + + /** + * Object skip spliterator. + * @param <T> the type of data to iterate over + */ + static final class SkipSpliterator<T> extends Spliterators.AbstractSpliterator<T> { + private long skip; + private final Spliterator<T> original; + + public SkipSpliterator(long skip, Spliterator<T> original) { + super( + original.hasCharacteristics(Spliterator.SIZED) + ? Math.max(0, original.estimateSize() - skip) + : Long.MAX_VALUE, + original.characteristics() + ); + this.skip = skip; + this.original = original; + } + + @Override + public Comparator<? super T> getComparator() { + return original.getComparator(); + } + + @Override + public boolean tryAdvance(Consumer<? super T> action) { + while (skip > 0) { + if (!original.tryAdvance(ignore -> { })) { + return false; + } + skip--; + } + return original.tryAdvance(action); + } + } + + /** + * Object limit spliterator. + * @param <T> the type of data to iterate over + */ + static final class LimitSpliterator<T> extends Spliterators.AbstractSpliterator<T> { + private final long limit; + private final Spliterator<T> original; + private int position = 0; + + public LimitSpliterator(long limit, Spliterator<T> original) { + super( + original.hasCharacteristics(Spliterator.SIZED) + ? Math.min(original.estimateSize(), limit) + : Long.MAX_VALUE, + original.characteristics() + ); + this.limit = limit; + this.original = original; + } + + @Override + public Comparator<? super T> getComparator() { + return original.getComparator(); + } + + @Override + public boolean tryAdvance(Consumer<? super T> action) { + if (limit <= position) { + return false; + } + boolean result = original.tryAdvance(action); + position++; + return result; + } + } + + /** + * Main implementation of Stream, wrapping a single spliterator and an optional parent stream. + * @param <T> + */ + static class StreamSource<T> extends TerminatableStream<StreamSource<T>> implements Stream<T> { + private final Spliterator<T> spliterator; + + public StreamSource(TerminatableStream<?> prev, Spliterator<T> spliterator) { + super(prev); + this.spliterator = spliterator; + } + + // terminal + @Override + public Spliterator<T> spliterator() { + terminate(); + return spliterator; + } + + @Override + public Iterator<T> iterator() { + return Spliterators.iterator(spliterator()); + } + + @Override + public long count() { + terminate(); + long count = 0; + while (spliterator.tryAdvance(a -> { })) { + count++; + } + return count; + } + + @Override + public void forEach(Consumer<? super T> action) { + forEachOrdered(action); + } + + @Override + public void forEachOrdered(Consumer<? super T> action) { + terminate(); + spliterator.forEachRemaining(action); + } + + @Override + public Object[] toArray() { + return toArray(Object[]::new); + } + + @Override + public <A> A[] toArray(IntFunction<A[]> generator) { + List<T> collected = collect(Collectors.toList()); + return collected.toArray(generator.apply(collected.size())); + } + + @Override + public <R> R collect(Supplier<R> supplier, + BiConsumer<R, ? super T> accumulator, + BiConsumer<R, R> combiner) { + return collect(Collector.of(supplier, accumulator, (a, b) -> { + combiner.accept(a, b); + return a; + })); + } + + @Override + public <R, A> R collect(final Collector<? super T, A, R> collector) { + return collector.finisher().apply(reduce( + collector.supplier().get(), + (a, t) -> { + collector.accumulator().accept(a, t); + return a; + }, + collector.combiner() + )); + } + + @Override + public Optional<T> findFirst() { + terminate(); + ValueConsumer<T> holder = new ValueConsumer<T>(); + if (spliterator.tryAdvance(holder)) { + return Optional.of(holder.value); + } + return Optional.empty(); + } + + @Override + public Optional<T> findAny() { + return findFirst(); + } + + @Override + public boolean anyMatch(Predicate<? super T> predicate) { + return filter(predicate).findFirst().isPresent(); + } + + @Override + public boolean allMatch(final Predicate<? super T> predicate) { + return !anyMatch(predicate.negate()); + } + + @Override + public boolean noneMatch(final Predicate<? super T> predicate) { + return !anyMatch(predicate); + } + + @Override + public Optional<T> min(final Comparator<? super T> comparator) { + return reduce(BinaryOperator.minBy(comparator)); + } + + @Override + public Optional<T> max(final Comparator<? super T> comparator) { + return reduce(BinaryOperator.maxBy(comparator)); + } + + @Override + public T reduce(T identity, BinaryOperator<T> accumulator) { + return reduce(identity, accumulator, accumulator); + } + + @Override + public Optional<T> reduce(BinaryOperator<T> accumulator) { + ValueConsumer<T> consumer = new ValueConsumer<T>(); + if (!spliterator.tryAdvance(consumer)) { + terminate(); + return Optional.empty(); + } + return Optional.of(reduce(consumer.value, accumulator)); + } + + // combiner is ignored, since we don't parallelize + @Override + public <U> U reduce(U identity, + BiFunction<U, ? super T, U> accumulator, + BinaryOperator<U> combiner) { + terminate(); + final ValueConsumer<U> consumer = new ValueConsumer<U>(); + consumer.value = identity; + spliterator.forEachRemaining(item -> { + consumer.accept(accumulator.apply(consumer.value, item)); + }); + return consumer.value; + } + // end terminal + + // intermediate + @Override + public Stream<T> filter(Predicate<? super T> predicate) { + throwIfTerminated(); + return new StreamSource<>(this, new FilterSpliterator<>(predicate, spliterator)); + } + + @Override + public <R> Stream<R> map(Function<? super T, ? extends R> mapper) { + throwIfTerminated(); + return new StreamSource<>(this, new MapToObjSpliterator<>(mapper, spliterator)); + } + + @Override + public IntStream mapToInt(ToIntFunction<? super T> mapper) { + throwIfTerminated(); + return new IntStream.IntStreamSource(this, new MapToIntSpliterator<>(mapper, spliterator)); + } + + @Override + public LongStream mapToLong(ToLongFunction<? super T> mapper) { + throwIfTerminated(); + return new LongStream.LongStreamSource(this, new MapToLongSpliterator<>(mapper, spliterator)); + } + + @Override + public DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper) { + throwIfTerminated(); + return new DoubleStream.DoubleStreamSource( + this, + new MapToDoubleSpliterator<>(mapper, spliterator) + ); + } + + @Override + public <R> Stream<R> flatMap(final Function<? super T, ? extends Stream<? extends R>> mapper) { + throwIfTerminated(); + final Spliterator<? extends Stream<? extends R>> spliteratorOfStreams = + new MapToObjSpliterator<>(mapper, spliterator); + return new StreamSource<R>(this, new Spliterators.AbstractSpliterator<R>(Long.MAX_VALUE, 0) { + Stream<? extends R> nextStream; + Spliterator<? extends R> next; + + @Override + public boolean tryAdvance(Consumer<? super R> action) { + // look for a new spliterator + while (advanceToNextSpliterator()) { + // if we have one, try to read and use it + if (next.tryAdvance(action)) { + return true; + } else { + nextStream.close(); + nextStream = null; + // failed, null it out so we can find another + next = null; + } + } + return false; + } + + private boolean advanceToNextSpliterator() { + while (next == null) { + if (!spliteratorOfStreams.tryAdvance(n -> { + if (n != null) { + nextStream = n; + next = n.spliterator(); + } + })) { + return false; + } + } + return true; + } + }); + } + + @Override + public IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) { + throwIfTerminated(); + final Spliterator<? extends IntStream> spliteratorOfStreams = + new MapToObjSpliterator<>(mapper, spliterator); + return new IntStream.IntStreamSource( + this, + new Spliterators.AbstractIntSpliterator(Long.MAX_VALUE, 0) { + IntStream nextStream; + Spliterator.OfInt next; + + @Override + public boolean tryAdvance(IntConsumer action) { + // look for a new spliterator + while (advanceToNextSpliterator()) { + // if we have one, try to read and use it + if (next.tryAdvance(action)) { + return true; + } else { + nextStream.close(); + nextStream = null; + // failed, null it out so we can find another + next = null; + } + } + return false; + } + + private boolean advanceToNextSpliterator() { + while (next == null) { + if (!spliteratorOfStreams.tryAdvance(n -> { + if (n != null) { + nextStream = n; + next = n.spliterator(); + } + })) { + return false; + } + } + return true; + } + } + ); + } + + @Override + public LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper) { + throwIfTerminated(); + final Spliterator<? extends LongStream> spliteratorOfStreams = + new MapToObjSpliterator<>(mapper, spliterator); + return new LongStream.LongStreamSource( + this, + new Spliterators.AbstractLongSpliterator(Long.MAX_VALUE, 0) { + LongStream nextStream; + Spliterator.OfLong next; + + @Override + public boolean tryAdvance(LongConsumer action) { + // look for a new spliterator + while (advanceToNextSpliterator()) { + // if we have one, try to read and use it + if (next.tryAdvance(action)) { + return true; + } else { + nextStream.close(); + nextStream = null; + // failed, null it out so we can find another + next = null; + } + } + return false; + } + + private boolean advanceToNextSpliterator() { + while (next == null) { + if (!spliteratorOfStreams.tryAdvance(n -> { + if (n != null) { + nextStream = n; + next = n.spliterator(); + } + })) { + return false; + } + } + return true; + } + } + ); + } + + @Override + public DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) { + throwIfTerminated(); + final Spliterator<? extends DoubleStream> spliteratorOfStreams = + new MapToObjSpliterator<>(mapper, spliterator); + return new DoubleStream.DoubleStreamSource( + this, + new Spliterators.AbstractDoubleSpliterator(Long.MAX_VALUE, 0) { + DoubleStream nextStream; + Spliterator.OfDouble next; + + @Override + public boolean tryAdvance(DoubleConsumer action) { + // look for a new spliterator + while (advanceToNextSpliterator()) { + // if we have one, try to read and use it + if (next.tryAdvance(action)) { + return true; + } else { + nextStream.close(); + nextStream = null; + // failed, null it out so we can find another + next = null; + } + } + return false; + } + + private boolean advanceToNextSpliterator() { + while (next == null) { + if (!spliteratorOfStreams.tryAdvance(n -> { + if (n != null) { + nextStream = n; + next = n.spliterator(); + } + })) { + return false; + } + } + return true; + } + } + ); + } + + @Override + public Stream<T> distinct() { + throwIfTerminated(); + HashSet<T> seen = new HashSet<>(); + return filter(seen::add); + } + + @Override + public Stream<T> sorted() { + throwIfTerminated(); + Comparator<T> c = (Comparator) Comparator.naturalOrder(); + return sorted(c); + } + + @Override + public Stream<T> sorted(final Comparator<? super T> comparator) { + throwIfTerminated(); + return new StreamSource<>( + this, + new Spliterators.AbstractSpliterator<T>( + spliterator.estimateSize(), + spliterator.characteristics() | Spliterator.SORTED + ) { + Spliterator<T> ordered = null; + + @Override + public Comparator<? super T> getComparator() { + return comparator == Comparator.naturalOrder() ? null : comparator; + } + + @Override + public boolean tryAdvance(Consumer<? super T> action) { + if (ordered == null) { + List<T> list = new ArrayList<>(); + spliterator.forEachRemaining(list::add); + Collections.sort(list, comparator); + ordered = list.spliterator(); + } + return ordered.tryAdvance(action); + } + } + ); + } + + @Override + public Stream<T> peek(final Consumer<? super T> action) { + checkNotNull(action); + throwIfTerminated(); + return new StreamSource<>( + this, + new Spliterators.AbstractSpliterator<T>( + spliterator.estimateSize(), + spliterator.characteristics() + ) { + @Override + public boolean tryAdvance(final Consumer<? super T> innerAction) { + return spliterator.tryAdvance(item -> { + action.accept(item); + innerAction.accept(item); + }); + } + } + ); + } + + @Override + public Stream<T> limit(long maxSize) { + throwIfTerminated(); + checkState(maxSize >= 0, "maxSize may not be negative"); + return new StreamSource<>(this, new LimitSpliterator<>(maxSize, spliterator)); + } + + @Override + public Stream<T> skip(long n) { + throwIfTerminated(); + checkState(n >= 0, "n may not be negative"); + if (n == 0) { + return this; + } + return new StreamSource<>(this, new SkipSpliterator<>(n, spliterator)); + } + + @Override + public boolean isParallel() { + throwIfTerminated(); + return false; + } + + @Override + public Stream<T> sequential() { + throwIfTerminated(); + return this; + } + + @Override + public Stream<T> parallel() { + throwIfTerminated(); + // do nothing, no such thing as gwt+parallel + return this; + } + + @Override + public Stream<T> unordered() { + throwIfTerminated(); + return this; + } + } +}
diff --git a/user/super/com/google/gwt/emul/java/util/stream/StreamSupport.java b/user/super/com/google/gwt/emul/java/util/stream/StreamSupport.java new file mode 100644 index 0000000..12b34e3 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/stream/StreamSupport.java
@@ -0,0 +1,84 @@ +/* + * Copyright 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package java.util.stream; + +import java.util.Spliterator; +import java.util.function.Supplier; + +/** + * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/StreamSupport.html"> + * the official Java API doc</a> for details. + */ +public final class StreamSupport { + + public static DoubleStream doubleStream(Spliterator.OfDouble spliterator, boolean parallel) { + return new DoubleStream.DoubleStreamSource(null, spliterator); + } + + public static DoubleStream doubleStream(Supplier<? extends Spliterator.OfDouble> supplier, + int characteristics, + boolean parallel) { + // TODO this is somewhat convoluted, and would be better served by a lazy singleton spliterator + return Stream.of(supplier).map(Supplier::get).flatMapToDouble(doubleSpliterator -> { + return doubleStream(doubleSpliterator, parallel); + }); + } + + public static IntStream intStream(Spliterator.OfInt spliterator, boolean parallel) { + return new IntStream.IntStreamSource(null, spliterator); + } + + public static IntStream intStream(Supplier<? extends Spliterator.OfInt> supplier, + int characteristics, + boolean parallel) { + // TODO this is somewhat convoluted, and would be better served by a lazy singleton spliterator + return Stream.of(supplier).map(Supplier::get).flatMapToInt(intSpliterator -> { + return intStream(intSpliterator, parallel); + }); + } + + public static LongStream longStream(Spliterator.OfLong spliterator, boolean parallel) { + return new LongStream.LongStreamSource(null, spliterator); + } + + public static LongStream longStream(Supplier<? extends Spliterator.OfLong> supplier, + int characteristics, + final boolean parallel) { + // TODO this is somewhat convoluted, and would be better served by a lazy singleton spliterator + return Stream.of(supplier).map(Supplier::get).flatMapToLong(longSpliterator -> { + return longStream(longSpliterator, parallel); + }); + } + + public static <T> Stream<T> stream(Spliterator<T> spliterator, boolean parallel) { + return new Stream.StreamSource<T>(null, spliterator); + } + + public static <T> Stream<T> stream(Supplier<? extends Spliterator<T>> supplier, + int characteristics, + final boolean parallel) { + // TODO this is somewhat convoluted, and would be better served by a lazy singleton spliterator + return Stream.of(supplier).map(Supplier::get).flatMap(spliterator -> { + return stream(spliterator, parallel); + }); + } + + private StreamSupport() { + // prevent instantiation + } + +}
diff --git a/user/super/com/google/gwt/emul/java/util/stream/TerminatableStream.java b/user/super/com/google/gwt/emul/java/util/stream/TerminatableStream.java new file mode 100644 index 0000000..018331f --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/stream/TerminatableStream.java
@@ -0,0 +1,102 @@ +/* + * Copyright 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package java.util.stream; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; + +// package protected, as not part of jre +class TerminatableStream<T extends TerminatableStream<T>> { + // root-only fields, ignored for non-root instances + private boolean terminated = false; + private final List<Runnable> onClose; + + private final TerminatableStream<?> root; + + public TerminatableStream(TerminatableStream<?> previous) { + if (previous == null) { + root = null; + onClose = new ArrayList<>(); + } else { + root = previous; + onClose = null; + } + } + + void throwIfTerminated() { + if (root != null) { + root.throwIfTerminated(); + } else if (terminated) { + throw new IllegalStateException("Stream already terminated, can't be modified or used"); + } + } + + // note that not all terminals directly call this, but they must use it indirectly + void terminate() { + if (root == null) { + throwIfTerminated(); + terminated = true; + } else { + root.terminate(); + } + } + + public T onClose(Runnable closeHandler) { + if (root == null) { + onClose.add(closeHandler); + } else { + root.onClose(closeHandler); + } + + return (T) this; + } + + public void close() { + if (root == null) { + terminated = true; + + // TODO this might not be quite right, make sure that it behaves the same way when throwing + // the same exception multiple times as JDK does. + LinkedHashSet<Throwable> throwables = new LinkedHashSet<>(); + onClose.forEach((runnable) -> { + try { + runnable.run(); + } catch (Throwable e) { + throwables.add(e); + } + }); + onClose.clear(); + if (!throwables.isEmpty()) { + Iterator<Throwable> iterator = throwables.iterator(); + Throwable outer = iterator.next(); + iterator.forEachRemaining(outer::addSuppressed); + + if (outer instanceof RuntimeException) { + throw (RuntimeException) outer; + } + if (outer instanceof Error) { + throw (Error) outer; + } + assert false : "Couldn't have caught this exception from a Runnable! " + outer; + } + } else { + root.close(); + } + } +} \ No newline at end of file
diff --git a/user/test/com/google/gwt/emultest/EmulJava8Suite.java b/user/test/com/google/gwt/emultest/EmulJava8Suite.java index e898b30..572eb86 100644 --- a/user/test/com/google/gwt/emultest/EmulJava8Suite.java +++ b/user/test/com/google/gwt/emultest/EmulJava8Suite.java
@@ -43,6 +43,11 @@ import com.google.gwt.emultest.java8.util.TreeMapTest; import com.google.gwt.emultest.java8.util.VectorTest; import com.google.gwt.emultest.java8.util.stream.CollectorsTest; +import com.google.gwt.emultest.java8.util.stream.DoubleStreamTest; +import com.google.gwt.emultest.java8.util.stream.IntStreamTest; +import com.google.gwt.emultest.java8.util.stream.LongStreamTest; +import com.google.gwt.emultest.java8.util.stream.StreamSupportTest; +import com.google.gwt.emultest.java8.util.stream.StreamTest; import com.google.gwt.junit.tools.GWTTestSuite; import junit.framework.Test; @@ -90,6 +95,11 @@ //-- java.util.stream suite.addTestSuite(CollectorsTest.class); + suite.addTestSuite(DoubleStreamTest.class); + suite.addTestSuite(IntStreamTest.class); + suite.addTestSuite(LongStreamTest.class); + suite.addTestSuite(StreamTest.class); + suite.addTestSuite(StreamSupportTest.class); return suite; }
diff --git a/user/test/com/google/gwt/emultest/java/util/EmulTestBase.java b/user/test/com/google/gwt/emultest/java/util/EmulTestBase.java index a3babcc..c1fc5de 100644 --- a/user/test/com/google/gwt/emultest/java/util/EmulTestBase.java +++ b/user/test/com/google/gwt/emultest/java/util/EmulTestBase.java
@@ -17,6 +17,7 @@ import com.google.gwt.junit.client.GWTTestCase; +import java.util.Arrays; import java.util.List; /** @@ -41,6 +42,21 @@ } } + public static void assertEquals(int[] expected, int[] actual) { + assertTrue("expected: " + Arrays.toString(expected) + ", actual: " + Arrays.toString(actual), + Arrays.equals(expected, actual)); + } + + public static void assertEquals(long[] expected, long[] actual) { + assertTrue("expected: " + Arrays.toString(expected) + ", actual: " + Arrays.toString(actual), + Arrays.equals(expected, actual)); + } + + public static void assertEquals(double[] expected, double[] actual) { + assertTrue("expected: " + Arrays.toString(expected) + ", actual: " + Arrays.toString(actual), + Arrays.equals(expected, actual)); + } + @Override public String getModuleName() { return "com.google.gwt.emultest.EmulSuite";
diff --git a/user/test/com/google/gwt/emultest/java8/util/ArraysTest.java b/user/test/com/google/gwt/emultest/java8/util/ArraysTest.java index 3a4d42b..ffb42dd 100644 --- a/user/test/com/google/gwt/emultest/java8/util/ArraysTest.java +++ b/user/test/com/google/gwt/emultest/java8/util/ArraysTest.java
@@ -135,4 +135,156 @@ Arrays.setAll(array, i -> "" + (i + 1)); assertEquals(new String[]{"1", "2", "3"}, array); } + + public void testArrayStreamInt() { + int[] six = {1, 2, 3, 4, 5, 6}; + + // zero entries + assertEquals(new int[]{}, Arrays.stream(six, 0, 0).toArray()); + assertEquals(new int[]{}, Arrays.stream(six, 1, 1).toArray()); + + // single entry + assertEquals(new int[]{1}, Arrays.stream(six, 0, 1).toArray()); + assertEquals(new int[]{2}, Arrays.stream(six, 1, 2).toArray()); + + // multiple entries + assertEquals(new int[]{1, 2, 3}, Arrays.stream(six, 0, 3).toArray()); + assertEquals(new int[]{4, 5, 6}, Arrays.stream(six, 3, 6).toArray()); + + try { + // start < 0 + Arrays.stream(six, -1, 1); + fail("expected aioobe"); + } catch (ArrayIndexOutOfBoundsException expected) { + // expected + } + try { + // end < start + Arrays.stream(six, 2, 1); + fail("expected aioobe"); + } catch (ArrayIndexOutOfBoundsException expected) { + // expected + } + try { + // end > size + Arrays.stream(six, 0, 7); + fail("expected aioobe"); + } catch (ArrayIndexOutOfBoundsException expected) { + // expected + } + } + + public void testArrayStreamLong() { + long[] six = {1, 2, 3, 4, 5, 6}; + + // zero entries + assertEquals(new long[]{}, Arrays.stream(six, 0, 0).toArray()); + assertEquals(new long[]{}, Arrays.stream(six, 1, 1).toArray()); + + // single entry + assertEquals(new long[]{1}, Arrays.stream(six, 0, 1).toArray()); + assertEquals(new long[]{2}, Arrays.stream(six, 1, 2).toArray()); + + // multiple entries + assertEquals(new long[]{1, 2, 3}, Arrays.stream(six, 0, 3).toArray()); + assertEquals(new long[]{4, 5, 6}, Arrays.stream(six, 3, 6).toArray()); + + try { + // start < 0 + Arrays.stream(six, -1, 1); + fail("expected aioobe"); + } catch (ArrayIndexOutOfBoundsException expected) { + // expected + } + try { + // end < start + Arrays.stream(six, 2, 1); + fail("expected aioobe"); + } catch (ArrayIndexOutOfBoundsException expected) { + // expected + } + try { + // end > size + Arrays.stream(six, 0, 7); + fail("expected aioobe"); + } catch (ArrayIndexOutOfBoundsException expected) { + // expected + } + } + + public void testArrayStreamDouble() { + double[] six = {1, 2, 3, 4, 5, 6}; + + // zero entries + assertEquals(new double[]{}, Arrays.stream(six, 0, 0).toArray()); + assertEquals(new double[]{}, Arrays.stream(six, 1, 1).toArray()); + + // single entry + assertEquals(new double[]{1}, Arrays.stream(six, 0, 1).toArray()); + assertEquals(new double[]{2}, Arrays.stream(six, 1, 2).toArray()); + + // multiple entries + assertEquals(new double[]{1, 2, 3}, Arrays.stream(six, 0, 3).toArray()); + assertEquals(new double[]{4, 5, 6}, Arrays.stream(six, 3, 6).toArray()); + + try { + // start < 0 + Arrays.stream(six, -1, 1); + fail("expected aioobe"); + } catch (ArrayIndexOutOfBoundsException expected) { + // expected + } + try { + // end < start + Arrays.stream(six, 2, 1); + fail("expected aioobe"); + } catch (ArrayIndexOutOfBoundsException expected) { + // expected + } + try { + // end > size + Arrays.stream(six, 0, 7); + fail("expected aioobe"); + } catch (ArrayIndexOutOfBoundsException expected) { + // expected + } + } + + public void testArrayStreamObject() { + String[] six = {"1", "2", "3", "4", "5", "6"}; + + // zero entries + assertEquals(new String[]{}, Arrays.stream(six, 0, 0).toArray()); + assertEquals(new String[]{}, Arrays.stream(six, 1, 1).toArray()); + + // single entry + assertEquals(new String[]{"1"}, Arrays.stream(six, 0, 1).toArray()); + assertEquals(new String[]{"2"}, Arrays.stream(six, 1, 2).toArray()); + + // multiple entries + assertEquals(new String[]{"1", "2", "3"}, Arrays.stream(six, 0, 3).toArray()); + assertEquals(new String[]{"4", "5", "6"}, Arrays.stream(six, 3, 6).toArray()); + + try { + // start < 0 + Arrays.stream(six, -1, 1); + fail("expected aioobe"); + } catch (ArrayIndexOutOfBoundsException expected) { + // expected + } + try { + // end < start + Arrays.stream(six, 2, 1); + fail("expected aioobe"); + } catch (ArrayIndexOutOfBoundsException expected) { + // expected + } + try { + // end > size + Arrays.stream(six, 0, 7); + fail("expected aioobe"); + } catch (ArrayIndexOutOfBoundsException expected) { + // expected + } + } }
diff --git a/user/test/com/google/gwt/emultest/java8/util/stream/DoubleStreamTest.java b/user/test/com/google/gwt/emultest/java8/util/stream/DoubleStreamTest.java new file mode 100644 index 0000000..fb0cc17 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java8/util/stream/DoubleStreamTest.java
@@ -0,0 +1,443 @@ +/* + * Copyright 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.gwt.emultest.java8.util.stream; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.DoubleSummaryStatistics; +import java.util.Iterator; +import java.util.List; +import java.util.OptionalDouble; +import java.util.Spliterator; +import java.util.function.DoubleSupplier; +import java.util.function.Supplier; +import java.util.stream.DoubleStream; +import java.util.stream.Stream; + +/** + * Tests {@link DoubleStream}. + */ +public class DoubleStreamTest extends EmulTestBase { + + public void testEmptyStream() { + DoubleStream empty = DoubleStream.empty(); + assertEquals(0, empty.count()); + try { + empty.count(); + fail("second terminal operation should have thrown IllegalStateEx"); + } catch (IllegalStateException expected) { + // expected + } + + assertEquals(0, DoubleStream.empty().limit(2).toArray().length); + assertEquals(0L, DoubleStream.empty().count()); + assertEquals(0L, DoubleStream.empty().limit(2).count()); + + assertFalse(DoubleStream.empty().findFirst().isPresent()); + assertFalse(DoubleStream.empty().findAny().isPresent()); + assertFalse(DoubleStream.empty().max().isPresent()); + assertFalse(DoubleStream.empty().min().isPresent()); + assertTrue(DoubleStream.empty().noneMatch(item -> true)); + assertTrue(DoubleStream.empty().allMatch(item -> true)); + assertFalse(DoubleStream.empty().anyMatch(item -> true)); + assertEquals(new double[0], DoubleStream.empty().toArray()); + } + + public void testStreamOfOne() { + Supplier<DoubleStream> one = () -> DoubleStream.of(1); + assertEquals(new double[]{1d}, one.get().toArray()); + assertEquals(1L, one.get().count()); + assertEquals(1d, one.get().findFirst().getAsDouble(), 0d); + assertEquals(1d, one.get().findAny().getAsDouble(), 0d); + } + + public void testBuilder() { + DoubleStream s = DoubleStream.builder() + .add(1d) + .add(3d) + .add(2d) + .build(); + + assertEquals( + new double[] {1d, 3d, 2d}, + s.toArray() + ); + + DoubleStream.Builder builder = DoubleStream.builder(); + DoubleStream built = builder.build(); + assertEquals(0, built.count()); + try { + builder.build(); + fail("build() after build() should fail"); + } catch (IllegalStateException expected) { + // expected + } + try { + builder.add(10d); + fail("add() after build() should fail"); + } catch (IllegalStateException expected) { + // expected + } + } + + public void testConcat() { + Supplier<DoubleStream> adbc = () -> { + return DoubleStream.concat(DoubleStream.of(1, 4), DoubleStream.of(2, 3)); + }; + + assertEquals(new double[]{1d, 4d, 2d, 3d}, adbc.get().toArray()); + assertEquals(new double[]{1d, 2d, 3d, 4d}, adbc.get().sorted().toArray()); + + List<String> closed = new ArrayList<>(); + DoubleStream first = DoubleStream.of(1d).onClose(() -> closed.add("first")); + DoubleStream second = DoubleStream.of(2d).onClose(() -> closed.add("second")); + + DoubleStream concat = DoubleStream.concat(first, second); + + // read everything, make sure we saw it all and didn't close automatically + double collectedAll = concat.sum(); + assertEquals(3d, collectedAll); + assertEquals(0, closed.size()); + + concat.close(); + assertEquals(Arrays.asList("first", "second"), closed); + } + + public void testIterate() { + assertEquals( + new double[]{10d, 11d, 12d, 13d, 14d}, + DoubleStream.iterate(0d, l -> l + 1d) + .skip(10) + .limit(5) + .toArray() + ); + } + + public void testGenerate() { + // infinite, but if you limit it is already too short to skip much + assertEquals(new double[0], DoubleStream.generate(makeGenerator()).limit(4).skip(5).toArray()); + + assertEquals( + new double[]{10d, 11d, 12d, 13d, 14d}, + DoubleStream.generate(makeGenerator()) + .skip(10) + .limit(5) + .toArray() + ); + } + + private DoubleSupplier makeGenerator() { + return new DoubleSupplier() { + double next = 0d; + + @Override + public double getAsDouble() { + return next++; + } + }; + } + + public void testToArray() { + assertEquals(new double[0], DoubleStream.of().toArray()); + assertEquals(new double[] {1d}, DoubleStream.of(1d).toArray()); + assertEquals(new double[] {3d, 2d, 0d}, DoubleStream.of(3d, 2d, 0d).toArray()); + } + + public void testReduce() { + double reduced = DoubleStream.of(1d, 2d, 4d).reduce(0, Double::sum); + assertEquals(7d, reduced, 0d); + + reduced = DoubleStream.of().reduce(0, Double::sum); + assertEquals(0d, reduced, 0d); + + OptionalDouble maybe = DoubleStream.of(1d, 4d, 8d).reduce(Double::sum); + assertTrue(maybe.isPresent()); + assertEquals(13, maybe.getAsDouble(), 0d); + maybe = DoubleStream.of().reduce(Double::sum); + assertFalse(maybe.isPresent()); + } + + public void testFilter() { + // unconsumed stream never runs filter + boolean[] data = {false}; + DoubleStream.of(1d, 2d, 3d).filter(i -> data[0] |= true); + assertFalse(data[0]); + + // one result + assertEquals( + new double[]{1d}, + DoubleStream.of(1d, 2d, 3d, 4d, 3d).filter(a -> a == 1).toArray() + ); + // zero results + assertEquals( + new double[0], + DoubleStream.of(1d, 2d, 3d, 4d, 3d).filter(a -> false).toArray() + ); + // two results + assertEquals( + new double[] {2d, 4d}, + DoubleStream.of(1d, 2d, 3d, 4d, 3d).filter(a -> a % 2 == 0).toArray() + ); + // all + assertEquals( + new double[] {1d, 2d, 3d, 4d, 3d}, + DoubleStream.of(1d, 2d, 3d, 4d, 3d).filter(a -> true).toArray() + ); + } + + public void testMap() { + // unconsumed stream never runs map + int[] data = {0}; + DoubleStream.of(1d, 2d, 3d).map(i -> data[0]++); + assertEquals(0, data[0]); + + assertEquals( + new double[] {2d, 4d, 6d}, + DoubleStream.of(1d, 2d, 3d).map(i -> i * 2).toArray() + ); + } + + public void testPeek() { + // unconsumed stream never peeks + boolean[] data = {false}; + DoubleStream.of(1d, 2d, 3d).peek(i -> data[0] |= true); + assertFalse(data[0]); + + // make sure we saw it all in order + double[] items = new double[] {1d, 2d, 3d}; + List<Double> peeked = new ArrayList<>(); + DoubleStream.of(items).peek(peeked::add).forEach(item -> { + // do nothing, just run + }); + assertEquals(items.length, peeked.size()); + for (int i = 0; i < items.length; i++) { + assertEquals(items[i], peeked.get(i), 0d); + } + } + + // same impl, no parallel in browser + public void testFindFirstOrAny() { + OptionalDouble any = DoubleStream.of(1d, 2d).findAny(); + assertTrue(any.isPresent()); + assertEquals(1d, any.getAsDouble(), 0d); + } + + public void testAnyMatch() { + // all + assertTrue(DoubleStream.of(1d, 2d).anyMatch(s -> true)); + + // some + assertTrue(DoubleStream.of(1d, 2d).anyMatch(s -> s == 1d)); + + // none + assertFalse(DoubleStream.of(1d, 2d).anyMatch(s -> false)); + } + + public void testAllMatch() { + // all + assertTrue(DoubleStream.of(1d, 2d).allMatch(s -> true)); + + // some + assertFalse(DoubleStream.of(1d, 2d).allMatch(s -> s == 1d)); + + // none + assertFalse(DoubleStream.of(1d, 2d).allMatch(s -> false)); + } + + public void testNoneMatch() { + // all + assertFalse(DoubleStream.of(1d, 2d).noneMatch(s -> true)); + + // some + assertFalse(DoubleStream.of(1d, 2d).noneMatch(s -> s == 1d)); + + // none + assertTrue(DoubleStream.of(1d, 2d).noneMatch(s -> false)); + } + + public void testFlatMap() { + assertEquals(0L, DoubleStream.empty().flatMap(value -> DoubleStream.of(1d)).count()); + assertEquals(0L, DoubleStream.of(1d).flatMap(value -> DoubleStream.empty()).count()); + assertEquals(0L, DoubleStream.of(1d).flatMap(value -> DoubleStream.of()).count()); + assertEquals(0L, DoubleStream.of().flatMap(value -> DoubleStream.of(1d)).count()); + assertEquals(1L, DoubleStream.of(1d).flatMap(value -> DoubleStream.of(1d)).count()); + + DoubleStream values = DoubleStream.of(1d, 2d, 3d); + + assertEquals( + new double[] {1d, 2d, 2d, 4d, 3d, 6d}, + values.flatMap(i -> DoubleStream.of(i, i * 2d)).toArray() + ); + } + + public void testMapToOthers() { + Supplier<DoubleStream> s = () -> DoubleStream.of(1d, 2d, 10d); + + assertEquals( + new String[]{"1", "2", "10"}, + s.get().mapToObj(DoubleStreamTest::toIntegralString).toArray(String[]::new) + ); + + assertEquals( + new long[]{1L, 2L, 10L}, + s.get().mapToLong(i -> (long) i).toArray() + ); + + assertEquals( + new int[] {1, 2, 10}, + s.get().mapToInt(i -> (int) i).toArray() + ); + } + + public void testDistinct() { + double[] distinct = DoubleStream.of(1d, 2d, 3d, 2d).distinct().toArray(); + assertEquals(3, distinct.length); + assertEquals(1d + 2d + 3d, distinct[0] + distinct[1] + distinct[2], 0d); + } + + public void testSorted() { + double[] sorted = DoubleStream.of(3d, 1d, 2d).sorted().toArray(); + assertEquals(new double[] {1d, 2d, 3d}, sorted); + } + + public void testMinMax() { + Supplier<DoubleStream> stream = () -> DoubleStream.of(2d, 3d, 4d, 1d); + + assertEquals(1d, stream.get().min().orElse(0), 0d); + assertEquals(4d, stream.get().max().orElse(0), 0d); + + assertFalse(stream.get().filter(a -> false).max().isPresent()); + assertFalse(stream.get().filter(a -> false).min().isPresent()); + } + + public void testCountLimitSkip() { + Supplier<DoubleStream> stream = () -> DoubleStream.of(1d, 2d, 3d, 4d); + + assertEquals(4L, stream.get().count()); + + assertEquals(4L, stream.get().limit(4).count()); + assertEquals(4L, stream.get().limit(5).count()); + assertEquals(3L, stream.get().limit(3).count()); + + assertEquals(3L, stream.get().skip(1).limit(3).count()); + + assertEquals(2L, stream.get().limit(3).skip(1).count()); + + assertEquals(1L, stream.get().skip(3).count()); + + assertEquals(new double[] {3d, 4d}, stream.get().skip(2).limit(3).toArray()); + assertEquals(new double[] {3d}, stream.get().skip(2).limit(1).toArray()); + + assertEquals(new double[] {4d}, stream.get().skip(3).toArray()); + assertEquals(new double[] {}, stream.get().skip(5).toArray()); + + assertEquals(new double[] {1d, 2d}, stream.get().limit(2).toArray()); + + assertEquals(new double[] {2d}, stream.get().limit(2).skip(1).toArray()); + } + + public void testBoxed() { + Supplier<DoubleStream> stream = () -> DoubleStream.of(1d, 2d); + Stream<Double> expected = stream.get().mapToObj(Double::valueOf); + assertEquals(expected.toArray(), stream.get().boxed().toArray()); + } + + public void testSummaryStats() { + Supplier<DoubleStream> stream = () -> DoubleStream.of(1d, 2d, 3d); + DoubleSummaryStatistics summaryStats = stream.get().summaryStatistics(); + assertEquals(3L, summaryStats.getCount()); + assertEquals(1d, summaryStats.getMin(), 0d); + assertEquals(2d, summaryStats.getAverage(), 0d); + assertEquals(3d, summaryStats.getMax(), 0d); + assertEquals(6d, summaryStats.getSum(), 0d); + + summaryStats.accept(6L); + assertEquals(4L, summaryStats.getCount()); + assertEquals(1d, summaryStats.getMin(), 0d); + assertEquals(3d, summaryStats.getAverage(), 0d); + assertEquals(6d, summaryStats.getMax(), 0d); + assertEquals(12d, summaryStats.getSum(), 0d); + + DoubleSummaryStatistics combinedSumStats = stream.get().summaryStatistics(); + combinedSumStats.combine(DoubleStream.of(4d, 5d, 6d, 0d).summaryStatistics()); + assertEquals(7L, combinedSumStats.getCount()); + assertEquals(0d, combinedSumStats.getMin(), 0d); + assertEquals(3d, combinedSumStats.getAverage(), 0d); + assertEquals(6d, combinedSumStats.getMax(), 0d); + assertEquals(21d, combinedSumStats.getSum(), 0d); + } + + public void testAverage() { + assertFalse(DoubleStream.empty().average().isPresent()); + assertEquals(2.0d, DoubleStream.of(1d, 2d, 3d).average().getAsDouble(), 0d); + assertEquals(0d, DoubleStream.of(1d, 2d, -3d).average().getAsDouble(), 0d); + assertEquals(-2.0d, DoubleStream.of(-1d, -2d, -3d).average().getAsDouble(), 0d); + } + + public void testSum() { + assertEquals(6d, DoubleStream.of(1d, 2d, 3d).sum(), 0d); + assertEquals(0d, DoubleStream.of(1d, 2d, -3d).sum(), 0d); + assertEquals(-6d, DoubleStream.of(-1d, -2d, -3d).sum(), 0d); + } + + public void testCollect() { + // noinspection StringConcatenationInsideStringBufferAppend + String val = DoubleStream.of(1d, 2d, 3d, 4d, 5d).collect(StringBuilder::new, + (stringBuilder, d) -> stringBuilder.append(toIntegralString(d)), + StringBuilder::append).toString(); + + assertEquals("12345", val); + } + + public void testForEach() { + List<Double> vals = new ArrayList<>(); + DoubleStream.of(1d, 2d, 3d, 4d, 5d).forEach(vals::add); + assertEquals(5, vals.size()); + assertEquals(new Double[] {1d, 2d, 3d, 4d, 5d}, vals.toArray(new Double[vals.size()])); + } + + public void testIterator() { + List<Double> vals = new ArrayList<>(); + Iterator<Double> iterator = DoubleStream.of(1d, 2d, 3d, 4d, 5d).iterator(); + while (iterator.hasNext()) { + vals.add(iterator.next()); + } + assertEquals(5, vals.size()); + assertEquals(new Double[] {1d, 2d, 3d, 4d, 5d}, vals.toArray(new Double[vals.size()])); + } + + public void testSpliterator() { + Spliterator<Double> spliterator = DoubleStream.of(1d, 2d, 3d, 4d, 5d).spliterator(); + assertEquals(5, spliterator.estimateSize()); + assertEquals(5, spliterator.getExactSizeIfKnown()); + + List<Double> vals = new ArrayList<>(); + while (spliterator.tryAdvance(vals::add)) { + // work is all done in the condition + } + + assertEquals(5, vals.size()); + assertEquals(new Double[] {1d, 2d, 3d, 4d, 5d}, vals.toArray(new Double[vals.size()])); + } + + // See https://github.com/gwtproject/gwt/issues/8615 + private static String toIntegralString(double value) { + return "" + (int) value; + } +}
diff --git a/user/test/com/google/gwt/emultest/java8/util/stream/IntStreamTest.java b/user/test/com/google/gwt/emultest/java8/util/stream/IntStreamTest.java new file mode 100644 index 0000000..232aebe --- /dev/null +++ b/user/test/com/google/gwt/emultest/java8/util/stream/IntStreamTest.java
@@ -0,0 +1,462 @@ +/* + * Copyright 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.gwt.emultest.java8.util.stream; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.IntSummaryStatistics; +import java.util.Iterator; +import java.util.List; +import java.util.OptionalInt; +import java.util.Spliterator; +import java.util.function.IntSupplier; +import java.util.function.Supplier; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +/** + * Tests {@link IntStream}. + */ +public class IntStreamTest extends EmulTestBase { + + public void testEmptyStream() { + IntStream empty = IntStream.empty(); + assertEquals(0, empty.count()); + try { + empty.count(); + fail("second terminal operation should have thrown IllegalStateEx"); + } catch (IllegalStateException expected) { + // expected + } + + assertEquals(0, IntStream.empty().limit(2).toArray().length); + assertEquals(0L, IntStream.empty().count()); + assertEquals(0L, IntStream.empty().limit(2).count()); + + assertFalse(IntStream.empty().findFirst().isPresent()); + assertFalse(IntStream.empty().findAny().isPresent()); + assertFalse(IntStream.empty().max().isPresent()); + assertFalse(IntStream.empty().min().isPresent()); + assertTrue(IntStream.empty().noneMatch(item -> true)); + assertTrue(IntStream.empty().allMatch(item -> true)); + assertFalse(IntStream.empty().anyMatch(item -> true)); + assertEquals(new int[0], IntStream.empty().toArray()); + } + + public void testStreamOfOne() { + Supplier<IntStream> one = () -> IntStream.of(1); + assertEquals(new int[]{1}, one.get().toArray()); + assertEquals(1L, one.get().count()); + assertEquals(1, one.get().findFirst().getAsInt()); + assertEquals(1, one.get().findAny().getAsInt()); + } + + public void testBuilder() { + IntStream s = IntStream.builder() + .add(1) + .add(3) + .add(2) + .build(); + + assertEquals( + new int[] {1, 3, 2}, + s.toArray() + ); + + IntStream.Builder builder = IntStream.builder(); + IntStream built = builder.build(); + assertEquals(0, built.count()); + try { + builder.build(); + fail("build() after build() should fail"); + } catch (IllegalStateException expected) { + // expected + } + try { + builder.add(10); + fail("add() after build() should fail"); + } catch (IllegalStateException expected) { + // expected + } + } + + public void testConcat() { + Supplier<IntStream> adbc = () -> IntStream.concat(IntStream.of(1, 4), IntStream.of(2, 3)); + + assertEquals(new int[]{1, 4, 2, 3}, adbc.get().toArray()); + assertEquals(new int[]{1, 2, 3, 4}, adbc.get().sorted().toArray()); + + List<String> closed = new ArrayList<>(); + IntStream first = IntStream.of(1).onClose(() -> closed.add("first")); + IntStream second = IntStream.of(2).onClose(() -> closed.add("second")); + + IntStream concat = IntStream.concat(first, second); + + // read everything, make sure we saw it all and didn't close automatically + int collectedAll = concat.sum(); + assertEquals(3, collectedAll); + assertEquals(0, closed.size()); + + concat.close(); + assertEquals(Arrays.asList("first", "second"), closed); + } + + public void testIterate() { + assertEquals( + new int[]{10, 11, 12, 13, 14}, + IntStream.iterate(0, i -> i + 1) + .skip(10) + .limit(5) + .toArray() + ); + } + + public void testGenerate() { + // infinite, but if you limit it is already too short to skip much + assertEquals(new int[0], IntStream.generate(makeGenerator()).limit(4).skip(5).toArray()); + + assertEquals( + new int[] {10, 11, 12, 13, 14}, + IntStream.generate(makeGenerator()) + .skip(10) + .limit(5) + .toArray() + ); + } + + private IntSupplier makeGenerator() { + return new IntSupplier() { + int next = 0; + + @Override + public int getAsInt() { + return next++; + } + }; + } + + public void testRange() { + assertEquals(new int[] {1, 2, 3, 4}, IntStream.range(1, 5).toArray()); + assertEquals(new int[] {-1, 0, 1, 2, 3, 4}, IntStream.range(-1, 5).toArray()); + assertEquals(new int[] {}, IntStream.range(1, -5).toArray()); + assertEquals(new int[] {}, IntStream.range(-1, -5).toArray()); + } + + public void testRangeClosed() { + assertEquals(new int[] {1, 2, 3, 4, 5}, IntStream.rangeClosed(1, 5).toArray()); + assertEquals(new int[] {-1, 0, 1, 2, 3, 4, 5}, IntStream.rangeClosed(-1, 5).toArray()); + assertEquals(new int[] {}, IntStream.rangeClosed(1, -5).toArray()); + assertEquals(new int[] {}, IntStream.rangeClosed(-1, -5).toArray()); + } + + public void testToArray() { + assertEquals(new int[0], IntStream.of().toArray()); + assertEquals(new int[] {1}, IntStream.of(1).toArray()); + assertEquals(new int[] {3,2,0}, IntStream.of(3,2,0).toArray()); + } + + public void testReduce() { + int reduced = IntStream.of(1, 2, 4).reduce(0, Integer::sum); + assertEquals(7, reduced); + + reduced = IntStream.of().reduce(0, Integer::sum); + assertEquals(0, reduced); + + OptionalInt maybe = IntStream.of(1, 4, 8).reduce(Integer::sum); + assertTrue(maybe.isPresent()); + assertEquals(13, maybe.getAsInt()); + maybe = IntStream.of().reduce(Integer::sum); + assertFalse(maybe.isPresent()); + } + + public void testFilter() { + // unconsumed stream never runs filter + boolean[] data = {false}; + IntStream.of(1, 2, 3).filter(i -> data[0] |= true); + assertFalse(data[0]); + + // one result + assertEquals( + new int[] {1}, + IntStream.of(1, 2, 3, 4, 3).filter(a -> a == 1).toArray() + ); + // zero results + assertEquals( + new int[0], + IntStream.of(1, 2, 3, 4, 3).filter(a -> false).toArray() + ); + // two results + assertEquals( + new int[] {2, 4}, + IntStream.of(1, 2, 3, 4, 3).filter(a -> a % 2 == 0).toArray() + ); + // all + assertEquals( + new int[] {1, 2, 3, 4, 3}, + IntStream.of(1, 2, 3, 4, 3).filter(a -> true).toArray() + ); + } + + public void testMap() { + // unconsumed stream never runs map + int[] data = {0}; + IntStream.of(1, 2, 3).map(i -> data[0]++); + assertEquals(0, data[0]); + + assertEquals( + new int[] {2, 4, 6}, + IntStream.of(1, 2, 3).map(i -> i * 2).toArray() + ); + } + + public void testPeek() { + // unconsumed stream never peeks + boolean[] data = {false}; + IntStream.of(1, 2, 3).peek(i -> data[0] |= true); + assertFalse(data[0]); + + // make sure we saw it all in order + int[] items = new int[] {1, 2, 3}; + List<Integer> peeked = new ArrayList<>(); + IntStream.of(items).peek(peeked::add).forEach(item -> { + // do nothing, just run + }); + assertEquals(items.length, peeked.size()); + for (int i = 0; i < items.length; i++) { + assertEquals(items[i], (int) peeked.get(i)); + } + } + + // same impl, no parallel in browser + public void testFindFirstOrAny() { + OptionalInt any = IntStream.of(1, 2).findAny(); + assertTrue(any.isPresent()); + assertEquals(1, any.getAsInt()); + } + + public void testAnyMatch() { + // all + assertTrue(IntStream.of(1, 2).anyMatch(s -> true)); + + // some + assertTrue(IntStream.of(1, 2).anyMatch(s -> s == 1)); + + // none + assertFalse(IntStream.of(1, 2).anyMatch(s -> false)); + } + + public void testAllMatch() { + // all + assertTrue(IntStream.of(1, 2).allMatch(s -> true)); + + // some + assertFalse(IntStream.of(1, 2).allMatch(s -> s == 1)); + + // none + assertFalse(IntStream.of(1, 2).allMatch(s -> false)); + } + + public void testNoneMatch() { + // all + assertFalse(IntStream.of(1, 2).noneMatch(s -> true)); + + // some + assertFalse(IntStream.of(1, 2).noneMatch(s -> s == 1)); + + // none + assertTrue(IntStream.of(1, 2).noneMatch(s -> false)); + } + + public void testFlatMap() { + assertEquals(0, IntStream.empty().flatMap(value -> IntStream.of(1)).count()); + assertEquals(0, IntStream.of(1).flatMap(value -> IntStream.empty()).count()); + assertEquals(0, IntStream.of(1).flatMap(value -> IntStream.of()).count()); + assertEquals(0, IntStream.of().flatMap(value -> IntStream.of(1)).count()); + assertEquals(1, IntStream.of(1).flatMap(value -> IntStream.of(1)).count()); + + IntStream values = IntStream.of(1, 2, 3); + + assertEquals( + new int[] {1, 2, 2, 4, 3, 6}, + values.flatMap(i -> IntStream.of(i, i * 2)).toArray() + ); + } + + public void testMapToOthers() { + Supplier<IntStream> s = () -> IntStream.of(1, 2, 10); + + assertEquals( + new String[] {"1", "2", "10"}, + s.get().mapToObj(String::valueOf).toArray(String[]::new) + ); + + assertEquals( + new long[] {1, 2, 10}, + s.get().mapToLong(i -> (long) i).toArray() + ); + + assertEquals( + new double[] {1, 2, 10}, + s.get().mapToDouble(i -> (double) i).toArray() + ); + } + + public void testDistinct() { + int[] distinct = IntStream.of(1, 2, 3, 2).distinct().toArray(); + assertEquals(3, distinct.length); + assertEquals(1 + 2 + 3, distinct[0] + distinct[1] + distinct[2]); + } + + public void testSorted() { + int[] sorted = IntStream.of(3, 1, 2).sorted().toArray(); + assertEquals(new int[] {1, 2, 3}, sorted); + } + + public void testMinMax() { + Supplier<IntStream> stream = () -> IntStream.of(2, 3, 4, 1); + + assertEquals(1, stream.get().min().orElse(0)); + assertEquals(4, stream.get().max().orElse(0)); + + assertFalse(stream.get().filter(a -> false).max().isPresent()); + assertFalse(stream.get().filter(a -> false).min().isPresent()); + } + + public void testCountLimitSkip() { + Supplier<IntStream> stream = () -> IntStream.of(1, 2, 3, 4); + + assertEquals(4, stream.get().count()); + + assertEquals(4, stream.get().limit(4).count()); + assertEquals(4, stream.get().limit(5).count()); + assertEquals(3, stream.get().limit(3).count()); + + assertEquals(3, stream.get().skip(1).limit(3).count()); + + assertEquals(2, stream.get().limit(3).skip(1).count()); + + assertEquals(1, stream.get().skip(3).count()); + + assertEquals(new int[] {3, 4}, stream.get().skip(2).limit(3).toArray()); + assertEquals(new int[] {3}, stream.get().skip(2).limit(1).toArray()); + + assertEquals(new int[] {4}, stream.get().skip(3).toArray()); + assertEquals(new int[] {}, stream.get().skip(5).toArray()); + + assertEquals(new int[] {1, 2}, stream.get().limit(2).toArray()); + + assertEquals(new int[] {2}, stream.get().limit(2).skip(1).toArray()); + } + + public void testBoxed() { + Supplier<IntStream> stream = () -> IntStream.of(1, 2); + Stream<Integer> expected = stream.get().mapToObj(Integer::valueOf); + assertEquals(expected.toArray(), stream.get().boxed().toArray()); + } + + public void testAsOtherPrimitive() { + Supplier<IntStream> stream = () -> IntStream.of(1, 2); + + DoubleStream actualDoubleStream = stream.get().asDoubleStream(); + assertEquals(new double[]{1, 2}, actualDoubleStream.toArray()); + + LongStream actualLongStream = stream.get().asLongStream(); + assertEquals(new long[] {1, 2}, actualLongStream.toArray()); + } + + public void testSummaryStats() { + Supplier<IntStream> stream = () -> IntStream.of(1, 2, 3); + IntSummaryStatistics summaryStats = stream.get().summaryStatistics(); + assertEquals(3, summaryStats.getCount()); + assertEquals(1, summaryStats.getMin()); + assertEquals(2, summaryStats.getAverage(), 0d); + assertEquals(3, summaryStats.getMax()); + assertEquals(6, summaryStats.getSum()); + + summaryStats.accept(6); + assertEquals(4, summaryStats.getCount()); + assertEquals(1, summaryStats.getMin()); + assertEquals(3, summaryStats.getAverage(), 0d); + assertEquals(6, summaryStats.getMax()); + assertEquals(12, summaryStats.getSum()); + + IntSummaryStatistics combinedSumStats = stream.get().summaryStatistics(); + combinedSumStats.combine(IntStream.of(4, 5, 6, 0).summaryStatistics()); + assertEquals(7, combinedSumStats.getCount()); + assertEquals(0, combinedSumStats.getMin()); + assertEquals(3, combinedSumStats.getAverage(), 0d); + assertEquals(6, combinedSumStats.getMax()); + assertEquals(21, combinedSumStats.getSum()); + } + + public void testAverage() { + assertFalse(IntStream.empty().average().isPresent()); + assertEquals(2.0d, IntStream.of(1, 2, 3).average().getAsDouble(), 0d); + assertEquals(0d, IntStream.of(1, 2, -3).average().getAsDouble(), 0d); + assertEquals(-2.0d, IntStream.of(-1, -2, -3).average().getAsDouble(), 0d); + } + + public void testSum() { + assertEquals(6, IntStream.of(1, 2, 3).sum()); + assertEquals(0, IntStream.of(1, 2, -3).sum()); + assertEquals(-6, IntStream.of(-1, -2, -3).sum()); + } + + public void testCollect() { + String val = IntStream.of(1, 2, 3, 4, 5).collect(StringBuilder::new, + // TODO switch to a lambda reference once #9340 is fixed + (stringBuilder, i) -> stringBuilder.append(i), + StringBuilder::append).toString(); + + assertEquals("12345", val); + } + + public void testForEach() { + List<Integer> vals = new ArrayList<>(); + IntStream.of(1, 2, 3, 4, 5).forEach(vals::add); + assertEquals(5, vals.size()); + assertEquals(new Integer[] {1, 2, 3, 4, 5}, vals.toArray(new Integer[vals.size()])); + } + + public void testIterator() { + List<Integer> vals = new ArrayList<>(); + Iterator<Integer> iterator = IntStream.of(1, 2, 3, 4, 5).iterator(); + while (iterator.hasNext()) { + vals.add(iterator.next()); + } + assertEquals(5, vals.size()); + assertEquals(new Integer[] {1, 2, 3, 4, 5}, vals.toArray(new Integer[vals.size()])); + } + + public void testSpliterator() { + Spliterator<Integer> spliterator = IntStream.of(1, 2, 3, 4, 5).spliterator(); + assertEquals(5, spliterator.estimateSize()); + assertEquals(5, spliterator.getExactSizeIfKnown()); + + List<Integer> vals = new ArrayList<>(); + while (spliterator.tryAdvance(vals::add)) { + // work is all done in the condition + } + + assertEquals(5, vals.size()); + assertEquals(new Integer[] {1, 2, 3, 4, 5}, vals.toArray(new Integer[vals.size()])); + } +}
diff --git a/user/test/com/google/gwt/emultest/java8/util/stream/LongStreamTest.java b/user/test/com/google/gwt/emultest/java8/util/stream/LongStreamTest.java new file mode 100644 index 0000000..407f4bf --- /dev/null +++ b/user/test/com/google/gwt/emultest/java8/util/stream/LongStreamTest.java
@@ -0,0 +1,457 @@ +/* + * Copyright 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.gwt.emultest.java8.util.stream; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.LongSummaryStatistics; +import java.util.OptionalLong; +import java.util.Spliterator; +import java.util.function.LongSupplier; +import java.util.function.Supplier; +import java.util.stream.DoubleStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +/** + * Tests {@link LongStream}. + */ +public class LongStreamTest extends EmulTestBase { + + public void testEmptyStream() { + LongStream empty = LongStream.empty(); + assertEquals(0, empty.count()); + try { + empty.count(); + fail("second terminal operation should have thrown IllegalStateEx"); + } catch (IllegalStateException expected) { + // expected + } + + assertEquals(0, LongStream.empty().limit(2).toArray().length); + assertEquals(0L, LongStream.empty().count()); + assertEquals(0L, LongStream.empty().limit(2).count()); + + assertFalse(LongStream.empty().findFirst().isPresent()); + assertFalse(LongStream.empty().findAny().isPresent()); + assertFalse(LongStream.empty().max().isPresent()); + assertFalse(LongStream.empty().min().isPresent()); + assertTrue(LongStream.empty().noneMatch(item -> true)); + assertTrue(LongStream.empty().allMatch(item -> true)); + assertFalse(LongStream.empty().anyMatch(item -> true)); + assertEquals(new long[0], LongStream.empty().toArray()); + } + + public void testStreamOfOne() { + Supplier<LongStream> one = () -> LongStream.of(1); + assertEquals(new long[]{1L}, one.get().toArray()); + assertEquals(1L, one.get().count()); + assertEquals(1, one.get().findFirst().getAsLong()); + assertEquals(1, one.get().findAny().getAsLong()); + } + + public void testBuilder() { + LongStream s = LongStream.builder() + .add(1L) + .add(3L) + .add(2L) + .build(); + + assertEquals( + new long[] {1L, 3L, 2L}, + s.toArray() + ); + + LongStream.Builder builder = LongStream.builder(); + LongStream built = builder.build(); + assertEquals(0L, built.count()); + try { + builder.build(); + fail("build() after build() should fail"); + } catch (IllegalStateException expected) { + // expected + } + try { + builder.add(10L); + fail("add() after build() should fail"); + } catch (IllegalStateException expected) { + // expected + } + } + + public void testConcat() { + Supplier<LongStream> adbc = () -> LongStream.concat(LongStream.of(1, 4), LongStream.of(2, 3)); + + assertEquals(new long[]{1L, 4L, 2L, 3L}, adbc.get().toArray()); + assertEquals(new long[]{1L, 2L, 3L, 4L}, adbc.get().sorted().toArray()); + + List<String> closed = new ArrayList<>(); + LongStream first = LongStream.of(1L).onClose(() -> closed.add("first")); + LongStream second = LongStream.of(2L).onClose(() -> closed.add("second")); + + LongStream concat = LongStream.concat(first, second); + + // read everything, make sure we saw it all and didn't close automatically + long collectedAll = concat.sum(); + assertEquals(3L, collectedAll); + assertEquals(0, closed.size()); + + concat.close(); + assertEquals(Arrays.asList("first", "second"), closed); + } + + public void testIterate() { + assertEquals( + new long[]{10L, 11L, 12L, 13L, 14L}, + LongStream.iterate(0L, l -> l + 1L) + .skip(10) + .limit(5) + .toArray() + ); + } + + public void testGenerate() { + // infinite, but if you limit it is already too short to skip much + assertEquals(new long[0], LongStream.generate(makeGenerator()).limit(4).skip(5).toArray()); + + assertEquals( + new long[]{10L, 11L, 12L, 13L, 14L}, + LongStream.generate(makeGenerator()) + .skip(10) + .limit(5) + .toArray() + ); + } + + private LongSupplier makeGenerator() { + return new LongSupplier() { + long next = 0L; + + @Override + public long getAsLong() { + return next++; + } + }; + } + + public void testRange() { + assertEquals(new long[] {1L, 2L, 3L, 4L}, LongStream.range(1, 5).toArray()); + assertEquals(new long[] {-1L, 0L, 1L, 2L, 3L, 4L}, LongStream.range(-1, 5).toArray()); + assertEquals(new long[] {}, LongStream.range(1, -5).toArray()); + assertEquals(new long[] {}, LongStream.range(-1, -5).toArray()); + } + + public void testRangeClosed() { + assertEquals(new long[] {1L, 2L, 3L, 4L, 5L}, LongStream.rangeClosed(1, 5).toArray()); + assertEquals(new long[] {-1L, 0L, 1L, 2L, 3L, 4L, 5L}, LongStream.rangeClosed(-1, 5).toArray()); + assertEquals(new long[] {}, LongStream.rangeClosed(1, -5).toArray()); + assertEquals(new long[] {}, LongStream.rangeClosed(-1, -5).toArray()); + } + + public void testToArray() { + assertEquals(new long[0], LongStream.of().toArray()); + assertEquals(new long[] {1L}, LongStream.of(1L).toArray()); + assertEquals(new long[] {3L, 2L, 0L}, LongStream.of(3L,2L,0L).toArray()); + } + + public void testReduce() { + long reduced = LongStream.of(1L, 2L, 4L).reduce(0, Long::sum); + assertEquals(7, reduced); + + reduced = LongStream.of().reduce(0, Long::sum); + assertEquals(0L, reduced); + + OptionalLong maybe = LongStream.of(1L, 4L, 8L).reduce(Long::sum); + assertTrue(maybe.isPresent()); + assertEquals(13L, maybe.getAsLong()); + maybe = LongStream.of().reduce(Long::sum); + assertFalse(maybe.isPresent()); + } + + public void testFilter() { + // unconsumed stream never runs filter + boolean[] data = {false}; + LongStream.of(1L, 2L, 3L).filter(i -> data[0] |= true); + assertFalse(data[0]); + + // one result + assertEquals( + new long[] {1L}, + LongStream.of(1L, 2L, 3L, 4L, 3L).filter(a -> a == 1).toArray() + ); + // zero results + assertEquals( + new long[0], + LongStream.of(1L, 2L, 3L, 4L, 3L).filter(a -> false).toArray() + ); + // two results + assertEquals( + new long[] {2L, 4L}, + LongStream.of(1L, 2L, 3L, 4L, 3L).filter(a -> a % 2 == 0).toArray() + ); + // all + assertEquals( + new long[] {1L, 2L, 3L, 4L, 3L}, + LongStream.of(1L, 2L, 3L, 4L, 3L).filter(a -> true).toArray() + ); + } + + public void testMap() { + // unconsumed stream never runs map + int[] data = {0}; + LongStream.of(1L, 2L, 3L).map(i -> data[0]++); + assertEquals(0, data[0]); + + assertEquals( + new long[] {2L, 4L, 6L}, + LongStream.of(1L, 2L, 3L).map(i -> i * 2).toArray() + ); + } + + public void testPeek() { + // unconsumed stream never peeks + boolean[] data = {false}; + LongStream.of(1L, 2L, 3L).peek(i -> data[0] |= true); + assertFalse(data[0]); + + // make sure we saw it all in order + long[] items = new long[] {1L, 2L, 3L}; + List<Long> peeked = new ArrayList<>(); + LongStream.of(items).peek(peeked::add).forEach(item -> { + // do nothing, just run + }); + assertEquals(items.length, peeked.size()); + for (int i = 0; i < items.length; i++) { + assertEquals(items[i], (long) peeked.get(i)); + } + } + + // same impl, no parallel in browser + public void testFindFirstOrAny() { + OptionalLong any = LongStream.of(1L, 2L).findAny(); + assertTrue(any.isPresent()); + assertEquals(1L, any.getAsLong()); + } + + public void testAnyMatch() { + // all + assertTrue(LongStream.of(1L, 2L).anyMatch(s -> true)); + + // some + assertTrue(LongStream.of(1L, 2L).anyMatch(s -> s == 1L)); + + // none + assertFalse(LongStream.of(1L, 2L).anyMatch(s -> false)); + } + + public void testAllMatch() { + // all + assertTrue(LongStream.of(1L, 2L).allMatch(s -> true)); + + // some + assertFalse(LongStream.of(1L, 2L).allMatch(s -> s == 1L)); + + // none + assertFalse(LongStream.of(1L, 2L).allMatch(s -> false)); + } + + public void testNoneMatch() { + // all + assertFalse(LongStream.of(1L, 2L).noneMatch(s -> true)); + + // some + assertFalse(LongStream.of(1L, 2L).noneMatch(s -> s == 1L)); + + // none + assertTrue(LongStream.of(1L, 2L).noneMatch(s -> false)); + } + + public void testFlatMap() { + assertEquals(0L, LongStream.empty().flatMap(value -> LongStream.of(1L)).count()); + assertEquals(0L, LongStream.of(1L).flatMap(value -> LongStream.empty()).count()); + assertEquals(0L, LongStream.of(1L).flatMap(value -> LongStream.of()).count()); + assertEquals(0L, LongStream.of().flatMap(value -> LongStream.of(1L)).count()); + assertEquals(1L, LongStream.of(1L).flatMap(value -> LongStream.of(1L)).count()); + + LongStream values = LongStream.of(1L, 2L, 3L); + + assertEquals( + new long[] {1L, 2L, 2L, 4L, 3L, 6L}, + values.flatMap(i -> LongStream.of(i, i * 2)).toArray() + ); + } + + public void testMapToOthers() { + Supplier<LongStream> s = () -> LongStream.of(1, 2, 10); + + assertEquals( + new String[] {"1", "2", "10"}, + s.get().mapToObj(String::valueOf).toArray(String[]::new) + ); + + assertEquals( + new int[] {1, 2, 10}, + s.get().mapToInt(i -> (int) i).toArray() + ); + + assertEquals( + new double[] {1d, 2d, 10d}, + s.get().mapToDouble(i -> (double) i).toArray() + ); + } + + public void testDistinct() { + long[] distinct = LongStream.of(1L, 2L, 3L, 2L).distinct().toArray(); + assertEquals(3, distinct.length); + assertEquals(1L + 2L + 3L, distinct[0] + distinct[1] + distinct[2]); + } + + public void testSorted() { + long[] sorted = LongStream.of(3L, 1L, 2L).sorted().toArray(); + assertEquals(new long[] {1L, 2L, 3L}, sorted); + } + + public void testMinMax() { + Supplier<LongStream> stream = () -> LongStream.of(2L, 3L, 4L, 1L); + + assertEquals(1L, stream.get().min().orElse(0)); + assertEquals(4L, stream.get().max().orElse(0)); + + assertFalse(stream.get().filter(a -> false).max().isPresent()); + assertFalse(stream.get().filter(a -> false).min().isPresent()); + } + + public void testCountLimitSkip() { + Supplier<LongStream> stream = () -> LongStream.of(1L, 2L, 3L, 4L); + + assertEquals(4L, stream.get().count()); + + assertEquals(4L, stream.get().limit(4).count()); + assertEquals(4L, stream.get().limit(5).count()); + assertEquals(3L, stream.get().limit(3).count()); + + assertEquals(3L, stream.get().skip(1).limit(3).count()); + + assertEquals(2L, stream.get().limit(3).skip(1).count()); + + assertEquals(1L, stream.get().skip(3).count()); + + assertEquals(new long[] {3L, 4L}, stream.get().skip(2).limit(3).toArray()); + assertEquals(new long[] {3L}, stream.get().skip(2).limit(1).toArray()); + + assertEquals(new long[] {4L}, stream.get().skip(3).toArray()); + assertEquals(new long[] {}, stream.get().skip(5).toArray()); + + assertEquals(new long[] {1L, 2L}, stream.get().limit(2).toArray()); + + assertEquals(new long[] {2L}, stream.get().limit(2).skip(1).toArray()); + } + + public void testBoxed() { + Supplier<LongStream> stream = () -> LongStream.of(1L, 2L); + Stream<Long> expected = stream.get().mapToObj(Long::valueOf); + assertEquals(expected.toArray(), stream.get().boxed().toArray()); + } + + public void testAsOtherPrimitive() { + Supplier<LongStream> stream = () -> LongStream.of(1L, 2L); + DoubleStream actualDoubleStream = stream.get().asDoubleStream(); + assertEquals(new double[]{1, 2}, actualDoubleStream.toArray()); + } + + public void testSummaryStats() { + Supplier<LongStream> stream = () -> LongStream.of(1L, 2L, 3L); + LongSummaryStatistics summaryStats = stream.get().summaryStatistics(); + assertEquals(3L, summaryStats.getCount()); + assertEquals(1L, summaryStats.getMin()); + assertEquals(2L, summaryStats.getAverage(), 0d); + assertEquals(3L, summaryStats.getMax()); + assertEquals(6L, summaryStats.getSum()); + + summaryStats.accept(6L); + assertEquals(4L, summaryStats.getCount()); + assertEquals(1L, summaryStats.getMin()); + assertEquals(3L, summaryStats.getAverage(), 0d); + assertEquals(6L, summaryStats.getMax()); + assertEquals(12L, summaryStats.getSum()); + + LongSummaryStatistics combinedSumStats = stream.get().summaryStatistics(); + combinedSumStats.combine(LongStream.of(4L, 5L, 6L, 0L).summaryStatistics()); + assertEquals(7L, combinedSumStats.getCount()); + assertEquals(0L, combinedSumStats.getMin()); + assertEquals(3L, combinedSumStats.getAverage(), 0d); + assertEquals(6L, combinedSumStats.getMax()); + assertEquals(21L, combinedSumStats.getSum()); + } + + public void testAverage() { + assertFalse(LongStream.empty().average().isPresent()); + assertEquals(2.0d, LongStream.of(1L, 2L, 3L).average().getAsDouble(), 0d); + assertEquals(0d, LongStream.of(1L, 2L, -3L).average().getAsDouble(), 0d); + assertEquals(-2.0d, LongStream.of(-1L, -2L, -3L).average().getAsDouble(), 0d); + } + + public void testSum() { + assertEquals(6L, LongStream.of(1L, 2L, 3L).sum()); + assertEquals(0L, LongStream.of(1L, 2L, -3L).sum()); + assertEquals(-6L, LongStream.of(-1L, -2L, -3L).sum()); + } + + public void testCollect() { + String val = LongStream.of(1L, 2L, 3L, 4L, 5L).collect(StringBuilder::new, + // TODO switch to a lambda reference once #9340 is fixed + (stringBuilder, lng) -> stringBuilder.append(lng), + StringBuilder::append).toString(); + + assertEquals("12345", val); + } + + public void testForEach() { + List<Long> vals = new ArrayList<>(); + LongStream.of(1L, 2L, 3L, 4L, 5L).forEach(vals::add); + assertEquals(5, vals.size()); + assertEquals(new Long[] {1L, 2L, 3L, 4L, 5L}, vals.toArray(new Long[vals.size()])); + } + + public void testIterator() { + List<Long> vals = new ArrayList<>(); + Iterator<Long> iterator = LongStream.of(1L, 2L, 3L, 4L, 5L).iterator(); + while (iterator.hasNext()) { + vals.add(iterator.next()); + } + assertEquals(5, vals.size()); + assertEquals(new Long[] {1L, 2L, 3L, 4L, 5L}, vals.toArray(new Long[vals.size()])); + } + + public void testSpliterator() { + Spliterator<Long> spliterator = LongStream.of(1L, 2L, 3L, 4L, 5L).spliterator(); + assertEquals(5, spliterator.estimateSize()); + assertEquals(5, spliterator.getExactSizeIfKnown()); + + List<Long> vals = new ArrayList<>(); + while (spliterator.tryAdvance(vals::add)) { + // work is all done in the condition + } + + assertEquals(5, vals.size()); + assertEquals(new Long[] {1L, 2L, 3L, 4L, 5L}, vals.toArray(new Long[vals.size()])); + } +}
diff --git a/user/test/com/google/gwt/emultest/java8/util/stream/StreamSupportTest.java b/user/test/com/google/gwt/emultest/java8/util/stream/StreamSupportTest.java new file mode 100644 index 0000000..0ff6331 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java8/util/stream/StreamSupportTest.java
@@ -0,0 +1,109 @@ +/* + * Copyright 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.gwt.emultest.java8.util.stream; + +import static java.util.stream.StreamSupport.doubleStream; +import static java.util.stream.StreamSupport.intStream; +import static java.util.stream.StreamSupport.longStream; +import static java.util.stream.StreamSupport.stream; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.Spliterators; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +/** + * Tests {@link java.util.stream.StreamSupport}. + */ +public class StreamSupportTest extends EmulTestBase { + + public void testDoubleStream() { + DoubleStream doubles = doubleStream( + Spliterators.spliterator(new double[] {1d, 2d, 3d, 4d}, 0), + false + ); + + assertNotNull(doubles); + assertEquals(new double[]{1d, 2d, 3d}, doubles.limit(3).toArray()); + + doubles = doubleStream(() -> Spliterators.spliterator(new double[] {1d, 2d, 3d, 4d}, 0), 0, + false); + + assertNotNull(doubles); + assertEquals( + new double[] {2d, 3d}, + doubles.skip(1).limit(2).toArray() + ); + } + + public void testIntStream() { + IntStream ints = intStream( + Spliterators.spliterator(new int[] {1, 2, 3, 4}, 0), + false + ); + + assertNotNull(ints); + assertEquals(new int[] {1, 2, 3}, ints.limit(3).toArray()); + + ints = intStream(() -> Spliterators.spliterator(new int[] {1, 2, 3, 4}, 0), 0, false); + + assertNotNull(ints); + assertEquals( + new int[] {2, 3}, + ints.skip(1).limit(2).toArray() + ); + } + + public void testLongStream() { + LongStream longs = longStream( + Spliterators.spliterator(new long[] {1L, 2L, 3L, 4L}, 0), + false + ); + + assertNotNull(longs); + assertEquals(new long[] {1L, 2L, 3L}, longs.limit(3).toArray()); + + longs = longStream(() -> Spliterators.spliterator(new long[] {1L, 2L, 3L, 4L}, 0), 0, false); + + assertNotNull(longs); + assertEquals( + new long[] {2L, 3L}, + longs.skip(1).limit(2).toArray() + ); + } + + public void testStream() { + Stream<String> strings = stream( + Spliterators.spliterator(new String[] {"a", "b", "c", "d"}, 0), + false + ); + + assertNotNull(strings); + assertEquals(new String[] {"a", "b", "c"}, strings.limit(3).toArray()); + + strings = stream(() -> Spliterators.spliterator(new String[] {"a", "b", "c", "d"}, 0), 0, false); + + assertNotNull(strings); + assertEquals( + new String[] {"b", "c"}, + strings.skip(1).limit(2).toArray() + ); + } +}
diff --git a/user/test/com/google/gwt/emultest/java8/util/stream/StreamTest.java b/user/test/com/google/gwt/emultest/java8/util/stream/StreamTest.java new file mode 100644 index 0000000..d46bfaf --- /dev/null +++ b/user/test/com/google/gwt/emultest/java8/util/stream/StreamTest.java
@@ -0,0 +1,627 @@ +/* + * Copyright 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.gwt.emultest.java8.util.stream; + +import static java.util.Arrays.asList; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.Spliterator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +/** + * Tests {@link Stream}. + */ +public class StreamTest extends EmulTestBase { + + public void testEmptyStream() { + Stream<Object> empty = Stream.empty(); + assertEquals(0, empty.count()); + try { + empty.count(); + fail("second terminal operation should have thrown IllegalStateEx"); + } catch (IllegalStateException expected) { + // expected + } + + assertEquals(0, Stream.empty().limit(2).collect(Collectors.toList()).size()); + assertEquals(0, Stream.empty().count()); + assertEquals(0, Stream.empty().limit(2).count()); + + assertFalse(Stream.empty().findFirst().isPresent()); + assertFalse(Stream.empty().findAny().isPresent()); + assertFalse(Stream.<String>empty().max(Comparator.naturalOrder()).isPresent()); + assertFalse(Stream.<String>empty().min(Comparator.naturalOrder()).isPresent()); + assertTrue(Stream.empty().allMatch(item -> true)); + assertFalse(Stream.empty().anyMatch(item -> true)); + assertTrue(Stream.empty().noneMatch(item -> false)); + assertFalse(Stream.empty().iterator().hasNext()); + assertFalse(Stream.empty().spliterator().tryAdvance(a -> fail("should not advance"))); + Stream.empty().spliterator().forEachRemaining(a -> fail("should not advance")); + assertEquals(new Object[0], Stream.empty().toArray()); + assertEquals(new Object[0], Stream.empty().toArray(Object[]::new)); + } + + public void testStreamOfOne() { + Supplier<Stream<String>> one = () -> Stream.of(""); + assertEquals(Collections.singletonList(""), one.get().collect(Collectors.toList())); + assertEquals(1L, one.get().count()); + assertEquals("", one.get().findFirst().get()); + assertEquals("", one.get().findAny().get()); + } + + public void testBuilder() { + Supplier<Stream<String>> s = () -> Stream.<String>builder() + .add("1") + .add("3") + .add("2") + .build(); + + Optional<String> max = s.get().filter(str -> !str.equals("3")).max(Comparator.naturalOrder()); + assertTrue(max.isPresent()); + assertEquals("2", max.get()); + + max = s.get().max(Comparator.reverseOrder()); + assertTrue(max.isPresent()); + assertEquals("1", max.get()); + + Stream.Builder<Object> builder = Stream.builder(); + Stream<Object> built = builder.build(); + assertEquals(0, built.count()); + try { + builder.build(); + fail("build() after build() should fail"); + } catch (IllegalStateException expected) { + // expected + } + try { + builder.add("asdf"); + fail("add() after build() should fail"); + } catch (IllegalStateException expected) { + // expected + } + } + + public void testConcat() { + Supplier<Stream<String>> adbc = () -> Stream.concat(Stream.of("a", "d"), Stream.of("b", "c")); + + assertEquals(new String[]{"a", "d", "b", "c"}, adbc.get().toArray(String[]::new)); + assertEquals(new String[]{"a", "b", "c", "d"}, adbc.get().sorted().toArray(String[]::new)); + + List<String> closed = new ArrayList<>(); + Stream<String> first = Stream.of("first").onClose(() -> closed.add("first")); + Stream<String> second = Stream.of("second").onClose(() -> closed.add("second")); + + Stream<String> concat = Stream.concat(first, second); + + // read everything, make sure we saw it all and didn't close automatically + String collectedAll = concat.collect(Collectors.joining()); + assertEquals("firstsecond", collectedAll); + assertEquals(0, closed.size()); + + concat.close(); + assertEquals(Arrays.asList("first", "second"), closed); + } + + public void testIterate() { + assertEquals( + new Integer[]{10, 11, 12, 13, 14}, + Stream.iterate(0, i -> i + 1) + .skip(10) + .limit(5) + .toArray(Integer[]::new) + ); + } + + public void testGenerate() { + // infinite, but if you limit it is already too short to skip much + assertEquals(new Integer[]{}, Stream.generate(makeGenerator()) + .limit(4) + .skip(5) + .toArray(Integer[]::new)); + + assertEquals(new Integer[]{10, 11, 12, 13, 14}, Stream.generate(makeGenerator()) + .skip(10) + .limit(5) + .toArray(Integer[]::new)); + } + + private Supplier<Integer> makeGenerator() { + return new Supplier<Integer>() { + int next = 0; + + @Override + public Integer get() { + return next++; + } + }; + } + + public void testSpliterator() { + final String[] values = new String[] {"a", "b", "c"}; + + Spliterator<String> spliterator = Stream.of(values).spliterator(); + assertEquals(3, spliterator.estimateSize()); + assertEquals(3, spliterator.getExactSizeIfKnown()); + + List<String> actualValues = new ArrayList<>(); + while (spliterator.tryAdvance(actualValues::add)) { + // work is all done in the condition + } + + assertEquals(asList(values), actualValues); + } + + public void testIterator() { + final String[] values = new String[] {"a", "b", "c"}; + + List<String> actualValues = new ArrayList<String>(); + Iterator<String> iterator = Stream.of(values).iterator(); + while (iterator.hasNext()) { + actualValues.add(iterator.next()); + } + assertEquals(asList(values), actualValues); + } + + public void testForEach() { + final String[] values = new String[] {"a", "b", "c"}; + + List<String> actualValues = new ArrayList<>(); + Stream.of(values).forEach(actualValues::add); + assertEquals(asList(values), actualValues); + } + + // toArray + public void testToArray() { + assertEquals( + new Object[]{"a", "b"}, + asList("a", "b").stream().toArray() + ); + assertEquals( + new String[]{"a", "b"}, + asList("a", "b").stream().toArray(String[]::new) + ); + } + + // reduce + public void testReduce() { + String reduced = Stream.of("a", "b", "c").reduce("", String::concat); + assertEquals("abc", reduced); + + reduced = Stream.<String>of().reduce("initial", String::concat); + assertEquals("initial", reduced); + + Optional<String> maybe = Stream.of("a", "b", "c").reduce(String::concat); + assertTrue(maybe.isPresent()); + assertEquals("abc", maybe.get()); + maybe = Stream.<String>of().reduce(String::concat); + assertFalse(maybe.isPresent()); + + reduced = Stream.of("a", "b", "c").reduce("", String::concat, String::concat); + assertEquals("abc", reduced); + } + + public void testCollect() { + final String[] values = new String[] {"a", "b", "c"}; + + String collectedString = Stream.of(values).collect(StringBuilder::new, + StringBuilder::append, + StringBuilder::append).toString(); + assertEquals("abc", collectedString); + + List<String> collectedList = Stream.of(values).collect(Collectors.toList()); + assertEquals(asList(values), collectedList); + } + + public void testFilter() { + // unconsumed stream never runs filter + boolean[] data = {false}; + Stream.of(1, 2, 3).filter(i -> data[0] |= true); + assertFalse(data[0]); + + // one result + assertEquals( + Collections.singletonList("a"), + Stream.of("a", "b", "c", "d", "c").filter(a -> a.equals("a")).collect(Collectors.toList()) + ); + // zero results + assertEquals( + Collections.emptyList(), + Stream.of("a", "b", "c", "d", "c").filter(a -> false).collect(Collectors.toList()) + ); + // two results + assertEquals( + asList("c3", "c5"), + Stream.of("a1", "b2", "c3", "d4", "c5") + .filter(a -> a.startsWith("c")) + .collect(Collectors.toList()) + ); + // all + assertEquals( + asList("a", "b", "c", "d", "c"), + Stream.of("a", "b", "c", "d", "c").filter(a -> true).collect(Collectors.toList()) + ); + } + + public void testMap() { + // unconsumed stream never runs map + boolean[] data = {false}; + Stream.of(1, 2, 3).map(i -> data[0] |= true); + assertFalse(data[0]); + + assertEquals( + asList("#1", "#2", "#3"), + Stream.of(1, 2, 3).map(i -> "#" + i).collect(Collectors.toList()) + ); + } + + public void testPeek() { + // unconsumed stream never peeks + boolean[] data = {false}; + Stream.of(1, 2, 3).peek(i -> data[0] |= true); + assertFalse(data[0]); + + // make sure we saw it all in order + List<String> items = asList("a", "b", "c"); + List<String> peeked = new ArrayList<>(); + items.stream().peek(peeked::add).forEach(item -> { + // deliberately do nothing, just run + }); + assertEquals(items, peeked); + } + + // same impl, no parallel in browser + public void testFindFirstOrAny() { + Optional<String> any = Stream.of("a", "b").findAny(); + assertTrue(any.isPresent()); + assertEquals("a", any.get()); + } + + public void testAnyMatch() { + // all + assertTrue(Stream.of("a", "b").anyMatch(s -> true)); + + // some + assertTrue(Stream.of("a", "b").anyMatch(s -> s.equals("a"))); + + // none + assertFalse(Stream.of("a", "b").anyMatch(s -> false)); + } + + public void testAllMatch() { + // all + assertTrue(Stream.of("a", "b").allMatch(s -> true)); + + // some + assertFalse(Stream.of("a", "b").allMatch(s -> s.equals("a"))); + + // none + assertFalse(Stream.of("a", "b").allMatch(s -> false)); + } + + public void testNoneMatch() { + // all + assertFalse(Stream.of("a", "b").noneMatch(s -> true)); + + // some + assertFalse(Stream.of("a", "b").noneMatch(s -> s.equals("a"))); + + // none + assertTrue(Stream.of("a", "b").noneMatch(s -> false)); + } + + public void testFlatMap() { + assertEquals(0, Stream.<Stream<String>>empty().flatMap(Function.identity()).count()); + assertEquals(0, Stream.of(Stream.<String>empty()).flatMap(Function.identity()).count()); + assertEquals(0, Stream.of(Stream.of()).flatMap(Function.identity()).count()); + assertEquals(1, Stream.of(Stream.of("")).flatMap(Function.identity()).count()); + + Stream<Stream<String>> strings = Stream.of(Stream.of("a", "b"), Stream.empty(), Stream.of("c")); + + assertEquals( + asList("a", "b", "c"), + strings.flatMap(Function.identity()).collect(Collectors.toList()) + ); + } + public void testMapToPrimitives() { + Supplier<Stream<String>> s = () -> Stream.of("1", "2", "10"); + + assertEquals( + new int[]{1, 2, 10}, + s.get().mapToInt(Integer::parseInt).toArray() + ); + + assertEquals( + new long[]{1, 2, 10}, + s.get().mapToLong(Long::parseLong).toArray() + ); + + assertEquals( + new double[]{1, 2, 10}, + s.get().mapToDouble(Double::parseDouble).toArray() + ); + } + + public void testFlatMapToPrimitives() { + assertEquals(0, Stream.<IntStream>empty().flatMapToInt(Function.identity()).count()); + assertEquals(0, Stream.of(IntStream.empty()).flatMapToInt(Function.identity()).count()); + assertEquals(0, Stream.of(IntStream.of()).flatMapToInt(Function.identity()).count()); + assertEquals(1, Stream.of(IntStream.of(0)).flatMapToInt(Function.identity()).count()); + + Stream<IntStream> intStreams = Stream.of( + IntStream.of(1, 2), + IntStream.empty(), + IntStream.of(5) + ); + assertEquals( + new int[]{1, 2, 5}, + intStreams.flatMapToInt(Function.identity()).toArray() + ); + + Stream<LongStream> longStreams = Stream.of( + LongStream.of(1, 2), + LongStream.empty(), + LongStream.of(5) + ); + assertEquals( + new long[]{1, 2, 5}, + longStreams.flatMapToLong(Function.identity()).toArray() + ); + + Stream<DoubleStream> doubleStreams = Stream.of( + DoubleStream.of(1, 2), + DoubleStream.empty(), + DoubleStream.of(5) + ); + assertEquals( + new double[]{1, 2, 5}, + doubleStreams.flatMapToDouble(Function.identity()).toArray() + ); + } + + public void testDistinct() { + List<String> distinct = asList("a", "b", "c", "b").stream() + .distinct() + .collect(Collectors.toList()); + assertEquals(3, distinct.size()); + assertTrue(distinct.contains("a")); + assertTrue(distinct.contains("b")); + assertTrue(distinct.contains("c")); + } + + public void testSorted() { + List<String> sorted = asList("c", "a", "b").stream().sorted().collect(Collectors.toList()); + List<String> reversed = asList("c", "a", "b").stream() + .sorted(Comparator.reverseOrder()) + .collect(Collectors.toList()); + + assertEquals(asList("a", "b", "c"), sorted); + assertEquals(asList("c", "b", "a"), reversed); + } + + public void testMinMax() { + Supplier<Stream<String>> stream = () -> Stream.of("b", "c", "d", "a"); + + assertEquals("a", stream.get().min(Comparator.naturalOrder()).orElse(null)); + assertEquals("d", stream.get().min(Comparator.reverseOrder()).orElse(null)); + assertEquals("a", stream.get().max(Comparator.reverseOrder()).orElse(null)); + assertEquals("d", stream.get().max(Comparator.naturalOrder()).orElse(null)); + + assertFalse(stream.get().filter(a -> false).max(Comparator.naturalOrder()).isPresent()); + assertFalse(stream.get().filter(a -> false).min(Comparator.naturalOrder()).isPresent()); + } + + public void testCountLimitSkip() { + Supplier<Stream<String>> stream = () -> asList("a", "b", "c", "d").stream(); + + assertEquals(4, stream.get().count()); + + assertEquals(4, stream.get().limit(4).count()); + assertEquals(4, stream.get().limit(5).count()); + assertEquals(3, stream.get().limit(3).count()); + + assertEquals(3, stream.get().skip(1).limit(3).count()); + + assertEquals(2, stream.get().limit(3).skip(1).count()); + + assertEquals(1, stream.get().skip(3).count()); + + assertEquals(asList("c", "d"), stream.get().skip(2).limit(3).collect(Collectors.toList())); + assertEquals(Collections.singletonList("c"), stream.get() + .skip(2) + .limit(1) + .collect(Collectors.toList())); + + assertEquals(Collections.singletonList("d"), stream.get() + .skip(3) + .collect(Collectors.toList())); + assertEquals(Collections.emptyList(), stream.get() + .skip(5) + .collect(Collectors.toList())); + + assertEquals(asList("a", "b"), stream.get() + .limit(2) + .collect(Collectors.toList())); + + assertEquals(Collections.singletonList("b"), stream.get() + .limit(2) + .skip(1) + .collect(Collectors.toList())); + } + + // This frustrating test was written first on the JVM stream to discover the basic behavior before + // trying to implement it in GWT. As far as I can tell, none of this is clearly described in + // javadoc. Also note that it is *not* required to use the returned stream from calling onClose + public void testCloseQuirks() { + // all subclasses use the same close()/onClose(...) impl, just test once with Stream.empty() + + Stream<Object> s = Stream.of(1); + s.close(); + // allow multiple close + s.close(); + + // Add a handler, close, and attempt to re-close - handler only runs the one time + int[] calledCount = {0}; + s = Stream.empty(); + s.onClose(() -> calledCount[0]++); + // shouldn't have been called yet + assertEquals(0, calledCount[0]); + s.close(); + // called once + assertEquals(1, calledCount[0]); + s.close(); + // not called again on subsequent closes + assertEquals(1, calledCount[0]); + + // Add a handler after close, and re-close, the handler will only go off after the _second_ + // close + calledCount[0] = 0; + s = Stream.of(1); + s.close(); + s = s.onClose(() -> calledCount[0]++); + // shouldn't have been called yet + assertEquals(0, calledCount[0]); + s.close(); + // frustratingly, the JVM apparently permits each handler when added to let the stream be closed + // _again_ + assertEquals(1, calledCount[0]); + + // Adding yet another runnable and closing again demonstrates this - only the new one is run, + // the old ones are not + s = s.onClose(() -> calledCount[0]++); + s.close(); + assertEquals(2, calledCount[0]); + + // Add two handlers, ensure both are called, and neither called the second time the stream is + // closed + calledCount[0] = 0; + s = Stream.empty(); + s = s.onClose(() -> calledCount[0]++); + s = s.onClose(() -> calledCount[0]++); + s.close(); + assertEquals(2, calledCount[0]); + s.close(); + assertEquals(2, calledCount[0]); + } + + public void testClose() { + // terminate stream before closing, confirm that handler is called + Stream<Object> s = Stream.of("a", "b", "c"); + int[] calledCount = {0}; + s = s.onClose(() -> calledCount[0]++); + + long count = s.count(); + assertEquals(3, count); + assertEquals(0, calledCount[0]); + + s.close(); + assertEquals(1, calledCount[0]); + + // terminate stream after closing, confirm that handler is called, and terminating fails + s = Stream.of("a", "b", "c"); + calledCount[0] = 0; + s = s.onClose(() -> calledCount[0]++); + + s.close(); + assertEquals(1, calledCount[0]); + + try { + s.count(); + fail("Expected IllegalStateException"); + } catch (IllegalStateException expected) { + // expected + } + assertEquals(1, calledCount[0]); + } + + public void testCloseException() { + // Try a single exception, confirm we catch it + Stream<Object> s = Stream.of(1, 2, 3); + + RuntimeException a = new RuntimeException("a"); + s.onClose(() -> { + throw a; + }); + try { + s.close(); + fail("RuntimeException expected"); + } catch (RuntimeException expected) { + assertSame(a, expected); + assertEquals(0, expected.getSuppressed().length); + } + + // Throw an exception in two of the three handlers, confirm both arrive and the third was called + // correctly + s = Stream.of(1, 2, 3); + + RuntimeException a2 = new RuntimeException("a"); + IllegalStateException b = new IllegalStateException("b"); + int[] calledCount = {0}; + s + .onClose(() -> { + throw a2; + }) + .onClose(() -> { + throw b; + }) + .onClose(() -> calledCount[0]++); + + try { + s.close(); + fail("RuntimeException expected"); + } catch (RuntimeException expected) { + assertSame(a2, expected); + assertEquals(1, expected.getSuppressed().length); + Throwable firstSuppressed = expected.getSuppressed()[0]; + assertSame(b, firstSuppressed); + } + assertEquals(1, calledCount[0]); + + // Throw the same exception instance twice, ensure it only arrives once + s = Stream.of(1, 2, 3); + + RuntimeException t = new RuntimeException("a"); + s + .onClose(() -> { + throw t; + }) + .onClose(() -> { + throw t; + }); + + try { + s.close(); + fail("RuntimeException expected"); + } catch (RuntimeException expected) { + assertSame(t, expected); + assertEquals(0, expected.getSuppressed().length); + } + } +} \ No newline at end of file