This is an automated email from the ASF dual-hosted git repository.
daim pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
The following commit(s) were added to refs/heads/trunk by this push:
new cd8c0f30f3 OAK-11680 : added Iterators.cycle replacement in
oak-commons (#2250)
cd8c0f30f3 is described below
commit cd8c0f30f354b37e2b007cb926e83928b02be1e8
Author: Rishabh Kumar <[email protected]>
AuthorDate: Tue Apr 29 11:22:27 2025 +0530
OAK-11680 : added Iterators.cycle replacement in oak-commons (#2250)
Co-authored-by: Rishabh Kumar <[email protected]>
---
.../oak/commons/collections/CollectionUtils.java | 29 ++++++
.../oak/commons/collections/IteratorUtils.java | 45 +++++++++
.../commons/collections/CollectionUtilsTest.java | 76 ++++++++++++++-
.../oak/commons/collections/IteratorUtilsTest.java | 105 +++++++++++++++++++++
4 files changed, 251 insertions(+), 4 deletions(-)
diff --git
a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/CollectionUtils.java
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/CollectionUtils.java
index a20affce1c..1c3bb714e2 100644
---
a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/CollectionUtils.java
+++
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/CollectionUtils.java
@@ -18,6 +18,8 @@
*/
package org.apache.jackrabbit.oak.commons.collections;
+import java.util.Collection;
+
/**
* Utility methods for collections conversions.
*/
@@ -49,4 +51,31 @@ public class CollectionUtils {
return 1 + (int) (capacity / 0.75f);
}
+
+ /**
+ * Converts an Iterable to a Collection.
+ * <p>
+ * If the provided iterable is already a Collection, it is simply cast and
returned.
+ * Otherwise, the elements from the iterable are copied into a new List
using
+ * {@link ListUtils#toList(Iterable)}.
+ * <p>
+ * Example usage:
+ * <pre>
+ * Iterable<String> iterable = () -> Arrays.asList("a", "b",
"c").iterator();
+ * Collection<String> collection =
CollectionUtils.asCollection(iterable);
+ * // collection will contain "a", "b", "c"
+ * </pre>
+ *
+ * @param <E> the type of elements in the iterable
+ * @param iterable the iterable to convert, must not be null
+ * @return a Collection containing all elements of the iterable
+ * @throws NullPointerException if the iterable is null
+ */
+ @SuppressWarnings("unchecked")
+ static <E> Collection<E> toCollection(final Iterable<? extends E>
iterable) {
+ if (iterable instanceof Collection) {
+ return (Collection<E>) iterable;
+ }
+ return ListUtils.toList(iterable);
+ }
}
\ No newline at end of file
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 214e404b47..b6830593e4 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
@@ -429,5 +429,50 @@ public class IteratorUtils {
public static <F, T> Iterator<T> transform(Iterator<? extends F> itr,
final Function<? super F, ? extends T> transform) {
return
org.apache.commons.collections4.IteratorUtils.transformedIterator(itr,
transform::apply);
}
+
+ /**
+ * Creates an iterator that cycles indefinitely over the provided elements.
+ * <p>
+ * The returned iterator will continuously loop through the given elements
in the same order.
+ * If no elements are provided, the iterator will be empty.
+ * <p>
+ * Example usage:
+ * <pre>
+ * Iterator<String> cyclingIterator = IteratorUtils.cycle("a", "b",
"c");
+ * // Iterates: "a", "b", "c", "a", "b", "c", ...
+ * </pre>
+ *
+ * @param <E> the type of elements in the iterator
+ * @param elements the elements to cycle through, must not be null
+ * @return an iterator that cycles indefinitely over the provided elements
+ * @throws NullPointerException if the elements array is null
+ */
+ @SafeVarargs
+ public static <E> Iterator<E> cycle(final E... elements) {
+ Objects.requireNonNull(elements, "elements must not be null");
+ return IteratorUtils.cycle(SetUtils.toLinkedSet(elements));
+ }
+
+ /**
+ * Creates an iterator that cycles indefinitely over the elements of the
given iterable.
+ * <p>
+ * The returned iterator will continuously loop through the elements of
the iterable in the same order.
+ * If the iterable is empty, the iterator will also be empty.
+ * <p>
+ * Example usage:
+ * <pre>
+ * List<String> list = Arrays.asList("a", "b", "c");
+ * Iterator<String> cyclingIterator = IteratorUtils.cycle(list);
+ * // Iterates: "a", "b", "c", "a", "b", "c", ...
+ * </pre>
+ *
+ * @param <E> the type of elements in the iterable
+ * @param iterable the iterable to cycle through, must not be null
+ * @return an iterator that cycles indefinitely over the elements of the
iterable
+ * @throws NullPointerException if the iterable is null
+ */
+ public static <E> Iterator<E> cycle(final Iterable<E> iterable) {
+ return
org.apache.commons.collections4.IteratorUtils.loopingIterator(CollectionUtils.toCollection(iterable));
+ }
}
diff --git
a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/CollectionUtilsTest.java
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/CollectionUtilsTest.java
index 65d97851be..4b798fbbe8 100644
---
a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/CollectionUtilsTest.java
+++
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/CollectionUtilsTest.java
@@ -21,7 +21,10 @@ package org.apache.jackrabbit.oak.commons.collections;
import org.junit.Assert;
import org.junit.Test;
-import static org.junit.Assert.fail;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
public class CollectionUtilsTest {
@@ -37,9 +40,74 @@ public class CollectionUtilsTest {
Assert.assertEquals(1073741824, capacity);
}
- @Test(expected = IllegalArgumentException.class)
+ @Test
public void ensureCapacityWithNegativeValue() {
- int capacity = CollectionUtils.ensureCapacity(-8);
- fail("Should throw IllegalArgumentException");
+ Assert.assertThrows(IllegalArgumentException.class, () ->
CollectionUtils.ensureCapacity(-8));
+ }
+
+ @Test
+ public void testToCollectionWithCollection() {
+ // Create a Collection
+ Collection<String> original = Arrays.asList("a", "b", "c");
+
+ // Convert to Collection
+ Collection<String> result = CollectionUtils.toCollection(original);
+
+ // Verify it's the same instance
+ Assert.assertSame(original, result);
+ Assert.assertEquals(Arrays.asList("a", "b", "c"), result);
+ }
+
+ @Test
+ public void testToCollectionWithNonCollection() {
+ // Create a non-Collection Iterable using custom implementation
+ Iterable<Integer> iterable = () -> Arrays.asList(1, 2, 3).iterator();
+
+ // Convert to Collection
+ Collection<Integer> result = CollectionUtils.toCollection(iterable);
+
+ // Verify it created a new List with the correct elements
+ Assert.assertTrue(result instanceof List);
+ Assert.assertEquals(Arrays.asList(1, 2, 3), result);
+ }
+
+ @Test
+ public void testToCollectionWithNull() {
+ Assert.assertThrows(NullPointerException.class, () ->
CollectionUtils.toCollection(null));
+ }
+
+ @Test
+ public void testToCollectionWithEmptyIterable() {
+ Iterable<String> empty = Collections.emptyList();
+ Collection<String> result = CollectionUtils.toCollection(empty);
+
+ Assert.assertTrue(result.isEmpty());
+ Assert.assertEquals(0, result.size());
+ }
+
+ @Test
+ public void testToCollectionWithCustomIterable() {
+ // Create a custom Iterable
+ Iterable<Character> chars = () -> Arrays.asList('a', 'b',
'c').iterator();
+
+ Collection<Character> result = CollectionUtils.toCollection(chars);
+
+ Assert.assertEquals(3, result.size());
+ Assert.assertTrue(result.contains('a'));
+ Assert.assertTrue(result.contains('b'));
+ Assert.assertTrue(result.contains('c'));
+ }
+
+ @Test
+ public void testToCollectionMutability() {
+ // Create a non-Collection Iterable
+ Iterable<String> iterable = () -> Arrays.asList("a", "b").iterator();
+
+ // Convert to Collection
+ Collection<String> result = CollectionUtils.toCollection(iterable);
+
+ // Verify the resulting collection is mutable
+ result.add("c");
+ Assert.assertEquals(Arrays.asList("a", "b", "c"), result);
}
}
\ No newline at end of file
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 bb549e068c..ddcd9a8a7b 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
@@ -1030,4 +1030,109 @@ public class IteratorUtilsTest {
Assert.assertEquals(Arrays.asList(4, 4, 4, 4), result);
}
+
+ @Test
+ public void testCycleWithNonEmptyElements() {
+ Iterator<String> cyclingIterator = IteratorUtils.cycle("a", "b", "c");
+
+ Assert.assertTrue(cyclingIterator.hasNext());
+ Assert.assertEquals("a", cyclingIterator.next());
+ Assert.assertEquals("b", cyclingIterator.next());
+ Assert.assertEquals("c", cyclingIterator.next());
+ Assert.assertEquals("a", cyclingIterator.next()); // Cycles back
+ Assert.assertEquals("b", cyclingIterator.next()); // Cycles back
+ Assert.assertEquals("c", cyclingIterator.next()); // Cycles back
+ }
+
+ @Test
+ public void testCycleWithSingleElement() {
+ Iterator<Integer> cyclingIterator = IteratorUtils.cycle(42);
+
+ Assert.assertTrue(cyclingIterator.hasNext());
+ Assert.assertEquals(Integer.valueOf(42), cyclingIterator.next());
+ Assert.assertEquals(Integer.valueOf(42), cyclingIterator.next()); //
Repeats
+ }
+
+ @Test
+ public void testCycleWithEmptyElements() {
+ Iterator<Object> cyclingIterator = IteratorUtils.cycle();
+
+ Assert.assertFalse(cyclingIterator.hasNext());
+ }
+
+ @Test
+ public void testCycleWithNullElements() {
+ Assert.assertThrows(NullPointerException.class,() ->
IteratorUtils.cycle((String[]) null));
+ }
+
+ @Test
+ public void testCycleWithRemove() {
+ Iterator<String> cyclingIterator = IteratorUtils.cycle("x", "y");
+
+ Assert.assertEquals("x", cyclingIterator.next());
+ cyclingIterator.remove(); // Should remove "x"
+ Assert.assertEquals("y", cyclingIterator.next());
+ Assert.assertEquals("y", cyclingIterator.next());
+ cyclingIterator.remove(); // Should remove "y"
+ Assert.assertFalse(cyclingIterator.hasNext());
+ }
+
+ @Test
+ public void testCycleWithNonEmptyIterable() {
+ List<String> list = Arrays.asList("a", "b", "c");
+ Iterator<String> cyclingIterator = IteratorUtils.cycle(list);
+
+ Assert.assertTrue(cyclingIterator.hasNext());
+ Assert.assertEquals("a", cyclingIterator.next());
+ Assert.assertEquals("b", cyclingIterator.next());
+ Assert.assertEquals("c", cyclingIterator.next());
+ Assert.assertEquals("a", cyclingIterator.next()); // Cycles back
+ }
+
+ @Test
+ public void testCycleWithSingleElementIterable() {
+ List<Integer> list = Collections.singletonList(42);
+ Iterator<Integer> cyclingIterator = IteratorUtils.cycle(list);
+
+ Assert.assertTrue(cyclingIterator.hasNext());
+ Assert.assertEquals(Integer.valueOf(42), cyclingIterator.next());
+ Assert.assertEquals(Integer.valueOf(42), cyclingIterator.next()); //
Repeats
+ }
+
+ @Test
+ public void testCycleWithEmptyIterable() {
+ List<Object> emptyList = Collections.emptyList();
+ Iterator<Object> cyclingIterator = IteratorUtils.cycle(emptyList);
+
+ Assert.assertFalse(cyclingIterator.hasNext());
+ }
+
+ @Test
+ public void testCycleWithNullIterable() {
+ Assert.assertThrows(NullPointerException.class, () ->
IteratorUtils.cycle((Iterable<String>) null));
+ }
+
+ @Test
+ public void testCycleWithRemoveWhenIterableAllows() {
+ List<String> list = new ArrayList<>(Arrays.asList("x", "y"));
+ Iterator<String> cyclingIterator = IteratorUtils.cycle(list);
+
+ Assert.assertEquals("x", cyclingIterator.next());
+ cyclingIterator.remove(); // Removes "x"
+ Assert.assertEquals("y", cyclingIterator.next());
+ cyclingIterator.remove(); // Removes "y"
+ Assert.assertFalse(cyclingIterator.hasNext());
+ }
+
+ @Test
+ public void testCycleWithRemoveWhenIterableDisallows() {
+ List<String> list = Arrays.asList("x", "y");
+ Iterator<String> cyclingIterator = IteratorUtils.cycle(list);
+
+ Assert.assertEquals("x", cyclingIterator.next());
+ Assert.assertThrows(UnsupportedOperationException.class,
cyclingIterator::remove); // Doesn't Removes "x"
+ Assert.assertEquals("y", cyclingIterator.next());
+ Assert.assertEquals("x", cyclingIterator.next());
+ Assert.assertTrue(cyclingIterator.hasNext());
+ }
}