GROOVY-9337: add extension methods for Optional, OptionalInt, et al.
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 13dd84f..2547bc1 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/PluginDefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/PluginDefaultGroovyMethods.java
@@ -26,6 +26,9 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
@@ -33,6 +36,15 @@
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.function.DoubleFunction;
+import java.util.function.DoublePredicate;
+import java.util.function.IntFunction;
+import java.util.function.IntPredicate;
+import java.util.function.LongFunction;
+import java.util.function.LongPredicate;
+import java.util.function.ToDoubleFunction;
+import java.util.function.ToIntFunction;
+import java.util.function.ToLongFunction;
import java.util.stream.BaseStream;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -52,7 +64,11 @@
}
/**
- * Coerce an Optional instance to a boolean value.
+ * Coerce an {@code Optional} instance to a {@code boolean} value.
+ * <pre class="groovyTestCase">
+ * assert !Optional.empty().asBoolean()
+ * assert Optional.of(1234).asBoolean()
+ * </pre>
*
* @param optional the Optional
* @return {@code true} if a value is present, otherwise {@code false}
@@ -64,6 +80,226 @@
}
/**
+ * If a value is present in the {@code OptionalInt}, returns the value,
+ * otherwise throws {@code NoSuchElementException}.
+ * <pre class="groovyTestCase">
+ * assert OptionalInt.of(1234).get() == 1234
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static int get(final OptionalInt self) {
+ return self.getAsInt();
+ }
+
+ /**
+ * If a value is present in the {@code OptionalLong}, returns the value,
+ * otherwise throws {@code NoSuchElementException}.
+ * <pre class="groovyTestCase">
+ * assert OptionalLong.of(1234L).get() == 1234L
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static long get(final OptionalLong self) {
+ return self.getAsLong();
+ }
+
+ /**
+ * If a value is present in the {@code OptionalDouble}, returns the value,
+ * otherwise throws {@code NoSuchElementException}.
+ * <pre class="groovyTestCase">
+ * assert OptionalDouble.of(Math.PI).get() == Math.PI
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static double get(final OptionalDouble self) {
+ return self.getAsDouble();
+ }
+
+ /**
+ * Tests given value against specified type and changes generics of result.
+ * This is equivalent to: <code>self.filter(it -> it instanceof Type).map(it -> (Type) it)</code>
+ * <pre class="groovyTestCase">
+ * assert !Optional.empty().filter(Number).isPresent()
+ * assert !Optional.of('x').filter(Number).isPresent()
+ * assert Optional.of(1234).filter(Number).isPresent()
+ * assert Optional.of(1234).filter(Number).get().equals(1234)
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static <T> Optional<T> filter(final Optional<?> self, final Class<T> type) {
+ return self.filter(type::isInstance).map(type::cast);
+ }
+
+ /**
+ * If a value is present in the {@code OptionalInt}, tests the value using
+ * the given predicate and returns the optional if the test returns true or
+ * else empty.
+ * <pre class="groovyTestCase">
+ * assert !OptionalInt.empty().filter(i -> true).isPresent()
+ * assert OptionalInt.of(1234).filter(i -> true).isPresent()
+ * assert !OptionalInt.of(1234).filter(i -> false).isPresent()
+ * assert OptionalInt.of(1234).filter(i -> true).getAsInt() == 1234
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static OptionalInt filter(final OptionalInt self, final IntPredicate test) {
+ if (!self.isPresent() || !test.test(self.getAsInt())) {
+ return OptionalInt.empty();
+ }
+ return self;
+ }
+
+ /**
+ * If a value is present in the {@code OptionalLong}, tests the value using
+ * the given predicate and returns the optional if the test returns true or
+ * else empty.
+ * <pre class="groovyTestCase">
+ * assert !OptionalLong.empty().filter(n -> true).isPresent()
+ * assert OptionalLong.of(123L).filter(n -> true).isPresent()
+ * assert !OptionalLong.of(123L).filter(n -> false).isPresent()
+ * assert OptionalLong.of(123L).filter(n -> true).getAsLong() == 123L
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static OptionalLong filter(final OptionalLong self, final LongPredicate test) {
+ if (!self.isPresent() || !test.test(self.getAsLong())) {
+ return OptionalLong.empty();
+ }
+ return self;
+ }
+
+ /**
+ * If a value is present in the {@code OptionalDouble}, tests the value using
+ * the given predicate and returns the optional if the test returns true or
+ * empty otherwise.
+ * <pre class="groovyTestCase">
+ * assert !OptionalDouble.empty().filter(n -> true).isPresent()
+ * assert OptionalDouble.of(Math.PI).filter(n -> true).isPresent()
+ * assert !OptionalDouble.of(Math.PI).filter(n -> false).isPresent()
+ * assert OptionalDouble.of(Math.PI).filter(n -> true).getAsDouble() == Math.PI
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static OptionalDouble filter(final OptionalDouble self, final DoublePredicate test) {
+ if (!self.isPresent() || !test.test(self.getAsDouble())) {
+ return OptionalDouble.empty();
+ }
+ return self;
+ }
+
+ /**
+ * If a value is present in the {@code OptionalInt}, returns an {@code Optional}
+ * consisting of the result of applying the given function to the value or else empty.
+ * <pre class="groovyTestCase">
+ * 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()
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static <T> Optional<T> mapToObj(final OptionalInt self, final IntFunction<? extends T> mapper) {
+ if (!self.isPresent()) {
+ return Optional.empty();
+ }
+ return Optional.ofNullable(mapper.apply(self.getAsInt()));
+ }
+
+ /**
+ * If a value is present in the {@code OptionalLong}, returns an {@code Optional}
+ * consisting of the result of applying the given function to the value or else empty.
+ * <pre class="groovyTestCase">
+ * 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()
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static <T> Optional<T> mapToObj(final OptionalLong self, final LongFunction<? extends T> mapper) {
+ if (!self.isPresent()) {
+ return Optional.empty();
+ }
+ return Optional.ofNullable(mapper.apply(self.getAsLong()));
+ }
+
+ /**
+ * If a value is present in the {@code OptionalDouble}, returns an {@code Optional}
+ * consisting of the result of applying the given function to the value or else empty.
+ * <pre class="groovyTestCase">
+ * 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()
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static <T> Optional<T> mapToObj(final OptionalDouble self, final DoubleFunction<? extends T> mapper) {
+ if (!self.isPresent()) {
+ return Optional.empty();
+ }
+ return Optional.ofNullable(mapper.apply(self.getAsDouble()));
+ }
+
+ /**
+ * If a value is present in the {@code OptionalInt}, returns an {@code OptionalInt}
+ * consisting of the result of applying the given function to the value or else empty.
+ * <pre class="groovyTestCase">
+ * assert !Optional.empty().mapToInt(x -> 42).isPresent()
+ * assert Optional.of('x').mapToInt(x -> 42).getAsInt() == 42
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static <T> OptionalInt mapToInt(final Optional<T> self, final ToIntFunction<? super T> mapper) {
+ if (!self.isPresent()) {
+ return OptionalInt.empty();
+ }
+ return OptionalInt.of(mapper.applyAsInt(self.get()));
+ }
+
+ /**
+ * If a value is present in the {@code OptionalLong}, returns an {@code OptionalLong}
+ * consisting of the result of applying the given function to the value or else empty.
+ * <pre class="groovyTestCase">
+ * assert !Optional.empty().mapToLong(x -> 42L).isPresent()
+ * assert Optional.of('x').mapToLong(x -> 42L).getAsLong() == 42L
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static <T> OptionalLong mapToLong(final Optional<T> self, final ToLongFunction<? super T> mapper) {
+ if (!self.isPresent()) {
+ return OptionalLong.empty();
+ }
+ return OptionalLong.of(mapper.applyAsLong(self.get()));
+ }
+
+ /**
+ * If a value is present in the {@code OptionalDouble}, returns an {@code OptionalDouble}
+ * consisting of the result of applying the given function to the value or else empty.
+ * <pre class="groovyTestCase">
+ * assert !Optional.empty().mapToDouble(x -> Math.PI).isPresent()
+ * assert Optional.of('x').mapToDouble(x -> Math.PI).getAsDouble() == Math.PI
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+ public static <T> OptionalDouble mapToDouble(final Optional<T> self, final ToDoubleFunction<? super T> mapper) {
+ if (!self.isPresent()) {
+ return OptionalDouble.empty();
+ }
+ return OptionalDouble.of(mapper.applyAsDouble(self.get()));
+ }
+
+ /**
* If the optional contains a value, returns an optional containing the transformed value obtained using the <code>transform</code> closure
* or otherwise an empty optional.
* <pre class="groovyTestCase">
@@ -315,4 +551,14 @@
public static Stream<Float> stream(final float[] self) {
return IntStream.range(0, self.length).mapToObj(i -> self[i]);
}
+
+ /**
+ * If a value is present in the {@link Optional}, returns a {@link Stream}
+ * with the value as its source or else an empty stream.
+ *
+ * @since 3.0.0
+ */
+ public static <T> Stream<T> stream(final Optional<T> self) {
+ return self.map(Stream::of).orElseGet(Stream::empty);
+ }
}