From 4499cf08620c412556a9721f98451d5e882d2c80 Mon Sep 17 00:00:00 2001 From: Brent Worden Date: Wed, 25 May 2011 18:04:29 +0000 Subject: [PATCH] COLLECTIONS-213. Added multiple use support to IteratorIterable. git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1127604 13f79535-47bb-0310-9956-ffa450edef68 --- .../commons/collections/IteratorUtils.java | 20 ++++- .../iterators/IteratorIterable.java | 74 +++++++++++++++++-- .../collections/TestIteratorUtils.java | 37 ++++++++++ .../iterators/TestIteratorIterable.java | 30 +++++++- 4 files changed, 150 insertions(+), 11 deletions(-) diff --git a/src/java/org/apache/commons/collections/IteratorUtils.java b/src/java/org/apache/commons/collections/IteratorUtils.java index c1b5f1cd8..81da617a5 100644 --- a/src/java/org/apache/commons/collections/IteratorUtils.java +++ b/src/java/org/apache/commons/collections/IteratorUtils.java @@ -759,7 +759,8 @@ public class IteratorUtils { } /** - * Gets an iterable that wraps an iterator. + * Gets an iterable that wraps an iterator. The returned iterable can be + * used for a single iteration. * * @param iterator the iterator to use, not null * @return a new, single use iterable @@ -769,7 +770,22 @@ public class IteratorUtils { if (iterator == null) { throw new NullPointerException("Iterator must not be null"); } - return new IteratorIterable(iterator); + return new IteratorIterable(iterator, false); + } + + /** + * Gets an iterable that wraps an iterator. The returned iterable can be + * used for multiple iterations. + * + * @param iterator the iterator to use, not null + * @return a new, multiple use iterable + * @throws NullPointerException if iterator is null + */ + public static Iterable asMultipleUseIterable(Iterator iterator) { + if (iterator == null) { + throw new NullPointerException("Iterator must not be null"); + } + return new IteratorIterable(iterator, true); } /** diff --git a/src/java/org/apache/commons/collections/iterators/IteratorIterable.java b/src/java/org/apache/commons/collections/iterators/IteratorIterable.java index 23e78b35c..1c28e5fc6 100644 --- a/src/java/org/apache/commons/collections/iterators/IteratorIterable.java +++ b/src/java/org/apache/commons/collections/iterators/IteratorIterable.java @@ -15,11 +15,47 @@ package org.apache.commons.collections.iterators; import java.util.Iterator; +import org.apache.commons.collections.ResettableIterator; + /** * Adapter to make an {@link Iterator Iterator} instance appear to be an - * {@link Iterable Iterable} instance. Unlike normal iterable instance, the - * {@link #iterator()} method always returns the same iterator instance. This - * prohibits this iterator to be only usable for one iterative operation. + * {@link Iterable Iterable} instance. The iterable can be constructed in one + * of two variants: single use, multiple use. + * + *

+ * In the single use iterable case, the iterable is only usable for one + * iterative operation over the source iterator. Subsequent iterative + * operations use the same, exhausted source iterator. To create a single use + * iterable, construct a new {@link IteratorIterable} using a {@link Iterator} + * that is NOT a {@link ResettableIterator} iterator: + *

+ *   Iterator iterator = // some non-resettable iterator
+ *   Iterable iterable = new IteratorIterable(iterator);
+ * 
+ *

+ * + *

+ * In the multiple use iterable case, the iterable is usable for any number of + * iterative operations over the source iterator. Of special note, even though + * the iterable supports multiple iterations, it does not support concurrent + * iterations. To implicitly create a multiple use iterable, construct a new + * {@link IteratorIterable} using a {@link ResettableIterator} iterator: + *

+ *   Integer[] array = {Integer.valueOf(1),Integer.valueOf(2),Integer.valueOf(3)};
+ *   Iterator iterator = IteratorUtils.arrayIterator(array); // a resettable iterator
+ *   Iterable iterable = new IteratorIterable(iterator);
+ * 
+ *

+ * + *

+ * A multiple use iterable can also be explicitly constructed using any + * {@link Iterator} and specifying true for the + * multipleUse flag: + *

+ *   Iterator iterator = // some non-resettable iterator
+ *   Iterable iterable = new IteratorIterable(iterator, true);
+ * 
+ *

* * @since Commons Collections 4.0 * @version $Revision: $ $Date: $ @@ -47,9 +83,12 @@ public class IteratorIterable implements Iterable { }; } - /** the iterator being used. */ - private final Iterator iterator; - + /** the iterator being adapted into an iterable. */ + private final Iterator iterator; + + /** the iterator parameterized as the {@link #iterator()} return type. */ + private final Iterator typeSafeIterator; + /** * Constructs a new IteratorIterable that will use the given * iterator. @@ -57,8 +96,24 @@ public class IteratorIterable implements Iterable { * @param iterator the iterator to use. */ public IteratorIterable(Iterator iterator) { + this(iterator, false); + } + + /** + * Constructs a new IteratorIterable that will use the given + * iterator. + * + * @param iterator the iterator to use. + * @param multipleUse true if the new iterable can be used in multiple iterations + */ + public IteratorIterable(Iterator iterator, boolean multipleUse) { super(); - this.iterator = createTypesafeIterator(iterator); + if (multipleUse && !(iterator instanceof ResettableIterator)) { + this.iterator = new ListIteratorWrapper(iterator); + } else { + this.iterator = iterator; + } + this.typeSafeIterator = createTypesafeIterator(this.iterator); } /** @@ -67,6 +122,9 @@ public class IteratorIterable implements Iterable { * @return the iterator */ public Iterator iterator() { - return iterator; + if (iterator instanceof ResettableIterator) { + ((ResettableIterator)iterator).reset(); + } + return typeSafeIterator; } } diff --git a/src/test/org/apache/commons/collections/TestIteratorUtils.java b/src/test/org/apache/commons/collections/TestIteratorUtils.java index 1e437ac95..18a76ff7d 100644 --- a/src/test/org/apache/commons/collections/TestIteratorUtils.java +++ b/src/test/org/apache/commons/collections/TestIteratorUtils.java @@ -61,6 +61,8 @@ public class TestIteratorUtils extends BulkTest { assertEquals(expected, actual.intValue()); ++expected; } + // insure iteration occurred + assertTrue(expected > 0); // single use iterator for(Integer actual : iterable) { @@ -76,6 +78,41 @@ public class TestIteratorUtils extends BulkTest { // success } } + + public void testAsMultipleIterable() { + List list = new ArrayList(); + list.add(Integer.valueOf(0)); + list.add(Integer.valueOf(1)); + list.add(Integer.valueOf(2)); + Iterator iterator = list.iterator(); + + Iterable iterable = IteratorUtils.asMultipleUseIterable(iterator); + int expected = 0; + for(Integer actual : iterable) { + assertEquals(expected, actual.intValue()); + ++expected; + } + // insure iteration occurred + assertTrue(expected > 0); + + // multiple use iterator + expected = 0; + for(Integer actual : iterable) { + assertEquals(expected, actual.intValue()); + ++expected; + } + // insure iteration occurred + assertTrue(expected > 0); + } + + public void testAsMultipleIterableNull() { + try { + IteratorUtils.asMultipleUseIterable(null); + fail("Expecting NullPointerException"); + } catch (NullPointerException ex) { + // success + } + } public void testToList() { List list = new ArrayList(); diff --git a/src/test/org/apache/commons/collections/iterators/TestIteratorIterable.java b/src/test/org/apache/commons/collections/iterators/TestIteratorIterable.java index c811fc677..891a44e4e 100644 --- a/src/test/org/apache/commons/collections/iterators/TestIteratorIterable.java +++ b/src/test/org/apache/commons/collections/iterators/TestIteratorIterable.java @@ -36,19 +36,47 @@ public class TestIteratorIterable extends BulkTest { super(name); } - public void testIterator() { + private Iterator createIterator() { List list = new ArrayList(); list.add(Integer.valueOf(0)); list.add(Integer.valueOf(1)); list.add(Integer.valueOf(2)); Iterator iter = list.iterator(); + return iter; + } + public void testIterator() { + Iterator iter = createIterator(); Iterable iterable = new IteratorIterable(iter); + + // first use + verifyIteration(iterable); + + // second use + for (Number actual : iterable) { + fail("should not be able to iterate twice"); + } + } + + public void testMultipleUserIterator() { + Iterator iter = createIterator(); + + Iterable iterable = new IteratorIterable(iter, true); + + // first use + verifyIteration(iterable); + + // second use + verifyIteration(iterable); + } + + private void verifyIteration(Iterable iterable) { int expected = 0; for (Number actual : iterable) { assertEquals(expected, actual.intValue()); ++expected; } + assertTrue(expected > 0); } }