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 7bda86b8dc OAK-11674 : added Iterators.filter replacement in oak-commons (#2245) 7bda86b8dc is described below commit 7bda86b8dc417ea6631759b901d904e34df281a7 Author: Rishabh Kumar <rishabhdaim1...@gmail.com> AuthorDate: Thu Apr 24 15:58:14 2025 +0530 OAK-11674 : added Iterators.filter replacement in oak-commons (#2245) Co-authored-by: Rishabh Kumar <d...@adobe.com> --- .../oak/commons/collections/IteratorUtils.java | 30 ++++++ .../oak/commons/collections/IteratorUtilsTest.java | 114 +++++++++++++++++++++ 2 files changed, 144 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 b48e109a61..6d7d62fccf 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 @@ -30,6 +30,7 @@ import java.util.NoSuchElementException; import java.util.Objects; import java.util.PriorityQueue; import java.util.Queue; +import java.util.function.Predicate; /** * Utility methods for {@link Iterator} conversions. @@ -369,4 +370,33 @@ public class IteratorUtils { iterators.forEachRemaining(eIteratorChain::addIterator); return eIteratorChain; } + + /** + * Returns an iterator containing only the elements that match the given predicate. + * <p> + * This method creates a new iterator that will iterate through elements from the + * source iterator but only return elements that satisfy the specified predicate. + * The filtering occurs during iteration and the method doesn't consume the source iterator + * until the returned iterator is advanced. + * <p> + * Example usage: + * <pre> + * Iterator<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5).iterator(); + * Predicate<Integer> isEven = n -> n % 2 == 0; + * Iterator<Integer> evenNumbers = IteratorUtils.filter(numbers, isEven); + * // evenNumbers will iterate through 2, 4 + * </pre> + * <p> + * The returned iterator supports {@link Iterator#remove()} if the source iterator supports it. + * + * @param <T> the type of objects in the iterator + * @param itr the source iterator, must not be null + * @param predicate the predicate to apply to each element, must not be null + * @return a filtered iterator + * @throws NullPointerException if either the iterator or predicate is null + */ + public static <T> Iterator<T> filter(final Iterator<? extends T> itr, final Predicate<? super T> predicate) { + return org.apache.commons.collections4.IteratorUtils.filteredIterator(itr, predicate::test); + } } + 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 47a4393823..faa04276ec 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 @@ -817,4 +817,118 @@ public class IteratorUtilsTest { Assert.assertThrows(NullPointerException.class, () -> IteratorUtils.chainedIterator(new ArrayList<>(Arrays.asList(iterator1, iterator2, null)).iterator())); } + + @Test + public void testFilterWithMatchingElements() { + List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); + Iterator<Integer> filtered = IteratorUtils.filter(list.iterator(), n -> n % 2 == 0); + + List<Integer> result = new ArrayList<>(); + filtered.forEachRemaining(result::add); + + Assert.assertEquals(Arrays.asList(2, 4), result); + } + + @Test + public void testFilterWithNoMatchingElements() { + List<String> list = Arrays.asList("apple", "banana", "cherry"); + Iterator<String> filtered = IteratorUtils.filter(list.iterator(), s -> s.startsWith("d")); + + Assert.assertFalse(filtered.hasNext()); + } + + @Test + public void testFilterWithAllMatchingElements() { + List<Integer> list = Arrays.asList(10, 20, 30, 40); + Iterator<Integer> filtered = IteratorUtils.filter(list.iterator(), n -> n > 0); + + List<Integer> result = new ArrayList<>(); + filtered.forEachRemaining(result::add); + + Assert.assertEquals(list, result); + } + + @Test + public void testFilterWithEmptyIterator() { + Iterator<String> emptyIterator = Collections.emptyIterator(); + Iterator<String> filtered = IteratorUtils.filter(emptyIterator, s -> true); + + Assert.assertFalse(filtered.hasNext()); + } + + @Test + public void testFilterWithNullIterator() { + Assert.assertThrows(NullPointerException.class, () -> IteratorUtils.filter(null, item -> true)); + } + + @Test + public void testFilterWithNullPredicate() { + Iterator<String> iterator = Arrays.asList("a", "b").iterator(); + Assert.assertThrows(NullPointerException.class, () -> IteratorUtils.filter(iterator, null)); + } + + @Test + public void testFilterWithRemove() { + List<String> list = new ArrayList<>(Arrays.asList("keep", "remove", "keep")); + Iterator<String> filtered = IteratorUtils.filter(list.iterator(), "keep"::equals); + + // First element matches + Assert.assertTrue(filtered.hasNext()); + Assert.assertEquals("keep", filtered.next()); + filtered.remove(); + + // Skip "remove" as it doesn't match + Assert.assertTrue(filtered.hasNext()); + Assert.assertEquals("keep", filtered.next()); + + Assert.assertEquals(Arrays.asList("remove", "keep"), list); + } + + @Test + public void testFilterWithNullElements() { + List<String> list = Arrays.asList("a", null, "b", null, "c"); + Iterator<String> filtered = IteratorUtils.filter(list.iterator(), Objects::isNull); + + List<String> result = new ArrayList<>(); + filtered.forEachRemaining(result::add); + + Assert.assertEquals(Arrays.asList(null, null), result); + } + + @Test + public void testFilterWithCustomObjects() { + class Person { + private final String name; + private final int age; + + Person(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + @Override + public String toString() { + return name; + } + } + + List<Person> people = Arrays.asList( + new Person("Alice", 25), + new Person("Bob", 17), + new Person("Charlie", 30), + new Person("David", 16) + ); + + // Filter adults (age >= 18) + Iterator<Person> adults = IteratorUtils.filter(people.iterator(), p -> p.getAge() >= 18); + + List<String> adultNames = new ArrayList<>(); + adults.forEachRemaining(p -> adultNames.add(p.toString())); + + Assert.assertEquals(Arrays.asList("Alice", "Charlie"), adultNames); + } }