/* * 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 ndf; 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.ndf = new NativeDecimalFormat(pattern, symbols); } catch (IllegalArgumentException ex) { throw new IllegalArgumentException(pattern); } super.setMaximumFractionDigits(ndf.getMaximumFractionDigits()); super.setMaximumIntegerDigits(ndf.getMaximumIntegerDigits()); super.setMinimumFractionDigits(ndf.getMinimumFractionDigits()); super.setMinimumIntegerDigits(ndf.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) { ndf.applyLocalizedPattern(pattern); updateFieldsFromNative(); } /** * 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) { ndf.applyPattern(pattern); updateFieldsFromNative(); } private void updateFieldsFromNative() { maximumIntegerDigits = ndf.getMaximumIntegerDigits(); minimumIntegerDigits = ndf.getMinimumIntegerDigits(); maximumFractionDigits = ndf.getMaximumFractionDigits(); minimumFractionDigits = ndf.getMinimumFractionDigits(); } /** * Returns a new instance of {@code DecimalFormat} with the same pattern and * properties. */ @Override public Object clone() { DecimalFormat clone = (DecimalFormat) super.clone(); clone.ndf = (NativeDecimalFormat) ndf.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.ndf == null ? other.ndf == null : this.ndf.equals(other.ndf)) && 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("object == null"); } if (roundingMode == RoundingMode.UNNECESSARY && (object instanceof Float || object instanceof Double)) { checkRoundingUnnecessary(((Number) object).doubleValue()); } return ndf.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"); } } private void checkRoundingUnnecessary(Object value) { // ICU4C doesn't support this rounding mode, so we have to fake it. // This implementation reduces code duplication, but adds boxing // overhead and sends you all the way back through. But since we // have to format your string multiple times in this mode, you're already // screwed performance-wise. 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); } } @Override public StringBuffer format(double value, StringBuffer buffer, FieldPosition position) { checkBufferAndFieldPosition(buffer, position); if (roundingMode == RoundingMode.UNNECESSARY) { checkRoundingUnnecessary(value); } buffer.append(ndf.formatDouble(value, position)); return buffer; } @Override public StringBuffer format(long value, StringBuffer buffer, FieldPosition position) { checkBufferAndFieldPosition(buffer, position); if (roundingMode == RoundingMode.UNNECESSARY) { checkRoundingUnnecessary(value); } buffer.append(ndf.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) ? ndf.formatLong(bigInteger.longValue(), position) : ndf.formatBigInteger(bigInteger, position); buffer.append(chars); return buffer; } else if (number instanceof BigDecimal) { buffer.append(ndf.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 ndf.getGroupingSize(); } /** * Returns the prefix which is formatted or parsed before a negative number. * * @return the negative prefix. */ public String getNegativePrefix() { return ndf.getNegativePrefix(); } /** * Returns the suffix which is formatted or parsed after a negative number. * * @return the negative suffix. */ public String getNegativeSuffix() { return ndf.getNegativeSuffix(); } /** * Returns the prefix which is formatted or parsed before a positive number. * * @return the positive prefix. */ public String getPositivePrefix() { return ndf.getPositivePrefix(); } /** * Returns the suffix which is formatted or parsed after a positive number. * * @return the positive suffix. */ public String getPositiveSuffix() { return ndf.getPositiveSuffix(); } @Override public int hashCode() { return getPositivePrefix().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 ndf.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 ndf.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. super.setParseIntegerOnly(value); ndf.setParseIntegerOnly(value); } @Override public boolean isParseIntegerOnly() { return ndf.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 = ndf.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. */ 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(); ndf.setDecimalFormatSymbols(this.symbols); } } /** * Sets the currency used by this decimal format. The min and max fraction * digits remain the same. */ @Override public void setCurrency(Currency currency) { ndf.setCurrency(Currency.getInstance(currency.getCurrencyCode())); symbols.setCurrency(currency); } /** * Sets whether the decimal separator is shown even when there are no fractional * digits. */ public void setDecimalSeparatorAlwaysShown(boolean value) { ndf.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. */ public void setGroupingSize(int value) { ndf.setGroupingSize(value); } /** * Sets whether or not digit grouping will be used in this format. Grouping * affects both formatting and parsing. */ @Override public void setGroupingUsed(boolean value) { ndf.setGroupingUsed(value); } /** * Returns true if digit grouping is used in this format. Grouping affects both * formatting and parsing. */ @Override public boolean isGroupingUsed() { return ndf.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. */ @Override public void setMaximumFractionDigits(int value) { super.setMaximumFractionDigits(value); ndf.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. */ @Override public void setMaximumIntegerDigits(int value) { super.setMaximumIntegerDigits(value); ndf.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. */ @Override public void setMinimumFractionDigits(int value) { super.setMinimumFractionDigits(value); ndf.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. */ @Override public void setMinimumIntegerDigits(int value) { super.setMinimumIntegerDigits(value); ndf.setMinimumIntegerDigits(getMinimumIntegerDigits()); } /** * Returns the multiplier which is applied to the number before formatting * or after parsing. The multiplier is meant for tasks like parsing percentages. * For example, given a multiplier of 100, 1.23 would be formatted as "123" and * "123" would be parsed as 1.23. */ public int getMultiplier() { return ndf.getMultiplier(); } /** * Sets the multiplier which is applied to the number before formatting or * after parsing. The multiplier meant for tasks like parsing percentages. * For example, given a multiplier of 100, 1.23 would be formatted as "123" and * "123" would be parsed as 1.23. */ public void setMultiplier(int value) { ndf.setMultiplier(value); } /** * Sets the prefix which is formatted or parsed before a negative number. */ public void setNegativePrefix(String value) { ndf.setNegativePrefix(value); } /** * Sets the suffix which is formatted or parsed after a negative number. */ public void setNegativeSuffix(String value) { ndf.setNegativeSuffix(value); } /** * Sets the prefix which is formatted or parsed before a positive number. */ public void setPositivePrefix(String value) { ndf.setPositivePrefix(value); } /** * Sets the suffix which is formatted or parsed after a positive number. */ public void setPositiveSuffix(String value) { ndf.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) { ndf.setParseBigDecimal(newValue); } /** * Returns the pattern of this decimal format using localized pattern * characters. * * @return the localized pattern. */ public String toLocalizedPattern() { return ndf.toLocalizedPattern(); } /** * Returns the pattern of this decimal format using non-localized pattern * characters. * * @return the non-localized pattern. */ public String toPattern() { return ndf.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", ndf.getPositivePrefix()); fields.put("positiveSuffix", ndf.getPositiveSuffix()); fields.put("negativePrefix", ndf.getNegativePrefix()); fields.put("negativeSuffix", ndf.getNegativeSuffix()); fields.put("posPrefixPattern", (String) null); fields.put("posSuffixPattern", (String) null); fields.put("negPrefixPattern", (String) null); fields.put("negSuffixPattern", (String) null); fields.put("multiplier", ndf.getMultiplier()); fields.put("groupingSize", (byte) ndf.getGroupingSize()); fields.put("groupingUsed", ndf.isGroupingUsed()); fields.put("decimalSeparatorAlwaysShown", ndf.isDecimalSeparatorAlwaysShown()); fields.put("parseBigDecimal", ndf.isParseBigDecimal()); fields.put("roundingMode", roundingMode); fields.put("symbols", symbols); fields.put("useExponentialNotation", false); fields.put("minExponentDigits", (byte) 0); fields.put("maximumIntegerDigits", ndf.getMaximumIntegerDigits()); fields.put("minimumIntegerDigits", ndf.getMinimumIntegerDigits()); fields.put("maximumFractionDigits", ndf.getMaximumFractionDigits()); fields.put("minimumFractionDigits", ndf.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(""); ndf.setPositivePrefix((String) fields.get("positivePrefix", "")); ndf.setPositiveSuffix((String) fields.get("positiveSuffix", "")); ndf.setNegativePrefix((String) fields.get("negativePrefix", "-")); ndf.setNegativeSuffix((String) fields.get("negativeSuffix", "")); ndf.setMultiplier(fields.get("multiplier", 1)); ndf.setGroupingSize(fields.get("groupingSize", (byte) 3)); ndf.setGroupingUsed(fields.get("groupingUsed", true)); ndf.setDecimalSeparatorAlwaysShown(fields.get("decimalSeparatorAlwaysShown", false)); setRoundingMode((RoundingMode) fields.get("roundingMode", RoundingMode.HALF_EVEN)); 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. ndf.setMaximumIntegerDigits(maximumIntegerDigits); super.setMaximumIntegerDigits(ndf.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("roundingMode == null"); } this.roundingMode = roundingMode; if (roundingMode != RoundingMode.UNNECESSARY) { // ICU4C doesn't support UNNECESSARY. double roundingIncrement = 1.0 / Math.pow(10, Math.max(0, getMaximumFractionDigits())); ndf.setRoundingMode(roundingMode, roundingIncrement); } } public String toString() { return ndf.toString(); } }