From eb5822b70b3e578dbe692a60b8a93b90a9d4df93 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Mon, 25 Jan 2010 23:13:46 -0800 Subject: [PATCH] Simplify our DecimalFormat. Both the is-a and has-a hierarchies for our DecimalFormat implementation were over-complicated. This patch starts to address that, and makes cloning twice as fast (50us versus 100us), but not as fast as I'd like (<10us), and without making much of a dent in the time it takes to create a new NumberFormat (550us versus 600us). The speed of cloning is important because Formatter has a hack that uses it, and I want to change NumberFormat so that it always hands out clones... at least until I have time to make "new NumberFormat" acceptably fast. Also fixes DecimalFormat.applyLocalizedPattern (which used to behave as if you'd called applyPattern). --- .../java/com/ibm/icu4jni/text/DecimalFormat.java | 218 ++++++++--------- .../com/ibm/icu4jni/text/DecimalFormatSymbols.java | 268 --------------------- .../com/ibm/icu4jni/text/NativeDecimalFormat.java | 26 +- .../icu/src/main/native/NativeDecimalFormat.cpp | 124 ++++------ libcore/icu/src/main/native/Resources.cpp | 5 +- libcore/luni/src/test/java/java/text/AllTests.java | 1 + .../src/test/java/java/text/DecimalFormatTest.java | 37 +++ .../src/main/java/java/text/DecimalFormat.java | 185 +++++--------- .../main/java/java/text/DecimalFormatSymbols.java | 13 - .../text/tests/java/text/DecimalFormatTest.java | 4 +- 10 files changed, 281 insertions(+), 600 deletions(-) delete mode 100644 libcore/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormatSymbols.java create mode 100644 libcore/luni/src/test/java/java/text/DecimalFormatTest.java 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 index 4b296d581..d995dc690 100644 --- a/libcore/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormat.java +++ b/libcore/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormat.java @@ -18,11 +18,13 @@ 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; @@ -30,19 +32,32 @@ import java.text.ParsePosition; import java.util.Currency; import java.util.Locale; -public class DecimalFormat extends NumberFormat { - - private int addr; - - private DecimalFormatSymbols symbols; - - // fix to be icu4j conform (harmony wants this field to exist) - // for serialization of java.text.DecimalFormat - @SuppressWarnings("unused") - private boolean useExponentialNotation = false; - @SuppressWarnings("unused") - private byte minExponentDigits = 0; - +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; @@ -50,37 +65,52 @@ public class DecimalFormat extends NumberFormat { /** * Cache the BigDecimal form of the multiplier. This is null until we've - * formatted a BigDecimal (with a multipler that is not 1), or the user has + * 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, DecimalFormatSymbols icuSymbols) { - this.addr = icuSymbols.getAddr(); - this.symbols = icuSymbols; - applyPattern(pattern); + 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 super.hashCode() * 37 + this.getPositivePrefix().hashCode(); + return this.getPositivePrefix().hashCode(); } @Override public Object clone() { - String pat = this.toPattern(); - DecimalFormatSymbols sym = (DecimalFormatSymbols) this.symbols.clone(); - DecimalFormat newdf = new DecimalFormat(pat, sym); - newdf.setMaximumIntegerDigits(this.getMaximumIntegerDigits()); - newdf.setMaximumFractionDigits(this.getMaximumFractionDigits()); - newdf.setMinimumIntegerDigits(this.getMinimumIntegerDigits()); - newdf.setMinimumFractionDigits(this.getMinimumFractionDigits()); - newdf.setGroupingUsed(this.isGroupingUsed()); - newdf.setGroupingSize(this.getGroupingSize()); - return newdf; + 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; @@ -89,39 +119,47 @@ public class DecimalFormat extends NumberFormat { return false; } DecimalFormat obj = (DecimalFormat) object; - - if(obj.addr == this.addr) { + 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(); + } - boolean result = super.equals(object); - - - result &= obj.toPattern().equals(this.toPattern()); - result &= obj.isDecimalSeparatorAlwaysShown() == this.isDecimalSeparatorAlwaysShown(); - result &= obj.getGroupingSize() == this.getGroupingSize(); - result &= obj.getMultiplier() == this.getMultiplier(); - result &= obj.getNegativePrefix().equals(this.getNegativePrefix()); - result &= obj.getNegativeSuffix().equals(this.getNegativeSuffix()); - result &= obj.getPositivePrefix().equals(this.getPositivePrefix()); - result &= obj.getPositiveSuffix().equals(this.getPositiveSuffix()); - result &= obj.getMaximumIntegerDigits() == this.getMaximumIntegerDigits(); - result &= obj.getMaximumFractionDigits() == this.getMaximumFractionDigits(); - result &= obj.getMinimumIntegerDigits() == this.getMinimumIntegerDigits(); - result &= obj.getMinimumFractionDigits() == this.getMinimumFractionDigits(); - result &= obj.isGroupingUsed() == this.isGroupingUsed(); - Currency objCurr = obj.getCurrency(); - Currency thisCurr = this.getCurrency(); - if(objCurr != null) { - result &= objCurr.getCurrencyCode().equals(thisCurr.getCurrencyCode()); - result &= objCurr.getSymbol().equals(thisCurr.getSymbol()); - result &= objCurr.getDefaultFractionDigits() == thisCurr.getDefaultFractionDigits(); - } else { - result &= thisCurr == null; - } - result &= obj.getDecimalFormatSymbols().equals(this.getDecimalFormatSymbols()); + /** + * 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()); - return result; + 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) { @@ -132,7 +170,6 @@ public class DecimalFormat extends NumberFormat { return valBigDecimal.multiply(multiplierBigDecimal); } - @Override public StringBuffer format(Object value, StringBuffer buffer, FieldPosition field) { if (!(value instanceof Number)) { throw new IllegalArgumentException(); @@ -170,7 +207,6 @@ public class DecimalFormat extends NumberFormat { } } - @Override public StringBuffer format(long value, StringBuffer buffer, FieldPosition field) { if (buffer == null || field == null) { throw new NullPointerException(); @@ -180,7 +216,6 @@ public class DecimalFormat extends NumberFormat { return buffer; } - @Override public StringBuffer format(double value, StringBuffer buffer, FieldPosition field) { if (buffer == null || field == null) { throw new NullPointerException(); @@ -191,30 +226,13 @@ public class DecimalFormat extends NumberFormat { } public void applyLocalizedPattern(String pattern) { - if (pattern == null) { - throw new NullPointerException("pattern was null"); - } - try { - NativeDecimalFormat.applyPatternImpl(this.addr, false, pattern); - } catch(RuntimeException re) { - throw new IllegalArgumentException( - "applying localized pattern failed for pattern: " + pattern, re); - } + NativeDecimalFormat.applyPattern(this.addr, true, pattern); } public void applyPattern(String pattern) { - if (pattern == null) { - throw new NullPointerException("pattern was null"); - } - try { - NativeDecimalFormat.applyPatternImpl(this.addr, false, pattern); - } catch(RuntimeException re) { - throw new IllegalArgumentException( - "applying pattern failed for pattern: " + pattern, re); - } + NativeDecimalFormat.applyPattern(this.addr, false, pattern); } - @Override public AttributedCharacterIterator formatToCharacterIterator(Object object) { if (!(object instanceof Number)) { throw new IllegalArgumentException(); @@ -283,40 +301,38 @@ public class DecimalFormat extends NumberFormat { return NativeDecimalFormat.toPatternImpl(this.addr, false); } - @Override public Number parse(String string, ParsePosition position) { return NativeDecimalFormat.parse(addr, string, position); } // start getter and setter - @Override public int getMaximumFractionDigits() { - return NativeDecimalFormat.getAttribute(this .addr, + return NativeDecimalFormat.getAttribute(this.addr, UNumberFormatAttribute.UNUM_MAX_FRACTION_DIGITS.ordinal()); } - @Override public int getMaximumIntegerDigits() { - return NativeDecimalFormat.getAttribute(this .addr, + return NativeDecimalFormat.getAttribute(this.addr, UNumberFormatAttribute.UNUM_MAX_INTEGER_DIGITS.ordinal()); } - @Override public int getMinimumFractionDigits() { - return NativeDecimalFormat.getAttribute(this .addr, + return NativeDecimalFormat.getAttribute(this.addr, UNumberFormatAttribute.UNUM_MIN_FRACTION_DIGITS.ordinal()); } - @Override public int getMinimumIntegerDigits() { - return NativeDecimalFormat.getAttribute(this .addr, + return NativeDecimalFormat.getAttribute(this.addr, UNumberFormatAttribute.UNUM_MIN_INTEGER_DIGITS.ordinal()); } - @Override public Currency getCurrency() { - return this.symbols.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() { @@ -366,35 +382,25 @@ public class DecimalFormat extends NumberFormat { UNumberFormatAttribute.UNUM_DECIMAL_ALWAYS_SHOWN.ordinal()) != 0; } - @Override public boolean isParseIntegerOnly() { return NativeDecimalFormat.getAttribute(this.addr, UNumberFormatAttribute.UNUM_PARSE_INT_ONLY.ordinal()) != 0; } - @Override public boolean isGroupingUsed() { return NativeDecimalFormat.getAttribute(this.addr, UNumberFormatAttribute.UNUM_GROUPING_USED.ordinal()) != 0; } - public DecimalFormatSymbols getDecimalFormatSymbols() { - return this.symbols; - } - - public void setDecimalFormatSymbols(DecimalFormatSymbols icuSymbols) { - this.symbols = icuSymbols; - } - public void setDecimalSeparatorAlwaysShown(boolean value) { int i = value ? -1 : 0; NativeDecimalFormat.setAttribute(this.addr, UNumberFormatAttribute.UNUM_DECIMAL_ALWAYS_SHOWN.ordinal(), i); } - @Override public void setCurrency(Currency currency) { - this.symbols.setCurrency(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) { @@ -402,32 +408,27 @@ public class DecimalFormat extends NumberFormat { UNumberFormatAttribute.UNUM_GROUPING_SIZE.ordinal(), value); } - @Override public void setGroupingUsed(boolean value) { int i = value ? -1 : 0; NativeDecimalFormat.setAttribute(this.addr, UNumberFormatAttribute.UNUM_GROUPING_USED.ordinal(), i); } - @Override public void setMaximumFractionDigits(int value) { NativeDecimalFormat.setAttribute(this.addr, UNumberFormatAttribute.UNUM_MAX_FRACTION_DIGITS.ordinal(), value); } - @Override public void setMaximumIntegerDigits(int value) { NativeDecimalFormat.setAttribute(this.addr, UNumberFormatAttribute.UNUM_MAX_INTEGER_DIGITS.ordinal(), value); } - @Override public void setMinimumFractionDigits(int value) { NativeDecimalFormat.setAttribute(this.addr, UNumberFormatAttribute.UNUM_MIN_FRACTION_DIGITS.ordinal(), value); } - @Override public void setMinimumIntegerDigits(int value) { NativeDecimalFormat.setAttribute(this.addr, UNumberFormatAttribute.UNUM_MIN_INTEGER_DIGITS.ordinal(), value); @@ -476,7 +477,6 @@ public class DecimalFormat extends NumberFormat { } } - @Override public void setParseIntegerOnly(boolean value) { int i = value ? -1 : 0; NativeDecimalFormat.setAttribute(this.addr, diff --git a/libcore/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormatSymbols.java b/libcore/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormatSymbols.java deleted file mode 100644 index 2d5ae0270..000000000 --- a/libcore/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormatSymbols.java +++ /dev/null @@ -1,268 +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.util.LocaleData; - -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Currency; -import java.util.Locale; -import java.util.ResourceBundle; - -public class DecimalFormatSymbols implements Cloneable { - - // 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; - - private final int addr; - - // Used to implement clone. - private DecimalFormatSymbols(DecimalFormatSymbols other) { - this.addr = NativeDecimalFormat.cloneImpl(other.addr); - } - - public DecimalFormatSymbols(Locale locale) { - LocaleData localeData = com.ibm.icu4jni.util.Resources.getLocaleData(locale); - this.addr = NativeDecimalFormat.openDecimalFormatImpl(locale.toString(), - localeData.numberPattern); - NativeDecimalFormat.setSymbol(this.addr, UNUM_CURRENCY_SYMBOL, localeData.currencySymbol); - NativeDecimalFormat.setSymbol(this.addr, UNUM_INTL_CURRENCY_SYMBOL, - localeData.internationalCurrencySymbol); - } - - public DecimalFormatSymbols(Locale locale, java.text.DecimalFormatSymbols symbols) { - LocaleData localeData = com.ibm.icu4jni.util.Resources.getLocaleData(locale); - this.addr = NativeDecimalFormat.openDecimalFormatImpl(locale.toString(), - localeData.numberPattern); - copySymbols(symbols); - } - - /** - * Copies the java.text.DecimalFormatSymbols' settings into this object. - */ - public void copySymbols(final java.text.DecimalFormatSymbols dfs) { - setCurrencySymbol(dfs.getCurrencySymbol()); - setDecimalSeparator(dfs.getDecimalSeparator()); - setDigit(dfs.getDigit()); - setGroupingSeparator(dfs.getGroupingSeparator()); - setInfinity(dfs.getInfinity()); - setInternationalCurrencySymbol(dfs.getInternationalCurrencySymbol()); - setMinusSign(dfs.getMinusSign()); - setMonetaryDecimalSeparator(dfs.getMonetaryDecimalSeparator()); - setNaN(dfs.getNaN()); - setPatternSeparator(dfs.getPatternSeparator()); - setPercent(dfs.getPercent()); - setPerMill(dfs.getPerMill()); - setZeroDigit(dfs.getZeroDigit()); - } - - @Override - public boolean equals(Object object) { - if(object == null) { - return false; - } - if(!(object instanceof DecimalFormatSymbols)) { - return false; - } - - DecimalFormatSymbols sym = (DecimalFormatSymbols) object; - - if(sym.addr == this.addr) { - return true; - } - - boolean result = true; - - Currency objCurr = sym.getCurrency(); - Currency thisCurr = this.getCurrency(); - if(objCurr != null) { - result &= objCurr.getCurrencyCode().equals(thisCurr.getCurrencyCode()); - result &= objCurr.getSymbol().equals(thisCurr.getSymbol()); - result &= objCurr.getDefaultFractionDigits() == thisCurr.getDefaultFractionDigits(); - } else { - result &= thisCurr == null; - } - result &= sym.getCurrencySymbol().equals(this.getCurrencySymbol()); - result &= sym.getDecimalSeparator() == this.getDecimalSeparator(); - result &= sym.getDigit() == this.getDigit(); - result &= sym.getGroupingSeparator() == this.getGroupingSeparator(); - result &= sym.getInfinity().equals(this.getInfinity()); - result &= sym.getInternationalCurrencySymbol().equals( - this.getInternationalCurrencySymbol()); - result &= sym.getMinusSign() == this.getMinusSign(); - result &= sym.getMonetaryDecimalSeparator() == - this.getMonetaryDecimalSeparator(); - result &= sym.getNaN().equals(this.getNaN()); - result &= sym.getPatternSeparator() == this.getPatternSeparator(); - result &= sym.getPercent() == this.getPercent(); - result &= sym.getPerMill() == this.getPerMill(); - result &= sym.getZeroDigit() == this.getZeroDigit(); - - return result; - } - - @Override - public Object clone() { - return new DecimalFormatSymbols(this); - } - - 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 setCurrencySymbol(String symbol) { - NativeDecimalFormat.setSymbol(this.addr, UNUM_CURRENCY_SYMBOL, symbol); - } - - public void setDecimalSeparator(char symbol) { - NativeDecimalFormat.setSymbol(this.addr, UNUM_DECIMAL_SEPARATOR_SYMBOL, symbol); - } - - public void setDigit(char symbol) { - NativeDecimalFormat.setSymbol(this.addr, UNUM_DIGIT_SYMBOL, symbol); - } - - public void setGroupingSeparator(char symbol) { - NativeDecimalFormat.setSymbol(this.addr, UNUM_GROUPING_SEPARATOR_SYMBOL, symbol); - NativeDecimalFormat.setSymbol(this.addr, UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL, symbol); - } - - public void setInfinity(String symbol) { - NativeDecimalFormat.setSymbol(this.addr, UNUM_INFINITY_SYMBOL, symbol); - } - - public void setInternationalCurrencySymbol(String symbol) { - NativeDecimalFormat.setSymbol(this.addr, UNUM_INTL_CURRENCY_SYMBOL, symbol); - } - - public void setMinusSign(char symbol) { - NativeDecimalFormat.setSymbol(this.addr, UNUM_MINUS_SIGN_SYMBOL, symbol); - } - - public void setMonetaryDecimalSeparator(char symbol) { - NativeDecimalFormat.setSymbol(this.addr, UNUM_MONETARY_SEPARATOR_SYMBOL, symbol); - } - - public void setNaN(String symbol) { - NativeDecimalFormat.setSymbol(this.addr, UNUM_NAN_SYMBOL, symbol); - } - - public void setPatternSeparator(char symbol) { - NativeDecimalFormat.setSymbol(this.addr, UNUM_PATTERN_SEPARATOR_SYMBOL, symbol); - } - - public void setPercent(char symbol) { - NativeDecimalFormat.setSymbol(this.addr, UNUM_PERCENT_SYMBOL, symbol); - } - - public void setPerMill(char symbol) { - NativeDecimalFormat.setSymbol(this.addr, UNUM_PERMILL_SYMBOL, symbol); - } - - public void setZeroDigit(char symbol) { - NativeDecimalFormat.setSymbol(this.addr, UNUM_ZERO_DIGIT_SYMBOL, symbol); - } - - 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 String getCurrencySymbol() { - return NativeDecimalFormat.getSymbol(this.addr, UNUM_CURRENCY_SYMBOL); - } - - public char getDecimalSeparator() { - return NativeDecimalFormat.getSymbol(this.addr, UNUM_DECIMAL_SEPARATOR_SYMBOL).charAt(0); - } - - public char getDigit() { - return NativeDecimalFormat.getSymbol(this.addr, UNUM_DIGIT_SYMBOL).charAt(0); - } - - public char getGroupingSeparator() { - return NativeDecimalFormat.getSymbol(this.addr, UNUM_GROUPING_SEPARATOR_SYMBOL).charAt(0); - } - - public String getInfinity() { - return NativeDecimalFormat.getSymbol(this.addr, UNUM_INFINITY_SYMBOL); - } - - public String getInternationalCurrencySymbol() { - return NativeDecimalFormat.getSymbol(this.addr, UNUM_INTL_CURRENCY_SYMBOL); - } - - public char getMinusSign() { - return NativeDecimalFormat.getSymbol(this.addr, UNUM_MINUS_SIGN_SYMBOL).charAt(0); - } - - public char getMonetaryDecimalSeparator() { - return NativeDecimalFormat.getSymbol(this.addr, UNUM_MONETARY_SEPARATOR_SYMBOL).charAt(0); - } - - public String getNaN() { - return NativeDecimalFormat.getSymbol(this.addr, UNUM_NAN_SYMBOL); - } - - public char getPatternSeparator() { - return NativeDecimalFormat.getSymbol(this.addr, UNUM_PATTERN_SEPARATOR_SYMBOL).charAt(0); - } - - public char getPercent() { - return NativeDecimalFormat.getSymbol(this.addr, UNUM_PERCENT_SYMBOL).charAt(0); - } - - public char getPerMill() { - return NativeDecimalFormat.getSymbol(this.addr, UNUM_PERMILL_SYMBOL).charAt(0); - } - - public char getZeroDigit() { - return NativeDecimalFormat.getSymbol(this.addr, UNUM_ZERO_DIGIT_SYMBOL).charAt(0); - } - - int getAddr() { - return this.addr; - } - - protected void finalize() { - NativeDecimalFormat.closeDecimalFormatImpl(this.addr); - } -} 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 88e0d5f86..ec07fbdcd 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 @@ -55,15 +55,26 @@ final class NativeDecimalFormat { UNUM_PUBLIC_RULESETS } - static native int openDecimalFormatImpl(String locale, String pattern); + 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); + } 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 cloneImpl(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); @@ -74,7 +85,16 @@ final class NativeDecimalFormat { static native String getTextAttribute(int addr, int symbol); - static native void applyPatternImpl(int addr, boolean localized, String pattern); + static void applyPattern(int addr, boolean localized, String pattern) { + try { + NativeDecimalFormat.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); diff --git a/libcore/icu/src/main/native/NativeDecimalFormat.cpp b/libcore/icu/src/main/native/NativeDecimalFormat.cpp index cde200155..e974521ab 100644 --- a/libcore/icu/src/main/native/NativeDecimalFormat.cpp +++ b/libcore/icu/src/main/native/NativeDecimalFormat.cpp @@ -14,8 +14,10 @@ * limitations under the License. */ +#define LOG_TAG "NativeDecimalFormat" #include "JNIHelp.h" #include "AndroidSystemNatives.h" +#include "cutils/log.h" #include "unicode/unum.h" #include "unicode/numfmt.h" #include "unicode/decimfmt.h" @@ -26,14 +28,20 @@ #include #include -#define LOG_TAG "NativeDecimalFormat" -#include "cutils/log.h" +// FIXME: move to JNIHelp.h +static void jniThrowNullPointerException(JNIEnv* env) { + jniThrowException(env, "java/lang/NullPointerException", NULL); +} -static jint openDecimalFormatImpl(JNIEnv *env, jclass clazz, jstring locale, - jstring pattern) { +DecimalFormat* toDecimalFormat(jint addr) { + return reinterpret_cast(static_cast(addr)); +} - // the errorcode returned by unum_open - UErrorCode status = U_ZERO_ERROR; +static jint openDecimalFormatImpl(JNIEnv* env, jclass clazz, jstring locale, jstring pattern) { + if (pattern == NULL) { + jniThrowNullPointerException(env); + return 0; + } // prepare the pattern string for the call to unum_open const UChar *pattChars = env->GetStringChars(pattern, NULL); @@ -43,6 +51,7 @@ static jint openDecimalFormatImpl(JNIEnv *env, jclass clazz, jstring locale, const char *localeChars = env->GetStringUTFChars(locale, NULL); // open a default type number format + UErrorCode status = U_ZERO_ERROR; UNumberFormat *fmt = unum_open(UNUM_PATTERN_DECIMAL, pattChars, pattLen, localeChars, NULL, &status); @@ -50,13 +59,8 @@ static jint openDecimalFormatImpl(JNIEnv *env, jclass clazz, jstring locale, env->ReleaseStringChars(pattern, pattChars); env->ReleaseStringUTFChars(locale, localeChars); - // check for an error - if (icu4jni_error(env, status) != FALSE) { - return 0; - } - - // return the handle to the number format - return (long) fmt; + icu4jni_error(env, status); + return static_cast(reinterpret_cast(fmt)); } static void closeDecimalFormatImpl(JNIEnv *env, jclass clazz, jint addr) { @@ -71,7 +75,7 @@ static void closeDecimalFormatImpl(JNIEnv *env, jclass clazz, jint addr) { static void setSymbol(JNIEnv *env, uintptr_t addr, jint symbol, const UChar* chars, int32_t charCount) { UErrorCode status = U_ZERO_ERROR; - UNumberFormat* fmt = reinterpret_cast(addr); + UNumberFormat* fmt = reinterpret_cast(static_cast(addr)); unum_setSymbol(fmt, static_cast(symbol), chars, charCount, &status); icu4jni_error(env, status); @@ -199,59 +203,29 @@ static jstring getTextAttribute(JNIEnv *env, jclass clazz, jint addr, return res; } -static void applyPatternImpl(JNIEnv *env, jclass clazz, jint addr, - jboolean localized, jstring pattern) { - - // the errorcode returned by unum_applyPattern +static void applyPatternImpl(JNIEnv *env, jclass clazz, jint addr, jboolean localized, jstring pattern) { + if (pattern == NULL) { + jniThrowNullPointerException(env); + return; + } + UNumberFormat* fmt = reinterpret_cast(static_cast(addr)); + const UChar* chars = env->GetStringChars(pattern, NULL); + jsize charCount = env->GetStringLength(pattern); UErrorCode status = U_ZERO_ERROR; - - // get the pointer to the number format - UNumberFormat *fmt = (UNumberFormat *)(int)addr; - - const UChar *pattChars = env->GetStringChars(pattern, NULL); - int pattLen = env->GetStringLength(pattern); - - unum_applyPattern(fmt, localized, pattChars, pattLen, NULL, &status); - - env->ReleaseStringChars(pattern, pattChars); - + unum_applyPattern(fmt, localized, chars, charCount, NULL, &status); + env->ReleaseStringChars(pattern, chars); icu4jni_error(env, status); } -static jstring toPatternImpl(JNIEnv *env, jclass clazz, jint addr, - jboolean localized) { - - uint32_t resultlength, reslenneeded; - - // the errorcode returned by unum_toPattern - UErrorCode status = U_ZERO_ERROR; - - // get the pointer to the number format - UNumberFormat *fmt = (UNumberFormat *)(int)addr; - - UChar* result = NULL; - resultlength=0; - - // find out how long the result will be - reslenneeded=unum_toPattern(fmt, localized, result, resultlength, &status); - - result = NULL; - if(status==U_BUFFER_OVERFLOW_ERROR) { - status=U_ZERO_ERROR; - resultlength=reslenneeded+1; - result=(UChar*)malloc(sizeof(UChar) * resultlength); - reslenneeded=unum_toPattern(fmt, localized, result, resultlength, - &status); - } - if (icu4jni_error(env, status) != FALSE) { - return NULL; +static jstring toPatternImpl(JNIEnv *env, jclass, jint addr, jboolean localized) { + DecimalFormat* fmt = toDecimalFormat(addr); + UnicodeString pattern; + if (localized) { + fmt->toLocalizedPattern(pattern); + } else { + fmt->toPattern(pattern); } - - jstring res = env->NewString(result, reslenneeded); - - free(result); - - return res; + return env->NewString(pattern.getBuffer(), pattern.length()); } template @@ -274,7 +248,7 @@ static jstring format(JNIEnv *env, jint addr, jobject field, jstring fieldType, fp.setField(FieldPosition::DONT_CARE); UnicodeString str; - DecimalFormat* fmt = reinterpret_cast(static_cast(addr)); + DecimalFormat* fmt = toDecimalFormat(addr); fmt->format(val, str, fp, attrBufferPtr); if (attrBufferPtr && strlen(attrBuffer.buffer) > 0) { @@ -314,8 +288,7 @@ static jstring format(JNIEnv *env, jint addr, jobject field, jstring fieldType, } } - const UChar* chars = str.getBuffer(); - jstring result = env->NewString(chars, str.length()); + jstring result = env->NewString(str.getBuffer(), str.length()); delete[] attrBuffer.buffer; return result; } @@ -362,7 +335,7 @@ static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring val const char *digits = (isPositive ? valueChars : valueChars + 1); int length = strlen(digits); - DecimalFormat* fmt = reinterpret_cast(static_cast(addr)); + DecimalFormat* fmt = toDecimalFormat(addr); // The length of our digit list buffer must be the actual string length + 3, // because ICU will append some additional characters at the head and at the @@ -609,20 +582,9 @@ static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text, } } -static jint cloneImpl(JNIEnv *env, jclass clazz, jint addr) { - - UErrorCode status = U_ZERO_ERROR; - - UNumberFormat *fmt = (UNumberFormat *)(int)addr; - - UNumberFormat *result = unum_clone(fmt, &status); - - if(icu4jni_error(env, status) != FALSE) { - return 0; - } - - return (long) result; - +static jint cloneDecimalFormatImpl(JNIEnv *env, jclass, jint addr) { + DecimalFormat* fmt = toDecimalFormat(addr); + return static_cast(reinterpret_cast(fmt->clone())); } static JNINativeMethod gMethods[] = { @@ -651,7 +613,7 @@ static JNINativeMethod gMethods[] = { {"parse", "(ILjava/lang/String;Ljava/text/ParsePosition;)Ljava/lang/Number;", (void*) parse}, - {"cloneImpl", "(I)I", (void*) cloneImpl} + {"cloneDecimalFormatImpl", "(I)I", (void*) cloneDecimalFormatImpl} }; int register_com_ibm_icu4jni_text_NativeDecimalFormat(JNIEnv* env) { return jniRegisterNativeMethods(env, diff --git a/libcore/icu/src/main/native/Resources.cpp b/libcore/icu/src/main/native/Resources.cpp index 0df2859af..00bf7466b 100644 --- a/libcore/icu/src/main/native/Resources.cpp +++ b/libcore/icu/src/main/native/Resources.cpp @@ -14,8 +14,10 @@ * limitations under the License. */ +#define LOG_TAG "Resources" #include "JNIHelp.h" #include "AndroidSystemNatives.h" +#include "cutils/log.h" #include "unicode/numfmt.h" #include "unicode/locid.h" #include "unicode/ucal.h" @@ -37,9 +39,6 @@ #include #include -#define LOG_TAG "Resources" -#include "cutils/log.h" - static jclass string_class; class ScopedResourceBundle { diff --git a/libcore/luni/src/test/java/java/text/AllTests.java b/libcore/luni/src/test/java/java/text/AllTests.java index 9d13a1a75..4e96007b6 100644 --- a/libcore/luni/src/test/java/java/text/AllTests.java +++ b/libcore/luni/src/test/java/java/text/AllTests.java @@ -22,6 +22,7 @@ import junit.framework.TestSuite; public class AllTests { public static final Test suite() { TestSuite suite = tests.TestSuiteFactory.createTestSuite(); + suite.addTestSuite(java.text.DecimalFormatTest.class); suite.addTestSuite(java.text.NumberFormatTest.class); return suite; } diff --git a/libcore/luni/src/test/java/java/text/DecimalFormatTest.java b/libcore/luni/src/test/java/java/text/DecimalFormatTest.java new file mode 100644 index 000000000..6b2eb3d4f --- /dev/null +++ b/libcore/luni/src/test/java/java/text/DecimalFormatTest.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 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 java.text; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import java.math.BigInteger; +import java.util.Locale; + +public class DecimalFormatTest extends junit.framework.TestCase { + // Android fails this test, truncating to 127 digits. + public void test_setMaximumIntegerDigits() throws Exception { + NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US); + numberFormat.setGroupingUsed(false); + numberFormat.setMinimumIntegerDigits(400); + // The RI's documentation suggests that the int should be formatted to 309 characters -- + // a magic number they don't explain -- but the BigInteger should be formatted to the 400 + // characters we asked for. In practice, the RI uses 309 in both cases. + assertEquals(309, numberFormat.format(123).length()); + assertEquals(309, numberFormat.format(BigInteger.valueOf(123)).length()); + } +} diff --git a/libcore/text/src/main/java/java/text/DecimalFormat.java b/libcore/text/src/main/java/java/text/DecimalFormat.java index 3123e8c00..b8bc41b81 100644 --- a/libcore/text/src/main/java/java/text/DecimalFormat.java +++ b/libcore/text/src/main/java/java/text/DecimalFormat.java @@ -552,29 +552,17 @@ public class DecimalFormat extends NumberFormat { private transient com.ibm.icu4jni.text.DecimalFormat dform; - private transient com.ibm.icu4jni.text.DecimalFormatSymbols icuSymbols; - - private static final int CURRENT_SERIAL_VERTION = 3; - - private transient int serialVersionOnStream = 3; - /** * Constructs a new {@code DecimalFormat} for formatting and parsing numbers * for the default locale. */ public DecimalFormat() { + // BEGIN android-changed: reduce duplication. Locale locale = Locale.getDefault(); - // BEGIN android-changed - icuSymbols = new com.ibm.icu4jni.text.DecimalFormatSymbols(locale); - symbols = new DecimalFormatSymbols(locale); - LocaleData localeData = com.ibm.icu4jni.util.Resources.getLocaleData(Locale.getDefault()); - dform = new com.ibm.icu4jni.text.DecimalFormat(localeData.numberPattern, icuSymbols); + this.symbols = new DecimalFormatSymbols(locale); + LocaleData localeData = com.ibm.icu4jni.util.Resources.getLocaleData(locale); + initNative(localeData.numberPattern, locale); // END android-changed - - super.setMaximumFractionDigits(dform.getMaximumFractionDigits()); - super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits()); - super.setMinimumFractionDigits(dform.getMinimumFractionDigits()); - super.setMinimumIntegerDigits(dform.getMinimumIntegerDigits()); } /** @@ -587,17 +575,9 @@ public class DecimalFormat extends NumberFormat { * if the pattern cannot be parsed. */ public DecimalFormat(String pattern) { - Locale locale = Locale.getDefault(); - // BEGIN android-changed - icuSymbols = new com.ibm.icu4jni.text.DecimalFormatSymbols(locale); + // BEGIN android-changed: reduce duplication. + this(pattern, Locale.getDefault()); // END android-changed - symbols = new DecimalFormatSymbols(locale); - dform = new com.ibm.icu4jni.text.DecimalFormat(pattern, icuSymbols); - - super.setMaximumFractionDigits(dform.getMaximumFractionDigits()); - super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits()); - super.setMinimumFractionDigits(dform.getMinimumFractionDigits()); - super.setMinimumIntegerDigits(dform.getMinimumIntegerDigits()); } /** @@ -612,25 +592,26 @@ public class DecimalFormat extends NumberFormat { * if the pattern cannot be parsed. */ public DecimalFormat(String pattern, DecimalFormatSymbols value) { - symbols = (DecimalFormatSymbols) value.clone(); - Locale locale = symbols.getLocale(); - // BEGIN android-changed - icuSymbols = new com.ibm.icu4jni.text.DecimalFormatSymbols(locale, symbols); + // BEGIN android-changed: reduce duplication. + this.symbols = (DecimalFormatSymbols) value.clone(); + initNative(pattern, symbols.getLocale()); // END android-changed - dform = new com.ibm.icu4jni.text.DecimalFormat(pattern, icuSymbols); - - super.setMaximumFractionDigits(dform.getMaximumFractionDigits()); - super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits()); - super.setMinimumFractionDigits(dform.getMinimumFractionDigits()); - super.setMinimumIntegerDigits(dform.getMinimumIntegerDigits()); } // BEGIN android-added: used by NumberFormat.getInstance because cloning DecimalFormatSymbols is slow. DecimalFormat(String pattern, Locale locale) { this.symbols = new DecimalFormatSymbols(locale); - this.icuSymbols = new com.ibm.icu4jni.text.DecimalFormatSymbols(locale, symbols); - this.dform = new com.ibm.icu4jni.text.DecimalFormat(pattern, icuSymbols); + initNative(pattern, locale); + } + // END android-added + // BEGIN android-changed: reduce duplication. + private void initNative(String pattern, Locale locale) { + try { + this.dform = new com.ibm.icu4jni.text.DecimalFormat(pattern, locale, symbols); + } catch (IllegalArgumentException ex) { + throw new IllegalArgumentException(pattern); + } super.setMaximumFractionDigits(dform.getMaximumFractionDigits()); super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits()); super.setMinimumFractionDigits(dform.getMinimumFractionDigits()); @@ -638,19 +619,6 @@ public class DecimalFormat extends NumberFormat { } // END android-added - // BEGIN android-removed - // DecimalFormat(String pattern, DecimalFormatSymbols value, com.ibm.icu4jni.text.DecimalFormat icuFormat) { - // symbols = value; - // icuSymbols = value.getIcuSymbols(); - // dform = icuFormat; - // - // super.setMaximumFractionDigits(dform.getMaximumFractionDigits()); - // super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits()); - // super.setMinimumFractionDigits(dform.getMinimumFractionDigits()); - // super.setMinimumIntegerDigits(dform.getMinimumIntegerDigits()); - // } - // END android-removed - /** * Changes the pattern of this decimal format to the specified pattern which * uses localized pattern characters. @@ -674,7 +642,6 @@ public class DecimalFormat extends NumberFormat { * if the pattern cannot be parsed. */ public void applyPattern(String pattern) { - dform.applyPattern(pattern); } @@ -712,9 +679,9 @@ public class DecimalFormat extends NumberFormat { if (!(object instanceof DecimalFormat)) { return false; } - DecimalFormat format = (DecimalFormat) object; - return (this.dform == null ? format.dform == null : this.dform - .equals(format.dform)); + DecimalFormat other = (DecimalFormat) object; + return (this.dform == null ? other.dform == null : this.dform.equals(other.dform)) && + getDecimalFormatSymbols().equals(other.getDecimalFormatSymbols()); } /** @@ -1058,10 +1025,10 @@ public class DecimalFormat extends NumberFormat { */ public void setDecimalFormatSymbols(DecimalFormatSymbols value) { if (value != null) { - symbols = (DecimalFormatSymbols) value.clone(); - icuSymbols = dform.getDecimalFormatSymbols(); - icuSymbols.copySymbols(symbols); // android-changed - dform.setDecimalFormatSymbols(icuSymbols); + // BEGIN android-changed: the Java object is canonical, and we copy down to native code. + this.symbols = (DecimalFormatSymbols) value.clone(); + dform.setDecimalFormatSymbols(this.symbols); + // END android-changed } } @@ -1076,8 +1043,7 @@ public class DecimalFormat extends NumberFormat { @Override public void setCurrency(Currency currency) { // BEGIN android-changed - dform.setCurrency(Currency.getInstance(currency - .getCurrencyCode())); + dform.setCurrency(Currency.getInstance(currency.getCurrencyCode())); // END android-changed symbols.setCurrency(currency); } @@ -1129,67 +1095,55 @@ public class DecimalFormat extends NumberFormat { } /** - * Sets the maximum number of fraction digits that are printed when - * formatting numbers other than {@code BigDecimal} and {@code BigInteger}. - * If the maximum is less than the number of fraction digits, the least - * significant digits are truncated. If the value passed is bigger than 340 - * then it is replaced by 340. If the value passed is negative then it is - * replaced by 0. + * Sets the maximum number of digits after the decimal point. + * If the value passed is negative then it is replaced by 0. + * Regardless of this setting, no more than 340 digits will be used. * - * @param value - * the maximum number of fraction digits. + * @param value the maximum number of fraction digits. */ @Override public void setMaximumFractionDigits(int value) { super.setMaximumFractionDigits(value); - dform.setMaximumFractionDigits(value); + dform.setMaximumFractionDigits(getMaximumFractionDigits()); } /** - * Sets the maximum number of integer digits that are printed when - * formatting numbers other than {@code BigDecimal} and {@code BigInteger}. - * If the maximum is less than the number of integer digits, the most - * significant digits are truncated. If the value passed is bigger than 309 - * then it is replaced by 309. If the value passed is negative then it is - * replaced by 0. + * Sets the maximum number of digits before the decimal point. + * If the value passed is negative then it is replaced by 0. + * Regardless of this setting, no more than 309 digits will be used. * - * @param value - * the maximum number of integer digits. + * @param value the maximum number of integer digits. */ @Override public void setMaximumIntegerDigits(int value) { super.setMaximumIntegerDigits(value); - dform.setMaximumIntegerDigits(value); + dform.setMaximumIntegerDigits(getMaximumIntegerDigits()); } /** - * Sets the minimum number of fraction digits that are printed when - * formatting numbers other than {@code BigDecimal} and {@code BigInteger}. - * If the value passed is bigger than 340 then it is replaced by 340. If the - * value passed is negative then it is replaced by 0. + * Sets the minimum number of digits after the decimal point. + * If the value passed is negative then it is replaced by 0. + * Regardless of this setting, no more than 340 digits will be used. * - * @param value - * the minimum number of fraction digits. + * @param value the minimum number of fraction digits. */ @Override public void setMinimumFractionDigits(int value) { super.setMinimumFractionDigits(value); - dform.setMinimumFractionDigits(value); + dform.setMinimumFractionDigits(getMinimumFractionDigits()); } /** - * Sets the minimum number of integer digits that are printed when - * formatting numbers other than {@code BigDecimal} and {@code BigInteger}. - * If the value passed is bigger than 309 then it is replaced by 309. If the - * value passed is negative then it is replaced by 0. + * Sets the minimum number of digits before the decimal point. + * If the value passed is negative then it is replaced by 0. + * Regardless of this setting, no more than 309 digits will be used. * - * @param value - * the minimum number of integer digits. + * @param value the minimum number of integer digits. */ @Override public void setMinimumIntegerDigits(int value) { super.setMinimumIntegerDigits(value); - dform.setMinimumIntegerDigits(value); + dform.setMinimumIntegerDigits(getMinimumIntegerDigits()); } /** @@ -1340,17 +1294,13 @@ public class DecimalFormat extends NumberFormat { .isDecimalSeparatorAlwaysShown()); fields.put("parseBigDecimal", parseBigDecimal); fields.put("symbols", symbols); - boolean useExponentialNotation = ((Boolean) Format.getInternalField( - "useExponentialNotation", dform)).booleanValue(); - fields.put("useExponentialNotation", useExponentialNotation); - byte minExponentDigits = ((Byte) Format.getInternalField( - "minExponentDigits", dform)).byteValue(); - fields.put("minExponentDigits", minExponentDigits); + fields.put("useExponentialNotation", false); + fields.put("minExponentDigits", 0); fields.put("maximumIntegerDigits", dform.getMaximumIntegerDigits()); fields.put("minimumIntegerDigits", dform.getMinimumIntegerDigits()); fields.put("maximumFractionDigits", dform.getMaximumFractionDigits()); fields.put("minimumFractionDigits", dform.getMinimumFractionDigits()); - fields.put("serialVersionOnStream", CURRENT_SERIAL_VERTION); + fields.put("serialVersionOnStream", 3); stream.writeFields(); } @@ -1390,26 +1340,17 @@ public class DecimalFormat extends NumberFormat { boolean parseBigDecimal = fields.get("parseBigDecimal", false); symbols = (DecimalFormatSymbols) fields.get("symbols", null); - boolean useExponentialNotation = fields.get("useExponentialNotation", - false); - byte minExponentDigits = fields.get("minExponentDigits", (byte) 0); - int maximumIntegerDigits = fields.get("maximumIntegerDigits", 309); int minimumIntegerDigits = fields.get("minimumIntegerDigits", 309); int maximumFractionDigits = fields.get("maximumFractionDigits", 340); int minimumFractionDigits = fields.get("minimumFractionDigits", 340); - this.serialVersionOnStream = fields.get("serialVersionOnStream", 0); + int serialVersionOnStream = fields.get("serialVersionOnStream", 0); Locale locale = (Locale) Format.getInternalField("locale", symbols); // BEGIN android-changed - icuSymbols = new com.ibm.icu4jni.text.DecimalFormatSymbols(locale, symbols); - dform = new com.ibm.icu4jni.text.DecimalFormat("", //$NON-NLS-1$ - icuSymbols); + //this.dform = new com.ibm.icu4jni.text.DecimalFormat("", locale, symbols); + initNative("", locale); // END android-changed - setInternalField("useExponentialNotation", dform, Boolean - .valueOf(useExponentialNotation)); - setInternalField("minExponentDigits", dform, - new Byte(minExponentDigits)); dform.setPositivePrefix(positivePrefix); dform.setPositiveSuffix(positiveSuffix); dform.setNegativePrefix(negativePrefix); @@ -1424,11 +1365,18 @@ public class DecimalFormat extends NumberFormat { dform.setGroupingUsed(groupingUsed); // END android-added dform.setDecimalSeparatorAlwaysShown(decimalSeparatorAlwaysShown); - dform.setMinimumIntegerDigits(minimumIntegerDigits); + setMinimumIntegerDigits(minimumIntegerDigits); + // BEGIN android-changed: tell ICU what we want, then ask it what we can have, and then + // set that in our Java object. This isn't RI-compatible, but then very little of our + // 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); - dform.setMinimumFractionDigits(minimumFractionDigits); - dform.setMaximumFractionDigits(maximumFractionDigits); - this.setParseBigDecimal(parseBigDecimal); + super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits()); + // END android-changed + setMinimumFractionDigits(minimumFractionDigits); + setMaximumFractionDigits(maximumFractionDigits); + setParseBigDecimal(parseBigDecimal); if (serialVersionOnStream < 3) { setMaximumIntegerDigits(super.getMaximumIntegerDigits()); @@ -1436,11 +1384,6 @@ public class DecimalFormat extends NumberFormat { setMaximumFractionDigits(super.getMaximumFractionDigits()); setMinimumFractionDigits(super.getMinimumFractionDigits()); } - if (serialVersionOnStream < 1) { - this.setInternalField("useExponentialNotation", dform, - Boolean.FALSE); - } - serialVersionOnStream = 3; } /* diff --git a/libcore/text/src/main/java/java/text/DecimalFormatSymbols.java b/libcore/text/src/main/java/java/text/DecimalFormatSymbols.java index 1a35d0a45..6d1dcde1f 100644 --- a/libcore/text/src/main/java/java/text/DecimalFormatSymbols.java +++ b/libcore/text/src/main/java/java/text/DecimalFormatSymbols.java @@ -15,10 +15,6 @@ * limitations under the License. */ -// BEGIN android-note -// changed from ICU to resource bundles -// END android-note - package java.text; import java.io.IOException; @@ -29,9 +25,6 @@ import java.io.Serializable; import java.util.Arrays; import java.util.Currency; import java.util.Locale; -// BEGIN android-added -import java.util.ResourceBundle; -// END android-added import com.ibm.icu4jni.util.LocaleData; @@ -557,10 +550,4 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { Locale getLocale(){ return locale; } - - // BEGIN android-removed - // com.ibm.icu4jni.text.DecimalFormatSymbols getIcuSymbols() { - // return icuSymbols; - // } - // END android-removed } diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatTest.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatTest.java index ef70bf2e3..ec8c9a2a1 100644 --- a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatTest.java +++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatTest.java @@ -1270,7 +1270,7 @@ public class DecimalFormatTest extends TestCase { try { new DecimalFormat(null); - fail("NullPointerException was thrown."); + fail("NullPointerException wasn't thrown."); } catch(NullPointerException npe){ //expected } @@ -1279,7 +1279,7 @@ public class DecimalFormatTest extends TestCase { for(String str:incPatterns) { try { new DecimalFormat(str); - fail("NullPointerException was thrown for pattern: " + str); + fail("IllegalArgumentException wasn't thrown for pattern: " + str); } catch(IllegalArgumentException iae){ //expected } -- 2.11.0