/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; import java.util.Currency; import java.util.Locale; import libcore.icu.LocaleData; import libcore.icu.NativeDecimalFormat; /** * A concrete subclass of {@link NumberFormat} that formats decimal numbers. It * has a variety of features designed to make it possible to parse and format * numbers in any locale, including support for Western, Arabic, or Indic * digits. It also supports different flavors of numbers, including integers * ("123"), fixed-point numbers ("123.4"), scientific notation ("1.23E4"), * percentages ("12%"), and currency amounts ("$123"). All of these flavors can * be easily localized. *
* This is an enhanced version of {@code DecimalFormat} that is based on * the standard version in the RI. New or changed functionality is labeled * NEW. *
* To obtain a {@link NumberFormat} for a specific locale (including the default * locale), call one of {@code NumberFormat}'s factory methods such as * {@code NumberFormat.getInstance}. Do not call the {@code DecimalFormat} * constructors directly, unless you know what you are doing, since the * {@link NumberFormat} factory methods may return subclasses other than * {@code DecimalFormat}. If you need to customize the format object, do * something like this:
* ** ** NumberFormat f = NumberFormat.getInstance(loc); * if (f instanceof DecimalFormat) { * ((DecimalFormat)f).setDecimalSeparatorAlwaysShown(true); * } ** *
* A {@code DecimalFormat} consists of a pattern and a set of * symbols. The pattern may be set directly using * {@link #applyPattern(String)}, or indirectly using other API methods which * manipulate aspects of the pattern, such as the minimum number of integer * digits. The symbols are stored in a {@link DecimalFormatSymbols} object. When * using the {@link NumberFormat} factory methods, the pattern and symbols are * read from ICU's locale data. *
* Many characters in a pattern are taken literally; they are matched during * parsing and are written out unchanged during formatting. On the other hand, * special characters stand for other characters, strings, or classes of * characters. For example, the '#' character is replaced by a localized digit. * Often the replacement character is the same as the pattern character; in the * U.S. locale, the ',' grouping character is replaced by ','. However, the * replacement is still happening, and if the symbols are modified, the grouping * character changes. Some special characters affect the behavior of the * formatter by their presence; for example, if the percent character is seen, * then the value is multiplied by 100 before being displayed. *
* To insert a special character in a pattern as a literal, that is, without any * special meaning, the character must be quoted. There are some exceptions to * this which are noted below. *
* The characters listed here are used in non-localized patterns. Localized * patterns use the corresponding characters taken from this formatter's * {@link DecimalFormatSymbols} object instead, and these characters lose their * special status. Two exceptions are the currency sign and quote, which are not * localized. *
**
* *Symbol *Location *Localized? *Meaning ** *{@code 0} *Number *Yes *Digit. ** *{@code @} *Number *No *NEW Significant * digit. ** *{@code #} *Number *Yes *Digit, leading zeroes are not shown. ** *{@code .} *Number *Yes *Decimal separator or monetary decimal separator. ** *{@code -} *Number *Yes *Minus sign. ** *{@code ,} *Number *Yes *Grouping separator. ** *{@code E} *Number *Yes *Separates mantissa and exponent in scientific notation. * Does not need to be quoted in prefix or suffix. ** *{@code +} *Exponent *Yes *NEW Prefix * positive exponents with localized plus sign. * Does not need to be quoted in prefix or suffix. ** *{@code ;} *Subpattern boundary *Yes *Separates positive and negative subpatterns. ** *{@code %} *Prefix or suffix *Yes *Multiply by 100 and show as percentage. ** *{@code \u2030} ({@code \u005Cu2030}) *Prefix or suffix *Yes *Multiply by 1000 and show as per mille. ** *{@code \u00A4} ({@code \u005Cu00A4}) *Prefix or suffix *No *Currency sign, replaced by currency symbol. If doubled, replaced by * international currency symbol. If present in a pattern, the monetary decimal * separator is used instead of the decimal separator. ** *{@code '} *Prefix or suffix *No *Used to quote special characters in a prefix or suffix, for example, * {@code "'#'#"} formats 123 to {@code "#123"}. To create a single quote * itself, use two in a row: {@code "# o''clock"}. ** *{@code *} *Prefix or suffix boundary *Yes *NEW Pad escape, * precedes pad character. *
* A {@code DecimalFormat} pattern contains a positive and negative subpattern, * for example, "#,##0.00;(#,##0.00)". Each subpattern has a prefix, a numeric * part and a suffix. If there is no explicit negative subpattern, the negative * subpattern is the localized minus sign prefixed to the positive subpattern. * That is, "0.00" alone is equivalent to "0.00;-0.00". If there is an explicit * negative subpattern, it serves only to specify the negative prefix and * suffix; the number of digits, minimal digits, and other characteristics are * ignored in the negative subpattern. This means that "#,##0.0#;(#)" produces * precisely the same result as "#,##0.0#;(#,##0.0#)". *
* The prefixes, suffixes, and various symbols used for infinity, digits, * thousands separators, decimal separators, etc. may be set to arbitrary * values, and they will appear properly during formatting. However, care must * be taken that the symbols and strings do not conflict, or parsing will be * unreliable. For example, either the positive and negative prefixes or the * suffixes must be distinct for {@link #parse} to be able to distinguish * positive from negative values. Another example is that the decimal separator * and thousands separator should be distinct characters, or parsing will be * impossible. *
* The grouping separator is a character that separates clusters of * integer digits to make large numbers more legible. It is commonly used for * thousands, but in some locales it separates ten-thousands. The grouping * size * is the number of digits between the grouping separators, such as 3 for * "100,000,000" or 4 for "1 0000 0000". There are actually two different * grouping sizes: One used for the least significant integer digits, the * primary grouping size, and one used for all others, the * secondary grouping size. In most locales these are the same, but * sometimes they are different. For example, if the primary grouping interval * is 3, and the secondary is 2, then this corresponds to the pattern * "#,##,##0", and the number 123456789 is formatted as "12,34,56,789". If a * pattern contains multiple grouping separators, the interval between the last * one and the end of the integer defines the primary grouping size, and the * interval between the last two defines the secondary grouping size. All others * are ignored, so "#,##,###,####", "###,###,####" and "##,#,###,####" produce * the same result. *
* Illegal patterns, such as "#.#.#" or "#.###,###", will cause * {@code DecimalFormat} to throw an {@link IllegalArgumentException} with a * message that describes the problem. *
* pattern := subpattern (';' subpattern)? * subpattern := prefix? number exponent? suffix? * number := (integer ('.' fraction)?) | sigDigits * prefix := '\\u0000'..'\\uFFFD' - specialCharacters * suffix := '\\u0000'..'\\uFFFD' - specialCharacters * integer := '#'* '0'* '0' * fraction := '0'* '#'* * sigDigits := '#'* '@' '@'* '#'* * exponent := 'E' '+'? '0'* '0' * padSpec := '*' padChar * padChar := '\\u0000'..'\\uFFFD' - quote * * Notation: * X* 0 or more instances of X * X? 0 or 1 instances of X * X|Y either X or Y * C..D any character from C up to D, inclusive * S-T characters in S, except those in T ** * The first subpattern is for positive numbers. The second (optional) * subpattern is for negative numbers. *
* Not indicated in the BNF syntax above: *
* {@code DecimalFormat} parses all Unicode characters that represent decimal * digits, as defined by {@link Character#digit(int, int)}. In addition, * {@code DecimalFormat} also recognizes as digits the ten consecutive * characters starting with the localized zero digit defined in the * {@link DecimalFormatSymbols} object. During formatting, the * {@link DecimalFormatSymbols}-based digits are written out. *
* During parsing, grouping separators are ignored. *
* If {@link #parse(String, ParsePosition)} fails to parse a string, it returns * {@code null} and leaves the parse position unchanged. *
* Formatting is guided by several parameters, all of which can be specified * either using a pattern or using the API. The following description applies to * formats that do not use scientific notation or significant digits. *
* Special Values *
* {@code NaN} is represented as a single character, typically * {@code \u005cuFFFD}. This character is determined by the * {@link DecimalFormatSymbols} object. This is the only value for which the * prefixes and suffixes are not used. *
* Infinity is represented as a single character, typically {@code \u005cu221E},
* with the positive or negative prefixes and suffixes applied. The infinity
* character is determined by the {@link DecimalFormatSymbols} object.
* Scientific Notation
*
*
* Numbers in scientific notation are expressed as the product of a mantissa and * a power of ten, for example, 1234 can be expressed as 1.234 x 103. * The mantissa is typically in the half-open interval [1.0, 10.0) or sometimes * [0.0, 1.0), but it does not need to be. {@code DecimalFormat} supports * arbitrary mantissas. {@code DecimalFormat} can be instructed to use * scientific notation through the API or through the pattern. In a pattern, the * exponent character immediately followed by one or more digit characters * indicates scientific notation. Example: "0.###E0" formats the number 1234 as * "1.234E3". *
*
{@code DecimalFormat} has two ways of controlling how many digits are * shown: (a) significant digit counts or (b) integer and fraction digit counts. * Integer and fraction digit counts are described above. When a formatter uses * significant digits counts, the number of integer and fraction digits is not * specified directly, and the formatter settings for these counts are ignored. * Instead, the formatter uses as many integer and fraction digits as required * to display the specified number of significant digits. ***
* *Pattern *Minimum significant digits *Maximum significant digits *Number *Output of format() ** *{@code @@@} * 3 *3 *12345 *{@code 12300} ** *{@code @@@} *3 *3 *0.12345 *{@code 0.123} ** *{@code @@##} *2 *4 *3.14159 *{@code 3.142} ** *{@code @@##} *2 *4 *1.23004 *{@code 1.23} *
* {@code DecimalFormat} supports padding the result of {@code format} to a * specific width. Padding may be specified either through the API or through * the pattern syntax. In a pattern, the pad escape character followed by a * single pad character causes padding to be parsed and formatted. The pad * escape character is '*' in unlocalized patterns. For example, * {@code "$*x#,##0.00"} formats 123 to {@code "$xx123.00"}, and 1234 to * {@code "$1,234.00"}. *
* {@code DecimalFormat} objects are not synchronized. Multiple threads should * not access one formatter concurrently. * * @see Format * @see NumberFormat */ public class DecimalFormat extends NumberFormat { private static final long serialVersionUID = 864413376551465018L; private transient DecimalFormatSymbols symbols; private transient NativeDecimalFormat dform; private final Object finalizerGuardian = new Object() { @Override protected void finalize() throws Throwable { try { dform.close(); } finally { super.finalize(); } } }; private transient RoundingMode roundingMode = RoundingMode.HALF_EVEN; /** * Constructs a new {@code DecimalFormat} for formatting and parsing numbers * for the user's default locale. * See "Be wary of the default locale". */ public DecimalFormat() { Locale locale = Locale.getDefault(); this.symbols = new DecimalFormatSymbols(locale); initNative(LocaleData.get(locale).numberPattern); } /** * Constructs a new {@code DecimalFormat} using the specified non-localized * pattern and the {@code DecimalFormatSymbols} for the user's default Locale. * See "Be wary of the default locale". * @param pattern * the non-localized pattern. * @throws IllegalArgumentException * if the pattern cannot be parsed. */ public DecimalFormat(String pattern) { this(pattern, Locale.getDefault()); } /** * Constructs a new {@code DecimalFormat} using the specified non-localized * pattern and {@code DecimalFormatSymbols}. * * @param pattern * the non-localized pattern. * @param value * the DecimalFormatSymbols. * @throws IllegalArgumentException * if the pattern cannot be parsed. */ public DecimalFormat(String pattern, DecimalFormatSymbols value) { this.symbols = (DecimalFormatSymbols) value.clone(); initNative(pattern); } // Used by NumberFormat.getInstance because cloning DecimalFormatSymbols is slow. DecimalFormat(String pattern, Locale locale) { this.symbols = new DecimalFormatSymbols(locale); initNative(pattern); } private void initNative(String pattern) { try { this.dform = new NativeDecimalFormat(pattern, symbols); } catch (IllegalArgumentException ex) { throw new IllegalArgumentException(pattern); } super.setMaximumFractionDigits(dform.getMaximumFractionDigits()); super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits()); super.setMinimumFractionDigits(dform.getMinimumFractionDigits()); super.setMinimumIntegerDigits(dform.getMinimumIntegerDigits()); } /** * Changes the pattern of this decimal format to the specified pattern which * uses localized pattern characters. * * @param pattern * the localized pattern. * @throws IllegalArgumentException * if the pattern cannot be parsed. */ public void applyLocalizedPattern(String pattern) { dform.applyLocalizedPattern(pattern); } /** * Changes the pattern of this decimal format to the specified pattern which * uses non-localized pattern characters. * * @param pattern * the non-localized pattern. * @throws IllegalArgumentException * if the pattern cannot be parsed. */ public void applyPattern(String pattern) { dform.applyPattern(pattern); } /** * Returns a new instance of {@code DecimalFormat} with the same pattern and * properties as this decimal format. * * @return a shallow copy of this decimal format. * @see java.lang.Cloneable */ @Override public Object clone() { DecimalFormat clone = (DecimalFormat) super.clone(); clone.dform = (NativeDecimalFormat) dform.clone(); clone.symbols = (DecimalFormatSymbols) symbols.clone(); return clone; } /** * Compares the specified object to this decimal format and indicates if * they are equal. In order to be equal, {@code object} must be an instance * of {@code DecimalFormat} with the same pattern and properties. * * @param object * the object to compare with this object. * @return {@code true} if the specified object is equal to this decimal * format; {@code false} otherwise. * @see #hashCode */ @Override public boolean equals(Object object) { if (this == object) { return true; } if (!(object instanceof DecimalFormat)) { return false; } DecimalFormat other = (DecimalFormat) object; return (this.dform == null ? other.dform == null : this.dform.equals(other.dform)) && getDecimalFormatSymbols().equals(other.getDecimalFormatSymbols()); } /** * Formats the specified object using the rules of this decimal format and * returns an {@code AttributedCharacterIterator} with the formatted number * and attributes. * * @param object * the object to format. * @return an AttributedCharacterIterator with the formatted number and * attributes. * @throws IllegalArgumentException * if {@code object} cannot be formatted by this format. * @throws NullPointerException * if {@code object} is {@code null}. */ @Override public AttributedCharacterIterator formatToCharacterIterator(Object object) { if (object == null) { throw new NullPointerException(); } return dform.formatToCharacterIterator(object); } private void checkBufferAndFieldPosition(StringBuffer buffer, FieldPosition position) { if (buffer == null) { throw new NullPointerException("buffer == null"); } if (position == null) { throw new NullPointerException("position == null"); } } @Override public StringBuffer format(double value, StringBuffer buffer, FieldPosition position) { checkBufferAndFieldPosition(buffer, position); // All float/double/Float/Double formatting ends up here... if (roundingMode == RoundingMode.UNNECESSARY) { // ICU4C doesn't support this rounding mode, so we have to fake it. try { setRoundingMode(RoundingMode.UP); String upResult = format(value, new StringBuffer(), new FieldPosition(0)).toString(); setRoundingMode(RoundingMode.DOWN); String downResult = format(value, new StringBuffer(), new FieldPosition(0)).toString(); if (!upResult.equals(downResult)) { throw new ArithmeticException("rounding mode UNNECESSARY but rounding required"); } } finally { setRoundingMode(RoundingMode.UNNECESSARY); } } buffer.append(dform.formatDouble(value, position)); return buffer; } @Override public StringBuffer format(long value, StringBuffer buffer, FieldPosition position) { checkBufferAndFieldPosition(buffer, position); buffer.append(dform.formatLong(value, position)); return buffer; } @Override public final StringBuffer format(Object number, StringBuffer buffer, FieldPosition position) { checkBufferAndFieldPosition(buffer, position); if (number instanceof BigInteger) { BigInteger bigInteger = (BigInteger) number; char[] chars = (bigInteger.bitLength() < 64) ? dform.formatLong(bigInteger.longValue(), position) : dform.formatBigInteger(bigInteger, position); buffer.append(chars); return buffer; } else if (number instanceof BigDecimal) { buffer.append(dform.formatBigDecimal((BigDecimal) number, position)); return buffer; } return super.format(number, buffer, position); } /** * Returns the {@code DecimalFormatSymbols} used by this decimal format. * * @return a copy of the {@code DecimalFormatSymbols} used by this decimal * format. */ public DecimalFormatSymbols getDecimalFormatSymbols() { return (DecimalFormatSymbols) symbols.clone(); } /** * Returns the currency used by this decimal format. * * @return the currency used by this decimal format. * @see DecimalFormatSymbols#getCurrency() */ @Override public Currency getCurrency() { return symbols.getCurrency(); } /** * Returns the number of digits grouped together by the grouping separator. * This only allows to get the primary grouping size. There is no API to get * the secondary grouping size. * * @return the number of digits grouped together. */ public int getGroupingSize() { return dform.getGroupingSize(); } /** * Returns the multiplier which is applied to the number before formatting * or after parsing. * * @return the multiplier. */ public int getMultiplier() { return dform.getMultiplier(); } /** * Returns the prefix which is formatted or parsed before a negative number. * * @return the negative prefix. */ public String getNegativePrefix() { return dform.getNegativePrefix(); } /** * Returns the suffix which is formatted or parsed after a negative number. * * @return the negative suffix. */ public String getNegativeSuffix() { return dform.getNegativeSuffix(); } /** * Returns the prefix which is formatted or parsed before a positive number. * * @return the positive prefix. */ public String getPositivePrefix() { return dform.getPositivePrefix(); } /** * Returns the suffix which is formatted or parsed after a positive number. * * @return the positive suffix. */ public String getPositiveSuffix() { return dform.getPositiveSuffix(); } @Override public int hashCode() { return dform.hashCode(); } /** * Indicates whether the decimal separator is shown when there are no * fractional digits. * * @return {@code true} if the decimal separator should always be formatted; * {@code false} otherwise. */ public boolean isDecimalSeparatorAlwaysShown() { return dform.isDecimalSeparatorAlwaysShown(); } /** * This value indicates whether the return object of the parse operation is * of type {@code BigDecimal}. This value defaults to {@code false}. * * @return {@code true} if parse always returns {@code BigDecimals}, * {@code false} if the type of the result is {@code Long} or * {@code Double}. */ public boolean isParseBigDecimal() { return dform.isParseBigDecimal(); } /** * Sets the flag that indicates whether numbers will be parsed as integers. * When this decimal format is used for parsing and this value is set to * {@code true}, then the resulting numbers will be of type * {@code java.lang.Integer}. Special cases are NaN, positive and negative * infinity, which are still returned as {@code java.lang.Double}. * * * @param value * {@code true} that the resulting numbers of parse operations * will be of type {@code java.lang.Integer} except for the * special cases described above. */ @Override public void setParseIntegerOnly(boolean value) { // In this implementation, NativeDecimalFormat is wrapped to // fulfill most of the format and parse feature. And this method is // delegated to the wrapped instance of NativeDecimalFormat. dform.setParseIntegerOnly(value); } /** * Indicates whether parsing with this decimal format will only * return numbers of type {@code java.lang.Integer}. * * @return {@code true} if this {@code DecimalFormat}'s parse method only * returns {@code java.lang.Integer}; {@code false} otherwise. */ @Override public boolean isParseIntegerOnly() { return dform.isParseIntegerOnly(); } private static final Double NEGATIVE_ZERO_DOUBLE = new Double(-0.0); /** * Parses a {@code Long} or {@code Double} from the specified string * starting at the index specified by {@code position}. If the string is * successfully parsed then the index of the {@code ParsePosition} is * updated to the index following the parsed text. On error, the index is * unchanged and the error index of {@code ParsePosition} is set to the * index where the error occurred. * * @param string * the string to parse. * @param position * input/output parameter, specifies the start index in * {@code string} from where to start parsing. If parsing is * successful, it is updated with the index following the parsed * text; on error, the index is unchanged and the error index is * set to the index where the error occurred. * @return a {@code Long} or {@code Double} resulting from the parse or * {@code null} if there is an error. The result will be a * {@code Long} if the parsed number is an integer in the range of a * long, otherwise the result is a {@code Double}. If * {@code isParseBigDecimal} is {@code true} then it returns the * result as a {@code BigDecimal}. */ @Override public Number parse(String string, ParsePosition position) { Number number = dform.parse(string, position); if (number == null) { return null; } if (this.isParseBigDecimal()) { if (number instanceof Long) { return new BigDecimal(number.longValue()); } if ((number instanceof Double) && !((Double) number).isInfinite() && !((Double) number).isNaN()) { return new BigDecimal(number.toString()); } if (number instanceof BigInteger) { return new BigDecimal(number.toString()); } return number; } if ((number instanceof BigDecimal) || (number instanceof BigInteger)) { return new Double(number.doubleValue()); } if (this.isParseIntegerOnly() && number.equals(NEGATIVE_ZERO_DOUBLE)) { return Long.valueOf(0); } return number; } /** * Sets the {@code DecimalFormatSymbols} used by this decimal format. * * @param value * the {@code DecimalFormatSymbols} to set. */ public void setDecimalFormatSymbols(DecimalFormatSymbols value) { if (value != null) { // The Java object is canonical, and we copy down to native code. this.symbols = (DecimalFormatSymbols) value.clone(); dform.setDecimalFormatSymbols(this.symbols); } } /** * Sets the currency used by this decimal format. The min and max fraction * digits remain the same. * * @param currency * the currency this {@code DecimalFormat} should use. * @see DecimalFormatSymbols#setCurrency(Currency) */ @Override public void setCurrency(Currency currency) { dform.setCurrency(Currency.getInstance(currency.getCurrencyCode())); symbols.setCurrency(currency); } /** * Sets whether the decimal separator is shown when there are no fractional * digits. * * @param value * {@code true} if the decimal separator should always be * formatted; {@code false} otherwise. */ public void setDecimalSeparatorAlwaysShown(boolean value) { dform.setDecimalSeparatorAlwaysShown(value); } /** * Sets the number of digits grouped together by the grouping separator. * This only allows to set the primary grouping size; the secondary grouping * size can only be set with a pattern. * * @param value * the number of digits grouped together. */ public void setGroupingSize(int value) { dform.setGroupingSize(value); } /** * Sets whether or not grouping will be used in this format. Grouping * affects both parsing and formatting. * * @param value * {@code true} if grouping is used; {@code false} otherwise. */ @Override public void setGroupingUsed(boolean value) { dform.setGroupingUsed(value); } /** * Indicates whether grouping will be used in this format. * * @return {@code true} if grouping is used; {@code false} otherwise. */ @Override public boolean isGroupingUsed() { return dform.isGroupingUsed(); } /** * 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. */ @Override public void setMaximumFractionDigits(int value) { super.setMaximumFractionDigits(value); dform.setMaximumFractionDigits(getMaximumFractionDigits()); // Changing the maximum fraction digits needs to update ICU4C's rounding configuration. setRoundingMode(roundingMode); } /** * 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. */ @Override public void setMaximumIntegerDigits(int value) { super.setMaximumIntegerDigits(value); dform.setMaximumIntegerDigits(getMaximumIntegerDigits()); } /** * 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. */ @Override public void setMinimumFractionDigits(int value) { super.setMinimumFractionDigits(value); dform.setMinimumFractionDigits(getMinimumFractionDigits()); } /** * 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. */ @Override public void setMinimumIntegerDigits(int value) { super.setMinimumIntegerDigits(value); dform.setMinimumIntegerDigits(getMinimumIntegerDigits()); } /** * Sets the multiplier which is applied to the number before formatting or * after parsing. * * @param value * the multiplier. */ public void setMultiplier(int value) { dform.setMultiplier(value); } /** * Sets the prefix which is formatted or parsed before a negative number. * * @param value * the negative prefix. */ public void setNegativePrefix(String value) { dform.setNegativePrefix(value); } /** * Sets the suffix which is formatted or parsed after a negative number. * * @param value * the negative suffix. */ public void setNegativeSuffix(String value) { dform.setNegativeSuffix(value); } /** * Sets the prefix which is formatted or parsed before a positive number. * * @param value * the positive prefix. */ public void setPositivePrefix(String value) { dform.setPositivePrefix(value); } /** * Sets the suffix which is formatted or parsed after a positive number. * * @param value * the positive suffix. */ public void setPositiveSuffix(String value) { dform.setPositiveSuffix(value); } /** * Sets the behavior of the parse method. If set to {@code true} then all * the returned objects will be of type {@code BigDecimal}. * * @param newValue * {@code true} if all the returned objects should be of type * {@code BigDecimal}; {@code false} otherwise. */ public void setParseBigDecimal(boolean newValue) { dform.setParseBigDecimal(newValue); } /** * Returns the pattern of this decimal format using localized pattern * characters. * * @return the localized pattern. */ public String toLocalizedPattern() { return dform.toLocalizedPattern(); } /** * Returns the pattern of this decimal format using non-localized pattern * characters. * * @return the non-localized pattern. */ public String toPattern() { return dform.toPattern(); } // the fields list to be serialized private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("positivePrefix", String.class), new ObjectStreamField("positiveSuffix", String.class), new ObjectStreamField("negativePrefix", String.class), new ObjectStreamField("negativeSuffix", String.class), new ObjectStreamField("posPrefixPattern", String.class), new ObjectStreamField("posSuffixPattern", String.class), new ObjectStreamField("negPrefixPattern", String.class), new ObjectStreamField("negSuffixPattern", String.class), new ObjectStreamField("multiplier", int.class), new ObjectStreamField("groupingSize", byte.class), new ObjectStreamField("groupingUsed", boolean.class), new ObjectStreamField("decimalSeparatorAlwaysShown", boolean.class), new ObjectStreamField("parseBigDecimal", boolean.class), new ObjectStreamField("roundingMode", RoundingMode.class), new ObjectStreamField("symbols", DecimalFormatSymbols.class), new ObjectStreamField("useExponentialNotation", boolean.class), new ObjectStreamField("minExponentDigits", byte.class), new ObjectStreamField("maximumIntegerDigits", int.class), new ObjectStreamField("minimumIntegerDigits", int.class), new ObjectStreamField("maximumFractionDigits", int.class), new ObjectStreamField("minimumFractionDigits", int.class), new ObjectStreamField("serialVersionOnStream", int.class), }; /** * Writes serialized fields following serialized forms specified by Java * specification. * * @param stream * the output stream to write serialized bytes * @throws IOException * if some I/O error occurs * @throws ClassNotFoundException */ private void writeObject(ObjectOutputStream stream) throws IOException, ClassNotFoundException { ObjectOutputStream.PutField fields = stream.putFields(); fields.put("positivePrefix", dform.getPositivePrefix()); fields.put("positiveSuffix", dform.getPositiveSuffix()); fields.put("negativePrefix", dform.getNegativePrefix()); fields.put("negativeSuffix", dform.getNegativeSuffix()); fields.put("posPrefixPattern", (String) null); fields.put("posSuffixPattern", (String) null); fields.put("negPrefixPattern", (String) null); fields.put("negSuffixPattern", (String) null); fields.put("multiplier", dform.getMultiplier()); fields.put("groupingSize", (byte) dform.getGroupingSize()); fields.put("groupingUsed", dform.isGroupingUsed()); fields.put("decimalSeparatorAlwaysShown", dform.isDecimalSeparatorAlwaysShown()); fields.put("parseBigDecimal", dform.isParseBigDecimal()); fields.put("roundingMode", roundingMode); fields.put("symbols", symbols); fields.put("useExponentialNotation", false); fields.put("minExponentDigits", (byte) 0); fields.put("maximumIntegerDigits", dform.getMaximumIntegerDigits()); fields.put("minimumIntegerDigits", dform.getMinimumIntegerDigits()); fields.put("maximumFractionDigits", dform.getMaximumFractionDigits()); fields.put("minimumFractionDigits", dform.getMinimumFractionDigits()); fields.put("serialVersionOnStream", 4); stream.writeFields(); } /** * Reads serialized fields following serialized forms specified by Java * specification. * * @param stream * the input stream to read serialized bytes * @throws IOException * if some I/O error occurs * @throws ClassNotFoundException * if some class of serialized objects or fields cannot be found */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { ObjectInputStream.GetField fields = stream.readFields(); this.symbols = (DecimalFormatSymbols) fields.get("symbols", null); initNative(""); dform.setPositivePrefix((String) fields.get("positivePrefix", "")); dform.setPositiveSuffix((String) fields.get("positiveSuffix", "")); dform.setNegativePrefix((String) fields.get("negativePrefix", "-")); dform.setNegativeSuffix((String) fields.get("negativeSuffix", "")); dform.setMultiplier(fields.get("multiplier", 1)); dform.setGroupingSize(fields.get("groupingSize", (byte) 3)); dform.setGroupingUsed(fields.get("groupingUsed", true)); dform.setDecimalSeparatorAlwaysShown(fields.get("decimalSeparatorAlwaysShown", false)); setRoundingMode((RoundingMode) fields.get("roundingMode", RoundingMode.HALF_EVEN)); final int maximumIntegerDigits = fields.get("maximumIntegerDigits", 309); final int minimumIntegerDigits = fields.get("minimumIntegerDigits", 309); final int maximumFractionDigits = fields.get("maximumFractionDigits", 340); final int minimumFractionDigits = fields.get("minimumFractionDigits", 340); // 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); super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits()); setMinimumIntegerDigits(minimumIntegerDigits); setMinimumFractionDigits(minimumFractionDigits); setMaximumFractionDigits(maximumFractionDigits); setParseBigDecimal(fields.get("parseBigDecimal", false)); if (fields.get("serialVersionOnStream", 0) < 3) { setMaximumIntegerDigits(super.getMaximumIntegerDigits()); setMinimumIntegerDigits(super.getMinimumIntegerDigits()); setMaximumFractionDigits(super.getMaximumFractionDigits()); setMinimumFractionDigits(super.getMinimumFractionDigits()); } } /** * Returns the {@code RoundingMode} used by this {@code NumberFormat}. * @since 1.6 */ public RoundingMode getRoundingMode() { return roundingMode; } /** * Sets the {@code RoundingMode} used by this {@code NumberFormat}. * @since 1.6 */ public void setRoundingMode(RoundingMode roundingMode) { if (roundingMode == null) { throw new NullPointerException(); } this.roundingMode = roundingMode; if (roundingMode != RoundingMode.UNNECESSARY) { // ICU4C doesn't support UNNECESSARY. double roundingIncrement = 1.0 / Math.pow(10, Math.max(0, getMaximumFractionDigits())); dform.setRoundingMode(roundingMode, roundingIncrement); } } }