This is an automated email from the ASF dual-hosted git repository. daim pushed a commit to branch OAK-11458 in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
commit 8753df25e0350b481ab43db51669533865996d9b Author: Rishabh Kumar <[email protected]> AuthorDate: Thu Feb 6 13:17:17 2025 +0530 OAK-11458 : added utils class for repalcing Guava's Iterables --- oak-commons/pom.xml | 9 +- .../oak/commons/collections/IterableUtils.java | 145 ++++++++++++++++++ .../oak/commons/collections/package-info.java | 2 +- .../oak/commons/collections/IterableUtilsTest.java | 169 +++++++++++++++++++++ 4 files changed, 319 insertions(+), 6 deletions(-) diff --git a/oak-commons/pom.xml b/oak-commons/pom.xml index d89a934f6d..385104b03c 100644 --- a/oak-commons/pom.xml +++ b/oak-commons/pom.xml @@ -96,6 +96,10 @@ <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-collections4</artifactId> + </dependency> <dependency> <groupId>org.apache.jackrabbit</groupId> <artifactId>jackrabbit-jcr-commons</artifactId> @@ -128,11 +132,6 @@ <artifactId>commons-lang3</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-collections4</artifactId> - <scope>test</scope> - </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> 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 new file mode 100644 index 0000000000..7688db8950 --- /dev/null +++ b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IterableUtils.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.jackrabbit.oak.commons.collections; + +import org.apache.commons.collections4.iterators.LazyIteratorChain; + +import java.util.Iterator; +import java.util.Objects; + +/** + * Utility methods for {@link Iterable} conversions. + */ +public class IterableUtils { + + private IterableUtils() { + // no instances for you + } + + /** + * Combines two iterables into a single iterable. + * <p> + * The returned iterable has an iterator that traverses the elements in {@code a}, + * followed by the elements in {@code b}. The source iterators are not polled until necessary. + * <p> + * The returned iterable's iterator supports {@code remove()} when the corresponding + * input iterator supports it. + * + * @param <E> the element type + * @param a the first iterable, may not be null + * @param b the second iterable, may not be null + * @return a new iterable, combining the provided iterables + * @throws NullPointerException if either of the provided iterables is null + */ + public static <E> Iterable<E> chainedIterable(final Iterable<? extends E> a, + final Iterable<? extends E> b) { + return chainedIterable(new Iterable[] {a, b}); + } + + /** + * Combines three iterables into a single iterable. + * <p> + * The returned iterable has an iterator that traverses the elements in {@code a}, + * followed by the elements in {@code b} and {@code c}. The source + * iterators are not polled until necessary. + * <p> + * The returned iterable's iterator supports {@code remove()} when the corresponding + * input iterator supports it. + * + * @param <E> the element type + * @param a the first iterable, may not be null + * @param b the second iterable, may not be null + * @param c the third iterable, may not be null + * @return a new iterable, combining the provided iterables + * @throws NullPointerException if either of the provided iterables is null + */ + public static <E> Iterable<E> chainedIterable(final Iterable<? extends E> a, + final Iterable<? extends E> b, + final Iterable<? extends E> c) { + return chainedIterable(new Iterable[] {a, b, c}); + } + + /** + * Combines four iterables into a single iterable. + * <p> + * The returned iterable has an iterator that traverses the elements in {@code a}, + * followed by the elements in {@code b}, {@code c} and {@code d}. The source + * iterators are not polled until necessary. + * <p> + * The returned iterable's iterator supports {@code remove()} when the corresponding + * input iterator supports it. + * + * @param <E> the element type + * @param a the first iterable, may not be null + * @param b the second iterable, may not be null + * @param c the third iterable, may not be null + * @param d the fourth iterable, may not be null + * @return a new iterable, combining the provided iterables + * @throws NullPointerException if either of the provided iterables is null + */ + public static <E> Iterable<E> chainedIterable(final Iterable<? extends E> a, + final Iterable<? extends E> b, + final Iterable<? extends E> c, + final Iterable<? extends E> d) { + return chainedIterable(new Iterable[] {a, b, c, d}); + } + + /** + * Combines the provided iterables into a single iterable. + * <p> + * The returned iterable has an iterator that traverses the elements in the order + * of the arguments, i.e. iterables[0], iterables[1], .... The source iterators + * are not polled until necessary. + * <p> + * The returned iterable's iterator supports {@code remove()} when the corresponding + * input iterator supports it. + * + * @param <E> the element type + * @param iterables the iterables to combine, may not be null + * @return a new iterable, combining the provided iterables + * @throws NullPointerException if either of the provided iterables is null + */ + @SafeVarargs + public static <E> Iterable<E> chainedIterable(final Iterable<? extends E>... iterables) { + Objects.requireNonNull(iterables); + return org.apache.commons.collections4.IterableUtils.chainedIterable(iterables); + } + + /** + * Creates an {@code Iterable} that chains multiple {@code Iterable} instances into a single {@code Iterable}. + * <p> + * The returned {@code Iterable} will iterate over all elements of the first {@code Iterable}, + * then all elements of the second, and so on. + * + * @param <E> the type of elements returned by the iterator + * @param iterables an {@code Iterable} of {@code Iterable} instances to be chained + * @return an {@code Iterable} that provides a single view of all elements in the input {@code Iterable} instances + * @throws NullPointerException if the input {@code Iterable} or any of its elements are null + */ + public static <E> Iterable<E> chainedIterable(final Iterable<? extends Iterable<? extends E>> iterables) { + Objects.requireNonNull(iterables); + return () -> new LazyIteratorChain<>() { + private final Iterator<? extends Iterable<? extends E>> iterator = iterables.iterator(); + + protected Iterator<? extends E> nextIterator(int count) { + return iterator.hasNext() ? iterator.next().iterator() : null; + } + }; + } +} diff --git a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/package-info.java b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/package-info.java index 95dfb8621e..fa16acc93d 100644 --- a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/package-info.java +++ b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/package-info.java @@ -21,7 +21,7 @@ * Utilities for Java collections and streams. */ @Internal(since = "1.0.0") -@Version("2.0.0") +@Version("2.1.0") package org.apache.jackrabbit.oak.commons.collections; import org.apache.jackrabbit.oak.commons.annotations.Internal; import org.osgi.annotation.versioning.Version; 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 new file mode 100644 index 0000000000..6a620a6fc6 --- /dev/null +++ b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IterableUtilsTest.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.jackrabbit.oak.commons.collections; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * Unit tests for the {@link IterableUtils} class. + * <p> + * This class contains test cases to verify the functionality of the methods + * in the {@link IterableUtils} class. + */ +public class IterableUtilsTest { + + + @Test + public void testTwoChainedIterable() { + List<Integer> list1 = Arrays.asList(1, 2, 3); + List<Integer> list2 = Arrays.asList(4, 5); + + Iterable<Integer> chained = IterableUtils.chainedIterable(list1, list2); + + Iterator<Integer> iterator = chained.iterator(); + List<Integer> expected = Arrays.asList(1, 2, 3, 4, 5); + for (Integer value : expected) { + Assert.assertTrue(iterator.hasNext()); + Assert.assertEquals(value, iterator.next()); + } + Assert.assertFalse(iterator.hasNext()); + } + + @Test + public void testThreeChainedIterable() { + List<Integer> list1 = Arrays.asList(1, 2, 3); + List<Integer> list2 = Arrays.asList(4, 5); + List<Integer> list3 = Arrays.asList(6, 7, 8, 9); + + Iterable<Integer> chained = IterableUtils.chainedIterable(list1, list2, list3); + + Iterator<Integer> iterator = chained.iterator(); + List<Integer> expected = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); + for (Integer value : expected) { + Assert.assertTrue(iterator.hasNext()); + Assert.assertEquals(value, iterator.next()); + } + Assert.assertFalse(iterator.hasNext()); + } + + @Test + public void testChainedIterable() { + List<Integer> list1 = Arrays.asList(1, 2, 3); + List<Integer> list2 = Arrays.asList(4, 5); + List<Integer> list3 = Arrays.asList(6, 7, 8, 9); + + Iterable<Iterable<Integer>> iterables = Arrays.asList(list1, list2, list3); + Iterable<Integer> chained = IterableUtils.chainedIterable(iterables); + + Iterator<Integer> iterator = chained.iterator(); + List<Integer> expected = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); + for (Integer value : expected) { + Assert.assertTrue(iterator.hasNext()); + Assert.assertEquals(value, iterator.next()); + } + Assert.assertFalse(iterator.hasNext()); + } + + @Test + public void testFourChainedIterable() { + List<Integer> list1 = Arrays.asList(1, 2, 3); + List<Integer> list2 = Arrays.asList(4, 5); + List<Integer> list3 = Arrays.asList(6, 7, 8, 9); + List<Integer> list4 = Arrays.asList(10, 11, 12, 13); + + Iterable<Integer> chained = IterableUtils.chainedIterable(list1, list2, list3, list4); + + Iterator<Integer> iterator = chained.iterator(); + List<Integer> expected = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13); + for (Integer value : expected) { + Assert.assertTrue(iterator.hasNext()); + Assert.assertEquals(value, iterator.next()); + } + Assert.assertFalse(iterator.hasNext()); + } + + @Test + public void testChainedIterableVaragrs() { + List<Integer> list1 = Arrays.asList(1, 2, 3); + List<Integer> list2 = Arrays.asList(4, 5); + List<Integer> list3 = Arrays.asList(6, 7, 8, 9); + List<Integer> list4 = Arrays.asList(10, 11, 12, 13); + List<Integer> list5 = Arrays.asList(14, 15, 16); + + Iterable<Integer> chained = IterableUtils.chainedIterable(list1, list2, list3, list4, list5); + + Iterator<Integer> iterator = chained.iterator(); + List<Integer> expected = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + for (Integer value : expected) { + Assert.assertTrue(iterator.hasNext()); + Assert.assertEquals(value, iterator.next()); + } + Assert.assertFalse(iterator.hasNext()); + } + + @Test + public void testChainedIterableEmpty() { + Iterable<Iterable<Integer>> iterables = Collections.emptyList(); + Iterable<Integer> chained = IterableUtils.chainedIterable(iterables); + + Iterator<Integer> iterator = chained.iterator(); + Assert.assertFalse(iterator.hasNext()); + } + + @Test + public void testChainedIterableSingle() { + List<Integer> list = Arrays.asList(1, 2, 3); + Iterable<Iterable<Integer>> iterables = Collections.singletonList(list); + Iterable<Integer> chained = IterableUtils.chainedIterable(iterables); + + Iterator<Integer> iterator = chained.iterator(); + List<Integer> expected = Arrays.asList(1, 2, 3); + for (Integer value : expected) { + Assert.assertTrue(iterator.hasNext()); + Assert.assertEquals(value, iterator.next()); + } + Assert.assertFalse(iterator.hasNext()); + } + + @Test + public void testChainedIterableNullElement() { + List<Integer> list1 = Arrays.asList(1, 2, 3); + List<Integer> list2 = null; + List<Integer> list3 = Arrays.asList(6, 7, 8, 9); + + Iterable<Iterable<Integer>> iterables = Arrays.asList(list1, list2, list3); + Iterable<Integer> chained = IterableUtils.chainedIterable(iterables); + + Iterator<Integer> iterator = chained.iterator(); + List<Integer> expected = Arrays.asList(1, 2, 3); + for (Integer value : expected) { + Assert.assertTrue(iterator.hasNext()); + Assert.assertEquals(value, iterator.next()); + } + + // now next iterator should be null + Assert.assertThrows(NullPointerException.class, iterator::hasNext); + } +}
