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 );