/* * 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. */ // BEGIN android-note // The icu implementation used was changed from icu4j to icu4jni. // END android-note 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.security.AccessController; import java.security.PrivilegedAction; import java.util.Currency; import java.util.Locale; import com.ibm.icu4jni.text.NativeDecimalFormat; import com.ibm.icu4jni.util.LocaleData; /** * 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. * <p> * <strong>This is an enhanced version of {@code DecimalFormat} that is based on * the standard version in the RI. New or changed functionality is labeled * <strong><font color="red">NEW</font></strong>.</strong> * <p> * 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: <blockquote> * * <pre> * NumberFormat f = NumberFormat.getInstance(loc); * if (f instanceof DecimalFormat) { * ((DecimalFormat)f).setDecimalSeparatorAlwaysShown(true); * } * </pre> * * </blockquote> * <h5>Example:</h5> * <blockquote> * * <pre> * // Print out a number using the localized number, currency, * // and percent format for each locale * Locale[] locales = NumberFormat.getAvailableLocales(); * double myNumber = -1234.56; * NumberFormat format; * for (int j = 0; j < 3; ++j) { * System.out.println("FORMAT"); * for (int i = 0; i < locales.length; ++i) { * if (locales[i].getCountry().length() == 0) { * // Skip language-only locales * continue; * } * System.out.print(locales[i].getDisplayName()); * switch (j) { * case 0: * format = NumberFormat.getInstance(locales[i]); * break; * case 1: * format = NumberFormat.getCurrencyInstance(locales[i]); * break; * default: * format = NumberFormat.getPercentInstance(locales[i]); * break; * } * try { * // Assume format is a DecimalFormat * System.out.print(": "; + ((DecimalFormat)format).toPattern() + " -> " * + form.format(myNumber)); * } catch (Exception e) { * } * try { * System.out.println(" -> " + format.parse(form.format(myNumber))); * } catch (ParseException e) { * } * } * } * </pre> * * </blockquote> * <h4>Patterns</h4> * <p> * A {@code DecimalFormat} consists of a <em>pattern</em> and a set of * <em>symbols</em>. 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. * <h4>Special Pattern Characters</h4> * <p> * 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. * <p> * 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. * <p> * 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. * <blockquote> <table border="0" cellspacing="3" cellpadding="0" summary="Chart * showing symbol, location, localized, and meaning."> * <tr bgcolor="#ccccff"> * <th align="left">Symbol</th> * <th align="left">Location</th> * <th align="left">Localized?</th> * <th align="left">Meaning</th> * </tr> * <tr valign="top"> * <td>{@code 0}</td> * <td>Number</td> * <td>Yes</td> * <td>Digit.</td> * </tr> * <tr valign="top"> * <td>{@code @}</td> * <td>Number</td> * <td>No</td> * <td><strong><font color="red">NEW</font> </strong> Significant * digit.</td> * </tr> * <tr valign="top" bgcolor="#eeeeff"> * <td>{@code #}</td> * <td>Number</td> * <td>Yes</td> * <td>Digit, leading zeroes are not shown.</td> * </tr> * <tr valign="top"> * <td>{@code .}</td> * <td>Number</td> * <td>Yes</td> * <td>Decimal separator or monetary decimal separator.</td> * </tr> * <tr valign="top" bgcolor="#eeeeff"> * <td>{@code -}</td> * <td>Number</td> * <td>Yes</td> * <td>Minus sign.</td> * </tr> * <tr valign="top"> * <td>{@code ,}</td> * <td>Number</td> * <td>Yes</td> * <td>Grouping separator.</td> * </tr> * <tr valign="top" bgcolor="#eeeeff"> * <td>{@code E}</td> * <td>Number</td> * <td>Yes</td> * <td>Separates mantissa and exponent in scientific notation. * <em>Does not need to be quoted in prefix or suffix.</em></td> * </tr> * <tr valign="top"> * <td>{@code +}</td> * <td>Exponent</td> * <td>Yes</td> * <td><strong><font color="red">NEW</font> </strong> Prefix * positive exponents with localized plus sign. * <em>Does not need to be quoted in prefix or suffix.</em></td> * </tr> * <tr valign="top" bgcolor="#eeeeff"> * <td>{@code ;}</td> * <td>Subpattern boundary</td> * <td>Yes</td> * <td>Separates positive and negative subpatterns.</td> * </tr> * <tr valign="top"> * <td>{@code %}</td> * <td>Prefix or suffix</td> * <td>Yes</td> * <td>Multiply by 100 and show as percentage.</td> * </tr> * <tr valign="top" bgcolor="#eeeeff"> * <td>{@code \u2030} ({@code \u2030})</td> * <td>Prefix or suffix</td> * <td>Yes</td> * <td>Multiply by 1000 and show as per mille.</td> * </tr> * <tr valign="top"> * <td>{@code ¤} ({@code \u00A4})</td> * <td>Prefix or suffix</td> * <td>No</td> * <td>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.</td> * </tr> * <tr valign="top" bgcolor="#eeeeff"> * <td>{@code '}</td> * <td>Prefix or suffix</td> * <td>No</td> * <td>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"}.</td> * </tr> * <tr valign="top"> * <td>{@code *}</td> * <td>Prefix or suffix boundary</td> * <td>Yes</td> * <td><strong><font color="red">NEW</font> </strong> Pad escape, * precedes pad character. </td> * </tr> * </table> </blockquote> * <p> * A {@code DecimalFormat} pattern contains a postive 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#)". * <p> * 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. * <p> * The <em>grouping separator</em> 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 <em>grouping * size</em> * 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 * <em>primary grouping size</em>, and one used for all others, the * <em>secondary grouping size</em>. 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. * <p> * Illegal patterns, such as "#.#.#" or "#.###,###", will cause * {@code DecimalFormat} to throw an {@link IllegalArgumentException} with a * message that describes the problem. * <h4>Pattern BNF</h4> * * <pre> * 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 * </pre> * * The first subpattern is for positive numbers. The second (optional) * subpattern is for negative numbers. * <p> * Not indicated in the BNF syntax above: * <ul> * <li>The grouping separator ',' can occur inside the integer and sigDigits * elements, between any two pattern characters of that element, as long as the * integer or sigDigits element is not followed by the exponent element. * <li><font color="red"><strong>NEW</strong> </font> Two * grouping intervals are recognized: The one between the decimal point and the * first grouping symbol and the one between the first and second grouping * symbols. These intervals are identical in most locales, but in some locales * they differ. For example, the pattern "#,##,###" formats the number * 123456789 as "12,34,56,789".</li> * <li> <strong><font color="red">NEW</font> </strong> The pad * specifier {@code padSpec} may appear before the prefix, after the prefix, * before the suffix, after the suffix or not at all. * </ul> * <h4>Parsing</h4> * <p> * {@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. * <p> * During parsing, grouping separators are ignored. * <p> * If {@link #parse(String, ParsePosition)} fails to parse a string, it returns * {@code null} and leaves the parse position unchanged. * <h4>Formatting</h4> * <p> * 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 <a href="#sci">scientific notation</a> or <a * href="#sigdig">significant digits</a>. * <ul> * <li>If the number of actual integer digits exceeds the * <em>maximum integer digits</em>, then only the least significant digits * are shown. For example, 1997 is formatted as "97" if maximum integer digits * is set to 2. * <li>If the number of actual integer digits is less than the * <em>minimum integer digits</em>, then leading zeros are added. For * example, 1997 is formatted as "01997" if minimum integer digits is set to 5. * <li>If the number of actual fraction digits exceeds the <em>maximum * fraction digits</em>, * then half-even rounding is performed to the maximum fraction digits. For * example, 0.125 is formatted as "0.12" if the maximum fraction digits is 2. * <li>If the number of actual fraction digits is less than the * <em>minimum fraction digits</em>, then trailing zeros are added. For * example, 0.125 is formatted as "0.1250" if the mimimum fraction digits is set * to 4. * <li>Trailing fractional zeros are not displayed if they occur <em>j</em> * positions after the decimal, where <em>j</em> is less than the maximum * fraction digits. For example, 0.10004 is formatted as "0.1" if the maximum * fraction digits is four or less. * </ul> * <p> * <strong>Special Values</strong> * <p> * {@code NaN} is represented as a single character, typically * {@code \uFFFD}. This character is determined by the * {@link DecimalFormatSymbols} object. This is the only value for which the * prefixes and suffixes are not used. * <p> * Infinity is represented as a single character, typically {@code \u221E}, * with the positive or negative prefixes and suffixes applied. The infinity * character is determined by the {@link DecimalFormatSymbols} object. <a * name="sci"> * <h4>Scientific Notation</h4> * </a> * <p> * 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 10<sup>3</sup>. * 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". * <ul> * <li>The number of digit characters after the exponent character gives the * minimum exponent digit count. There is no maximum. Negative exponents are * formatted using the localized minus sign, <em>not</em> the prefix and * suffix from the pattern. This allows patterns such as "0.###E0 m/s". To * prefix positive exponents with a localized plus sign, specify '+' between the * exponent and the digits: "0.###E+0" will produce formats "1E+1", "1E+0", * "1E-1", etc. (In localized patterns, use the localized plus sign rather than * '+'.) * <li>The minimum number of integer digits is achieved by adjusting the * exponent. Example: 0.00123 formatted with "00.###E0" yields "12.3E-4". This * only happens if there is no maximum number of integer digits. If there is a * maximum, then the minimum number of integer digits is fixed at one. * <li>The maximum number of integer digits, if present, specifies the exponent * grouping. The most common use of this is to generate <em>engineering * notation</em>, * in which the exponent is a multiple of three, e.g., "##0.###E0". The number * 12345 is formatted using "##0.###E0" as "12.345E3". * <li>When using scientific notation, the formatter controls the digit counts * using significant digits logic. The maximum number of significant digits * limits the total number of integer and fraction digits that will be shown in * the mantissa; it does not affect parsing. For example, 12345 formatted with * "##0.##E0" is "12.3E3". See the section on significant digits for more * details. * <li>The number of significant digits shown is determined as follows: If no * significant digits are used in the pattern then the minimum number of * significant digits shown is one, the maximum number of significant digits * shown is the sum of the <em>minimum integer</em> and * <em>maximum fraction</em> digits, and it is unaffected by the maximum * integer digits. If this sum is zero, then all significant digits are shown. * If significant digits are used in the pattern then the number of integer * digits is fixed at one and there is no exponent grouping. * <li>Exponential patterns may not contain grouping separators. * </ul> * <a name="sigdig"> * <h4> <strong><font color="red">NEW</font> </strong> Significant * Digits</h4> * <p> * </a> {@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. * <h5>Examples:</h5> * <blockquote> <table border=0 cellspacing=3 cellpadding=0> * <tr bgcolor="#ccccff"> * <th align="left">Pattern</th> * <th align="left">Minimum significant digits</th> * <th align="left">Maximum significant digits</th> * <th align="left">Number</th> * <th align="left">Output of format()</th> * </tr> * <tr valign="top"> * <td>{@code @@@} * <td>3</td> * <td>3</td> * <td>12345</td> * <td>{@code 12300}</td> * </tr> * <tr valign="top" bgcolor="#eeeeff"> * <td>{@code @@@}</td> * <td>3</td> * <td>3</td> * <td>0.12345</td> * <td>{@code 0.123}</td> * </tr> * <tr valign="top"> * <td>{@code @@##}</td> * <td>2</td> * <td>4</td> * <td>3.14159</td> * <td>{@code 3.142}</td> * </tr> * <tr valign="top" bgcolor="#eeeeff"> * <td>{@code @@##}</td> * <td>2</td> * <td>4</td> * <td>1.23004</td> * <td>{@code 1.23}</td> * </tr> * </table> </blockquote> * <ul> * <li>Significant digit counts may be expressed using patterns that specify a * minimum and maximum number of significant digits. These are indicated by the * {@code '@'} and {@code '#'} characters. The minimum number of significant * digits is the number of {@code '@'} characters. The maximum number of * significant digits is the number of {@code '@'} characters plus the number of * {@code '#'} characters following on the right. For example, the pattern * {@code "@@@"} indicates exactly 3 significant digits. The pattern * {@code "@##"} indicates from 1 to 3 significant digits. Trailing zero digits * to the right of the decimal separator are suppressed after the minimum number * of significant digits have been shown. For example, the pattern {@code "@##"} * formats the number 0.1203 as {@code "0.12"}. * <li>If a pattern uses significant digits, it may not contain a decimal * separator, nor the {@code '0'} pattern character. Patterns such as * {@code "@00"} or {@code "@.###"} are disallowed. * <li>Any number of {@code '#'} characters may be prepended to the left of the * leftmost {@code '@'} character. These have no effect on the minimum and * maximum significant digit counts, but may be used to position grouping * separators. For example, {@code "#,#@#"} indicates a minimum of one * significant digit, a maximum of two significant digits, and a grouping size * of three. * <li>In order to enable significant digits formatting, use a pattern * containing the {@code '@'} pattern character. * <li>In order to disable significant digits formatting, use a pattern that * does not contain the {@code '@'} pattern character. * <li>The number of significant digits has no effect on parsing. * <li>Significant digits may be used together with exponential notation. Such * patterns are equivalent to a normal exponential pattern with a minimum and * maximum integer digit count of one, a minimum fraction digit count of the * number of '@' characters in the pattern - 1, and a maximum fraction digit * count of the number of '@' and '#' characters in the pattern - 1. For * example, the pattern {@code "@@###E0"} is equivalent to {@code "0.0###E0"}. * <li>If signficant digits are in use then the integer and fraction digit * counts, as set via the API, are ignored. * </ul> * <h4> <strong><font color="red">NEW</font> </strong> Padding</h4> * <p> * {@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"}. * <ul> * <li>When padding is in effect, the width of the positive subpattern, * including prefix and suffix, determines the format width. For example, in the * pattern {@code "* #0 o''clock"}, the format width is 10.</li> * <li>The width is counted in 16-bit code units (Java {@code char}s).</li> * <li>Some parameters which usually do not matter have meaning when padding is * used, because the pattern width is significant with padding. In the pattern "* * ##,##,#,##0.##", the format width is 14. The initial characters "##,##," do * not affect the grouping size or maximum integer digits, but they do affect * the format width.</li> * <li>Padding may be inserted at one of four locations: before the prefix, * after the prefix, before the suffix or after the suffix. If padding is * specified in any other location, {@link #applyPattern} throws an {@link * IllegalArgumentException}. If there is no prefix, before the prefix and after * the prefix are equivalent, likewise for the suffix.</li> * <li>When specified in a pattern, the 16-bit {@code char} immediately * following the pad escape is the pad character. This may be any character, * including a special pattern character. That is, the pad escape * <em>escapes</em> the following character. If there is no character after * the pad escape, then the pattern is illegal.</li> * </ul> * <h4>Synchronization</h4> * <p> * {@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 boolean parseBigDecimal = false; private transient DecimalFormatSymbols symbols; private transient NativeDecimalFormat dform; /** * Constructs a new {@code DecimalFormat} for formatting and parsing numbers * for the default locale. */ public DecimalFormat() { // BEGIN android-changed: reduce duplication. Locale locale = Locale.getDefault(); this.symbols = new DecimalFormatSymbols(locale); LocaleData localeData = com.ibm.icu4jni.util.Resources.getLocaleData(locale); initNative(localeData.numberPattern); // END android-changed } /** * Constructs a new {@code DecimalFormat} using the specified non-localized * pattern and the {@code DecimalFormatSymbols} for the default Locale. * * @param pattern * the non-localized pattern. * @throws IllegalArgumentException * if the pattern cannot be parsed. */ public DecimalFormat(String pattern) { // BEGIN android-changed: reduce duplication. this(pattern, Locale.getDefault()); // END android-changed } /** * 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) { // BEGIN android-changed: reduce duplication. this.symbols = (DecimalFormatSymbols) value.clone(); initNative(pattern); // END android-changed } // BEGIN android-added: used by NumberFormat.getInstance because cloning DecimalFormatSymbols is slow. DecimalFormat(String pattern, Locale locale) { this.symbols = new DecimalFormatSymbols(locale); initNative(pattern); } // END android-added // BEGIN android-changed: reduce duplication. 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()); } // END android-added /** * 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); } @Override public StringBuffer format(double value, StringBuffer buffer, FieldPosition position) { return dform.format(value, buffer, position); } @Override public StringBuffer format(long value, StringBuffer buffer, FieldPosition position) { return dform.format(value, buffer, position); } @Override public final StringBuffer format(Object number, StringBuffer toAppendTo, FieldPosition pos) { if (number instanceof BigInteger) { BigInteger bigInteger = (BigInteger) number; if (bigInteger.bitLength() < 64) { return dform.format(bigInteger.longValue(), toAppendTo, pos); } else { return dform.formatBigInteger(bigInteger, toAppendTo, pos); } } else if (number instanceof BigDecimal) { return dform.formatBigDecimal((BigDecimal) number, toAppendTo, pos); } return super.format(number, toAppendTo, pos); } /** * 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 this.parseBigDecimal; } /** * 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 (null == number) { return null; } // BEGIN android-removed // 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.doubleValue()); // } // if (number instanceof BigInteger) { // return new BigDecimal(number.doubleValue()); // } // if (number instanceof com.ibm.icu.math.BigDecimal) { // return new BigDecimal(number.toString()); // } // return number; // } // if ((number instanceof com.ibm.icu.math.BigDecimal) // || (number instanceof BigInteger)) { // return new Double(number.doubleValue()); // } // END android-removed // BEGIN android-added 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()); } // END android-added if (this.isParseIntegerOnly() && number.equals(NEGATIVE_ZERO_DOUBLE)) { return Long.valueOf(0); // android-changed } 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) { // BEGIN android-changed: the Java object is canonical, and we copy down to native code. this.symbols = (DecimalFormatSymbols) value.clone(); dform.setDecimalFormatSymbols(this.symbols); // END android-changed } } /** * 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) { // BEGIN android-changed dform.setCurrency(Currency.getInstance(currency.getCurrencyCode())); // END android-changed 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()); } /** * 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 behaviour 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) { this.parseBigDecimal = 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), //$NON-NLS-1$ new ObjectStreamField("positiveSuffix", String.class), //$NON-NLS-1$ new ObjectStreamField("negativePrefix", String.class), //$NON-NLS-1$ new ObjectStreamField("negativeSuffix", String.class), //$NON-NLS-1$ new ObjectStreamField("posPrefixPattern", String.class), //$NON-NLS-1$ new ObjectStreamField("posSuffixPattern", String.class), //$NON-NLS-1$ new ObjectStreamField("negPrefixPattern", String.class), //$NON-NLS-1$ new ObjectStreamField("negSuffixPattern", String.class), //$NON-NLS-1$ new ObjectStreamField("multiplier", int.class), //$NON-NLS-1$ new ObjectStreamField("groupingSize", byte.class), //$NON-NLS-1$ // BEGIN android-added new ObjectStreamField("groupingUsed", boolean.class), //$NON-NLS-1$ // END android-added new ObjectStreamField("decimalSeparatorAlwaysShown", boolean.class), //$NON-NLS-1$ new ObjectStreamField("parseBigDecimal", boolean.class), //$NON-NLS-1$ new ObjectStreamField("symbols", DecimalFormatSymbols.class), //$NON-NLS-1$ new ObjectStreamField("useExponentialNotation", boolean.class), //$NON-NLS-1$ new ObjectStreamField("minExponentDigits", byte.class), //$NON-NLS-1$ new ObjectStreamField("maximumIntegerDigits", int.class), //$NON-NLS-1$ new ObjectStreamField("minimumIntegerDigits", int.class), //$NON-NLS-1$ new ObjectStreamField("maximumFractionDigits", int.class), //$NON-NLS-1$ new ObjectStreamField("minimumFractionDigits", int.class), //$NON-NLS-1$ new ObjectStreamField("serialVersionOnStream", int.class), }; //$NON-NLS-1$ /** * 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 */ @SuppressWarnings("nls") 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()); // BEGIN android-added fields.put("groupingUsed", dform.isGroupingUsed()); // END android-added fields.put("decimalSeparatorAlwaysShown", dform .isDecimalSeparatorAlwaysShown()); fields.put("parseBigDecimal", parseBigDecimal); 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", 3); 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 */ @SuppressWarnings("nls") private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { // BEGIN android-changed 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)); 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); // BEGIN android-changed: tell ICU what we want, then ask it what we can have, and then // set that in our Java object. This isn't RI-compatible, but then very little of our // behavior in this area is, and it's not obvious how we can second-guess ICU (or tell // it to just do exactly what we ask). We only need to do this with maximumIntegerDigits // because ICU doesn't seem to have its own ideas about the other options. dform.setMaximumIntegerDigits(maximumIntegerDigits); 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()); } // END android-changed } }