Adds java.util.ListIterator to the emulated JRE collection classes.

Fixes issue #165.  
Review by: knorton


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@434 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/super/com/google/gwt/emul/java/util/AbstractList.java b/user/super/com/google/gwt/emul/java/util/AbstractList.java
index 85d5166..ee288fc 100644
--- a/user/super/com/google/gwt/emul/java/util/AbstractList.java
+++ b/user/super/com/google/gwt/emul/java/util/AbstractList.java
@@ -20,7 +20,12 @@
  */
 public abstract class AbstractList extends AbstractCollection implements List {
 
-  private final class IteratorImpl implements Iterator {
+  private class IteratorImpl implements Iterator {
+    /* 
+     * i is the index of the item that will be returned on the next call to next()
+     * last is the index of the item that was returned on the previous call
+     * to next() or previous (for ListIterator), -1 if no such item exists.
+     */
 
     int i = 0, last = -1;
 
@@ -32,18 +37,73 @@
       if (!hasNext()) {
         throw new NoSuchElementException();
       }
-      return get(last = i++);
-    }
+      return get(last = i++); 
+      }    
 
     public void remove() {
       if (last < 0) {
         throw new IllegalStateException();
-      }
-      AbstractList.this.remove(i - 1);
-      --i;
+      }     
+      AbstractList.this.remove(last);
+      i = last;
       last = -1;
     }
   }
+  
+  /**
+   * Implementation of <code>ListIterator</code> for abstract lists.
+   */
+  private final class ListIteratorImpl extends IteratorImpl implements
+      ListIterator {
+    /* 
+     * i is the index of the item that will be returned on the next call to next()
+     * last is the index of the item that was returned on the previous call
+     * to next() or previous (for ListIterator), -1 if no such item exists.
+     */
+    
+    private ListIteratorImpl() {
+      // Nothing to do
+    }
+
+    private ListIteratorImpl(int start) {
+      int size = AbstractList.this.size();
+      if (start < 0 || start > size) {
+        throw new IndexOutOfBoundsException("Size: " + AbstractList.this.size() + " Index: " + i);
+      }
+      i = start;
+    }
+
+    public void add(Object o) {
+      AbstractList.this.add(i++, o);
+      last = -1;
+    }
+
+    public boolean hasPrevious() {
+      return i > 0;
+    }
+
+    public int nextIndex() {
+      return i;
+    }
+
+    public Object previous() {
+      if (!hasPrevious()) {
+        throw new NoSuchElementException();
+      }
+      return get(last = --i);
+    }
+
+    public int previousIndex() {
+      return i - 1;
+    }
+
+    public void set(Object o) {
+      if (last == -1) {
+        throw new IllegalStateException();
+      }
+      AbstractList.this.set(last, o);
+    }
+  }
 
   public void add(int index, Object element) {
     throw new UnsupportedOperationException("add");
@@ -131,6 +191,14 @@
     return -1;
   }
 
+  public ListIterator listIterator() {
+    return new ListIteratorImpl();
+  }
+
+  public ListIterator listIterator(int from) {
+    return new ListIteratorImpl(from);
+  }
+
   public Object remove(int index) {
     throw new UnsupportedOperationException("remove");
   }
diff --git a/user/super/com/google/gwt/emul/java/util/List.java b/user/super/com/google/gwt/emul/java/util/List.java
index 7435136..edf0bd7 100644
--- a/user/super/com/google/gwt/emul/java/util/List.java
+++ b/user/super/com/google/gwt/emul/java/util/List.java
@@ -48,6 +48,10 @@
 
   int lastIndexOf(Object o);
 
+  ListIterator listIterator();
+
+  ListIterator listIterator(int from);
+
   Object remove(int index);
 
   boolean remove(Object o);
diff --git a/user/super/com/google/gwt/emul/java/util/ListIterator.java b/user/super/com/google/gwt/emul/java/util/ListIterator.java
new file mode 100644
index 0000000..ad7d43c
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/ListIterator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2006 Google Inc.
+ * 
+ * Licensed 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 java.util;
+
+/**
+ * Uses Java 1.4 ListIterator for documentation. The methods hasNext, next, and
+ * remove are repeated to allow the specialized ListIterator documentation to
+ * be associated with them.
+ */
+public interface ListIterator extends Iterator {
+  void add(Object o);
+
+  boolean hasNext();
+
+  boolean hasPrevious();
+
+  Object next();
+
+  int nextIndex();
+
+  Object previous();
+
+  int previousIndex();
+
+  void remove();
+
+  void set(Object o);
+}
diff --git a/user/test/com/google/gwt/emultest/java/util/ArrayListTest.java b/user/test/com/google/gwt/emultest/java/util/ArrayListTest.java
index 328456d..8672a86 100644
--- a/user/test/com/google/gwt/emultest/java/util/ArrayListTest.java
+++ b/user/test/com/google/gwt/emultest/java/util/ArrayListTest.java
@@ -22,6 +22,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.ListIterator;
 
 /** Tests ArrayList, and, by extension AbstractList.  Uses inheritance to 
  * inherit all of Apache's TestList and TestCollection. */
@@ -29,17 +30,84 @@
   public ArrayListTest() {
   }
 
-  protected List makeEmptyList() {
-    return new ArrayList();
-  }
-
   public void testAddWatch() {
     ArrayList s = new ArrayList();
     s.add("watch");
     assertEquals(s.get(0), "watch");
   }
 
+  public void testListIteratorAddInSeveralPositions() {
+    ArrayList l = new ArrayList();
+    ListIterator i = l.listIterator();
+    l.add(new Integer(0));
+    for (int n = 2; n < 5; n += 2) {
+      l.add(new Integer(n));
+    }
+    i = l.listIterator();
+    i.next();
+    i.add(new Integer(1));
+    i.next();
+    i.next();
+    i.previous();
+    i.add(new Integer(3));
+    i.next();
+    i.add(new Integer(5));
+    i.add(new Integer(6));
+    for (int n = 0; n < 6; n++) {
+      assertEquals(new Integer(n), l.get(n));
+    }
+  }
 
+  public void testListIteratorCreateInvalid() {
+    ArrayList l = new ArrayList();
+    l.add(new Integer(1));
+    ListIterator i = l.listIterator(0);
+    try {
+      i = l.listIterator(1);
+    } catch (IndexOutOfBoundsException e) {
+      // expected
+    }
+    try {
+      i = l.listIterator(-1);
+    } catch (IndexOutOfBoundsException e) {
+      // expected
+    }    
+  }
+  
+  public void testListIteratorHasNextHasPreviousAndIndexes() {
+    List l = new ArrayList();
+    ListIterator i = l.listIterator();
+    assertFalse(i.hasNext());
+    assertFalse(i.hasPrevious());
+    i.add(new Integer(1));
+    assertEquals(1,i.nextIndex());
+    assertEquals(0, i.previousIndex());
+    i = l.listIterator();
+    assertEquals(0,i.nextIndex());
+    assertEquals(-1, i.previousIndex());
+    assertTrue(i.hasNext());
+    assertFalse(i.hasPrevious());
+    i.next();
+    assertEquals(1,i.nextIndex());
+    assertEquals(0, i.previousIndex());
+    assertFalse(i.hasNext());
+    assertTrue(i.hasPrevious());    
+  }
+  
+  public void testListIteratorSetInSeveralPositions() {
+    ArrayList l = new ArrayList();
+    for (int n = 0; n < 5; n += 2) {
+      l.add(new Integer(n));
+    }
+    ListIterator i = l.listIterator();
+    for (int n = 0; n < 3; n++) {
+      l.set(n, new Integer(n));
+    }
+    for (int n = 0; n < 3; n++) {
+      assertEquals(new Integer(n), l.get(n));
+    }
+  }
+  
   public void testRemoveRange() {
     if (GWT.isScript()) {
       ArrayList l = new ArrayList();
@@ -49,6 +117,10 @@
       verifyRemoveRangeWorks(l);
     }
   }
+  
+  protected List makeEmptyList() {
+    return new ArrayList();
+  }
 
   private native void verifyRemoveRangeWorks(ArrayList l) /*-{
     var startIndex = l.@java.util.ArrayList::startIndex;
@@ -75,5 +147,4 @@
       @junit.framework.Assert::fail(Ljava/lang/String;)("endIndex - 1 should be empty");
     }
   }-*/;
-  
 }
diff --git a/user/test/org/apache/commons/collections/AbstractTestListIterator.java b/user/test/org/apache/commons/collections/AbstractTestListIterator.java
new file mode 100755
index 0000000..24701f5
--- /dev/null
+++ b/user/test/org/apache/commons/collections/AbstractTestListIterator.java
@@ -0,0 +1,285 @@
+/*
+ * 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.
+ * 
+ * THIS CODE HAS BEEN EXTENSIVELY HACKED BY GOOGLE TO WORK WITH GWT. 
+ */
+package org.apache.commons.collections;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Abstract class for testing the ListIterator interface.
+ * <p>
+ * This class provides a framework for testing an implementation of
+ * ListIterator. Concrete subclasses must provide the list iterator to be
+ * tested. They must also specify certain details of how the list iterator
+ * operates by overriding the supportsXxx() methods if necessary.
+ * 
+ * @since Commons Collections 3.0
+ * @version $Revision$ $Date$
+ * 
+ * @author Rodney Waldhoff
+ * @author Stephen Colebourne
+ */
+public abstract class AbstractTestListIterator extends TestIterator {
+
+  // -----------------------------------------------------------------------
+  /**
+   * Implement this method to return a list iterator over an empty collection.
+   * 
+   * @return an empty iterator
+   */
+  public abstract ListIterator makeEmptyListIterator();
+
+  /**
+   * Implement this method to return a list iterator over a collection with
+   * elements.
+   * 
+   * @return a full iterator
+   */
+  public abstract ListIterator makeFullListIterator();
+
+  /**
+   * Implements the abstract superclass method to return the list iterator.
+   * 
+   * @return an empty iterator
+   */
+  public Iterator makeEmptyIterator() {
+    return makeEmptyListIterator();
+  }
+
+  /**
+   * Implements the abstract superclass method to return the list iterator.
+   * 
+   * @return a full iterator
+   */
+  public Iterator makeFullIterator() {
+    return makeFullListIterator();
+  }
+
+  /**
+   * Whether or not we are testing an iterator that supports add(). Default is
+   * true.
+   * 
+   * @return true if Iterator supports add
+   */
+  public boolean supportsAdd() {
+    return true;
+  }
+
+  /**
+   * Whether or not we are testing an iterator that supports set(). Default is
+   * true.
+   * 
+   * @return true if Iterator supports set
+   */
+  public boolean supportsSet() {
+    return true;
+  }
+
+  /**
+   * The value to be used in the add and set tests. Default is null.
+   */
+  public Object addSetValue() {
+    return null;
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Test that the empty list iterator contract is correct.
+   */
+  public void testEmptyListIteratorIsIndeedEmpty() {
+    if (supportsEmptyIterator() == false) {
+      return;
+    }
+
+    ListIterator it = makeEmptyListIterator();
+
+    assertEquals(false, it.hasNext());
+    assertEquals(0, it.nextIndex());
+    assertEquals(false, it.hasPrevious());
+    assertEquals(-1, it.previousIndex());
+
+    // next() should throw a NoSuchElementException
+    try {
+      it.next();
+      fail("NoSuchElementException must be thrown from empty ListIterator");
+    } catch (NoSuchElementException e) {
+    }
+
+    // previous() should throw a NoSuchElementException
+    try {
+      it.previous();
+      fail("NoSuchElementException must be thrown from empty ListIterator");
+    } catch (NoSuchElementException e) {
+    }
+  }
+
+  /**
+   * Test navigation through the iterator.
+   */
+  public void testWalkForwardAndBack() {
+    ArrayList list = new ArrayList();
+    ListIterator it = makeFullListIterator();
+    while (it.hasNext()) {
+      list.add(it.next());
+    }
+
+    // check state at end
+    assertEquals(false, it.hasNext());
+    assertEquals(true, it.hasPrevious());
+    try {
+      it.next();
+      fail("NoSuchElementException must be thrown from next at end of ListIterator");
+    } catch (NoSuchElementException e) {
+    }
+
+    // loop back through comparing
+    for (int i = list.size() - 1; i >= 0; i--) {
+      assertEquals(i + 1, it.nextIndex());
+      assertEquals(i, it.previousIndex());
+
+      Object obj = list.get(i);
+      assertEquals(obj, it.previous());
+    }
+
+    // check state at start
+    assertEquals(true, it.hasNext());
+    assertEquals(false, it.hasPrevious());
+    try {
+      it.previous();
+      fail("NoSuchElementException must be thrown from previous at start of ListIterator");
+    } catch (NoSuchElementException e) {
+    }
+  }
+
+  /**
+   * Test add behaviour.
+   */
+  public void testAdd() {
+    ListIterator it = makeFullListIterator();
+
+    Object addValue = addSetValue();
+    if (supportsAdd() == false) {
+      // check for UnsupportedOperationException if not supported
+      try {
+        it.add(addValue);
+      } catch (UnsupportedOperationException ex) {
+      }
+      return;
+    }
+
+    // add at start should be OK, added should be previous
+    it = makeFullListIterator();
+    it.add(addValue);
+    assertEquals(addValue, it.previous());
+
+    // add at start should be OK, added should not be next
+    it = makeFullListIterator();
+    it.add(addValue);
+    assertTrue(addValue != it.next());
+
+    // add in middle and at end should be OK
+    it = makeFullListIterator();
+    while (it.hasNext()) {
+      it.next();
+      it.add(addValue);
+      // check add OK
+      assertEquals(addValue, it.previous());
+      it.next();
+    }
+  }
+
+  /**
+   * Test set behaviour.
+   */
+  public void testSet() {
+    ListIterator it = makeFullListIterator();
+
+    if (supportsSet() == false) {
+      // check for UnsupportedOperationException if not supported
+      try {
+        it.set(addSetValue());
+      } catch (UnsupportedOperationException ex) {
+      }
+      return;
+    }
+
+    // should throw IllegalStateException before next() called
+    try {
+      it.set(addSetValue());
+      fail();
+    } catch (IllegalStateException ex) {
+    }
+
+    // set after next should be fine
+    it.next();
+    it.set(addSetValue());
+
+    // repeated set calls should be fine
+    it.set(addSetValue());
+
+  }
+
+  public void testRemoveThenSet() {
+    ListIterator it = makeFullListIterator();
+    if (supportsRemove() && supportsSet()) {
+      it.next();
+      it.remove();
+      try {
+        it.set(addSetValue());
+        fail("IllegalStateException must be thrown from set after remove");
+      } catch (IllegalStateException e) {
+      }
+    }
+  }
+
+  public void testAddThenSet() {
+    ListIterator it = makeFullListIterator();
+    // add then set
+    if (supportsAdd() && supportsSet()) {
+      it.next();
+      it.add(addSetValue());
+      try {
+        it.set(addSetValue());
+        fail("IllegalStateException must be thrown from set after add");
+      } catch (IllegalStateException e) {
+      }
+    }
+  }
+
+  /**
+   * Test remove after add behaviour.
+   */
+  public void testAddThenRemove() {
+    ListIterator it = makeFullListIterator();
+
+    // add then remove
+    if (supportsAdd() && supportsRemove()) {
+      it.next();
+      it.add(addSetValue());
+      try {
+        it.remove();
+        fail("IllegalStateException must be thrown from remove after add");
+      } catch (IllegalStateException e) {
+      }
+    }
+  }
+
+}
diff --git a/user/test/org/apache/commons/collections/TestIterator.java b/user/test/org/apache/commons/collections/TestIterator.java
index fcb94d6..9b779da 100644
--- a/user/test/org/apache/commons/collections/TestIterator.java
+++ b/user/test/org/apache/commons/collections/TestIterator.java
@@ -52,6 +52,16 @@
     }
 
     /**
+     * Whether or not we are testing an iterator that supports remove.  
+     * Default is true.
+     * 
+     * @return true if Iterators can remove elements
+     */
+    public boolean supportsRemove() {
+        return true;
+    }
+    
+    /**
      * Should throw a NoSuchElementException.
      */
     public void testEmptyIterator() {
diff --git a/user/test/org/apache/commons/collections/TestList.java b/user/test/org/apache/commons/collections/TestList.java
index 40e294c..9a0135b 100644
--- a/user/test/org/apache/commons/collections/TestList.java
+++ b/user/test/org/apache/commons/collections/TestList.java
@@ -22,6 +22,8 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
 
 
 /**
@@ -84,6 +86,28 @@
     }
 
 
+
+    /**
+     * Whether or not we are testing an iterator that can be
+     * empty.  Default is true.
+     * 
+     * @return true if Iterators can be empty
+     */
+    public boolean supportsEmptyIterator() {
+        return true;
+    }
+
+    /**
+     * Whether or not we are testing an list that allows
+     * element set.  Default is true.
+     * 
+     * @return true if Lists support element set
+     */
+    public boolean isSetSupported() {
+        return true;
+    }
+    
+    
     /**
      *  Returns the {@link collection} field cast to a {@link List}.
      *
@@ -106,7 +130,315 @@
 
   
 
+      public class TestListIterator extends AbstractTestListIterator {
 
+        public Object addSetValue() {
+          return TestList.this.getOtherElements()[0];
+        }
+
+        public boolean supportsRemove() {
+          return TestList.this.isRemoveSupported();
+        }
+
+        public boolean supportsAdd() {
+          return TestList.this.isAddSupported();
+        }
+
+        public boolean supportsSet() {
+          return TestList.this.isSetSupported();
+        }
+
+        public ListIterator makeEmptyListIterator() {
+          resetEmpty();
+          return ((List) TestList.this.collection).listIterator();
+        }
+
+        public ListIterator makeFullListIterator() {
+          resetFull();
+          return ((List) TestList.this.collection).listIterator();
+        }
+
+        public Object makeObject() {
+          return ((List) TestList.this.collection).listIterator();
+        }
+      }
+
+      /**
+       * Tests the read-only bits of {@link List#listIterator()}.
+       */
+      public void testListListIterator() {
+        resetFull();
+        forwardTest(getList().listIterator(), 0);
+        backwardTest(getList().listIterator(), 0);
+      }
+
+      /**
+       * Tests the read-only bits of {@link List#listIterator(int)}.
+       */
+      public void testListListIteratorByIndex() {
+        resetFull();
+        try {
+          getList().listIterator(-1);
+        } catch (IndexOutOfBoundsException ex) {
+        }
+        resetFull();
+        try {
+          getList().listIterator(getList().size() + 1);
+        } catch (IndexOutOfBoundsException ex) {
+        }
+        resetFull();
+        for (int i = 0; i <= confirmed.size(); i++) {
+          forwardTest(getList().listIterator(i), i);
+          backwardTest(getList().listIterator(i), i);
+        }
+        resetFull();
+        for (int i = 0; i <= confirmed.size(); i++) {
+          backwardTest(getList().listIterator(i), i);
+        }
+      }
+
+      // -----------------------------------------------------------------------
+      /**
+       * Tests remove on list iterator is correct.
+       */
+      public void testListListIteratorPreviousRemoveNext() {
+        if (isRemoveSupported() == false)
+          return;
+        resetFull();
+        if (collection.size() < 4)
+          return;
+        ListIterator it = getList().listIterator();
+        Object zero = it.next();
+        Object one = it.next();
+        Object two = it.next();
+        Object two2 = it.previous();
+        Object one2 = it.previous();
+        assertEquals(one, one2);
+        assertEquals(two, two2);
+        assertEquals(zero, getList().get(0));
+        assertEquals(one, getList().get(1));
+        assertEquals(two, getList().get(2));
+        
+        it.remove(); // removed element at index 1 (one)
+        assertEquals(zero, getList().get(0));
+        assertEquals(two, getList().get(1));
+        Object two3 = it.next(); // do next after remove
+        assertEquals(two, two3);
+        assertEquals(collection.size() > 2, it.hasNext());
+        assertEquals(true, it.hasPrevious());
+      }
+
+      /**
+       * Tests remove on list iterator is correct.
+       */
+      public void testListListIteratorPreviousRemovePrevious() {
+        if (isRemoveSupported() == false)
+          return;
+        resetFull();
+        if (collection.size() < 4)
+          return;
+        ListIterator it = getList().listIterator();
+        Object zero = it.next();
+        Object one = it.next();
+        Object two = it.next();
+        Object two2 = it.previous();
+        Object one2 = it.previous();
+        assertEquals(one, one2);
+        assertEquals(two, two2);
+        assertEquals(zero, getList().get(0));
+        assertEquals(one, getList().get(1));
+        assertEquals(two, getList().get(2));
+
+        it.remove(); // removed element at index 1 (one)
+        assertEquals(zero, getList().get(0));
+        assertEquals(two, getList().get(1));
+        Object zero3 = it.previous(); // do previous after remove
+        assertEquals(zero, zero3);
+        assertEquals(false, it.hasPrevious());
+        assertEquals(collection.size() > 2, it.hasNext());
+      }
+
+      /**
+       * Tests remove on list iterator is correct.
+       */
+      public void testListListIteratorNextRemoveNext() {
+        if (isRemoveSupported() == false)
+          return;
+        resetFull();
+        if (collection.size() < 4)
+          return;
+        ListIterator it = getList().listIterator();
+        Object zero = it.next();
+        Object one = it.next();
+        Object two = it.next();
+        assertEquals(zero, getList().get(0));
+        assertEquals(one, getList().get(1));
+        assertEquals(two, getList().get(2));
+        Object three = getList().get(3);
+
+        it.remove(); // removed element at index 2 (two)
+        assertEquals(zero, getList().get(0));
+        assertEquals(one, getList().get(1));
+        Object three2 = it.next(); // do next after remove
+        assertEquals(three, three2);
+        assertEquals(collection.size() > 3, it.hasNext());
+        assertEquals(true, it.hasPrevious());
+      }
+
+      /**
+       * Tests remove on list iterator is correct.
+       */
+      public void testListListIteratorNextRemovePrevious() {
+        if (isRemoveSupported() == false)
+          return;
+        resetFull();
+        if (collection.size() < 4)
+          return;
+        ListIterator it = getList().listIterator();
+        Object zero = it.next();
+        Object one = it.next();
+        Object two = it.next();
+        assertEquals(zero, getList().get(0));
+        assertEquals(one, getList().get(1));
+        assertEquals(two, getList().get(2));
+
+        it.remove(); // removed element at index 2 (two)
+        assertEquals(zero, getList().get(0));
+        assertEquals(one, getList().get(1));
+        Object one2 = it.previous(); // do previous after remove
+        assertEquals(one, one2);
+        assertEquals(true, it.hasNext());
+        assertEquals(true, it.hasPrevious());
+      }
+
+      // -----------------------------------------------------------------------
+      /**
+       * Traverses to the end of the given iterator.
+       * 
+       * @param iter the iterator to traverse
+       * @param i the starting index
+       */
+      private void forwardTest(ListIterator iter, int i) {
+        List list = getList();
+        int max = getFullElements().length;
+
+        while (i < max) {
+          assertTrue("Iterator should have next", iter.hasNext());
+          assertEquals("Iterator.nextIndex should work", iter.nextIndex(), i);
+          assertEquals("Iterator.previousIndex should work", iter.previousIndex(),
+              i - 1);
+          Object o = iter.next();
+          assertEquals("Iterator returned correct element f", list.get(i), o);
+          i++;
+        }
+
+        assertTrue("Iterator shouldn't have next", !iter.hasNext());
+        assertEquals("nextIndex should be size", iter.nextIndex(), max);
+        assertEquals("previousIndex should be size - 1", iter.previousIndex(),
+            max - 1);
+
+        try {
+          iter.next();
+          fail("Exhausted iterator should raise NoSuchElement");
+        } catch (NoSuchElementException e) {
+          // expected
+        }
+      }
+
+      /**
+       * Traverses to the beginning of the given iterator.
+       * 
+       * @param iter the iterator to traverse
+       * @param i the starting index
+       */
+      private void backwardTest(ListIterator iter, int i) {
+        List list = getList();
+
+        while (i > 0) {
+          assertTrue("Iterator should have previous, i:" + i, iter.hasPrevious());
+          assertEquals("Iterator.nextIndex should work, i:" + i, iter.nextIndex(),
+              i);
+          assertEquals("Iterator.previousIndex should work, i:" + i,
+              iter.previousIndex(), i - 1);
+          Object o = iter.previous();
+          assertEquals("Iterator returned correct element b", list.get(i - 1), o);
+          i--;
+        }
+
+        assertTrue("Iterator shouldn't have previous", !iter.hasPrevious());
+        int nextIndex = iter.nextIndex();
+        assertEquals("nextIndex should be 0, actual value: " + nextIndex,
+            nextIndex, 0);
+        int prevIndex = iter.previousIndex();
+        assertEquals("previousIndex should be -1, actual value: " + prevIndex,
+            prevIndex, -1);
+
+        try {
+          iter.previous();
+          fail("Exhausted iterator should raise NoSuchElement");
+        } catch (NoSuchElementException e) {
+          // expected
+        }
+
+      }
+
+      /**
+       * Tests the {@link ListIterator#add(Object)} method of the list iterator.
+       */
+      public void testListIteratorAdd() {
+        if (!isAddSupported())
+          return;
+
+        resetEmpty();
+        List list1 = getList();
+        List list2 = getConfirmedList();
+
+        Object[] elements = getFullElements();
+        ListIterator iter1 = list1.listIterator();
+        ListIterator iter2 = list2.listIterator();
+
+        for (int i = 0; i < elements.length; i++) {
+          iter1.add(elements[i]);
+          iter2.add(elements[i]);
+          verify();
+        }
+
+        resetFull();
+        iter1 = getList().listIterator();
+        iter2 = getConfirmedList().listIterator();
+        for (int i = 0; i < elements.length; i++) {
+          iter1.next();
+          iter2.next();
+          iter1.add(elements[i]);
+          iter2.add(elements[i]);
+          verify();
+        }
+      }
+
+      /**
+       * Tests the {@link ListIterator#set(Object)} method of the list iterator.
+       */
+      public void testListIteratorSet() {
+        if (!isSetSupported())
+          return;
+
+        Object[] elements = getFullElements();
+
+        resetFull();
+        ListIterator iter1 = getList().listIterator();
+        ListIterator iter2 = getConfirmedList().listIterator();
+        for (int i = 0; i < elements.length; i++) {
+          iter1.next();
+          iter2.next();
+          iter1.set(elements[i]);
+          iter2.set(elements[i]);
+          verify();
+        }
+      }
+
+      
+      
+      
     /**
      *  Tests {@link List#add(int,Object)}.
      */