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

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

commit d3f9814e211b245127ecc0f38672d02846f08010
Author: Rishabh Kumar <[email protected]>
AuthorDate: Fri Apr 18 21:45:00 2025 +0530

    OAK-11669 : added Iterators.concat replacement in oak-commons
---
 .../oak/commons/collections/IteratorUtils.java     |  83 ++++++++
 .../oak/commons/collections/IteratorUtilsTest.java | 228 +++++++++++++++++++++
 2 files changed, 311 insertions(+)

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 ab756ce8b1..b48e109a61 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
@@ -18,9 +18,11 @@
  */
 package org.apache.jackrabbit.oak.commons.collections;
 
+import org.apache.commons.collections4.iterators.IteratorChain;
 import org.apache.commons.collections4.iterators.PeekingIterator;
 import org.jetbrains.annotations.NotNull;
 
+import java.util.Collection;
 import java.util.Comparator;
 import java.util.Enumeration;
 import java.util.Iterator;
@@ -286,4 +288,85 @@ public class IteratorUtils {
     public static <T> Enumeration<T> asEnumeration(final Iterator<T> iterator) 
{
         return 
org.apache.commons.collections4.IteratorUtils.asEnumeration(iterator);
     }
+
+    /**
+     * Returns an iterator that iterates through two iterators in sequence.
+     * <p>
+     * This method creates a new iterator that will first iterate through the 
elements
+     * in the first iterator and then, when the first iterator is exhausted, 
will iterate
+     * through the elements in the second iterator.
+     * <p>
+     * The returned iterator supports {@link Iterator#remove()} if the 
provided iterators
+     * support it.
+     *
+     * @param <E> the element type
+     * @param iterator1 the first iterator to chain, may be null
+     * @param iterator2 the second iterator to chain, may be null
+     * @return an iterator that chains the specified iterators together
+     * @throws NullPointerException if any of the iterator is null
+     */
+    public static <E> Iterator<E> chainedIterator(final Iterator<? extends E> 
iterator1,
+                                                  final Iterator<? extends E> 
iterator2) {
+        return 
org.apache.commons.collections4.IteratorUtils.chainedIterator(iterator1, 
iterator2);
+    }
+
+    /**
+     * Returns an iterator that iterates through varargs of iterators in 
sequence.
+     * <p>
+     * This method creates a new iterator that will first iterate through the 
elements
+     * in the first iterator and then, when the first iterator is exhausted, 
will iterate
+     * through the elements in the second iterator and so on...
+     * <p>
+     * The returned iterator supports {@link Iterator#remove()} if the 
underlying iterator
+     * support it.
+     *
+     * @param <E> the element type
+     * @param iterators array of iterators to chain must not be null
+     * @return an iterator that chains the specified iterators together
+     * @throws NullPointerException if iterators array is null or contains a 
null iterator
+     */
+    @SafeVarargs
+    public static <E> Iterator<E> chainedIterator(final Iterator<? extends 
E>... iterators) {
+        return 
org.apache.commons.collections4.IteratorUtils.chainedIterator(iterators);
+    }
+
+    /**
+     * Returns an iterator that iterates through a collection of iterators in 
sequence.
+     * <p>
+     * This method creates a new iterator that will first iterate through the 
elements
+     * in the first iterator and then, when the first iterator is exhausted, 
will iterate
+     * through the elements in the second iterator and so on...
+     * <p>
+     * The returned iterator supports {@link Iterator#remove()} if the 
underlying iterator
+     * support it.
+     *
+     * @param <E> the element type
+     * @param iterators collection of iterators to chain must not be null
+     * @return an iterator that chains the specified iterators together
+     * @throws NullPointerException if an iterators collection is null or 
contains a null iterator
+     */
+    public static <E> Iterator<E> chainedIterator(final Collection<Iterator<? 
extends E>> iterators) {
+        return 
org.apache.commons.collections4.IteratorUtils.chainedIterator(iterators);
+    }
+
+    /**
+     * Returns an iterator that iterates through an iterator of iterators in 
sequence.
+     * <p>
+     * This method creates a new iterator that will first iterate through the 
elements
+     * in the first iterator and then, when the first iterator is exhausted, 
will iterate
+     * through the elements in the second iterator and so on...
+     * <p>
+     * The returned iterator supports {@link Iterator#remove()} if the 
underlying iterator
+     * support it.
+     *
+     * @param <E> the element type
+     * @param iterators an iterator of iterators to chain must not be null
+     * @return an iterator that chains the specified iterators together
+     * @throws NullPointerException if an iterators collection is null or 
contains a null iterator
+     */
+    public static <E> Iterator<E> chainedIterator(final Iterator<? extends 
Iterator<? extends E>> iterators) {
+        final IteratorChain<E> eIteratorChain = new IteratorChain<>();
+        iterators.forEachRemaining(eIteratorChain::addIterator);
+        return eIteratorChain;
+    }
 }
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 0b96cb8df5..47a4393823 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
@@ -589,4 +589,232 @@ public class IteratorUtilsTest {
         Assert.assertEquals(list.get(1), enumeration.nextElement());
         Assert.assertFalse(enumeration.hasMoreElements());
     }
+
+    @Test
+    public void testChainedIteratorBothNonEmpty() {
+        Iterator<String> iterator1 = Arrays.asList("a", "b").iterator();
+        Iterator<String> iterator2 = Arrays.asList("c", "d").iterator();
+        Iterator<String> chain = IteratorUtils.chainedIterator(iterator1, 
iterator2);
+
+        // it should iterate the elements in order, first from iterator1 and 
then from iterator2
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("a", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("b", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("c", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("d", chain.next());
+        Assert.assertFalse(chain.hasNext());
+    }
+
+    @Test
+    public void testChainedIteratorFirstEmpty() {
+        Iterator<String> empty = Collections.emptyIterator();
+        Iterator<String> nonEmpty = Arrays.asList("a", "b").iterator();
+        Iterator<String> chain = IteratorUtils.chainedIterator(empty, 
nonEmpty);
+
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("a", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("b", chain.next());
+        Assert.assertFalse(chain.hasNext());
+    }
+
+    @Test
+    public void testChainedIteratorSecondEmpty() {
+        Iterator<String> nonEmpty = Arrays.asList("a", "b").iterator();
+        Iterator<String> empty = Collections.emptyIterator();
+        Iterator<String> chain = IteratorUtils.chainedIterator(nonEmpty, 
empty);
+
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("a", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("b", chain.next());
+        Assert.assertFalse(chain.hasNext());
+    }
+
+    @Test
+    public void testChainedIteratorBothEmpty() {
+        Iterator<String> empty1 = Collections.emptyIterator();
+        Iterator<String> empty2 = Collections.emptyIterator();
+        Iterator<String> chain = IteratorUtils.chainedIterator(empty1, empty2);
+
+        Assert.assertFalse(chain.hasNext());
+    }
+
+    @Test
+    public void testChainedIteratorNullFirst() {
+        Iterator<String> nonEmpty = Arrays.asList("a", "b").iterator();
+
+        Assert.assertThrows(NullPointerException.class, () -> 
IteratorUtils.chainedIterator(null, nonEmpty));
+    }
+
+    @Test
+    public void testChainedIteratorNullSecond() {
+        Iterator<String> nonEmpty = Arrays.asList("a", "b").iterator();
+
+        Assert.assertThrows(NullPointerException.class, () -> 
IteratorUtils.chainedIterator(nonEmpty, null));
+    }
+
+    @Test
+    public void testChainedIteratorBothNull() {
+        Assert.assertThrows(NullPointerException.class, () -> 
IteratorUtils.chainedIterator(null, null));
+    }
+
+    @Test
+    public void testChainedIteratorRemove() {
+        List<String> list1 = new ArrayList<>(Arrays.asList("a", "b"));
+        List<String> list2 = new ArrayList<>(Arrays.asList("c", "d"));
+
+        Iterator<String> iterator1 = list1.iterator();
+        Iterator<String> iterator2 = list2.iterator();
+        Iterator<String> chain = IteratorUtils.chainedIterator(iterator1, 
iterator2);
+
+        // Remove an element from the first iterator
+        chain.next(); // "a"
+        chain.remove();
+        Assert.assertEquals(List.of("b"), list1);
+
+        // Move to second iterator and remove an element
+        chain.next(); // "b"
+        chain.next(); // "c"
+        chain.remove();
+        Assert.assertEquals(List.of("d"), list2);
+    }
+
+    @Test
+    public void testChainedIteratorRemoveNotSupported() {
+        List<String> list1 = List.of("a", "b");
+        List<String> list2 = new ArrayList<>(Arrays.asList("c", "d"));
+
+        Iterator<String> iterator1 = list1.iterator();
+        Iterator<String> iterator2 = list2.iterator();
+        Iterator<String> chain = IteratorUtils.chainedIterator(iterator1, 
iterator2);
+
+        // Remove an element from the first iterator
+        chain.next(); // "a"
+        Assert.assertThrows(UnsupportedOperationException.class, 
chain::remove);
+        Assert.assertEquals(List.of("a", "b"), list1);
+
+        // Move to second iterator and remove an element
+        chain.next(); // "b"
+        chain.next(); // "c"
+        chain.remove();
+        Assert.assertEquals(List.of("d"), list2);
+    }
+
+    @Test
+    public void testChainedIteratorWithDifferentTypes() {
+        Iterator<Integer> intIterator = Arrays.asList(1, 2).iterator();
+        Iterator<Double> doubleIterator = Arrays.asList(3.0, 4.0).iterator();
+
+        // Chain iterators with a common supertype
+        Iterator<Number> chain = IteratorUtils.chainedIterator(intIterator, 
doubleIterator);
+
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals(1, chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals(2, chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals(3.0, chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals(4.0, chain.next());
+        Assert.assertFalse(chain.hasNext());
+    }
+
+    // for chained Iterator with varargs
+
+    @Test
+    public void testChainedIteratorArrays() {
+        Iterator<String> iterator1 = Arrays.asList("a", "b").iterator();
+        Iterator<String> iterator2 = Arrays.asList("c", "d").iterator();
+        Iterator<String> iterator3 = Arrays.asList("e", "f").iterator();
+        Iterator<String> chain = IteratorUtils.chainedIterator(iterator1, 
iterator2, iterator3);
+
+        // it should iterate the elements in order, first from iterator1 and 
then from iterator2
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("a", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("b", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("c", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("d", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("e", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("f", chain.next());
+        Assert.assertFalse(chain.hasNext());
+    }
+
+    @Test
+    public void testChainedIteratorArrayHasNull() {
+        Iterator<String> iterator1 = Arrays.asList("a", "b").iterator();
+        Iterator<String> iterator2 = Arrays.asList("a", "b").iterator();
+
+        Assert.assertThrows(NullPointerException.class, () -> 
IteratorUtils.chainedIterator(iterator1, null, iterator2));
+    }
+
+    @Test
+    public void testChainedIteratorCollection() {
+        Iterator<String> iterator1 = Arrays.asList("a", "b").iterator();
+        Iterator<String> iterator2 = Arrays.asList("c", "d").iterator();
+        Iterator<String> iterator3 = Arrays.asList("e", "f").iterator();
+        Iterator<String> chain = 
IteratorUtils.chainedIterator(List.of(iterator1, iterator2, iterator3));
+
+        // it should iterate the elements in order, first from iterator1 and 
then from iterator2
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("a", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("b", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("c", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("d", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("e", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("f", chain.next());
+        Assert.assertFalse(chain.hasNext());
+    }
+
+    @Test
+    public void testChainedIteratorCollectionHasNull() {
+        Iterator<String> iterator1 = Arrays.asList("a", "b").iterator();
+        Iterator<String> iterator2 = Arrays.asList("a", "b").iterator();
+
+        Assert.assertThrows(NullPointerException.class, () -> 
IteratorUtils.chainedIterator(new ArrayList<>(Arrays.asList(iterator1, 
iterator2, null))));
+    }
+
+    @Test
+    public void testChainedIterators() {
+        Iterator<String> iterator1 = Arrays.asList("a", "b").iterator();
+        Iterator<String> iterator2 = Arrays.asList("c", "d").iterator();
+        Iterator<String> iterator3 = Arrays.asList("e", "f").iterator();
+        Iterator<String> chain = 
IteratorUtils.chainedIterator(Arrays.asList(iterator1, iterator2, 
iterator3).iterator());
+
+        // it should iterate the elements in order, first from iterator1 and 
then from iterator2
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("a", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("b", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("c", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("d", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("e", chain.next());
+        Assert.assertTrue(chain.hasNext());
+        Assert.assertEquals("f", chain.next());
+        Assert.assertFalse(chain.hasNext());
+    }
+
+    @Test
+    public void testChainedIteratorsHasNull() {
+        Iterator<String> iterator1 = Arrays.asList("a", "b").iterator();
+        Iterator<String> iterator2 = Arrays.asList("a", "b").iterator();
+
+        Assert.assertThrows(NullPointerException.class, () -> 
IteratorUtils.chainedIterator(new ArrayList<>(Arrays.asList(iterator1, 
iterator2, null)).iterator()));
+    }
 }

Reply via email to