Merge "Spelling fixes"
diff --git a/libcore/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormat.java b/libcore/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormat.java
deleted file mode 100644
index d995dc6..0000000
--- a/libcore/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormat.java
+++ /dev/null
@@ -1,565 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.ibm.icu4jni.text;
-
-import com.ibm.icu4jni.text.NativeDecimalFormat.UNumberFormatAttribute;
-import com.ibm.icu4jni.text.NativeDecimalFormat.UNumberFormatTextAttribute;
-import com.ibm.icu4jni.util.LocaleData;
-
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.text.AttributedCharacterIterator;
-import java.text.AttributedString;
-import java.text.DecimalFormatSymbols;
-import java.text.FieldPosition;
-import java.text.Format;
-import java.text.NumberFormat;
-import java.text.ParsePosition;
-import java.util.Currency;
-import java.util.Locale;
-
-public class DecimalFormat {
-    // Constants corresponding to the native type UNumberFormatSymbol, for use with getSymbol/setSymbol.
-    private static final int UNUM_DECIMAL_SEPARATOR_SYMBOL = 0;
-    private static final int UNUM_GROUPING_SEPARATOR_SYMBOL = 1;
-    private static final int UNUM_PATTERN_SEPARATOR_SYMBOL = 2;
-    private static final int UNUM_PERCENT_SYMBOL = 3;
-    private static final int UNUM_ZERO_DIGIT_SYMBOL = 4;
-    private static final int UNUM_DIGIT_SYMBOL = 5;
-    private static final int UNUM_MINUS_SIGN_SYMBOL = 6;
-    private static final int UNUM_PLUS_SIGN_SYMBOL = 7;
-    private static final int UNUM_CURRENCY_SYMBOL = 8;
-    private static final int UNUM_INTL_CURRENCY_SYMBOL = 9;
-    private static final int UNUM_MONETARY_SEPARATOR_SYMBOL = 10;
-    private static final int UNUM_EXPONENTIAL_SYMBOL = 11;
-    private static final int UNUM_PERMILL_SYMBOL = 12;
-    private static final int UNUM_PAD_ESCAPE_SYMBOL = 13;
-    private static final int UNUM_INFINITY_SYMBOL = 14;
-    private static final int UNUM_NAN_SYMBOL = 15;
-    private static final int UNUM_SIGNIFICANT_DIGIT_SYMBOL = 16;
-    private static final int UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL = 17;
-    private static final int UNUM_FORMAT_SYMBOL_COUNT = 18;
-
-    // The address of the ICU DecimalFormat* on the native heap.
-    private final int addr;
-
-    // TODO: store all these in java.text.DecimalFormat instead!
-    private boolean negPrefNull;
-    private boolean negSuffNull;
-    private boolean posPrefNull;
-    private boolean posSuffNull;
-
-    /**
-     * Cache the BigDecimal form of the multiplier. This is null until we've
-     * formatted a BigDecimal (with a multiplier that is not 1), or the user has
-     * explicitly called {@link #setMultiplier(int)} with any multiplier.
-     */
-    private transient BigDecimal multiplierBigDecimal = null;
-
-    public DecimalFormat(String pattern, Locale locale, DecimalFormatSymbols symbols) {
-        this.addr = NativeDecimalFormat.openDecimalFormat(locale.toString(), pattern);
-        setDecimalFormatSymbols(symbols);
-    }
-
-    // Used to implement clone.
-    private DecimalFormat(DecimalFormat other) {
-        this.addr = NativeDecimalFormat.cloneDecimalFormatImpl(other.addr);
-        this.negPrefNull = other.negPrefNull;
-        this.negSuffNull = other.negSuffNull;
-        this.posPrefNull = other.posPrefNull;
-        this.posSuffNull = other.posSuffNull;
-    }
-
-    // TODO: remove this and just have java.text.DecimalFormat.hashCode do the right thing itself.
-    @Override
-    public int hashCode() {
-        return this.getPositivePrefix().hashCode();
-    }
-
-    @Override
-    public Object clone() {
-        return new DecimalFormat(this);
-    }
-
-    @Override
-    protected void finalize() {
-        NativeDecimalFormat.closeDecimalFormatImpl(this.addr);
-    }
-
-    /**
-     * Note: this doesn't check that the underlying native DecimalFormat objects' configured
-     * native DecimalFormatSymbols objects are equal. It is assumed that the
-     * caller (java.text.DecimalFormat) will check the java.text.DecimalFormatSymbols objects
-     * instead, for performance.
-     * 
-     * This is also unreasonably expensive, calling down to JNI multiple times.
-     * 
-     * TODO: remove this and just have java.text.DecimalFormat.equals do the right thing itself.
-     */
-    @Override
-    public boolean equals(Object object) {
-        if (object == this) {
-            return true;
-        }
-        if (!(object instanceof DecimalFormat)) {
-            return false;
-        }
-        DecimalFormat obj = (DecimalFormat) object;
-        if (obj.addr == this.addr) {
-            return true;
-        }
-        return obj.toPattern().equals(this.toPattern()) &&
-                obj.isDecimalSeparatorAlwaysShown() == this.isDecimalSeparatorAlwaysShown() &&
-                obj.getGroupingSize() == this.getGroupingSize() &&
-                obj.getMultiplier() == this.getMultiplier() &&
-                obj.getNegativePrefix().equals(this.getNegativePrefix()) &&
-                obj.getNegativeSuffix().equals(this.getNegativeSuffix()) &&
-                obj.getPositivePrefix().equals(this.getPositivePrefix()) &&
-                obj.getPositiveSuffix().equals(this.getPositiveSuffix()) &&
-                obj.getMaximumIntegerDigits() == this.getMaximumIntegerDigits() &&
-                obj.getMaximumFractionDigits() == this.getMaximumFractionDigits() &&
-                obj.getMinimumIntegerDigits() == this.getMinimumIntegerDigits() &&
-                obj.getMinimumFractionDigits() == this.getMinimumFractionDigits() &&
-                obj.isGroupingUsed() == this.isGroupingUsed() &&
-                obj.getCurrency() == this.getCurrency();
-    }
-
-    /**
-     * Copies the java.text.DecimalFormatSymbols' settings into our native peer.
-     */
-    public void setDecimalFormatSymbols(final java.text.DecimalFormatSymbols dfs) {
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_CURRENCY_SYMBOL, dfs.getCurrencySymbol());
-
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_DECIMAL_SEPARATOR_SYMBOL, dfs.getDecimalSeparator());
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_DIGIT_SYMBOL, dfs.getDigit());
-
-        char groupingSeparator = dfs.getGroupingSeparator();
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_GROUPING_SEPARATOR_SYMBOL, groupingSeparator);
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL, groupingSeparator);
-
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_INFINITY_SYMBOL, dfs.getInfinity());
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_INTL_CURRENCY_SYMBOL, dfs.getInternationalCurrencySymbol());
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_MINUS_SIGN_SYMBOL, dfs.getMinusSign());
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_MONETARY_SEPARATOR_SYMBOL, dfs.getMonetaryDecimalSeparator());
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_NAN_SYMBOL, dfs.getNaN());
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_PATTERN_SEPARATOR_SYMBOL, dfs.getPatternSeparator());
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_PERCENT_SYMBOL, dfs.getPercent());
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_PERMILL_SYMBOL, dfs.getPerMill());
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_ZERO_DIGIT_SYMBOL, dfs.getZeroDigit());
-    }
-
-    private BigDecimal applyMultiplier(BigDecimal valBigDecimal) {
-       if (multiplierBigDecimal == null) {
-           multiplierBigDecimal = BigDecimal.valueOf(getMultiplier());
-       }
-       // Get new value by multiplying multiplier.
-       return valBigDecimal.multiply(multiplierBigDecimal);
-    }
-
-    public StringBuffer format(Object value, StringBuffer buffer, FieldPosition field) {
-        if (!(value instanceof Number)) {
-            throw new IllegalArgumentException();
-        }
-        if (buffer == null || field == null) {
-            throw new NullPointerException();
-        }
-        String fieldType = getFieldType(field.getFieldAttribute());
-        Number number = (Number) value;
-        if (number instanceof BigInteger) {
-            BigInteger valBigInteger = (BigInteger) number;
-            String result = NativeDecimalFormat.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 = NativeDecimalFormat.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 = NativeDecimalFormat.format(this.addr, dv, field, fieldType, null);
-            return buffer.append(result);
-        } else {
-            long lv = number.longValue();
-            String result = NativeDecimalFormat.format(this.addr, lv, field, fieldType, null);
-            return buffer.append(result);
-        }
-    }
-
-    public StringBuffer format(long value, StringBuffer buffer, FieldPosition field) {
-        if (buffer == null || field == null) {
-            throw new NullPointerException();
-        }
-        String fieldType = getFieldType(field.getFieldAttribute());
-        buffer.append(NativeDecimalFormat.format(this.addr, value, field, fieldType, null));
-        return buffer;
-    }
-
-    public StringBuffer format(double value, StringBuffer buffer, FieldPosition field) {
-        if (buffer == null || field == null) {
-            throw new NullPointerException();
-        }
-        String fieldType = getFieldType(field.getFieldAttribute());
-        buffer.append(NativeDecimalFormat.format(this.addr, value, field, fieldType, null));
-        return buffer;
-    }
-
-    public void applyLocalizedPattern(String pattern) {
-        NativeDecimalFormat.applyPattern(this.addr, true, pattern);
-    }
-
-    public void applyPattern(String pattern) {
-        NativeDecimalFormat.applyPattern(this.addr, false, pattern);
-    }
-
-    public AttributedCharacterIterator formatToCharacterIterator(Object object) {
-        if (!(object instanceof Number)) {
-            throw new IllegalArgumentException();
-        }
-        Number number = (Number) object;
-        String text = null;
-        StringBuffer attributes = new StringBuffer();
-
-        if(number instanceof BigInteger) {
-            BigInteger valBigInteger = (BigInteger) number;
-            text = NativeDecimalFormat.format(this.addr,
-                    valBigInteger.toString(10), null, null, attributes, 0);
-        } 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);
-            text = NativeDecimalFormat.format(this.addr, val.toString(), null,
-                    null, attributes, scale);
-        } else if (number instanceof Double || number instanceof Float) {
-            double dv = number.doubleValue();
-            text = NativeDecimalFormat.format(this.addr, dv, null, null, attributes);
-        } else {
-            long lv = number.longValue();
-            text = NativeDecimalFormat.format(this.addr, lv, null, null, attributes);
-        }
-
-        AttributedString as = new AttributedString(text);
-
-        String[] attrs = attributes.toString().split(";");
-        // add NumberFormat field attributes to the AttributedString
-        int size = attrs.length / 3;
-        if(size * 3 != attrs.length) {
-            return as.getIterator();
-        }
-        for (int i = 0; i < size; i++) {
-            Format.Field attribute = getField(attrs[3*i]);
-            as.addAttribute(attribute, attribute, Integer.parseInt(attrs[3*i+1]),
-                    Integer.parseInt(attrs[3*i+2]));
-        }
-
-        // return the CharacterIterator from AttributedString
-        return as.getIterator();
-    }
-
-    private int makeScalePositive(int scale, StringBuilder val) {
-        if (scale < 0) {
-            scale = -scale;
-            for (int i = scale; i > 0; i--) {
-                val.append('0');
-            }
-            scale = 0;
-        }
-        return scale;
-    }
-
-    public String toLocalizedPattern() {
-        return NativeDecimalFormat.toPatternImpl(this.addr, true);
-    }
-
-    public String toPattern() {
-        return NativeDecimalFormat.toPatternImpl(this.addr, false);
-    }
-
-    public Number parse(String string, ParsePosition position) {
-        return NativeDecimalFormat.parse(addr, string, position);
-    }
-
-    // start getter and setter
-
-    public int getMaximumFractionDigits() {
-        return NativeDecimalFormat.getAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_MAX_FRACTION_DIGITS.ordinal());
-    }
-
-    public int getMaximumIntegerDigits() {
-        return NativeDecimalFormat.getAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_MAX_INTEGER_DIGITS.ordinal());
-    }
-
-    public int getMinimumFractionDigits() {
-        return NativeDecimalFormat.getAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_MIN_FRACTION_DIGITS.ordinal());
-    }
-
-    public int getMinimumIntegerDigits() {
-        return NativeDecimalFormat.getAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_MIN_INTEGER_DIGITS.ordinal());
-    }
-
-    public Currency getCurrency() {
-        String curr = NativeDecimalFormat.getSymbol(this.addr, UNUM_INTL_CURRENCY_SYMBOL);
-        if (curr.equals("") || curr.equals("\u00a4\u00a4")) {
-            return null;
-        }
-        return Currency.getInstance(curr);
-    }
-
-    public int getGroupingSize() {
-        return NativeDecimalFormat.getAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_GROUPING_SIZE.ordinal());
-    }
-
-    public int getMultiplier() {
-        return NativeDecimalFormat.getAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_MULTIPLIER.ordinal());
-    }
-
-    public String getNegativePrefix() {
-        if (negPrefNull) {
-            return null;
-        }
-        return NativeDecimalFormat.getTextAttribute(this.addr,
-                UNumberFormatTextAttribute.UNUM_NEGATIVE_PREFIX.ordinal());
-    }
-
-    public String getNegativeSuffix() {
-        if (negSuffNull) {
-            return null;
-        }
-        return NativeDecimalFormat.getTextAttribute(this.addr,
-                UNumberFormatTextAttribute.UNUM_NEGATIVE_SUFFIX.ordinal());
-    }
-
-    public String getPositivePrefix() {
-        if (posPrefNull) {
-            return null;
-        }
-        return NativeDecimalFormat.getTextAttribute(this.addr,
-                UNumberFormatTextAttribute.UNUM_POSITIVE_PREFIX.ordinal());
-    }
-
-    public String getPositiveSuffix() {
-        if (posSuffNull) {
-            return null;
-        }
-        return NativeDecimalFormat.getTextAttribute(this.addr,
-                UNumberFormatTextAttribute.UNUM_POSITIVE_SUFFIX.ordinal());
-    }
-
-    public boolean isDecimalSeparatorAlwaysShown() {
-        return NativeDecimalFormat.getAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_DECIMAL_ALWAYS_SHOWN.ordinal()) != 0;
-    }
-
-    public boolean isParseIntegerOnly() {
-        return NativeDecimalFormat.getAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_PARSE_INT_ONLY.ordinal()) != 0;
-    }
-
-    public boolean isGroupingUsed() {
-        return NativeDecimalFormat.getAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_GROUPING_USED.ordinal()) != 0;
-    }
-
-    public void setDecimalSeparatorAlwaysShown(boolean value) {
-        int i = value ? -1 : 0;
-        NativeDecimalFormat.setAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_DECIMAL_ALWAYS_SHOWN.ordinal(), i);
-    }
-
-    public void setCurrency(Currency currency) {
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_CURRENCY_SYMBOL, currency.getSymbol());
-        NativeDecimalFormat.setSymbol(this.addr, UNUM_INTL_CURRENCY_SYMBOL, currency.getCurrencyCode());
-    }
-
-    public void setGroupingSize(int value) {
-        NativeDecimalFormat.setAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_GROUPING_SIZE.ordinal(), value);
-    }
-
-    public void setGroupingUsed(boolean value) {
-        int i = value ? -1 : 0;
-        NativeDecimalFormat.setAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_GROUPING_USED.ordinal(), i);
-    }
-
-    public void setMaximumFractionDigits(int value) {
-        NativeDecimalFormat.setAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_MAX_FRACTION_DIGITS.ordinal(), value);
-    }
-
-    public void setMaximumIntegerDigits(int value) {
-        NativeDecimalFormat.setAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_MAX_INTEGER_DIGITS.ordinal(), value);
-    }
-
-    public void setMinimumFractionDigits(int value) {
-        NativeDecimalFormat.setAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_MIN_FRACTION_DIGITS.ordinal(), value);
-    }
-
-    public void setMinimumIntegerDigits(int value) {
-        NativeDecimalFormat.setAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_MIN_INTEGER_DIGITS.ordinal(), value);
-    }
-
-    public void setMultiplier(int value) {
-        NativeDecimalFormat.setAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_MULTIPLIER.ordinal(), value);
-        // Update the cached BigDecimal for multiplier.
-        multiplierBigDecimal = BigDecimal.valueOf(value);
-    }
-
-    public void setNegativePrefix(String value) {
-        negPrefNull = value == null;
-        if (!negPrefNull) {
-            NativeDecimalFormat.setTextAttribute(this.addr,
-                    UNumberFormatTextAttribute.UNUM_NEGATIVE_PREFIX.ordinal(),
-                    value);
-        }
-    }
-
-    public void setNegativeSuffix(String value) {
-        negSuffNull = value == null;
-        if (!negSuffNull) {
-            NativeDecimalFormat.setTextAttribute(this.addr,
-                    UNumberFormatTextAttribute.UNUM_NEGATIVE_SUFFIX.ordinal(),
-                    value);
-        }
-    }
-
-    public void setPositivePrefix(String value) {
-        posPrefNull = value == null;
-        if (!posPrefNull) {
-            NativeDecimalFormat.setTextAttribute(this.addr,
-                    UNumberFormatTextAttribute.UNUM_POSITIVE_PREFIX.ordinal(),
-                    value);
-        }
-    }
-
-    public void setPositiveSuffix(String value) {
-        posSuffNull = value == null;
-        if (!posSuffNull) {
-            NativeDecimalFormat.setTextAttribute(this.addr,
-                    UNumberFormatTextAttribute.UNUM_POSITIVE_SUFFIX.ordinal(),
-                    value);
-        }
-    }
-
-    public void setParseIntegerOnly(boolean value) {
-        int i = value ? -1 : 0;
-        NativeDecimalFormat.setAttribute(this.addr,
-                UNumberFormatAttribute.UNUM_PARSE_INT_ONLY.ordinal(), i);
-    }
-
-    static protected String getFieldType(Format.Field field) {
-        if(field == null) {
-            return null;
-        }
-        if(field.equals(NumberFormat.Field.SIGN)) {
-            return "sign";
-        }
-        if(field.equals(NumberFormat.Field.INTEGER)) {
-            return "integer";
-        }
-        if(field.equals(NumberFormat.Field.FRACTION)) {
-            return "fraction";
-        }
-        if(field.equals(NumberFormat.Field.EXPONENT)) {
-            return "exponent";
-        }
-        if(field.equals(NumberFormat.Field.EXPONENT_SIGN)) {
-            return "exponent_sign";
-        }
-        if(field.equals(NumberFormat.Field.EXPONENT_SYMBOL)) {
-            return "exponent_symbol";
-        }
-        if(field.equals(NumberFormat.Field.CURRENCY)) {
-            return "currency";
-        }
-        if(field.equals(NumberFormat.Field.GROUPING_SEPARATOR)) {
-            return "grouping_separator";
-        }
-        if(field.equals(NumberFormat.Field.DECIMAL_SEPARATOR)) {
-            return "decimal_separator";
-        }
-        if(field.equals(NumberFormat.Field.PERCENT)) {
-            return "percent";
-        }
-        if(field.equals(NumberFormat.Field.PERMILLE)) {
-            return "permille";
-        }
-        return null;
-    }
-
-    protected Format.Field getField(String type) {
-        if(type.equals("")) {
-            return null;
-        }
-        if(type.equals("sign")) {
-            return NumberFormat.Field.SIGN;
-        }
-        if(type.equals("integer")) {
-            return NumberFormat.Field.INTEGER;
-        }
-        if(type.equals("fraction")) {
-            return NumberFormat.Field.FRACTION;
-        }
-        if(type.equals("exponent")) {
-            return NumberFormat.Field.EXPONENT;
-        }
-        if(type.equals("exponent_sign")) {
-            return NumberFormat.Field.EXPONENT_SIGN;
-        }
-        if(type.equals("exponent_symbol")) {
-            return NumberFormat.Field.EXPONENT_SYMBOL;
-        }
-        if(type.equals("currency")) {
-            return NumberFormat.Field.CURRENCY;
-        }
-        if(type.equals("grouping_separator")) {
-            return NumberFormat.Field.GROUPING_SEPARATOR;
-        }
-        if(type.equals("decimal_separator")) {
-            return NumberFormat.Field.DECIMAL_SEPARATOR;
-        }
-        if(type.equals("percent")) {
-            return NumberFormat.Field.PERCENT;
-        }
-        if(type.equals("permille")) {
-            return NumberFormat.Field.PERMILLE;
-        }
-        return null;
-    }
-}
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 ec07fbd..fd22ed0 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
@@ -16,93 +16,609 @@
 
 package com.ibm.icu4jni.text;
 
+import com.ibm.icu4jni.util.LocaleData;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.AttributedCharacterIterator;
+import java.text.AttributedString;
+import java.text.DecimalFormatSymbols;
 import java.text.FieldPosition;
+import java.text.Format;
+import java.text.NumberFormat;
 import java.text.ParsePosition;
+import java.util.Currency;
+import java.util.Locale;
 
-final class NativeDecimalFormat {
+public class NativeDecimalFormat {
+    /**
+     * Constants corresponding to the native type UNumberFormatSymbol, for getSymbol/setSymbol.
+     */
+    private static final int UNUM_DECIMAL_SEPARATOR_SYMBOL = 0;
+    private static final int UNUM_GROUPING_SEPARATOR_SYMBOL = 1;
+    private static final int UNUM_PATTERN_SEPARATOR_SYMBOL = 2;
+    private static final int UNUM_PERCENT_SYMBOL = 3;
+    private static final int UNUM_ZERO_DIGIT_SYMBOL = 4;
+    private static final int UNUM_DIGIT_SYMBOL = 5;
+    private static final int UNUM_MINUS_SIGN_SYMBOL = 6;
+    private static final int UNUM_PLUS_SIGN_SYMBOL = 7;
+    private static final int UNUM_CURRENCY_SYMBOL = 8;
+    private static final int UNUM_INTL_CURRENCY_SYMBOL = 9;
+    private static final int UNUM_MONETARY_SEPARATOR_SYMBOL = 10;
+    private static final int UNUM_EXPONENTIAL_SYMBOL = 11;
+    private static final int UNUM_PERMILL_SYMBOL = 12;
+    private static final int UNUM_PAD_ESCAPE_SYMBOL = 13;
+    private static final int UNUM_INFINITY_SYMBOL = 14;
+    private static final int UNUM_NAN_SYMBOL = 15;
+    private static final int UNUM_SIGNIFICANT_DIGIT_SYMBOL = 16;
+    private static final int UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL = 17;
+    private static final int UNUM_FORMAT_SYMBOL_COUNT = 18;
 
-    enum UNumberFormatAttribute {
-        UNUM_PARSE_INT_ONLY, 
-        UNUM_GROUPING_USED, 
-        UNUM_DECIMAL_ALWAYS_SHOWN, 
-        UNUM_MAX_INTEGER_DIGITS,
-        UNUM_MIN_INTEGER_DIGITS, 
-        UNUM_INTEGER_DIGITS, 
-        UNUM_MAX_FRACTION_DIGITS, 
-        UNUM_MIN_FRACTION_DIGITS,
-        UNUM_FRACTION_DIGITS,
-        UNUM_MULTIPLIER, 
-        UNUM_GROUPING_SIZE, 
-        UNUM_ROUNDING_MODE,
-        UNUM_ROUNDING_INCREMENT, 
-        UNUM_FORMAT_WIDTH, 
-        UNUM_PADDING_POSITION, 
-        UNUM_SECONDARY_GROUPING_SIZE,
-        UNUM_SIGNIFICANT_DIGITS_USED, 
-        UNUM_MIN_SIGNIFICANT_DIGITS, 
-        UNUM_MAX_SIGNIFICANT_DIGITS, 
-        UNUM_LENIENT_PARSE
+    /**
+     * Constants corresponding to the native type UNumberFormatAttribute, for
+     * getAttribute/setAttribute.
+     */
+    private static final int UNUM_PARSE_INT_ONLY = 0;
+    private static final int UNUM_GROUPING_USED = 1;
+    private static final int UNUM_DECIMAL_ALWAYS_SHOWN = 2;
+    private static final int UNUM_MAX_INTEGER_DIGITS = 3;
+    private static final int UNUM_MIN_INTEGER_DIGITS = 4;
+    private static final int UNUM_INTEGER_DIGITS = 5;
+    private static final int UNUM_MAX_FRACTION_DIGITS = 6;
+    private static final int UNUM_MIN_FRACTION_DIGITS = 7;
+    private static final int UNUM_FRACTION_DIGITS = 8;
+    private static final int UNUM_MULTIPLIER = 9;
+    private static final int UNUM_GROUPING_SIZE = 10;
+    private static final int UNUM_ROUNDING_MODE = 11;
+    private static final int UNUM_ROUNDING_INCREMENT = 12;
+    private static final int UNUM_FORMAT_WIDTH = 13;
+    private static final int UNUM_PADDING_POSITION = 14;
+    private static final int UNUM_SECONDARY_GROUPING_SIZE = 15;
+    private static final int UNUM_SIGNIFICANT_DIGITS_USED = 16;
+    private static final int UNUM_MIN_SIGNIFICANT_DIGITS = 17;
+    private static final int UNUM_MAX_SIGNIFICANT_DIGITS = 18;
+    private static final int UNUM_LENIENT_PARSE = 19;
+
+    /**
+     * Constants corresponding to the native type UNumberFormatTextAttribute, for
+     * getTextAttribute/setTextAttribute.
+     */
+    private static final int UNUM_POSITIVE_PREFIX = 0;
+    private static final int UNUM_POSITIVE_SUFFIX = 1;
+    private static final int UNUM_NEGATIVE_PREFIX = 2;
+    private static final int UNUM_NEGATIVE_SUFFIX = 3;
+    private static final int UNUM_PADDING_CHARACTER = 4;
+    private static final int UNUM_CURRENCY_CODE = 5;
+    private static final int UNUM_DEFAULT_RULESET = 6;
+    private static final int UNUM_PUBLIC_RULESETS = 7;
+
+    /**
+     * The address of the ICU DecimalFormat* on the native heap.
+     */
+    private final int addr;
+
+    /**
+     * The last pattern we gave to ICU, so we can make repeated applications cheap.
+     * This helps in cases like String.format("%.2f,%.2f\n", x, y) where the DecimalFormat is
+     * reused.
+     */
+    private String lastPattern;
+
+    // TODO: store all these in DecimalFormat instead!
+    private boolean negPrefNull;
+    private boolean negSuffNull;
+    private boolean posPrefNull;
+    private boolean posSuffNull;
+
+    /**
+     * Cache the BigDecimal form of the multiplier. This is null until we've
+     * formatted a BigDecimal (with a multiplier that is not 1), or the user has
+     * explicitly called {@link #setMultiplier(int)} with any multiplier.
+     */
+    private BigDecimal multiplierBigDecimal = null;
+
+    public NativeDecimalFormat(String pattern, Locale locale, DecimalFormatSymbols symbols) {
+        this.addr = openDecimalFormat(locale.toString(), pattern);
+        this.lastPattern = pattern;
+        setDecimalFormatSymbols(symbols);
     }
 
-    enum UNumberFormatTextAttribute {
-        UNUM_POSITIVE_PREFIX, 
-        UNUM_POSITIVE_SUFFIX, 
-        UNUM_NEGATIVE_PREFIX, 
-        UNUM_NEGATIVE_SUFFIX,
-        UNUM_PADDING_CHARACTER, 
-        UNUM_CURRENCY_CODE, 
-        UNUM_DEFAULT_RULESET, 
-        UNUM_PUBLIC_RULESETS
+    // Used to implement clone.
+    private NativeDecimalFormat(NativeDecimalFormat other) {
+        this.addr = cloneDecimalFormatImpl(other.addr);
+        this.lastPattern = other.lastPattern;
+        this.negPrefNull = other.negPrefNull;
+        this.negSuffNull = other.negSuffNull;
+        this.posPrefNull = other.posPrefNull;
+        this.posSuffNull = other.posSuffNull;
     }
-    
-    static int openDecimalFormat(String locale, String pattern) {
+
+    // TODO: remove this and just have DecimalFormat.hashCode do the right thing itself.
+    @Override
+    public int hashCode() {
+        return this.getPositivePrefix().hashCode();
+    }
+
+    @Override
+    public Object clone() {
+        return new NativeDecimalFormat(this);
+    }
+
+    @Override
+    protected void finalize() {
+        closeDecimalFormatImpl(this.addr);
+    }
+
+    /**
+     * Note: this doesn't check that the underlying native DecimalFormat objects' configured
+     * native DecimalFormatSymbols objects are equal. It is assumed that the
+     * caller (DecimalFormat) will check the DecimalFormatSymbols objects
+     * instead, for performance.
+     * 
+     * This is also unreasonably expensive, calling down to JNI multiple times.
+     * 
+     * TODO: remove this and just have DecimalFormat.equals do the right thing itself.
+     */
+    @Override
+    public boolean equals(Object object) {
+        if (object == this) {
+            return true;
+        }
+        if (!(object instanceof NativeDecimalFormat)) {
+            return false;
+        }
+        NativeDecimalFormat obj = (NativeDecimalFormat) object;
+        if (obj.addr == this.addr) {
+            return true;
+        }
+        return obj.toPattern().equals(this.toPattern()) &&
+                obj.isDecimalSeparatorAlwaysShown() == this.isDecimalSeparatorAlwaysShown() &&
+                obj.getGroupingSize() == this.getGroupingSize() &&
+                obj.getMultiplier() == this.getMultiplier() &&
+                obj.getNegativePrefix().equals(this.getNegativePrefix()) &&
+                obj.getNegativeSuffix().equals(this.getNegativeSuffix()) &&
+                obj.getPositivePrefix().equals(this.getPositivePrefix()) &&
+                obj.getPositiveSuffix().equals(this.getPositiveSuffix()) &&
+                obj.getMaximumIntegerDigits() == this.getMaximumIntegerDigits() &&
+                obj.getMaximumFractionDigits() == this.getMaximumFractionDigits() &&
+                obj.getMinimumIntegerDigits() == this.getMinimumIntegerDigits() &&
+                obj.getMinimumFractionDigits() == this.getMinimumFractionDigits() &&
+                obj.isGroupingUsed() == this.isGroupingUsed() &&
+                obj.getCurrency() == this.getCurrency();
+    }
+
+    /**
+     * Copies the DecimalFormatSymbols settings into our native peer.
+     */
+    public void setDecimalFormatSymbols(final DecimalFormatSymbols dfs) {
+        setSymbol(this.addr, UNUM_CURRENCY_SYMBOL, dfs.getCurrencySymbol());
+
+        setSymbol(this.addr, UNUM_DECIMAL_SEPARATOR_SYMBOL, dfs.getDecimalSeparator());
+        setSymbol(this.addr, UNUM_DIGIT_SYMBOL, dfs.getDigit());
+
+        char groupingSeparator = dfs.getGroupingSeparator();
+        setSymbol(this.addr, UNUM_GROUPING_SEPARATOR_SYMBOL, groupingSeparator);
+        setSymbol(this.addr, UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL, groupingSeparator);
+
+        setSymbol(this.addr, UNUM_INFINITY_SYMBOL, dfs.getInfinity());
+        setSymbol(this.addr, UNUM_INTL_CURRENCY_SYMBOL, dfs.getInternationalCurrencySymbol());
+        setSymbol(this.addr, UNUM_MINUS_SIGN_SYMBOL, dfs.getMinusSign());
+        setSymbol(this.addr, UNUM_MONETARY_SEPARATOR_SYMBOL, dfs.getMonetaryDecimalSeparator());
+        setSymbol(this.addr, UNUM_NAN_SYMBOL, dfs.getNaN());
+        setSymbol(this.addr, UNUM_PATTERN_SEPARATOR_SYMBOL, dfs.getPatternSeparator());
+        setSymbol(this.addr, UNUM_PERCENT_SYMBOL, dfs.getPercent());
+        setSymbol(this.addr, UNUM_PERMILL_SYMBOL, dfs.getPerMill());
+        setSymbol(this.addr, UNUM_ZERO_DIGIT_SYMBOL, dfs.getZeroDigit());
+    }
+
+    private BigDecimal applyMultiplier(BigDecimal valBigDecimal) {
+       if (multiplierBigDecimal == null) {
+           multiplierBigDecimal = BigDecimal.valueOf(getMultiplier());
+       }
+       // Get new value by multiplying multiplier.
+       return valBigDecimal.multiply(multiplierBigDecimal);
+    }
+
+    public StringBuffer format(Object value, StringBuffer buffer, FieldPosition field) {
+        if (!(value instanceof Number)) {
+            throw new IllegalArgumentException();
+        }
+        if (buffer == null || field == null) {
+            throw new NullPointerException();
+        }
+        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);
+            return buffer.append(result);
+        } else {
+            long lv = number.longValue();
+            String result = format(this.addr, lv, field, fieldType, null);
+            return buffer.append(result);
+        }
+    }
+
+    public StringBuffer format(long value, StringBuffer buffer, FieldPosition field) {
+        if (buffer == null || field == null) {
+            throw new NullPointerException();
+        }
+        String fieldType = getFieldType(field.getFieldAttribute());
+        buffer.append(format(this.addr, value, field, fieldType, null));
+        return buffer;
+    }
+
+    public StringBuffer format(double value, StringBuffer buffer, FieldPosition field) {
+        if (buffer == null || field == null) {
+            throw new NullPointerException();
+        }
+        String fieldType = getFieldType(field.getFieldAttribute());
+        buffer.append(format(this.addr, value, field, fieldType, null));
+        return buffer;
+    }
+
+    public void applyLocalizedPattern(String pattern) {
+        applyPattern(this.addr, true, pattern);
+        lastPattern = null;
+    }
+
+    public void applyPattern(String pattern) {
+        if (lastPattern != null && pattern.equals(lastPattern)) {
+            return;
+        }
+        applyPattern(this.addr, false, pattern);
+        lastPattern = pattern;
+    }
+
+    public AttributedCharacterIterator formatToCharacterIterator(Object object) {
+        if (!(object instanceof Number)) {
+            throw new IllegalArgumentException();
+        }
+        Number number = (Number) object;
+        String text = null;
+        StringBuffer attributes = new StringBuffer();
+
+        if(number instanceof BigInteger) {
+            BigInteger valBigInteger = (BigInteger) number;
+            text = format(this.addr, valBigInteger.toString(10), null, null, attributes, 0);
+        } 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);
+            text = format(this.addr, val.toString(), null, null, attributes, scale);
+        } else if (number instanceof Double || number instanceof Float) {
+            double dv = number.doubleValue();
+            text = format(this.addr, dv, null, null, attributes);
+        } else {
+            long lv = number.longValue();
+            text = format(this.addr, lv, null, null, attributes);
+        }
+
+        AttributedString as = new AttributedString(text);
+
+        String[] attrs = attributes.toString().split(";");
+        // add NumberFormat field attributes to the AttributedString
+        int size = attrs.length / 3;
+        if(size * 3 != attrs.length) {
+            return as.getIterator();
+        }
+        for (int i = 0; i < size; i++) {
+            Format.Field attribute = getField(attrs[3*i]);
+            as.addAttribute(attribute, attribute, Integer.parseInt(attrs[3*i+1]),
+                    Integer.parseInt(attrs[3*i+2]));
+        }
+
+        // return the CharacterIterator from AttributedString
+        return as.getIterator();
+    }
+
+    private int makeScalePositive(int scale, StringBuilder val) {
+        if (scale < 0) {
+            scale = -scale;
+            for (int i = scale; i > 0; i--) {
+                val.append('0');
+            }
+            scale = 0;
+        }
+        return scale;
+    }
+
+    public String toLocalizedPattern() {
+        return toPatternImpl(this.addr, true);
+    }
+
+    public String toPattern() {
+        return toPatternImpl(this.addr, false);
+    }
+
+    public Number parse(String string, ParsePosition position) {
+        return parse(addr, string, position);
+    }
+
+    // start getter and setter
+
+    public int getMaximumFractionDigits() {
+        return getAttribute(this.addr, UNUM_MAX_FRACTION_DIGITS);
+    }
+
+    public int getMaximumIntegerDigits() {
+        return getAttribute(this.addr, UNUM_MAX_INTEGER_DIGITS);
+    }
+
+    public int getMinimumFractionDigits() {
+        return getAttribute(this.addr, UNUM_MIN_FRACTION_DIGITS);
+    }
+
+    public int getMinimumIntegerDigits() {
+        return getAttribute(this.addr, UNUM_MIN_INTEGER_DIGITS);
+    }
+
+    public Currency getCurrency() {
+        String curr = getSymbol(this.addr, UNUM_INTL_CURRENCY_SYMBOL);
+        if (curr.equals("") || curr.equals("\u00a4\u00a4")) {
+            return null;
+        }
+        return Currency.getInstance(curr);
+    }
+
+    public int getGroupingSize() {
+        return getAttribute(this.addr, UNUM_GROUPING_SIZE);
+    }
+
+    public int getMultiplier() {
+        return getAttribute(this.addr, UNUM_MULTIPLIER);
+    }
+
+    public String getNegativePrefix() {
+        if (negPrefNull) {
+            return null;
+        }
+        return getTextAttribute(this.addr, UNUM_NEGATIVE_PREFIX);
+    }
+
+    public String getNegativeSuffix() {
+        if (negSuffNull) {
+            return null;
+        }
+        return getTextAttribute(this.addr, UNUM_NEGATIVE_SUFFIX);
+    }
+
+    public String getPositivePrefix() {
+        if (posPrefNull) {
+            return null;
+        }
+        return getTextAttribute(this.addr, UNUM_POSITIVE_PREFIX);
+    }
+
+    public String getPositiveSuffix() {
+        if (posSuffNull) {
+            return null;
+        }
+        return getTextAttribute(this.addr, UNUM_POSITIVE_SUFFIX);
+    }
+
+    public boolean isDecimalSeparatorAlwaysShown() {
+        return getAttribute(this.addr, UNUM_DECIMAL_ALWAYS_SHOWN) != 0;
+    }
+
+    public boolean isParseIntegerOnly() {
+        return getAttribute(this.addr, UNUM_PARSE_INT_ONLY) != 0;
+    }
+
+    public boolean isGroupingUsed() {
+        return getAttribute(this.addr, UNUM_GROUPING_USED) != 0;
+    }
+
+    public void setDecimalSeparatorAlwaysShown(boolean value) {
+        int i = value ? -1 : 0;
+        setAttribute(this.addr, UNUM_DECIMAL_ALWAYS_SHOWN, i);
+    }
+
+    public void setCurrency(Currency currency) {
+        setSymbol(this.addr, UNUM_CURRENCY_SYMBOL, currency.getSymbol());
+        setSymbol(this.addr, UNUM_INTL_CURRENCY_SYMBOL, currency.getCurrencyCode());
+    }
+
+    public void setGroupingSize(int value) {
+        setAttribute(this.addr, UNUM_GROUPING_SIZE, value);
+    }
+
+    public void setGroupingUsed(boolean value) {
+        int i = value ? -1 : 0;
+        setAttribute(this.addr, UNUM_GROUPING_USED, i);
+    }
+
+    public void setMaximumFractionDigits(int value) {
+        setAttribute(this.addr, UNUM_MAX_FRACTION_DIGITS, value);
+    }
+
+    public void setMaximumIntegerDigits(int value) {
+        setAttribute(this.addr, UNUM_MAX_INTEGER_DIGITS, value);
+    }
+
+    public void setMinimumFractionDigits(int value) {
+        setAttribute(this.addr, UNUM_MIN_FRACTION_DIGITS, value);
+    }
+
+    public void setMinimumIntegerDigits(int value) {
+        setAttribute(this.addr, UNUM_MIN_INTEGER_DIGITS, value);
+    }
+
+    public void setMultiplier(int value) {
+        setAttribute(this.addr, UNUM_MULTIPLIER, value);
+        // Update the cached BigDecimal for multiplier.
+        multiplierBigDecimal = BigDecimal.valueOf(value);
+    }
+
+    public void setNegativePrefix(String value) {
+        negPrefNull = value == null;
+        if (!negPrefNull) {
+            setTextAttribute(this.addr, UNUM_NEGATIVE_PREFIX, value);
+        }
+    }
+
+    public void setNegativeSuffix(String value) {
+        negSuffNull = value == null;
+        if (!negSuffNull) {
+            setTextAttribute(this.addr, UNUM_NEGATIVE_SUFFIX, value);
+        }
+    }
+
+    public void setPositivePrefix(String value) {
+        posPrefNull = value == null;
+        if (!posPrefNull) {
+            setTextAttribute(this.addr, UNUM_POSITIVE_PREFIX, value);
+        }
+    }
+
+    public void setPositiveSuffix(String value) {
+        posSuffNull = value == null;
+        if (!posSuffNull) {
+            setTextAttribute(this.addr, UNUM_POSITIVE_SUFFIX, value);
+        }
+    }
+
+    public void setParseIntegerOnly(boolean value) {
+        int i = value ? -1 : 0;
+        setAttribute(this.addr, UNUM_PARSE_INT_ONLY, i);
+    }
+
+    static protected String getFieldType(Format.Field field) {
+        if(field == null) {
+            return null;
+        }
+        if(field.equals(NumberFormat.Field.SIGN)) {
+            return "sign";
+        }
+        if(field.equals(NumberFormat.Field.INTEGER)) {
+            return "integer";
+        }
+        if(field.equals(NumberFormat.Field.FRACTION)) {
+            return "fraction";
+        }
+        if(field.equals(NumberFormat.Field.EXPONENT)) {
+            return "exponent";
+        }
+        if(field.equals(NumberFormat.Field.EXPONENT_SIGN)) {
+            return "exponent_sign";
+        }
+        if(field.equals(NumberFormat.Field.EXPONENT_SYMBOL)) {
+            return "exponent_symbol";
+        }
+        if(field.equals(NumberFormat.Field.CURRENCY)) {
+            return "currency";
+        }
+        if(field.equals(NumberFormat.Field.GROUPING_SEPARATOR)) {
+            return "grouping_separator";
+        }
+        if(field.equals(NumberFormat.Field.DECIMAL_SEPARATOR)) {
+            return "decimal_separator";
+        }
+        if(field.equals(NumberFormat.Field.PERCENT)) {
+            return "percent";
+        }
+        if(field.equals(NumberFormat.Field.PERMILLE)) {
+            return "permille";
+        }
+        return null;
+    }
+
+    protected Format.Field getField(String type) {
+        if(type.equals("")) {
+            return null;
+        }
+        if(type.equals("sign")) {
+            return NumberFormat.Field.SIGN;
+        }
+        if(type.equals("integer")) {
+            return NumberFormat.Field.INTEGER;
+        }
+        if(type.equals("fraction")) {
+            return NumberFormat.Field.FRACTION;
+        }
+        if(type.equals("exponent")) {
+            return NumberFormat.Field.EXPONENT;
+        }
+        if(type.equals("exponent_sign")) {
+            return NumberFormat.Field.EXPONENT_SIGN;
+        }
+        if(type.equals("exponent_symbol")) {
+            return NumberFormat.Field.EXPONENT_SYMBOL;
+        }
+        if(type.equals("currency")) {
+            return NumberFormat.Field.CURRENCY;
+        }
+        if(type.equals("grouping_separator")) {
+            return NumberFormat.Field.GROUPING_SEPARATOR;
+        }
+        if(type.equals("decimal_separator")) {
+            return NumberFormat.Field.DECIMAL_SEPARATOR;
+        }
+        if(type.equals("percent")) {
+            return NumberFormat.Field.PERCENT;
+        }
+        if(type.equals("permille")) {
+            return NumberFormat.Field.PERMILLE;
+        }
+        return null;
+    }
+
+    private static int openDecimalFormat(String locale, String pattern) {
         try {
             // FIXME: if we're about to override everything, should we just ask for the cheapest locale (presumably the root locale)?
-            return NativeDecimalFormat.openDecimalFormatImpl(locale, pattern);
+            return openDecimalFormatImpl(locale, pattern);
         } catch (NullPointerException npe) {
             throw npe;
         } catch (RuntimeException re) {
             throw new IllegalArgumentException("syntax error: " + re.getMessage() + ": " + pattern);
         }
     }
-    private static native int openDecimalFormatImpl(String locale, String pattern);
 
-    static native void closeDecimalFormatImpl(int addr);
-    
-    static native int cloneDecimalFormatImpl(int addr);
-    
-    static native void setSymbol(int addr, int symbol, String str);
-    static native void setSymbol(int addr, int symbol, char ch);
-    
-    // FIXME: do we need this any more? the Java-side object should be the canonical source.
-    static native String getSymbol(int addr, int symbol);
-    
-    static native void setAttribute(int addr, int symbol, int i);
-    
-    static native int getAttribute(int addr, int symbol);
-
-    static native void setTextAttribute(int addr, int symbol, String str);
-
-    static native String getTextAttribute(int addr, int symbol);
-
-    static void applyPattern(int addr, boolean localized, String pattern) {
+    private static void applyPattern(int addr, boolean localized, String pattern) {
         try {
-            NativeDecimalFormat.applyPatternImpl(addr, localized, pattern);
+            applyPatternImpl(addr, localized, pattern);
         } catch (NullPointerException npe) {
             throw npe;
         } catch (RuntimeException re) {
             throw new IllegalArgumentException("syntax error: " + re.getMessage() + ": " + pattern);
         }
     }
+
     private static native void applyPatternImpl(int addr, boolean localized, String pattern);
-
-    static native String toPatternImpl(int addr, boolean localized);
-    
-    static native String format(int addr, long value, FieldPosition position, String fieldType, StringBuffer attributes);
-
-    static native String format(int addr, double value, FieldPosition position, String fieldType, StringBuffer attributes);
-
-    static native String format(int addr, String value, FieldPosition position, String fieldType, StringBuffer attributes, int scale);
-    
-    static native Number parse(int addr, String string, ParsePosition position);
+    private static native int cloneDecimalFormatImpl(int addr);
+    private static native void closeDecimalFormatImpl(int addr);
+    private static native String format(int addr, long value, FieldPosition position, String fieldType, StringBuffer attributes);
+    private static native String format(int addr, double value, FieldPosition position, String fieldType, StringBuffer attributes);
+    private static native String format(int addr, String value, FieldPosition position, String fieldType, StringBuffer attributes, int scale);
+    private static native int getAttribute(int addr, int symbol);
+    // FIXME: do we need getSymbol any more? the Java-side object should be the canonical source.
+    private static native String getSymbol(int addr, int symbol);
+    private static native String getTextAttribute(int addr, int symbol);
+    private static native int openDecimalFormatImpl(String locale, String pattern);
+    private static native Number parse(int addr, String string, ParsePosition position);
+    private static native void setSymbol(int addr, int symbol, String str);
+    private static native void setSymbol(int addr, int symbol, char ch);
+    private static native void setAttribute(int addr, int symbol, int i);
+    private static native void setTextAttribute(int addr, int symbol, String str);
+    private static native String toPatternImpl(int addr, boolean localized);
 }
diff --git a/libcore/luni/src/main/java/java/util/Currency.java b/libcore/luni/src/main/java/java/util/Currency.java
index e6b0202..ed9868f 100644
--- a/libcore/luni/src/main/java/java/util/Currency.java
+++ b/libcore/luni/src/main/java/java/util/Currency.java
@@ -37,6 +37,7 @@
     private static final long serialVersionUID = -158308464356906721L;
 
     private static final Hashtable<String, Currency> codesToCurrencies = new Hashtable<String, Currency>();
+    private static final Hashtable<Locale, Currency> localesToCurrencies = new Hashtable<Locale, Currency>();
 
     private final String currencyCode;
 
@@ -99,9 +100,14 @@
      */
     public static Currency getInstance(Locale locale) {
         // BEGIN android-changed
+        Currency currency = localesToCurrencies.get(locale);
+        if (currency != null) {
+            return currency;
+        }
         String country = locale.getCountry();
         String variant = locale.getVariant();
-        if (variant.length() > 0 && "EURO, HK, PREEURO".indexOf(variant) != -1) {
+        if (variant.length() > 0 && (variant.equals("EURO") || variant.equals("HK") ||
+                variant.equals("PREEURO"))) {
             country = country + "_" + variant;
         }
 
@@ -111,7 +117,9 @@
         } else if (currencyCode.equals("None")) {
             return null;
         }
-        return getInstance(currencyCode);
+        Currency result = getInstance(currencyCode);
+        localesToCurrencies.put(locale, result);
+        return result;
         // END android-changed
     }
 
diff --git a/libcore/luni/src/main/java/java/util/Formatter.java b/libcore/luni/src/main/java/java/util/Formatter.java
index 6be458e..12561e8 100644
--- a/libcore/luni/src/main/java/java/util/Formatter.java
+++ b/libcore/luni/src/main/java/java/util/Formatter.java
@@ -1313,21 +1313,16 @@
      * information contained in the format token.
      */
     private static class Transformer {
-
         private Formatter formatter;
-
         private FormatToken formatToken;
-
         private Object arg;
-
         private Locale locale;
-
+        private DecimalFormatSymbols decimalFormatSymbols;
         private static String lineSeparator;
 
         // BEGIN android-changed
-        // These objects are mutated during use, so can't be cached safely.
+        // This object is mutated during use, so can't be cached safely.
         // private NumberFormat numberFormat;
-        // private DecimalFormatSymbols decimalFormatSymbols;
         // END android-changed
 
         private DateTimeUtil dateTimeUtil;
@@ -1343,11 +1338,14 @@
             // END android-changed
         }
 
-        private DecimalFormatSymbols getDecimalFormatSymbols() {
-            // BEGIN android-changed
-            return LocaleCache.getDecimalFormatSymbols(locale);
-            // END android-changed
+        // BEGIN android-changed
+        DecimalFormatSymbols getDecimalFormatSymbols() {
+            if (decimalFormatSymbols == null) {
+                decimalFormatSymbols = new DecimalFormatSymbols(locale);
+            }
+            return decimalFormatSymbols;
         }
+        // END android-changed
 
         /*
          * Gets the formatted string according to the format token and the
@@ -1900,21 +1898,17 @@
                 throw new IllegalFormatFlagsException(formatToken.getStrFlags());
             }
 
-            if ('e' == Character.toLowerCase(currentConversionType)) {
+            if (currentConversionType == 'e' || currentConversionType == 'E') {
                 if (formatToken.flagComma) {
                     throw new FormatFlagsConversionMismatchException(formatToken.getStrFlags(),
                             currentConversionType);
                 }
-            }
-
-            if ('g' == Character.toLowerCase(currentConversionType)) {
+            } else if (currentConversionType == 'g' || currentConversionType == 'G') {
                 if (formatToken.flagSharp) {
                     throw new FormatFlagsConversionMismatchException(formatToken.getStrFlags(),
                             currentConversionType);
                 }
-            }
-
-            if ('a' == Character.toLowerCase(currentConversionType)) {
+            } else if (currentConversionType == 'a' || currentConversionType == 'A') {
                 if (formatToken.flagComma || formatToken.flagParenthesis) {
                     throw new FormatFlagsConversionMismatchException(formatToken.getStrFlags(),
                             currentConversionType);
@@ -1934,19 +1928,20 @@
                 return specialNumberResult;
             }
 
-            if (Character.toLowerCase(currentConversionType) != 'a' &&
+            if (currentConversionType != 'a' && currentConversionType != 'A' &&
                     !formatToken.isPrecisionSet()) {
                 formatToken.setPrecision(FormatToken.DEFAULT_PRECISION);
             }
 
             // output result
+            DecimalFormatSymbols decimalFormatSymbols = getDecimalFormatSymbols();
             FloatUtil floatUtil = new FloatUtil(result, formatToken,
-                    (DecimalFormat) getNumberFormat(), arg);
-            floatUtil.transform();
+                    (DecimalFormat) getNumberFormat(), decimalFormatSymbols, arg);
+            floatUtil.transform(currentConversionType);
 
             formatToken.setPrecision(FormatToken.UNSET);
 
-            if (getDecimalFormatSymbols().getMinusSign() == result.charAt(0)) {
+            if (decimalFormatSymbols.getMinusSign() == result.charAt(0)) {
                 if (formatToken.flagParenthesis) {
                     return wrapParentheses(result);
                 }
@@ -1963,11 +1958,11 @@
 
             char firstChar = result.charAt(0);
             if (formatToken.flagZero
-                    && (firstChar == floatUtil.getAddSign() || firstChar == floatUtil.getMinusSign())) {
+                    && (firstChar == floatUtil.getAddSign() || firstChar == decimalFormatSymbols.getMinusSign())) {
                 startIndex = 1;
             }
 
-            if ('a' == Character.toLowerCase(currentConversionType)) {
+            if (currentConversionType == 'a' || currentConversionType == 'A') {
                 startIndex += 2;
             }
             return padding(result, startIndex);
@@ -2023,26 +2018,22 @@
 
     private static class FloatUtil {
         private final StringBuilder result;
-
         private final DecimalFormat decimalFormat;
-
+        private final DecimalFormatSymbols decimalFormatSymbols;
         private final FormatToken formatToken;
-
         private final Object argument;
 
-        private final char minusSign;
-
-        FloatUtil(StringBuilder result, FormatToken formatToken,
-                DecimalFormat decimalFormat, Object argument) {
+        FloatUtil(StringBuilder result, FormatToken formatToken, DecimalFormat decimalFormat,
+                DecimalFormatSymbols decimalFormatSymbols, Object argument) {
             this.result = result;
             this.formatToken = formatToken;
             this.decimalFormat = decimalFormat;
+            this.decimalFormatSymbols = decimalFormatSymbols;
             this.argument = argument;
-            this.minusSign = decimalFormat.getDecimalFormatSymbols().getMinusSign();
         }
 
-        void transform() {
-            switch (formatToken.getConversionType()) {
+        void transform(char conversionType) {
+            switch (conversionType) {
                 case 'e':
                 case 'E': {
                     transform_e();
@@ -2063,16 +2054,11 @@
                     break;
                 }
                 default: {
-                    throw new UnknownFormatConversionException(String
-                            .valueOf(formatToken.getConversionType()));
+                    throw new UnknownFormatConversionException(String.valueOf(conversionType));
                 }
             }
         }
 
-        char getMinusSign() {
-            return minusSign;
-        }
-
         char getAddSign() {
             return '+';
         }
@@ -2096,9 +2082,7 @@
             // out.
             if (formatToken.flagSharp && formatToken.getPrecision() == 0) {
                 int indexOfE = result.indexOf("e"); //$NON-NLS-1$
-                char dot = decimalFormat.getDecimalFormatSymbols()
-                        .getDecimalSeparator();
-                result.insert(indexOfE, dot);
+                result.insert(indexOfE, decimalFormatSymbols.getDecimalSeparator());
             }
         }
 
@@ -2165,42 +2149,42 @@
             } else {
                 transform_f();
             }
-
         }
 
         void transform_f() {
-            StringBuilder pattern = new StringBuilder();
-            if (formatToken.flagComma) {
-                pattern.append(',');
-                int groupingSize = decimalFormat.getGroupingSize();
-                if (groupingSize > 1) {
-                    char[] sharps = new char[groupingSize - 1];
-                    Arrays.fill(sharps, '#');
-                    pattern.append(sharps);
+            // TODO: store a default DecimalFormat we can clone?
+            String pattern = "0.000000";
+            if (formatToken.flagComma || formatToken.getPrecision() != 6) {
+                StringBuilder patternBuilder = new StringBuilder();
+                if (formatToken.flagComma) {
+                    patternBuilder.append(',');
+                    int groupingSize = decimalFormat.getGroupingSize();
+                    if (groupingSize > 1) {
+                        char[] sharps = new char[groupingSize - 1];
+                        Arrays.fill(sharps, '#');
+                        patternBuilder.append(sharps);
+                    }
                 }
+                patternBuilder.append(0);
+                if (formatToken.getPrecision() > 0) {
+                    patternBuilder.append('.');
+                    char[] zeros = new char[formatToken.getPrecision()];
+                    Arrays.fill(zeros, '0');
+                    patternBuilder.append(zeros);
+                }
+                pattern = patternBuilder.toString();
             }
-
-            pattern.append(0);
-
-            if (formatToken.getPrecision() > 0) {
-                pattern.append('.');
-                char[] zeros = new char[formatToken.getPrecision()];
-                Arrays.fill(zeros, '0');
-                pattern.append(zeros);
-            }
-            decimalFormat.applyPattern(pattern.toString());
+            // TODO: if DecimalFormat.toPattern was cheap, we could make this cheap (preferably *in* DecimalFormat).
+            decimalFormat.applyPattern(pattern);
             result.append(decimalFormat.format(argument));
             // if the flag is sharp and decimal separator is always given
             // out.
             if (formatToken.flagSharp && formatToken.getPrecision() == 0) {
-                char dot = decimalFormat.getDecimalFormatSymbols().getDecimalSeparator();
-                result.append(dot);
+                result.append(decimalFormatSymbols.getDecimalSeparator());
             }
         }
 
         void transform_a() {
-            char currentConversionType = formatToken.getConversionType();
-
             if (argument instanceof Float) {
                 Float F = (Float) argument;
                 result.append(Float.toHexString(F.floatValue()));
@@ -2211,7 +2195,7 @@
             } else {
                 // BigInteger is not supported.
                 throw new IllegalFormatConversionException(
-                        currentConversionType, argument.getClass());
+                        formatToken.getConversionType(), argument.getClass());
             }
 
             if (!formatToken.isPrecisionSet()) {
diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/LocaleCache.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/LocaleCache.java
index 5318e38..7b9957d 100644
--- a/libcore/luni/src/main/java/org/apache/harmony/luni/util/LocaleCache.java
+++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/LocaleCache.java
@@ -32,8 +32,6 @@
 
     private NumberFormat numberFormat = null;
 
-    private DecimalFormatSymbols decimalFormatSymbols = null;
-
     private final Locale locale;
 
     private LocaleCache(Locale locale) {
@@ -66,18 +64,4 @@
         // NumberFormat is mutable, so return a new clone each time.
         return (NumberFormat) lc.numberFormat.clone();
     }
-
-    /**
-     * Returns a DecimalFormatSymbols object for the specified Locale.
-     */
-    public static DecimalFormatSymbols getDecimalFormatSymbols(Locale locale) {
-        LocaleCache lc = getLocaleCache(locale);
-        if (lc.decimalFormatSymbols == null) {
-            lc.decimalFormatSymbols = new DecimalFormatSymbols(locale);
-        }
-
-        // DecimalFormatSymbols is mutable, so return a new clone each time.
-        return (DecimalFormatSymbols) lc.decimalFormatSymbols.clone();
-    }
-
 }
diff --git a/libcore/luni/src/test/java/java/util/FormatterTest.java b/libcore/luni/src/test/java/java/util/FormatterTest.java
index 2af7d97..2adbbb4 100644
--- a/libcore/luni/src/test/java/java/util/FormatterTest.java
+++ b/libcore/luni/src/test/java/java/util/FormatterTest.java
@@ -28,9 +28,18 @@
         assertEquals("JAKOB ARJOUN\u0130", String.format(new Locale("tr", "TR"), "%S", "jakob arjouni"));
     }
 
+    // Creating a NumberFormat is expensive, so we like to reuse them, but we need to be careful
+    // because they're mutable.
+    public void test_NumberFormat_reuse() throws Exception {
+        assertEquals("7.000000 7", String.format("%.6f %d", 7, 7));
+    }
+
     public void test_formatNull() throws Exception {
+        // We fast-path %s and %d (with no configuration) but need to make sure we handle the
+        // special case of the null argument...
         assertEquals("null", String.format(Locale.US, "%s", null));
         assertEquals("null", String.format(Locale.US, "%d", null));
+        // ...without screwing up conversions that don't take an argument.
         assertEquals("%", String.format(Locale.US, "%%"));
     }
 }
diff --git a/libcore/text/src/main/java/java/text/DecimalFormat.java b/libcore/text/src/main/java/java/text/DecimalFormat.java
index b8bc41b..0991adb 100644
--- a/libcore/text/src/main/java/java/text/DecimalFormat.java
+++ b/libcore/text/src/main/java/java/text/DecimalFormat.java
@@ -32,6 +32,7 @@
 import java.util.Currency;
 import java.util.Locale;
 
+import com.ibm.icu4jni.text.NativeDecimalFormat;
 import com.ibm.icu4jni.util.LocaleData;
 
 /**
@@ -550,7 +551,7 @@
 
     private transient DecimalFormatSymbols symbols;
 
-    private transient com.ibm.icu4jni.text.DecimalFormat dform;
+    private transient NativeDecimalFormat dform;
 
     /**
      * Constructs a new {@code DecimalFormat} for formatting and parsing numbers
@@ -608,7 +609,7 @@
     // BEGIN android-changed: reduce duplication.
     private void initNative(String pattern, Locale locale) {
         try {
-            this.dform = new com.ibm.icu4jni.text.DecimalFormat(pattern, locale, symbols);
+            this.dform = new NativeDecimalFormat(pattern, locale, symbols);
         } catch (IllegalArgumentException ex) {
             throw new IllegalArgumentException(pattern);
         }
@@ -655,7 +656,7 @@
     @Override
     public Object clone() {
         DecimalFormat clone = (DecimalFormat) super.clone();
-        clone.dform = (com.ibm.icu4jni.text.DecimalFormat) dform.clone();
+        clone.dform = (NativeDecimalFormat) dform.clone();
         clone.symbols = (DecimalFormatSymbols) symbols.clone();
         return clone;
     }
@@ -917,10 +918,9 @@
      */
     @Override
     public void setParseIntegerOnly(boolean value) {
-        // In this implementation, com.ibm.icu.text.DecimalFormat is wrapped to
+        // In this implementation, NativeDecimalFormat is wrapped to
         // fulfill most of the format and parse feature. And this method is
-        // delegated to the wrapped instance of com.ibm.icu.text.DecimalFormat.
-
+        // delegated to the wrapped instance of NativeDecimalFormat.
         dform.setParseIntegerOnly(value);
     }
 
@@ -1348,7 +1348,6 @@
 
         Locale locale = (Locale) Format.getInternalField("locale", symbols);
         // BEGIN android-changed
-        //this.dform = new com.ibm.icu4jni.text.DecimalFormat("", locale, symbols);
         initNative("", locale);
         // END android-changed
         dform.setPositivePrefix(positivePrefix);
diff --git a/vm/Thread.c b/vm/Thread.c
index e4ee8e3..05c89e2 100644
--- a/vm/Thread.c
+++ b/vm/Thread.c
@@ -224,8 +224,8 @@
    use printf on stdout to print GC debug messages)
 */
 
-#define kMaxThreadId        ((1<<15) - 1)
-#define kMainThreadId       ((1<<1) | 1)
+#define kMaxThreadId        ((1 << 16) - 1)
+#define kMainThreadId       1
 
 
 static Thread* allocThread(int interpStackSize);
@@ -1192,12 +1192,10 @@
  */
 static void assignThreadId(Thread* thread)
 {
-    /* Find a small unique integer.  threadIdMap is a vector of
+    /*
+     * Find a small unique integer.  threadIdMap is a vector of
      * kMaxThreadId bits;  dvmAllocBit() returns the index of a
      * bit, meaning that it will always be < kMaxThreadId.
-     *
-     * The thin locking magic requires that the low bit is always
-     * set, so we do it once, here.
      */
     int num = dvmAllocBit(gDvm.threadIdMap);
     if (num < 0) {
@@ -1205,7 +1203,7 @@
         dvmAbort();     // TODO: make this a non-fatal error result
     }
 
-    thread->threadId = ((num + 1) << 1) | 1;
+    thread->threadId = num + 1;
 
     assert(thread->threadId != 0);
     assert(thread->threadId != DVM_LOCK_INITIAL_THIN_VALUE);
diff --git a/vm/compiler/codegen/arm/CodegenDriver.c b/vm/compiler/codegen/arm/CodegenDriver.c
index 1993c9d..8861102 100644
--- a/vm/compiler/codegen/arm/CodegenDriver.c
+++ b/vm/compiler/codegen/arm/CodegenDriver.c
@@ -2454,7 +2454,19 @@
             RegLocation rlResult;
             ClassObject *classPtr =
               (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]);
-            assert(classPtr != NULL);
+            /*
+             * Note: It is possible that classPtr is NULL at this point,
+             * even though this instruction has been successfully interpreted.
+             * If the previous interpretation had a null source, the
+             * interpreter would not have bothered to resolve the clazz.
+             * Bail out to the interpreter in this case, and log it
+             * so that we can tell if it happens frequently.
+             */
+            if (classPtr == NULL) {
+                LOGD("null clazz in OP_INSTANCE_OF, single-stepping");
+                genInterpSingleStep(cUnit, mir);
+                break;
+            }
             flushAllRegs(cUnit);   /* Send everything to home location */
             loadValueDirectFixed(cUnit, rlSrc, r0);  /* Ref */
             loadConstant(cUnit, r2, (int) classPtr );