You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by mb...@apache.org on 2009/03/09 22:43:53 UTC

svn commit: r751857 - in /commons/proper/collections/branches/collections_jdk5_branch/src: java/org/apache/commons/collections/iterators/ test/org/apache/commons/collections/iterators/

Author: mbenson
Date: Mon Mar  9 21:43:53 2009
New Revision: 751857

URL: http://svn.apache.org/viewvc?rev=751857&view=rev
Log:
handle more ListIterator functionality when possible

Added:
    commons/proper/collections/branches/collections_jdk5_branch/src/test/org/apache/commons/collections/iterators/TestListIteratorWrapper2.java   (with props)
Modified:
    commons/proper/collections/branches/collections_jdk5_branch/src/java/org/apache/commons/collections/iterators/ListIteratorWrapper.java
    commons/proper/collections/branches/collections_jdk5_branch/src/test/org/apache/commons/collections/iterators/TestListIteratorWrapper.java

Modified: commons/proper/collections/branches/collections_jdk5_branch/src/java/org/apache/commons/collections/iterators/ListIteratorWrapper.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/branches/collections_jdk5_branch/src/java/org/apache/commons/collections/iterators/ListIteratorWrapper.java?rev=751857&r1=751856&r2=751857&view=diff
==============================================================================
--- commons/proper/collections/branches/collections_jdk5_branch/src/java/org/apache/commons/collections/iterators/ListIteratorWrapper.java (original)
+++ commons/proper/collections/branches/collections_jdk5_branch/src/java/org/apache/commons/collections/iterators/ListIteratorWrapper.java Mon Mar  9 21:43:53 2009
@@ -16,15 +16,21 @@
  */
 package org.apache.commons.collections.iterators;
 
+import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.NoSuchElementException;
 
+import org.apache.commons.collections.ResettableIterator;
 import org.apache.commons.collections.ResettableListIterator;
 
 /**
- * Converts an iterator into a list iterator by caching the returned entries.
+ * Converts an {@link Iterator} into a {@link ResettableListIterator}.
+ * For plain <code>Iterator</code>s this is accomplished by caching the returned
+ * elements.  This class can also be used to simply add {@link ResettableIterator}
+ * functionality to a given {@link ListIterator}.
  * <p>
  * The <code>ListIterator</code> interface has additional useful methods
  * for navigation - <code>previous()</code> and the index methods.
@@ -32,7 +38,7 @@
  * <code>ListIterator</code>. It achieves this by building a list internally
  * of as the underlying iterator is traversed.
  * <p>
- * The optional operations of <code>ListIterator</code> are not supported.
+ * The optional operations of <code>ListIterator</code> are not supported for plain <code>Iterator</code>s.
  * <p>
  * This class implements ResettableListIterator from Commons Collections 3.2.
  *
@@ -41,13 +47,17 @@
  *
  * @author Morgan Delagrange
  * @author Stephen Colebourne
+ * @author Matt Benson
  */
 public class ListIteratorWrapper<E> implements ResettableListIterator<E> {
 
-    /** Message used when remove, set or add are called. */
+    /** Message used when set or add are called. */
     private static final String UNSUPPORTED_OPERATION_MESSAGE =
         "ListIteratorWrapper does not support optional operations of ListIterator.";
 
+    /** Message used when set or add are called. */
+    private static final String CANNOT_REMOVE_MESSAGE = "Cannot remove element at index {0}.";
+
     /** The underlying iterator being decorated. */
     private final Iterator<? extends E> iterator;
     /** The list being used to cache the iterator. */
@@ -57,6 +67,8 @@
     private int currentIndex = 0;
     /** The current index of the wrapped iterator. */
     private int wrappedIteratorIndex = 0;
+    /** recall whether the wrapped iterator's "cursor" is in such a state as to allow remove() to be called */
+    private boolean removeState;
 
     // Constructor
     //-------------------------------------------------------------------------
@@ -78,12 +90,19 @@
     // ListIterator interface
     //-------------------------------------------------------------------------
     /**
-     * Throws {@link UnsupportedOperationException}.
+     * Throws {@link UnsupportedOperationException}
+     * unless the underlying <code>Iterator</code> is a <code>ListIterator</code>.
      *
-     * @param obj  the object to add, ignored
-     * @throws UnsupportedOperationException always
+     * @param obj  the object to add
+     * @throws UnsupportedOperationException
      */
     public void add(E obj) throws UnsupportedOperationException {
+        if (iterator instanceof ListIterator) {
+            @SuppressWarnings("unchecked")
+            ListIterator<E> li = (ListIterator<E>) iterator;
+            li.add(obj);
+            return;
+        }
         throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE);
     }
 
@@ -93,7 +112,7 @@
      * @return true if there are more elements
      */
     public boolean hasNext() {
-        if (currentIndex == wrappedIteratorIndex) {
+        if (currentIndex == wrappedIteratorIndex || iterator instanceof ListIterator) {
             return iterator.hasNext();
         }
         return true;
@@ -105,10 +124,12 @@
      * @return true if there are previous elements
      */
     public boolean hasPrevious() {
-        if (currentIndex == 0) {
-            return false;
+        if (iterator instanceof ListIterator) {
+            @SuppressWarnings("unchecked")
+            ListIterator li = (ListIterator) iterator;
+            return li.hasPrevious();
         }
-        return true;
+        return currentIndex > 0;
     }
 
     /**
@@ -118,6 +139,10 @@
      * @throws NoSuchElementException if there are no more elements
      */
     public E next() throws NoSuchElementException {
+        if (iterator instanceof ListIterator) {
+            return iterator.next();
+        }
+
         if (currentIndex < wrappedIteratorIndex) {
             ++currentIndex;
             return list.get(currentIndex - 1);
@@ -127,15 +152,21 @@
         list.add(retval);
         ++currentIndex;
         ++wrappedIteratorIndex;
+        removeState = true;
         return retval;
     }
 
     /**
-     * Returns in the index of the next element.
+     * Returns the index of the next element.
      *
      * @return the index of the next element
      */
     public int nextIndex() {
+        if (iterator instanceof ListIterator) {
+            @SuppressWarnings("unchecked")
+            ListIterator li = (ListIterator) iterator;
+            return li.nextIndex();
+        }
         return currentIndex;
     }
 
@@ -146,11 +177,17 @@
      * @throws NoSuchElementException  if there are no previous elements
      */
     public E previous() throws NoSuchElementException {
+        if (iterator instanceof ListIterator) {
+            @SuppressWarnings("unchecked")
+            ListIterator<E> li = (ListIterator<E>) iterator;
+            return li.previous();
+        }
+
         if (currentIndex == 0) {
             throw new NoSuchElementException();
         }
-        --currentIndex;
-        return list.get(currentIndex);    
+        removeState = wrappedIteratorIndex == currentIndex;
+        return list.get(--currentIndex);
     }
 
     /**
@@ -159,25 +196,52 @@
      * @return  the index of the previous element
      */
     public int previousIndex() {
+        if (iterator instanceof ListIterator) {
+            @SuppressWarnings("unchecked")
+            ListIterator li = (ListIterator) iterator;
+            return li.previousIndex();
+        }
         return currentIndex - 1;
     }
 
     /**
-     * Throws {@link UnsupportedOperationException}.
+     * Throws {@link UnsupportedOperationException} if {@link #previous()} has ever been called.
      *
      * @throws UnsupportedOperationException always
      */
     public void remove() throws UnsupportedOperationException {
-        throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE);
+        if (iterator instanceof ListIterator) {
+            iterator.remove();
+            return;
+        }
+        int removeIndex = currentIndex;
+        if (currentIndex == wrappedIteratorIndex) {
+            --removeIndex;
+        }
+        if (!removeState || wrappedIteratorIndex - currentIndex > 1) {
+            throw new IllegalStateException(MessageFormat.format(CANNOT_REMOVE_MESSAGE, removeIndex));
+        }
+        iterator.remove();
+        list.remove(removeIndex);
+        currentIndex = removeIndex;
+        wrappedIteratorIndex--;
+        removeState = false;
     }
 
     /**
-     * Throws {@link UnsupportedOperationException}.
+     * Throws {@link UnsupportedOperationException}
+     * unless the underlying <code>Iterator</code> is a <code>ListIterator</code>.
      *
-     * @param obj  the object to set, ignored
-     * @throws UnsupportedOperationException always
+     * @param obj  the object to set
+     * @throws UnsupportedOperationException
      */
     public void set(E obj) throws UnsupportedOperationException {
+        if (iterator instanceof ListIterator) {
+            @SuppressWarnings("unchecked")
+            ListIterator<E> li = (ListIterator<E>) iterator;
+            li.set(obj);
+            return;
+        }
         throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE);
     }
 
@@ -190,6 +254,14 @@
      * @since Commons Collections 3.2
      */
     public void reset()  {
+        if (iterator instanceof ListIterator) {
+            @SuppressWarnings("unchecked")
+            ListIterator li = (ListIterator) iterator;
+            while (li.previousIndex() >= 0) {
+                li.previous();
+            }
+            return;
+        }
         currentIndex = 0;
     }
 

Modified: commons/proper/collections/branches/collections_jdk5_branch/src/test/org/apache/commons/collections/iterators/TestListIteratorWrapper.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/branches/collections_jdk5_branch/src/test/org/apache/commons/collections/iterators/TestListIteratorWrapper.java?rev=751857&r1=751856&r2=751857&view=diff
==============================================================================
--- commons/proper/collections/branches/collections_jdk5_branch/src/test/org/apache/commons/collections/iterators/TestListIteratorWrapper.java (original)
+++ commons/proper/collections/branches/collections_jdk5_branch/src/test/org/apache/commons/collections/iterators/TestListIteratorWrapper.java Mon Mar  9 21:43:53 2009
@@ -115,13 +115,99 @@
     public void testRemove() {
         ListIterator<E> iter = makeObject();
 
+        //initial state:
+        assertEquals(-1, iter.previousIndex());
+        assertEquals(0, iter.nextIndex());
+
+        try {
+            iter.remove();
+            fail("ListIteratorWrapper#remove() should fail; must be initially positioned first");
+        } catch (IllegalStateException e) {
+        }
+
+        //no change from invalid op:
+        assertEquals(-1, iter.previousIndex());
+        assertEquals(0, iter.nextIndex());
+
+        //establish size:
+        int sz = list1.size();
+
+        //verify initial next() call:
+        assertEquals(list1.get(0), iter.next());
+        assertEquals(0, iter.previousIndex());
+        assertEquals(1, iter.nextIndex());
+
+        //verify remove():
+        iter.remove();
+        assertEquals(--sz, list1.size());
+        //like we never started iterating:
+        assertEquals(-1, iter.previousIndex());
+        assertEquals(0, iter.nextIndex());
+ 
         try {
             iter.remove();
-            fail("FilterIterator does not support the remove() method");
-        } catch (UnsupportedOperationException e) {
+            fail("ListIteratorWrapper#remove() should fail; must be repositioned first");
+        } catch (IllegalStateException e) {
+        }
+
+        //no change from invalid op:
+        assertEquals(-1, iter.previousIndex());
+        assertEquals(0, iter.nextIndex());
+
+        //two consecutive next() calls:
+        assertEquals(list1.get(0), iter.next());
+        assertEquals(0, iter.previousIndex());
+        assertEquals(1, iter.nextIndex());
+
+        assertEquals(list1.get(1), iter.next());
+        assertEquals(1, iter.previousIndex());
+        assertEquals(2, iter.nextIndex());
+
+        //call previous():
+        assertEquals(list1.get(1), iter.previous());
+        assertEquals(0, iter.previousIndex());
+        assertEquals(1, iter.nextIndex());
+
+        //should support remove() after calling previous() once from tip because we haven't changed the underlying iterator's position:
+        iter.remove();
+        assertEquals(--sz, list1.size());
+        assertEquals(0, iter.previousIndex());
+        assertEquals(1, iter.nextIndex());
+
+        //dig into cache
+        assertEquals(list1.get(0), iter.previous());
+        assertEquals(-1, iter.previousIndex());
+        assertEquals(0, iter.nextIndex());
 
+        try {
+            iter.remove();
+            fail("ListIteratorWrapper does not support the remove() method while dug into the cache via previous()");
+        } catch (IllegalStateException e) {
         }
 
+        //no change from invalid op:
+        assertEquals(-1, iter.previousIndex());
+        assertEquals(0, iter.nextIndex());
+
+        //dig out of cache, first next() maintains current position:
+        assertEquals(list1.get(0), iter.next());
+        assertEquals(0, iter.previousIndex());
+        assertEquals(1, iter.nextIndex());
+        //continue traversing underlying iterator with this next() call, and we're out of the hole, so to speak:
+        assertEquals(list1.get(1), iter.next());
+        assertEquals(1, iter.previousIndex());
+        assertEquals(2, iter.nextIndex());
+
+        //verify remove() works again:
+        iter.remove();
+        assertEquals(--sz, list1.size());
+        assertEquals(0, iter.previousIndex());
+        assertEquals(1, iter.nextIndex());
+
+        assertEquals(list1.get(1), iter.next());
+        assertEquals(1, iter.previousIndex());
+        assertEquals(2, iter.nextIndex());
+
     }
 
     public void testReset() {

Added: commons/proper/collections/branches/collections_jdk5_branch/src/test/org/apache/commons/collections/iterators/TestListIteratorWrapper2.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/branches/collections_jdk5_branch/src/test/org/apache/commons/collections/iterators/TestListIteratorWrapper2.java?rev=751857&view=auto
==============================================================================
--- commons/proper/collections/branches/collections_jdk5_branch/src/test/org/apache/commons/collections/iterators/TestListIteratorWrapper2.java (added)
+++ commons/proper/collections/branches/collections_jdk5_branch/src/test/org/apache/commons/collections/iterators/TestListIteratorWrapper2.java Mon Mar  9 21:43:53 2009
@@ -0,0 +1,213 @@
+/*
+ * 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.collections.iterators;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.commons.collections.ResettableListIterator;
+
+/**
+ * Tests the ListIteratorWrapper to insure that it behaves as expected when wrapping a ListIterator.
+ *
+ * @version $Revision$ $Date$
+ *
+ * @author Morgan Delagrange
+ */
+public class TestListIteratorWrapper2<E> extends AbstractTestIterator<E> {
+
+    protected String[] testArray = {
+        "One", "Two", "Three", "Four", "Five", "Six"
+    };
+
+    protected List<E> list1 = null;
+
+    public static Test suite() {
+        return new TestSuite(TestListIteratorWrapper2.class);
+    }
+
+    public TestListIteratorWrapper2(String testName) {
+        super(testName);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void setUp() {
+        list1 = new ArrayList<E>();
+        list1.add((E) "One");
+        list1.add((E) "Two");
+        list1.add((E) "Three");
+        list1.add((E) "Four");
+        list1.add((E) "Five");
+        list1.add((E) "Six");
+    }
+
+    public ResettableListIterator<E> makeEmptyIterator() {
+        ArrayList<E> list = new ArrayList<E>();
+        return new ListIteratorWrapper<E>(list.listIterator());
+    }
+
+    public ResettableListIterator<E> makeObject() {
+        return new ListIteratorWrapper<E>(list1.listIterator());
+    }
+
+    public void testIterator() {
+        ListIterator<E> iter = makeObject();
+        for (int i = 0; i < testArray.length; i++) {
+            Object testValue = testArray[i];
+            Object iterValue = iter.next();
+
+            assertEquals("Iteration value is correct", testValue, iterValue);
+        }
+
+        assertTrue("Iterator should now be empty", !iter.hasNext());
+
+        try {
+            iter.next();
+        } catch (Exception e) {
+            assertTrue("NoSuchElementException must be thrown",
+                       e.getClass().equals((new NoSuchElementException()).getClass()));
+        }
+
+        // now, read it backwards
+        for (int i = testArray.length - 1; i > -1; --i) {
+            Object testValue = testArray[i];
+            E iterValue = iter.previous();
+
+            assertEquals( "Iteration value is correct", testValue, iterValue );
+        }
+
+        try {
+            iter.previous();
+        } catch (Exception e) {
+            assertTrue("NoSuchElementException must be thrown",
+                       e.getClass().equals((new NoSuchElementException()).getClass()));
+        }
+
+        // now, read it forwards again
+        for (int i = 0; i < testArray.length; i++) {
+            Object testValue = testArray[i];
+            Object iterValue = iter.next();
+
+            assertEquals("Iteration value is correct", testValue, iterValue);
+        }
+
+    }
+
+    public void testRemove() {
+        ListIterator<E> iter = makeObject();
+
+        //initial state:
+        assertEquals(-1, iter.previousIndex());
+        assertEquals(0, iter.nextIndex());
+
+        try {
+            iter.remove();
+            fail("ListIteratorWrapper#remove() should fail; must be initially positioned first");
+        } catch (IllegalStateException e) {
+        }
+
+        //no change from invalid op:
+        assertEquals(-1, iter.previousIndex());
+        assertEquals(0, iter.nextIndex());
+
+        //establish size:
+        int sz = list1.size();
+
+        //verify initial next() call:
+        assertEquals(list1.get(0), iter.next());
+        assertEquals(0, iter.previousIndex());
+        assertEquals(1, iter.nextIndex());
+
+        //verify remove():
+        iter.remove();
+        assertEquals(--sz, list1.size());
+        //like we never started iterating:
+        assertEquals(-1, iter.previousIndex());
+        assertEquals(0, iter.nextIndex());
+ 
+        try {
+            iter.remove();
+            fail("ListIteratorWrapper#remove() should fail; must be repositioned first");
+        } catch (IllegalStateException e) {
+        }
+
+        //no change from invalid op:
+        assertEquals(-1, iter.previousIndex());
+        assertEquals(0, iter.nextIndex());
+
+        //two consecutive next() calls:
+        assertEquals(list1.get(0), iter.next());
+        assertEquals(0, iter.previousIndex());
+        assertEquals(1, iter.nextIndex());
+
+        assertEquals(list1.get(1), iter.next());
+        assertEquals(1, iter.previousIndex());
+        assertEquals(2, iter.nextIndex());
+
+        //call previous():
+        assertEquals(list1.get(1), iter.previous());
+        assertEquals(0, iter.previousIndex());
+        assertEquals(1, iter.nextIndex());
+
+        //should support remove() after calling previous() once from tip because we haven't changed the underlying iterator's position:
+        iter.remove();
+        assertEquals(--sz, list1.size());
+        assertEquals(0, iter.previousIndex());
+        assertEquals(1, iter.nextIndex());
+
+        //this would dig into cache on a plain Iterator, but forwards directly to wrapped ListIterator:
+        assertEquals(list1.get(0), iter.previous());
+        assertEquals(-1, iter.previousIndex());
+        assertEquals(0, iter.nextIndex());
+
+        //here's the proof; remove() still works:
+        iter.remove();
+        assertEquals(--sz, list1.size());
+        assertEquals(-1, iter.previousIndex());
+        assertEquals(0, iter.nextIndex());
+
+        //further testing would be fairly meaningless:
+    }
+
+    public void testReset() {
+        ResettableListIterator<E> iter = makeObject();
+        E first = iter.next();
+        E second = iter.next();
+
+        iter.reset();
+
+        // after reset, there shouldn't be any previous elements
+        assertFalse("No previous elements after reset()", iter.hasPrevious());
+
+        // after reset, the results should be the same as before
+        assertEquals("First element should be the same", first, iter.next());
+        assertEquals("Second elment should be the same", second, iter.next());
+
+        // after passing the point, where we resetted, continuation should work as expected
+        for (int i = 2; i < testArray.length; i++) {
+            Object testValue = testArray[i];
+            E iterValue = iter.next();
+
+            assertEquals("Iteration value is correct", testValue, iterValue);
+        }
+    }
+
+}

Propchange: commons/proper/collections/branches/collections_jdk5_branch/src/test/org/apache/commons/collections/iterators/TestListIteratorWrapper2.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/collections/branches/collections_jdk5_branch/src/test/org/apache/commons/collections/iterators/TestListIteratorWrapper2.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL