blob: 65d4d48b7821f9640db66de6832b7d81745d36e3 [file] [log] [blame]
/*
* 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 &lt; 3; ++j) {
* System.out.println(&quot;FORMAT&quot;);
* for (int i = 0; i &lt; 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(&quot;: &quot;; + ((DecimalFormat)format).toPattern() + &quot; -&gt; &quot;
* + form.format(myNumber));
* } catch (Exception e) {
* }
* try {
* System.out.println(&quot; -&gt; &quot; + 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>&nbsp;</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>&nbsp;</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 &#92;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 &#164;} ({@code &#92;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>&nbsp;</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>&nbsp;</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 &quot;#,##,###&quot; formats the number
* 123456789 as &quot;12,34,56,789&quot;.</li>
* <li> <strong><font color="red">NEW</font>&nbsp;</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 &#92;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 &#92;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>&nbsp;</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>&nbsp;</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
}
}