You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by da...@apache.org on 2025/05/04 08:05:46 UTC

(jackrabbit-oak) branch OAK-11691 created (now fd152d6cf6)

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

daim pushed a change to branch OAK-11691
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


      at fd152d6cf6 OAK-11691 : added Iterators.partition replacement in oak-commons

This branch includes the following new commits:

     new fd152d6cf6 OAK-11691 : added Iterators.partition replacement in oak-commons

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(jackrabbit-oak) 01/01: OAK-11691 : added Iterators.partition replacement in oak-commons

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

daim pushed a commit to branch OAK-11691
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git

commit fd152d6cf67b488cf3a71172a1e7452c3b872c38
Author: Rishabh Kumar <di...@adobe.com>
AuthorDate: Sun May 4 13:35:10 2025 +0530

    OAK-11691 : added Iterators.partition replacement in oak-commons
---
 .../oak/commons/collections/IterableUtils.java     |  23 +----
 .../oak/commons/collections/IteratorUtils.java     |  57 +++++++++++
 .../oak/commons/collections/IterableUtilsTest.java |  25 +++++
 .../oak/commons/collections/IteratorUtilsTest.java | 111 +++++++++++++++++++++
 4 files changed, 194 insertions(+), 22 deletions(-)

diff --git a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IterableUtils.java b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IterableUtils.java
index 9f7b8d393a..4f93afc75f 100644
--- a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IterableUtils.java
+++ b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IterableUtils.java
@@ -238,28 +238,7 @@ public class IterableUtils {
         return new Iterable<>() {
             @Override
             public @NotNull Iterator<List<T>> iterator() {
-                return new Iterator<>() {
-                    private final Iterator<T> iterator = itr.iterator();
-
-                    @Override
-                    public boolean hasNext() {
-                        return iterator.hasNext();
-                    }
-
-                    @Override
-                    public List<T> next() {
-                        // check if there are elements left, throw an exception if not
-                        if (!hasNext()) {
-                            throw new NoSuchElementException();
-                        }
-
-                        List<T> currentPartition = new ArrayList<>(size);
-                        for (int i = 0; i < size && iterator.hasNext(); i++) {
-                            currentPartition.add(iterator.next());
-                        }
-                        return currentPartition;
-                    }
-                };
+                return IteratorUtils.partition(itr.iterator(), size);
             }
         };
     }
diff --git a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtils.java b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtils.java
index b6830593e4..7d98918b8c 100644
--- a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtils.java
+++ b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtils.java
@@ -20,12 +20,17 @@ package org.apache.jackrabbit.oak.commons.collections;
 
 import org.apache.commons.collections4.iterators.IteratorChain;
 import org.apache.commons.collections4.iterators.PeekingIterator;
+import org.apache.commons.collections4.iterators.UnmodifiableIterator;
+import org.apache.jackrabbit.oak.commons.conditions.Validate;
 import org.jetbrains.annotations.NotNull;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.Enumeration;
 import java.util.Iterator;
+import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.PriorityQueue;
@@ -474,5 +479,57 @@ public class IteratorUtils {
     public static <E> Iterator<E> cycle(final Iterable<E> iterable) {
         return org.apache.commons.collections4.IteratorUtils.loopingIterator(CollectionUtils.toCollection(iterable));
     }
+
+    /**
+     * Returns an iterator that partitions the elements of another iterator into fixed-size lists.
+     * <p>
+     * This method creates a new iterator that will group elements from the source iterator
+     * into lists of the specified size. The final list may be smaller than the requested size
+     * if there are not enough elements remaining in the source iterator.
+     * <p>
+     * The returned lists are unmodifiable. The source iterator is consumed only as the
+     * returned iterator is advanced.
+     * <p>
+     * Example usage:
+     * <pre>
+     * Iterator&lt;Integer&gt; numbers = Arrays.asList(1, 2, 3, 4, 5).iterator();
+     * Iterator&lt;List&lt;Integer&gt;&gt; partitioned = IteratorUtils.partition(numbers, 2);
+     * // partitioned will iterate through [1, 2], [3, 4], [5]
+     * </pre>
+     *
+     * @param <T> the type of elements in the source iterator
+     * @param iterator the source iterator to partition, must not be null
+     * @param size the size of each partition, must be greater than 0
+     * @return an iterator of fixed-size lists containing the elements of the source iterator
+     * @throws NullPointerException if the iterator is null
+     * @throws IllegalArgumentException if size is less than or equal to 0
+     */
+    public static <T> Iterator<List<T>> partition(final Iterator<T> iterator, final int size) {
+
+        Objects.requireNonNull(iterator, "Iterator must not be null.");
+        Validate.checkArgument(size > 0, "Size must be greater than 0.");
+
+        return UnmodifiableIterator.unmodifiableIterator(new Iterator<>() {
+
+            @Override
+            public boolean hasNext() {
+                return iterator.hasNext();
+            }
+
+            @Override
+            public List<T> next() {
+                // check if there are elements left, throw an exception if not
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+
+                final List<T> currentPartition = new ArrayList<>(size);
+                for (int i = 0; i < size && iterator.hasNext(); i++) {
+                    currentPartition.add(iterator.next());
+                }
+                return Collections.unmodifiableList(currentPartition);
+            }
+        });
+    }
 }
 
diff --git a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IterableUtilsTest.java b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IterableUtilsTest.java
index 4acd80283c..7b8e17336b 100644
--- a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IterableUtilsTest.java
+++ b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IterableUtilsTest.java
@@ -400,6 +400,31 @@ public class IterableUtilsTest {
         Assert.assertThrows(NoSuchElementException.class, iterator::next);
     }
 
+    @Test
+    public void testPartitionReturnsUnmodifiableLists() {
+        List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
+        Iterable<List<String>> partitioned = IterableUtils.partition(list, 2);
+
+        List<String> partition = partitioned.iterator().next();
+        Assert.assertThrows(UnsupportedOperationException.class, () -> partition.add("d")); // Should throw UnsupportedOperationException
+    }
+
+    @Test
+    public void testPartitionWithRemovableIterable() {
+        List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
+        Iterable<List<String>> partitioned = IterableUtils.partition(list, 2);
+
+        // Get first partition
+        List<String> partition = partitioned.iterator().next();
+        Assert.assertEquals(Arrays.asList("a", "b"), partition);
+
+        // Original iterator shouldn't support removal through partition
+        Assert.assertThrows(UnsupportedOperationException.class, partitioned.iterator()::remove);
+
+        // But original list should still have all elements
+        Assert.assertEquals(Arrays.asList("a", "b", "c", "d"), list);
+    }
+
     @Test
     public void testFilterWithNonEmptyIterable() {
         Iterable<Integer> iterable = Arrays.asList(1, 2, 3, 4, 5);
diff --git a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtilsTest.java b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtilsTest.java
index ddcd9a8a7b..7abc2a8186 100644
--- a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtilsTest.java
+++ b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtilsTest.java
@@ -1135,4 +1135,115 @@ public class IteratorUtilsTest {
         Assert.assertEquals("x", cyclingIterator.next());
         Assert.assertTrue(cyclingIterator.hasNext());
     }
+
+    @Test
+    public void testPartitionWithEvenDivision() {
+        List<String> list = Arrays.asList("a", "b", "c", "d");
+        Iterator<List<String>> partitioned = IteratorUtils.partition(list.iterator(), 2);
+
+        Assert.assertTrue(partitioned.hasNext());
+        Assert.assertEquals(Arrays.asList("a", "b"), partitioned.next());
+        Assert.assertTrue(partitioned.hasNext());
+        Assert.assertEquals(Arrays.asList("c", "d"), partitioned.next());
+        Assert.assertFalse(partitioned.hasNext());
+    }
+
+    @Test
+    public void testPartitionWithUnevenDivision() {
+        List<String> list = Arrays.asList("a", "b", "c", "d", "e");
+        Iterator<List<String>> partitioned = IteratorUtils.partition(list.iterator(), 2);
+
+        Assert.assertTrue(partitioned.hasNext());
+        Assert.assertEquals(Arrays.asList("a", "b"), partitioned.next());
+        Assert.assertTrue(partitioned.hasNext());
+        Assert.assertEquals(Arrays.asList("c", "d"), partitioned.next());
+        Assert.assertTrue(partitioned.hasNext());
+        Assert.assertEquals(List.of("e"), partitioned.next());
+        Assert.assertFalse(partitioned.hasNext());
+    }
+
+    @Test
+    public void testPartitionWithEmptyIterator() {
+        Iterator<List<String>> partitioned = IteratorUtils.partition(Collections.emptyIterator(), 3);
+        Assert.assertFalse(partitioned.hasNext());
+    }
+
+    @Test
+    public void testPartitionWithSizeOne() {
+        List<Integer> list = Arrays.asList(1, 2, 3);
+        Iterator<List<Integer>> partitioned = IteratorUtils.partition(list.iterator(), 1);
+
+        Assert.assertTrue(partitioned.hasNext());
+        Assert.assertEquals(Collections.singletonList(1), partitioned.next());
+        Assert.assertTrue(partitioned.hasNext());
+        Assert.assertEquals(Collections.singletonList(2), partitioned.next());
+        Assert.assertTrue(partitioned.hasNext());
+        Assert.assertEquals(Collections.singletonList(3), partitioned.next());
+        Assert.assertFalse(partitioned.hasNext());
+    }
+
+    @Test
+    public void testPartitionWithLargeIterator() {
+        // Create a large list
+        List<Integer> list = new ArrayList<>();
+        for (int i = 0; i < 1000; i++) {
+            list.add(i);
+        }
+
+        Iterator<List<Integer>> partitioned = IteratorUtils.partition(list.iterator(), 100);
+
+        // Verify we get 10 partitions of 100 elements each
+        int partitionCount = 0;
+        while (partitioned.hasNext()) {
+            List<Integer> partition = partitioned.next();
+            if (partitionCount < 9) {
+                Assert.assertEquals(100, partition.size());
+            }
+            partitionCount++;
+        }
+        Assert.assertEquals(10, partitionCount);
+    }
+
+    @Test
+    public void testPartitionWithNullIterator() {
+        Assert.assertThrows(NullPointerException.class, () -> IteratorUtils.partition(null, 5));
+    }
+
+    @Test
+    public void testPartitionWithZeroSize() {
+        List<String> list = Arrays.asList("a", "b", "c");
+        Assert.assertThrows(IllegalArgumentException.class, () -> IteratorUtils.partition(list.iterator(), 0));
+    }
+
+    @Test
+    public void testPartitionWithNegativeSize() {
+        List<String> list = Arrays.asList("a", "b", "c");
+        Assert.assertThrows(IllegalArgumentException.class, () -> IteratorUtils.partition(list.iterator(), -1));
+    }
+
+    @Test
+    public void testPartitionReturnsUnmodifiableLists() {
+        List<String> list = Arrays.asList("a", "b", "c");
+        Iterator<List<String>> partitioned = IteratorUtils.partition(list.iterator(), 2);
+
+        List<String> partition = partitioned.next();
+        Assert.assertThrows(UnsupportedOperationException.class, () -> partition.add("d")); // Should throw UnsupportedOperationException
+    }
+
+    @Test
+    public void testPartitionWithRemovableIterator() {
+        List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
+        Iterator<String> iterator = list.iterator();
+        Iterator<List<String>> partitioned = IteratorUtils.partition(iterator, 2);
+
+        // Get first partition
+        List<String> partition = partitioned.next();
+        Assert.assertEquals(Arrays.asList("a", "b"), partition);
+
+        // Original iterator shouldn't support removal through partition
+        Assert.assertThrows(UnsupportedOperationException.class, partitioned::remove);
+
+        // But original list should still have all elements
+        Assert.assertEquals(Arrays.asList("a", "b", "c", "d"), list);
+    }
 }