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

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-collections.git

commit d591c85785eac742e3192d133a673c0a58baa9e7
Author: Gary Gregory <garydgreg...@gmail.com>
AuthorDate: Fri Nov 1 18:00:57 2024 -0400

    Refactor ExtendedIterator and FilterIterator with a new interface
    IteratorOperations
    
    - Inspired by Apache Jena's ExtendedIterator and Claude Warren's
    ExtendedIterator
    - Add missing Javadoc
---
 src/changes/changes.xml                            |   1 +
 .../apache/commons/collections4/IteratorUtils.java |  28 ++++
 .../collections4/iterators/ExtendedIterator.java   | 165 +++++++++------------
 .../collections4/iterators/FilterIterator.java     |  14 +-
 .../collections4/iterators/IteratorOperations.java |  88 +++++++++++
 .../collections4/iterators/FilterIteratorTest.java |  61 ++++++++
 6 files changed, 252 insertions(+), 105 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 009514e3b..ef7095fc2 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -61,6 +61,7 @@
     <action type="fix" dev="ggregory" due-to="Gary Gregory">Add missing test 
AbstractIteratorTest.testForEachRemaining().</action> 
     <action type="fix" dev="ggregory" due-to="Gary Gregory, Claude Warren">Add 
FilterIterator.removeNext() #564.</action>
     <action type="fix" issue="COLLECTIONS-870" dev="ggregory" due-to="Claude 
Warren, Gary Gregory">Added ExtendedIterator and tests #564.</action> 
+    <action type="add" dev="ggregory" due-to="Gary Gregory">Refactor 
ExtendedIterator and FilterIterator with a new interface 
IteratorOperations.</action>
     <!-- UPDATE -->
     <action type="update" dev="ggregory" due-to="Gary Gregory">Bump 
org.apache.commons:commons-parent from 71 to 78 #534, #545, #550 #555, 
#566.</action>
     <action type="update" issue="COLLECTIONS-857" dev="ggregory" 
due-to="Claude Warren">Update bloom filter documentation #508.</action>
diff --git a/src/main/java/org/apache/commons/collections4/IteratorUtils.java 
b/src/main/java/org/apache/commons/collections4/IteratorUtils.java
index 1b6249204..c8b246b3d 100644
--- a/src/main/java/org/apache/commons/collections4/IteratorUtils.java
+++ b/src/main/java/org/apache/commons/collections4/IteratorUtils.java
@@ -28,7 +28,11 @@ import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Spliterator;
+import java.util.Spliterators;
 import java.util.function.IntFunction;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
 import org.apache.commons.collections4.functors.EqualPredicate;
 import org.apache.commons.collections4.iterators.ArrayIterator;
@@ -1226,6 +1230,30 @@ public class IteratorUtils {
         return new SkippingIterator<>(iterator, offset);
     }
 
+    /**
+     * Creates a stream on the given Iterable.
+     *
+     * @param <E> the type of elements in the Iterable.
+     * @param iterable the Iterable to stream or null.
+     * @return a new Stream or {@link Stream#empty()} if the Iterable is null.
+     * @since 4.5.0-M3
+     */
+    public static <E> Stream<E> stream(final Iterable<E> iterable) {
+        return iterable == null ? Stream.empty() : 
StreamSupport.stream(iterable.spliterator(), false);
+    }
+
+    /**
+     * Creates a stream on the given Iterator.
+     *
+     * @param <E> the type of elements in the Iterator.
+     * @param iterator the Iterator to stream or null.
+     * @return a new Stream or {@link Stream#empty()} if the Iterator is null.
+     * @since 4.5.0-M3
+     */
+    public static <E> Stream<E> stream(final Iterator<E> iterator) {
+        return iterator == null ? Stream.empty() : 
StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 
Spliterator.ORDERED), false);
+    }
+
     /**
      * Gets an array based on an iterator.
      * <p>
diff --git 
a/src/main/java/org/apache/commons/collections4/iterators/ExtendedIterator.java 
b/src/main/java/org/apache/commons/collections4/iterators/ExtendedIterator.java
index 7b6733543..662d35b89 100644
--- 
a/src/main/java/org/apache/commons/collections4/iterators/ExtendedIterator.java
+++ 
b/src/main/java/org/apache/commons/collections4/iterators/ExtendedIterator.java
@@ -16,7 +16,6 @@
  */
 package org.apache.commons.collections4.iterators;
 
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.function.Consumer;
@@ -24,24 +23,45 @@ import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Stream;
 
-
 /**
- * Extends Iterator functionality to include operations commonly found on 
streams (e.g. filtering, concatenating, mapping).
- * It also provides convenience methods for common operations.
+ * Extends Iterator functionality to include operations commonly found on 
streams (e.g. filtering, concatenating, mapping). It also provides convenience 
methods
+ * for common operations.
+ *
  * @param <T> The type of object returned from the iterator.
  * @since 4.5.0-M3
  */
-public final class ExtendedIterator<T> implements Iterator<T> {
+public final class ExtendedIterator<T> implements IteratorOperations<T> {
+
     /**
-     * Set to <code>true</code> if this wrapping doesn't permit the use of
-     * {@link #remove()}, otherwise removal is delegated to the base iterator.
+     * Create an ExtendedIterator returning the elements of <code>it</code>. 
If <code>it</code> is itself an ExtendedIterator, return that; otherwise wrap
+     * <code>it</code>.
+     *
+     * @param <T> The type of object returned from the iterator.
+     * @param it The iterator to wrap.
+     * @return An Extended iterator wrapping {@code it}
      */
-    private final boolean throwOnRemove;
+    public static <T> ExtendedIterator<T> create(final Iterator<T> it) {
+        return it instanceof ExtendedIterator<?> ? (ExtendedIterator<T>) it : 
new ExtendedIterator<>(it, false);
+    }
 
     /**
-     * Creates an ExtendedIterator wrapped round <code>it</code>,
-     * which does not permit <code>.remove()</code>
-     * even if <code>it</code> does.
+     * Creates an ExtendedIterator wrapped round a {@link Stream}. The 
extended iterator does not permit <code>.remove()</code>.
+     * <p>
+     * The stream should not be used directly. The effect of doing so is 
undefined.
+     * </p>
+     *
+     * @param <T> The type of object returned from the iterator.
+     * @param stream the Stream to create an iterator from.
+     * @return an Extended iterator on the {@code stream} iterator.
+     */
+    public static <T> ExtendedIterator<T> create(final Stream<T> stream) {
+        return new ExtendedIterator<>(stream.iterator(), true);
+    }
+
+    /**
+     * Creates an ExtendedIterator wrapped round <code>it</code>, which does 
not permit <code>.remove()</code> even if <code>it</code> does.
+     *
+     * @param <T> The type of object returned from the iterator.
      * @param it The Iterator to wrap.
      * @return an Extended iterator on {@code it}
      * @throws UnsupportedOperationException if remove() is called on the 
resulting iterator.
@@ -51,22 +71,18 @@ public final class ExtendedIterator<T> implements 
Iterator<T> {
     }
 
     /**
-     * Creates an ExtendedIterator wrapped round a {@link Stream}.
-     * The extended iterator does not permit <code>.remove()</code>.
-     * <p>
-     * The stream should not be used directly. The effect of doing so is
-     * undefined.
-     * </p>
-     * @param stream the Stream to create an iterator from.
-     * @return an Extended iterator on the {@code stream} iterator.
+     * Creates an empty Extended iterator.
+     *
+     * @return An empty Extended iterator.
      */
-    public static <T> ExtendedIterator<T> create(final Stream<T> stream) {
-        return new ExtendedIterator<T>(stream.iterator(), true);
+    public static ExtendedIterator<?> emptyIterator() {
+        return new ExtendedIterator<>(Collections.emptyIterator(), false);
     }
 
     /**
-     * Flattens an iterator of iterators into an Iterator over the next level 
values.
-     * Similar to list splicing in lisp.
+     * Flattens an iterator of iterators into an Iterator over the next level 
values. Similar to list splicing in lisp.
+     *
+     * @param <T> The type of object returned from the iterator.
      * @param iterators An iterator of iterators.
      * @return An iterator over the logical concatenation of the inner 
iterators.
      */
@@ -82,32 +98,17 @@ public final class ExtendedIterator<T> implements 
Iterator<T> {
     }
 
     /**
-     * Creates an empty Extended iterator.
-     * @return An empty Extended iterator.
-     */
-    public static ExtendedIterator<?> emptyIterator() {
-        return new ExtendedIterator<>(Collections.emptyIterator(), false);
-    }
-
-    /**
-     * Create an ExtendedIterator returning the elements of <code>it</code>.
-     * If <code>it</code> is itself an ExtendedIterator, return that;
-     * otherwise wrap <code>it</code>.
-     * @param it The iterator to wrap.
-     * @return An Extended iterator wrapping {@code it}
+     * Set to <code>true</code> if this wrapping doesn't permit the use of 
{@link #remove()}, otherwise removal is delegated to the base iterator.
      */
-    public static <T> ExtendedIterator<T> create(final Iterator<T> it) {
-        return it instanceof ExtendedIterator<?>
-                ? (ExtendedIterator<T>) it
-                : new ExtendedIterator<>(it, false);
-    }
+    private final boolean throwOnRemove;
 
-    /** the base iterator that we wrap */
+    /** The base iterator that we wrap */
     private final Iterator<? extends T> base;
 
     /**
-     * Initialise this wrapping with the given base iterator and 
remove-control.
-     * @param base the base iterator that this iterator wraps
+     * Initialize this wrapping with the given base iterator and 
remove-control.
+     *
+     * @param base          the base iterator that this iterator wraps
      * @param throwOnRemove true if .remove() must throw an exception
      */
     private ExtendedIterator(final Iterator<? extends T> base, final boolean 
throwOnRemove) {
@@ -115,41 +116,9 @@ public final class ExtendedIterator<T> implements 
Iterator<T> {
         this.throwOnRemove = throwOnRemove;
     }
 
-    @Override
-    public boolean hasNext() {
-        return base.hasNext();
-    }
-
-    @Override
-    public T next() {
-        return base.next();
-    }
-
-    @Override
-    public void forEachRemaining(final Consumer<? super T> action) {
-        base.forEachRemaining(action);
-    }
-
-    @Override
-    public void remove() {
-        if (throwOnRemove) {
-            throw new UnsupportedOperationException();
-        }
-        base.remove();
-    }
-
-    /**
-     * Returns the next item and removes it from the iterator.
-     * @return the next item from the iterator.
-     */
-    public T removeNext() {
-        T result = next();
-        remove();
-        return result;
-    }
-
     /**
      * Chains the {@code other} iterator to the end of this one.
+     *
      * @param other the other iterator to extend this iterator with.
      * @return A new iterator returning the contents of {@code this} iterator 
followed by the contents of {@code other} iterator.
      * @param <X> The type of object returned from the other iterator.
@@ -159,38 +128,50 @@ public final class ExtendedIterator<T> implements 
Iterator<T> {
             ((IteratorChain<T>) base).addIterator(other);
             return this;
         }
-        return new ExtendedIterator<T>(new IteratorChain<T>(this.base, other), 
this.throwOnRemove);
+        return new ExtendedIterator<>(new IteratorChain<>(this.base, other), 
this.throwOnRemove);
     }
 
     /**
-     * Filter this iterator using a predicate.  Only items for which the 
predicate returns {@code true} will
-     * be included in the result.
+     * Filter this iterator using a predicate. Only items for which the 
predicate returns {@code true} will be included in the result.
+     *
      * @param predicate The predicate to filter the items with.
      * @return An iterator filtered by the predicate.
      */
     public ExtendedIterator<T> filter(final Predicate<T> predicate) {
-        return new ExtendedIterator<T>(new FilterIterator<>(this, 
predicate::test), this.throwOnRemove);
+        return new ExtendedIterator<>(new FilterIterator<>(this, 
predicate::test), this.throwOnRemove);
+    }
+
+    @Override
+    public void forEachRemaining(final Consumer<? super T> action) {
+        base.forEachRemaining(action);
+    }
+
+    @Override
+    public boolean hasNext() {
+        return base.hasNext();
     }
 
     /**
      * Map the elements of the iterator to a now type.
+     *
      * @param function The function to map elements of {@code <T>} to type 
{@code <U>}.
      * @return An Extended iterator that returns a {@code <U>} for very {@code 
<T>} in the original iterator.
      * @param <U> The object type to return.
      */
     public <U> ExtendedIterator<U> map(final Function<T, U> function) {
-        return new ExtendedIterator<U>(new TransformIterator<>(this, 
function::apply), false);
+        return new ExtendedIterator<>(new TransformIterator<>(this, 
function::apply), false);
     }
 
-    /**
-     * A method to add the remaining elements in the iterator an arbitrary 
collection.
-     * This method consumes the iterator.
-     * @param collection THe collection to add elements to.
-     * @return the {@code collection} with the elements added.
-     * @param <U> A collection of objects of type {@code <T>}.
-     */
-    public <U extends Collection<T>> U addTo(final U collection) {
-        this.forEachRemaining(collection::add);
-        return collection;
+    @Override
+    public T next() {
+        return base.next();
+    }
+
+    @Override
+    public void remove() {
+        if (throwOnRemove) {
+            throw new UnsupportedOperationException();
+        }
+        base.remove();
     }
 }
diff --git 
a/src/main/java/org/apache/commons/collections4/iterators/FilterIterator.java 
b/src/main/java/org/apache/commons/collections4/iterators/FilterIterator.java
index e9872a2b1..9fc280cd2 100644
--- 
a/src/main/java/org/apache/commons/collections4/iterators/FilterIterator.java
+++ 
b/src/main/java/org/apache/commons/collections4/iterators/FilterIterator.java
@@ -32,7 +32,7 @@ import org.apache.commons.collections4.functors.TruePredicate;
  * @param <E> the type of elements returned by this iterator.
  * @since 1.0
  */
-public class FilterIterator<E> implements Iterator<E> {
+public class FilterIterator<E> implements IteratorOperations<E> {
 
     /** The iterator to be filtered. */
     private Iterator<? extends E> iterator;
@@ -141,18 +141,6 @@ public class FilterIterator<E> implements Iterator<E> {
         iterator.remove();
     }
 
-    /**
-     * Returns the next item and removes it from the iterator.
-     *
-     * @return the next item from the iterator.
-     * @since 4.5.0-M3
-     */
-    public E removeNext() {
-        final E result = next();
-        remove();
-        return result;
-    }
-
     private Predicate<? super E> safePredicate(final Predicate<? super E> 
predicate) {
         return predicate != null ? predicate : TruePredicate.truePredicate();
     }
diff --git 
a/src/main/java/org/apache/commons/collections4/iterators/IteratorOperations.java
 
b/src/main/java/org/apache/commons/collections4/iterators/IteratorOperations.java
new file mode 100644
index 000000000..54056e2cb
--- /dev/null
+++ 
b/src/main/java/org/apache/commons/collections4/iterators/IteratorOperations.java
@@ -0,0 +1,88 @@
+/*
+ * 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.commons.collections4.iterators;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Supplier;
+
+/**
+ * Extends {@link Iterator} with additional default methods.
+ *
+ * @param <E> the type of elements returned by this iterator.
+ * @since 4.5.0-M3
+ */
+public interface IteratorOperations<E> extends Iterator<E> {
+
+    /**
+     * Adds the remaining elements in the iterator to an arbitrary {@link 
Collection}. This method consumes the iterator.
+     *
+     * @param collection The target collection to add elements to.
+     * @return the given {@code collection}.
+     * @param <C> A collection of objects of type {@code <E>}.
+     */
+    default <C extends Collection<E>> C addTo(final C collection) {
+        forEachRemaining(collection::add);
+        return collection;
+    }
+
+    /**
+     * Returns the next item and removes it from the iterator.
+     *
+     * @return the next item from the iterator.
+     */
+    default E removeNext() {
+        final E result = next();
+        remove();
+        return result;
+    }
+
+    /**
+     * Adds the remaining elements in the iterator to a new {@link Collection} 
provided by the supplier. This method consumes the iterator.
+     *
+     * @param collectionSupplier supplies a collection target.
+     * @param <C> the collection type.
+     * @return a new Collection containing the remaining elements of this 
instance.
+     */
+    default <C extends Collection<E>> C toCollection(final Supplier<C> 
collectionSupplier) {
+        return addTo(collectionSupplier.get());
+    }
+
+    /**
+     * Adds the remaining elements in the iterator to a new {@link List}. This 
method consumes the iterator.
+     *
+     * @return a new List containing the remaining elements of this instance.
+     */
+    default List<E> toList() {
+        return toCollection(ArrayList::new);
+    }
+
+    /**
+     * Adds the remaining elements in the iterator to a new {@link Set}. This 
method consumes the iterator.
+     *
+     * @return a new Set containing the remaining elements of this instance.
+     */
+    default Set<E> toSet() {
+        return toCollection(HashSet::new);
+    }
+
+}
diff --git 
a/src/test/java/org/apache/commons/collections4/iterators/FilterIteratorTest.java
 
b/src/test/java/org/apache/commons/collections4/iterators/FilterIteratorTest.java
index 582e79a4d..ed28a538e 100644
--- 
a/src/test/java/org/apache/commons/collections4/iterators/FilterIteratorTest.java
+++ 
b/src/test/java/org/apache/commons/collections4/iterators/FilterIteratorTest.java
@@ -16,17 +16,22 @@
  */
 package org.apache.commons.collections4.iterators;
 
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Deque;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.Set;
 
 import org.apache.commons.collections4.IteratorUtils;
 import org.apache.commons.collections4.Predicate;
@@ -125,6 +130,38 @@ public class FilterIteratorTest<E> extends 
AbstractIteratorTest<E> {
         iterator = null;
     }
 
+    @Test
+    public void testAddTo() {
+        final List<E> expected = new ArrayList<>(list);
+        expected.addAll(list);
+        final FilterIterator<E> filterIterator = new 
FilterIterator<>(list.iterator());
+        final List<E> actual = filterIterator.addTo(new ArrayList<>(list));
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testAddToCollection() {
+        final List<E> expected = new ArrayList<>(list);
+        expected.addAll(list);
+        final FilterIterator<E> filterIterator = new 
FilterIterator<>(list.iterator());
+        final List<E> actual = filterIterator.toCollection(() -> new 
ArrayList<>(list));
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testAddToEmpty() {
+        final FilterIterator<E> filterIterator = makeEmptyIterator();
+        final List<E> actual = filterIterator.addTo(new ArrayList<>(list));
+        assertEquals(list, actual);
+    }
+
+    @Test
+    public void testAddToEmptyToEmpty() {
+        final FilterIterator<E> filterIterator = makeEmptyIterator();
+        final List<E> actual = filterIterator.addTo(new ArrayList<>());
+        assertTrue(actual.isEmpty());
+    }
+
     /**
      * Tests a predicate that accepts some but not all elements.
      */
@@ -254,6 +291,30 @@ public class FilterIteratorTest<E> extends 
AbstractIteratorTest<E> {
         assertFalse(filterIterator.hasNext());
     }
 
+    @Test
+    public void testToCollectionAsDeque() {
+        final Deque<E> expected = new ArrayDeque<>(list);
+        final FilterIterator<E> filterIterator = new 
FilterIterator<>(list.iterator());
+        final Deque<E> actual = filterIterator.toCollection(ArrayDeque::new);
+        assertArrayEquals(expected.toArray(), actual.toArray());
+    }
+
+    @Test
+    public void testToList() {
+        final List<E> expected = new ArrayList<>(list);
+        final FilterIterator<E> filterIterator = new 
FilterIterator<>(list.iterator());
+        final List<E> actual = filterIterator.toList();
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testToSet() {
+        final Set<E> expected = new HashSet<>(list);
+        final FilterIterator<E> filterIterator = new 
FilterIterator<>(list.iterator());
+        final Set<E> actual = filterIterator.toSet();
+        assertEquals(expected, actual);
+    }
+
     private void verifyElementsInPredicate(final String[] elements) {
         final Predicate<E> pred = x -> {
             for (final String element : elements) {

Reply via email to