Merge "Fix NumberFormat's behavior with BigInteger and custom Number subclasses."
diff --git a/libcore/icu/src/main/java/com/ibm/icu4jni/text/NativeDecimalFormat.java b/libcore/icu/src/main/java/com/ibm/icu4jni/text/NativeDecimalFormat.java
index fd22ed0..c8fc252 100644
--- a/libcore/icu/src/main/java/com/ibm/icu4jni/text/NativeDecimalFormat.java
+++ b/libcore/icu/src/main/java/com/ibm/icu4jni/text/NativeDecimalFormat.java
@@ -218,7 +218,32 @@
        // Get new value by multiplying multiplier.
        return valBigDecimal.multiply(multiplierBigDecimal);
     }
-
+    
+    public StringBuffer formatBigDecimal(BigDecimal value, StringBuffer buffer, FieldPosition field) {
+        if (buffer == null || field == null) {
+            throw new NullPointerException();
+        }
+        if (getMultiplier() != 1) {
+            value = applyMultiplier(value);
+        }
+        StringBuilder val = new StringBuilder();
+        val.append(value.unscaledValue().toString(10));
+        int scale = value.scale();
+        scale = makeScalePositive(scale, val);
+        String fieldType = getFieldType(field.getFieldAttribute());
+        String result = format(this.addr, val.toString(), field, fieldType, null, scale);
+        return buffer.append(result);
+    }
+    
+    public StringBuffer formatBigInteger(BigInteger value, StringBuffer buffer, FieldPosition field) {
+        if (buffer == null || field == null) {
+            throw new NullPointerException();
+        }
+        String fieldType = getFieldType(field.getFieldAttribute());
+        String result = format(this.addr, value.toString(10), field, fieldType, null, 0);
+        return buffer.append(result);
+    }
+    /*
     public StringBuffer format(Object value, StringBuffer buffer, FieldPosition field) {
         if (!(value instanceof Number)) {
             throw new IllegalArgumentException();
@@ -228,21 +253,6 @@
         }
         String fieldType = getFieldType(field.getFieldAttribute());
         Number number = (Number) value;
-        if (number instanceof BigInteger) {
-            BigInteger valBigInteger = (BigInteger) number;
-            String result = format(this.addr, valBigInteger.toString(10), field, fieldType, null, 0);
-            return buffer.append(result);
-        } else if (number instanceof BigDecimal) {
-            BigDecimal valBigDecimal = (BigDecimal) number;
-            if (getMultiplier() != 1) {
-                valBigDecimal = applyMultiplier(valBigDecimal);
-            }
-            StringBuilder val = new StringBuilder();
-            val.append(valBigDecimal.unscaledValue().toString(10));
-            int scale = valBigDecimal.scale();
-            scale = makeScalePositive(scale, val);
-            String result = format(this.addr, val.toString(), field, fieldType, null, scale);
-            return buffer.append(result);
         } else if (number instanceof Double || number instanceof Float) {
             double dv = number.doubleValue();
             String result = format(this.addr, dv, field, fieldType, null);
@@ -253,7 +263,7 @@
             return buffer.append(result);
         }
     }
-
+*/
     public StringBuffer format(long value, StringBuffer buffer, FieldPosition field) {
         if (buffer == null || field == null) {
             throw new NullPointerException();
diff --git a/libcore/luni/src/test/java/java/text/NumberFormatTest.java b/libcore/luni/src/test/java/java/text/NumberFormatTest.java
index 3626a44..66e1759 100644
--- a/libcore/luni/src/test/java/java/text/NumberFormatTest.java
+++ b/libcore/luni/src/test/java/java/text/NumberFormatTest.java
@@ -19,9 +19,49 @@
 import junit.framework.Test;
 import junit.framework.TestSuite;
 
+import java.math.BigInteger;
 import java.util.Locale;
 
 public class NumberFormatTest extends junit.framework.TestCase {
+    // NumberFormat.format(Object, StringBuffer, FieldPosition) guarantees it calls doubleValue for
+    // custom Number subclasses.
+    public void test_custom_Number_gets_longValue() throws Exception {
+        class MyNumber extends Number {
+            public byte byteValue() { throw new UnsupportedOperationException(); }
+            public double doubleValue() { return 123; }
+            public float floatValue() { throw new UnsupportedOperationException(); }
+            public int intValue() { throw new UnsupportedOperationException(); }
+            public long longValue() { throw new UnsupportedOperationException(); }
+            public short shortValue() { throw new UnsupportedOperationException(); }
+            public String toString() { throw new UnsupportedOperationException(); }
+        }
+        NumberFormat nf = NumberFormat.getNumberInstance(Locale.US);
+        assertEquals("123", nf.format(new MyNumber()));
+    }
+
+    // NumberFormat.format(Object, StringBuffer, FieldPosition) guarantees it calls longValue for
+    // any BigInteger with a bitLength strictly less than 64.
+    public void test_small_BigInteger_gets_longValue() throws Exception {
+        class MyNumberFormat extends NumberFormat {
+            public StringBuffer format(double value, StringBuffer b, FieldPosition f) {
+                b.append("double");
+                return b;
+            }
+            public StringBuffer format(long value, StringBuffer b, FieldPosition f) {
+                b.append("long");
+                return b;
+            }
+            public Number parse(String string, ParsePosition p) {
+                throw new UnsupportedOperationException();
+            }
+        }
+        NumberFormat nf = new MyNumberFormat();
+        assertEquals("long", nf.format(BigInteger.valueOf(Long.MAX_VALUE)));
+        assertEquals("double", nf.format(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE)));
+        assertEquals("long", nf.format(BigInteger.valueOf(Long.MIN_VALUE)));
+        assertEquals("double", nf.format(BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE)));
+    }
+
     public void test_getIntegerInstance_ar() throws Exception {
         NumberFormat numberFormat = NumberFormat.getNumberInstance(new Locale("ar"));
         assertEquals("#,##0.###;#,##0.###-", ((DecimalFormat) numberFormat).toPattern());
diff --git a/libcore/text/src/main/java/java/text/DateFormat.java b/libcore/text/src/main/java/java/text/DateFormat.java
index 8c9ded5..bf7ebbe 100644
--- a/libcore/text/src/main/java/java/text/DateFormat.java
+++ b/libcore/text/src/main/java/java/text/DateFormat.java
@@ -26,9 +26,6 @@
 import java.util.Date;
 import java.util.Hashtable;
 import java.util.Locale;
-// BEGIN android-added
-import java.util.ResourceBundle;
-// BEGIN android-added
 import java.util.TimeZone;
 
 import com.ibm.icu4jni.util.LocaleData;
diff --git a/libcore/text/src/main/java/java/text/DateFormatSymbols.java b/libcore/text/src/main/java/java/text/DateFormatSymbols.java
index 184cdcf..ac23ad2 100644
--- a/libcore/text/src/main/java/java/text/DateFormatSymbols.java
+++ b/libcore/text/src/main/java/java/text/DateFormatSymbols.java
@@ -26,9 +26,6 @@
 import java.io.Serializable;
 import java.util.Arrays;
 import java.util.Locale;
-// BEGIN android-added
-import java.util.ResourceBundle;
-// END android-added
 
 // BEGIN android-added
 import com.ibm.icu4jni.util.LocaleData;
diff --git a/libcore/text/src/main/java/java/text/DecimalFormat.java b/libcore/text/src/main/java/java/text/DecimalFormat.java
index 0991adb..8d5d022 100644
--- a/libcore/text/src/main/java/java/text/DecimalFormat.java
+++ b/libcore/text/src/main/java/java/text/DecimalFormat.java
@@ -707,89 +707,27 @@
         return dform.formatToCharacterIterator(object);
     }
 
-    /**
-     * Formats the specified double value as a string using the pattern of this
-     * decimal format and appends the string to the specified string buffer.
-     * <p>
-     * If the {@code field} member of {@code position} contains a value
-     * specifying a format field, then its {@code beginIndex} and
-     * {@code endIndex} members will be updated with the position of the first
-     * occurrence of this field in the formatted text.
-     *
-     * @param value
-     *            the double to format.
-     * @param buffer
-     *            the target string buffer to append the formatted double value
-     *            to.
-     * @param position
-     *            on input: an optional alignment field; on output: the offsets
-     *            of the alignment field in the formatted text.
-     * @return the string buffer.
-     */
     @Override
-    public StringBuffer format(double value, StringBuffer buffer,
-            FieldPosition position) {
+    public StringBuffer format(double value, StringBuffer buffer, FieldPosition position) {
         return dform.format(value, buffer, position);
     }
 
-    /**
-     * Formats the specified long value as a string using the pattern of this
-     * decimal format and appends the string to the specified string buffer.
-     * <p>
-     * If the {@code field} member of {@code position} contains a value
-     * specifying a format field, then its {@code beginIndex} and
-     * {@code endIndex} members will be updated with the position of the first
-     * occurrence of this field in the formatted text.
-     *
-     * @param value
-     *            the long to format.
-     * @param buffer
-     *            the target string buffer to append the formatted long value
-     *            to.
-     * @param position
-     *            on input: an optional alignment field; on output: the offsets
-     *            of the alignment field in the formatted text.
-     * @return the string buffer.
-     */
     @Override
-    public StringBuffer format(long value, StringBuffer buffer,
-            FieldPosition position) {
+    public StringBuffer format(long value, StringBuffer buffer, FieldPosition position) {
         return dform.format(value, buffer, position);
     }
 
-    /**
-     * Formats the specified object as a string using the pattern of this
-     * decimal format and appends the string to the specified string buffer.
-     * <p>
-     * If the {@code field} member of {@code position} contains a value
-     * specifying a format field, then its {@code beginIndex} and
-     * {@code endIndex} members will be updated with the position of the first
-     * occurrence of this field in the formatted text.
-     *
-     * @param number
-     *            the object to format.
-     * @param toAppendTo
-     *            the target string buffer to append the formatted number to.
-     * @param pos
-     *            on input: an optional alignment field; on output: the offsets
-     *            of the alignment field in the formatted text.
-     * @return the string buffer.
-     * @throws IllegalArgumentException
-     *             if {@code number} is not an instance of {@code Number}.
-     * @throws NullPointerException
-     *             if {@code toAppendTo} or {@code pos} is {@code null}.
-     */
     @Override
-    public final StringBuffer format(Object number, StringBuffer toAppendTo,
-            FieldPosition pos) {
-        if (!(number instanceof Number)) {
-            throw new IllegalArgumentException();
-        }
-        if (toAppendTo == null || pos == null) {
-            throw new NullPointerException();
-        }
-        if (number instanceof BigInteger || number instanceof BigDecimal) {
-            return dform.format(number, toAppendTo, pos);
+    public final StringBuffer format(Object number, StringBuffer toAppendTo, FieldPosition pos) {
+        if (number instanceof BigInteger) {
+            BigInteger bigInteger = (BigInteger) number;
+            if (bigInteger.bitLength() < 64) {
+                return dform.format(bigInteger.longValue(), toAppendTo, pos);
+            } else {
+                return dform.formatBigInteger(bigInteger, toAppendTo, pos);
+            }
+        } else if (number instanceof BigDecimal) {
+            return dform.formatBigDecimal((BigDecimal) number, toAppendTo, pos);
         }
         return super.format(number, toAppendTo, pos);
     }
diff --git a/libcore/text/src/main/java/java/text/Format.java b/libcore/text/src/main/java/java/text/Format.java
index eb1b837..567b0f6 100644
--- a/libcore/text/src/main/java/java/text/Format.java
+++ b/libcore/text/src/main/java/java/text/Format.java
@@ -22,7 +22,6 @@
 import java.security.PrivilegedAction;
 // BEGIN android-added
 import java.util.Locale;
-import java.util.ResourceBundle;
 // END android-added
 
 import org.apache.harmony.text.internal.nls.Messages;
diff --git a/libcore/text/src/main/java/java/text/NumberFormat.java b/libcore/text/src/main/java/java/text/NumberFormat.java
index 5f6693d..0ad6ac4 100644
--- a/libcore/text/src/main/java/java/text/NumberFormat.java
+++ b/libcore/text/src/main/java/java/text/NumberFormat.java
@@ -26,11 +26,9 @@
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.ObjectStreamField;
+import java.math.BigInteger;
 import java.util.Currency;
 import java.util.Locale;
-// BEGIN android-added
-import java.util.ResourceBundle;
-// END android-added
 
 import com.ibm.icu4jni.util.LocaleData;
 import org.apache.harmony.text.internal.nls.Messages;
@@ -242,8 +240,7 @@
      *            of the alignment field in the formatted text.
      * @return the string buffer.
      */
-    public abstract StringBuffer format(double value, StringBuffer buffer,
-            FieldPosition field);
+    public abstract StringBuffer format(double value, StringBuffer buffer, FieldPosition field);
 
     /**
      * Formats the specified long using the rules of this number format.
@@ -276,12 +273,15 @@
      *            of the alignment field in the formatted text.
      * @return the string buffer.
      */
-    public abstract StringBuffer format(long value, StringBuffer buffer,
-            FieldPosition field);
+    public abstract StringBuffer format(long value, StringBuffer buffer, FieldPosition field);
 
     /**
-     * Formats the specified object as a string using the pattern of this number
-     * format and appends the string to the specified string buffer.
+     * Formats a number into a supplied buffer.
+     * <p>
+     * The number must be a subclass of {@code Number}. Instances of {@code Byte}, {@code Short},
+     * {@code Integer}, and {@code Long} have {@code Number.longValue} invoked, as do instances of
+     * {@code BigInteger} where {@code BigInteger.bitLength} returns <i>less than</i> 64. All other
+     * values have {@code Number.doubleValue} invoked instead.
      * <p>
      * If the {@code field} member of {@code field} contains a value specifying
      * a format field, then its {@code beginIndex} and {@code endIndex} members
@@ -300,14 +300,15 @@
      *             if {@code object} is not an instance of {@code Number}.
      */
     @Override
-    public StringBuffer format(Object object, StringBuffer buffer,
-            FieldPosition field) {
-        if (object instanceof Double || object instanceof Float) {
-            double dv = ((Number) object).doubleValue();
-            return format(dv, buffer, field);
-        } else if (object instanceof Number) {
+    public StringBuffer format(Object object, StringBuffer buffer, FieldPosition field) {
+        if (object instanceof Byte || object instanceof Short || object instanceof Integer ||
+                object instanceof Long ||
+                (object instanceof BigInteger && ((BigInteger) object).bitLength() < 64)) {
             long lv = ((Number) object).longValue();
             return format(lv, buffer, field);
+        } else if (object instanceof Number) {
+            double dv = ((Number) object).doubleValue();
+            return format(dv, buffer, field);
         }
         throw new IllegalArgumentException();
     }
diff --git a/libcore/text/src/main/java/java/text/SimpleDateFormat.java b/libcore/text/src/main/java/java/text/SimpleDateFormat.java
index e2422ad..a67c7e6 100644
--- a/libcore/text/src/main/java/java/text/SimpleDateFormat.java
+++ b/libcore/text/src/main/java/java/text/SimpleDateFormat.java
@@ -1040,7 +1040,6 @@
      * @return the {@code DateFormatSymbols} object.
      */
     public DateFormatSymbols getDateFormatSymbols() {
-        // Return a clone so the arrays in the ResourceBundle are not modified
         return (DateFormatSymbols) formatData.clone();
     }