Vector: Rework CME checks in hasNext() and next().

Port over commit b10b2a3ab693cfd6156d06ffe4e00ce69b9c9194
from java.util.ArrayList to java.util.Vector.

Also adds the same set of tests to Vector / ArrayList so
to make sure their iterators behave identically in this regard.

bug: 27430229

Change-Id: Idd8e44737a216ac5ea9f1e6fed8892b364997292
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java
index 87b1cd9..3ec7c86 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java
@@ -1023,7 +1023,7 @@
 
     // http://b/25867131 et al.
     public void testIteratorAddAfterCompleteIteration() {
-        ArrayList<String> strings = new ArrayList<String>();
+        ArrayList<String> strings = new ArrayList<>();
         strings.add("string1");
         Iterator<String> it = strings.iterator();
         assertTrue(it.hasNext());
@@ -1053,6 +1053,16 @@
         assertEquals("string2", it.next());
     }
 
+    // http://b/27430229
+    public void testRemoveAllDuringIteration() {
+        ArrayList<String> list = new ArrayList<>();
+        list.add("food");
+        Iterator<String> iterator = list.iterator();
+        iterator.next();
+        list.clear();
+        assertFalse(iterator.hasNext());
+    }
+
     public void test_forEach() throws Exception {
         ArrayList<Integer> list = new ArrayList<>();
         list.add(0);
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
index a6f5626..a9f64a2 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
@@ -1475,6 +1475,48 @@
         testRemoveIfCME(Vector<Integer>::new);
     }
 
+    // http://b/25867131 et al.
+    public void testIteratorAddAfterCompleteIteration() {
+        Vector<String> strings = new Vector<>();
+        strings.add("string1");
+        Iterator<String> it = strings.iterator();
+        assertTrue(it.hasNext());
+        assertEquals("string1", it.next());
+        assertFalse(it.hasNext());
+        strings.add("string2");
+        // The value of hasNext() must not flap between true and false. If we returned "true"
+        // here, we'd fail with a CME on the next call to next() anyway.
+        assertFalse(it.hasNext());
+    }
+
+    public void testHasNextAfterRemoval() {
+        Vector<String> strings = new Vector<>();
+        strings.add("string1");
+        Iterator<String> it = strings.iterator();
+        it.next();
+        it.remove();
+        assertFalse(it.hasNext());
+
+        strings = new Vector<>();
+        strings.add("string1");
+        strings.add("string2");
+        it = strings.iterator();
+        it.next();
+        it.remove();
+        assertTrue(it.hasNext());
+        assertEquals("string2", it.next());
+    }
+
+    // http://b/27430229
+    public void testRemoveAllDuringIteration() {
+        Vector<String> vector = new Vector<>();
+        vector.add("food");
+        Iterator<String> vectorIterator = vector.iterator();
+        vectorIterator.next();
+        vector.removeAllElements();
+        assertFalse(vectorIterator.hasNext());
+    }
+
     /**
      * Sets up the fixture, for example, open a network connection. This method
      * is called before a test is executed.
diff --git a/ojluni/src/main/java/java/util/Vector.java b/ojluni/src/main/java/java/util/Vector.java
index 6afdfbd..d51c47e 100755
--- a/ojluni/src/main/java/java/util/Vector.java
+++ b/ojluni/src/main/java/java/util/Vector.java
@@ -1121,21 +1121,26 @@
      * An optimized version of AbstractList.Itr
      */
     private class Itr implements Iterator<E> {
+        // The "limit" of this iterator. This is the size of the list at the time the
+        // iterator was created. Adding & removing elements will invalidate the iteration
+        // anyway (and cause next() to throw) so saving this value will guarantee that the
+        // value of hasNext() remains stable and won't flap between true and false when elements
+        // are added and removed from the list.
+        protected int limit = Vector.this.elementCount;
+
         int cursor;       // index of next element to return
         int lastRet = -1; // index of last element returned; -1 if no such
         int expectedModCount = modCount;
 
         public boolean hasNext() {
-            // Racy but within spec, since modifications are checked
-            // within or after synchronization in next/previous
-            return cursor != elementCount;
+            return cursor < limit;
         }
 
         public E next() {
             synchronized (Vector.this) {
                 checkForComodification();
                 int i = cursor;
-                if (i >= elementCount)
+                if (i >= limit)
                     throw new NoSuchElementException();
                 cursor = i + 1;
                 return elementData(lastRet = i);
@@ -1149,6 +1154,7 @@
                 checkForComodification();
                 Vector.this.remove(lastRet);
                 expectedModCount = modCount;
+                limit--;
             }
             cursor = lastRet;
             lastRet = -1;
@@ -1158,7 +1164,7 @@
         public void forEachRemaining(Consumer<? super E> action) {
             Objects.requireNonNull(action);
             synchronized (Vector.this) {
-                final int size = elementCount;
+                final int size = limit;
                 int i = cursor;
                 if (i >= size) {
                     return;