Refactor Arrays.copyOf, Arrays.copyOfRange and throw AIOBE.

Change-Id: I92f47ba509deabae1485a52e47e1814b41f8417a
diff --git a/user/super/com/google/gwt/emul/java/util/Arrays.java b/user/super/com/google/gwt/emul/java/util/Arrays.java
index b288ec2..6fddd5d 100644
--- a/user/super/com/google/gwt/emul/java/util/Arrays.java
+++ b/user/super/com/google/gwt/emul/java/util/Arrays.java
@@ -20,7 +20,6 @@
 import static javaemul.internal.InternalPreconditions.checkArgument;
 import static javaemul.internal.InternalPreconditions.checkArraySize;
 import static javaemul.internal.InternalPreconditions.checkCriticalArrayBounds;
-import static javaemul.internal.InternalPreconditions.checkCriticalPositionIndexes;
 import static javaemul.internal.InternalPreconditions.checkElementIndex;
 import static javaemul.internal.InternalPreconditions.checkNotNull;
 
@@ -526,115 +525,113 @@
 
   public static boolean[] copyOf(boolean[] original, int newLength) {
     checkArraySize(newLength);
-    return copyOfRange(original, 0, newLength);
+    return copyPrimitiveArray(original, new boolean[newLength], 0, newLength);
   }
 
   public static byte[] copyOf(byte[] original, int newLength) {
     checkArraySize(newLength);
-    return copyOfRange(original, 0, newLength);
+    return copyPrimitiveArray(original, new byte[newLength], 0, newLength);
   }
 
   public static char[] copyOf(char[] original, int newLength) {
     checkArraySize(newLength);
-    return copyOfRange(original, 0, newLength);
+    return copyPrimitiveArray(original, new char[newLength], 0, newLength);
   }
 
   public static double[] copyOf(double[] original, int newLength) {
     checkArraySize(newLength);
-    return copyOfRange(original, 0, newLength);
+    return copyPrimitiveArray(original, new double[newLength], 0, newLength);
   }
 
   public static float[] copyOf(float[] original, int newLength) {
     checkArraySize(newLength);
-    return copyOfRange(original, 0, newLength);
+    return copyPrimitiveArray(original, new float[newLength], 0, newLength);
   }
 
   public static int[] copyOf(int[] original, int newLength) {
     checkArraySize(newLength);
-    return copyOfRange(original, 0, newLength);
+    return copyPrimitiveArray(original, new int[newLength], 0, newLength);
   }
 
   public static long[] copyOf(long[] original, int newLength) {
     checkArraySize(newLength);
-    return copyOfRange(original, 0, newLength);
+    return copyPrimitiveArray(original, new long[newLength], 0, newLength);
   }
 
   public static short[] copyOf(short[] original, int newLength) {
     checkArraySize(newLength);
-    return copyOfRange(original, 0, newLength);
+    return copyPrimitiveArray(original, new short[newLength], 0, newLength);
   }
 
   public static <T> T[] copyOf(T[] original, int newLength) {
     checkArraySize(newLength);
-    checkNotNull(original, "original");
-    T[] clone = ArrayHelper.clone(original, 0, newLength);
-    ArrayHelper.setLength(clone, newLength);
-    return clone;
+    return copyObjectArray(original, 0, newLength);
   }
 
   public static boolean[] copyOfRange(boolean[] original, int from, int to) {
-    int len = getCopyLength(original, from, to);
-    boolean[] copy = new boolean[to - from];
-    ArrayHelper.copy(original, from, copy, 0, len);
-    return copy;
+    checkCopyOfRange(original, from, to);
+    return copyPrimitiveArray(original, new boolean[to - from], from, to);
   }
 
   public static byte[] copyOfRange(byte[] original, int from, int to) {
-    int len = getCopyLength(original, from, to);
-    byte[] copy = new byte[to - from];
-    ArrayHelper.copy(original, from, copy, 0, len);
-    return copy;
+    checkCopyOfRange(original, from, to);
+    return copyPrimitiveArray(original, new byte[to - from], from, to);
   }
 
   public static char[] copyOfRange(char[] original, int from, int to) {
-    int len = getCopyLength(original, from, to);
-    char[] copy = new char[to - from];
-    ArrayHelper.copy(original, from, copy, 0, len);
-    return copy;
+    checkCopyOfRange(original, from, to);
+    return copyPrimitiveArray(original, new char[to - from], from, to);
   }
 
   public static double[] copyOfRange(double[] original, int from, int to) {
-    int len = getCopyLength(original, from, to);
-    double[] copy = new double[to - from];
-    ArrayHelper.copy(original, from, copy, 0, len);
-    return copy;
+    checkCopyOfRange(original, from, to);
+    return copyPrimitiveArray(original, new double[to - from], from, to);
   }
 
   public static float[] copyOfRange(float[] original, int from, int to) {
-    int len = getCopyLength(original, from, to);
-    float[] copy = new float[to - from];
-    ArrayHelper.copy(original, from, copy, 0, len);
-    return copy;
+    checkCopyOfRange(original, from, to);
+    return copyPrimitiveArray(original, new float[to - from], from, to);
   }
 
   public static int[] copyOfRange(int[] original, int from, int to) {
-    int len = getCopyLength(original, from, to);
-    int[] copy = new int[to - from];
-    ArrayHelper.copy(original, from, copy, 0, len);
-    return copy;
+    checkCopyOfRange(original, from, to);
+    return copyPrimitiveArray(original, new int[to - from], from, to);
   }
 
   public static long[] copyOfRange(long[] original, int from, int to) {
-    int len = getCopyLength(original, from, to);
-    long[] copy = new long[to - from];
-    ArrayHelper.copy(original, from, copy, 0, len);
-    return copy;
+    checkCopyOfRange(original, from, to);
+    return copyPrimitiveArray(original, new long[to - from], from, to);
   }
 
   public static short[] copyOfRange(short[] original, int from, int to) {
-    int len = getCopyLength(original, from, to);
-    short[] copy = new short[to - from];
-    ArrayHelper.copy(original, from, copy, 0, len);
-    return copy;
+    checkCopyOfRange(original, from, to);
+    return copyPrimitiveArray(original, new short[to - from], from, to);
   }
 
   public static <T> T[] copyOfRange(T[] original, int from, int to) {
-    int len = getCopyLength(original, from, to);
-    T[] copy = ArrayHelper.createFrom(original, to - from);
-    ArrayHelper.copy(original, from, copy, 0, len);
+    checkCopyOfRange(original, from, to);
+    return copyObjectArray(original, from, to);
+  }
+
+  private static <T> T copyPrimitiveArray(T original, T copy, int from, int to) {
+    int len = ArrayHelper.getLength(original);
+    int copyLen = Math.min(to, len) - from;
+    ArrayHelper.copy(original, from, copy, 0, copyLen);
     return copy;
   }
 
+  private static <T> T[] copyObjectArray(T[] original, int from, int to) {
+    T[] copy = ArrayHelper.clone(original, from, to);
+    ArrayHelper.setLength(copy, to - from);
+    return copy;
+  }
+
+  private static void checkCopyOfRange(Object original, int from, int to) {
+    checkArgument(from <= to, "%s > %s", from, to);
+    int len = ArrayHelper.getLength(original);
+    checkCriticalArrayBounds(from, from, len);
+  }
+
   public static boolean deepEquals(Object[] a1, Object[] a2) {
     if (a1 == a2) {
       return true;
@@ -1632,14 +1629,6 @@
     return joiner.toString();
   }
 
-  private static int getCopyLength(Object array, int from, int to) {
-    checkArgument(from <= to, "%s > %s", from, to);
-    int len = ArrayHelper.getLength(array);
-    to = Math.min(to, len);
-    checkCriticalPositionIndexes(from, to, len);
-    return to - from;
-  }
-
   /**
    * Sort a small subsection of an array by insertion sort.
    *
diff --git a/user/test/com/google/gwt/emultest/java/util/ArraysTest.java b/user/test/com/google/gwt/emultest/java/util/ArraysTest.java
index 8d72b3b..3c65b15 100644
--- a/user/test/com/google/gwt/emultest/java/util/ArraysTest.java
+++ b/user/test/com/google/gwt/emultest/java/util/ArraysTest.java
@@ -540,6 +540,9 @@
     ret = Arrays.copyOf(a1, 2);
     assertTrue(Arrays.equals(new boolean[]{true, true}, ret));
 
+    ret = Arrays.copyOf(a1, 0);
+    assertEquals(0, ret.length);
+
     ret = Arrays.copyOf(a1, a1.length * 2);
     assertEquals(a1.length * 2, ret.length);
     int i = 0;
@@ -559,6 +562,9 @@
     for (; i < ret.length; i++) {
       assertEquals(false, ret[i]);
     }
+
+    ret = Arrays.copyOf(new boolean[] {false, true}, 3);
+    assertTrue(Arrays.equals(new boolean[] {false, true, false}, ret));
   }
 
   /**
@@ -573,6 +579,9 @@
     ret = Arrays.copyOf(a1, 2);
     assertTrue(Arrays.equals(new byte[] {9, 8}, ret));
 
+    ret = Arrays.copyOf(a1, 0);
+    assertEquals(0, ret.length);
+
     ret = Arrays.copyOf(a1, a1.length * 2);
     assertEquals(a1.length * 2, ret.length);
     int i = 0;
@@ -592,6 +601,9 @@
     for (; i < ret.length; i++) {
       assertEquals((byte) 0, ret[i]);
     }
+
+    ret = Arrays.copyOf(new byte[] {1, 2}, 3);
+    assertTrue(Arrays.equals(new byte[] {1, 2, 0}, ret));
   }
 
   /**
@@ -606,6 +618,9 @@
     ret = Arrays.copyOf(a1, 2);
     assertTrue(Arrays.equals(new char[] {9, 8}, ret));
 
+    ret = Arrays.copyOf(a1, 0);
+    assertEquals(0, ret.length);
+
     ret = Arrays.copyOf(a1, a1.length * 2);
     assertEquals(a1.length * 2, ret.length);
     int i = 0;
@@ -625,6 +640,9 @@
     for (; i < ret.length; i++) {
       assertEquals((char) 0, ret[i]);
     }
+
+    ret = Arrays.copyOf(new char[] {1, 2}, 3);
+    assertTrue(Arrays.equals(new char[] {1, 2, 0}, ret));
   }
 
   /**
@@ -639,6 +657,9 @@
     ret = Arrays.copyOf(a1, 2);
     assertTrue(Arrays.equals(new double[] {0.5, 1.25}, ret));
 
+    ret = Arrays.copyOf(a1, 0);
+    assertEquals(0, ret.length);
+
     ret = Arrays.copyOf(a1, a1.length * 2);
     assertEquals(a1.length * 2, ret.length);
     int i = 0;
@@ -658,6 +679,9 @@
     for (; i < ret.length; i++) {
       assertEquals(0., ret[i]);
     }
+
+    ret = Arrays.copyOf(new double[] {1, 2}, 3);
+    assertTrue(Arrays.equals(new double[] {1, 2, 0}, ret));
   }
 
   /**
@@ -672,6 +696,9 @@
     ret = Arrays.copyOf(a1, 2);
     assertTrue(Arrays.equals(new float[] {0.5f, 1.25f}, ret));
 
+    ret = Arrays.copyOf(a1, 0);
+    assertEquals(0, ret.length);
+
     ret = Arrays.copyOf(a1, a1.length * 2);
     assertEquals(a1.length * 2, ret.length);
     int i = 0;
@@ -691,6 +718,9 @@
     for (; i < ret.length; i++) {
       assertEquals(0f, ret[i]);
     }
+
+    ret = Arrays.copyOf(new float[] {1, 2}, 3);
+    assertTrue(Arrays.equals(new float[] {1, 2, 0}, ret));
   }
 
   /**
@@ -705,6 +735,9 @@
     ret = Arrays.copyOf(a1, 2);
     assertTrue(Arrays.equals(new int[] {9, 8}, ret));
 
+    ret = Arrays.copyOf(a1, 0);
+    assertEquals(0, ret.length);
+
     ret = Arrays.copyOf(a1, a1.length * 2);
     assertEquals(a1.length * 2, ret.length);
     int i = 0;
@@ -724,6 +757,9 @@
     for (; i < ret.length; i++) {
       assertEquals(0, ret[i]);
     }
+
+    ret = Arrays.copyOf(new int[] {1, 2}, 3);
+    assertTrue(Arrays.equals(new int[] {1, 2, 0}, ret));
   }
 
   /**
@@ -738,6 +774,9 @@
     ret = Arrays.copyOf(a1, 2);
     assertTrue(Arrays.equals(new long[] {9, 8}, ret));
 
+    ret = Arrays.copyOf(a1, 0);
+    assertEquals(0, ret.length);
+
     ret = Arrays.copyOf(a1, a1.length * 2);
     assertEquals(a1.length * 2, ret.length);
     int i = 0;
@@ -757,6 +796,9 @@
     for (; i < ret.length; i++) {
       assertEquals(0L, ret[i]);
     }
+
+    ret = Arrays.copyOf(new long[] {1, 2}, 3);
+    assertTrue(Arrays.equals(new long[] {1, 2, 0}, ret));
   }
 
   /**
@@ -771,6 +813,9 @@
     ret = Arrays.copyOf(a1, 2);
     assertTrue(Arrays.equals(new short[] {9, 8}, ret));
 
+    ret = Arrays.copyOf(a1, 0);
+    assertEquals(0, ret.length);
+
     ret = Arrays.copyOf(a1, a1.length * 2);
     assertEquals(a1.length * 2, ret.length);
     int i = 0;
@@ -790,6 +835,9 @@
     for (; i < ret.length; i++) {
       assertEquals((short) 0, ret[i]);
     }
+
+    ret = Arrays.copyOf(new short[] {1, 2}, 3);
+    assertTrue(Arrays.equals(new short[] {1, 2, 0}, ret));
   }
 
   /**
@@ -808,6 +856,9 @@
     ret = Arrays.copyOf(a1, 2);
     assertTrue(Arrays.equals(new Object[] {null, obj1}, ret));
 
+    ret = Arrays.copyOf(a1, 0);
+    assertEquals(0, ret.length);
+
     ret = Arrays.copyOf(a1, a1.length * 2);
     assertEquals(a1.length * 2, ret.length);
     int i = 0;
@@ -827,6 +878,9 @@
     for (; i < ret.length; i++) {
       assertEquals(null, ret[i]);
     }
+
+    ret = Arrays.copyOf(new Object[] {obj1, obj2}, 3);
+    assertTrue(Arrays.equals(new Object[] {obj1, obj2, null}, ret));
   }
 
   /**