add null-safe stream() extension methods for Object, Optional, Iterable,
Iterator, Spliterator, Enumeration and Optional[Int|Long|Double]
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v8/PluginDefaultGroovyMethods.java b/src/main/java/org/codehaus/groovy/vmplugin/v8/PluginDefaultGroovyMethods.java
index 2547bc1..d81df24 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/PluginDefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/PluginDefaultGroovyMethods.java
@@ -21,8 +21,11 @@
import groovy.lang.Closure;
import groovy.transform.stc.ClosureParams;
import groovy.transform.stc.FirstParam;
+import org.codehaus.groovy.runtime.NullObject;
import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -36,6 +39,7 @@
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
import java.util.function.DoubleFunction;
import java.util.function.DoublePredicate;
import java.util.function.IntFunction;
@@ -47,7 +51,9 @@
import java.util.function.ToLongFunction;
import java.util.stream.BaseStream;
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;
import java.util.stream.StreamSupport;
@@ -201,6 +207,7 @@
* assert !OptionalInt.empty().mapToObj(x -> new Object()).isPresent()
* assert OptionalInt.of(1234).mapToObj(x -> new Object()).isPresent()
* assert !OptionalInt.of(1234).mapToObj(x -> null).isPresent()
+ * assert OptionalInt.of(1234).mapToObj(Integer::toString).get() == '1234'
* </pre>
*
* @since 3.0.0
@@ -219,6 +226,7 @@
* assert !OptionalLong.empty().mapToObj(x -> new Object()).isPresent()
* assert OptionalLong.of(123L).mapToObj(x -> new Object()).isPresent()
* assert !OptionalLong.of(123L).mapToObj(x -> null).isPresent()
+ * assert OptionalLong.of(1234L).mapToObj(Long::toString).get() == '1234'
* </pre>
*
* @since 3.0.0
@@ -237,6 +245,7 @@
* assert !OptionalDouble.empty().mapToObj(x -> new Object()).isPresent()
* assert OptionalDouble.of(Math.PI).mapToObj(x -> new Object()).isPresent()
* assert !OptionalDouble.of(Math.PI).mapToObj(x -> null).isPresent()
+ * assert OptionalDouble.of(Math.PI).mapToObj(Double::toString).get().startsWith('3.14')
* </pre>
*
* @since 3.0.0
@@ -382,56 +391,78 @@
/**
* Accumulates the elements of stream into a new List.
- * @param stream the Stream
+ * @param self the Stream
* @param <T> the type of element
* @return a new {@code java.util.List} instance
*
* @since 2.5.0
*/
- public static <T> List<T> toList(final Stream<T> stream) {
- return stream.collect(Collectors.<T>toList());
+ public static <T> List<T> toList(final Stream<T> self) {
+ return self.collect(Collectors.<T>toList());
}
/**
* Accumulates the elements of stream into a new Set.
- * @param stream the Stream
+ * @param self the Stream
* @param <T> the type of element
* @return a new {@code java.util.Set} instance
*
* @since 2.5.0
*/
- public static <T> Set<T> toSet(final Stream<T> stream) {
- return stream.collect(Collectors.<T>toSet());
+ public static <T> Set<T> toSet(final Stream<T> self) {
+ return self.collect(Collectors.<T>toSet());
}
/**
* Accumulates the elements of stream into a new List.
- * @param stream the {@code java.util.stream.BaseStream}
+ * @param self the {@code java.util.stream.BaseStream}
* @param <T> the type of element
* @return a new {@code java.util.List} instance
*
* @since 2.5.0
*/
- public static <T> List<T> toList(final BaseStream<T, ? extends BaseStream> stream) {
- return StreamSupport.stream(
- Spliterators.spliteratorUnknownSize(stream.iterator(), Spliterator.ORDERED),
- false
- ).collect(Collectors.<T>toList());
+ public static <T> List<T> toList(final BaseStream<T, ? extends BaseStream> self) {
+ return stream(self.iterator()).collect(Collectors.<T>toList());
}
/**
* Accumulates the elements of stream into a new Set.
- * @param stream the {@code java.util.stream.BaseStream}
+ * @param self the {@code java.util.stream.BaseStream}
* @param <T> the type of element
* @return a new {@code java.util.Set} instance
*
* @since 2.5.0
*/
- public static <T> Set<T> toSet(final BaseStream<T, ? extends BaseStream> stream) {
- return StreamSupport.stream(
- Spliterators.spliteratorUnknownSize(stream.iterator(), Spliterator.ORDERED),
- false
- ).collect(Collectors.<T>toSet());
+ public static <T> Set<T> toSet(final BaseStream<T, ? extends BaseStream> self) {
+ return stream(self.iterator()).collect(Collectors.<T>toSet());
+ }
+
+ /**
+ * Returns an empty sequential {@link Stream}.
+ * <pre class="groovyTestCase">
+ * def item = null
+ * assert item.stream().toList() == []
+ * assert !item.stream().findFirst().isPresent()
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static <T> Stream<T> stream(final NullObject self) {
+ return Stream.empty();
+ }
+
+ /**
+ * Returns a sequential {@link Stream} containing a single element.
+ * <pre class="groovyTestCase">
+ * def item = 'string'
+ * assert item.stream().toList() == ['string']
+ * assert item.stream().findFirst().isPresent()
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static <T> Stream<T> stream(final T self) {
+ return Stream.of(self);
}
/**
@@ -458,7 +489,7 @@
* @since 2.5.0
*/
public static Stream<Integer> stream(final int[] self) {
- return IntStream.range(0, self.length).mapToObj(i -> self[i]);
+ return Arrays.stream(self).mapToObj(Integer::valueOf);
}
/**
@@ -471,7 +502,7 @@
* @since 2.5.0
*/
public static Stream<Long> stream(final long[] self) {
- return IntStream.range(0, self.length).mapToObj(i -> self[i]);
+ return Arrays.stream(self).mapToObj(Long::valueOf);
}
/**
@@ -484,7 +515,7 @@
* @since 2.5.0
*/
public static Stream<Double> stream(final double[] self) {
- return IntStream.range(0, self.length).mapToObj(i -> self[i]);
+ return Arrays.stream(self).mapToObj(Double::valueOf);
}
/**
@@ -553,6 +584,82 @@
}
/**
+ * Returns a sequential {@link Stream} with the specified element(s) as its
+ * source.
+ * <pre class="groovyTestCase">
+ * def tokens = new StringTokenizer('one two')
+ * assert tokens.stream().toList() == ['one', 'two']
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static <T> Stream<T> stream(final Enumeration<T> self) {
+ return stream(new Spliterators.AbstractSpliterator<T>(Long.MAX_VALUE, Spliterator.ORDERED) {
+ @Override
+ public void forEachRemaining(final Consumer<? super T> action) {
+ while (self.hasMoreElements()) {
+ action.accept(self.nextElement());
+ }
+ }
+ @Override
+ public boolean tryAdvance(final Consumer<? super T> action) {
+ if (self.hasMoreElements()) {
+ action.accept(self.nextElement());
+ return true;
+ }
+ return false;
+ }
+ });
+ }
+
+ /**
+ * Returns a sequential {@link Stream} with the specified element(s) as its
+ * source.
+ * <pre class="groovyTestCase">
+ * class Items implements Iterable<String> {
+ * Iterator<String> iterator() {
+ * ['one', 'two'].iterator()
+ * }
+ * }
+ * def items = new Items()
+ * assert items.stream().toList() == ['one', 'two']
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static <T> Stream<T> stream(final Iterable<T> self) {
+ return StreamSupport.stream(self.spliterator(), false);
+ }
+
+ /**
+ * Returns a sequential {@link Stream} with the specified element(s) as its
+ * source.
+ * <pre class="groovyTestCase">
+ * [].iterator().stream().toList().isEmpty()
+ * ['one', 'two'].iterator().stream().toList() == ['one', 'two']
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static <T> Stream<T> stream(final Iterator<T> self) {
+ return stream(Spliterators.spliteratorUnknownSize(self, Spliterator.ORDERED));
+ }
+
+ /**
+ * Returns a sequential {@link Stream} with the specified element(s) as its
+ * source.
+ * <pre class="groovyTestCase">
+ * [].spliterator().stream().toList().isEmpty()
+ * ['one', 'two'].spliterator().stream().toList() == ['one', 'two']
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static <T> Stream<T> stream(final Spliterator<T> self) {
+ return StreamSupport.stream(self, false);
+ }
+
+ /**
* If a value is present in the {@link Optional}, returns a {@link Stream}
* with the value as its source or else an empty stream.
*
@@ -561,4 +668,43 @@
public static <T> Stream<T> stream(final Optional<T> self) {
return self.map(Stream::of).orElseGet(Stream::empty);
}
+
+ /**
+ * If a value is present in the {@link OptionalInt}, returns an {@link IntStream}
+ * with the value as its source or else an empty stream.
+ *
+ * @since 3.0.0
+ */
+ public static IntStream stream(final OptionalInt self) {
+ if (!self.isPresent()) {
+ return IntStream.empty();
+ }
+ return IntStream.of(self.getAsInt());
+ }
+
+ /**
+ * If a value is present in the {@link OptionalLong}, returns a {@link LongStream}
+ * with the value as its source or else an empty stream.
+ *
+ * @since 3.0.0
+ */
+ public static LongStream stream(final OptionalLong self) {
+ if (!self.isPresent()) {
+ return LongStream.empty();
+ }
+ return LongStream.of(self.getAsLong());
+ }
+
+ /**
+ * If a value is present in the {@link OptionalDouble}, returns a {@link DoubleStream}
+ * with the value as its source or else an empty stream.
+ *
+ * @since 3.0.0
+ */
+ public static DoubleStream stream(final OptionalDouble self) {
+ if (!self.isPresent()) {
+ return DoubleStream.empty();
+ }
+ return DoubleStream.of(self.getAsDouble());
+ }
}