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())); + } }
