You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2021/04/30 08:32:17 UTC

[groovy] 01/02: Move stream-related extension methods to StreamGroovyMethods

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 37af188c3f944f730b6140f44b1e72131a13137a
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Apr 28 16:04:39 2021 -0500

    Move stream-related extension methods to StreamGroovyMethods
---
 build.gradle                                       |   8 +-
 .../groovy/runtime/DefaultGroovyMethods.java       |  20 +-
 .../groovy/runtime/StreamGroovyMethods.java        | 486 ++++++++++++++
 .../typehandling/DefaultTypeTransformation.java    |   4 +-
 .../vmplugin/v8/PluginDefaultGroovyMethods.java    | 746 +++++----------------
 subprojects/groovy-binary/build.gradle             |   1 +
 6 files changed, 675 insertions(+), 590 deletions(-)

diff --git a/build.gradle b/build.gradle
index 980e5d9..b3ae7fb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -231,10 +231,10 @@ tasks.named('test') {
 
 if (!JavaVersion.current().java10Compatible) {
     logger.lifecycle '''
-    **************************************** WARNING ********************************************
-    ******   You are running the build with an older JDK. NEVER try to release with 1.8.   ******
-    ******   You must use a JDK 10+ in order to compile all features of the language.     ******
-    *********************************************************************************************
+**************************************** WARNING ********************************************
+******   You are running the build with an older JDK. NEVER try to release with 1.8.   ******
+******   You must use a JDK 10+ in order to compile all features of the language.      ******
+*********************************************************************************************
 '''
 }
 
diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
index c756d84..d58862c 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -219,15 +219,17 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
             ProcessGroovyMethods.class,
             ResourceGroovyMethods.class,
             SocketGroovyMethods.class,
-            StringGroovyMethods.class//,
-            // Below are registered as module extension classes
-//            DateUtilExtensions.class,
-//            DateTimeStaticExtensions.class,
-//            DateTimeExtensions.class,
-//            SqlExtensions.class,
-//            SwingGroovyMethods.class,
-//            XmlExtensions.class,
-//            NioExtensions.class
+            StreamGroovyMethods.class,
+            StringGroovyMethods.class,
+            /* registered extensions:
+            DateUtilExtensions.class,
+            DateTimeExtensions.class,
+            DateTimeStaticExtensions.class,
+            NioExtensions.class,
+            SqlExtensions.class,
+            SwingGroovyMethods.class,
+            XmlExtensions.class,
+            */
     };
     private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
     private static final NumberAwareComparator<Comparable> COMPARABLE_NUMBER_AWARE_COMPARATOR = new NumberAwareComparator<>();
diff --git a/src/main/java/org/codehaus/groovy/runtime/StreamGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/StreamGroovyMethods.java
new file mode 100644
index 0000000..fa82132
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/StreamGroovyMethods.java
@@ -0,0 +1,486 @@
+package org.codehaus.groovy.runtime;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+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;
+import java.util.function.Consumer;
+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;
+
+public class StreamGroovyMethods {
+
+    private StreamGroovyMethods() {
+    }
+
+    /**
+     * TODO
+     *
+     * <pre class="groovyTestCase">
+     * import java.util.stream.Stream
+     * assert (Stream.of(1) + [2]).toList() == [1,2]
+     * assert (Stream.of(1) + []).toList() == [1]
+     * </pre>
+     *
+     * @since 4.0.0
+     */
+    public static <T> Stream<T> plus(final Stream<? extends T> lhs, final Collection<? extends T> rhs) {
+        return Stream.concat(lhs, rhs.stream());
+    }
+
+    /**
+     * TODO
+     *
+     * <pre class="groovyTestCase">
+     * import java.util.stream.Stream
+     * assert (Stream.of(1) + [2]).toList() == [1,2]
+     * assert (Stream.of(1) + []).toList() == [1]
+     * </pre>
+     *
+     * @since 4.0.0
+     */
+    public static <T> Stream<T> plus(final Stream<? extends T> lhs, final Iterable<? extends T> rhs) {
+        return Stream.concat(lhs, stream(rhs));
+    }
+
+    /**
+     * TODO
+     *
+     * <pre class="groovyTestCase">
+     * import java.util.stream.Stream
+     * assert (Stream.of(1) + Stream.&lt;Integer>empty()).toList() == [1]
+     * assert (Stream.of(1) + Stream.of(2)).toList() == [1,2]
+     * assert (Stream.of(1) + [2].stream()).toList() == [1,2]
+     * </pre>
+     *
+     * @since 4.0.0
+     */
+    public static <T> Stream<T> plus(final Stream<? extends T> lhs, final Stream<? extends T> rhs) {
+        return Stream.concat(lhs, rhs);
+    }
+
+    //--------------------------------------------------------------------------
+
+    /**
+     * 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);
+    }
+
+    /**
+     * Returns a sequential {@link Stream} with the specified array as its
+     * source.
+     *
+     * @param <T> The type of the array elements
+     * @param self The array, assumed to be unmodified during use
+     * @return a {@code Stream} for the array
+     *
+     * @since 2.5.0
+     */
+    public static <T> Stream<T> stream(final T[] self) {
+        return Arrays.stream(self);
+    }
+
+    /**
+     * Returns a sequential {@link Stream} with the specified array as its
+     * source.
+     *
+     * @param self The array, assumed to be unmodified during use
+     * @return a {@code Stream} for the array
+     *
+     * @since 2.5.0
+     */
+    public static Stream<Integer> stream(final int[] self) {
+        return Arrays.stream(self).boxed();
+    }
+
+    /**
+     * Returns a sequential {@link Stream} with the specified array as its
+     * source.
+     *
+     * @param self The array, assumed to be unmodified during use
+     * @return a {@code Stream} for the array
+     *
+     * @since 2.5.0
+     */
+    public static Stream<Long> stream(final long[] self) {
+        return Arrays.stream(self).boxed();
+    }
+
+    /**
+     * Returns a sequential {@link Stream} with the specified array as its
+     * source.
+     *
+     * @param self The array, assumed to be unmodified during use
+     * @return a {@code Stream} for the array
+     *
+     * @since 2.5.0
+     */
+    public static Stream<Double> stream(final double[] self) {
+        return Arrays.stream(self).boxed();
+    }
+
+    /**
+     * Returns a sequential {@link Stream} with the specified array as its
+     * source.
+     *
+     * @param self The array, assumed to be unmodified during use
+     * @return a {@code Stream} for the array
+     *
+     * @since 2.5.0
+     */
+    public static Stream<Character> stream(final char[] self) {
+        return IntStream.range(0, self.length).mapToObj(i -> self[i]);
+    }
+
+    /**
+     * Returns a sequential {@link Stream} with the specified array as its
+     * source.
+     *
+     * @param self The array, assumed to be unmodified during use
+     * @return a {@code Stream} for the array
+     *
+     * @since 2.5.0
+     */
+    public static Stream<Byte> stream(final byte[] self) {
+        return IntStream.range(0, self.length).mapToObj(i -> self[i]);
+    }
+
+    /**
+     * Returns a sequential {@link Stream} with the specified array as its
+     * source.
+     *
+     * @param self The array, assumed to be unmodified during use
+     * @return a {@code Stream} for the array
+     *
+     * @since 2.5.0
+     */
+    public static Stream<Short> stream(final short[] self) {
+        return IntStream.range(0, self.length).mapToObj(i -> self[i]);
+    }
+
+    /**
+     * Returns a sequential {@link Stream} with the specified array as its
+     * source.
+     *
+     * @param self The array, assumed to be unmodified during use
+     * @return a {@code Stream} for the array
+     *
+     * @since 2.5.0
+     */
+    public static Stream<Boolean> stream(final boolean[] self) {
+        return IntStream.range(0, self.length).mapToObj(i -> self[i]);
+    }
+
+    /**
+     * Returns a sequential {@link Stream} with the specified array as its
+     * source.
+     *
+     * @param self The array, assumed to be unmodified during use
+     * @return a {@code Stream} for the array
+     *
+     * @since 2.5.0
+     */
+    public static Stream<Float> stream(final float[] self) {
+        return IntStream.range(0, self.length).mapToObj(i -> self[i]);
+    }
+
+    /**
+     * 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&lt;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">
+     * assert [].spliterator().stream().toList().isEmpty()
+     * assert ['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);
+    }
+
+    /**
+     * 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();
+    }
+
+    /**
+     * 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);
+    }
+
+    //
+
+    /**
+     * 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());
+    }
+
+    /**
+     * Returns a sequential {@link IntStream} with the specified array as its
+     * source.
+     *
+     * @param self The array, assumed to be unmodified during use
+     * @return a {@code Stream} for the array
+     *
+     * @since 3.0.8
+     */
+    public static IntStream intStream(final int[] self) {
+        return Arrays.stream(self);
+    }
+
+    /**
+     * Returns a sequential {@link LongStream} with the specified array as its
+     * source.
+     *
+     * @param self The array, assumed to be unmodified during use
+     * @return a {@code Stream} for the array
+     *
+     * @since 3.0.8
+     */
+    public static LongStream longStream(final long[] self) {
+        return Arrays.stream(self);
+    }
+
+    /**
+     * Returns a sequential {@link DoubleStream} with the specified array as its
+     * source.
+     *
+     * @param self The array, assumed to be unmodified during use
+     * @return a {@code Stream} for the array
+     *
+     * @since 3.0.8
+     */
+    public static DoubleStream doubleStream(final double[] self) {
+        return Arrays.stream(self);
+    }
+
+    //--------------------------------------------------------------------------
+
+    /**
+     * Returns an array containing the elements of the stream.
+     * <pre class="groovyTestCase">
+     * import static groovy.test.GroovyAssert.shouldFail
+     *
+     * assert Arrays.equals([].stream().toArray(Object), new Object[0])
+     * assert Arrays.equals([].stream().toArray(String), new String[0])
+     * assert Arrays.equals([].stream().toArray(String[]), new String[0][])
+     * assert Arrays.equals(['x'].stream().toArray(Object), ['x'].toArray())
+     * assert Arrays.equals(['x'].stream().toArray(String), ['x'] as String[])
+     * assert Arrays.deepEquals([['x'] as String[]].stream().toArray(String[]), [['x'] as String[]] as String[][])
+     * assert Arrays.equals(['x'].stream().toArray(CharSequence), ['x'] as CharSequence[])
+     *
+     * shouldFail(ArrayStoreException) {
+     *     ['x'].stream().toArray(Thread)
+     * }
+     *
+     * shouldFail(IllegalArgumentException) {
+     *     ['x'].stream().toArray((Class) null)
+     * }
+     *
+     * // Stream#toArray(IntFunction) should still be used for closure literal:
+     * assert Arrays.equals(['x'].stream().toArray { n -&gt; new String[n] }, ['x'] as String[])
+     *
+     * // Stream#toArray(IntFunction) should still be used for method reference:
+     * assert Arrays.equals(['x'].stream().toArray(String[]::new), ['x'] as String[])
+     * </pre>
+     *
+     * @param self the stream
+     * @param type the array element type
+     *
+     * @since 3.0.4
+     */
+    public static <T> T[] toArray(final Stream<? extends T> self, final Class<T> type) {
+        if (type == null) throw new IllegalArgumentException("type cannot be null");
+        return self.toArray(length -> (T[]) Array.newInstance(type, length));
+    }
+
+    /**
+     * Accumulates the elements of stream into a new List.
+     *
+     * @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> self) {
+        return self.collect(Collectors.toList());
+    }
+
+    /**
+     * Accumulates the elements of stream into a new List.
+     *
+     * @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> self) {
+        return stream(self.iterator()).collect(Collectors.toList());
+    }
+
+    /**
+     * Accumulates the elements of stream into a new Set.
+     *
+     * @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> self) {
+        return self.collect(Collectors.toSet());
+    }
+
+    /**
+     * Accumulates the elements of stream into a new Set.
+     *
+     * @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> self) {
+        return stream(self.iterator()).collect(Collectors.toSet());
+    }
+}
diff --git a/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java b/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java
index c96e4f9..503d782 100644
--- a/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java
+++ b/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java
@@ -30,8 +30,8 @@ import org.codehaus.groovy.runtime.IteratorClosureAdapter;
 import org.codehaus.groovy.runtime.MethodClosure;
 import org.codehaus.groovy.runtime.NullObject;
 import org.codehaus.groovy.runtime.ResourceGroovyMethods;
+import org.codehaus.groovy.runtime.StreamGroovyMethods;
 import org.codehaus.groovy.runtime.StringGroovyMethods;
-import org.codehaus.groovy.vmplugin.v8.PluginDefaultGroovyMethods;
 
 import java.io.File;
 import java.io.IOException;
@@ -476,7 +476,7 @@ public class DefaultTypeTransformation {
         } else if (value.getClass().isArray()) {
             return arrayAsCollection(value);
         } else if (value instanceof BaseStream) {
-            return PluginDefaultGroovyMethods.toList((BaseStream) value);
+            return StreamGroovyMethods.toList((BaseStream) value);
         } else if (value instanceof MethodClosure) {
             MethodClosure method = (MethodClosure) value;
             IteratorClosureAdapter adapter = new IteratorClosureAdapter(method.getDelegate());
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 106d209..874b4a6 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/PluginDefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/PluginDefaultGroovyMethods.java
@@ -26,29 +26,19 @@ import groovy.transform.stc.ClosureParams;
 import groovy.transform.stc.FirstParam;
 import org.codehaus.groovy.runtime.DefaultGroovyMethodsSupport;
 import org.codehaus.groovy.runtime.InvokerHelper;
-import org.codehaus.groovy.runtime.NullObject;
 import org.codehaus.groovy.runtime.RangeInfo;
 
-import java.lang.management.ManagementFactory;
-import java.lang.reflect.Array;
 import java.lang.reflect.Method;
 import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.Iterator;
-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;
 import java.util.concurrent.ExecutionException;
 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;
@@ -59,13 +49,6 @@ import java.util.function.Supplier;
 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.DoubleStream;
-import java.util.stream.IntStream;
-import java.util.stream.LongStream;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
 
 /**
  * Defines new Groovy methods which appear on standard Java 8 classes within the
@@ -75,10 +58,116 @@ import java.util.stream.StreamSupport;
  */
 public class PluginDefaultGroovyMethods extends DefaultGroovyMethodsSupport {
 
-    // No instances, static methods only
     private PluginDefaultGroovyMethods() {
     }
 
+    //--------------------------------------------------------------------------
+    // Enum
+
+    /**
+     * Overloads the {@code ++} operator for enums. It will invoke Groovy's
+     * default next behaviour for enums that do not have their own next method.
+     *
+     * @param self an Enum
+     * @return the next defined enum from the enum class
+     *
+     * @since 1.5.2
+     */
+    public static Object next(final Enum self) {
+        for (Method method : self.getClass().getMethods()) {
+            if (method.getName().equals("next") && method.getParameterCount() == 0) {
+                return InvokerHelper.invokeMethod(self, "next", InvokerHelper.EMPTY_ARGS);
+            }
+        }
+        Object[] values = (Object[]) InvokerHelper.invokeStaticMethod(self.getClass(), "values", InvokerHelper.EMPTY_ARGS);
+        int index = Arrays.asList(values).indexOf(self);
+        return values[index < values.length - 1 ? index + 1 : 0];
+    }
+
+    /**
+     * Overloads the {@code --} operator for enums. It will invoke Groovy's
+     * default previous behaviour for enums that do not have their own previous method.
+     *
+     * @param self an Enum
+     * @return the previous defined enum from the enum class
+     *
+     * @since 1.5.2
+     */
+    public static Object previous(final Enum self) {
+        for (Method method : self.getClass().getMethods()) {
+            if (method.getName().equals("previous") && method.getParameterCount() == 0) {
+                return InvokerHelper.invokeMethod(self, "previous", InvokerHelper.EMPTY_ARGS);
+            }
+        }
+        Object[] values = (Object[]) InvokerHelper.invokeStaticMethod(self.getClass(), "values", InvokerHelper.EMPTY_ARGS);
+        int index = Arrays.asList(values).indexOf(self);
+        return values[index > 0 ? index - 1 : values.length - 1];
+    }
+
+    //--------------------------------------------------------------------------
+    // Future
+
+    /**
+     * Returns a Future asynchronously returning a transformed result.
+     * <pre class="_temp_disabled_groovyTestCase">
+     * import java.util.concurrent.*
+     * def executor = Executors.newSingleThreadExecutor()
+     * Future<String> foobar = executor.submit{ "foobar" }
+     * Future<Integer> foobarSize = foobar.collect{ it.size() }
+     * assert foobarSize.get() == 6
+     * executor.shutdown()
+     * </pre>
+     *
+     * @param self      a Future
+     * @param transform the closure used to transform the Future value
+     * @return a Future allowing the transformed value to be obtained asynchronously
+     *
+     * @since 3.0.0
+     */
+    public static <S,T> Future<T> collect(final Future<S> self, @ClosureParams(FirstParam.FirstGenericType.class) final Closure<T> transform) {
+        Objects.requireNonNull(self);
+        Objects.requireNonNull(transform);
+        return new TransformedFuture<T>(self, transform);
+    }
+
+    private static class TransformedFuture<E> implements Future<E> {
+        private final Future delegate;
+        private final Closure<E> transform;
+
+        private TransformedFuture(final Future delegate, final Closure<E> transform) {
+            this.delegate = delegate;
+            this.transform = transform;
+        }
+
+        @Override
+        public boolean cancel(final boolean mayInterruptIfRunning) {
+            return delegate.cancel(mayInterruptIfRunning);
+        }
+
+        @Override
+        public boolean isCancelled() {
+            return delegate.isCancelled();
+        }
+
+        @Override
+        public boolean isDone() {
+            return delegate.isDone();
+        }
+
+        @Override
+        public E get() throws InterruptedException, ExecutionException {
+            return transform.call(delegate.get());
+        }
+
+        @Override
+        public E get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+            return transform.call(delegate.get(timeout, unit));
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    // Optional
+
     /**
      * Coerce an {@code Optional} instance to a {@code boolean} value.
      * <pre class="groovyTestCase">
@@ -135,6 +224,26 @@ public class PluginDefaultGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
+     * 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">
+     * assert Optional.of("foobar").collect{ it.size() }.get() == 6
+     * assert !Optional.empty().collect{ it.size() }.isPresent()
+     * </pre>
+     *
+     * @param self      an Optional
+     * @param transform the closure used to transform the optional value if present
+     * @return an Optional containing the transformed value or empty if the optional is empty or the transform returns null
+     *
+     * @since 3.0.0
+     */
+    public static <S,T> Optional<T> collect(final Optional<S> self, @ClosureParams(FirstParam.FirstGenericType.class) final Closure<T> transform) {
+        Objects.requireNonNull(self);
+        Objects.requireNonNull(transform);
+        return self.map(transform::call);
+    }
+
+    /**
      * Tests given value against specified type and changes generics of result.
      * This is equivalent to: <code>self.filter(it -&gt; it instanceof Type).map(it -&gt; (Type) it)</code>
      * <pre class="groovyTestCase">
@@ -310,102 +419,42 @@ public class PluginDefaultGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
-     * 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.
+     * Provides similar functionality to JDK9 {@code or} on JDK8.
      * <pre class="groovyTestCase">
-     * assert Optional.of("foobar").collect{ it.size() }.get() == 6
-     * assert !Optional.empty().collect{ it.size() }.isPresent()
+     * def x = Optional.empty()
+     * def y = Optional.of('y')
+     * assert y.orOptional(() -> Optional.of('z')).get() == 'y'
+     * assert x.orOptional(() -> Optional.of('z')).get() == 'z'
      * </pre>
      *
-     * @param self      an Optional
-     * @param transform the closure used to transform the optional value if present
-     * @return an Optional containing the transformed value or empty if the optional is empty or the transform returns null
-     *
-     * @since 3.0.0
-     */
-    public static <S,T> Optional<T> collect(final Optional<S> self, @ClosureParams(FirstParam.FirstGenericType.class) final Closure<T> transform) {
-        Objects.requireNonNull(self);
-        Objects.requireNonNull(transform);
-        return self.map(transform::call);
-    }
-
-    /**
-     * Returns a Future asynchronously returning a transformed result.
-     * <pre class="_temp_disabled_groovyTestCase">
-     * import java.util.concurrent.*
-     * def executor = Executors.newSingleThreadExecutor()
-     * Future<String> foobar = executor.submit{ "foobar" }
-     * Future<Integer> foobarSize = foobar.collect{ it.size() }
-     * assert foobarSize.get() == 6
-     * executor.shutdown()
-     * </pre>
-     *
-     * @param self      a Future
-     * @param transform the closure used to transform the Future value
-     * @return a Future allowing the transformed value to be obtained asynchronously
-     *
-     * @since 3.0.0
-     */
-    public static <S,T> Future<T> collect(final Future<S> self, @ClosureParams(FirstParam.FirstGenericType.class) final Closure<T> transform) {
-        Objects.requireNonNull(self);
-        Objects.requireNonNull(transform);
-        return new TransformedFuture<T>(self, transform);
-    }
-
-    /**
-     * Overloads the {@code ++} operator for enums. It will invoke Groovy's
-     * default next behaviour for enums that do not have their own next method.
-     *
-     * @param self an Enum
-     * @return the next defined enum from the enum class
-     *
-     * @since 1.5.2
+     * @since 3.0.6
      */
-    public static Object next(final Enum self) {
-        for (Method method : self.getClass().getMethods()) {
-            if (method.getName().equals("next") && method.getParameterCount() == 0) {
-                return InvokerHelper.invokeMethod(self, "next", InvokerHelper.EMPTY_ARGS);
-            }
+    public static <T> Optional<T> orOptional(final Optional<T> self, final Supplier<Optional<? extends T>> supplier) {
+        if (self.isPresent()) {
+            return self;
         }
-        Object[] values = (Object[]) InvokerHelper.invokeStaticMethod(self.getClass(), "values", InvokerHelper.EMPTY_ARGS);
-        int index = Arrays.asList(values).indexOf(self);
-        return values[index < values.length - 1 ? index + 1 : 0];
+        return (Optional<T>) supplier.get();
     }
 
+    //--------------------------------------------------------------------------
+    // Runtime
+
     /**
-     * Overloads the {@code --} operator for enums. It will invoke Groovy's
-     * default previous behaviour for enums that do not have their own previous method.
-     *
-     * @param self an Enum
-     * @return the previous defined enum from the enum class
+     * Gets the pid of the current Java process.
      *
-     * @since 1.5.2
+     * @since 4.0.0
      */
-    public static Object previous(final Enum self) {
-        for (Method method : self.getClass().getMethods()) {
-            if (method.getName().equals("previous") && method.getParameterCount() == 0) {
-                return InvokerHelper.invokeMethod(self, "previous", InvokerHelper.EMPTY_ARGS);
-            }
+    public static String getPid(final Runtime self) {
+        String name = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
+        int index = name.indexOf('@');
+        if (index == -1) { // should never happen
+            return name;
         }
-        Object[] values = (Object[]) InvokerHelper.invokeStaticMethod(self.getClass(), "values", InvokerHelper.EMPTY_ARGS);
-        int index = Arrays.asList(values).indexOf(self);
-        return values[index > 0 ? index - 1 : values.length - 1];
+        return name.substring(0, index);
     }
 
-    /**
-     * Provides the standard Groovy <code>size()</code> method for <code>StringBuilder</code>.
-     *
-     * @param self a StringBuilder
-     * @return the length of the StringBuilder
-     *
-     * @since 1.5.2
-     *
-     * @see org.codehaus.groovy.runtime.StringGroovyMethods#size(CharSequence)
-     */
-    @Deprecated
-    public static int size(final StringBuilder self) {
-        return self.length();
-    }
+    //--------------------------------------------------------------------------
+    // StringBuilder
 
     /**
      * Overloads the left shift operator to provide an easy way to append multiple
@@ -431,18 +480,16 @@ public class PluginDefaultGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
-     * Supports the range subscript operator for StringBuilder.
-     * Index values are treated as characters within the builder.
+     * Appends a String to this StringBuilder.
      *
      * @param self  a StringBuilder
-     * @param range a Range
-     * @param value the object that's toString() will be inserted
+     * @param value a String
+     * @return a String
      *
      * @since 1.5.2
      */
-    public static void putAt(final StringBuilder self, final IntRange range, final Object value) {
-        RangeInfo info = DefaultGroovyMethodsSupport.subListBorders(self.length(), range);
-        self.replace(info.from, info.to, value.toString());
+    public static String plus(final StringBuilder self, final String value) {
+        return self + value;
     }
 
     /**
@@ -455,488 +502,37 @@ public class PluginDefaultGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 1.5.2
      */
     public static void putAt(final StringBuilder self, final EmptyRange range, final Object value) {
-        RangeInfo info = DefaultGroovyMethodsSupport.subListBorders(self.length(), range);
+        RangeInfo info = subListBorders(self.length(), range);
         self.replace(info.from, info.to, value.toString());
     }
 
     /**
-     * Appends a String to this StringBuilder.
+     * Supports the range subscript operator for StringBuilder.
+     * Index values are treated as characters within the builder.
      *
      * @param self  a StringBuilder
-     * @param value a String
-     * @return a String
+     * @param range a Range
+     * @param value the object that's toString() will be inserted
      *
      * @since 1.5.2
      */
-    public static String plus(final StringBuilder self, final String value) {
-        return self + value;
-    }
-
-    private static class TransformedFuture<E> implements Future<E> {
-        private final Future delegate;
-        private final Closure<E> transform;
-
-        private TransformedFuture(final Future delegate, final Closure<E> transform) {
-            this.delegate = delegate;
-            this.transform = transform;
-        }
-
-        @Override
-        public boolean cancel(final boolean mayInterruptIfRunning) {
-            return delegate.cancel(mayInterruptIfRunning);
-        }
-
-        @Override
-        public boolean isCancelled() {
-            return delegate.isCancelled();
-        }
-
-        @Override
-        public boolean isDone() {
-            return delegate.isDone();
-        }
-
-        @Override
-        public E get() throws InterruptedException, ExecutionException {
-            return transform.call(delegate.get());
-        }
-
-        @Override
-        public E get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
-            return transform.call(delegate.get(timeout, unit));
-        }
-    }
-
-    /**
-     * Returns an array containing the elements of the stream.
-     * <pre class="groovyTestCase">
-     * import static groovy.test.GroovyAssert.shouldFail
-     *
-     * assert Arrays.equals([].stream().toArray(Object), new Object[0])
-     * assert Arrays.equals([].stream().toArray(String), new String[0])
-     * assert Arrays.equals([].stream().toArray(String[]), new String[0][])
-     * assert Arrays.equals(['x'].stream().toArray(Object), ['x'].toArray())
-     * assert Arrays.equals(['x'].stream().toArray(String), ['x'] as String[])
-     * assert Arrays.deepEquals([['x'] as String[]].stream().toArray(String[]), [['x'] as String[]] as String[][])
-     * assert Arrays.equals(['x'].stream().toArray(CharSequence), ['x'] as CharSequence[])
-     *
-     * shouldFail(ArrayStoreException) {
-     *     ['x'].stream().toArray(Thread)
-     * }
-     *
-     * shouldFail(IllegalArgumentException) {
-     *     ['x'].stream().toArray((Class) null)
-     * }
-     *
-     * // Stream#toArray(IntFunction) should still be used for closure literal:
-     * assert Arrays.equals(['x'].stream().toArray { n -&gt; new String[n] }, ['x'] as String[])
-     *
-     * // Stream#toArray(IntFunction) should still be used for method reference:
-     * assert Arrays.equals(['x'].stream().toArray(String[]::new), ['x'] as String[])
-     * </pre>
-     *
-     * @param self the stream
-     * @param type the array element type
-     *
-     * @since 3.0.4
-     */
-    public static <T> T[] toArray(final Stream<? extends T> self, final Class<T> type) {
-        if (type == null) throw new IllegalArgumentException("type cannot be null");
-        return self.toArray(length -> (T[]) Array.newInstance(type, length));
-    }
-
-    /**
-     * Accumulates the elements of stream into a new List.
-     *
-     * @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> self) {
-        return self.collect(Collectors.toList());
-    }
-
-    /**
-     * Accumulates the elements of stream into a new Set.
-     *
-     * @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> self) {
-        return self.collect(Collectors.toSet());
-    }
-
-    /**
-     * Accumulates the elements of stream into a new List.
-     *
-     * @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> self) {
-        return stream(self.iterator()).collect(Collectors.toList());
-    }
-
-    /**
-     * Accumulates the elements of stream into a new Set.
-     *
-     * @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> self) {
-        return stream(self.iterator()).collect(Collectors.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);
-    }
-
-    /**
-     * Returns a sequential {@link Stream} with the specified array as its
-     * source.
-     *
-     * @param <T> The type of the array elements
-     * @param self The array, assumed to be unmodified during use
-     * @return a {@code Stream} for the array
-     *
-     * @since 2.5.0
-     */
-    public static <T> Stream<T> stream(final T[] self) {
-        return Arrays.stream(self);
-    }
-
-    /**
-     * Returns a sequential {@link Stream} with the specified array as its
-     * source.
-     *
-     * @param self The array, assumed to be unmodified during use
-     * @return a {@code Stream} for the array
-     *
-     * @since 2.5.0
-     */
-    public static Stream<Integer> stream(final int[] self) {
-        return Arrays.stream(self).boxed();
-    }
-
-    /**
-     * Returns a sequential {@link IntStream} with the specified array as its
-     * source.
-     *
-     * @param self The array, assumed to be unmodified during use
-     * @return a {@code Stream} for the array
-     *
-     * @since 3.0.8
-     */
-    public static IntStream intStream(final int[] self) {
-        return Arrays.stream(self);
-    }
-
-    /**
-     * Returns a sequential {@link Stream} with the specified array as its
-     * source.
-     *
-     * @param self The array, assumed to be unmodified during use
-     * @return a {@code Stream} for the array
-     *
-     * @since 2.5.0
-     */
-    public static Stream<Long> stream(final long[] self) {
-        return Arrays.stream(self).boxed();
-    }
-
-    /**
-     * Returns a sequential {@link LongStream} with the specified array as its
-     * source.
-     *
-     * @param self The array, assumed to be unmodified during use
-     * @return a {@code Stream} for the array
-     *
-     * @since 3.0.8
-     */
-    public static LongStream longStream(final long[] self) {
-        return Arrays.stream(self);
-    }
-
-    /**
-     * Returns a sequential {@link Stream} with the specified array as its
-     * source.
-     *
-     * @param self The array, assumed to be unmodified during use
-     * @return a {@code Stream} for the array
-     *
-     * @since 2.5.0
-     */
-    public static Stream<Double> stream(final double[] self) {
-        return Arrays.stream(self).boxed();
-    }
-
-    /**
-     * Returns a sequential {@link DoubleStream} with the specified array as its
-     * source.
-     *
-     * @param self The array, assumed to be unmodified during use
-     * @return a {@code Stream} for the array
-     *
-     * @since 3.0.8
-     */
-    public static DoubleStream doubleStream(final double[] self) {
-        return Arrays.stream(self);
-    }
-
-    /**
-     * Returns a sequential {@link Stream} with the specified array as its
-     * source.
-     *
-     * @param self The array, assumed to be unmodified during use
-     * @return a {@code Stream} for the array
-     *
-     * @since 2.5.0
-     */
-    public static Stream<Character> stream(final char[] self) {
-        return IntStream.range(0, self.length).mapToObj(i -> self[i]);
-    }
-
-    /**
-     * Returns a sequential {@link Stream} with the specified array as its
-     * source.
-     *
-     * @param self The array, assumed to be unmodified during use
-     * @return a {@code Stream} for the array
-     *
-     * @since 2.5.0
-     */
-    public static Stream<Byte> stream(final byte[] self) {
-        return IntStream.range(0, self.length).mapToObj(i -> self[i]);
-    }
-
-    /**
-     * Returns a sequential {@link Stream} with the specified array as its
-     * source.
-     *
-     * @param self The array, assumed to be unmodified during use
-     * @return a {@code Stream} for the array
-     *
-     * @since 2.5.0
-     */
-    public static Stream<Short> stream(final short[] self) {
-        return IntStream.range(0, self.length).mapToObj(i -> self[i]);
-    }
-
-    /**
-     * Returns a sequential {@link Stream} with the specified array as its
-     * source.
-     *
-     * @param self The array, assumed to be unmodified during use
-     * @return a {@code Stream} for the array
-     *
-     * @since 2.5.0
-     */
-    public static Stream<Boolean> stream(final boolean[] self) {
-        return IntStream.range(0, self.length).mapToObj(i -> self[i]);
-    }
-
-    /**
-     * Returns a sequential {@link Stream} with the specified array as its
-     * source.
-     *
-     * @param self The array, assumed to be unmodified during use
-     * @return a {@code Stream} for the array
-     *
-     * @since 2.5.0
-     */
-    public static Stream<Float> stream(final float[] self) {
-        return IntStream.range(0, self.length).mapToObj(i -> self[i]);
-    }
-
-    /**
-     * 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">
-     * assert [].spliterator().stream().toList().isEmpty()
-     * assert ['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.
-     *
-     * @since 3.0.0
-     */
-    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());
+    public static void putAt(final StringBuilder self, final IntRange range, final Object value) {
+        RangeInfo info = subListBorders(self.length(), range);
+        self.replace(info.from, info.to, value.toString());
     }
 
     /**
-     * If a value is present in the {@link OptionalDouble}, returns a {@link DoubleStream}
-     * with the value as its source or else an empty stream.
+     * Provides the standard Groovy {@code size()} method for StringBuilder.
      *
-     * @since 3.0.0
-     */
-    public static DoubleStream stream(final OptionalDouble self) {
-        if (!self.isPresent()) {
-            return DoubleStream.empty();
-        }
-        return DoubleStream.of(self.getAsDouble());
-    }
-
-    /**
-     * Provide similar functionality to JDK9 {@code or} on JDK8.
+     * @param self a StringBuilder
+     * @return the length of the StringBuilder
      *
-     * @since 3.0.6
-     */
-    public static <T> Optional<T> orOptional(Optional<T> self, Supplier<? extends Optional<? extends T>> supplier) {
-        if (self.isPresent()) {
-            return self;
-        }
-        return (Optional<T>) supplier.get();
-    }
-
-    /**
-     * Get the pid of the current Java process
+     * @since 1.5.2
      *
-     * @param self
-     * @return the pid
-     * @since 4.0.0
+     * @see org.codehaus.groovy.runtime.StringGroovyMethods#size(CharSequence)
      */
-    public static String getPid(Runtime self) {
-        String name = ManagementFactory.getRuntimeMXBean().getName();
-        int index = name.indexOf('@');
-        if (-1 == index) { // should never happen
-            return name;
-        }
-        return name.substring(0, index);
+    @Deprecated
+    public static int size(final StringBuilder self) {
+        return self.length();
     }
 }
diff --git a/subprojects/groovy-binary/build.gradle b/subprojects/groovy-binary/build.gradle
index 3e4fcb8..61b82c5 100644
--- a/subprojects/groovy-binary/build.gradle
+++ b/subprojects/groovy-binary/build.gradle
@@ -45,6 +45,7 @@ distribution {
             'org.codehaus.groovy.runtime.ProcessGroovyMethods',
             'org.codehaus.groovy.runtime.ResourceGroovyMethods',
             'org.codehaus.groovy.runtime.SocketGroovyMethods',
+            'org.codehaus.groovy.runtime.StreamGroovyMethods',
             'org.codehaus.groovy.runtime.StringGroovyMethods',
             'org.codehaus.groovy.vmplugin.v8.PluginDefaultGroovyMethods',
             'org.codehaus.groovy.vmplugin.v9.PluginDefaultGroovyMethods',