am 63529068: am 82d74983: Merge "Fix a native memory leak in SimpleDateFormat cloning."

* commit '63529068e9655252a46b372fa8919c59177fb453':
  Fix a native memory leak in SimpleDateFormat cloning.
diff --git a/luni/src/main/java/java/text/DateFormat.java b/luni/src/main/java/java/text/DateFormat.java
index 4055d20..b45e699 100644
--- a/luni/src/main/java/java/text/DateFormat.java
+++ b/luni/src/main/java/java/text/DateFormat.java
@@ -286,10 +286,6 @@
 
     /**
      * Returns a new instance of {@code DateFormat} with the same properties.
-     *
-     * @return a shallow copy of this {@code DateFormat}.
-     *
-     * @see java.lang.Cloneable
      */
     @Override
     public Object clone() {
diff --git a/luni/src/main/java/java/text/DecimalFormat.java b/luni/src/main/java/java/text/DecimalFormat.java
index eded6a8..948bec1 100644
--- a/luni/src/main/java/java/text/DecimalFormat.java
+++ b/luni/src/main/java/java/text/DecimalFormat.java
@@ -501,16 +501,7 @@
 
     private transient DecimalFormatSymbols symbols;
 
-    private transient NativeDecimalFormat dform;
-    private final Object finalizerGuardian = new Object() {
-        @Override protected void finalize() throws Throwable {
-            try {
-                dform.close();
-            } finally {
-                super.finalize();
-            }
-        }
-    };
+    private transient NativeDecimalFormat ndf;
 
     private transient RoundingMode roundingMode = RoundingMode.HALF_EVEN;
 
@@ -562,14 +553,14 @@
 
     private void initNative(String pattern) {
         try {
-            this.dform = new NativeDecimalFormat(pattern, symbols);
+            this.ndf = new NativeDecimalFormat(pattern, symbols);
         } catch (IllegalArgumentException ex) {
             throw new IllegalArgumentException(pattern);
         }
-        super.setMaximumFractionDigits(dform.getMaximumFractionDigits());
-        super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits());
-        super.setMinimumFractionDigits(dform.getMinimumFractionDigits());
-        super.setMinimumIntegerDigits(dform.getMinimumIntegerDigits());
+        super.setMaximumFractionDigits(ndf.getMaximumFractionDigits());
+        super.setMaximumIntegerDigits(ndf.getMaximumIntegerDigits());
+        super.setMinimumFractionDigits(ndf.getMinimumFractionDigits());
+        super.setMinimumIntegerDigits(ndf.getMinimumIntegerDigits());
     }
 
     /**
@@ -582,7 +573,7 @@
      *            if the pattern cannot be parsed.
      */
     public void applyLocalizedPattern(String pattern) {
-        dform.applyLocalizedPattern(pattern);
+        ndf.applyLocalizedPattern(pattern);
     }
 
     /**
@@ -595,20 +586,17 @@
      *            if the pattern cannot be parsed.
      */
     public void applyPattern(String pattern) {
-        dform.applyPattern(pattern);
+        ndf.applyPattern(pattern);
     }
 
     /**
      * Returns a new instance of {@code DecimalFormat} with the same pattern and
-     * properties as this decimal format.
-     *
-     * @return a shallow copy of this decimal format.
-     * @see java.lang.Cloneable
+     * properties.
      */
     @Override
     public Object clone() {
         DecimalFormat clone = (DecimalFormat) super.clone();
-        clone.dform = (NativeDecimalFormat) dform.clone();
+        clone.ndf = (NativeDecimalFormat) ndf.clone();
         clone.symbols = (DecimalFormatSymbols) symbols.clone();
         return clone;
     }
@@ -633,7 +621,7 @@
             return false;
         }
         DecimalFormat other = (DecimalFormat) object;
-        return (this.dform == null ? other.dform == null : this.dform.equals(other.dform)) &&
+        return (this.ndf == null ? other.ndf == null : this.ndf.equals(other.ndf)) &&
                 getDecimalFormatSymbols().equals(other.getDecimalFormatSymbols());
     }
 
@@ -656,7 +644,7 @@
         if (object == null) {
             throw new NullPointerException("object == null");
         }
-        return dform.formatToCharacterIterator(object);
+        return ndf.formatToCharacterIterator(object);
     }
 
     private void checkBufferAndFieldPosition(StringBuffer buffer, FieldPosition position) {
@@ -686,14 +674,14 @@
                 setRoundingMode(RoundingMode.UNNECESSARY);
             }
         }
-        buffer.append(dform.formatDouble(value, position));
+        buffer.append(ndf.formatDouble(value, position));
         return buffer;
     }
 
     @Override
     public StringBuffer format(long value, StringBuffer buffer, FieldPosition position) {
         checkBufferAndFieldPosition(buffer, position);
-        buffer.append(dform.formatLong(value, position));
+        buffer.append(ndf.formatLong(value, position));
         return buffer;
     }
 
@@ -703,12 +691,12 @@
         if (number instanceof BigInteger) {
             BigInteger bigInteger = (BigInteger) number;
             char[] chars = (bigInteger.bitLength() < 64)
-                    ? dform.formatLong(bigInteger.longValue(), position)
-                    : dform.formatBigInteger(bigInteger, position);
+                    ? ndf.formatLong(bigInteger.longValue(), position)
+                    : ndf.formatBigInteger(bigInteger, position);
             buffer.append(chars);
             return buffer;
         } else if (number instanceof BigDecimal) {
-            buffer.append(dform.formatBigDecimal((BigDecimal) number, position));
+            buffer.append(ndf.formatBigDecimal((BigDecimal) number, position));
             return buffer;
         }
         return super.format(number, buffer, position);
@@ -743,7 +731,7 @@
      * @return the number of digits grouped together.
      */
     public int getGroupingSize() {
-        return dform.getGroupingSize();
+        return ndf.getGroupingSize();
     }
 
     /**
@@ -753,7 +741,7 @@
      * @return the multiplier.
      */
     public int getMultiplier() {
-        return dform.getMultiplier();
+        return ndf.getMultiplier();
     }
 
     /**
@@ -762,7 +750,7 @@
      * @return the negative prefix.
      */
     public String getNegativePrefix() {
-        return dform.getNegativePrefix();
+        return ndf.getNegativePrefix();
     }
 
     /**
@@ -771,7 +759,7 @@
      * @return the negative suffix.
      */
     public String getNegativeSuffix() {
-        return dform.getNegativeSuffix();
+        return ndf.getNegativeSuffix();
     }
 
     /**
@@ -780,7 +768,7 @@
      * @return the positive prefix.
      */
     public String getPositivePrefix() {
-        return dform.getPositivePrefix();
+        return ndf.getPositivePrefix();
     }
 
     /**
@@ -789,12 +777,12 @@
      * @return the positive suffix.
      */
     public String getPositiveSuffix() {
-        return dform.getPositiveSuffix();
+        return ndf.getPositiveSuffix();
     }
 
     @Override
     public int hashCode() {
-        return dform.hashCode();
+        return getPositivePrefix().hashCode();
     }
 
     /**
@@ -805,7 +793,7 @@
      *         {@code false} otherwise.
      */
     public boolean isDecimalSeparatorAlwaysShown() {
-        return dform.isDecimalSeparatorAlwaysShown();
+        return ndf.isDecimalSeparatorAlwaysShown();
     }
 
     /**
@@ -817,7 +805,7 @@
      *         {@code Double}.
      */
     public boolean isParseBigDecimal() {
-        return dform.isParseBigDecimal();
+        return ndf.isParseBigDecimal();
     }
 
     /**
@@ -838,7 +826,7 @@
         // 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 NativeDecimalFormat.
-        dform.setParseIntegerOnly(value);
+        ndf.setParseIntegerOnly(value);
     }
 
     /**
@@ -850,7 +838,7 @@
      */
     @Override
     public boolean isParseIntegerOnly() {
-        return dform.isParseIntegerOnly();
+        return ndf.isParseIntegerOnly();
     }
 
     private static final Double NEGATIVE_ZERO_DOUBLE = new Double(-0.0);
@@ -880,7 +868,7 @@
      */
     @Override
     public Number parse(String string, ParsePosition position) {
-        Number number = dform.parse(string, position);
+        Number number = ndf.parse(string, position);
         if (number == null) {
             return null;
         }
@@ -918,7 +906,7 @@
         if (value != null) {
             // The Java object is canonical, and we copy down to native code.
             this.symbols = (DecimalFormatSymbols) value.clone();
-            dform.setDecimalFormatSymbols(this.symbols);
+            ndf.setDecimalFormatSymbols(this.symbols);
         }
     }
 
@@ -932,7 +920,7 @@
      */
     @Override
     public void setCurrency(Currency currency) {
-        dform.setCurrency(Currency.getInstance(currency.getCurrencyCode()));
+        ndf.setCurrency(Currency.getInstance(currency.getCurrencyCode()));
         symbols.setCurrency(currency);
     }
 
@@ -945,7 +933,7 @@
      *            formatted; {@code false} otherwise.
      */
     public void setDecimalSeparatorAlwaysShown(boolean value) {
-        dform.setDecimalSeparatorAlwaysShown(value);
+        ndf.setDecimalSeparatorAlwaysShown(value);
     }
 
     /**
@@ -957,7 +945,7 @@
      *            the number of digits grouped together.
      */
     public void setGroupingSize(int value) {
-        dform.setGroupingSize(value);
+        ndf.setGroupingSize(value);
     }
 
     /**
@@ -969,7 +957,7 @@
      */
     @Override
     public void setGroupingUsed(boolean value) {
-        dform.setGroupingUsed(value);
+        ndf.setGroupingUsed(value);
     }
 
     /**
@@ -979,7 +967,7 @@
      */
     @Override
     public boolean isGroupingUsed() {
-        return dform.isGroupingUsed();
+        return ndf.isGroupingUsed();
     }
 
     /**
@@ -992,7 +980,7 @@
     @Override
     public void setMaximumFractionDigits(int value) {
         super.setMaximumFractionDigits(value);
-        dform.setMaximumFractionDigits(getMaximumFractionDigits());
+        ndf.setMaximumFractionDigits(getMaximumFractionDigits());
         // Changing the maximum fraction digits needs to update ICU4C's rounding configuration.
         setRoundingMode(roundingMode);
     }
@@ -1007,7 +995,7 @@
     @Override
     public void setMaximumIntegerDigits(int value) {
         super.setMaximumIntegerDigits(value);
-        dform.setMaximumIntegerDigits(getMaximumIntegerDigits());
+        ndf.setMaximumIntegerDigits(getMaximumIntegerDigits());
     }
 
     /**
@@ -1020,7 +1008,7 @@
     @Override
     public void setMinimumFractionDigits(int value) {
         super.setMinimumFractionDigits(value);
-        dform.setMinimumFractionDigits(getMinimumFractionDigits());
+        ndf.setMinimumFractionDigits(getMinimumFractionDigits());
     }
 
     /**
@@ -1033,7 +1021,7 @@
     @Override
     public void setMinimumIntegerDigits(int value) {
         super.setMinimumIntegerDigits(value);
-        dform.setMinimumIntegerDigits(getMinimumIntegerDigits());
+        ndf.setMinimumIntegerDigits(getMinimumIntegerDigits());
     }
 
     /**
@@ -1044,7 +1032,7 @@
      *            the multiplier.
      */
     public void setMultiplier(int value) {
-        dform.setMultiplier(value);
+        ndf.setMultiplier(value);
     }
 
     /**
@@ -1054,7 +1042,7 @@
      *            the negative prefix.
      */
     public void setNegativePrefix(String value) {
-        dform.setNegativePrefix(value);
+        ndf.setNegativePrefix(value);
     }
 
     /**
@@ -1064,7 +1052,7 @@
      *            the negative suffix.
      */
     public void setNegativeSuffix(String value) {
-        dform.setNegativeSuffix(value);
+        ndf.setNegativeSuffix(value);
     }
 
     /**
@@ -1074,7 +1062,7 @@
      *            the positive prefix.
      */
     public void setPositivePrefix(String value) {
-        dform.setPositivePrefix(value);
+        ndf.setPositivePrefix(value);
     }
 
     /**
@@ -1084,7 +1072,7 @@
      *            the positive suffix.
      */
     public void setPositiveSuffix(String value) {
-        dform.setPositiveSuffix(value);
+        ndf.setPositiveSuffix(value);
     }
 
     /**
@@ -1096,7 +1084,7 @@
      *            {@code BigDecimal}; {@code false} otherwise.
      */
     public void setParseBigDecimal(boolean newValue) {
-        dform.setParseBigDecimal(newValue);
+        ndf.setParseBigDecimal(newValue);
     }
 
     /**
@@ -1106,7 +1094,7 @@
      * @return the localized pattern.
      */
     public String toLocalizedPattern() {
-        return dform.toLocalizedPattern();
+        return ndf.toLocalizedPattern();
     }
 
     /**
@@ -1116,7 +1104,7 @@
      * @return the non-localized pattern.
      */
     public String toPattern() {
-        return dform.toPattern();
+        return ndf.toPattern();
     }
 
     // the fields list to be serialized
@@ -1157,27 +1145,27 @@
      */
     private void writeObject(ObjectOutputStream stream) throws IOException, ClassNotFoundException {
         ObjectOutputStream.PutField fields = stream.putFields();
-        fields.put("positivePrefix", dform.getPositivePrefix());
-        fields.put("positiveSuffix", dform.getPositiveSuffix());
-        fields.put("negativePrefix", dform.getNegativePrefix());
-        fields.put("negativeSuffix", dform.getNegativeSuffix());
+        fields.put("positivePrefix", ndf.getPositivePrefix());
+        fields.put("positiveSuffix", ndf.getPositiveSuffix());
+        fields.put("negativePrefix", ndf.getNegativePrefix());
+        fields.put("negativeSuffix", ndf.getNegativeSuffix());
         fields.put("posPrefixPattern", (String) null);
         fields.put("posSuffixPattern", (String) null);
         fields.put("negPrefixPattern", (String) null);
         fields.put("negSuffixPattern", (String) null);
-        fields.put("multiplier", dform.getMultiplier());
-        fields.put("groupingSize", (byte) dform.getGroupingSize());
-        fields.put("groupingUsed", dform.isGroupingUsed());
-        fields.put("decimalSeparatorAlwaysShown", dform.isDecimalSeparatorAlwaysShown());
-        fields.put("parseBigDecimal", dform.isParseBigDecimal());
+        fields.put("multiplier", ndf.getMultiplier());
+        fields.put("groupingSize", (byte) ndf.getGroupingSize());
+        fields.put("groupingUsed", ndf.isGroupingUsed());
+        fields.put("decimalSeparatorAlwaysShown", ndf.isDecimalSeparatorAlwaysShown());
+        fields.put("parseBigDecimal", ndf.isParseBigDecimal());
         fields.put("roundingMode", roundingMode);
         fields.put("symbols", symbols);
         fields.put("useExponentialNotation", false);
         fields.put("minExponentDigits", (byte) 0);
-        fields.put("maximumIntegerDigits", dform.getMaximumIntegerDigits());
-        fields.put("minimumIntegerDigits", dform.getMinimumIntegerDigits());
-        fields.put("maximumFractionDigits", dform.getMaximumFractionDigits());
-        fields.put("minimumFractionDigits", dform.getMinimumFractionDigits());
+        fields.put("maximumIntegerDigits", ndf.getMaximumIntegerDigits());
+        fields.put("minimumIntegerDigits", ndf.getMinimumIntegerDigits());
+        fields.put("maximumFractionDigits", ndf.getMaximumFractionDigits());
+        fields.put("minimumFractionDigits", ndf.getMinimumFractionDigits());
         fields.put("serialVersionOnStream", 4);
         stream.writeFields();
     }
@@ -1198,14 +1186,14 @@
         this.symbols = (DecimalFormatSymbols) fields.get("symbols", null);
 
         initNative("");
-        dform.setPositivePrefix((String) fields.get("positivePrefix", ""));
-        dform.setPositiveSuffix((String) fields.get("positiveSuffix", ""));
-        dform.setNegativePrefix((String) fields.get("negativePrefix", "-"));
-        dform.setNegativeSuffix((String) fields.get("negativeSuffix", ""));
-        dform.setMultiplier(fields.get("multiplier", 1));
-        dform.setGroupingSize(fields.get("groupingSize", (byte) 3));
-        dform.setGroupingUsed(fields.get("groupingUsed", true));
-        dform.setDecimalSeparatorAlwaysShown(fields.get("decimalSeparatorAlwaysShown", false));
+        ndf.setPositivePrefix((String) fields.get("positivePrefix", ""));
+        ndf.setPositiveSuffix((String) fields.get("positiveSuffix", ""));
+        ndf.setNegativePrefix((String) fields.get("negativePrefix", "-"));
+        ndf.setNegativeSuffix((String) fields.get("negativeSuffix", ""));
+        ndf.setMultiplier(fields.get("multiplier", 1));
+        ndf.setGroupingSize(fields.get("groupingSize", (byte) 3));
+        ndf.setGroupingUsed(fields.get("groupingUsed", true));
+        ndf.setDecimalSeparatorAlwaysShown(fields.get("decimalSeparatorAlwaysShown", false));
 
         setRoundingMode((RoundingMode) fields.get("roundingMode", RoundingMode.HALF_EVEN));
 
@@ -1218,8 +1206,8 @@
         // behavior in this area is, and it's not obvious how we can second-guess ICU (or tell
         // it to just do exactly what we ask). We only need to do this with maximumIntegerDigits
         // because ICU doesn't seem to have its own ideas about the other options.
-        dform.setMaximumIntegerDigits(maximumIntegerDigits);
-        super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits());
+        ndf.setMaximumIntegerDigits(maximumIntegerDigits);
+        super.setMaximumIntegerDigits(ndf.getMaximumIntegerDigits());
 
         setMinimumIntegerDigits(minimumIntegerDigits);
         setMinimumFractionDigits(minimumFractionDigits);
@@ -1253,7 +1241,7 @@
         this.roundingMode = roundingMode;
         if (roundingMode != RoundingMode.UNNECESSARY) { // ICU4C doesn't support UNNECESSARY.
             double roundingIncrement = 1.0 / Math.pow(10, Math.max(0, getMaximumFractionDigits()));
-            dform.setRoundingMode(roundingMode, roundingIncrement);
+            ndf.setRoundingMode(roundingMode, roundingIncrement);
         }
     }
 }
diff --git a/luni/src/main/java/java/text/NumberFormat.java b/luni/src/main/java/java/text/NumberFormat.java
index 070174b..c285e3d 100644
--- a/luni/src/main/java/java/text/NumberFormat.java
+++ b/luni/src/main/java/java/text/NumberFormat.java
@@ -165,11 +165,7 @@
     }
 
     /**
-     * Returns a new {@code NumberFormat} with the same properties as this
-     * {@code NumberFormat}.
-     *
-     * @return a shallow copy of this {@code NumberFormat}.
-     * @see java.lang.Cloneable
+     * Returns a new {@code NumberFormat} with the same properties.
      */
     @Override
     public Object clone() {
diff --git a/luni/src/main/java/java/text/SimpleDateFormat.java b/luni/src/main/java/java/text/SimpleDateFormat.java
index da5af85..f682c0b 100644
--- a/luni/src/main/java/java/text/SimpleDateFormat.java
+++ b/luni/src/main/java/java/text/SimpleDateFormat.java
@@ -411,9 +411,6 @@
     /**
      * Returns a new {@code SimpleDateFormat} with the same pattern and
      * properties as this simple date format.
-     *
-     * @return a shallow copy of this simple date format.
-     * @see java.lang.Cloneable
      */
     @Override
     public Object clone() {
diff --git a/luni/src/main/java/libcore/icu/NativeDecimalFormat.java b/luni/src/main/java/libcore/icu/NativeDecimalFormat.java
index 74775e5..f6b5214 100644
--- a/luni/src/main/java/libcore/icu/NativeDecimalFormat.java
+++ b/luni/src/main/java/libcore/icu/NativeDecimalFormat.java
@@ -29,7 +29,7 @@
 import java.util.Currency;
 import java.util.NoSuchElementException;
 
-public final class NativeDecimalFormat {
+public final class NativeDecimalFormat implements Cloneable {
     /**
      * Constants corresponding to the native type UNumberFormatSymbol, for setSymbol.
      */
@@ -144,22 +144,6 @@
         this.lastPattern = pattern;
     }
 
-    // Used to implement clone.
-    private NativeDecimalFormat(NativeDecimalFormat other) {
-        this.address = cloneImpl(other.address);
-        this.lastPattern = other.lastPattern;
-        this.negPrefNull = other.negPrefNull;
-        this.negSuffNull = other.negSuffNull;
-        this.posPrefNull = other.posPrefNull;
-        this.posSuffNull = other.posSuffNull;
-    }
-
-    // TODO: remove this and just have DecimalFormat.hashCode do the right thing itself.
-    @Override
-    public int hashCode() {
-        return this.getPositivePrefix().hashCode();
-    }
-
     public synchronized void close() {
         if (address != 0) {
             close(address);
@@ -167,9 +151,27 @@
         }
     }
 
-    @Override
-    public Object clone() {
-        return new NativeDecimalFormat(this);
+    @Override protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    @Override public Object clone() {
+        try {
+            NativeDecimalFormat clone = (NativeDecimalFormat) super.clone();
+            clone.address = cloneImpl(address);
+            clone.lastPattern = lastPattern;
+            clone.negPrefNull = negPrefNull;
+            clone.negSuffNull = negSuffNull;
+            clone.posPrefNull = posPrefNull;
+            clone.posSuffNull = posSuffNull;
+            return clone;
+        } catch (CloneNotSupportedException unexpected) {
+            throw new AssertionError(unexpected);
+        }
     }
 
     /**