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


The following commit(s) were added to refs/heads/master by this push:
     new 90fee12ab7 GROOVY-11604: Add returnEarly boolean variant for DGM#chop
90fee12ab7 is described below

commit 90fee12ab7d57fee880e172389332c347259b92f
Author: Paul King <[email protected]>
AuthorDate: Mon Apr 7 10:30:49 2025 +1000

    GROOVY-11604: Add returnEarly boolean variant for DGM#chop
---
 .../groovy/runtime/DefaultGroovyMethods.java       | 147 +++++++++++++++++++--
 1 file changed, 133 insertions(+), 14 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java 
b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
index 67ce63009e..6f174de4e2 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -96,6 +96,7 @@ import org.codehaus.groovy.runtime.typehandling.NumberMath;
 import org.codehaus.groovy.tools.RootLoader;
 import org.codehaus.groovy.transform.trait.Traits;
 import org.codehaus.groovy.util.ArrayIterable;
+import org.codehaus.groovy.util.IntArrayIterator;
 import org.codehaus.groovy.util.IteratorBufferedIterator;
 import org.codehaus.groovy.util.ListBufferedIterator;
 
@@ -1808,7 +1809,6 @@ public class DefaultGroovyMethods extends 
DefaultGroovyMethodsSupport {
 
     /**
      * Chops the Iterable into pieces, returning lists with sizes 
corresponding to the supplied chop sizes.
-     * If the Iterable isn't large enough, truncated (possibly empty) pieces 
are returned.
      * Using a chop size of -1 will cause that piece to contain all remaining 
items from the Iterable.
      * <p>
      * Example usage:
@@ -1818,7 +1818,7 @@ public class DefaultGroovyMethods extends 
DefaultGroovyMethodsSupport {
      * assert ('a'..'h').chop(2, 4) == [['a', 'b'], ['c', 'd', 'e', 'f']]
      * assert ['a', 'b', 'c', 'd', 'e'].chop(3) == [['a', 'b', 'c']]
      * assert ['a', 'b', 'c', 'd', 'e'].chop(1, 2, 3) == [['a'], ['b', 'c'], 
['d', 'e']]
-     * assert ['a', 'b', 'c', 'd', 'e'].chop(1, 2, 3, 3, 3) == [['a'], ['b', 
'c'], ['d', 'e'], [], []]
+     * assert ['a', 'b', 'c', 'd', 'e'].chop(1, 2, 3, 3, 3) == [['a'], ['b', 
'c'], ['d', 'e']]
      * </pre>
      *
      * @param self      an Iterable to be chopped
@@ -1828,7 +1828,29 @@ public class DefaultGroovyMethods extends 
DefaultGroovyMethodsSupport {
      * @since 2.5.2
      */
     public static <T> List<List<T>> chop(Iterable<T> self, int... chopSizes) {
-        return toList(chop(self.iterator(), chopSizes));
+        return chop(self, true, chopSizes);
+    }
+
+    /**
+     * Chops the Iterable into pieces, returning lists with sizes 
corresponding to the supplied chop sizes.
+     * When no more elements remain in the iterable, truncated (possibly 
empty) pieces are returned unless returnEarly is true.
+     * Using a chop size of -1 will cause that piece to contain all remaining 
items from the Iterable.
+     * <p>
+     * Example usage:
+     * <pre class="groovyTestCase">
+     * assert ['a', 'b', 'c', 'd', 'e'].chop(false, 1, 2, 3, 3, 3) == [['a'], 
['b', 'c'], ['d', 'e'], [], []]
+     * assert ['a', 'b', 'c', 'd', 'e'].chop(true, 1, 2, 3, 3, 3) == [['a'], 
['b', 'c'], ['d', 'e']]
+     * </pre>
+     *
+     * @param self      an Iterable to be chopped
+     * @param chopSizes the sizes for the returned pieces
+     * @param returnEarly as soon as the source is exhausted, ignore remaining 
asked for chop sizes
+     * @return a list of lists chopping the original iterable into pieces 
determined by chopSizes
+     * @see #collate(Iterable, int) to chop an Iterable into pieces of a fixed 
size
+     * @since 2.5.2
+     */
+    public static <T> List<List<T>> chop(Iterable<T> self, boolean 
returnEarly, int... chopSizes) {
+        return toList(chop(self.iterator(), returnEarly, chopSizes));
     }
 
     /**
@@ -1844,7 +1866,6 @@ public class DefaultGroovyMethods extends 
DefaultGroovyMethodsSupport {
 
     /**
      * Chops the iterator items into pieces, returning lists with sizes 
corresponding to the supplied chop sizes.
-     * If the iterator is exhausted early, truncated (possibly empty) pieces 
are returned.
      * Using a chop size of -1 will cause that piece to contain all remaining 
items from the iterator.
      * <p>
      * Example usage:
@@ -1858,32 +1879,130 @@ public class DefaultGroovyMethods extends 
DefaultGroovyMethodsSupport {
      * @since 5.0.0
      */
     public static <T> Iterator<List<T>> chop(Iterator<T> self, int... 
chopSizes) {
+        return chop(self, true, chopSizes);
+    }
+
+    /**
+     * Chops the source iterator elements into pieces, returning lists with 
sizes corresponding to the supplied chop sizes.
+     * If the source iterator is exhausted early, truncated (possibly empty) 
pieces are returned unless returnEarly is true.
+     * Using a chop size of -1 will cause that piece to contain all remaining 
items from the source iterator.
+     * <p>
+     * Example usage:
+     * <pre class="groovyTestCase">
+     * assert (1..6).iterator().chop(1, 2, -1).collect() == [[1], [2, 3], [4, 
5, 6]]
+     * </pre>
+     *
+     * @param self      an Iterator to be chopped
+     * @param chopSizes the sizes for the returned pieces
+     * @param returnEarly as soon as the source is exhausted, ignore remaining 
asked for chop sizes
+     * @return an iterator of lists chopping the original source iterator 
elements into pieces determined by chopSizes
+     * @since 5.0.0
+     */
+    public static <T> Iterator<List<T>> chop(Iterator<T> self, boolean 
returnEarly, int... chopSizes) {
+        Objects.requireNonNull(self);
+        return new ChopIterator<>(self, returnEarly, new 
IntArrayIterator(chopSizes));
+    }
+
+    /**
+     * Chops the source iterator elements into pieces, returning lists with 
sizes corresponding to the supplied chop sizes.
+     * Using a chop size of -1 will cause that piece to contain all remaining 
items from the source iterator.
+     * <p>
+     * Example usage:
+     * <pre class="groovyTestCase">
+     * import java.util.stream.Stream
+     *
+     * def stars = ['*']
+     *     .repeat()
+     *     .chop(Stream.iterate(1, n {@code ->} n + 2).iterator())
+     *     .take(10)
+     *     *.join()
+     *     .collect()
+     * def width = stars[-1].size()
+     * assert stars*.center(width, '.').join('\n') == '''
+     * .........*.........
+     * ........***........
+     * .......*****.......
+     * ......*******......
+     * .....*********.....
+     * ....***********....
+     * ...*************...
+     * ..***************..
+     * .*****************.
+     * *******************
+     * '''.trim()
+     * </pre>
+     *
+     * @param self      an Iterator of source elements to be chopped
+     * @param chopSizes the sizes for the returned pieces
+     * @return an iterator of lists chopping the original iterator elements 
into pieces determined by chopSizes
+     * @since 5.0.0
+     */
+    public static <T> Iterator<List<T>> chop(Iterator<T> self, 
Iterator<Integer> chopSizes) {
         Objects.requireNonNull(self);
-        return new ChopIterator<>(self, chopSizes);
+        return new ChopIterator<>(self, true, chopSizes);
+    }
+
+    /**
+     * Chops the source iterator elements into pieces, returning lists with 
sizes corresponding to the supplied chop sizes.
+     * If the source iterator is exhausted early, truncated (possibly empty) 
pieces are returned unless returnEarly is true.
+     * Using a chop size of -1 will cause that piece to contain all remaining 
items from the source iterator.
+     * <p>
+     * Example usage:
+     * <pre class="groovyTestCase">
+     * import java.util.stream.Stream
+     *
+     * def stars = ['*']
+     *     .repeat()
+     *     .chop(true, Stream.iterate(1, n {@code ->} n + 2).iterator())
+     *     .take(10)
+     *     *.join()
+     *     .collect()
+     * def width = stars[-1].size()
+     * assert stars*.center(width, '.').join('\n') == '''
+     * .........*.........
+     * ........***........
+     * .......*****.......
+     * ......*******......
+     * .....*********.....
+     * ....***********....
+     * ...*************...
+     * ..***************..
+     * .*****************.
+     * *******************
+     * '''.trim()
+     * </pre>
+     *
+     * @param self      an Iterator of source elements to be chopped
+     * @param chopSizes the sizes for the returned pieces
+     * @param returnEarly as soon as the source is exhausted, ignore remaining 
asked for chop sizes
+     * @return an iterator of lists chopping the original iterator elements 
into pieces determined by chopSizes
+     * @since 5.0.0
+     */
+    public static <T> Iterator<List<T>> chop(Iterator<T> self, boolean 
returnEarly, Iterator<Integer> chopSizes) {
+        Objects.requireNonNull(self);
+        return new ChopIterator<>(self, returnEarly, chopSizes);
     }
 
     private static final class ChopIterator<T> implements Iterator<List<T>> {
         private final Iterator<T> delegate;
-        private final Queue<Integer> remainingSizes = new LinkedList<>();
+        private final boolean returnEarly;
+        private final Iterator<Integer> chopSizes;
 
-        private ChopIterator(Iterator<T> delegate, int... chopSizes) {
+        private ChopIterator(Iterator<T> delegate, boolean returnEarly, 
Iterator<Integer> chopSizes) {
             this.delegate = delegate;
-            int count = chopSizes.length;
-            for (int i : chopSizes) {
-                --count;
-                remainingSizes.offer(i);
-            }
+            this.returnEarly = returnEarly;
+            this.chopSizes = chopSizes;
         }
 
         @Override
         public boolean hasNext() {
-            return !remainingSizes.isEmpty();
+            return chopSizes.hasNext() && (!returnEarly || delegate.hasNext());
         }
 
         @Override
         public List<T> next() {
             if (!hasNext()) throw new NoSuchElementException();
-            int size = remainingSizes.poll();
+            int size = chopSizes.next();
             List<T> next = new ArrayList<>();
             while (size-- != 0 && delegate.hasNext()) {
                 next.add(delegate.next());

Reply via email to