Fixes TreeMap's iterator which currently can get jacked up when doing removals.

Found by: jat
Suggested by: bruce
Patch by: scottb (+rdayal)
Review by: rdayal (desk)


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2928 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/super/com/google/gwt/emul/java/util/TreeMap.java b/user/super/com/google/gwt/emul/java/util/TreeMap.java
index 645fd15..abaf942 100644
--- a/user/super/com/google/gwt/emul/java/util/TreeMap.java
+++ b/user/super/com/google/gwt/emul/java/util/TreeMap.java
@@ -37,36 +37,14 @@
    */
 
   /**
-   * An iterator for entries kept in a TreeMap.
+   * Iterator for <code>EntrySet</code>.
    */
-  private class EntryIterator implements Iterator<Entry<K, V>> {
-
-    // The most recent node returned from next(); null if it hasn't been called
-    // or has been deleted.
-    private Node<K, V> current;
-
-    // Lower, inclusive bound on returned keys.
-    private K fromKey;
-
-    /*
-     * Since we don't keep parent pointers, we maintain our position in the tree
-     * via the stack which would be used for iterative in-order traversal. The
-     * top of the stack is always the next node to visit.
-     */
-    private Stack<Node<K, V>> inorderStack;
-
-    // Upper, exclusive bound on returned keys, or null if there is none --
-    // used for iterators on submaps.
-    private K toKey;
-
-    // The type of range bounds on this iterator.
-    private SubMapType type;
-
-    // true if the iterator hasn't been initialized yet.
-    private boolean uninitialized;
+  private final class EntryIterator implements Iterator<Entry<K, V>> {
+    private final Iterator<Map.Entry<K, V>> iter;
+    private Map.Entry<K, V> last = null;
 
     /**
-     * Create a new iterator with no restrictions on the returned values.
+     * Constructor for <code>EntrySetIterator</code>.
      */
     public EntryIterator() {
       this(SubMapType.All, null, null);
@@ -79,67 +57,46 @@
      * @param toKey the upper bound of keys to return.
      */
     public EntryIterator(SubMapType type, K fromKey, K toKey) {
-      inorderStack = new Stack<Node<K, V>>();
-      uninitialized = true;
-      this.type = type;
-      this.fromKey = fromKey;
-      this.toKey = toKey;
+      List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>();
+      inOrderAdd(list, type, TreeMap.this.root, fromKey, toKey);
+      this.iter = list.iterator();
     }
 
     public boolean hasNext() {
-      if (uninitialized) {
-        initialize();
-      }
-      if (inorderStack.isEmpty()) {
-        return false;
-      }
-      return inRange(inorderStack.peek().key);
+      return iter.hasNext();
     }
 
-    public Entry<K, V> next() {
-      if (uninitialized) {
-        initialize();
-      }
-      /*
-       * After returning the current node, push the left children of its right
-       * child on the stack.
-       */
-      current = inorderStack.pop();
-      if (current == null || !inRange(current.key)) {
-        throw new NoSuchElementException("No more elements");
-      }
-      pushLeftChildren(current.child[RIGHT]);
-      return current;
+    public Map.Entry<K, V> next() {
+      return last = iter.next();
     }
 
     public void remove() {
+      if (last == null) {
+        throw new IllegalStateException("Must call next() before remove().");
+      } else {
+        iter.remove();
+        TreeMap.this.remove(last.getKey());
+        last = null;
+      }
+    }
+
+    private void inOrderAdd(List<Map.Entry<K, V>> list, SubMapType type,
+        Node<K, V> current, K fromKey, K toKey) {
       if (current == null) {
-        throw new IllegalStateException("No current element");
+        return;
       }
-      TreeMap.this.remove(current.getKey());
-      current = null;
-    }
-
-    private void initialize() {
-      uninitialized = false;
-      pushLeftChildren(root);
-      current = null;
-      if (type == SubMapType.Tail || type == SubMapType.Range) {
-        // TODO(jat): rewrite this similar to getAdjacentEntry()
-        while (!inorderStack.isEmpty()) {
-          current = inorderStack.pop();
-          if (inRange(current.key)) {
-            // we found the starting point, so push it back on the stack
-            inorderStack.push(current);
-            current = null;
-            break;
-          }
-          pushLeftChildren(current.child[RIGHT]);
-        }
+      if (current.child[LEFT] != null) {
+        inOrderAdd(list, type, current.child[LEFT], fromKey, toKey);
+      }
+      if (inRange(type, current.getKey(), fromKey, toKey)) {
+        list.add(current);
+      }
+      if (current.child[RIGHT] != null) {
+        inOrderAdd(list, type, current.child[RIGHT], fromKey, toKey);
       }
     }
 
-    private boolean inRange(K key) {
+    private boolean inRange(SubMapType type, K key, K fromKey, K toKey) {
       if (type == SubMapType.Head || type == SubMapType.Range) {
         if (cmp.compare(key, toKey) >= 0) {
           return false;
@@ -152,19 +109,6 @@
       }
       return true;
     }
-
-    /**
-     * Follow the left children of the specified node, pushing them on the stack
-     * as we go.
-     * 
-     * @param node parent node to follow left children from.
-     */
-    private void pushLeftChildren(Node<K, V> node) {
-      while (node != null) {
-        inorderStack.push(node);
-        node = node.child[LEFT];
-      }
-    }
   }
 
   private final class EntrySet extends AbstractSet<Entry<K, V>> {
@@ -348,6 +292,9 @@
           // check key for compatibility with comparator
           cmp.compare(fromKey, fromKey);
           break;
+        case All:
+          // no checks are needed
+          break;
       }
       this.type = type;
       this.fromKey = fromKey;
@@ -659,6 +606,7 @@
     return state.value;
   }
 
+  @Override
   @SuppressWarnings("unchecked")
   public V remove(Object keyObj) {
     K key = (K) keyObj; // suppress unchecked cast