Merge "Add Java 6's exponent separator to DecimalFormatSymbols." into dalvik-dev
diff --git a/icu/src/main/java/com/ibm/icu4jni/util/LocaleData.java b/icu/src/main/java/com/ibm/icu4jni/util/LocaleData.java
index 109183d..e8572a2 100644
--- a/icu/src/main/java/com/ibm/icu4jni/util/LocaleData.java
+++ b/icu/src/main/java/com/ibm/icu4jni/util/LocaleData.java
@@ -52,6 +52,7 @@
     
     public String decimalPatternChars;
     
+    public String exponentSeparator;
     public String infinity;
     public String NaN;
     
@@ -82,6 +83,7 @@
                 "mediumDateFormat=" + mediumDateFormat + "," +
                 "shortDateFormat=" + shortDateFormat + "," +
                 "decimalPatternChars=" + decimalPatternChars + "," +
+                "exponentSeparator=" + exponentSeparator + "," +
                 "infinity=" + infinity + "," +
                 "NaN=" + NaN + "," +
                 "currencySymbol=" + currencySymbol + "," +
@@ -144,6 +146,9 @@
         if (overrides.decimalPatternChars != null) {
             decimalPatternChars = overrides.decimalPatternChars;
         }
+        if (overrides.exponentSeparator != null) {
+            exponentSeparator = overrides.exponentSeparator;
+        }
         if (overrides.NaN != null) {
             NaN = overrides.NaN;
         }
diff --git a/icu/src/main/native/Resources.cpp b/icu/src/main/native/Resources.cpp
index 00bf746..9dbdb3d 100644
--- a/icu/src/main/native/Resources.cpp
+++ b/icu/src/main/native/Resources.cpp
@@ -514,7 +514,7 @@
 static jstring getDecimalPatternChars(JNIEnv* env, UResourceBundle* rootElems) {
     UErrorCode status = U_ZERO_ERROR;
 
-    int zeroL, digitL, decSepL, groupL, listL, percentL, permillL, expL, currSepL, minusL;
+    int zeroL, digitL, decSepL, groupL, listL, percentL, permillL, currSepL, minusL;
 
     const jchar* zero = ures_getStringByIndex(rootElems, 4, &zeroL, &status);
     const jchar* digit = ures_getStringByIndex(rootElems, 5, &digitL, &status);
@@ -523,7 +523,6 @@
     const jchar* list = ures_getStringByIndex(rootElems, 2, &listL, &status);
     const jchar* percent = ures_getStringByIndex(rootElems, 3, &percentL, &status);
     const jchar* permill = ures_getStringByIndex(rootElems, 8, &permillL, &status);
-    const jchar* exp = ures_getStringByIndex(rootElems, 7, &expL, &status);
     const jchar* currSep = ures_getStringByIndex(rootElems, 0, &currSepL, &status);
     const jchar* minus = ures_getStringByIndex(rootElems, 6, &minusL, &status);
 
@@ -531,21 +530,18 @@
         return NULL;
     }
 
-    jchar patternChars[11];
-    patternChars[0] = 0;
-
-    u_strncat(patternChars, zero, 1);
-    u_strncat(patternChars, digit, 1);
-    u_strncat(patternChars, decSep, 1);
-    u_strncat(patternChars, group, 1);
-    u_strncat(patternChars, list, 1);
-    u_strncat(patternChars, percent, 1);
-    u_strncat(patternChars, permill, 1);
-    u_strncat(patternChars, exp, 1);
-    u_strncat(patternChars, currSep, 1);
-    u_strncat(patternChars, minus, 1);
-
-    return env->NewString(patternChars, 10);
+    jchar patternChars[] = {
+        zero[0],
+        digit[0],
+        decSep[0],
+        group[0],
+        list[0],
+        percent[0],
+        permill[0],
+        currSep[0],
+        minus[0],
+    };
+    return env->NewString(patternChars, sizeof(patternChars));
 }
 
 static jstring getIntCurrencyCode(JNIEnv* env, jstring locale) {
@@ -600,6 +596,8 @@
     const UChar* chars = ures_getStringByIndex(bundle, index, &charCount, &status);
     if (U_SUCCESS(status)) {
         setStringField(env, obj, fieldName, env->NewString(chars, charCount));
+    } else {
+        LOGE("Error setting field %s from ICU resource: %s", fieldName, u_errorName(status));
     }
 }
 
@@ -656,6 +654,7 @@
     ScopedResourceBundle numberElements(ures_getByKey(root.get(), "NumberElements", NULL, &status));
     if (U_SUCCESS(status) && ures_getSize(numberElements.get()) >= 11) {
         setStringField(env, localeData, "decimalPatternChars", getDecimalPatternChars(env, numberElements.get()));
+        setStringField(env, localeData, "exponentSeparator", numberElements.get(), 7);
         setStringField(env, localeData, "infinity", numberElements.get(), 9);
         setStringField(env, localeData, "NaN", numberElements.get(), 10);
     }
diff --git a/text/src/main/java/java/text/DecimalFormatSymbols.java b/text/src/main/java/java/text/DecimalFormatSymbols.java
index 8015c9d..2236486 100644
--- a/text/src/main/java/java/text/DecimalFormatSymbols.java
+++ b/text/src/main/java/java/text/DecimalFormatSymbols.java
@@ -51,16 +51,15 @@
     private static final int PATTERN_SEPARATOR = 4;
     private static final int PERCENT = 5;
     private static final int PER_MILL = 6;
-    private static final int EXPONENT = 7;
-    private static final int MONETARY_DECIMAL_SEPARATOR = 8;
-    private static final int MINUS_SIGN = 9;
+    private static final int MONETARY_DECIMAL_SEPARATOR = 7;
+    private static final int MINUS_SIGN = 8;
 
     // TODO: replace this with individual char fields.
     private transient char[] patternChars;
 
     private transient Currency currency;
-
     private transient Locale locale;
+    private transient String exponentSeparator;
 
     private String infinity, NaN, currencySymbol, intlCurrencySymbol;
 
@@ -89,13 +88,14 @@
         this.patternChars = localeData.decimalPatternChars.toCharArray();
         this.infinity = localeData.infinity;
         this.NaN = localeData.NaN;
+        this.exponentSeparator = localeData.exponentSeparator;
         this.locale = locale;
         try {
             currency = Currency.getInstance(locale);
             currencySymbol = currency.getSymbol(locale);
             intlCurrencySymbol = currency.getCurrencyCode();
         } catch (IllegalArgumentException e) {
-            currency = Currency.getInstance("XXX"); //$NON-NLS-1$
+            currency = Currency.getInstance("XXX");
             currencySymbol = localeData.currencySymbol;
             intlCurrencySymbol = localeData.internationalCurrencySymbol;
         }
@@ -178,21 +178,41 @@
             return false;
         }
         DecimalFormatSymbols obj = (DecimalFormatSymbols) object;
-        return Arrays.equals(patternChars, obj.patternChars)
-                && infinity.equals(obj.infinity) && NaN.equals(obj.NaN)
-                && currencySymbol.equals(obj.currencySymbol)
-                && intlCurrencySymbol.equals(obj.intlCurrencySymbol);
+        return currency.equals(obj.currency) &&
+                currencySymbol.equals(obj.currencySymbol) &&
+                patternChars[DECIMAL_SEPARATOR] == obj.patternChars[DECIMAL_SEPARATOR] &&
+                patternChars[DIGIT] == obj.patternChars[DIGIT] &&
+                exponentSeparator.equals(obj.exponentSeparator) &&
+                patternChars[GROUPING_SEPARATOR] == obj.patternChars[GROUPING_SEPARATOR] &&
+                infinity.equals(obj.infinity) &&
+                intlCurrencySymbol.equals(obj.intlCurrencySymbol) &&
+                patternChars[MINUS_SIGN] == obj.patternChars[MINUS_SIGN] &&
+                patternChars[MONETARY_DECIMAL_SEPARATOR] == obj.patternChars[MONETARY_DECIMAL_SEPARATOR] &&
+                NaN.equals(obj.NaN) &&
+                patternChars[PATTERN_SEPARATOR] == obj.patternChars[PATTERN_SEPARATOR] &&
+                patternChars[PER_MILL] == obj.patternChars[PER_MILL] &&
+                patternChars[PERCENT] == obj.patternChars[PERCENT] &&
+                patternChars[ZERO_DIGIT] == obj.patternChars[ZERO_DIGIT];
     }
 
     @Override
     public String toString() {
-        // Most of the externally-visible state is stashed in 'patternChars', and not obviously
-        // worth breaking out individually, since this is only meant for debugging.
         return getClass().getName() +
-                "[patternChars=" + new String(patternChars) +
-                ",infinity=" + infinity +
+                "[currency=" + currency +
                 ",currencySymbol=" + currencySymbol +
+                ",decimalSeparator=" + patternChars[DECIMAL_SEPARATOR] +
+                ",digit=" + patternChars[DIGIT] +
+                ",exponentSeparator=" + exponentSeparator +
+                ",groupingSeparator=" + patternChars[GROUPING_SEPARATOR] +
+                ",infinity=" + infinity +
                 ",intlCurrencySymbol=" + intlCurrencySymbol +
+                ",minusSign=" + patternChars[MINUS_SIGN] +
+                ",monetaryDecimalSeparator=" + patternChars[MONETARY_DECIMAL_SEPARATOR] +
+                ",NaN=" + NaN +
+                ",patternSeparator=" + patternChars[PATTERN_SEPARATOR] +
+                ",perMill=" + patternChars[PER_MILL] +
+                ",percent=" + patternChars[PERCENT] +
+                ",zeroDigit=" + patternChars[ZERO_DIGIT] +
                 "]";
     }
 
@@ -335,17 +355,19 @@
     }
 
     /*
-     * Returns the exponent as a character.
+     * Returns the string used to separate mantissa and exponent. Typically "E", as in "1.2E3".
+     * @since 1.6
+     * @hide
      */
-    char getExponential() {
-        return patternChars[EXPONENT];
+    public String getExponentSeparator() {
+        return exponentSeparator;
     }
 
     @Override
     public int hashCode() {
-        return new String(patternChars).hashCode() + infinity.hashCode()
-                + NaN.hashCode() + currencySymbol.hashCode()
-                + intlCurrencySymbol.hashCode();
+        return new String(patternChars).hashCode() + exponentSeparator.hashCode() +
+                infinity.hashCode() + NaN.hashCode() + currencySymbol.hashCode() +
+                intlCurrencySymbol.hashCode();
     }
 
     /**
@@ -525,77 +547,94 @@
         patternChars[ZERO_DIGIT] = value;
     }
 
-    /*
-     * Sets the exponent character.
+    /**
+     * Sets the string used to separate mantissa and exponent. Typically "E", as in "1.2E3".
+     * @since 1.6
+     * @hide
      */
-    void setExponential(char value) {
-        patternChars[EXPONENT] = value;
+    public void setExponentSeparator(String value) {
+        if (value == null) {
+            throw new NullPointerException();
+        }
+        this.exponentSeparator = value;
     }
 
     private static final ObjectStreamField[] serialPersistentFields = {
-            new ObjectStreamField("currencySymbol", String.class), //$NON-NLS-1$
-            new ObjectStreamField("decimalSeparator", Character.TYPE), //$NON-NLS-1$
-            new ObjectStreamField("digit", Character.TYPE), //$NON-NLS-1$
-            new ObjectStreamField("exponential", Character.TYPE), //$NON-NLS-1$
-            new ObjectStreamField("groupingSeparator", Character.TYPE), //$NON-NLS-1$
-            new ObjectStreamField("infinity", String.class), //$NON-NLS-1$
-            new ObjectStreamField("intlCurrencySymbol", String.class), //$NON-NLS-1$
-            new ObjectStreamField("minusSign", Character.TYPE), //$NON-NLS-1$
-            new ObjectStreamField("monetarySeparator", Character.TYPE), //$NON-NLS-1$
-            new ObjectStreamField("NaN", String.class), //$NON-NLS-1$
-            new ObjectStreamField("patternSeparator", Character.TYPE), //$NON-NLS-1$
-            new ObjectStreamField("percent", Character.TYPE), //$NON-NLS-1$
-            new ObjectStreamField("perMill", Character.TYPE), //$NON-NLS-1$
-            new ObjectStreamField("serialVersionOnStream", Integer.TYPE), //$NON-NLS-1$
-            new ObjectStreamField("zeroDigit", Character.TYPE), //$NON-NLS-1$
-            new ObjectStreamField("locale", Locale.class), }; //$NON-NLS-1$
+        new ObjectStreamField("currencySymbol", String.class),
+        new ObjectStreamField("decimalSeparator", Character.TYPE),
+        new ObjectStreamField("digit", Character.TYPE),
+        new ObjectStreamField("exponential", Character.TYPE),
+        new ObjectStreamField("exponentialSeparator", String.class),
+        new ObjectStreamField("groupingSeparator", Character.TYPE),
+        new ObjectStreamField("infinity", String.class),
+        new ObjectStreamField("intlCurrencySymbol", String.class),
+        new ObjectStreamField("minusSign", Character.TYPE),
+        new ObjectStreamField("monetarySeparator", Character.TYPE),
+        new ObjectStreamField("NaN", String.class),
+        new ObjectStreamField("patternSeparator", Character.TYPE),
+        new ObjectStreamField("percent", Character.TYPE),
+        new ObjectStreamField("perMill", Character.TYPE),
+        new ObjectStreamField("serialVersionOnStream", Integer.TYPE),
+        new ObjectStreamField("zeroDigit", Character.TYPE),
+        new ObjectStreamField("locale", Locale.class),
+    };
 
     private void writeObject(ObjectOutputStream stream) throws IOException {
         ObjectOutputStream.PutField fields = stream.putFields();
-        fields.put("currencySymbol", currencySymbol); //$NON-NLS-1$
-        fields.put("decimalSeparator", getDecimalSeparator()); //$NON-NLS-1$
-        fields.put("digit", getDigit()); //$NON-NLS-1$
-        fields.put("exponential", getExponential()); //$NON-NLS-1$
-        fields.put("groupingSeparator", getGroupingSeparator()); //$NON-NLS-1$
-        fields.put("infinity", infinity); //$NON-NLS-1$
-        fields.put("intlCurrencySymbol", intlCurrencySymbol); //$NON-NLS-1$
-        fields.put("minusSign", getMinusSign()); //$NON-NLS-1$
-        fields.put("monetarySeparator", getMonetaryDecimalSeparator()); //$NON-NLS-1$
-        fields.put("NaN", NaN); //$NON-NLS-1$
-        fields.put("patternSeparator", getPatternSeparator()); //$NON-NLS-1$
-        fields.put("percent", getPercent()); //$NON-NLS-1$
-        fields.put("perMill", getPerMill()); //$NON-NLS-1$
-        fields.put("serialVersionOnStream", 1); //$NON-NLS-1$
-        fields.put("zeroDigit", getZeroDigit()); //$NON-NLS-1$
-        fields.put("locale", locale); //$NON-NLS-1$
+        fields.put("currencySymbol", currencySymbol);
+        fields.put("decimalSeparator", getDecimalSeparator());
+        fields.put("digit", getDigit());
+        fields.put("exponential", exponentSeparator.charAt(0));
+        fields.put("exponentialSeparator", exponentSeparator);
+        fields.put("groupingSeparator", getGroupingSeparator());
+        fields.put("infinity", infinity);
+        fields.put("intlCurrencySymbol", intlCurrencySymbol);
+        fields.put("minusSign", getMinusSign());
+        fields.put("monetarySeparator", getMonetaryDecimalSeparator());
+        fields.put("NaN", NaN);
+        fields.put("patternSeparator", getPatternSeparator());
+        fields.put("percent", getPercent());
+        fields.put("perMill", getPerMill());
+        fields.put("serialVersionOnStream", 3);
+        fields.put("zeroDigit", getZeroDigit());
+        fields.put("locale", locale);
         stream.writeFields();
     }
 
-    private void readObject(ObjectInputStream stream) throws IOException,
-            ClassNotFoundException {
+    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
         ObjectInputStream.GetField fields = stream.readFields();
-        patternChars = new char[10];
-        currencySymbol = (String) fields.get("currencySymbol", ""); //$NON-NLS-1$ //$NON-NLS-2$
-        setDecimalSeparator(fields.get("decimalSeparator", '.')); //$NON-NLS-1$
-        setDigit(fields.get("digit", '#')); //$NON-NLS-1$
-        setGroupingSeparator(fields.get("groupingSeparator", ',')); //$NON-NLS-1$
-        infinity = (String) fields.get("infinity", ""); //$NON-NLS-1$ //$NON-NLS-2$
-        intlCurrencySymbol = (String) fields.get("intlCurrencySymbol", ""); //$NON-NLS-1$ //$NON-NLS-2$
-        setMinusSign(fields.get("minusSign", '-')); //$NON-NLS-1$
-        NaN = (String) fields.get("NaN", ""); //$NON-NLS-1$ //$NON-NLS-2$
-        setPatternSeparator(fields.get("patternSeparator", ';')); //$NON-NLS-1$
-        setPercent(fields.get("percent", '%')); //$NON-NLS-1$
-        setPerMill(fields.get("perMill", '\u2030')); //$NON-NLS-1$
-        setZeroDigit(fields.get("zeroDigit", '0')); //$NON-NLS-1$
-        locale = (Locale) fields.get("locale", null); //$NON-NLS-1$
-        if (fields.get("serialVersionOnStream", 0) == 0) { //$NON-NLS-1$
+        final int serialVersionOnStream = fields.get("serialVersionOnStream", 0);
+        patternChars = new char[9];
+        currencySymbol = (String) fields.get("currencySymbol", "");
+        setDecimalSeparator(fields.get("decimalSeparator", '.'));
+        setDigit(fields.get("digit", '#'));
+        setGroupingSeparator(fields.get("groupingSeparator", ','));
+        infinity = (String) fields.get("infinity", "");
+        intlCurrencySymbol = (String) fields.get("intlCurrencySymbol", "");
+        setMinusSign(fields.get("minusSign", '-'));
+        NaN = (String) fields.get("NaN", "");
+        setPatternSeparator(fields.get("patternSeparator", ';'));
+        setPercent(fields.get("percent", '%'));
+        setPerMill(fields.get("perMill", '\u2030'));
+        setZeroDigit(fields.get("zeroDigit", '0'));
+        locale = (Locale) fields.get("locale", null);
+        if (serialVersionOnStream == 0) {
             setMonetaryDecimalSeparator(getDecimalSeparator());
-            setExponential('E');
         } else {
-            setMonetaryDecimalSeparator(fields.get("monetarySeparator", '.')); //$NON-NLS-1$
-            setExponential(fields.get("exponential", 'E')); //$NON-NLS-1$
-
+            setMonetaryDecimalSeparator(fields.get("monetarySeparator", '.'));
         }
+
+        if (serialVersionOnStream == 0) {
+            // Prior to Java 1.1.6, the exponent separator wasn't configurable.
+            exponentSeparator = "E";
+        } else if (serialVersionOnStream < 3) {
+            // In Javas 1.1.6 and 1.4, there was a character field "exponential".
+            setExponentSeparator(String.valueOf(fields.get("exponential", 'E')));
+        } else {
+            // In Java 6, there's a new "exponentialSeparator" field.
+            setExponentSeparator((String) fields.get("exponentialSeparator", "E"));
+        }
+
         try {
             currency = Currency.getInstance(intlCurrencySymbol);
         } catch (IllegalArgumentException e) {
diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatSymbolsTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatSymbolsTest.java
index 574d310..604f0ea 100644
--- a/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatSymbolsTest.java
+++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatSymbolsTest.java
@@ -309,6 +309,15 @@
     }
 
     /**
+     * @tests java.text.DecimalFormatSymbols#getExponentSeparator()
+     */
+    public void test_getExponentSeparator() {
+        dfs.setExponentSeparator("EE");
+        assertEquals("Returned incorrect Exponent Separator symbol", "EE", dfs
+                .getExponentSeparator());
+    }
+
+    /**
      * @tests java.text.DecimalFormatSymbols#getGroupingSeparator()
      */
     @TestTargetNew(
@@ -566,6 +575,30 @@
     }
 
     /**
+     * @tests java.text.DecimalFormatSymbols#setExponentSeparator(String)
+     */
+    public void test_setExponentSeparator() {
+        try {
+            dfs.setExponentSeparator(null);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+
+        dfs.setExponentSeparator("");
+        assertEquals("Returned incorrect Exponent Separator symbol", "", dfs
+                .getExponentSeparator());
+
+        dfs.setExponentSeparator("what ever you want");
+        assertEquals("Returned incorrect Exponent Separator symbol",
+                "what ever you want", dfs.getExponentSeparator());
+
+        dfs.setExponentSeparator(" E ");
+        assertEquals("Returned incorrect Exponent Separator symbol", " E ", dfs
+                .getExponentSeparator());
+    }
+
+    /**
      * @tests java.text.DecimalFormatSymbols#setGroupingSeparator(char)
      */
     @TestTargetNew(