X-Git-Url: http://gitweb.fperrin.net/?a=blobdiff_plain;f=jars%2Ficu4j-4_2_1-src%2Fsrc%2Fcom%2Fibm%2Ficu%2Ftext%2FDecimalFormat.java;h=00c02e363d1717af347efe5feead0d5df7859a9a;hb=127973afabe0c34015667c599d68bf9453d85652;hp=706f010501a9f4c9533d3128800afdd5eaa7a225;hpb=92dfc8b7d39cbc2e55f3c547c0c265bc7ae3af86;p=Dictionary.git
diff --git a/jars/icu4j-4_2_1-src/src/com/ibm/icu/text/DecimalFormat.java b/jars/icu4j-4_2_1-src/src/com/ibm/icu/text/DecimalFormat.java
old mode 100755
new mode 100644
index 706f010..00c02e3
--- a/jars/icu4j-4_2_1-src/src/com/ibm/icu/text/DecimalFormat.java
+++ b/jars/icu4j-4_2_1-src/src/com/ibm/icu/text/DecimalFormat.java
@@ -1,5826 +1,5826 @@
-//##header
-/*
- *******************************************************************************
- * Copyright (C) 1996-2009, International Business Machines Corporation and *
- * others. All Rights Reserved. *
- *******************************************************************************
- */
-package com.ibm.icu.text;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.math.BigInteger;
-import java.text.ChoiceFormat;
-import java.text.FieldPosition;
-import java.text.ParsePosition;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-//#if defined(FOUNDATION10)
-//#else
-import java.io.ObjectOutputStream;
-//#endif
-
-//#if defined(FOUNDATION10) || defined(J2SE13)
-//#else
-import java.text.AttributedCharacterIterator;
-import java.text.AttributedString;
-import java.text.Format;
-import java.util.ArrayList;
-//#endif
-
-import com.ibm.icu.impl.UCharacterProperty;
-import com.ibm.icu.impl.Utility;
-import com.ibm.icu.lang.UCharacter;
-import com.ibm.icu.math.BigDecimal;
-import com.ibm.icu.util.Currency;
-import com.ibm.icu.util.CurrencyAmount;
-import com.ibm.icu.util.ULocale;
-import com.ibm.icu.math.MathContext;
-
-//This is an enhanced version of DecimalFormat that is based on the standard version in the JDK.
-/**
- * DecimalFormat
is 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.00", "USD123.00", "123.00 US dollars").
- * All of these flavors can be easily localized.
- *
- *
- *
To obtain a {@link NumberFormat} for a specific locale (including the
- * default locale) call one of NumberFormat
's factory methods such
- * as {@link NumberFormat#getInstance}. Do not call the DecimalFormat
- * constructors directly, unless you know what you are doing, since the
- * {@link NumberFormat} factory methods may return subclasses other than
- * DecimalFormat
. If you need to customize the format object, do
- * something like this:
- *
- *
- * - *- * NumberFormat f = NumberFormat.getInstance(loc); - * if (f instanceof DecimalFormat) { - * ((DecimalFormat) f).setDecimalSeparatorAlwaysShown(true); - * }
Example Usage - * - *
- * - *- * // 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) {} - * } - * }
- * Another example use getInstance(style) - *
- *
- * // Print out a number using the localized number, currency, - * // percent, scientific, integer, iso currency, and plural currency - * // format for each locale - * ULocale locale = new ULocale("en_US"); - * double myNumber = 1234.56; - * for (int j=NumberFormat.NUMBERSTYLE; j<=NumberFormat.PLURALCURRENCYSTYLE; ++j) { - * NumberFormat format = NumberFormat.getInstance(locale, j); - * 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) {} - * }- * - *
A DecimalFormat
consists of a pattern and a set of
- * symbols. The pattern may be set directly using
- * {@link #applyPattern}, or indirectly using other API methods which
- * manipulate aspects of the pattern, such as the minimum number of integer
- * digits. The symbols are stored in a {@link DecimalFormatSymbols}
- * object. When using the {@link NumberFormat} factory methods, the
- * pattern and symbols are read from ICU's locale data.
- *
- *
Many characters in a pattern are taken literally; they are matched during - * parsing and output unchanged during formatting. Special characters, on the - * other hand, stand for other characters, strings, or classes of characters. - * For example, the '#' character is replaced by a localized digit. Often the - * replacement character is the same as the pattern character; in the U.S. locale, - * the ',' grouping character is replaced by ','. However, the replacement is - * still happening, and if the symbols are modified, the grouping character - * changes. Some special characters affect the behavior of the formatter by - * their presence; for example, if the percent character is seen, then the - * value is multiplied by 100 before being displayed. - * - *
To insert a special character in a pattern as a literal, that is, without - * any special meaning, the character must be quoted. There are some exceptions to - * this which are noted below. - * - *
The characters listed here are used in non-localized patterns. Localized - * patterns use the corresponding characters taken from this formatter's - * {@link DecimalFormatSymbols} object instead, and these characters lose - * their special status. Two exceptions are the currency sign and quote, which - * are not localized. - * - *
- *- * - *- *
- *- * Symbol - * Location - * Localized? - * Meaning - * - * 0
- *Number - * Yes - * Digit - * - * 1-9
- *Number - * Yes - * '1' through '9' indicate rounding. - * - * - * @
- *Number - * No - * Significant digit - * - * #
- *Number - * Yes - * Digit, zero shows as absent - * - * .
- *Number - * Yes - * Decimal separator or monetary decimal separator - * - * -
- *Number - * Yes - * Minus sign - * - * ,
- *Number - * Yes - * Grouping separator - * - * E
- *Number - * Yes - * Separates mantissa and exponent in scientific notation. - * Need not be quoted in prefix or suffix. - * - * +
- *Exponent - * Yes - * Prefix positive exponents with localized plus sign. - * Need not be quoted in prefix or suffix. - * - * ;
- *Subpattern boundary - * Yes - * Separates positive and negative subpatterns - * - * %
- *Prefix or suffix - * Yes - * Multiply by 100 and show as percentage - * - * \u2030
- *Prefix or suffix - * Yes - * Multiply by 1000 and show as per mille - * - * ¤
(\u00A4
) - *Prefix or suffix - * No - * Currency sign, replaced by currency symbol. If - * doubled, replaced by international currency symbol. - * If tripled, replaced by currency plural names, for example, - * "US dollar" or "US dollars" for America. - * If present in a pattern, the monetary decimal separator - * is used instead of the decimal separator. - * - * '
- *Prefix or suffix - * No - * Used to quote special characters in a prefix or suffix, - * for example, "'#'#"
formats 123 to - *"#123"
. To create a single quote - * itself, use two in a row:"# o''clock"
. - *- * *
- *Prefix or suffix boundary - * Yes - * Pad escape, precedes pad character - *
A 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. That means that
- * "#,##0.0#;(#)" has precisely the same result as "#,##0.0#;(#,##0.0#)".
- *
- *
The prefixes, suffixes, and various symbols used for infinity, digits, - * thousands separators, decimal separators, etc. may be set to arbitrary - * values, and they will appear properly during formatting. However, care must - * be taken that the symbols and strings do not conflict, or parsing will be - * unreliable. For example, either the positive and negative prefixes or the - * suffixes must be distinct for {@link #parse} to be able - * to distinguish positive from negative values. Another example is that the - * decimal separator and thousands separator should be distinct characters, or - * parsing will be impossible. - * - *
The grouping separator is a character that separates clusters of - * integer digits to make large numbers more legible. It commonly used for - * thousands, but in some locales it separates ten-thousands. The grouping - * size is the number of digits between the grouping separators, such as 3 - * for "100,000,000" or 4 for "1 0000 0000". There are actually two different - * grouping sizes: One used for the least significant integer digits, the - * primary grouping size, and one used for all others, the - * secondary grouping size. In most locales these are the same, but - * sometimes they are different. For example, if the primary grouping interval - * is 3, and the secondary is 2, then this corresponds to the pattern - * "#,##,##0", and the number 123456789 is formatted as "12,34,56,789". If a - * pattern contains multiple grouping separators, the interval between the last - * one and the end of the integer defines the primary grouping size, and the - * interval between the last two defines the secondary grouping size. All others - * are ignored, so "#,##,###,####" == "###,###,####" == "##,#,###,####". - * - *
Illegal patterns, such as "#.#.#" or "#.###,###", will cause
- * DecimalFormat
to throw an {@link IllegalArgumentException}
- * with a message that describes the problem.
- *
- *
- * pattern := subpattern (';' subpattern)? - * subpattern := prefix? number exponent? suffix? - * number := (integer ('.' fraction)?) | sigDigits - * prefix := '\u0000'..'\uFFFD' - specialCharacters - * suffix := '\u0000'..'\uFFFD' - specialCharacters - * integer := '#'* '0'* '0' - * fraction := '0'* '#'* - * sigDigits := '#'* '@' '@'* '#'* - * exponent := 'E' '+'? '0'* '0' - * padSpec := '*' padChar - * padChar := '\u0000'..'\uFFFD' - quote - * - * Notation: - * X* 0 or more instances of X - * X? 0 or 1 instances of X - * X|Y either X or Y - * C..D any character from C up to D, inclusive - * S-T characters in S, except those in T - *- * The first subpattern is for positive numbers. The second (optional) - * subpattern is for negative numbers. - * - *
Not indicated in the BNF syntax above: - * - *
padSpec
may appear before the prefix,
- * after the prefix, before the suffix, after the suffix, or not at all.
- *
- * DecimalFormat
parses all Unicode characters that represent
- * decimal digits, as defined by {@link UCharacter#digit}. In addition,
- * 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 output.
- *
- *
During parsing, grouping separators are ignored. - * - *
For currency parsing, the formatter is able to parse every currency - * style formats no matter which style the formatter is constructed with. - * For example, a formatter instance gotten from - * NumberFormat.getInstance(ULocale, NumberFormat.CURRENCYSTYLE) can parse - * formats such as "USD1.00" and "3.00 US dollars". - * - *
If {@link #parse(String, ParsePosition)} fails to parse
- * a string, it returns null
and leaves the parse position
- * unchanged. The convenience method {@link #parse(String)}
- * indicates parse failure by throwing a {@link java.text.ParseException}.
- *
- *
Formatting is guided by several parameters, all of which can be - * specified either using a pattern or using the API. The following - * description applies to formats that do not use scientific - * notation or significant digits. - * - *
Special Values - * - *
NaN
is represented as a single character, typically
- * \uFFFD
. This character is determined by the
- * {@link DecimalFormatSymbols} object. This is the only value for which
- * the prefixes and suffixes are not used.
- *
- *
Infinity is represented as a single character, typically
- * \u221E
, with the positive or negative prefixes and suffixes
- * applied. The infinity character is determined by the
- * {@link DecimalFormatSymbols} object.
- *
- * Scientific Notation
- *
- *
Numbers in scientific notation are expressed as the product of a mantissa
- * and a power of ten, for example, 1234 can be expressed as 1.234 x 103. The
- * mantissa is typically in the half-open interval [1.0, 10.0) or sometimes [0.0, 1.0),
- * but it need not be. DecimalFormat
supports arbitrary mantissas.
- * 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".
- *
- *
DecimalFormat
has two ways of controlling how many
- * digits are shows: (a) significant digits counts, or (b) integer and
- * fraction digit counts. Integer and fraction digit counts are
- * described above. When a formatter is using 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 however many integer and fraction
- * digits are required to display the specified number of significant
- * digits. Examples:
- *
- * - *- * - *- *
- *- * Pattern - * Minimum significant digits - * Maximum significant digits - * Number - * Output of format() - * - * @@@
- *3 - * 3 - * 12345 - * 12300
- *- * @@@
- *3 - * 3 - * 0.12345 - * 0.123
- *- * @@##
- *2 - * 4 - * 3.14159 - * 3.142
- *- * @@##
- *2 - * 4 - * 1.23004 - * 1.23
- *
'@'
and '#'
- * characters. The minimum number of significant digits is the number
- * of '@'
characters. The maximum number of significant
- * digits is the number of '@'
characters plus the number
- * of '#'
characters following on the right. For
- * example, the pattern "@@@"
indicates exactly 3
- * significant digits. The pattern "@##"
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
- * "@##"
formats the number 0.1203 as
- * "0.12"
.
- *
- * '0'
pattern character.
- * Patterns such as "@00"
or "@.###"
are
- * disallowed.
- *
- * '#'
characters may be prepended to
- * the left of the leftmost '@'
character. These have no
- * effect on the minimum and maximum significant digits counts, but
- * may be used to position grouping separators. For example,
- * "#,#@#"
indicates a minimum of one significant digits,
- * a maximum of two significant digits, and a grouping size of three.
- *
- * '@'
pattern character. Alternatively,
- * call {@link #setSignificantDigitsUsed setSignificantDigitsUsed(true)}.
- *
- * '@'
pattern
- * character. Alternatively, call {@link #setSignificantDigitsUsed
- * setSignificantDigitsUsed(false)}.
- *
- * getMinimumSignificantDigits() - 1
, and a maximum fraction digit
- * count of getMaximumSignificantDigits() - 1
. For example, the
- * pattern "@@###E0"
is equivalent to "0.0###E0"
.
- *
- * DecimalFormat
supports padding the result of
- * {@link #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, and
- * can be localized using {@link DecimalFormatSymbols#setPadEscape}. For
- * example, "$*x#,##0.00"
formats 123 to "$xx123.00"
,
- * and 1234 to "$1,234.00"
.
- *
- *
"* #0 o''clock"
, the format width is 10.
- *
- * char
s).
- *
- * 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
- * escapes the following character. If there is no character after
- * the pad escape, then the pattern is illegal.
- *
- * - * Rounding - * - *
DecimalFormat
supports rounding to a specific increment. For
- * example, 1230 rounded to the nearest 50 is 1250. 1.234 rounded to the
- * nearest 0.65 is 1.3. The rounding increment may be specified through the API
- * or in a pattern. To specify a rounding increment in a pattern, include the
- * increment in the pattern itself. "#,#50" specifies a rounding increment of
- * 50. "#,##0.05" specifies a rounding increment of 0.05.
- *
- *
DecimalFormat
objects are not synchronized. Multiple
- * threads should not access one formatter concurrently.
- *
- * @see java.text.Format
- * @see NumberFormat
- * @author Mark Davis
- * @author Alan Liu
- * @stable ICU 2.0
- */
-public class DecimalFormat extends NumberFormat {
-
- /**
- * Create a DecimalFormat using the default pattern and symbols
- * for the default locale. This is a convenient way to obtain a
- * DecimalFormat when internationalization is not the main concern.
- *
- * To obtain standard formats for a given locale, use the factory methods - * on NumberFormat such as getNumberInstance. These factories will - * return the most appropriate sub-class of NumberFormat for a given - * locale. - * @see NumberFormat#getInstance - * @see NumberFormat#getNumberInstance - * @see NumberFormat#getCurrencyInstance - * @see NumberFormat#getPercentInstance - * @stable ICU 2.0 - */ - public DecimalFormat() { - // [NEW] - ULocale def = ULocale.getDefault(); - String pattern = getPattern(def, 0); - // Always applyPattern after the symbols are set - this.symbols = new DecimalFormatSymbols(def); - setCurrency(Currency.getInstance(def)); - applyPatternWithoutExpandAffix(pattern, false); - if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) { - currencyPluralInfo = new CurrencyPluralInfo(def); - // the exact pattern is not known until the plural count is known. - // so, no need to expand affix now. - } else { - expandAffixAdjustWidth(null); - } - } - - - /** - * Create a DecimalFormat from the given pattern and the symbols - * for the default locale. This is a convenient way to obtain a - * DecimalFormat when internationalization is not the main concern. - *
- * To obtain standard formats for a given locale, use the factory methods - * on NumberFormat such as getNumberInstance. These factories will - * return the most appropriate sub-class of NumberFormat for a given - * locale. - * @param pattern A non-localized pattern string. - * @exception IllegalArgumentException if the given pattern is invalid. - * @see NumberFormat#getInstance - * @see NumberFormat#getNumberInstance - * @see NumberFormat#getCurrencyInstance - * @see NumberFormat#getPercentInstance - * @stable ICU 2.0 - */ - public DecimalFormat(String pattern) { - // Always applyPattern after the symbols are set - ULocale def = ULocale.getDefault(); - this.symbols = new DecimalFormatSymbols(def); - setCurrency(Currency.getInstance(def)); - applyPatternWithoutExpandAffix( pattern, false ); - if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) { - currencyPluralInfo = new CurrencyPluralInfo(def); - } else { - expandAffixAdjustWidth(null); - } - } - - - /** - * Create a DecimalFormat from the given pattern and symbols. - * Use this constructor when you need to completely customize the - * behavior of the format. - *
- * To obtain standard formats for a given - * locale, use the factory methods on NumberFormat such as - * getInstance or getCurrencyInstance. If you need only minor adjustments - * to a standard format, you can modify the format returned by - * a NumberFormat factory method. - * @param pattern a non-localized pattern string - * @param symbols the set of symbols to be used - * @exception IllegalArgumentException if the given pattern is invalid - * @see NumberFormat#getInstance - * @see NumberFormat#getNumberInstance - * @see NumberFormat#getCurrencyInstance - * @see NumberFormat#getPercentInstance - * @see DecimalFormatSymbols - * @stable ICU 2.0 - */ - public DecimalFormat(String pattern, DecimalFormatSymbols symbols) { - createFromPatternAndSymbols(pattern, symbols); - } - - private void createFromPatternAndSymbols(String pattern, DecimalFormatSymbols inputSymbols) { - // Always applyPattern after the symbols are set - symbols = (DecimalFormatSymbols) inputSymbols.clone(); - setCurrencyForSymbols(); - applyPatternWithoutExpandAffix(pattern, false); - if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) { - currencyPluralInfo = new CurrencyPluralInfo(symbols.getLocale()); - } else { - expandAffixAdjustWidth(null); - } - } - - - /** - * Create a DecimalFormat from the given pattern, symbols, - * information used for currency plural format, and format style. - * Use this constructor when you need to completely customize the - * behavior of the format. - *
- * To obtain standard formats for a given - * locale, use the factory methods on NumberFormat such as - * getInstance or getCurrencyInstance. - *
- * If you need only minor adjustments to a standard format, - * you can modify the format returned by - * a NumberFormat factory method using the setters. - *
- * If you want to completely customize a decimal format, - * using your own DecimalFormatSymbols (such as group separators) and - * your own information for currency plural formatting (such as - * plural rule and currency plural patterns), you can use this constructor. - *
- * @param pattern a non-localized pattern string
- * @param symbols the set of symbols to be used
- * @param infoInput the information used for currency plural format,
- * including currency plural patterns and plural rules.
- * @param style the decimal formatting style,
- * it is one of the following values:
- * NumberFormat.NUMBERSTYLE;
- * NumberFormat.CURRENCYSTYLE;
- * NumberFormat.PERCENTSTYLE;
- * NumberFormat.SCIENTIFICSTYLE;
- * NumberFormat.INTEGERSTYLE;
- * NumberFormat.ISOCURRENCYSTYLE;
- * NumberFormat.PLURALCURRENCYSTYLE;
- * @draft ICU 4.2
- * @provisional This API might change or be removed in a future release.
- */
- public DecimalFormat(String pattern, DecimalFormatSymbols symbols,
- CurrencyPluralInfo infoInput,
- int style) {
- CurrencyPluralInfo info = infoInput;
- if (style == NumberFormat.PLURALCURRENCYSTYLE) {
- info = (CurrencyPluralInfo)infoInput.clone();
- }
- create(pattern, symbols, info, style);
- }
-
-
- private void create(String pattern, DecimalFormatSymbols inputSymbols,
- CurrencyPluralInfo info,
- int inputStyle) {
- if (inputStyle != NumberFormat.PLURALCURRENCYSTYLE) {
- createFromPatternAndSymbols(pattern, inputSymbols);
- } else {
- // Always applyPattern after the symbols are set
- symbols = (DecimalFormatSymbols) inputSymbols.clone();
- currencyPluralInfo = info;
- // the pattern used in format is not fixed until formatting,
- // in which, the number is known and
- // will be used to pick the right pattern based on plural count.
- // Here, set the pattern as the pattern of plural count == "other".
- // For most locale, the patterns are probably the same for all
- // plural count. If not, the right pattern need to be re-applied
- // during format.
- String currencyPluralPatternForOther = currencyPluralInfo.getCurrencyPluralPattern("other");
- applyPatternWithoutExpandAffix(currencyPluralPatternForOther,false);
- setCurrencyForSymbols();
- }
- style = inputStyle;
- }
-
-
- /*
- * Create a DecimalFormat for currency plural format
- * from the given pattern, symbols, and style.
- */
- DecimalFormat(String pattern, DecimalFormatSymbols inputSymbols, int style) {
- CurrencyPluralInfo info = null;
- if (style == NumberFormat.PLURALCURRENCYSTYLE) {
- info = new CurrencyPluralInfo(inputSymbols.getLocale());
- }
- create(pattern, inputSymbols, info, style);
- }
-
- /**
- * @stable ICU 2.0
- */
- public StringBuffer format(double number, StringBuffer result,
- FieldPosition fieldPosition) {
- return format(number, result, fieldPosition, false);
- }
-
- // [Spark/CDL] The actual method to format number. If boolean value
- // parseAttr == true, then attribute information will be recorded.
- private StringBuffer format(double number, StringBuffer result,
- FieldPosition fieldPosition, boolean parseAttr)
- {
- fieldPosition.setBeginIndex(0);
- fieldPosition.setEndIndex(0);
-
- if (Double.isNaN(number))
- {
- if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
- fieldPosition.setBeginIndex(result.length());
- }
-//#if defined(FOUNDATION10) || defined(J2SE13)
-//#else
- else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
- fieldPosition.setBeginIndex(result.length());
- }
-//#endif
-
- result.append(symbols.getNaN());
- // [Spark/CDL] Add attribute for NaN here.
- // result.append(symbols.getNaN());
-//#if defined(FOUNDATION10) || defined(J2SE13)
-//#else
- if (parseAttr) {
- addAttribute(Field.INTEGER, result.length()
- - symbols.getNaN().length(), result.length());
- }
-//#endif
- if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
- fieldPosition.setEndIndex(result.length());
- }
-//#if defined(FOUNDATION10) || defined(J2SE13)
-//#else
- else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
- fieldPosition.setEndIndex(result.length());
- }
-//#endif
-
- addPadding(result, fieldPosition, 0, 0);
- return result;
- }
-
- // Do this BEFORE checking to see if value is infinite or negative!
- if (multiplier != 1) number *= multiplier;
-
- /* Detecting whether a double is negative is easy with the exception of
- * the value -0.0. This is a double which has a zero mantissa (and
- * exponent), but a negative sign bit. It is semantically distinct from
- * a zero with a positive sign bit, and this distinction is important
- * to certain kinds of computations. However, it's a little tricky to
- * detect, since (-0.0 == 0.0) and !(-0.0 < 0.0). How then, you may
- * ask, does it behave distinctly from +0.0? Well, 1/(-0.0) ==
- * -Infinity. Proper detection of -0.0 is needed to deal with the
- * issues raised by bugs 4106658, 4106667, and 4147706. Liu 7/6/98.
- */
- boolean isNegative = (number < 0.0) || (number == 0.0 && 1/number < 0.0);
- if (isNegative) number = -number;
-
- // Apply rounding after multiplier
- if (roundingDouble > 0.0) {
- // number = roundingDouble
- // * round(number / roundingDouble, roundingMode, isNegative);
- double newNumber = round(number, roundingDouble, roundingDoubleReciprocal, roundingMode, isNegative);
- if (newNumber == 0.0 && number != newNumber) isNegative = false; // if we touched it, then make zero be zero.
- number = newNumber;
- }
-
- if (Double.isInfinite(number))
- {
- int prefixLen = appendAffix(result, isNegative, true, parseAttr);
-
- if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
- fieldPosition.setBeginIndex(result.length());
- }
-//#if defined(FOUNDATION10) || defined(J2SE13)
-//#else
- else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
- fieldPosition.setBeginIndex(result.length());
- }
-//#endif
-
- // [Spark/CDL] Add attribute for infinity here.
- result.append(symbols.getInfinity());
-//#if defined(FOUNDATION10) || defined(J2SE13)
-//#else
- if (parseAttr) {
- addAttribute(Field.INTEGER, result.length()
- - symbols.getInfinity().length(), result.length());
- }
-//#endif
- if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
- fieldPosition.setEndIndex(result.length());
- }
-//#if defined(FOUNDATION10) || defined(J2SE13)
-//#else
- else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
- fieldPosition.setEndIndex(result.length());
- }
-//#endif
-
- int suffixLen = appendAffix(result, isNegative, false, parseAttr);
-
- addPadding(result, fieldPosition, prefixLen, suffixLen);
- return result;
- }
-
- // At this point we are guaranteed a nonnegative finite
- // number.
- synchronized(digitList) {
- digitList.set(number, precision(false),
- !useExponentialNotation && !areSignificantDigitsUsed());
- return subformat(number, result, fieldPosition, isNegative, false,
- parseAttr);
- }
- }
-
- // [NEW]
- /**
- * Round a double value to the nearest multiple of the given
- * rounding increment, according to the given mode. This is
- * equivalent to rounding value/roundingInc to the nearest
- * integer, according to the given mode, and returning that
- * integer * roundingInc.
- * Note this is changed from the version in 2.4, since division of doubles
- * have inaccuracies. jitterbug 1871.
- * @param number the absolute value of the number to be rounded
- * @param roundingInc the rounding increment
- * @param roundingIncReciprocal if non-zero, is the
- * @param mode a BigDecimal rounding mode
- * @param isNegative true if the number to be rounded is negative
- * @return the absolute value of the rounded result
- */
- private static double round(double number, double roundingInc,
- double roundingIncReciprocal, int mode, boolean isNegative) {
-
- double div = roundingIncReciprocal == 0.0
- ? number / roundingInc
- : number * roundingIncReciprocal;
-
- // do the absolute cases first
-
- switch (mode) {
- case BigDecimal.ROUND_CEILING:
- div = (isNegative ? Math.floor(div + epsilon) : Math.ceil(div - epsilon));
- break;
- case BigDecimal.ROUND_FLOOR:
- div = (isNegative ? Math.ceil(div - epsilon) : Math.floor(div + epsilon));
- break;
- case BigDecimal.ROUND_DOWN:
- div = (Math.floor(div + epsilon));
- break;
- case BigDecimal.ROUND_UP:
- div = (Math.ceil(div - epsilon));
- break;
- case BigDecimal.ROUND_UNNECESSARY:
- if (div != Math.floor(div)) {
- throw new ArithmeticException("Rounding necessary");
- }
- return number;
- default:
-
- // Handle complex cases, where the choice depends on the closer value.
-
- // We figure out the distances to the two possible values, ceiling and floor.
- // We then go for the diff that is smaller.
- // Only if they are equal does the mode matter.
-
- double ceil = Math.ceil(div);
- double ceildiff = ceil - div; // (ceil * roundingInc) - number;
- double floor = Math.floor(div);
- double floordiff = div - floor; // number - (floor * roundingInc);
-
- // Note that the diff values were those mapped back to the "normal" space
- // by using the roundingInc. I don't have access to the original author of the code
- // but suspect that that was to produce better result in edge cases because of machine
- // precision, rather than simply using the difference between, say, ceil and div.
- // However, it didn't work in all cases. Am trying instead using an epsilon value.
-
- switch (mode) {
- case BigDecimal.ROUND_HALF_EVEN:
- // We should be able to just return Math.rint(a), but this
- // doesn't work in some VMs.
- // if one is smaller than the other, take the corresponding side
- if (floordiff + epsilon < ceildiff) {
- div = floor;
- } else if (ceildiff + epsilon < floordiff) {
- div = ceil;
- } else { // they are equal, so we want to round to whichever is even
- double testFloor = floor / 2;
- div = (testFloor == Math.floor(testFloor)) ? floor : ceil;
- }
- break;
- case BigDecimal.ROUND_HALF_DOWN:
- div = ((floordiff <= ceildiff + epsilon) ? floor : ceil);
- break;
- case BigDecimal.ROUND_HALF_UP:
- div = ((ceildiff <= floordiff + epsilon) ? ceil : floor);
- break;
- default:
- throw new IllegalArgumentException("Invalid rounding mode: " + mode);
- }
- }
- number = roundingIncReciprocal == 0.0
- ? div * roundingInc
- : div / roundingIncReciprocal;
- return number;
- }
- private static double epsilon = 0.00000000001;
-
- /**
- * @stable ICU 2.0
- */
- // [Spark/CDL] Delegate to format_long_StringBuffer_FieldPosition_boolean
- public StringBuffer format(long number, StringBuffer result,
- FieldPosition fieldPosition) {
- return format(number, result, fieldPosition, false);
- }
-
- private StringBuffer format(long number, StringBuffer result,
- FieldPosition fieldPosition, boolean parseAttr)
- {
- fieldPosition.setBeginIndex(0);
- fieldPosition.setEndIndex(0);
-
- // If we are to do rounding, we need to move into the BigDecimal
- // domain in order to do divide/multiply correctly.
- // [NEW]
- if (roundingIncrementICU != null) {
- return format(BigDecimal.valueOf(number), result, fieldPosition);
- }
-
- boolean isNegative = (number < 0);
- if (isNegative) number = -number;
-
- // In general, long values always represent real finite numbers, so
- // we don't have to check for +/- Infinity or NaN. However, there
- // is one case we have to be careful of: The multiplier can push
- // a number near MIN_VALUE or MAX_VALUE outside the legal range. We
- // check for this before multiplying, and if it happens we use BigInteger
- // instead.
- // [NEW]
- if (multiplier != 1) {
- boolean tooBig = false;
- if (number < 0) { // This can only happen if number == Long.MIN_VALUE
- long cutoff = Long.MIN_VALUE / multiplier;
- tooBig = (number <= cutoff); // number == cutoff can only happen if multiplier == -1
- } else {
- long cutoff = Long.MAX_VALUE / multiplier;
- tooBig = (number > cutoff);
- }
- if (tooBig) {
- // [Spark/CDL] Use
- // format_BigInteger_StringBuffer_FieldPosition_boolean instead
- // parseAttr is used to judge whether to synthesize attributes.
- return format(
- BigInteger.valueOf(isNegative ? -number : number),
- result, fieldPosition, parseAttr);
- }
- }
-
- number *= multiplier;
- synchronized(digitList) {
- digitList.set(number, precision(true));
- return subformat(number, result, fieldPosition, isNegative, true, parseAttr);
- }
- }
-
- // [NEW]
- /**
- * Format a BigInteger number.
- *
- * @stable ICU 2.0
- */
- public StringBuffer format(BigInteger number, StringBuffer result,
- FieldPosition fieldPosition) {
- return format(number, result, fieldPosition, false);
- }
-
- // [Spark/CDL]
- private StringBuffer format(BigInteger number, StringBuffer result,
- FieldPosition fieldPosition, boolean parseAttr) {
- // If we are to do rounding, we need to move into the BigDecimal
- // domain in order to do divide/multiply correctly.
- if (roundingIncrementICU != null) {
- return format(new BigDecimal(number), result, fieldPosition);
- }
-
- if (multiplier != 1) {
- number = number.multiply(BigInteger.valueOf(multiplier));
- }
-
- // At this point we are guaranteed a nonnegative finite
- // number.
- synchronized(digitList) {
- digitList.set(number, precision(true));
- return subformat(number.intValue(), result, fieldPosition, number.signum() < 0, true, parseAttr);
- }
- }
-
-//#if defined(FOUNDATION10)
-//#else
- // [NEW]
- /**
- * Format a BigDecimal number.
- * @stable ICU 2.0
- */
- public StringBuffer format(java.math.BigDecimal number, StringBuffer result,
- FieldPosition fieldPosition) {
- return format(number, result, fieldPosition, false);
- }
-
- private StringBuffer format(java.math.BigDecimal number,
- StringBuffer result, FieldPosition fieldPosition, boolean parseAttr) {
- if (multiplier != 1) {
- number = number.multiply(java.math.BigDecimal.valueOf(multiplier));
- }
-
- if (roundingIncrement != null) {
- number = number.divide(roundingIncrement, 0, roundingMode)
- .multiply(roundingIncrement);
- }
-
- synchronized(digitList) {
- digitList.set(number, precision(false),
- !useExponentialNotation && !areSignificantDigitsUsed());
- return subformat(number.doubleValue(), result, fieldPosition, number.signum() < 0, false, parseAttr);
- }
- }
-//#endif
-
- // [NEW]
- /**
- * Format a BigDecimal number.
- * @stable ICU 2.0
- */
- public StringBuffer format(BigDecimal number, StringBuffer result,
- FieldPosition fieldPosition) {
- /* This method is just a copy of the corresponding java.math.BigDecimal
- * method for now. It isn't very efficient since it must create a
- * conversion object to do math on the rounding increment. In the
- * future we may try to clean this up, or even better, limit our support
- * to just one flavor of BigDecimal.
- */
- if (multiplier != 1) {
- number = number.multiply(BigDecimal.valueOf(multiplier), mathContext);
- }
-
- if (roundingIncrementICU != null) {
- number = number.divide(roundingIncrementICU, 0, roundingMode)
- .multiply(roundingIncrementICU, mathContext);
- }
-
- synchronized(digitList) {
- digitList.set(number, precision(false),
- !useExponentialNotation && !areSignificantDigitsUsed());
- return subformat(number.doubleValue(), result, fieldPosition, number.signum() < 0, false, false);
- }
- }
-
- /**
- * Return true if a grouping separator belongs at the given
- * position, based on whether grouping is in use and the values of
- * the primary and secondary grouping interval.
- * @param pos the number of integer digits to the right of
- * the current position. Zero indicates the position after the
- * rightmost integer digit.
- * @return true if a grouping character belongs at the current
- * position.
- */
- private boolean isGroupingPosition(int pos) {
- boolean result = false;
- if (isGroupingUsed() && (pos > 0) && (groupingSize > 0)) {
- if ((groupingSize2 > 0) && (pos > groupingSize)) {
- result = ((pos - groupingSize) % groupingSize2) == 0;
- } else {
- result = pos % groupingSize == 0;
- }
- }
- return result;
- }
-
- /**
- * Return the number of fraction digits to display, or the total
- * number of digits for significant digit formats and exponential
- * formats.
- */
- private int precision(boolean isIntegral) {
- if (areSignificantDigitsUsed()) {
- return getMaximumSignificantDigits();
- } else if (useExponentialNotation) {
- return getMinimumIntegerDigits() + getMaximumFractionDigits();
- } else {
- return isIntegral ? 0 : getMaximumFractionDigits();
- }
- }
-
- private StringBuffer subformat(int number, StringBuffer result,
- FieldPosition fieldPosition,
- boolean isNegative, boolean isInteger,
- boolean parseAttr) {
- if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
- return subformat(currencyPluralInfo.select(number), result, fieldPosition,
- isNegative, isInteger, parseAttr);
- } else {
- return subformat(result, fieldPosition, isNegative, isInteger, parseAttr);
- }
- }
-
- private StringBuffer subformat(double number, StringBuffer result,
- FieldPosition fieldPosition,
- boolean isNegative, boolean isInteger,
- boolean parseAttr) {
- if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
- return subformat(currencyPluralInfo.select(number), result, fieldPosition,
- isNegative, isInteger, parseAttr);
- } else {
- return subformat(result, fieldPosition, isNegative, isInteger, parseAttr);
- }
- }
-
- private StringBuffer subformat(String pluralCount,
- StringBuffer result,
- FieldPosition fieldPosition, boolean isNegative, boolean isInteger,
- boolean parseAttr)
- {
- // There are 2 ways to activate currency plural format:
- // by applying a pattern with 3 currency sign directly,
- // or by instantiate a decimal formatter using PLURALCURRENCYSTYLE.
- // For both cases, the number of currency sign in the pattern is 3.
- // Even if the number of currency sign in the pattern is 3,
- // it does not mean we need to reset the pattern.
- // For 1st case, we do not need to reset pattern.
- // For 2nd case, we might need to reset pattern,
- // if the default pattern (corresponding to plural count 'other')
- // we use is different from the pattern based on 'pluralCount'.
- //
- // style is only valid when decimal formatter is constructed through
- // DecimalFormat(pattern, symbol, style)
- if (style == NumberFormat.PLURALCURRENCYSTYLE) {
- // May need to reset pattern if the style is PLURALCURRENCYSTYLE.
- String currencyPluralPattern = currencyPluralInfo.getCurrencyPluralPattern(pluralCount);
- if (formatPattern.equals(currencyPluralPattern) == false) {
- applyPatternWithoutExpandAffix(currencyPluralPattern, false);
- }
- }
- // Expand the affix to the right name according to
- // the plural rule.
- // This is only used for currency plural formatting.
- // Currency plural name is not a fixed static one,
- // it is a dynamic name based on the currency plural count.
- // So, the affixes need to be expanded here.
- // For other cases, the affix is a static one based on pattern alone,
- // and it is already expanded during applying pattern,
- // or setDecimalFormatSymbols, or setCurrency.
- expandAffixAdjustWidth(pluralCount);
- return subformat(result, fieldPosition, isNegative, isInteger, parseAttr);
- }
-
- /**
- * Complete the formatting of a finite number. On entry, the digitList must
- * be filled in with the correct digits.
- */
- private StringBuffer subformat(StringBuffer result,
- FieldPosition fieldPosition, boolean isNegative, boolean isInteger,
- boolean parseAttr)
- {
- // NOTE: This isn't required anymore because DigitList takes care of this.
- //
- // // The negative of the exponent represents the number of leading
- // // zeros between the decimal and the first non-zero digit, for
- // // a value < 0.1 (e.g., for 0.00123, -fExponent == 2). If this
- // // is more than the maximum fraction digits, then we have an underflow
- // // for the printed representation. We recognize this here and set
- // // the DigitList representation to zero in this situation.
- //
- // if (-digitList.decimalAt >= getMaximumFractionDigits())
- // {
- // digitList.count = 0;
- // }
-
- int i;
- char zero = symbols.getZeroDigit();
- int zeroDelta = zero - '0'; // '0' is the DigitList representation of zero
- char grouping = currencySignCount > 0 ?
- symbols.getMonetaryGroupingSeparator() :
- symbols.getGroupingSeparator();
- char decimal = currencySignCount > 0 ?
- symbols.getMonetaryDecimalSeparator() :
- symbols.getDecimalSeparator();
- boolean useSigDig = areSignificantDigitsUsed();
- int maxIntDig = getMaximumIntegerDigits();
- int minIntDig = getMinimumIntegerDigits();
-
- /* Per bug 4147706, DecimalFormat must respect the sign of numbers which
- * format as zero. This allows sensible computations and preserves
- * relations such as signum(1/x) = signum(x), where x is +Infinity or
- * -Infinity. Prior to this fix, we always formatted zero values as if
- * they were positive. Liu 7/6/98.
- */
- if (digitList.isZero())
- {
- digitList.decimalAt = 0; // Normalize
- }
-
- int prefixLen = appendAffix(result, isNegative, true, parseAttr);
-
- if (useExponentialNotation)
- {
- // Record field information for caller.
- if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
- fieldPosition.setBeginIndex(result.length());
- fieldPosition.setEndIndex(-1);
- } else if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
- fieldPosition.setBeginIndex(-1);
- }
-//#if defined(FOUNDATION10) || defined(J2SE13)
-//#else
- else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
- fieldPosition.setBeginIndex(result.length());
- fieldPosition.setEndIndex(-1);
- } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
- fieldPosition.setBeginIndex(-1);
- }
-//#endif
-
-//#if defined(FOUNDATION10) || defined(J2SE13)
-//#else
- // [Spark/CDL]
- // the begin index of integer part
- // the end index of integer part
- // the begin index of fractional part
- int intBegin = result.length();
- int intEnd = -1;
- int fracBegin = -1;
-//#endif
-
- int minFracDig = 0;
- if (useSigDig) {
- maxIntDig = minIntDig = 1;
- minFracDig = getMinimumSignificantDigits() - 1;
- } else {
- minFracDig = getMinimumFractionDigits();
- if (maxIntDig > MAX_SCIENTIFIC_INTEGER_DIGITS) {
- maxIntDig = 1;
- if (maxIntDig < minIntDig) {
- maxIntDig = minIntDig;
- }
- }
- if (maxIntDig > minIntDig) {
- minIntDig = 1;
- }
- }
-
- // Minimum integer digits are handled in exponential format by
- // adjusting the exponent. For example, 0.01234 with 3 minimum
- // integer digits is "123.4E-4".
-
- // Maximum integer digits are interpreted as indicating the
- // repeating range. This is useful for engineering notation, in
- // which the exponent is restricted to a multiple of 3. For
- // example, 0.01234 with 3 maximum integer digits is "12.34e-3".
- // If maximum integer digits are defined and are larger than
- // minimum integer digits, then minimum integer digits are
- // ignored.
-
- int exponent = digitList.decimalAt;
- if (maxIntDig > 1 && maxIntDig != minIntDig) {
- // A exponent increment is defined; adjust to it.
- exponent = (exponent > 0) ? (exponent - 1) / maxIntDig
- : (exponent / maxIntDig) - 1;
- exponent *= maxIntDig;
- } else {
- // No exponent increment is defined; use minimum integer digits.
- // If none is specified, as in "#E0", generate 1 integer digit.
- exponent -= (minIntDig > 0 || minFracDig > 0)
- ? minIntDig : 1;
- }
-
- // We now output a minimum number of digits, and more if there
- // are more digits, up to the maximum number of digits. We
- // place the decimal point after the "integer" digits, which
- // are the first (decimalAt - exponent) digits.
- int minimumDigits = minIntDig + minFracDig;
- // The number of integer digits is handled specially if the number
- // is zero, since then there may be no digits.
- int integerDigits = digitList.isZero() ? minIntDig :
- digitList.decimalAt - exponent;
- int totalDigits = digitList.count;
- if (minimumDigits > totalDigits) totalDigits = minimumDigits;
- if (integerDigits > totalDigits) totalDigits = integerDigits;
-
- for (i=0; i Examples: +123, $123, sFr123
- * @stable ICU 2.0
- */
- public String getPositivePrefix () {
- return positivePrefix;
- }
-
- /**
- * Set the positive prefix.
- * Examples: +123, $123, sFr123
- * @stable ICU 2.0
- */
- public void setPositivePrefix (String newValue) {
- positivePrefix = newValue;
- posPrefixPattern = null;
- }
-
- /**
- * Get the negative prefix.
- * Examples: -123, ($123) (with negative suffix), sFr-123
- * @stable ICU 2.0
- */
- public String getNegativePrefix () {
- return negativePrefix;
- }
-
- /**
- * Set the negative prefix.
- * Examples: -123, ($123) (with negative suffix), sFr-123
- * @stable ICU 2.0
- */
- public void setNegativePrefix (String newValue) {
- negativePrefix = newValue;
- negPrefixPattern = null;
- }
-
- /**
- * Get the positive suffix.
- * Example: 123%
- * @stable ICU 2.0
- */
- public String getPositiveSuffix () {
- return positiveSuffix;
- }
-
- /**
- * Set the positive suffix.
- * Example: 123%
- * @stable ICU 2.0
- */
- public void setPositiveSuffix (String newValue) {
- positiveSuffix = newValue;
- posSuffixPattern = null;
- }
-
- /**
- * Get the negative suffix.
- * Examples: -123%, ($123) (with positive suffixes)
- * @stable ICU 2.0
- */
- public String getNegativeSuffix () {
- return negativeSuffix;
- }
-
- /**
- * Set the positive suffix.
- * Examples: 123%
- * @stable ICU 2.0
- */
- public void setNegativeSuffix (String newValue) {
- negativeSuffix = newValue;
- negSuffixPattern = null;
- }
-
- /**
- * Get the multiplier for use in percent, permill, etc.
- * For a percentage, set the suffixes to have "%" and the multiplier to be 100.
- * (For Arabic, use arabic percent symbol).
- * For a permill, set the suffixes to have "\u2031" and the multiplier to be 1000.
- * Examples: with 100, 1.23 -> "123", and "123" -> 1.23
- * @stable ICU 2.0
- */
- public int getMultiplier () {
- return multiplier;
- }
-
- /**
- * Set the multiplier for use in percent, permill, etc.
- * For a percentage, set the suffixes to have "%" and the multiplier to be 100.
- * (For Arabic, use arabic percent symbol).
- * For a permill, set the suffixes to have "\u2031" and the multiplier to be 1000.
- * Examples: with 100, 1.23 -> "123", and "123" -> 1.23
- * @stable ICU 2.0
- */
- public void setMultiplier (int newValue) {
- if (newValue == 0) {
- throw new IllegalArgumentException("Bad multiplier: " + newValue);
- }
- multiplier = newValue;
- }
-
- // [NEW]
- /**
- * Get the rounding increment.
- * @return A positive rounding increment, or Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345
- * @stable ICU 2.0
- */
- public boolean isDecimalSeparatorAlwaysShown() {
- return decimalSeparatorAlwaysShown;
- }
-
- /**
- * Allows you to set the behavior of the decimal separator with integers.
- * (The decimal separator will always appear with decimals.)
- *
- * This only affects formatting, and only where
- * there might be no digits after the decimal point, e.g.,
- * if true, 3456.00 -> "3,456."
- * if false, 3456.00 -> "3456"
- * This is independent of parsing. If you want parsing to stop at the decimal
- * point, use setParseIntegerOnly.
- *
- * Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345
- * @stable ICU 2.0
- */
- public void setDecimalSeparatorAlwaysShown(boolean newValue) {
- decimalSeparatorAlwaysShown = newValue;
- }
-
- /**
- * Returns a copy of the CurrencyPluralInfo
- * used by this format.
- * It might return null if the decimal format is not a plural type
- * currency decimal format.
- * Plural type currency decimal format means either
- * the pattern in the decimal format contains 3 currency signs,
- * or the decimal format is initialized with PLURALCURRENCYSTYLE.
- * @return desired CurrencyPluralInfo
- * @see CurrencyPluralInfo
- * @draft ICU 4.2
- * @provisional This API might change or be removed in a future release.
- */
- public CurrencyPluralInfo getCurrencyPluralInfo() {
- try {
- // don't allow multiple references
- return currencyPluralInfo == null ?
- null :
- (CurrencyPluralInfo) currencyPluralInfo.clone();
- } catch (Exception foo) {
- return null; // should never happen
- }
- }
-
-
- /**
- * Sets the CurrencyPluralInfo used by this format. The
- * format uses a copy of the provided information.
- * @param newInfo desired CurrencyPluralInfo
- * @see CurrencyPluralInfo
- * @draft ICU 4.2
- * @provisional This API might change or be removed in a future release.
- */
- public void setCurrencyPluralInfo(CurrencyPluralInfo newInfo) {
- currencyPluralInfo = (CurrencyPluralInfo) newInfo.clone();
- isReadyForParsing = false;
- }
-
- /**
- * Standard override; no change in semantics.
- * @stable ICU 2.0
- */
- public Object clone() {
- try {
- DecimalFormat other = (DecimalFormat) super.clone();
- other.symbols = (DecimalFormatSymbols) symbols.clone();
- other.digitList = new DigitList(); // fix for JB#5358
- if (currencyPluralInfo != null) {
- other.currencyPluralInfo = (CurrencyPluralInfo)currencyPluralInfo.clone();
- }
- /*
- * TODO: We need to figure out whether we share a single copy
- * of DigitList by multiple cloned copies. format/subformat
- * are designed to use a single instance, but parse/subparse
- * implementation is not.
- */
- return other;
- } catch (Exception e) {
- throw new IllegalStateException();
- }
- }
-
- /**
- * Overrides equals
- * @stable ICU 2.0
- */
- public boolean equals(Object obj)
- {
- if (obj == null) return false;
- if (!super.equals(obj)) return false; // super does class check
-
- DecimalFormat other = (DecimalFormat) obj;
- /* Add the comparison of the four new added fields ,they are
- * posPrefixPattern, posSuffixPattern, negPrefixPattern, negSuffixPattern.
- * [Richard/GCL]
- */
- // following are added to accomodate changes for currency plural format.
- return currencySignCount == other.currencySignCount
- && (style != NumberFormat.PLURALCURRENCYSTYLE ||
- equals(posPrefixPattern, other.posPrefixPattern)
- && equals(posSuffixPattern, other.posSuffixPattern)
- && equals(negPrefixPattern, other.negPrefixPattern)
- && equals(negSuffixPattern, other.negSuffixPattern))
- && multiplier == other.multiplier
- && groupingSize == other.groupingSize
- && groupingSize2 == other.groupingSize2
- && decimalSeparatorAlwaysShown == other.decimalSeparatorAlwaysShown
- && useExponentialNotation == other.useExponentialNotation
- && (!useExponentialNotation ||
- minExponentDigits == other.minExponentDigits)
- && useSignificantDigits == other.useSignificantDigits
- && (!useSignificantDigits ||
- minSignificantDigits == other.minSignificantDigits &&
- maxSignificantDigits == other.maxSignificantDigits)
- && symbols.equals(other.symbols)
- && Utility.objectEquals(currencyPluralInfo, other.currencyPluralInfo);
- }
-
- //method to unquote the strings and compare
- private boolean equals(String pat1, String pat2){
- if (pat1 == null || pat2 == null) {
- return (pat1 == null && pat2 == null);
- }
- //fast path
- if(pat1.equals(pat2)){
- return true;
- }
- return unquote(pat1).equals(unquote(pat2));
- }
- private String unquote(String pat){
- StringBuffer buf = new StringBuffer(pat.length());
- int i=0;
- while(i
- * There is no limit to integer digits are set
- * by this routine, since that is the typical end-user desire;
- * use setMaximumInteger if you want to set a real value.
- * For negative numbers, use a second pattern, separated by a semicolon
- * Example "#,#00.0#" -> 1,234.56
- * This means a minimum of 2 integer digits, 1 fraction digit, and
- * a maximum of 2 fraction digits.
- * Example: "#,#00.0#;(#,#00.0#)" for negatives in parentheses.
- * In negative patterns, the minimum and maximum counts are ignored;
- * these are presumed to be set in the positive pattern.
- * @stable ICU 2.0
- */
- public void applyPattern( String pattern ) {
- applyPattern( pattern, false );
- }
-
- /**
- * Apply the given pattern to this Format object. The pattern
- * is assumed to be in a localized notation. A pattern is a
- * short-hand specification for the various formatting properties.
- * These properties can also be changed individually through the
- * various setter methods.
- *
- * There is no limit to integer digits are set
- * by this routine, since that is the typical end-user desire;
- * use setMaximumInteger if you want to set a real value.
- * For negative numbers, use a second pattern, separated by a semicolon
- * Example "#,#00.0#" -> 1,234.56
- * This means a minimum of 2 integer digits, 1 fraction digit, and
- * a maximum of 2 fraction digits.
- * Example: "#,#00.0#;(#,#00.0#)" for negatives in parantheses.
- * In negative patterns, the minimum and maximum counts are ignored;
- * these are presumed to be set in the positive pattern.
- * @stable ICU 2.0
- */
- public void applyLocalizedPattern( String pattern ) {
- applyPattern( pattern, true );
- }
-
-
- /**
- * CHANGED
- * Does the real work of applying a pattern.
- */
- private void applyPattern(String pattern, boolean localized) {
- applyPatternWithoutExpandAffix(pattern, localized);
- expandAffixAdjustWidth(null);
- }
-
- private void expandAffixAdjustWidth(String pluralCount) {
- /*Bug 4212072
- Update the affix strings according to symbols in order to keep
- the affix strings up to date.
- [Richard/GCL]
- */
- expandAffixes(pluralCount);
-
- // Now that we have the actual prefix and suffix, fix up formatWidth
- if (formatWidth > 0) {
- formatWidth += positivePrefix.length() + positiveSuffix.length();
- }
- }
-
- private void applyPatternWithoutExpandAffix(String pattern, boolean localized) {
- char zeroDigit = PATTERN_ZERO_DIGIT; // '0'
- char sigDigit = PATTERN_SIGNIFICANT_DIGIT; // '@'
- char groupingSeparator = PATTERN_GROUPING_SEPARATOR;
- char decimalSeparator = PATTERN_DECIMAL_SEPARATOR;
- char percent = PATTERN_PERCENT;
- char perMill = PATTERN_PER_MILLE;
- char digit = PATTERN_DIGIT; // '#'
- char separator = PATTERN_SEPARATOR;
- String exponent = String.valueOf(PATTERN_EXPONENT);
- char plus = PATTERN_PLUS_SIGN;
- char padEscape = PATTERN_PAD_ESCAPE;
- char minus = PATTERN_MINUS; //Bug 4212072 [Richard/GCL]
- if (localized) {
- zeroDigit = symbols.getZeroDigit();
- sigDigit = symbols.getSignificantDigit();
- groupingSeparator = symbols.getGroupingSeparator();
- decimalSeparator = symbols.getDecimalSeparator();
- percent = symbols.getPercent();
- perMill = symbols.getPerMill();
- digit = symbols.getDigit();
- separator = symbols.getPatternSeparator();
- exponent = symbols.getExponentSeparator();
- plus = symbols.getPlusSign();
- padEscape = symbols.getPadEscape();
- minus = symbols.getMinusSign(); //Bug 4212072 [Richard/GCL]
- }
- char nineDigit = (char) (zeroDigit + 9);
-
- boolean gotNegative = false;
-
- int pos = 0;
- // Part 0 is the positive pattern. Part 1, if present, is the negative
- // pattern.
- for (int part=0; part<2 && pos This pattern is expanded by the method
- * Note that the JDK 1.2 public API provides no way to set this field,
- * even though it is supported by the implementation and the stream format.
- * The intent is that this will be added to the API in the future.
- *
- * @serial
- */
- private boolean useExponentialNotation; // Newly persistent in JDK 1.2
-
- /**
- * The minimum number of digits used to display the exponent when a number is
- * formatted in exponential notation. This field is ignored if
- *
- * Note that the JDK 1.2 public API provides no way to set this field,
- * even though it is supported by the implementation and the stream format.
- * The intent is that this will be added to the API in the future.
- *
- * @serial
- */
- private byte minExponentDigits; // Newly persistent in JDK 1.2
-
- // [NEW]
- /**
- * If true, the exponent is always prefixed with either the plus
- * sign or the minus sign. Otherwise, only negative exponents are
- * prefixed with the minus sign. This has no effect unless
- * To obtain a {@link NumberFormat} for a specific locale (including the
+ * default locale) call one of Example Usage
+ *
+ *
+ * Another example use getInstance(style)
+ *
+ * A Many characters in a pattern are taken literally; they are matched during
+ * parsing and output unchanged during formatting. Special characters, on the
+ * other hand, stand for other characters, strings, or classes of characters.
+ * For example, the '#' character is replaced by a localized digit. Often the
+ * replacement character is the same as the pattern character; in the U.S. locale,
+ * the ',' grouping character is replaced by ','. However, the replacement is
+ * still happening, and if the symbols are modified, the grouping character
+ * changes. Some special characters affect the behavior of the formatter by
+ * their presence; for example, if the percent character is seen, then the
+ * value is multiplied by 100 before being displayed.
+ *
+ * To insert a special character in a pattern as a literal, that is, without
+ * any special meaning, the character must be quoted. There are some exceptions to
+ * this which are noted below.
+ *
+ * The characters listed here are used in non-localized patterns. Localized
+ * patterns use the corresponding characters taken from this formatter's
+ * {@link DecimalFormatSymbols} object instead, and these characters lose
+ * their special status. Two exceptions are the currency sign and quote, which
+ * are not localized.
+ *
+ * A The prefixes, suffixes, and various symbols used for infinity, digits,
+ * thousands separators, decimal separators, etc. may be set to arbitrary
+ * values, and they will appear properly during formatting. However, care must
+ * be taken that the symbols and strings do not conflict, or parsing will be
+ * unreliable. For example, either the positive and negative prefixes or the
+ * suffixes must be distinct for {@link #parse} to be able
+ * to distinguish positive from negative values. Another example is that the
+ * decimal separator and thousands separator should be distinct characters, or
+ * parsing will be impossible.
+ *
+ * The grouping separator is a character that separates clusters of
+ * integer digits to make large numbers more legible. It commonly used for
+ * thousands, but in some locales it separates ten-thousands. The grouping
+ * size is the number of digits between the grouping separators, such as 3
+ * for "100,000,000" or 4 for "1 0000 0000". There are actually two different
+ * grouping sizes: One used for the least significant integer digits, the
+ * primary grouping size, and one used for all others, the
+ * secondary grouping size. In most locales these are the same, but
+ * sometimes they are different. For example, if the primary grouping interval
+ * is 3, and the secondary is 2, then this corresponds to the pattern
+ * "#,##,##0", and the number 123456789 is formatted as "12,34,56,789". If a
+ * pattern contains multiple grouping separators, the interval between the last
+ * one and the end of the integer defines the primary grouping size, and the
+ * interval between the last two defines the secondary grouping size. All others
+ * are ignored, so "#,##,###,####" == "###,###,####" == "##,#,###,####".
+ *
+ * Illegal patterns, such as "#.#.#" or "#.###,###", will cause
+ * Not indicated in the BNF syntax above:
+ *
+ * During parsing, grouping separators are ignored.
+ *
+ * For currency parsing, the formatter is able to parse every currency
+ * style formats no matter which style the formatter is constructed with.
+ * For example, a formatter instance gotten from
+ * NumberFormat.getInstance(ULocale, NumberFormat.CURRENCYSTYLE) can parse
+ * formats such as "USD1.00" and "3.00 US dollars".
+ *
+ * If {@link #parse(String, ParsePosition)} fails to parse
+ * a string, it returns Formatting is guided by several parameters, all of which can be
+ * specified either using a pattern or using the API. The following
+ * description applies to formats that do not use scientific
+ * notation or significant digits.
+ *
+ * Special Values
+ *
+ * Infinity is represented as a single character, typically
+ * Numbers in scientific notation are expressed as the product of a mantissa
+ * and a power of ten, for example, 1234 can be expressed as 1.234 x 103. The
+ * mantissa is typically in the half-open interval [1.0, 10.0) or sometimes [0.0, 1.0),
+ * but it need not be.
+ * Rounding
+ *
+ *
+ * To obtain standard formats for a given locale, use the factory methods
+ * on NumberFormat such as getNumberInstance. These factories will
+ * return the most appropriate sub-class of NumberFormat for a given
+ * locale.
+ * @see NumberFormat#getInstance
+ * @see NumberFormat#getNumberInstance
+ * @see NumberFormat#getCurrencyInstance
+ * @see NumberFormat#getPercentInstance
+ * @stable ICU 2.0
+ */
+ public DecimalFormat() {
+ // [NEW]
+ ULocale def = ULocale.getDefault();
+ String pattern = getPattern(def, 0);
+ // Always applyPattern after the symbols are set
+ this.symbols = new DecimalFormatSymbols(def);
+ setCurrency(Currency.getInstance(def));
+ applyPatternWithoutExpandAffix(pattern, false);
+ if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
+ currencyPluralInfo = new CurrencyPluralInfo(def);
+ // the exact pattern is not known until the plural count is known.
+ // so, no need to expand affix now.
+ } else {
+ expandAffixAdjustWidth(null);
+ }
+ }
+
+
+ /**
+ * Create a DecimalFormat from the given pattern and the symbols
+ * for the default locale. This is a convenient way to obtain a
+ * DecimalFormat when internationalization is not the main concern.
+ *
+ * To obtain standard formats for a given locale, use the factory methods
+ * on NumberFormat such as getNumberInstance. These factories will
+ * return the most appropriate sub-class of NumberFormat for a given
+ * locale.
+ * @param pattern A non-localized pattern string.
+ * @exception IllegalArgumentException if the given pattern is invalid.
+ * @see NumberFormat#getInstance
+ * @see NumberFormat#getNumberInstance
+ * @see NumberFormat#getCurrencyInstance
+ * @see NumberFormat#getPercentInstance
+ * @stable ICU 2.0
+ */
+ public DecimalFormat(String pattern) {
+ // Always applyPattern after the symbols are set
+ ULocale def = ULocale.getDefault();
+ this.symbols = new DecimalFormatSymbols(def);
+ setCurrency(Currency.getInstance(def));
+ applyPatternWithoutExpandAffix( pattern, false );
+ if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
+ currencyPluralInfo = new CurrencyPluralInfo(def);
+ } else {
+ expandAffixAdjustWidth(null);
+ }
+ }
+
+
+ /**
+ * Create a DecimalFormat from the given pattern and symbols.
+ * Use this constructor when you need to completely customize the
+ * behavior of the format.
+ *
+ * To obtain standard formats for a given
+ * locale, use the factory methods on NumberFormat such as
+ * getInstance or getCurrencyInstance. If you need only minor adjustments
+ * to a standard format, you can modify the format returned by
+ * a NumberFormat factory method.
+ * @param pattern a non-localized pattern string
+ * @param symbols the set of symbols to be used
+ * @exception IllegalArgumentException if the given pattern is invalid
+ * @see NumberFormat#getInstance
+ * @see NumberFormat#getNumberInstance
+ * @see NumberFormat#getCurrencyInstance
+ * @see NumberFormat#getPercentInstance
+ * @see DecimalFormatSymbols
+ * @stable ICU 2.0
+ */
+ public DecimalFormat(String pattern, DecimalFormatSymbols symbols) {
+ createFromPatternAndSymbols(pattern, symbols);
+ }
+
+ private void createFromPatternAndSymbols(String pattern, DecimalFormatSymbols inputSymbols) {
+ // Always applyPattern after the symbols are set
+ symbols = (DecimalFormatSymbols) inputSymbols.clone();
+ setCurrencyForSymbols();
+ applyPatternWithoutExpandAffix(pattern, false);
+ if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
+ currencyPluralInfo = new CurrencyPluralInfo(symbols.getLocale());
+ } else {
+ expandAffixAdjustWidth(null);
+ }
+ }
+
+
+ /**
+ * Create a DecimalFormat from the given pattern, symbols,
+ * information used for currency plural format, and format style.
+ * Use this constructor when you need to completely customize the
+ * behavior of the format.
+ *
+ * To obtain standard formats for a given
+ * locale, use the factory methods on NumberFormat such as
+ * getInstance or getCurrencyInstance.
+ *
+ * If you need only minor adjustments to a standard format,
+ * you can modify the format returned by
+ * a NumberFormat factory method using the setters.
+ *
+ * If you want to completely customize a decimal format,
+ * using your own DecimalFormatSymbols (such as group separators) and
+ * your own information for currency plural formatting (such as
+ * plural rule and currency plural patterns), you can use this constructor.
+ *
+ * @param pattern a non-localized pattern string
+ * @param symbols the set of symbols to be used
+ * @param infoInput the information used for currency plural format,
+ * including currency plural patterns and plural rules.
+ * @param style the decimal formatting style,
+ * it is one of the following values:
+ * NumberFormat.NUMBERSTYLE;
+ * NumberFormat.CURRENCYSTYLE;
+ * NumberFormat.PERCENTSTYLE;
+ * NumberFormat.SCIENTIFICSTYLE;
+ * NumberFormat.INTEGERSTYLE;
+ * NumberFormat.ISOCURRENCYSTYLE;
+ * NumberFormat.PLURALCURRENCYSTYLE;
+ * @draft ICU 4.2
+ * @provisional This API might change or be removed in a future release.
+ */
+ public DecimalFormat(String pattern, DecimalFormatSymbols symbols,
+ CurrencyPluralInfo infoInput,
+ int style) {
+ CurrencyPluralInfo info = infoInput;
+ if (style == NumberFormat.PLURALCURRENCYSTYLE) {
+ info = (CurrencyPluralInfo)infoInput.clone();
+ }
+ create(pattern, symbols, info, style);
+ }
+
+
+ private void create(String pattern, DecimalFormatSymbols inputSymbols,
+ CurrencyPluralInfo info,
+ int inputStyle) {
+ if (inputStyle != NumberFormat.PLURALCURRENCYSTYLE) {
+ createFromPatternAndSymbols(pattern, inputSymbols);
+ } else {
+ // Always applyPattern after the symbols are set
+ symbols = (DecimalFormatSymbols) inputSymbols.clone();
+ currencyPluralInfo = info;
+ // the pattern used in format is not fixed until formatting,
+ // in which, the number is known and
+ // will be used to pick the right pattern based on plural count.
+ // Here, set the pattern as the pattern of plural count == "other".
+ // For most locale, the patterns are probably the same for all
+ // plural count. If not, the right pattern need to be re-applied
+ // during format.
+ String currencyPluralPatternForOther = currencyPluralInfo.getCurrencyPluralPattern("other");
+ applyPatternWithoutExpandAffix(currencyPluralPatternForOther,false);
+ setCurrencyForSymbols();
+ }
+ style = inputStyle;
+ }
+
+
+ /*
+ * Create a DecimalFormat for currency plural format
+ * from the given pattern, symbols, and style.
+ */
+ DecimalFormat(String pattern, DecimalFormatSymbols inputSymbols, int style) {
+ CurrencyPluralInfo info = null;
+ if (style == NumberFormat.PLURALCURRENCYSTYLE) {
+ info = new CurrencyPluralInfo(inputSymbols.getLocale());
+ }
+ create(pattern, inputSymbols, info, style);
+ }
+
+ /**
+ * @stable ICU 2.0
+ */
+ public StringBuffer format(double number, StringBuffer result,
+ FieldPosition fieldPosition) {
+ return format(number, result, fieldPosition, false);
+ }
+
+ // [Spark/CDL] The actual method to format number. If boolean value
+ // parseAttr == true, then attribute information will be recorded.
+ private StringBuffer format(double number, StringBuffer result,
+ FieldPosition fieldPosition, boolean parseAttr)
+ {
+ fieldPosition.setBeginIndex(0);
+ fieldPosition.setEndIndex(0);
+
+ if (Double.isNaN(number))
+ {
+ if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
+ fieldPosition.setBeginIndex(result.length());
+ }
+//#if defined(FOUNDATION10) || defined(J2SE13)
+//#else
+ else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
+ fieldPosition.setBeginIndex(result.length());
+ }
+//#endif
+
+ result.append(symbols.getNaN());
+ // [Spark/CDL] Add attribute for NaN here.
+ // result.append(symbols.getNaN());
+//#if defined(FOUNDATION10) || defined(J2SE13)
+//#else
+ if (parseAttr) {
+ addAttribute(Field.INTEGER, result.length()
+ - symbols.getNaN().length(), result.length());
+ }
+//#endif
+ if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
+ fieldPosition.setEndIndex(result.length());
+ }
+//#if defined(FOUNDATION10) || defined(J2SE13)
+//#else
+ else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
+ fieldPosition.setEndIndex(result.length());
+ }
+//#endif
+
+ addPadding(result, fieldPosition, 0, 0);
+ return result;
+ }
+
+ // Do this BEFORE checking to see if value is infinite or negative!
+ if (multiplier != 1) number *= multiplier;
+
+ /* Detecting whether a double is negative is easy with the exception of
+ * the value -0.0. This is a double which has a zero mantissa (and
+ * exponent), but a negative sign bit. It is semantically distinct from
+ * a zero with a positive sign bit, and this distinction is important
+ * to certain kinds of computations. However, it's a little tricky to
+ * detect, since (-0.0 == 0.0) and !(-0.0 < 0.0). How then, you may
+ * ask, does it behave distinctly from +0.0? Well, 1/(-0.0) ==
+ * -Infinity. Proper detection of -0.0 is needed to deal with the
+ * issues raised by bugs 4106658, 4106667, and 4147706. Liu 7/6/98.
+ */
+ boolean isNegative = (number < 0.0) || (number == 0.0 && 1/number < 0.0);
+ if (isNegative) number = -number;
+
+ // Apply rounding after multiplier
+ if (roundingDouble > 0.0) {
+ // number = roundingDouble
+ // * round(number / roundingDouble, roundingMode, isNegative);
+ double newNumber = round(number, roundingDouble, roundingDoubleReciprocal, roundingMode, isNegative);
+ if (newNumber == 0.0 && number != newNumber) isNegative = false; // if we touched it, then make zero be zero.
+ number = newNumber;
+ }
+
+ if (Double.isInfinite(number))
+ {
+ int prefixLen = appendAffix(result, isNegative, true, parseAttr);
+
+ if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
+ fieldPosition.setBeginIndex(result.length());
+ }
+//#if defined(FOUNDATION10) || defined(J2SE13)
+//#else
+ else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
+ fieldPosition.setBeginIndex(result.length());
+ }
+//#endif
+
+ // [Spark/CDL] Add attribute for infinity here.
+ result.append(symbols.getInfinity());
+//#if defined(FOUNDATION10) || defined(J2SE13)
+//#else
+ if (parseAttr) {
+ addAttribute(Field.INTEGER, result.length()
+ - symbols.getInfinity().length(), result.length());
+ }
+//#endif
+ if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
+ fieldPosition.setEndIndex(result.length());
+ }
+//#if defined(FOUNDATION10) || defined(J2SE13)
+//#else
+ else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
+ fieldPosition.setEndIndex(result.length());
+ }
+//#endif
+
+ int suffixLen = appendAffix(result, isNegative, false, parseAttr);
+
+ addPadding(result, fieldPosition, prefixLen, suffixLen);
+ return result;
+ }
+
+ // At this point we are guaranteed a nonnegative finite
+ // number.
+ synchronized(digitList) {
+ digitList.set(number, precision(false),
+ !useExponentialNotation && !areSignificantDigitsUsed());
+ return subformat(number, result, fieldPosition, isNegative, false,
+ parseAttr);
+ }
+ }
+
+ // [NEW]
+ /**
+ * Round a double value to the nearest multiple of the given
+ * rounding increment, according to the given mode. This is
+ * equivalent to rounding value/roundingInc to the nearest
+ * integer, according to the given mode, and returning that
+ * integer * roundingInc.
+ * Note this is changed from the version in 2.4, since division of doubles
+ * have inaccuracies. jitterbug 1871.
+ * @param number the absolute value of the number to be rounded
+ * @param roundingInc the rounding increment
+ * @param roundingIncReciprocal if non-zero, is the
+ * @param mode a BigDecimal rounding mode
+ * @param isNegative true if the number to be rounded is negative
+ * @return the absolute value of the rounded result
+ */
+ private static double round(double number, double roundingInc,
+ double roundingIncReciprocal, int mode, boolean isNegative) {
+
+ double div = roundingIncReciprocal == 0.0
+ ? number / roundingInc
+ : number * roundingIncReciprocal;
+
+ // do the absolute cases first
+
+ switch (mode) {
+ case BigDecimal.ROUND_CEILING:
+ div = (isNegative ? Math.floor(div + epsilon) : Math.ceil(div - epsilon));
+ break;
+ case BigDecimal.ROUND_FLOOR:
+ div = (isNegative ? Math.ceil(div - epsilon) : Math.floor(div + epsilon));
+ break;
+ case BigDecimal.ROUND_DOWN:
+ div = (Math.floor(div + epsilon));
+ break;
+ case BigDecimal.ROUND_UP:
+ div = (Math.ceil(div - epsilon));
+ break;
+ case BigDecimal.ROUND_UNNECESSARY:
+ if (div != Math.floor(div)) {
+ throw new ArithmeticException("Rounding necessary");
+ }
+ return number;
+ default:
+
+ // Handle complex cases, where the choice depends on the closer value.
+
+ // We figure out the distances to the two possible values, ceiling and floor.
+ // We then go for the diff that is smaller.
+ // Only if they are equal does the mode matter.
+
+ double ceil = Math.ceil(div);
+ double ceildiff = ceil - div; // (ceil * roundingInc) - number;
+ double floor = Math.floor(div);
+ double floordiff = div - floor; // number - (floor * roundingInc);
+
+ // Note that the diff values were those mapped back to the "normal" space
+ // by using the roundingInc. I don't have access to the original author of the code
+ // but suspect that that was to produce better result in edge cases because of machine
+ // precision, rather than simply using the difference between, say, ceil and div.
+ // However, it didn't work in all cases. Am trying instead using an epsilon value.
+
+ switch (mode) {
+ case BigDecimal.ROUND_HALF_EVEN:
+ // We should be able to just return Math.rint(a), but this
+ // doesn't work in some VMs.
+ // if one is smaller than the other, take the corresponding side
+ if (floordiff + epsilon < ceildiff) {
+ div = floor;
+ } else if (ceildiff + epsilon < floordiff) {
+ div = ceil;
+ } else { // they are equal, so we want to round to whichever is even
+ double testFloor = floor / 2;
+ div = (testFloor == Math.floor(testFloor)) ? floor : ceil;
+ }
+ break;
+ case BigDecimal.ROUND_HALF_DOWN:
+ div = ((floordiff <= ceildiff + epsilon) ? floor : ceil);
+ break;
+ case BigDecimal.ROUND_HALF_UP:
+ div = ((ceildiff <= floordiff + epsilon) ? ceil : floor);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid rounding mode: " + mode);
+ }
+ }
+ number = roundingIncReciprocal == 0.0
+ ? div * roundingInc
+ : div / roundingIncReciprocal;
+ return number;
+ }
+ private static double epsilon = 0.00000000001;
+
+ /**
+ * @stable ICU 2.0
+ */
+ // [Spark/CDL] Delegate to format_long_StringBuffer_FieldPosition_boolean
+ public StringBuffer format(long number, StringBuffer result,
+ FieldPosition fieldPosition) {
+ return format(number, result, fieldPosition, false);
+ }
+
+ private StringBuffer format(long number, StringBuffer result,
+ FieldPosition fieldPosition, boolean parseAttr)
+ {
+ fieldPosition.setBeginIndex(0);
+ fieldPosition.setEndIndex(0);
+
+ // If we are to do rounding, we need to move into the BigDecimal
+ // domain in order to do divide/multiply correctly.
+ // [NEW]
+ if (roundingIncrementICU != null) {
+ return format(BigDecimal.valueOf(number), result, fieldPosition);
+ }
+
+ boolean isNegative = (number < 0);
+ if (isNegative) number = -number;
+
+ // In general, long values always represent real finite numbers, so
+ // we don't have to check for +/- Infinity or NaN. However, there
+ // is one case we have to be careful of: The multiplier can push
+ // a number near MIN_VALUE or MAX_VALUE outside the legal range. We
+ // check for this before multiplying, and if it happens we use BigInteger
+ // instead.
+ // [NEW]
+ if (multiplier != 1) {
+ boolean tooBig = false;
+ if (number < 0) { // This can only happen if number == Long.MIN_VALUE
+ long cutoff = Long.MIN_VALUE / multiplier;
+ tooBig = (number <= cutoff); // number == cutoff can only happen if multiplier == -1
+ } else {
+ long cutoff = Long.MAX_VALUE / multiplier;
+ tooBig = (number > cutoff);
+ }
+ if (tooBig) {
+ // [Spark/CDL] Use
+ // format_BigInteger_StringBuffer_FieldPosition_boolean instead
+ // parseAttr is used to judge whether to synthesize attributes.
+ return format(
+ BigInteger.valueOf(isNegative ? -number : number),
+ result, fieldPosition, parseAttr);
+ }
+ }
+
+ number *= multiplier;
+ synchronized(digitList) {
+ digitList.set(number, precision(true));
+ return subformat(number, result, fieldPosition, isNegative, true, parseAttr);
+ }
+ }
+
+ // [NEW]
+ /**
+ * Format a BigInteger number.
+ *
+ * @stable ICU 2.0
+ */
+ public StringBuffer format(BigInteger number, StringBuffer result,
+ FieldPosition fieldPosition) {
+ return format(number, result, fieldPosition, false);
+ }
+
+ // [Spark/CDL]
+ private StringBuffer format(BigInteger number, StringBuffer result,
+ FieldPosition fieldPosition, boolean parseAttr) {
+ // If we are to do rounding, we need to move into the BigDecimal
+ // domain in order to do divide/multiply correctly.
+ if (roundingIncrementICU != null) {
+ return format(new BigDecimal(number), result, fieldPosition);
+ }
+
+ if (multiplier != 1) {
+ number = number.multiply(BigInteger.valueOf(multiplier));
+ }
+
+ // At this point we are guaranteed a nonnegative finite
+ // number.
+ synchronized(digitList) {
+ digitList.set(number, precision(true));
+ return subformat(number.intValue(), result, fieldPosition, number.signum() < 0, true, parseAttr);
+ }
+ }
+
+//#if defined(FOUNDATION10)
+//#else
+ // [NEW]
+ /**
+ * Format a BigDecimal number.
+ * @stable ICU 2.0
+ */
+ public StringBuffer format(java.math.BigDecimal number, StringBuffer result,
+ FieldPosition fieldPosition) {
+ return format(number, result, fieldPosition, false);
+ }
+
+ private StringBuffer format(java.math.BigDecimal number,
+ StringBuffer result, FieldPosition fieldPosition, boolean parseAttr) {
+ if (multiplier != 1) {
+ number = number.multiply(java.math.BigDecimal.valueOf(multiplier));
+ }
+
+ if (roundingIncrement != null) {
+ number = number.divide(roundingIncrement, 0, roundingMode)
+ .multiply(roundingIncrement);
+ }
+
+ synchronized(digitList) {
+ digitList.set(number, precision(false),
+ !useExponentialNotation && !areSignificantDigitsUsed());
+ return subformat(number.doubleValue(), result, fieldPosition, number.signum() < 0, false, parseAttr);
+ }
+ }
+//#endif
+
+ // [NEW]
+ /**
+ * Format a BigDecimal number.
+ * @stable ICU 2.0
+ */
+ public StringBuffer format(BigDecimal number, StringBuffer result,
+ FieldPosition fieldPosition) {
+ /* This method is just a copy of the corresponding java.math.BigDecimal
+ * method for now. It isn't very efficient since it must create a
+ * conversion object to do math on the rounding increment. In the
+ * future we may try to clean this up, or even better, limit our support
+ * to just one flavor of BigDecimal.
+ */
+ if (multiplier != 1) {
+ number = number.multiply(BigDecimal.valueOf(multiplier), mathContext);
+ }
+
+ if (roundingIncrementICU != null) {
+ number = number.divide(roundingIncrementICU, 0, roundingMode)
+ .multiply(roundingIncrementICU, mathContext);
+ }
+
+ synchronized(digitList) {
+ digitList.set(number, precision(false),
+ !useExponentialNotation && !areSignificantDigitsUsed());
+ return subformat(number.doubleValue(), result, fieldPosition, number.signum() < 0, false, false);
+ }
+ }
+
+ /**
+ * Return true if a grouping separator belongs at the given
+ * position, based on whether grouping is in use and the values of
+ * the primary and secondary grouping interval.
+ * @param pos the number of integer digits to the right of
+ * the current position. Zero indicates the position after the
+ * rightmost integer digit.
+ * @return true if a grouping character belongs at the current
+ * position.
+ */
+ private boolean isGroupingPosition(int pos) {
+ boolean result = false;
+ if (isGroupingUsed() && (pos > 0) && (groupingSize > 0)) {
+ if ((groupingSize2 > 0) && (pos > groupingSize)) {
+ result = ((pos - groupingSize) % groupingSize2) == 0;
+ } else {
+ result = pos % groupingSize == 0;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Return the number of fraction digits to display, or the total
+ * number of digits for significant digit formats and exponential
+ * formats.
+ */
+ private int precision(boolean isIntegral) {
+ if (areSignificantDigitsUsed()) {
+ return getMaximumSignificantDigits();
+ } else if (useExponentialNotation) {
+ return getMinimumIntegerDigits() + getMaximumFractionDigits();
+ } else {
+ return isIntegral ? 0 : getMaximumFractionDigits();
+ }
+ }
+
+ private StringBuffer subformat(int number, StringBuffer result,
+ FieldPosition fieldPosition,
+ boolean isNegative, boolean isInteger,
+ boolean parseAttr) {
+ if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
+ return subformat(currencyPluralInfo.select(number), result, fieldPosition,
+ isNegative, isInteger, parseAttr);
+ } else {
+ return subformat(result, fieldPosition, isNegative, isInteger, parseAttr);
+ }
+ }
+
+ private StringBuffer subformat(double number, StringBuffer result,
+ FieldPosition fieldPosition,
+ boolean isNegative, boolean isInteger,
+ boolean parseAttr) {
+ if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
+ return subformat(currencyPluralInfo.select(number), result, fieldPosition,
+ isNegative, isInteger, parseAttr);
+ } else {
+ return subformat(result, fieldPosition, isNegative, isInteger, parseAttr);
+ }
+ }
+
+ private StringBuffer subformat(String pluralCount,
+ StringBuffer result,
+ FieldPosition fieldPosition, boolean isNegative, boolean isInteger,
+ boolean parseAttr)
+ {
+ // There are 2 ways to activate currency plural format:
+ // by applying a pattern with 3 currency sign directly,
+ // or by instantiate a decimal formatter using PLURALCURRENCYSTYLE.
+ // For both cases, the number of currency sign in the pattern is 3.
+ // Even if the number of currency sign in the pattern is 3,
+ // it does not mean we need to reset the pattern.
+ // For 1st case, we do not need to reset pattern.
+ // For 2nd case, we might need to reset pattern,
+ // if the default pattern (corresponding to plural count 'other')
+ // we use is different from the pattern based on 'pluralCount'.
+ //
+ // style is only valid when decimal formatter is constructed through
+ // DecimalFormat(pattern, symbol, style)
+ if (style == NumberFormat.PLURALCURRENCYSTYLE) {
+ // May need to reset pattern if the style is PLURALCURRENCYSTYLE.
+ String currencyPluralPattern = currencyPluralInfo.getCurrencyPluralPattern(pluralCount);
+ if (formatPattern.equals(currencyPluralPattern) == false) {
+ applyPatternWithoutExpandAffix(currencyPluralPattern, false);
+ }
+ }
+ // Expand the affix to the right name according to
+ // the plural rule.
+ // This is only used for currency plural formatting.
+ // Currency plural name is not a fixed static one,
+ // it is a dynamic name based on the currency plural count.
+ // So, the affixes need to be expanded here.
+ // For other cases, the affix is a static one based on pattern alone,
+ // and it is already expanded during applying pattern,
+ // or setDecimalFormatSymbols, or setCurrency.
+ expandAffixAdjustWidth(pluralCount);
+ return subformat(result, fieldPosition, isNegative, isInteger, parseAttr);
+ }
+
+ /**
+ * Complete the formatting of a finite number. On entry, the digitList must
+ * be filled in with the correct digits.
+ */
+ private StringBuffer subformat(StringBuffer result,
+ FieldPosition fieldPosition, boolean isNegative, boolean isInteger,
+ boolean parseAttr)
+ {
+ // NOTE: This isn't required anymore because DigitList takes care of this.
+ //
+ // // The negative of the exponent represents the number of leading
+ // // zeros between the decimal and the first non-zero digit, for
+ // // a value < 0.1 (e.g., for 0.00123, -fExponent == 2). If this
+ // // is more than the maximum fraction digits, then we have an underflow
+ // // for the printed representation. We recognize this here and set
+ // // the DigitList representation to zero in this situation.
+ //
+ // if (-digitList.decimalAt >= getMaximumFractionDigits())
+ // {
+ // digitList.count = 0;
+ // }
+
+ int i;
+ char zero = symbols.getZeroDigit();
+ int zeroDelta = zero - '0'; // '0' is the DigitList representation of zero
+ char grouping = currencySignCount > 0 ?
+ symbols.getMonetaryGroupingSeparator() :
+ symbols.getGroupingSeparator();
+ char decimal = currencySignCount > 0 ?
+ symbols.getMonetaryDecimalSeparator() :
+ symbols.getDecimalSeparator();
+ boolean useSigDig = areSignificantDigitsUsed();
+ int maxIntDig = getMaximumIntegerDigits();
+ int minIntDig = getMinimumIntegerDigits();
+
+ /* Per bug 4147706, DecimalFormat must respect the sign of numbers which
+ * format as zero. This allows sensible computations and preserves
+ * relations such as signum(1/x) = signum(x), where x is +Infinity or
+ * -Infinity. Prior to this fix, we always formatted zero values as if
+ * they were positive. Liu 7/6/98.
+ */
+ if (digitList.isZero())
+ {
+ digitList.decimalAt = 0; // Normalize
+ }
+
+ int prefixLen = appendAffix(result, isNegative, true, parseAttr);
+
+ if (useExponentialNotation)
+ {
+ // Record field information for caller.
+ if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
+ fieldPosition.setBeginIndex(result.length());
+ fieldPosition.setEndIndex(-1);
+ } else if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
+ fieldPosition.setBeginIndex(-1);
+ }
+//#if defined(FOUNDATION10) || defined(J2SE13)
+//#else
+ else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
+ fieldPosition.setBeginIndex(result.length());
+ fieldPosition.setEndIndex(-1);
+ } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
+ fieldPosition.setBeginIndex(-1);
+ }
+//#endif
+
+//#if defined(FOUNDATION10) || defined(J2SE13)
+//#else
+ // [Spark/CDL]
+ // the begin index of integer part
+ // the end index of integer part
+ // the begin index of fractional part
+ int intBegin = result.length();
+ int intEnd = -1;
+ int fracBegin = -1;
+//#endif
+
+ int minFracDig = 0;
+ if (useSigDig) {
+ maxIntDig = minIntDig = 1;
+ minFracDig = getMinimumSignificantDigits() - 1;
+ } else {
+ minFracDig = getMinimumFractionDigits();
+ if (maxIntDig > MAX_SCIENTIFIC_INTEGER_DIGITS) {
+ maxIntDig = 1;
+ if (maxIntDig < minIntDig) {
+ maxIntDig = minIntDig;
+ }
+ }
+ if (maxIntDig > minIntDig) {
+ minIntDig = 1;
+ }
+ }
+
+ // Minimum integer digits are handled in exponential format by
+ // adjusting the exponent. For example, 0.01234 with 3 minimum
+ // integer digits is "123.4E-4".
+
+ // Maximum integer digits are interpreted as indicating the
+ // repeating range. This is useful for engineering notation, in
+ // which the exponent is restricted to a multiple of 3. For
+ // example, 0.01234 with 3 maximum integer digits is "12.34e-3".
+ // If maximum integer digits are defined and are larger than
+ // minimum integer digits, then minimum integer digits are
+ // ignored.
+
+ int exponent = digitList.decimalAt;
+ if (maxIntDig > 1 && maxIntDig != minIntDig) {
+ // A exponent increment is defined; adjust to it.
+ exponent = (exponent > 0) ? (exponent - 1) / maxIntDig
+ : (exponent / maxIntDig) - 1;
+ exponent *= maxIntDig;
+ } else {
+ // No exponent increment is defined; use minimum integer digits.
+ // If none is specified, as in "#E0", generate 1 integer digit.
+ exponent -= (minIntDig > 0 || minFracDig > 0)
+ ? minIntDig : 1;
+ }
+
+ // We now output a minimum number of digits, and more if there
+ // are more digits, up to the maximum number of digits. We
+ // place the decimal point after the "integer" digits, which
+ // are the first (decimalAt - exponent) digits.
+ int minimumDigits = minIntDig + minFracDig;
+ // The number of integer digits is handled specially if the number
+ // is zero, since then there may be no digits.
+ int integerDigits = digitList.isZero() ? minIntDig :
+ digitList.decimalAt - exponent;
+ int totalDigits = digitList.count;
+ if (minimumDigits > totalDigits) totalDigits = minimumDigits;
+ if (integerDigits > totalDigits) totalDigits = integerDigits;
+
+ for (i=0; i Examples: +123, $123, sFr123
+ * @stable ICU 2.0
+ */
+ public String getPositivePrefix () {
+ return positivePrefix;
+ }
+
+ /**
+ * Set the positive prefix.
+ * Examples: +123, $123, sFr123
+ * @stable ICU 2.0
+ */
+ public void setPositivePrefix (String newValue) {
+ positivePrefix = newValue;
+ posPrefixPattern = null;
+ }
+
+ /**
+ * Get the negative prefix.
+ * Examples: -123, ($123) (with negative suffix), sFr-123
+ * @stable ICU 2.0
+ */
+ public String getNegativePrefix () {
+ return negativePrefix;
+ }
+
+ /**
+ * Set the negative prefix.
+ * Examples: -123, ($123) (with negative suffix), sFr-123
+ * @stable ICU 2.0
+ */
+ public void setNegativePrefix (String newValue) {
+ negativePrefix = newValue;
+ negPrefixPattern = null;
+ }
+
+ /**
+ * Get the positive suffix.
+ * Example: 123%
+ * @stable ICU 2.0
+ */
+ public String getPositiveSuffix () {
+ return positiveSuffix;
+ }
+
+ /**
+ * Set the positive suffix.
+ * Example: 123%
+ * @stable ICU 2.0
+ */
+ public void setPositiveSuffix (String newValue) {
+ positiveSuffix = newValue;
+ posSuffixPattern = null;
+ }
+
+ /**
+ * Get the negative suffix.
+ * Examples: -123%, ($123) (with positive suffixes)
+ * @stable ICU 2.0
+ */
+ public String getNegativeSuffix () {
+ return negativeSuffix;
+ }
+
+ /**
+ * Set the positive suffix.
+ * Examples: 123%
+ * @stable ICU 2.0
+ */
+ public void setNegativeSuffix (String newValue) {
+ negativeSuffix = newValue;
+ negSuffixPattern = null;
+ }
+
+ /**
+ * Get the multiplier for use in percent, permill, etc.
+ * For a percentage, set the suffixes to have "%" and the multiplier to be 100.
+ * (For Arabic, use arabic percent symbol).
+ * For a permill, set the suffixes to have "\u2031" and the multiplier to be 1000.
+ * Examples: with 100, 1.23 -> "123", and "123" -> 1.23
+ * @stable ICU 2.0
+ */
+ public int getMultiplier () {
+ return multiplier;
+ }
+
+ /**
+ * Set the multiplier for use in percent, permill, etc.
+ * For a percentage, set the suffixes to have "%" and the multiplier to be 100.
+ * (For Arabic, use arabic percent symbol).
+ * For a permill, set the suffixes to have "\u2031" and the multiplier to be 1000.
+ * Examples: with 100, 1.23 -> "123", and "123" -> 1.23
+ * @stable ICU 2.0
+ */
+ public void setMultiplier (int newValue) {
+ if (newValue == 0) {
+ throw new IllegalArgumentException("Bad multiplier: " + newValue);
+ }
+ multiplier = newValue;
+ }
+
+ // [NEW]
+ /**
+ * Get the rounding increment.
+ * @return A positive rounding increment, or Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345
+ * @stable ICU 2.0
+ */
+ public boolean isDecimalSeparatorAlwaysShown() {
+ return decimalSeparatorAlwaysShown;
+ }
+
+ /**
+ * Allows you to set the behavior of the decimal separator with integers.
+ * (The decimal separator will always appear with decimals.)
+ *
+ * This only affects formatting, and only where
+ * there might be no digits after the decimal point, e.g.,
+ * if true, 3456.00 -> "3,456."
+ * if false, 3456.00 -> "3456"
+ * This is independent of parsing. If you want parsing to stop at the decimal
+ * point, use setParseIntegerOnly.
+ *
+ * Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345
+ * @stable ICU 2.0
+ */
+ public void setDecimalSeparatorAlwaysShown(boolean newValue) {
+ decimalSeparatorAlwaysShown = newValue;
+ }
+
+ /**
+ * Returns a copy of the CurrencyPluralInfo
+ * used by this format.
+ * It might return null if the decimal format is not a plural type
+ * currency decimal format.
+ * Plural type currency decimal format means either
+ * the pattern in the decimal format contains 3 currency signs,
+ * or the decimal format is initialized with PLURALCURRENCYSTYLE.
+ * @return desired CurrencyPluralInfo
+ * @see CurrencyPluralInfo
+ * @draft ICU 4.2
+ * @provisional This API might change or be removed in a future release.
+ */
+ public CurrencyPluralInfo getCurrencyPluralInfo() {
+ try {
+ // don't allow multiple references
+ return currencyPluralInfo == null ?
+ null :
+ (CurrencyPluralInfo) currencyPluralInfo.clone();
+ } catch (Exception foo) {
+ return null; // should never happen
+ }
+ }
+
+
+ /**
+ * Sets the CurrencyPluralInfo used by this format. The
+ * format uses a copy of the provided information.
+ * @param newInfo desired CurrencyPluralInfo
+ * @see CurrencyPluralInfo
+ * @draft ICU 4.2
+ * @provisional This API might change or be removed in a future release.
+ */
+ public void setCurrencyPluralInfo(CurrencyPluralInfo newInfo) {
+ currencyPluralInfo = (CurrencyPluralInfo) newInfo.clone();
+ isReadyForParsing = false;
+ }
+
+ /**
+ * Standard override; no change in semantics.
+ * @stable ICU 2.0
+ */
+ public Object clone() {
+ try {
+ DecimalFormat other = (DecimalFormat) super.clone();
+ other.symbols = (DecimalFormatSymbols) symbols.clone();
+ other.digitList = new DigitList(); // fix for JB#5358
+ if (currencyPluralInfo != null) {
+ other.currencyPluralInfo = (CurrencyPluralInfo)currencyPluralInfo.clone();
+ }
+ /*
+ * TODO: We need to figure out whether we share a single copy
+ * of DigitList by multiple cloned copies. format/subformat
+ * are designed to use a single instance, but parse/subparse
+ * implementation is not.
+ */
+ return other;
+ } catch (Exception e) {
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Overrides equals
+ * @stable ICU 2.0
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == null) return false;
+ if (!super.equals(obj)) return false; // super does class check
+
+ DecimalFormat other = (DecimalFormat) obj;
+ /* Add the comparison of the four new added fields ,they are
+ * posPrefixPattern, posSuffixPattern, negPrefixPattern, negSuffixPattern.
+ * [Richard/GCL]
+ */
+ // following are added to accomodate changes for currency plural format.
+ return currencySignCount == other.currencySignCount
+ && (style != NumberFormat.PLURALCURRENCYSTYLE ||
+ equals(posPrefixPattern, other.posPrefixPattern)
+ && equals(posSuffixPattern, other.posSuffixPattern)
+ && equals(negPrefixPattern, other.negPrefixPattern)
+ && equals(negSuffixPattern, other.negSuffixPattern))
+ && multiplier == other.multiplier
+ && groupingSize == other.groupingSize
+ && groupingSize2 == other.groupingSize2
+ && decimalSeparatorAlwaysShown == other.decimalSeparatorAlwaysShown
+ && useExponentialNotation == other.useExponentialNotation
+ && (!useExponentialNotation ||
+ minExponentDigits == other.minExponentDigits)
+ && useSignificantDigits == other.useSignificantDigits
+ && (!useSignificantDigits ||
+ minSignificantDigits == other.minSignificantDigits &&
+ maxSignificantDigits == other.maxSignificantDigits)
+ && symbols.equals(other.symbols)
+ && Utility.objectEquals(currencyPluralInfo, other.currencyPluralInfo);
+ }
+
+ //method to unquote the strings and compare
+ private boolean equals(String pat1, String pat2){
+ if (pat1 == null || pat2 == null) {
+ return (pat1 == null && pat2 == null);
+ }
+ //fast path
+ if(pat1.equals(pat2)){
+ return true;
+ }
+ return unquote(pat1).equals(unquote(pat2));
+ }
+ private String unquote(String pat){
+ StringBuffer buf = new StringBuffer(pat.length());
+ int i=0;
+ while(i
+ * There is no limit to integer digits are set
+ * by this routine, since that is the typical end-user desire;
+ * use setMaximumInteger if you want to set a real value.
+ * For negative numbers, use a second pattern, separated by a semicolon
+ * Example "#,#00.0#" -> 1,234.56
+ * This means a minimum of 2 integer digits, 1 fraction digit, and
+ * a maximum of 2 fraction digits.
+ * Example: "#,#00.0#;(#,#00.0#)" for negatives in parentheses.
+ * In negative patterns, the minimum and maximum counts are ignored;
+ * these are presumed to be set in the positive pattern.
+ * @stable ICU 2.0
+ */
+ public void applyPattern( String pattern ) {
+ applyPattern( pattern, false );
+ }
+
+ /**
+ * Apply the given pattern to this Format object. The pattern
+ * is assumed to be in a localized notation. A pattern is a
+ * short-hand specification for the various formatting properties.
+ * These properties can also be changed individually through the
+ * various setter methods.
+ *
+ * There is no limit to integer digits are set
+ * by this routine, since that is the typical end-user desire;
+ * use setMaximumInteger if you want to set a real value.
+ * For negative numbers, use a second pattern, separated by a semicolon
+ * Example "#,#00.0#" -> 1,234.56
+ * This means a minimum of 2 integer digits, 1 fraction digit, and
+ * a maximum of 2 fraction digits.
+ * Example: "#,#00.0#;(#,#00.0#)" for negatives in parantheses.
+ * In negative patterns, the minimum and maximum counts are ignored;
+ * these are presumed to be set in the positive pattern.
+ * @stable ICU 2.0
+ */
+ public void applyLocalizedPattern( String pattern ) {
+ applyPattern( pattern, true );
+ }
+
+
+ /**
+ * CHANGED
+ * Does the real work of applying a pattern.
+ */
+ private void applyPattern(String pattern, boolean localized) {
+ applyPatternWithoutExpandAffix(pattern, localized);
+ expandAffixAdjustWidth(null);
+ }
+
+ private void expandAffixAdjustWidth(String pluralCount) {
+ /*Bug 4212072
+ Update the affix strings according to symbols in order to keep
+ the affix strings up to date.
+ [Richard/GCL]
+ */
+ expandAffixes(pluralCount);
+
+ // Now that we have the actual prefix and suffix, fix up formatWidth
+ if (formatWidth > 0) {
+ formatWidth += positivePrefix.length() + positiveSuffix.length();
+ }
+ }
+
+ private void applyPatternWithoutExpandAffix(String pattern, boolean localized) {
+ char zeroDigit = PATTERN_ZERO_DIGIT; // '0'
+ char sigDigit = PATTERN_SIGNIFICANT_DIGIT; // '@'
+ char groupingSeparator = PATTERN_GROUPING_SEPARATOR;
+ char decimalSeparator = PATTERN_DECIMAL_SEPARATOR;
+ char percent = PATTERN_PERCENT;
+ char perMill = PATTERN_PER_MILLE;
+ char digit = PATTERN_DIGIT; // '#'
+ char separator = PATTERN_SEPARATOR;
+ String exponent = String.valueOf(PATTERN_EXPONENT);
+ char plus = PATTERN_PLUS_SIGN;
+ char padEscape = PATTERN_PAD_ESCAPE;
+ char minus = PATTERN_MINUS; //Bug 4212072 [Richard/GCL]
+ if (localized) {
+ zeroDigit = symbols.getZeroDigit();
+ sigDigit = symbols.getSignificantDigit();
+ groupingSeparator = symbols.getGroupingSeparator();
+ decimalSeparator = symbols.getDecimalSeparator();
+ percent = symbols.getPercent();
+ perMill = symbols.getPerMill();
+ digit = symbols.getDigit();
+ separator = symbols.getPatternSeparator();
+ exponent = symbols.getExponentSeparator();
+ plus = symbols.getPlusSign();
+ padEscape = symbols.getPadEscape();
+ minus = symbols.getMinusSign(); //Bug 4212072 [Richard/GCL]
+ }
+ char nineDigit = (char) (zeroDigit + 9);
+
+ boolean gotNegative = false;
+
+ int pos = 0;
+ // Part 0 is the positive pattern. Part 1, if present, is the negative
+ // pattern.
+ for (int part=0; part<2 && pos This pattern is expanded by the method
+ * Note that the JDK 1.2 public API provides no way to set this field,
+ * even though it is supported by the implementation and the stream format.
+ * The intent is that this will be added to the API in the future.
+ *
+ * @serial
+ */
+ private boolean useExponentialNotation; // Newly persistent in JDK 1.2
+
+ /**
+ * The minimum number of digits used to display the exponent when a number is
+ * formatted in exponential notation. This field is ignored if
+ *
+ * Note that the JDK 1.2 public API provides no way to set this field,
+ * even though it is supported by the implementation and the stream format.
+ * The intent is that this will be added to the API in the future.
+ *
+ * @serial
+ */
+ private byte minExponentDigits; // Newly persistent in JDK 1.2
+
+ // [NEW]
+ /**
+ * If true, the exponent is always prefixed with either the plus
+ * sign or the minus sign. Otherwise, only negative exponents are
+ * prefixed with the minus sign. This has no effect unless
+ * Double
objects are returned to
- * represent non-integral values which cannot be stored in a
- * BigDecimal
. These are NaN
, infinity,
- * -infinity, and -0.0. If {@link #isParseBigDecimal()} is false (the
- * default), all other values are returned as Long
,
- * BigInteger
, or BigDecimal
values,
- * in that order of preference. If {@link #isParseBigDecimal()} is true,
- * all other values are returned as BigDecimal
valuse.
- * If the parse fails, null is returned.
- * @param text the string to be parsed
- * @param parsePosition defines the position where parsing is to begin,
- * and upon return, the position where parsing left off. If the position
- * has not changed upon return, then parsing failed.
- * @return a Number
object with the parsed value or
- * null
if the parse failed
- * @stable ICU 2.0
- */
- public Number parse(String text, ParsePosition parsePosition) {
- return (Number) parse(text, parsePosition, false);
- }
-
- // [NEW]
- /**
- * Parses text from the given string as a CurrencyAmount. Unlike
- * the parse() method, this method will attempt to parse a generic
- * currency name, searching for a match of this object's locale's
- * currency display names, or for a 3-letter ISO currency code.
- * This method will fail if this format is not a currency format,
- * that is, if it does not contain the currency pattern symbol
- * (U+00A4) in its prefix or suffix.
- *
- * @param text the string to parse
- * @param pos input-output position; on input, the position within
- * text to match; must have 0 <= pos.getIndex() < text.length();
- * on output, the position after the last matched character. If
- * the parse fails, the position in unchanged upon output.
- * @return a CurrencyAmount, or null upon failure
- * @internal
- * @deprecated This API is ICU internal only.
- */
- CurrencyAmount parseCurrency(String text, ParsePosition pos) {
- return (CurrencyAmount) parse(text, pos, true);
- }
-
- /**
- * Parses the given text as either a Number or a CurrencyAmount.
- * @param text the string to parse
- * @param parsePosition input-output position; on input, the
- * position within text to match; must have 0 <= pos.getIndex() <
- * text.length(); on output, the position after the last matched
- * character. If the parse fails, the position in unchanged upon
- * output.
- * @param parseCurrency if true, a CurrencyAmount is parsed and
- * returned; otherwise a Number is parsed and returned
- * @return a Number or CurrencyAmount or null
- */
- private Object parse(String text, ParsePosition parsePosition, boolean parseCurrency) {
- int backup;
- int i = backup = parsePosition.getIndex();
-
- // Handle NaN as a special case:
-
- // Skip padding characters, if around prefix
- if (formatWidth > 0 && (padPosition == PAD_BEFORE_PREFIX ||
- padPosition == PAD_AFTER_PREFIX)) {
- i = skipPadding(text, i);
- }
- if (text.regionMatches(i, symbols.getNaN(),
- 0, symbols.getNaN().length())) {
- i += symbols.getNaN().length();
- // Skip padding characters, if around suffix
- if (formatWidth > 0 && (padPosition == PAD_BEFORE_SUFFIX ||
- padPosition == PAD_AFTER_SUFFIX)) {
- i = skipPadding(text, i);
- }
- parsePosition.setIndex(i);
- return new Double(Double.NaN);
- }
-
- // NaN parse failed; start over
- i = backup;
-
- boolean[] status = new boolean[STATUS_LENGTH];
- Currency[] currency = parseCurrency ? new Currency[1] : null;
- if (currencySignCount > 0) {
- if (!parseForCurrency(text, parsePosition, parseCurrency,
- currency, status)) {
- return null;
- }
- } else {
- if (!subparse(text, parsePosition, digitList, false, status,
- currency, negPrefixPattern, negSuffixPattern,
- posPrefixPattern, posSuffixPattern, Currency.SYMBOL_NAME)) {
- parsePosition.setIndex(backup);
- return null;
- }
- }
-
- Number n = null;
-
- // Handle infinity
- if (status[STATUS_INFINITE]) {
- n = new Double(status[STATUS_POSITIVE]
- ? Double.POSITIVE_INFINITY
- : Double.NEGATIVE_INFINITY);
- }
-
- // Handle underflow
- else if (status[STATUS_UNDERFLOW]) {
- n = status[STATUS_POSITIVE] ? new Double("0.0") : new Double("-0.0");
- }
-
- // Handle -0.0
- else if (!status[STATUS_POSITIVE] && digitList.isZero()) {
- n = new Double("-0.0");
- }
-
- else {
- // Do as much of the multiplier conversion as possible without
- // losing accuracy.
- int mult = multiplier; // Don't modify this.multiplier
- while (mult % 10 == 0) {
- --digitList.decimalAt;
- mult /= 10;
- }
-
- // Handle integral values
- if (!parseBigDecimal && mult == 1 && digitList.isIntegral()) {
- // hack quick long
- if (digitList.decimalAt < 12) { // quick check for long
- long l = 0;
- if (digitList.count > 0) {
- int nx = 0;
- while (nx < digitList.count) {
- l = l * 10 + (char)digitList.digits[nx++] - '0';
- }
- while (nx++ < digitList.decimalAt) {
- l *= 10;
- }
- if (!status[STATUS_POSITIVE]) {
- l = -l;
- }
- }
- n = new Long(l);
- } else {
- BigInteger big = digitList.getBigInteger(status[STATUS_POSITIVE]);
- n = (big.bitLength() < 64) ?
- (Number) new Long(big.longValue()) : (Number) big;
- }
- }
- // Handle non-integral values or the case where parseBigDecimal is set
- else {
- BigDecimal big = digitList.getBigDecimalICU(status[STATUS_POSITIVE]);
- n = big;
- if (mult != 1) {
- n = big.divide(BigDecimal.valueOf(mult), mathContext);
- }
- }
- }
-
- // Assemble into CurrencyAmount if necessary
- return parseCurrency ? (Object) new CurrencyAmount(n, currency[0])
- : (Object) n;
- }
-
-
- private boolean parseForCurrency(String text, ParsePosition parsePosition,
- boolean parseCurrency, Currency[] currency,
- boolean[] status) {
- int origPos = parsePosition.getIndex();
- if (!isReadyForParsing) {
- int savedCurrencySignCount = currencySignCount;
- setupCurrencyAffixForAllPatterns();
- // reset pattern back
- if (savedCurrencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
- applyPatternWithoutExpandAffix(formatPattern, false);
- } else {
- applyPattern(formatPattern, false);
- }
- isReadyForParsing = true;
- }
- int maxPosIndex = origPos;
- int maxErrorPos = -1;
- boolean[] savedStatus = null;
- // First, parse against current pattern.
- // Since current pattern could be set by applyPattern(),
- // it could be an arbitrary pattern, and it may not be the one
- // defined in current locale.
- boolean[] tmpStatus = new boolean[STATUS_LENGTH];
- ParsePosition tmpPos = new ParsePosition(origPos);
- DigitList tmpDigitList = new DigitList();
- boolean found;
- if (style == NumberFormat.PLURALCURRENCYSTYLE) {
- found = subparse(text, tmpPos, tmpDigitList, false,
- tmpStatus, currency, negPrefixPattern, negSuffixPattern,
- posPrefixPattern, posSuffixPattern, Currency.LONG_NAME);
- } else {
- found = subparse(text, tmpPos, tmpDigitList, false,
- tmpStatus, currency, negPrefixPattern, negSuffixPattern,
- posPrefixPattern, posSuffixPattern, Currency.SYMBOL_NAME);
- }
- if (found) {
- if (tmpPos.getIndex() > maxPosIndex) {
- maxPosIndex = tmpPos.getIndex();
- savedStatus = tmpStatus;
- digitList = tmpDigitList;
- }
- } else {
- maxErrorPos = tmpPos.getErrorIndex();
- }
- // Then, parse against affix patterns.
- // Those are currency patterns and currency plural patterns
- // defined in the locale.
- Iterator iter = affixPatternsForCurrency.iterator();
- while (iter.hasNext()) {
- AffixForCurrency affix = (AffixForCurrency)iter.next();
-
- tmpStatus = new boolean[STATUS_LENGTH];
- tmpPos = new ParsePosition(origPos);
- tmpDigitList = new DigitList();
- boolean result = subparse(text, tmpPos, tmpDigitList, false,
- tmpStatus, currency, affix.getNegPrefix(),
- affix.getNegSuffix(), affix.getPosPrefix(),
- affix.getPosSuffix(), affix.getPatternType());
- if (result) {
- found = true;
- if (tmpPos.getIndex() > maxPosIndex) {
- maxPosIndex = tmpPos.getIndex();
- savedStatus = tmpStatus;
- digitList = tmpDigitList;
- }
- } else {
- maxErrorPos = (tmpPos.getErrorIndex() > maxErrorPos) ?
- tmpPos.getErrorIndex() : maxErrorPos;
- }
- }
- // Finally, parse against simple affix to find the match.
- // For example, in TestMonster suite,
- // if the to-be-parsed text is "-\u00A40,00".
- // complexAffixCompare will not find match,
- // since there is no ISO code matches "\u00A4",
- // and the parse stops at "\u00A4".
- // We will just use simple affix comparison (look for exact match)
- // to pass it.
- tmpStatus = new boolean[STATUS_LENGTH];
- tmpPos = new ParsePosition(origPos);
- tmpDigitList = new DigitList();
- int savedCurrencySignCount = currencySignCount;
- // set currencySignCount to 0 so that compareAffix function will
- // fall to compareSimpleAffix path, not compareComplexAffix path.
- currencySignCount = 0;
- boolean result = subparse(text, tmpPos, tmpDigitList, false,
- tmpStatus, currency, negativePrefix, negativeSuffix,
- positivePrefix, positiveSuffix, Currency.SYMBOL_NAME);
- currencySignCount = savedCurrencySignCount;
- if (result) {
- if (tmpPos.getIndex() > maxPosIndex) {
- maxPosIndex = tmpPos.getIndex();
- savedStatus = tmpStatus;
- digitList = tmpDigitList;
- }
- found = true;
- } else {
- maxErrorPos = (tmpPos.getErrorIndex() > maxErrorPos) ?
- tmpPos.getErrorIndex() : maxErrorPos;
- }
-
- if (!found) {
- //parsePosition.setIndex(origPos);
- parsePosition.setErrorIndex(maxErrorPos);
- } else {
- parsePosition.setIndex(maxPosIndex);
- parsePosition.setErrorIndex(-1);
- for (int index = 0; index < STATUS_LENGTH; ++index) {
- status[index] = savedStatus[index];
- }
- }
- return found;
- }
-
-
- // Get affix patterns used in locale's currency pattern
- // (NumberPatterns[1]) and currency plural pattern (CurrencyUnitPatterns).
- private void setupCurrencyAffixForAllPatterns() {
- if (currencyPluralInfo == null) {
- currencyPluralInfo = new CurrencyPluralInfo(symbols.getLocale());
- }
- affixPatternsForCurrency = new HashSet();
-
- // save the current pattern, since it will be changed by
- // applyPatternWithoutExpandAffix
- String savedFormatPattern = formatPattern;
-
- // CURRENCYSTYLE and ISOCURRENCYSTYLE should have the same
- // prefix and suffix, so, only need to save one of them.
- // Here, chose onlyApplyPatternWithoutExpandAffix without
- // saving the actualy pattern in 'pattern' data member.
- // TODO: is it uloc?
- applyPatternWithoutExpandAffix(getPattern(symbols.getLocale(), NumberFormat.CURRENCYSTYLE), false);
- AffixForCurrency affixes = new AffixForCurrency(negPrefixPattern,
- negSuffixPattern,
- posPrefixPattern,
- posSuffixPattern,
- Currency.SYMBOL_NAME);
- affixPatternsForCurrency.add(affixes);
-
- // add plural pattern
- Iterator iter = currencyPluralInfo.pluralPatternIterator();
- Set currencyUnitPatternSet = new HashSet();
- while (iter.hasNext()) {
- String pluralCount = (String)iter.next();
- String currencyPattern = (String)currencyPluralInfo.getCurrencyPluralPattern(pluralCount);
- if (currencyPattern != null &&
- currencyUnitPatternSet.contains(currencyPattern) == false) {
- currencyUnitPatternSet.add(currencyPattern);
- applyPatternWithoutExpandAffix(currencyPattern, false);
- affixes = new AffixForCurrency(negPrefixPattern,
- negSuffixPattern,
- posPrefixPattern,
- posSuffixPattern,
- Currency.LONG_NAME);
- affixPatternsForCurrency.add(affixes);
- }
- }
- // reset pattern back
- formatPattern = savedFormatPattern;
- }
-
- private static final int CURRENCY_SIGN_COUNT_IN_SYMBOL_FORMAT = 1;
- private static final int CURRENCY_SIGN_COUNT_IN_ISO_FORMAT = 2;
- private static final int CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT = 3;
-
- private static final int STATUS_INFINITE = 0;
- private static final int STATUS_POSITIVE = 1;
- private static final int STATUS_UNDERFLOW = 2;
- private static final int STATUS_LENGTH = 3;
- private static final UnicodeSet dotEquivalents =(UnicodeSet) new UnicodeSet(
- "[.\u2024\u3002\uFE12\uFE52\uFF0E\uFF61]").freeze();
- private static final UnicodeSet commaEquivalents = (UnicodeSet) new UnicodeSet(
- "[,\u060C\u066B\u3001\uFE10\uFE11\uFE50\uFE51\uFF0C\uFF64]").freeze();
- private static final UnicodeSet otherGroupingSeparators = (UnicodeSet) new UnicodeSet(
- "[\\ '\u00A0\u066C\u2000-\u200A\u2018\u2019\u202F\u205F\u3000\uFF07]").freeze();
-
- private static final UnicodeSet strictDotEquivalents =(UnicodeSet) new UnicodeSet(
- "[.\u2024\uFE52\uFF0E\uFF61]").freeze();
- private static final UnicodeSet strictCommaEquivalents = (UnicodeSet) new UnicodeSet(
- "[,\u066B\uFE10\uFE50\uFF0C]").freeze();
- private static final UnicodeSet strictOtherGroupingSeparators = (UnicodeSet) new UnicodeSet(
- "[\\ '\u00A0\u066C\u2000-\u200A\u2018\u2019\u202F\u205F\u3000\uFF07]").freeze();
-
- private static final UnicodeSet defaultGroupingSeparators = (UnicodeSet) new UnicodeSet(
- dotEquivalents).addAll(commaEquivalents).addAll(otherGroupingSeparators).freeze();
- private static final UnicodeSet strictDefaultGroupingSeparators = (UnicodeSet) new UnicodeSet(
- strictDotEquivalents).addAll(strictCommaEquivalents).addAll(strictOtherGroupingSeparators).freeze();
-
- // When parsing a number with big exponential value, it requires to transform
- // the value into a string representation to construct BigInteger instance.
- // We want to set the maximum size because it can easily trigger OutOfMemoryException.
- // PARSE_MAX_EXPONENT is currently set to 1000, which is much bigger than
- // MAX_VALUE of Double (
- // See the problem reported by ticket#5698
- private static final int PARSE_MAX_EXPONENT = 1000;
-
- /**
- * CHANGED
- * Parse the given text into a number. The text is parsed beginning at
- * parsePosition, until an unparseable character is seen.
- * @param text The string to parse.
- * @param parsePosition The position at which to being parsing. Upon
- * return, the first unparseable character.
- * @param digits The DigitList to set to the parsed value.
- * @param isExponent If true, parse an exponent. This means no
- * infinite values and integer only.
- * @param status Upon return contains boolean status flags indicating
- * whether the value was infinite and whether it was positive.
- * @param currency return value for parsed currency, for generic
- * currency parsing mode, or null for normal parsing. In generic
- * currency parsing mode, any currency is parsed, not just the
- * currency that this formatter is set to.
- * @param negPrefix negative prefix pattern
- * @param negSuffix negative suffix pattern
- * @param posPrefix positive prefix pattern
- * @param negSuffix negative suffix pattern
- * @param type type of currency to parse against, LONG_NAME only or not.
- */
- private final boolean subparse(String text, ParsePosition parsePosition,
- DigitList digits, boolean isExponent,
- boolean status[], Currency currency[],
- String negPrefix, String negSuffix,
- String posPrefix, String posSuffix,
- int type)
- {
- int position = parsePosition.getIndex();
- int oldStart = parsePosition.getIndex();
-
- // Match padding before prefix
- if (formatWidth > 0 && padPosition == PAD_BEFORE_PREFIX) {
- position = skipPadding(text, position);
- }
-
- // Match positive and negative prefixes; prefer longest match.
- int posMatch = compareAffix(text, position, false, true, posPrefix, type, currency);
- int negMatch = compareAffix(text, position, true, true, negPrefix, type, currency);
- if (posMatch >= 0 && negMatch >= 0) {
- if (posMatch > negMatch) {
- negMatch = -1;
- } else if (negMatch > posMatch) {
- posMatch = -1;
- }
- }
- if (posMatch >= 0) {
- position += posMatch;
- } else if (negMatch >= 0) {
- position += negMatch;
- } else {
- parsePosition.setErrorIndex(position);
- return false;
- }
-
- // Match padding after prefix
- if (formatWidth > 0 && padPosition == PAD_AFTER_PREFIX) {
- position = skipPadding(text, position);
- }
-
- // process digits or Inf, find decimal position
- status[STATUS_INFINITE] = false;
- if (!isExponent && text.regionMatches(position,symbols.getInfinity(),0,
- symbols.getInfinity().length()))
- {
- position += symbols.getInfinity().length();
- status[STATUS_INFINITE] = true;
- } else {
- // We now have a string of digits, possibly with grouping symbols,
- // and decimal points. We want to process these into a DigitList.
- // We don't want to put a bunch of leading zeros into the DigitList
- // though, so we keep track of the location of the decimal point,
- // put only significant digits into the DigitList, and adjust the
- // exponent as needed.
-
- digits.decimalAt = digits.count = 0;
- char zero = symbols.getZeroDigit();
- char decimal = currencySignCount > 0 ?
- symbols.getMonetaryDecimalSeparator() : symbols.getDecimalSeparator();
- char grouping = symbols.getGroupingSeparator();
-
- String exponentSep = symbols.getExponentSeparator();
- boolean sawDecimal = false;
- boolean sawExponent = false;
- boolean sawDigit = false;
- long exponent = 0; // Set to the exponent value, if any
- int digit = 0;
-
- // strict parsing
- boolean strictParse = isParseStrict();
- boolean strictFail = false; // did we exit with a strict parse failure?
- int lastGroup = -1; // where did we last see a grouping separator?
- int gs2 = groupingSize2 == 0 ? groupingSize : groupingSize2;
-
- // Strict parsing leading zeroes. If a leading zero would
- // be forced by the pattern, then don't fail strict parsing.
- boolean strictLeadingZero = false;
- int leadingZeroPos = 0;
- int leadingZeroCount = 0;
-
- // equivalent grouping and decimal support
-
- // TODO markdavis Cache these if it makes a difference in performance.
- UnicodeSet decimalSet = new UnicodeSet(getSimilarDecimals(decimal, strictParse));
- UnicodeSet groupingSet = new UnicodeSet(strictParse ? strictDefaultGroupingSeparators : defaultGroupingSeparators)
- .add(grouping).removeAll(decimalSet);
-
- // we are guaranteed that
- // decimalSet contains the decimal, and
- // groupingSet contains the groupingSeparator
- // (unless decimal and grouping are the same, which should never happen. But in that case, groupingSet will just be empty.)
-
-
- // We have to track digitCount ourselves, because digits.count will
- // pin when the maximum allowable digits is reached.
- int digitCount = 0;
-
- int backup = -1;
- for (; position < text.length(); ++position)
- {
- char ch = text.charAt(position);
-
- /* We recognize all digit ranges, not only the Latin digit range
- * '0'..'9'. We do so by using the UCharacter.digit() method,
- * which converts a valid Unicode digit to the range 0..9.
- *
- * The character 'ch' may be a digit. If so, place its value
- * from 0 to 9 in 'digit'. First try using the locale digit,
- * which may or MAY NOT be a standard Unicode digit range. If
- * this fails, try using the standard Unicode digit ranges by
- * calling UCharacter.digit(). If this also fails, digit will
- * have a value outside the range 0..9.
- */
- digit = ch - zero;
- if (digit < 0 || digit > 9) digit = UCharacter.digit(ch, 10);
-
- if (digit == 0)
- {
- // Cancel out backup setting (see grouping handler below)
- if (strictParse && backup != -1) {
- // comma followed by digit, so group before comma is a
- // secondary group. If there was a group separator
- // before that, the group must == the secondary group
- // length, else it can be <= the the secondary group
- // length.
- if ((lastGroup != -1 && backup - lastGroup - 1 != gs2) ||
- (lastGroup == -1 && position - oldStart - 1 > gs2)) {
- strictFail = true;
- break;
- }
- lastGroup = backup;
- }
- backup = -1; // Do this BEFORE continue statement below!!!
- sawDigit = true;
-
- // Handle leading zeros
- if (digits.count == 0)
- {
- if (!sawDecimal) {
- if (strictParse && !isExponent) {
- // Allow leading zeros in exponents
- // Count leading zeros for checking later
- if (!strictLeadingZero) leadingZeroPos = position + 1;
- strictLeadingZero = true;
- ++leadingZeroCount;
- }
- // Ignore leading zeros in integer part of number.
- continue;
- }
-
- // If we have seen the decimal, but no significant digits yet,
- // then we account for leading zeros by decrementing the
- // digits.decimalAt into negative values.
- --digits.decimalAt;
- }
- else
- {
- ++digitCount;
- digits.append((char)(digit + '0'));
- }
- }
- else if (digit > 0 && digit <= 9) // [sic] digit==0 handled above
- {
- if (strictParse) {
- if (backup != -1) {
- if ((lastGroup != -1 && backup - lastGroup - 1 != gs2) ||
- (lastGroup == -1 && position - oldStart - 1 > gs2)) {
- strictFail = true;
- break;
- }
- lastGroup = backup;
- }
- }
-
- sawDigit = true;
- ++digitCount;
- digits.append((char)(digit + '0'));
-
- // Cancel out backup setting (see grouping handler below)
- backup = -1;
- }
- else if (!isExponent && decimalSet.contains(ch))
- {
- if (strictParse) {
- if (backup != -1 ||
- (lastGroup != -1 && position - lastGroup != groupingSize + 1)) {
- strictFail = true;
- break;
- }
- }
- // If we're only parsing integers, or if we ALREADY saw the
- // decimal, then don't parse this one.
- if (isParseIntegerOnly() || sawDecimal) break;
- digits.decimalAt = digitCount; // Not digits.count!
- sawDecimal = true;
-
- // Once we see a decimal character, we only accept that decimal character from then on.
- decimalSet.set(ch,ch);
- }
- else if (!isExponent && isGroupingUsed() && groupingSet.contains(ch))
- {
- if (sawDecimal) {
- break;
- }
- if (strictParse) {
- if ((!sawDigit || backup != -1)) {
- // leading group, or two group separators in a row
- strictFail = true;
- break;
- }
- }
- // Once we see a grouping character, we only accept that grouping character from then on.
- groupingSet.set(ch,ch);
-
- // Ignore grouping characters, if we are using them, but require
- // that they be followed by a digit. Otherwise we backup and
- // reprocess them.
- backup = position;
- }
- else if (!isExponent && !sawExponent &&
- text.regionMatches(position, exponentSep,
- 0, exponentSep.length()))
- {
- // Parse sign, if present
- boolean negExp = false;
- int pos = position + exponentSep.length();
- if (pos < text.length()) {
- ch = text.charAt(pos);
- if (ch == symbols.getPlusSign()) {
- ++pos;
- } else if (ch == symbols.getMinusSign()) {
- ++pos;
- negExp = true;
- }
- }
-
- DigitList exponentDigits = new DigitList();
- exponentDigits.count = 0;
- while (pos < text.length()) {
- digit = text.charAt(pos) - zero;
- if (digit < 0 || digit > 9) {
- /*
- Can't parse "[1E0]" when pattern is "0.###E0;[0.###E0]"
- Should update reassign the value of 'ch' in the
- code: digit = Character.digit(ch, 10);
- [Richard/GCL]
- */
- digit = UCharacter.digit(text.charAt(pos), 10);
- }
- if (digit >= 0 && digit <= 9) {
- exponentDigits.append((char)(digit + '0'));
- ++pos;
- } else {
- break;
- }
- }
-
- if (exponentDigits.count > 0) {
- // defer strict parse until we know we have a bona-fide exponent
- if (strictParse) {
- if (backup != -1 || lastGroup != -1) {
- strictFail = true;
- break;
- }
- }
-
- // Quick overflow check for exponential part.
- // Actual limit check will be done later in this code.
- if (exponentDigits.count > 10 /* maximum decimal digits for int */) {
- if (negExp) {
- // set underflow flag
- status[STATUS_UNDERFLOW] = true;
- } else {
- // set infinite flag
- status[STATUS_INFINITE] = true;
- }
- } else {
- exponentDigits.decimalAt = exponentDigits.count;
- exponent = exponentDigits.getLong();
- if (negExp) {
- exponent = -exponent;
- }
- }
- position = pos; // Advance past the exponent
- sawExponent = true;
- }
-
- break; // Whether we fail or succeed, we exit this loop
- }
- else break;
- }
-
- if (backup != -1) position = backup;
-
- // If there was no decimal point we have an integer
- if (!sawDecimal) digits.decimalAt = digitCount; // Not digits.count!
-
- // check for strict parse errors
- if (strictParse && strictLeadingZero) {
- if ((leadingZeroCount + digits.decimalAt) > this.getMinimumIntegerDigits()) {
- parsePosition.setIndex(oldStart);
- parsePosition.setErrorIndex(leadingZeroPos);
- return false;
- }
- }
- if (strictParse && !sawDecimal) {
- if (lastGroup != -1 && position - lastGroup != groupingSize + 1) {
- strictFail = true;
- }
- }
- if (strictFail) {
- // only set with strictParse and a leading zero error
- // leading zeros are an error with strict parsing except
- // immediately before nondigit (except group separator
- // followed by digit), or end of text.
-
- parsePosition.setIndex(oldStart);
- parsePosition.setErrorIndex(position);
- return false;
- }
-
- // Adjust for exponent, if any
- exponent += digits.decimalAt;
- if (exponent < -PARSE_MAX_EXPONENT) {
- status[STATUS_UNDERFLOW] = true;
- } else if (exponent > PARSE_MAX_EXPONENT) {
- status[STATUS_INFINITE] = true;
- } else {
- digits.decimalAt = (int)exponent;
- }
-
- // If none of the text string was recognized. For example, parse
- // "x" with pattern "#0.00" (return index and error index both 0)
- // parse "$" with pattern "$#0.00". (return index 0 and error index
- // 1).
- if (!sawDigit && digitCount == 0) {
- parsePosition.setIndex(oldStart);
- parsePosition.setErrorIndex(oldStart);
- return false;
- }
- }
-
- // Match padding before suffix
- if (formatWidth > 0 && padPosition == PAD_BEFORE_SUFFIX) {
- position = skipPadding(text, position);
- }
-
- // Match positive and negative suffixes; prefer longest match.
- if (posMatch >= 0) {
- posMatch = compareAffix(text, position, false, false, posSuffix, type, currency);
- }
- if (negMatch >= 0) {
- negMatch = compareAffix(text, position, true, false, negSuffix, type, currency);
- }
- if (posMatch >= 0 && negMatch >= 0) {
- if (posMatch > negMatch) {
- negMatch = -1;
- } else if (negMatch > posMatch) {
- posMatch = -1;
- }
- }
-
- // Fail if neither or both
- if ((posMatch >= 0) == (negMatch >= 0)) {
- parsePosition.setErrorIndex(position);
- return false;
- }
-
- position += (posMatch>=0 ? posMatch : negMatch);
-
- // Match padding after suffix
- if (formatWidth > 0 && padPosition == PAD_AFTER_SUFFIX) {
- position = skipPadding(text, position);
- }
-
- parsePosition.setIndex(position);
-
- status[STATUS_POSITIVE] = (posMatch >= 0);
-
- if (parsePosition.getIndex() == oldStart) {
- parsePosition.setErrorIndex(position);
- return false;
- }
- return true;
- }
-
- /**
- * Return characters that are used where this decimal is used.
- * @param decimal
- * @param strictParse
- * @return
- */
- private UnicodeSet getSimilarDecimals(char decimal, boolean strictParse) {
- if (dotEquivalents.contains(decimal)) {
- return strictParse ? strictDotEquivalents : dotEquivalents;
- }
- if (commaEquivalents.contains(decimal)) {
- return strictParse ? strictCommaEquivalents : commaEquivalents;
- }
- // if there is no match, return the character itself
- return new UnicodeSet().add(decimal);
- }
-
- /**
- * Starting at position, advance past a run of pad characters, if any.
- * Return the index of the first character after position that is not a pad
- * character. Result is >= position.
- */
- private final int skipPadding(String text, int position) {
- while (position < text.length() && text.charAt(position) == pad) {
- ++position;
- }
- return position;
- }
-
- /*
- * Return the length matched by the given affix, or -1 if none.
- * Runs of white space in the affix, match runs of white space in
- * the input. Pattern white space and input white space are
- * determined differently; see code.
- * @param text input text
- * @param pos offset into input at which to begin matching
- * @param isNegative
- * @param isPrefix
- * @param affixPat affix pattern used for currency affix comparison
- * @param type compare against currency type, LONG_NAME only or not.
- * @param currency return value for parsed currency, for generic
- * currency parsing mode, or null for normal parsing. In generic
- * currency parsing mode, any currency is parsed, not just the
- * currency that this formatter is set to.
- * @return length of input that matches, or -1 if match failure
- */
- private int compareAffix(String text, int pos,
- boolean isNegative, boolean isPrefix,
- String affixPat,
- int type,
- Currency[] currency) {
- if (currency != null || currencyChoice != null ||
- currencySignCount > 0) {
- return compareComplexAffix(affixPat, text, pos, type, currency);
- }
- if (isPrefix) {
- return compareSimpleAffix(isNegative ? negativePrefix : positivePrefix,
- text, pos);
- } else {
- return compareSimpleAffix(isNegative ? negativeSuffix : positiveSuffix,
- text, pos);
- }
-
- }
-
-
-
- /**
- * Return the length matched by the given affix, or -1 if none.
- * Runs of white space in the affix, match runs of white space in
- * the input. Pattern white space and input white space are
- * determined differently; see code.
- * @param affix pattern string, taken as a literal
- * @param input input text
- * @param pos offset into input at which to begin matching
- * @return length of input that matches, or -1 if match failure
- */
- private static int compareSimpleAffix(String affix, String input, int pos) {
- int start = pos;
- for (int i=0; inull
if rounding
- * is not in effect.
- * @see #setRoundingIncrement
- * @see #getRoundingMode
- * @see #setRoundingMode
- * @stable ICU 2.0
- */
-//#if defined(FOUNDATION10) || defined(ECLIPSE_FRAGMENT)
-//## public BigDecimal getRoundingIncrement() {
-//## if (roundingIncrementICU == null) return null;
-//## return new BigDecimal(roundingIncrementICU.toString());
-//## }
-//#else
- public java.math.BigDecimal getRoundingIncrement() {
- if (roundingIncrementICU == null) return null;
- return roundingIncrementICU.toBigDecimal();
- }
-//#endif
-
-//#if defined(FOUNDATION10)
-//#else
- // [NEW]
- /**
- * Set the rounding increment. This method also controls whether
- * rounding is enabled.
- * @param newValue A positive rounding increment, or null
or
- * BigDecimal(0.0)
to disable rounding.
- * @exception IllegalArgumentException if newValue
is < 0.0
- * @see #getRoundingIncrement
- * @see #getRoundingMode
- * @see #setRoundingMode
- * @stable ICU 2.0
- */
- public void setRoundingIncrement(java.math.BigDecimal newValue) {
- if (newValue == null) {
- setRoundingIncrement((BigDecimal)null);
- } else {
- setRoundingIncrement(new BigDecimal(newValue));
- }
- }
-//#endif
-
- // [NEW]
- /**
- * Set the rounding increment. This method also controls whether
- * rounding is enabled.
- * @param newValue A positive rounding increment, or null
or
- * BigDecimal(0.0)
to disable rounding.
- * @exception IllegalArgumentException if newValue
is < 0.0
- * @see #getRoundingIncrement
- * @see #getRoundingMode
- * @see #setRoundingMode
- * @stable ICU 3.6
- */
- public void setRoundingIncrement(BigDecimal newValue) {
- int i = newValue == null
- ? 0 : newValue.compareTo(BigDecimal.ZERO);
- if (i < 0) {
- throw new IllegalArgumentException("Illegal rounding increment");
- }
- if (i == 0) {
- setInternalRoundingIncrement(null);
- } else {
- setInternalRoundingIncrement(newValue);
- }
- setRoundingDouble();
- }
-
- // [NEW]
- /**
- * Set the rounding increment. This method also controls whether
- * rounding is enabled.
- * @param newValue A positive rounding increment, or 0.0 to disable
- * rounding.
- * @exception IllegalArgumentException if newValue
is < 0.0
- * @see #getRoundingIncrement
- * @see #getRoundingMode
- * @see #setRoundingMode
- * @stable ICU 2.0
- */
- public void setRoundingIncrement(double newValue) {
- if (newValue < 0.0) {
- throw new IllegalArgumentException("Illegal rounding increment");
- }
- roundingDouble = newValue;
- roundingDoubleReciprocal = 0.0d;
- if (newValue == 0.0d) {
- setRoundingIncrement((BigDecimal)null);
- } else {
- roundingDouble = newValue;
- if (roundingDouble < 1.0d) {
- double rawRoundedReciprocal = 1.0d/roundingDouble;
- setRoundingDoubleReciprocal(rawRoundedReciprocal);
- }
- setInternalRoundingIncrement(new BigDecimal(newValue));
- }
- }
-
-
- private void setRoundingDoubleReciprocal(double rawRoundedReciprocal) {
- roundingDoubleReciprocal = Math.rint(rawRoundedReciprocal);
- if (Math.abs(rawRoundedReciprocal - roundingDoubleReciprocal) > roundingIncrementEpsilon) {
- roundingDoubleReciprocal = 0.0d;
- }
- }
-
- static final double roundingIncrementEpsilon = 0.000000001;
-
- /**
- * Get the rounding mode.
- * @return A rounding mode, between BigDecimal.ROUND_UP
- * and BigDecimal.ROUND_UNNECESSARY
.
- * @see #setRoundingIncrement
- * @see #getRoundingIncrement
- * @see #setRoundingMode
- * @see java.math.BigDecimal
- * @stable ICU 2.0
- */
- public int getRoundingMode() {
- return roundingMode;
- }
-
- /**
- * Set the rounding mode. This has no effect unless the rounding
- * increment is greater than zero.
- * @param roundingMode A rounding mode, between
- * BigDecimal.ROUND_UP
and
- * BigDecimal.ROUND_UNNECESSARY
.
- * @exception IllegalArgumentException if roundingMode
- * is unrecognized.
- * @see #setRoundingIncrement
- * @see #getRoundingIncrement
- * @see #getRoundingMode
- * @see java.math.BigDecimal
- * @stable ICU 2.0
- */
- public void setRoundingMode(int roundingMode) {
- if (roundingMode < BigDecimal.ROUND_UP
- || roundingMode > BigDecimal.ROUND_UNNECESSARY) {
- throw new IllegalArgumentException("Invalid rounding mode: "
- + roundingMode);
- }
-
- this.roundingMode = roundingMode;
-
- if (getRoundingIncrement() == null) {
- setRoundingIncrement(Math.pow(10.0,(double)-getMaximumFractionDigits()));
- }
- }
-
- // [NEW]
- /**
- * Get the width to which the output of format()
is padded.
- * The width is counted in 16-bit code units.
- * @return the format width, or zero if no padding is in effect
- * @see #setFormatWidth
- * @see #getPadCharacter
- * @see #setPadCharacter
- * @see #getPadPosition
- * @see #setPadPosition
- * @stable ICU 2.0
- */
- public int getFormatWidth() {
- return formatWidth;
- }
-
- // [NEW]
- /**
- * Set the width to which the output of format()
is padded.
- * The width is counted in 16-bit code units.
- * This method also controls whether padding is enabled.
- * @param width the width to which to pad the result of
- * format()
, or zero to disable padding
- * @exception IllegalArgumentException if width
is < 0
- * @see #getFormatWidth
- * @see #getPadCharacter
- * @see #setPadCharacter
- * @see #getPadPosition
- * @see #setPadPosition
- * @stable ICU 2.0
- */
- public void setFormatWidth(int width) {
- if (width < 0) {
- throw new IllegalArgumentException("Illegal format width");
- }
- formatWidth = width;
- }
-
- // [NEW]
- /**
- * Get the character used to pad to the format width. The default is ' '.
- * @return the pad character
- * @see #setFormatWidth
- * @see #getFormatWidth
- * @see #setPadCharacter
- * @see #getPadPosition
- * @see #setPadPosition
- * @stable ICU 2.0
- */
- public char getPadCharacter() {
- return pad;
- }
-
- // [NEW]
- /**
- * Set the character used to pad to the format width. If padding
- * is not enabled, then this will take effect if padding is later
- * enabled.
- * @param padChar the pad character
- * @see #setFormatWidth
- * @see #getFormatWidth
- * @see #getPadCharacter
- * @see #getPadPosition
- * @see #setPadPosition
- * @stable ICU 2.0
- */
- public void setPadCharacter(char padChar) {
- pad = padChar;
- }
-
- // [NEW]
- /**
- * Get the position at which padding will take place. This is the location
- * at which padding will be inserted if the result of format()
- * is shorter than the format width.
- * @return the pad position, one of PAD_BEFORE_PREFIX
,
- * PAD_AFTER_PREFIX
, PAD_BEFORE_SUFFIX
, or
- * PAD_AFTER_SUFFIX
.
- * @see #setFormatWidth
- * @see #getFormatWidth
- * @see #setPadCharacter
- * @see #getPadCharacter
- * @see #setPadPosition
- * @see #PAD_BEFORE_PREFIX
- * @see #PAD_AFTER_PREFIX
- * @see #PAD_BEFORE_SUFFIX
- * @see #PAD_AFTER_SUFFIX
- * @stable ICU 2.0
- */
- public int getPadPosition() {
- return padPosition;
- }
-
- // [NEW]
- /**
- * Set the position at which padding will take place. This is the location
- * at which padding will be inserted if the result of format()
- * is shorter than the format width. This has no effect unless padding is
- * enabled.
- * @param padPos the pad position, one of PAD_BEFORE_PREFIX
,
- * PAD_AFTER_PREFIX
, PAD_BEFORE_SUFFIX
, or
- * PAD_AFTER_SUFFIX
.
- * @exception IllegalArgumentException if the pad position in
- * unrecognized
- * @see #setFormatWidth
- * @see #getFormatWidth
- * @see #setPadCharacter
- * @see #getPadCharacter
- * @see #getPadPosition
- * @see #PAD_BEFORE_PREFIX
- * @see #PAD_AFTER_PREFIX
- * @see #PAD_BEFORE_SUFFIX
- * @see #PAD_AFTER_SUFFIX
- * @stable ICU 2.0
- */
- public void setPadPosition(int padPos) {
- if (padPos < PAD_BEFORE_PREFIX || padPos > PAD_AFTER_SUFFIX) {
- throw new IllegalArgumentException("Illegal pad position");
- }
- padPosition = padPos;
- }
-
- // [NEW]
- /**
- * Return whether or not scientific notation is used.
- * @return true if this object formats and parses scientific notation
- * @see #setScientificNotation
- * @see #getMinimumExponentDigits
- * @see #setMinimumExponentDigits
- * @see #isExponentSignAlwaysShown
- * @see #setExponentSignAlwaysShown
- * @stable ICU 2.0
- */
- public boolean isScientificNotation() {
- return useExponentialNotation;
- }
-
- // [NEW]
- /**
- * Set whether or not scientific notation is used. When scientific notation
- * is used, the effective maximum number of integer digits is <= 8. If the
- * maximum number of integer digits is set to more than 8, the effective
- * maximum will be 1. This allows this call to generate a 'default' scientific
- * number format without additional changes.
- * @param useScientific true if this object formats and parses scientific
- * notation
- * @see #isScientificNotation
- * @see #getMinimumExponentDigits
- * @see #setMinimumExponentDigits
- * @see #isExponentSignAlwaysShown
- * @see #setExponentSignAlwaysShown
- * @stable ICU 2.0
- */
- public void setScientificNotation(boolean useScientific) {
- useExponentialNotation = useScientific;
- }
-
- // [NEW]
- /**
- * Return the minimum exponent digits that will be shown.
- * @return the minimum exponent digits that will be shown
- * @see #setScientificNotation
- * @see #isScientificNotation
- * @see #setMinimumExponentDigits
- * @see #isExponentSignAlwaysShown
- * @see #setExponentSignAlwaysShown
- * @stable ICU 2.0
- */
- public byte getMinimumExponentDigits() {
- return minExponentDigits;
- }
-
- // [NEW]
- /**
- * Set the minimum exponent digits that will be shown. This has no
- * effect unless scientific notation is in use.
- * @param minExpDig a value >= 1 indicating the fewest exponent digits
- * that will be shown
- * @exception IllegalArgumentException if minExpDig
< 1
- * @see #setScientificNotation
- * @see #isScientificNotation
- * @see #getMinimumExponentDigits
- * @see #isExponentSignAlwaysShown
- * @see #setExponentSignAlwaysShown
- * @stable ICU 2.0
- */
- public void setMinimumExponentDigits(byte minExpDig) {
- if (minExpDig < 1) {
- throw new IllegalArgumentException("Exponent digits must be >= 1");
- }
- minExponentDigits = minExpDig;
- }
-
- // [NEW]
- /**
- * Return whether the exponent sign is always shown.
- * @return true if the exponent is always prefixed with either the
- * localized minus sign or the localized plus sign, false if only negative
- * exponents are prefixed with the localized minus sign.
- * @see #setScientificNotation
- * @see #isScientificNotation
- * @see #setMinimumExponentDigits
- * @see #getMinimumExponentDigits
- * @see #setExponentSignAlwaysShown
- * @stable ICU 2.0
- */
- public boolean isExponentSignAlwaysShown() {
- return exponentSignAlwaysShown;
- }
-
- // [NEW]
- /**
- * Set whether the exponent sign is always shown. This has no effect
- * unless scientific notation is in use.
- * @param expSignAlways true if the exponent is always prefixed with either
- * the localized minus sign or the localized plus sign, false if only
- * negative exponents are prefixed with the localized minus sign.
- * @see #setScientificNotation
- * @see #isScientificNotation
- * @see #setMinimumExponentDigits
- * @see #getMinimumExponentDigits
- * @see #isExponentSignAlwaysShown
- * @stable ICU 2.0
- */
- public void setExponentSignAlwaysShown(boolean expSignAlways) {
- exponentSignAlwaysShown = expSignAlways;
- }
-
- /**
- * Return the grouping size. Grouping size is the number of digits between
- * grouping separators in the integer portion of a number. For example,
- * in the number "123,456.78", the grouping size is 3.
- * @see #setGroupingSize
- * @see NumberFormat#isGroupingUsed
- * @see DecimalFormatSymbols#getGroupingSeparator
- * @stable ICU 2.0
- */
- public int getGroupingSize () {
- return groupingSize;
- }
-
- /**
- * Set the grouping size. Grouping size is the number of digits between
- * grouping separators in the integer portion of a number. For example,
- * in the number "123,456.78", the grouping size is 3.
- * @see #getGroupingSize
- * @see NumberFormat#setGroupingUsed
- * @see DecimalFormatSymbols#setGroupingSeparator
- * @stable ICU 2.0
- */
- public void setGroupingSize (int newValue) {
- groupingSize = (byte)newValue;
- }
-
- // [NEW]
- /**
- * Return the secondary grouping size. In some locales one
- * grouping interval is used for the least significant integer
- * digits (the primary grouping size), and another is used for all
- * others (the secondary grouping size). A formatter supporting a
- * secondary grouping size will return a positive integer unequal
- * to the primary grouping size returned by
- * getGroupingSize()
. For example, if the primary
- * grouping size is 4, and the secondary grouping size is 2, then
- * the number 123456789 formats as "1,23,45,6789", and the pattern
- * appears as "#,##,###0".
- * @return the secondary grouping size, or a value less than
- * one if there is none
- * @see #setSecondaryGroupingSize
- * @see NumberFormat#isGroupingUsed
- * @see DecimalFormatSymbols#getGroupingSeparator
- * @stable ICU 2.0
- */
- public int getSecondaryGroupingSize () {
- return groupingSize2;
- }
-
- // [NEW]
- /**
- * Set the secondary grouping size. If set to a value less than 1,
- * then secondary grouping is turned off, and the primary grouping
- * size is used for all intervals, not just the least significant.
- * @see #getSecondaryGroupingSize
- * @see NumberFormat#setGroupingUsed
- * @see DecimalFormatSymbols#setGroupingSeparator
- * @stable ICU 2.0
- */
- public void setSecondaryGroupingSize (int newValue) {
- groupingSize2 = (byte)newValue;
- }
-
- // [NEW]
- /**
- * Returns the MathContext
- * used by this format.
- * @return desired MathContext
- * @see #mathContext
- * @see #getMathContext
- * @draft ICU 4.2
- * @provisional This API might change or be removed in a future release.
- */
- public MathContext getMathContextICU() {
- return mathContext;
- }
-
-//#if defined(FOUNDATION10) || defined(J2SE13) || defined(J2SE14) || defined(ECLIPSE_FRAGMENT)
-//#else
- // [NEW]
- /**
- * Returns the MathContext
- * used by this format.
- * @return desired MathContext
- * @see #mathContext
- * @see #getMathContext
- * @draft ICU 4.2
- * @provisional This API might change or be removed in a future release.
- */
- public java.math.MathContext getMathContext()
- {
- try
- {
- // don't allow multiple references
- return mathContext == null ?
- null :
- new java.math.MathContext(mathContext.getDigits(),
- java.math.RoundingMode.valueOf(mathContext.getRoundingMode()));
- }
- catch (Exception foo)
- {
- return null; // should never happen
- }
- }
-//#endif
-
- // [NEW]
- /**
- * Sets the MathContext used by this format.
- * @param newValue desired MathContext
- * @see #mathContext
- * @see #getMathContext
- * @draft ICU 4.2
- * @provisional This API might change or be removed in a future release.
- */
- public void setMathContextICU(MathContext newValue) {
- mathContext = newValue;
- }
-
-//#if defined(FOUNDATION10) || defined(J2SE13) || defined(J2SE14) || defined(ECLIPSE_FRAGMENT)
-//#else
- // [NEW]
- /**
- * Sets the MathContext used by this format.
- * @param newValue desired MathContext
- * @see #mathContext
- * @see #getMathContext
- * @draft ICU 4.2
- * @provisional This API might change or be removed in a future release.
- */
- public void setMathContext(java.math.MathContext newValue)
- {
- mathContext = new MathContext(newValue.getPrecision(),
- MathContext.SCIENTIFIC,
- false,
- (newValue.getRoundingMode()).ordinal()
- );
- }
-//#endif
-
- /**
- * Allows you to get the behavior of the decimal separator with integers.
- * (The decimal separator will always appear with decimals.)
- * min
is less than one then it is set
- * to one. If the maximum significant digits count is less than
- * min
, then it is set to min
. This
- * value has no effect unless areSignificantDigitsUsed() returns true.
- * @param min the fewest significant digits to be shown
- * @stable ICU 3.0
- */
- public void setMinimumSignificantDigits(int min) {
- if (min < 1) {
- min = 1;
- }
- // pin max sig dig to >= min
- int max = Math.max(maxSignificantDigits, min);
- minSignificantDigits = min;
- maxSignificantDigits = max;
- }
-
- /**
- * Sets the maximum number of significant digits that will be
- * displayed. If max
is less than one then it is set
- * to one. If the minimum significant digits count is greater
- * than max
, then it is set to max
. This
- * value has no effect unless areSignificantDigitsUsed() returns true.
- * @param max the most significant digits to be shown
- * @stable ICU 3.0
- */
- public void setMaximumSignificantDigits(int max) {
- if (max < 1) {
- max = 1;
- }
- // pin min sig dig to 1..max
- int min = Math.min(minSignificantDigits, max);
- minSignificantDigits = min;
- maxSignificantDigits = max;
- }
-
- /**
- * Returns true if significant digits are in use or false if
- * integer and fraction digit counts are in use.
- * @return true if significant digits are in use
- * @stable ICU 3.0
- */
- public boolean areSignificantDigitsUsed() {
- return useSignificantDigits;
- }
-
- /**
- * Sets whether significant digits are in use, or integer and
- * fraction digit counts are in use.
- * @param useSignificantDigits true to use significant digits, or
- * false to use integer and fraction digit counts
- * @stable ICU 3.0
- */
- public void setSignificantDigitsUsed(boolean useSignificantDigits) {
- this.useSignificantDigits = useSignificantDigits;
- }
-
- /**
- * Sets the Currency object used to display currency
- * amounts. This takes effect immediately, if this format is a
- * currency format. If this format is not a currency format, then
- * the currency object is used if and when this object becomes a
- * currency format through the application of a new pattern.
- * @param theCurrency new currency object to use. Must not be
- * null.
- * @stable ICU 2.2
- */
- public void setCurrency(Currency theCurrency) {
- // If we are a currency format, then modify our affixes to
- // encode the currency symbol for the given currency in our
- // locale, and adjust the decimal digits and rounding for the
- // given currency.
-
- super.setCurrency(theCurrency);
- if (theCurrency != null) {
- boolean[] isChoiceFormat = new boolean[1];
- String s = theCurrency.getName(symbols.getULocale(),
- Currency.SYMBOL_NAME,
- isChoiceFormat);
- symbols.setCurrencySymbol(s);
- symbols.setInternationalCurrencySymbol(theCurrency.getCurrencyCode());
- }
-
- if (currencySignCount > 0) {
- if (theCurrency != null) {
- setRoundingIncrement(theCurrency.getRoundingIncrement());
- int d = theCurrency.getDefaultFractionDigits();
- setMinimumFractionDigits(d);
- setMaximumFractionDigits(d);
- }
- expandAffixes(null);
- }
- }
-
- /**
- * Returns the currency in effect for this formatter. Subclasses
- * should override this method as needed. Unlike getCurrency(),
- * this method should never return null.
- * @internal
- * @deprecated This API is ICU internal only.
- */
- protected Currency getEffectiveCurrency() {
- Currency c = getCurrency();
- if (c == null) {
- c = Currency.getInstance(symbols.getInternationalCurrencySymbol());
- }
- return c;
- }
-
- /**
- * Sets the maximum number of digits allowed in the fraction portion of a
- * number. This override limits the fraction digit count to 340.
- * @see NumberFormat#setMaximumFractionDigits
- * @stable ICU 2.0
- */
- public void setMaximumFractionDigits(int newValue) {
- super.setMaximumFractionDigits(Math.min(newValue, DOUBLE_FRACTION_DIGITS));
- }
-
- /**
- * Sets the minimum number of digits allowed in the fraction portion of a
- * number. This override limits the fraction digit count to 340.
- * @see NumberFormat#setMinimumFractionDigits
- * @stable ICU 2.0
- */
- public void setMinimumFractionDigits(int newValue) {
- super.setMinimumFractionDigits(Math.min(newValue, DOUBLE_FRACTION_DIGITS));
- }
-
- /**
- * Sets whether {@link #parse(String, ParsePosition)} method returns BigDecimal.
- * The default value is false.
- * @param value true if {@link #parse(String, ParsePosition)} method returns
- * BigDecimal.
- * @stable ICU 3.6
- */
- public void setParseBigDecimal(boolean value) {
- parseBigDecimal = value;
- }
-
- /**
- * Returns whether {@link #parse(String, ParsePosition)} method returns BigDecimal.
- * @return true if {@link #parse(String, ParsePosition)} method returns BigDecimal.
- * @stable ICU 3.6
- */
- public boolean isParseBigDecimal() {
- return parseBigDecimal;
- }
-
-//#if defined(FOUNDATION10) || defined(J2SE13)
-//#else
- private void writeObject(ObjectOutputStream stream) throws IOException {
-// Doug, do we need this anymore?
-// if (roundingIncrementICU != null) {
-// roundingIncrement = roundingIncrementICU.toBigDecimal();
-// }
-
- // Ticket#6449
- // Format.Field instances are not serializable. When formatToCharacterIterator
- // is called, attributes (ArrayList) stores FieldPosition instances with
- // NumberFormat.Field. Because NumberFormat.Field is not serializable, we need
- // to clear the contents of the list when writeObject is called. We could remove
- // the field or make it transient, but it will break serialization compatibility.
- attributes.clear();
-
- stream.defaultWriteObject();
- }
-//#endif
-
- /**
- * First, read the default serializable fields from the stream. Then
- * if serialVersionOnStream
is less than 1, indicating that
- * the stream was written by JDK 1.1, initialize useExponentialNotation
- * to false, since it was not present in JDK 1.1.
- * Finally, set serialVersionOnStream back to the maximum allowed value so that
- * default serialization will work properly if this object is streamed out again.
- */
- private void readObject(ObjectInputStream stream)
- throws IOException, ClassNotFoundException
- {
- stream.defaultReadObject();
- /*Bug 4185761 validate fields
- [Richard/GCL]
- */
- // We only need to check the maximum counts because NumberFormat
- // .readObject has already ensured that the maximum is greater than the
- // minimum count.
- /*Commented for compatibility with previous version, and reserved for further use
- if (getMaximumIntegerDigits() > DOUBLE_INTEGER_DIGITS ||
- getMaximumFractionDigits() > DOUBLE_FRACTION_DIGITS) {
- throw new InvalidObjectException("Digit count out of range");
- }*/
- /* Truncate the maximumIntegerDigits to DOUBLE_INTEGER_DIGITS and maximumFractionDigits
- * to DOUBLE_FRACTION_DIGITS
- */
- if (getMaximumIntegerDigits() > DOUBLE_INTEGER_DIGITS) {
- setMaximumIntegerDigits(DOUBLE_INTEGER_DIGITS);
- }
- if (getMaximumFractionDigits() > DOUBLE_FRACTION_DIGITS) {
- setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS);
- }
- if (serialVersionOnStream < 2) {
- exponentSignAlwaysShown = false;
- setInternalRoundingIncrement(null);
- setRoundingDouble();
- roundingMode = BigDecimal.ROUND_HALF_EVEN;
- formatWidth = 0;
- pad = ' ';
- padPosition = PAD_BEFORE_PREFIX;
- if (serialVersionOnStream < 1) {
- // Didn't have exponential fields
- useExponentialNotation = false;
- }
- }
- if (serialVersionOnStream < 3) {
- // Versions prior to 3 do not store a currency object.
- // Create one to match the DecimalFormatSymbols object.
- setCurrencyForSymbols();
- }
- serialVersionOnStream = currentSerialVersion;
- digitList = new DigitList();
-
-//#if defined(FOUNDATION10)
-//#else
- if (roundingIncrement != null) {
- setInternalRoundingIncrement(new BigDecimal(roundingIncrement));
- setRoundingDouble();
- }
-//#endif
- }
-
-
- private void setInternalRoundingIncrement(BigDecimal value) {
- roundingIncrementICU = value;
-//#if defined(FOUNDATION10)
-//#else
- roundingIncrement = value == null ? null : value.toBigDecimal();
-//#endif
- }
-
- //----------------------------------------------------------------------
- // INSTANCE VARIABLES
- //----------------------------------------------------------------------
-
- private transient DigitList digitList = new DigitList();
-
- /**
- * The symbol used as a prefix when formatting positive numbers, e.g. "+".
- *
- * @serial
- * @see #getPositivePrefix
- */
- private String positivePrefix = "";
-
- /**
- * The symbol used as a suffix when formatting positive numbers.
- * This is often an empty string.
- *
- * @serial
- * @see #getPositiveSuffix
- */
- private String positiveSuffix = "";
-
- /**
- * The symbol used as a prefix when formatting negative numbers, e.g. "-".
- *
- * @serial
- * @see #getNegativePrefix
- */
- private String negativePrefix = "-";
-
- /**
- * The symbol used as a suffix when formatting negative numbers.
- * This is often an empty string.
- *
- * @serial
- * @see #getNegativeSuffix
- */
- private String negativeSuffix = "";
-
- /**
- * The prefix pattern for non-negative numbers. This variable corresponds
- * to positivePrefix
.
- *
- * expandAffix()
to
- * positivePrefix
to update the latter to reflect changes in
- * symbols
. If this variable is null
then
- * positivePrefix
is taken as a literal value that does not
- * change when symbols
changes. This variable is always
- * null
for DecimalFormat
objects older than
- * stream version 2 restored from stream.
- *
- * @serial
- */
- //[Richard/GCL]
- private String posPrefixPattern;
-
- /**
- * The suffix pattern for non-negative numbers. This variable corresponds
- * to positiveSuffix
. This variable is analogous to
- * posPrefixPattern
; see that variable for further
- * documentation.
- *
- * @serial
- */
- //[Richard/GCL]
- private String posSuffixPattern;
-
- /**
- * The prefix pattern for negative numbers. This variable corresponds
- * to negativePrefix
. This variable is analogous to
- * posPrefixPattern
; see that variable for further
- * documentation.
- *
- * @serial
- */
- //[Richard/GCL]
- private String negPrefixPattern;
-
- /**
- * The suffix pattern for negative numbers. This variable corresponds
- * to negativeSuffix
. This variable is analogous to
- * posPrefixPattern
; see that variable for further
- * documentation.
- *
- * @serial
- */
- //[Richard/GCL]
- private String negSuffixPattern;
-
-
-
- /**
- * Formatter for ChoiceFormat-based currency names. If this field
- * is not null, then delegate to it to format currency symbols.
- * @since ICU 2.6
- */
- private ChoiceFormat currencyChoice;
-
- /**
- * The multiplier for use in percent, permill, etc.
- *
- * @serial
- * @see #getMultiplier
- */
- private int multiplier = 1;
-
- /**
- * The number of digits between grouping separators in the integer
- * portion of a number. Must be greater than 0 if
- * NumberFormat.groupingUsed
is true.
- *
- * @serial
- * @see #getGroupingSize
- * @see NumberFormat#isGroupingUsed
- */
- private byte groupingSize = 3; // invariant, > 0 if useThousands
-
- // [NEW]
- /**
- * The secondary grouping size. This is only used for Hindi
- * numerals, which use a primary grouping of 3 and a secondary
- * grouping of 2, e.g., "12,34,567". If this value is less than
- * 1, then secondary grouping is equal to the primary grouping.
- *
- */
- private byte groupingSize2 = 0;
-
- /**
- * If true, forces the decimal separator to always appear in a formatted
- * number, even if the fractional part of the number is zero.
- *
- * @serial
- * @see #isDecimalSeparatorAlwaysShown
- */
- private boolean decimalSeparatorAlwaysShown = false;
-
-
- /**
- * The DecimalFormatSymbols
object used by this format.
- * It contains the symbols used to format numbers, e.g. the grouping separator,
- * decimal separator, and so on.
- *
- * @serial
- * @see #setDecimalFormatSymbols
- * @see DecimalFormatSymbols
- */
- private DecimalFormatSymbols symbols = null; // LIU new DecimalFormatSymbols();
-
- /**
- * True to use significant digits rather than integer and fraction
- * digit counts.
- * @serial
- * @since ICU 3.0
- */
- private boolean useSignificantDigits = false;
-
- /**
- * The minimum number of significant digits to show. Must be >= 1
- * and <= maxSignificantDigits. Ignored unless
- * useSignificantDigits == true.
- * @serial
- * @since ICU 3.0
- */
- private int minSignificantDigits = 1;
-
- /**
- * The maximum number of significant digits to show. Must be >=
- * minSignficantDigits. Ignored unless useSignificantDigits ==
- * true.
- * @serial
- * @since ICU 3.0
- */
- private int maxSignificantDigits = 6;
-
- /**
- * True to force the use of exponential (i.e. scientific) notation when formatting
- * numbers.
- * useExponentialNotation
is not true.
- * useExponentialNotation
is true.
- * @serial
- * @since AlphaWorks NumberFormat
- */
- private boolean exponentSignAlwaysShown = false;
-
-//#if defined(FOUNDATION10)
-//#else
- // [NEW]
- /**
- * The value to which numbers are rounded during formatting. For example,
- * if the rounding increment is 0.05, then 13.371 would be formatted as
- * 13.350, assuming 3 fraction digits. Has the value null
if
- * rounding is not in effect, or a positive value if rounding is in effect.
- * Default value null
.
- * @serial
- * @since AlphaWorks NumberFormat
- */
- // Note: this is kept in sync with roundingIncrementICU.
- // it is only kept around to avoid a conversion when formatting a java.math.BigDecimal
- private java.math.BigDecimal roundingIncrement = null;
-//#endif
-
- // [NEW]
- /**
- * The value to which numbers are rounded during formatting. For example,
- * if the rounding increment is 0.05, then 13.371 would be formatted as
- * 13.350, assuming 3 fraction digits. Has the value null
if
- * rounding is not in effect, or a positive value if rounding is in effect.
- * Default value null
.
- * WARNING: the roundingIncrement value is the one serialized.
- * @serial
- * @since AlphaWorks NumberFormat
- */
- private transient BigDecimal roundingIncrementICU = null;
-
- // [NEW]
- /**
- * The rounding increment as a double. If this value is <= 0, then no
- * rounding is done. This value is
- * roundingIncrementICU.doubleValue()
. Default value 0.0.
- */
- private transient double roundingDouble = 0.0;
-
- // [NEW]
- /**
- * If the roundingDouble is the reciprocal of an integer (the most common case!),
- * this is set to be that integer. Otherwise it is 0.0.
- */
- private transient double roundingDoubleReciprocal = 0.0;
-
- // [NEW]
- /**
- * The rounding mode. This value controls any rounding operations which
- * occur when applying a rounding increment or when reducing the number of
- * fraction digits to satisfy a maximum fraction digits limit. The value
- * may assume any of the BigDecimal
rounding mode values.
- * Default value BigDecimal.ROUND_HALF_EVEN
.
- * @serial
- * @since AlphaWorks NumberFormat
- */
- private int roundingMode = BigDecimal.ROUND_HALF_EVEN;
-
- // [NEW]
- /**
- * Operations on BigDecimal
numbers are controlled by a
- * {@link MathContext} object, which provides the context (precision and
- * other information) for the operation. The default MathContext
- * settings are digits=0, form=PLAIN, lostDigits=false,
- * roundingMode=ROUND_HALF_UP
; these settings perform fixed point
- * arithmetic with unlimited precision, as defined for the original BigDecimal
- * class in Java 1.1 and Java 1.2
- */
- private MathContext mathContext = new MathContext(0, MathContext.PLAIN); // context for plain unlimited math
-
- // [NEW]
- /**
- * The padded format width, or zero if there is no padding. Must
- * be >= 0. Default value zero.
- * @serial
- * @since AlphaWorks NumberFormat
- */
- private int formatWidth = 0;
-
- // [NEW]
- /**
- * The character used to pad the result of format to
- * formatWidth
, if padding is in effect. Default value ' '.
- * @serial
- * @since AlphaWorks NumberFormat
- */
- private char pad = ' ';
-
- // [NEW]
- /**
- * The position in the string at which the pad
character
- * will be inserted, if padding is in effect. Must have a value from
- * PAD_BEFORE_PREFIX
to PAD_AFTER_SUFFIX
.
- * Default value PAD_BEFORE_PREFIX
.
- * @serial
- * @since AlphaWorks NumberFormat
- */
- private int padPosition = PAD_BEFORE_PREFIX;
-
- /**
- * True if {@link #parse(String, ParsePosition)} to return BigDecimal
- * rather than Long, Double or BigDecimal except special values.
- * This property is introduced for J2SE 5 compatibility support.
- * @serial
- * @since ICU 3.6
- * @see #setParseBigDecimal(boolean)
- * @see #isParseBigDecimal()
- */
- private boolean parseBigDecimal = false;
-
- //----------------------------------------------------------------------
-
- static final int currentSerialVersion = 3;
-
- /**
- * The internal serial version which says which version was written
- * Possible values are:
- *
- *
- * @serial */
- private int serialVersionOnStream = currentSerialVersion;
-
- //----------------------------------------------------------------------
- // CONSTANTS
- //----------------------------------------------------------------------
-
- // [NEW]
- /**
- * Constant for useExponentialNotation
and minExponentDigits
.
- * getPadPosition()
and
- * setPadPosition()
specifying pad characters inserted before
- * the prefix.
- * @see #setPadPosition
- * @see #getPadPosition
- * @see #PAD_AFTER_PREFIX
- * @see #PAD_BEFORE_SUFFIX
- * @see #PAD_AFTER_SUFFIX
- * @stable ICU 2.0
- */
- public static final int PAD_BEFORE_PREFIX = 0;
-
- // [NEW]
- /**
- * Constant for getPadPosition()
and
- * setPadPosition()
specifying pad characters inserted after
- * the prefix.
- * @see #setPadPosition
- * @see #getPadPosition
- * @see #PAD_BEFORE_PREFIX
- * @see #PAD_BEFORE_SUFFIX
- * @see #PAD_AFTER_SUFFIX
- * @stable ICU 2.0
- */
- public static final int PAD_AFTER_PREFIX = 1;
-
- // [NEW]
- /**
- * Constant for getPadPosition()
and
- * setPadPosition()
specifying pad characters inserted before
- * the suffix.
- * @see #setPadPosition
- * @see #getPadPosition
- * @see #PAD_BEFORE_PREFIX
- * @see #PAD_AFTER_PREFIX
- * @see #PAD_AFTER_SUFFIX
- * @stable ICU 2.0
- */
- public static final int PAD_BEFORE_SUFFIX = 2;
-
- // [NEW]
- /**
- * Constant for getPadPosition()
and
- * setPadPosition()
specifying pad characters inserted after
- * the suffix.
- * @see #setPadPosition
- * @see #getPadPosition
- * @see #PAD_BEFORE_PREFIX
- * @see #PAD_AFTER_PREFIX
- * @see #PAD_BEFORE_SUFFIX
- * @stable ICU 2.0
- */
- public static final int PAD_AFTER_SUFFIX = 3;
-
- // Constants for characters used in programmatic (unlocalized) patterns.
- private static final char PATTERN_ZERO_DIGIT = '0';
- private static final char PATTERN_GROUPING_SEPARATOR = ',';
- private static final char PATTERN_DECIMAL_SEPARATOR = '.';
- private static final char PATTERN_DIGIT = '#';
- static final char PATTERN_SIGNIFICANT_DIGIT = '@';
- static final char PATTERN_EXPONENT = 'E'; // [NEW]
- static final char PATTERN_PLUS_SIGN = '+'; // [NEW]
-
- // Affix
- private static final char PATTERN_PER_MILLE = '\u2030';
- private static final char PATTERN_PERCENT = '%';
- static final char PATTERN_PAD_ESCAPE = '*'; // [NEW]
- /*Bug 4212072
- To meet the need of expandAffix(String, StirngBuffer)
- [Richard/GCL]
- */
- private static final char PATTERN_MINUS = '-'; //[Richard/GCL]
-
- // Other
- private static final char PATTERN_SEPARATOR = ';';
-
- // Pad escape is package private to allow access by DecimalFormatSymbols.
- // Also plus sign. Also exponent.
-
- /**
- * The CURRENCY_SIGN is the standard Unicode symbol for currency. It
- * is used in patterns and substitued with either the currency symbol,
- * or if it is doubled, with the international currency symbol. If the
- * CURRENCY_SIGN is seen in a pattern, then the decimal separator is
- * replaced with the monetary decimal separator.
- *
- * The CURRENCY_SIGN is not localized.
- */
- private static final char CURRENCY_SIGN = '\u00A4';
-
- private static final char QUOTE = '\'';
-
- /* Upper limit on integer and fraction digits for a Java double
- [Richard/GCL]
- */
- static final int DOUBLE_INTEGER_DIGITS = 309;
- static final int DOUBLE_FRACTION_DIGITS = 340;
-
- /**
- * When someone turns on scientific mode, we assume that more than this
- * number of digits is due to flipping from some other mode that didn't
- * restrict the maximum, and so we force 1 integer digit. We don't bother
- * to track and see if someone is using exponential notation with more than
- * this number, it wouldn't make sense anyway, and this is just to make sure
- * that someone turning on scientific mode with default settings doesn't
- * end up with lots of zeroes.
- */
- static final int MAX_SCIENTIFIC_INTEGER_DIGITS = 8;
-
-//#if defined(FOUNDATION10)
-//## // we're not compatible with other versions, since we have no java.math.BigDecimal field
-//## private static final long serialVersionUID = 2;
-//#else
- // Proclaim JDK 1.1 serial compatibility.
- private static final long serialVersionUID = 864413376551465018L;
-//#endif
-
-//#if defined(FOUNDATION10) || defined(J2SE13)
-//#else
- private ArrayList attributes = new ArrayList();
-//#endif
-
- /*
- * Following are used in currency format
- */
-/*
- // triple currency sign char array
- private static final char[] tripleCurrencySign = {0xA4, 0xA4, 0xA4};
- // triple currency sign string
- private static final String tripleCurrencyStr = new String(tripleCurrencySign);
-
- // default currency plural pattern char array
- private static final char[] defaultCurrencyPluralPatternChar = {0, '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4};
- // default currency plural pattern string
- private static final String defaultCurrencyPluralPattern = new String(defaultCurrencyPluralPatternChar);
-*/
-
- // pattern used in this formatter
- private String formatPattern = "";
- // style is only valid when decimal formatter is constructed by
- // DecimalFormat(pattern, decimalFormatSymbol, style)
- private int style = NumberFormat.NUMBERSTYLE;
- /*
- * Represents whether this is a currency format, and which
- * currency format style.
- * 0: not currency format type;
- * 1: currency style -- symbol name, such as "$" for US dollar.
- * 2: currency style -- ISO name, such as USD for US dollar.
- * 3: currency style -- plural long name, such as "US Dollar" for
- * "1.00 US Dollar", or "US Dollars" for
- * "3.00 US Dollars".
- */
- private int currencySignCount = 0;
-
-
- /* For parsing purose,
- * Need to remember all prefix patterns and suffix patterns of
- * every currency format pattern,
- * including the pattern of default currecny style, ISO currency style,
- * and plural currency style. And the patterns are set through applyPattern.
- * Following are used to represent the affix patterns in currency plural
- * formats.
- */
- private static final class AffixForCurrency {
- // negative prefix pattern
- private String negPrefixPatternForCurrency = null;
- // negative suffix pattern
- private String negSuffixPatternForCurrency = null;
- // positive prefix pattern
- private String posPrefixPatternForCurrency = null;
- // positive suffix pattern
- private String posSuffixPatternForCurrency = null;
- private int patternType;
-
- public AffixForCurrency() {
- patternType = Currency.SYMBOL_NAME;
- }
-
- public AffixForCurrency(String negPrefix, String negSuffix,
- String posPrefix, String posSuffix,
- int type) {
- negPrefixPatternForCurrency = negPrefix;
- negSuffixPatternForCurrency = negSuffix;
- posPrefixPatternForCurrency = posPrefix;
- posSuffixPatternForCurrency = posSuffix;
- patternType = type;
- }
-
- public String getNegPrefix() {
- return negPrefixPatternForCurrency;
- }
-
- public String getNegSuffix() {
- return negSuffixPatternForCurrency;
- }
-
- public String getPosPrefix() {
- return posPrefixPatternForCurrency;
- }
-
- public String getPosSuffix() {
- return posSuffixPatternForCurrency;
- }
-
- public int getPatternType() {
- return patternType;
- }
- }
- // Affix patter set for currency.
- // It is a set of AffixForCurrency,
- // each element of the set saves the negative prefix,
- // negative suffix, positive prefix, and positive suffix of a pattern.
- private transient Set affixPatternsForCurrency = null;
-
- // For currency parsing, since currency parsing need to parse
- // against all currency patterns, before the parsing, need to set up
- // the affix patterns for currency.
- private transient boolean isReadyForParsing = false;
-
- // Information needed for DecimalFormat to format/parse currency plural.
- private CurrencyPluralInfo currencyPluralInfo = null;
-
-}
-
-//eof
+//##header J2SE15
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2009, International Business Machines Corporation and *
+ * others. All Rights Reserved. *
+ *******************************************************************************
+ */
+package com.ibm.icu.text;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.math.BigInteger;
+import java.text.ChoiceFormat;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+//#if defined(FOUNDATION10)
+//#else
+import java.io.ObjectOutputStream;
+//#endif
+
+//#if defined(FOUNDATION10) || defined(J2SE13)
+//#else
+import java.text.AttributedCharacterIterator;
+import java.text.AttributedString;
+import java.text.Format;
+import java.util.ArrayList;
+//#endif
+
+import com.ibm.icu.impl.UCharacterProperty;
+import com.ibm.icu.impl.Utility;
+import com.ibm.icu.lang.UCharacter;
+import com.ibm.icu.math.BigDecimal;
+import com.ibm.icu.util.Currency;
+import com.ibm.icu.util.CurrencyAmount;
+import com.ibm.icu.util.ULocale;
+import com.ibm.icu.math.MathContext;
+
+//This is an enhanced version of DecimalFormat that is based on the standard version in the JDK.
+/**
+ * DecimalFormat
is 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.00", "USD123.00", "123.00 US dollars").
+ * All of these flavors can be easily localized.
+ *
+ *
+ * NumberFormat
's factory methods such
+ * as {@link NumberFormat#getInstance}. Do not call the DecimalFormat
+ * constructors directly, unless you know what you are doing, since the
+ * {@link NumberFormat} factory methods may return subclasses other than
+ * DecimalFormat
. If you need to customize the format object, do
+ * something like this:
+ *
+ *
+ *
+ *
+ * NumberFormat f = NumberFormat.getInstance(loc);
+ * if (f instanceof DecimalFormat) {
+ * ((DecimalFormat) f).setDecimalSeparatorAlwaysShown(true);
+ * }
+ *
+ *
+ * // 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) {}
+ * }
+ * }
+ * // Print out a number using the localized number, currency,
+ * // percent, scientific, integer, iso currency, and plural currency
+ * // format for each locale
+ * ULocale locale = new ULocale("en_US");
+ * double myNumber = 1234.56;
+ * for (int j=NumberFormat.NUMBERSTYLE; j<=NumberFormat.PLURALCURRENCYSTYLE; ++j) {
+ * NumberFormat format = NumberFormat.getInstance(locale, j);
+ * 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) {}
+ * }
+ *
+ * Patterns
+ *
+ * DecimalFormat
consists of a pattern and a set of
+ * symbols. The pattern may be set directly using
+ * {@link #applyPattern}, 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.
+ *
+ * Special Pattern Characters
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * Symbol
+ * Location
+ * Localized?
+ * Meaning
+ *
+ * 0
+ * Number
+ * Yes
+ * Digit
+ *
+ * 1-9
+ * Number
+ * Yes
+ * '1' through '9' indicate rounding.
+ *
+ *
+ * @
+ * Number
+ * No
+ * Significant digit
+ *
+ * #
+ * Number
+ * Yes
+ * Digit, zero shows as absent
+ *
+ * .
+ * Number
+ * Yes
+ * Decimal separator or monetary decimal separator
+ *
+ * -
+ * Number
+ * Yes
+ * Minus sign
+ *
+ * ,
+ * Number
+ * Yes
+ * Grouping separator
+ *
+ * E
+ * Number
+ * Yes
+ * Separates mantissa and exponent in scientific notation.
+ * Need not be quoted in prefix or suffix.
+ *
+ * +
+ * Exponent
+ * Yes
+ * Prefix positive exponents with localized plus sign.
+ * Need not be quoted in prefix or suffix.
+ *
+ * ;
+ * Subpattern boundary
+ * Yes
+ * Separates positive and negative subpatterns
+ *
+ * %
+ * Prefix or suffix
+ * Yes
+ * Multiply by 100 and show as percentage
+ *
+ * \u2030
+ * Prefix or suffix
+ * Yes
+ * Multiply by 1000 and show as per mille
+ *
+ * ¤
(\u00A4
)
+ * Prefix or suffix
+ * No
+ * Currency sign, replaced by currency symbol. If
+ * doubled, replaced by international currency symbol.
+ * If tripled, replaced by currency plural names, for example,
+ * "US dollar" or "US dollars" for America.
+ * If present in a pattern, the monetary decimal separator
+ * is used instead of the decimal separator.
+ *
+ * '
+ * Prefix or suffix
+ * No
+ * Used to quote special characters in a prefix or suffix,
+ * for example, "'#'#"
formats 123 to
+ * "#123"
. To create a single quote
+ * itself, use two in a row: "# o''clock"
.
+ *
+ * *
+ * Prefix or suffix boundary
+ * Yes
+ * Pad escape, precedes pad character
+ * 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. That means that
+ * "#,##0.0#;(#)" has precisely the same result as "#,##0.0#;(#,##0.0#)".
+ *
+ * DecimalFormat
to throw an {@link IllegalArgumentException}
+ * with a message that describes the problem.
+ *
+ * Pattern BNF
+ *
+ *
+ * pattern := subpattern (';' subpattern)?
+ * subpattern := prefix? number exponent? suffix?
+ * number := (integer ('.' fraction)?) | sigDigits
+ * prefix := '\u0000'..'\uFFFD' - specialCharacters
+ * suffix := '\u0000'..'\uFFFD' - specialCharacters
+ * integer := '#'* '0'* '0'
+ * fraction := '0'* '#'*
+ * sigDigits := '#'* '@' '@'* '#'*
+ * exponent := 'E' '+'? '0'* '0'
+ * padSpec := '*' padChar
+ * padChar := '\u0000'..'\uFFFD' - quote
+ *
+ * Notation:
+ * X* 0 or more instances of X
+ * X? 0 or 1 instances of X
+ * X|Y either X or Y
+ * C..D any character from C up to D, inclusive
+ * S-T characters in S, except those in T
+ *
+ * The first subpattern is for positive numbers. The second (optional)
+ * subpattern is for negative numbers.
+ *
+ *
+ *
+ * padSpec
may appear before the prefix,
+ * after the prefix, before the suffix, after the suffix, or not at all.
+ *
+ * Parsing
+ *
+ * DecimalFormat
parses all Unicode characters that represent
+ * decimal digits, as defined by {@link UCharacter#digit}. In addition,
+ * 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 output.
+ *
+ * null
and leaves the parse position
+ * unchanged. The convenience method {@link #parse(String)}
+ * indicates parse failure by throwing a {@link java.text.ParseException}.
+ *
+ * Formatting
+ *
+ *
+ *
+ * NaN
is represented as a single character, typically
+ * \uFFFD
. This character is determined by the
+ * {@link DecimalFormatSymbols} object. This is the only value for which
+ * the prefixes and suffixes are not used.
+ *
+ * \u221E
, with the positive or negative prefixes and suffixes
+ * applied. The infinity character is determined by the
+ * {@link DecimalFormatSymbols} object.
+ *
+ * Scientific Notation
+ *
+ * DecimalFormat
supports arbitrary mantissas.
+ * 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".
+ *
+ *
+ *
+ *
+ *
+ * Significant Digits
+ *
+ * DecimalFormat
has two ways of controlling how many
+ * digits are shows: (a) significant digits counts, or (b) integer and
+ * fraction digit counts. Integer and fraction digit counts are
+ * described above. When a formatter is using 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 however many integer and fraction
+ * digits are required to display the specified number of significant
+ * digits. Examples:
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * Pattern
+ * Minimum significant digits
+ * Maximum significant digits
+ * Number
+ * Output of format()
+ *
+ * @@@
+ * 3
+ * 3
+ * 12345
+ * 12300
+ *
+ * @@@
+ * 3
+ * 3
+ * 0.12345
+ * 0.123
+ *
+ * @@##
+ * 2
+ * 4
+ * 3.14159
+ * 3.142
+ *
+ * @@##
+ * 2
+ * 4
+ * 1.23004
+ * 1.23
+ *
+ *
+ *
+ * '@'
and '#'
+ * characters. The minimum number of significant digits is the number
+ * of '@'
characters. The maximum number of significant
+ * digits is the number of '@'
characters plus the number
+ * of '#'
characters following on the right. For
+ * example, the pattern "@@@"
indicates exactly 3
+ * significant digits. The pattern "@##"
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
+ * "@##"
formats the number 0.1203 as
+ * "0.12"
.
+ *
+ * '0'
pattern character.
+ * Patterns such as "@00"
or "@.###"
are
+ * disallowed.
+ *
+ * '#'
characters may be prepended to
+ * the left of the leftmost '@'
character. These have no
+ * effect on the minimum and maximum significant digits counts, but
+ * may be used to position grouping separators. For example,
+ * "#,#@#"
indicates a minimum of one significant digits,
+ * a maximum of two significant digits, and a grouping size of three.
+ *
+ * '@'
pattern character. Alternatively,
+ * call {@link #setSignificantDigitsUsed setSignificantDigitsUsed(true)}.
+ *
+ * '@'
pattern
+ * character. Alternatively, call {@link #setSignificantDigitsUsed
+ * setSignificantDigitsUsed(false)}.
+ *
+ * getMinimumSignificantDigits() - 1
, and a maximum fraction digit
+ * count of getMaximumSignificantDigits() - 1
. For example, the
+ * pattern "@@###E0"
is equivalent to "0.0###E0"
.
+ *
+ *
+ * Padding
+ *
+ * DecimalFormat
supports padding the result of
+ * {@link #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, and
+ * can be localized using {@link DecimalFormatSymbols#setPadEscape}. For
+ * example, "$*x#,##0.00"
formats 123 to "$xx123.00"
,
+ * and 1234 to "$1,234.00"
.
+ *
+ *
+ *
+ *
+ * "* #0 o''clock"
, the format width is 10.
+ *
+ * char
s).
+ *
+ * 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
+ * escapes the following character. If there is no character after
+ * the pad escape, then the pattern is illegal.
+ *
+ * DecimalFormat
supports rounding to a specific increment. For
+ * example, 1230 rounded to the nearest 50 is 1250. 1.234 rounded to the
+ * nearest 0.65 is 1.3. The rounding increment may be specified through the API
+ * or in a pattern. To specify a rounding increment in a pattern, include the
+ * increment in the pattern itself. "#,#50" specifies a rounding increment of
+ * 50. "#,##0.05" specifies a rounding increment of 0.05.
+ *
+ *
+ *
+ *
+ * Synchronization
+ *
+ * DecimalFormat
objects are not synchronized. Multiple
+ * threads should not access one formatter concurrently.
+ *
+ * @see java.text.Format
+ * @see NumberFormat
+ * @author Mark Davis
+ * @author Alan Liu
+ * @stable ICU 2.0
+ */
+public class DecimalFormat extends NumberFormat {
+
+ /**
+ * Create a DecimalFormat using the default pattern and symbols
+ * for the default locale. This is a convenient way to obtain a
+ * DecimalFormat when internationalization is not the main concern.
+ * Double
objects are returned to
+ * represent non-integral values which cannot be stored in a
+ * BigDecimal
. These are NaN
, infinity,
+ * -infinity, and -0.0. If {@link #isParseBigDecimal()} is false (the
+ * default), all other values are returned as Long
,
+ * BigInteger
, or BigDecimal
values,
+ * in that order of preference. If {@link #isParseBigDecimal()} is true,
+ * all other values are returned as BigDecimal
valuse.
+ * If the parse fails, null is returned.
+ * @param text the string to be parsed
+ * @param parsePosition defines the position where parsing is to begin,
+ * and upon return, the position where parsing left off. If the position
+ * has not changed upon return, then parsing failed.
+ * @return a Number
object with the parsed value or
+ * null
if the parse failed
+ * @stable ICU 2.0
+ */
+ public Number parse(String text, ParsePosition parsePosition) {
+ return (Number) parse(text, parsePosition, false);
+ }
+
+ // [NEW]
+ /**
+ * Parses text from the given string as a CurrencyAmount. Unlike
+ * the parse() method, this method will attempt to parse a generic
+ * currency name, searching for a match of this object's locale's
+ * currency display names, or for a 3-letter ISO currency code.
+ * This method will fail if this format is not a currency format,
+ * that is, if it does not contain the currency pattern symbol
+ * (U+00A4) in its prefix or suffix.
+ *
+ * @param text the string to parse
+ * @param pos input-output position; on input, the position within
+ * text to match; must have 0 <= pos.getIndex() < text.length();
+ * on output, the position after the last matched character. If
+ * the parse fails, the position in unchanged upon output.
+ * @return a CurrencyAmount, or null upon failure
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ CurrencyAmount parseCurrency(String text, ParsePosition pos) {
+ return (CurrencyAmount) parse(text, pos, true);
+ }
+
+ /**
+ * Parses the given text as either a Number or a CurrencyAmount.
+ * @param text the string to parse
+ * @param parsePosition input-output position; on input, the
+ * position within text to match; must have 0 <= pos.getIndex() <
+ * text.length(); on output, the position after the last matched
+ * character. If the parse fails, the position in unchanged upon
+ * output.
+ * @param parseCurrency if true, a CurrencyAmount is parsed and
+ * returned; otherwise a Number is parsed and returned
+ * @return a Number or CurrencyAmount or null
+ */
+ private Object parse(String text, ParsePosition parsePosition, boolean parseCurrency) {
+ int backup;
+ int i = backup = parsePosition.getIndex();
+
+ // Handle NaN as a special case:
+
+ // Skip padding characters, if around prefix
+ if (formatWidth > 0 && (padPosition == PAD_BEFORE_PREFIX ||
+ padPosition == PAD_AFTER_PREFIX)) {
+ i = skipPadding(text, i);
+ }
+ if (text.regionMatches(i, symbols.getNaN(),
+ 0, symbols.getNaN().length())) {
+ i += symbols.getNaN().length();
+ // Skip padding characters, if around suffix
+ if (formatWidth > 0 && (padPosition == PAD_BEFORE_SUFFIX ||
+ padPosition == PAD_AFTER_SUFFIX)) {
+ i = skipPadding(text, i);
+ }
+ parsePosition.setIndex(i);
+ return new Double(Double.NaN);
+ }
+
+ // NaN parse failed; start over
+ i = backup;
+
+ boolean[] status = new boolean[STATUS_LENGTH];
+ Currency[] currency = parseCurrency ? new Currency[1] : null;
+ if (currencySignCount > 0) {
+ if (!parseForCurrency(text, parsePosition, parseCurrency,
+ currency, status)) {
+ return null;
+ }
+ } else {
+ if (!subparse(text, parsePosition, digitList, false, status,
+ currency, negPrefixPattern, negSuffixPattern,
+ posPrefixPattern, posSuffixPattern, Currency.SYMBOL_NAME)) {
+ parsePosition.setIndex(backup);
+ return null;
+ }
+ }
+
+ Number n = null;
+
+ // Handle infinity
+ if (status[STATUS_INFINITE]) {
+ n = new Double(status[STATUS_POSITIVE]
+ ? Double.POSITIVE_INFINITY
+ : Double.NEGATIVE_INFINITY);
+ }
+
+ // Handle underflow
+ else if (status[STATUS_UNDERFLOW]) {
+ n = status[STATUS_POSITIVE] ? new Double("0.0") : new Double("-0.0");
+ }
+
+ // Handle -0.0
+ else if (!status[STATUS_POSITIVE] && digitList.isZero()) {
+ n = new Double("-0.0");
+ }
+
+ else {
+ // Do as much of the multiplier conversion as possible without
+ // losing accuracy.
+ int mult = multiplier; // Don't modify this.multiplier
+ while (mult % 10 == 0) {
+ --digitList.decimalAt;
+ mult /= 10;
+ }
+
+ // Handle integral values
+ if (!parseBigDecimal && mult == 1 && digitList.isIntegral()) {
+ // hack quick long
+ if (digitList.decimalAt < 12) { // quick check for long
+ long l = 0;
+ if (digitList.count > 0) {
+ int nx = 0;
+ while (nx < digitList.count) {
+ l = l * 10 + (char)digitList.digits[nx++] - '0';
+ }
+ while (nx++ < digitList.decimalAt) {
+ l *= 10;
+ }
+ if (!status[STATUS_POSITIVE]) {
+ l = -l;
+ }
+ }
+ n = new Long(l);
+ } else {
+ BigInteger big = digitList.getBigInteger(status[STATUS_POSITIVE]);
+ n = (big.bitLength() < 64) ?
+ (Number) new Long(big.longValue()) : (Number) big;
+ }
+ }
+ // Handle non-integral values or the case where parseBigDecimal is set
+ else {
+ BigDecimal big = digitList.getBigDecimalICU(status[STATUS_POSITIVE]);
+ n = big;
+ if (mult != 1) {
+ n = big.divide(BigDecimal.valueOf(mult), mathContext);
+ }
+ }
+ }
+
+ // Assemble into CurrencyAmount if necessary
+ return parseCurrency ? (Object) new CurrencyAmount(n, currency[0])
+ : (Object) n;
+ }
+
+
+ private boolean parseForCurrency(String text, ParsePosition parsePosition,
+ boolean parseCurrency, Currency[] currency,
+ boolean[] status) {
+ int origPos = parsePosition.getIndex();
+ if (!isReadyForParsing) {
+ int savedCurrencySignCount = currencySignCount;
+ setupCurrencyAffixForAllPatterns();
+ // reset pattern back
+ if (savedCurrencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
+ applyPatternWithoutExpandAffix(formatPattern, false);
+ } else {
+ applyPattern(formatPattern, false);
+ }
+ isReadyForParsing = true;
+ }
+ int maxPosIndex = origPos;
+ int maxErrorPos = -1;
+ boolean[] savedStatus = null;
+ // First, parse against current pattern.
+ // Since current pattern could be set by applyPattern(),
+ // it could be an arbitrary pattern, and it may not be the one
+ // defined in current locale.
+ boolean[] tmpStatus = new boolean[STATUS_LENGTH];
+ ParsePosition tmpPos = new ParsePosition(origPos);
+ DigitList tmpDigitList = new DigitList();
+ boolean found;
+ if (style == NumberFormat.PLURALCURRENCYSTYLE) {
+ found = subparse(text, tmpPos, tmpDigitList, false,
+ tmpStatus, currency, negPrefixPattern, negSuffixPattern,
+ posPrefixPattern, posSuffixPattern, Currency.LONG_NAME);
+ } else {
+ found = subparse(text, tmpPos, tmpDigitList, false,
+ tmpStatus, currency, negPrefixPattern, negSuffixPattern,
+ posPrefixPattern, posSuffixPattern, Currency.SYMBOL_NAME);
+ }
+ if (found) {
+ if (tmpPos.getIndex() > maxPosIndex) {
+ maxPosIndex = tmpPos.getIndex();
+ savedStatus = tmpStatus;
+ digitList = tmpDigitList;
+ }
+ } else {
+ maxErrorPos = tmpPos.getErrorIndex();
+ }
+ // Then, parse against affix patterns.
+ // Those are currency patterns and currency plural patterns
+ // defined in the locale.
+ Iterator iter = affixPatternsForCurrency.iterator();
+ while (iter.hasNext()) {
+ AffixForCurrency affix = (AffixForCurrency)iter.next();
+
+ tmpStatus = new boolean[STATUS_LENGTH];
+ tmpPos = new ParsePosition(origPos);
+ tmpDigitList = new DigitList();
+ boolean result = subparse(text, tmpPos, tmpDigitList, false,
+ tmpStatus, currency, affix.getNegPrefix(),
+ affix.getNegSuffix(), affix.getPosPrefix(),
+ affix.getPosSuffix(), affix.getPatternType());
+ if (result) {
+ found = true;
+ if (tmpPos.getIndex() > maxPosIndex) {
+ maxPosIndex = tmpPos.getIndex();
+ savedStatus = tmpStatus;
+ digitList = tmpDigitList;
+ }
+ } else {
+ maxErrorPos = (tmpPos.getErrorIndex() > maxErrorPos) ?
+ tmpPos.getErrorIndex() : maxErrorPos;
+ }
+ }
+ // Finally, parse against simple affix to find the match.
+ // For example, in TestMonster suite,
+ // if the to-be-parsed text is "-\u00A40,00".
+ // complexAffixCompare will not find match,
+ // since there is no ISO code matches "\u00A4",
+ // and the parse stops at "\u00A4".
+ // We will just use simple affix comparison (look for exact match)
+ // to pass it.
+ tmpStatus = new boolean[STATUS_LENGTH];
+ tmpPos = new ParsePosition(origPos);
+ tmpDigitList = new DigitList();
+ int savedCurrencySignCount = currencySignCount;
+ // set currencySignCount to 0 so that compareAffix function will
+ // fall to compareSimpleAffix path, not compareComplexAffix path.
+ currencySignCount = 0;
+ boolean result = subparse(text, tmpPos, tmpDigitList, false,
+ tmpStatus, currency, negativePrefix, negativeSuffix,
+ positivePrefix, positiveSuffix, Currency.SYMBOL_NAME);
+ currencySignCount = savedCurrencySignCount;
+ if (result) {
+ if (tmpPos.getIndex() > maxPosIndex) {
+ maxPosIndex = tmpPos.getIndex();
+ savedStatus = tmpStatus;
+ digitList = tmpDigitList;
+ }
+ found = true;
+ } else {
+ maxErrorPos = (tmpPos.getErrorIndex() > maxErrorPos) ?
+ tmpPos.getErrorIndex() : maxErrorPos;
+ }
+
+ if (!found) {
+ //parsePosition.setIndex(origPos);
+ parsePosition.setErrorIndex(maxErrorPos);
+ } else {
+ parsePosition.setIndex(maxPosIndex);
+ parsePosition.setErrorIndex(-1);
+ for (int index = 0; index < STATUS_LENGTH; ++index) {
+ status[index] = savedStatus[index];
+ }
+ }
+ return found;
+ }
+
+
+ // Get affix patterns used in locale's currency pattern
+ // (NumberPatterns[1]) and currency plural pattern (CurrencyUnitPatterns).
+ private void setupCurrencyAffixForAllPatterns() {
+ if (currencyPluralInfo == null) {
+ currencyPluralInfo = new CurrencyPluralInfo(symbols.getLocale());
+ }
+ affixPatternsForCurrency = new HashSet();
+
+ // save the current pattern, since it will be changed by
+ // applyPatternWithoutExpandAffix
+ String savedFormatPattern = formatPattern;
+
+ // CURRENCYSTYLE and ISOCURRENCYSTYLE should have the same
+ // prefix and suffix, so, only need to save one of them.
+ // Here, chose onlyApplyPatternWithoutExpandAffix without
+ // saving the actualy pattern in 'pattern' data member.
+ // TODO: is it uloc?
+ applyPatternWithoutExpandAffix(getPattern(symbols.getLocale(), NumberFormat.CURRENCYSTYLE), false);
+ AffixForCurrency affixes = new AffixForCurrency(negPrefixPattern,
+ negSuffixPattern,
+ posPrefixPattern,
+ posSuffixPattern,
+ Currency.SYMBOL_NAME);
+ affixPatternsForCurrency.add(affixes);
+
+ // add plural pattern
+ Iterator iter = currencyPluralInfo.pluralPatternIterator();
+ Set currencyUnitPatternSet = new HashSet();
+ while (iter.hasNext()) {
+ String pluralCount = (String)iter.next();
+ String currencyPattern = (String)currencyPluralInfo.getCurrencyPluralPattern(pluralCount);
+ if (currencyPattern != null &&
+ currencyUnitPatternSet.contains(currencyPattern) == false) {
+ currencyUnitPatternSet.add(currencyPattern);
+ applyPatternWithoutExpandAffix(currencyPattern, false);
+ affixes = new AffixForCurrency(negPrefixPattern,
+ negSuffixPattern,
+ posPrefixPattern,
+ posSuffixPattern,
+ Currency.LONG_NAME);
+ affixPatternsForCurrency.add(affixes);
+ }
+ }
+ // reset pattern back
+ formatPattern = savedFormatPattern;
+ }
+
+ private static final int CURRENCY_SIGN_COUNT_IN_SYMBOL_FORMAT = 1;
+ private static final int CURRENCY_SIGN_COUNT_IN_ISO_FORMAT = 2;
+ private static final int CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT = 3;
+
+ private static final int STATUS_INFINITE = 0;
+ private static final int STATUS_POSITIVE = 1;
+ private static final int STATUS_UNDERFLOW = 2;
+ private static final int STATUS_LENGTH = 3;
+ private static final UnicodeSet dotEquivalents =(UnicodeSet) new UnicodeSet(
+ "[.\u2024\u3002\uFE12\uFE52\uFF0E\uFF61]").freeze();
+ private static final UnicodeSet commaEquivalents = (UnicodeSet) new UnicodeSet(
+ "[,\u060C\u066B\u3001\uFE10\uFE11\uFE50\uFE51\uFF0C\uFF64]").freeze();
+ private static final UnicodeSet otherGroupingSeparators = (UnicodeSet) new UnicodeSet(
+ "[\\ '\u00A0\u066C\u2000-\u200A\u2018\u2019\u202F\u205F\u3000\uFF07]").freeze();
+
+ private static final UnicodeSet strictDotEquivalents =(UnicodeSet) new UnicodeSet(
+ "[.\u2024\uFE52\uFF0E\uFF61]").freeze();
+ private static final UnicodeSet strictCommaEquivalents = (UnicodeSet) new UnicodeSet(
+ "[,\u066B\uFE10\uFE50\uFF0C]").freeze();
+ private static final UnicodeSet strictOtherGroupingSeparators = (UnicodeSet) new UnicodeSet(
+ "[\\ '\u00A0\u066C\u2000-\u200A\u2018\u2019\u202F\u205F\u3000\uFF07]").freeze();
+
+ private static final UnicodeSet defaultGroupingSeparators = (UnicodeSet) new UnicodeSet(
+ dotEquivalents).addAll(commaEquivalents).addAll(otherGroupingSeparators).freeze();
+ private static final UnicodeSet strictDefaultGroupingSeparators = (UnicodeSet) new UnicodeSet(
+ strictDotEquivalents).addAll(strictCommaEquivalents).addAll(strictOtherGroupingSeparators).freeze();
+
+ // When parsing a number with big exponential value, it requires to transform
+ // the value into a string representation to construct BigInteger instance.
+ // We want to set the maximum size because it can easily trigger OutOfMemoryException.
+ // PARSE_MAX_EXPONENT is currently set to 1000, which is much bigger than
+ // MAX_VALUE of Double (
+ // See the problem reported by ticket#5698
+ private static final int PARSE_MAX_EXPONENT = 1000;
+
+ /**
+ * CHANGED
+ * Parse the given text into a number. The text is parsed beginning at
+ * parsePosition, until an unparseable character is seen.
+ * @param text The string to parse.
+ * @param parsePosition The position at which to being parsing. Upon
+ * return, the first unparseable character.
+ * @param digits The DigitList to set to the parsed value.
+ * @param isExponent If true, parse an exponent. This means no
+ * infinite values and integer only.
+ * @param status Upon return contains boolean status flags indicating
+ * whether the value was infinite and whether it was positive.
+ * @param currency return value for parsed currency, for generic
+ * currency parsing mode, or null for normal parsing. In generic
+ * currency parsing mode, any currency is parsed, not just the
+ * currency that this formatter is set to.
+ * @param negPrefix negative prefix pattern
+ * @param negSuffix negative suffix pattern
+ * @param posPrefix positive prefix pattern
+ * @param negSuffix negative suffix pattern
+ * @param type type of currency to parse against, LONG_NAME only or not.
+ */
+ private final boolean subparse(String text, ParsePosition parsePosition,
+ DigitList digits, boolean isExponent,
+ boolean status[], Currency currency[],
+ String negPrefix, String negSuffix,
+ String posPrefix, String posSuffix,
+ int type)
+ {
+ int position = parsePosition.getIndex();
+ int oldStart = parsePosition.getIndex();
+
+ // Match padding before prefix
+ if (formatWidth > 0 && padPosition == PAD_BEFORE_PREFIX) {
+ position = skipPadding(text, position);
+ }
+
+ // Match positive and negative prefixes; prefer longest match.
+ int posMatch = compareAffix(text, position, false, true, posPrefix, type, currency);
+ int negMatch = compareAffix(text, position, true, true, negPrefix, type, currency);
+ if (posMatch >= 0 && negMatch >= 0) {
+ if (posMatch > negMatch) {
+ negMatch = -1;
+ } else if (negMatch > posMatch) {
+ posMatch = -1;
+ }
+ }
+ if (posMatch >= 0) {
+ position += posMatch;
+ } else if (negMatch >= 0) {
+ position += negMatch;
+ } else {
+ parsePosition.setErrorIndex(position);
+ return false;
+ }
+
+ // Match padding after prefix
+ if (formatWidth > 0 && padPosition == PAD_AFTER_PREFIX) {
+ position = skipPadding(text, position);
+ }
+
+ // process digits or Inf, find decimal position
+ status[STATUS_INFINITE] = false;
+ if (!isExponent && text.regionMatches(position,symbols.getInfinity(),0,
+ symbols.getInfinity().length()))
+ {
+ position += symbols.getInfinity().length();
+ status[STATUS_INFINITE] = true;
+ } else {
+ // We now have a string of digits, possibly with grouping symbols,
+ // and decimal points. We want to process these into a DigitList.
+ // We don't want to put a bunch of leading zeros into the DigitList
+ // though, so we keep track of the location of the decimal point,
+ // put only significant digits into the DigitList, and adjust the
+ // exponent as needed.
+
+ digits.decimalAt = digits.count = 0;
+ char zero = symbols.getZeroDigit();
+ char decimal = currencySignCount > 0 ?
+ symbols.getMonetaryDecimalSeparator() : symbols.getDecimalSeparator();
+ char grouping = symbols.getGroupingSeparator();
+
+ String exponentSep = symbols.getExponentSeparator();
+ boolean sawDecimal = false;
+ boolean sawExponent = false;
+ boolean sawDigit = false;
+ long exponent = 0; // Set to the exponent value, if any
+ int digit = 0;
+
+ // strict parsing
+ boolean strictParse = isParseStrict();
+ boolean strictFail = false; // did we exit with a strict parse failure?
+ int lastGroup = -1; // where did we last see a grouping separator?
+ int gs2 = groupingSize2 == 0 ? groupingSize : groupingSize2;
+
+ // Strict parsing leading zeroes. If a leading zero would
+ // be forced by the pattern, then don't fail strict parsing.
+ boolean strictLeadingZero = false;
+ int leadingZeroPos = 0;
+ int leadingZeroCount = 0;
+
+ // equivalent grouping and decimal support
+
+ // TODO markdavis Cache these if it makes a difference in performance.
+ UnicodeSet decimalSet = new UnicodeSet(getSimilarDecimals(decimal, strictParse));
+ UnicodeSet groupingSet = new UnicodeSet(strictParse ? strictDefaultGroupingSeparators : defaultGroupingSeparators)
+ .add(grouping).removeAll(decimalSet);
+
+ // we are guaranteed that
+ // decimalSet contains the decimal, and
+ // groupingSet contains the groupingSeparator
+ // (unless decimal and grouping are the same, which should never happen. But in that case, groupingSet will just be empty.)
+
+
+ // We have to track digitCount ourselves, because digits.count will
+ // pin when the maximum allowable digits is reached.
+ int digitCount = 0;
+
+ int backup = -1;
+ for (; position < text.length(); ++position)
+ {
+ char ch = text.charAt(position);
+
+ /* We recognize all digit ranges, not only the Latin digit range
+ * '0'..'9'. We do so by using the UCharacter.digit() method,
+ * which converts a valid Unicode digit to the range 0..9.
+ *
+ * The character 'ch' may be a digit. If so, place its value
+ * from 0 to 9 in 'digit'. First try using the locale digit,
+ * which may or MAY NOT be a standard Unicode digit range. If
+ * this fails, try using the standard Unicode digit ranges by
+ * calling UCharacter.digit(). If this also fails, digit will
+ * have a value outside the range 0..9.
+ */
+ digit = ch - zero;
+ if (digit < 0 || digit > 9) digit = UCharacter.digit(ch, 10);
+
+ if (digit == 0)
+ {
+ // Cancel out backup setting (see grouping handler below)
+ if (strictParse && backup != -1) {
+ // comma followed by digit, so group before comma is a
+ // secondary group. If there was a group separator
+ // before that, the group must == the secondary group
+ // length, else it can be <= the the secondary group
+ // length.
+ if ((lastGroup != -1 && backup - lastGroup - 1 != gs2) ||
+ (lastGroup == -1 && position - oldStart - 1 > gs2)) {
+ strictFail = true;
+ break;
+ }
+ lastGroup = backup;
+ }
+ backup = -1; // Do this BEFORE continue statement below!!!
+ sawDigit = true;
+
+ // Handle leading zeros
+ if (digits.count == 0)
+ {
+ if (!sawDecimal) {
+ if (strictParse && !isExponent) {
+ // Allow leading zeros in exponents
+ // Count leading zeros for checking later
+ if (!strictLeadingZero) leadingZeroPos = position + 1;
+ strictLeadingZero = true;
+ ++leadingZeroCount;
+ }
+ // Ignore leading zeros in integer part of number.
+ continue;
+ }
+
+ // If we have seen the decimal, but no significant digits yet,
+ // then we account for leading zeros by decrementing the
+ // digits.decimalAt into negative values.
+ --digits.decimalAt;
+ }
+ else
+ {
+ ++digitCount;
+ digits.append((char)(digit + '0'));
+ }
+ }
+ else if (digit > 0 && digit <= 9) // [sic] digit==0 handled above
+ {
+ if (strictParse) {
+ if (backup != -1) {
+ if ((lastGroup != -1 && backup - lastGroup - 1 != gs2) ||
+ (lastGroup == -1 && position - oldStart - 1 > gs2)) {
+ strictFail = true;
+ break;
+ }
+ lastGroup = backup;
+ }
+ }
+
+ sawDigit = true;
+ ++digitCount;
+ digits.append((char)(digit + '0'));
+
+ // Cancel out backup setting (see grouping handler below)
+ backup = -1;
+ }
+ else if (!isExponent && decimalSet.contains(ch))
+ {
+ if (strictParse) {
+ if (backup != -1 ||
+ (lastGroup != -1 && position - lastGroup != groupingSize + 1)) {
+ strictFail = true;
+ break;
+ }
+ }
+ // If we're only parsing integers, or if we ALREADY saw the
+ // decimal, then don't parse this one.
+ if (isParseIntegerOnly() || sawDecimal) break;
+ digits.decimalAt = digitCount; // Not digits.count!
+ sawDecimal = true;
+
+ // Once we see a decimal character, we only accept that decimal character from then on.
+ decimalSet.set(ch,ch);
+ }
+ else if (!isExponent && isGroupingUsed() && groupingSet.contains(ch))
+ {
+ if (sawDecimal) {
+ break;
+ }
+ if (strictParse) {
+ if ((!sawDigit || backup != -1)) {
+ // leading group, or two group separators in a row
+ strictFail = true;
+ break;
+ }
+ }
+ // Once we see a grouping character, we only accept that grouping character from then on.
+ groupingSet.set(ch,ch);
+
+ // Ignore grouping characters, if we are using them, but require
+ // that they be followed by a digit. Otherwise we backup and
+ // reprocess them.
+ backup = position;
+ }
+ else if (!isExponent && !sawExponent &&
+ text.regionMatches(position, exponentSep,
+ 0, exponentSep.length()))
+ {
+ // Parse sign, if present
+ boolean negExp = false;
+ int pos = position + exponentSep.length();
+ if (pos < text.length()) {
+ ch = text.charAt(pos);
+ if (ch == symbols.getPlusSign()) {
+ ++pos;
+ } else if (ch == symbols.getMinusSign()) {
+ ++pos;
+ negExp = true;
+ }
+ }
+
+ DigitList exponentDigits = new DigitList();
+ exponentDigits.count = 0;
+ while (pos < text.length()) {
+ digit = text.charAt(pos) - zero;
+ if (digit < 0 || digit > 9) {
+ /*
+ Can't parse "[1E0]" when pattern is "0.###E0;[0.###E0]"
+ Should update reassign the value of 'ch' in the
+ code: digit = Character.digit(ch, 10);
+ [Richard/GCL]
+ */
+ digit = UCharacter.digit(text.charAt(pos), 10);
+ }
+ if (digit >= 0 && digit <= 9) {
+ exponentDigits.append((char)(digit + '0'));
+ ++pos;
+ } else {
+ break;
+ }
+ }
+
+ if (exponentDigits.count > 0) {
+ // defer strict parse until we know we have a bona-fide exponent
+ if (strictParse) {
+ if (backup != -1 || lastGroup != -1) {
+ strictFail = true;
+ break;
+ }
+ }
+
+ // Quick overflow check for exponential part.
+ // Actual limit check will be done later in this code.
+ if (exponentDigits.count > 10 /* maximum decimal digits for int */) {
+ if (negExp) {
+ // set underflow flag
+ status[STATUS_UNDERFLOW] = true;
+ } else {
+ // set infinite flag
+ status[STATUS_INFINITE] = true;
+ }
+ } else {
+ exponentDigits.decimalAt = exponentDigits.count;
+ exponent = exponentDigits.getLong();
+ if (negExp) {
+ exponent = -exponent;
+ }
+ }
+ position = pos; // Advance past the exponent
+ sawExponent = true;
+ }
+
+ break; // Whether we fail or succeed, we exit this loop
+ }
+ else break;
+ }
+
+ if (backup != -1) position = backup;
+
+ // If there was no decimal point we have an integer
+ if (!sawDecimal) digits.decimalAt = digitCount; // Not digits.count!
+
+ // check for strict parse errors
+ if (strictParse && strictLeadingZero) {
+ if ((leadingZeroCount + digits.decimalAt) > this.getMinimumIntegerDigits()) {
+ parsePosition.setIndex(oldStart);
+ parsePosition.setErrorIndex(leadingZeroPos);
+ return false;
+ }
+ }
+ if (strictParse && !sawDecimal) {
+ if (lastGroup != -1 && position - lastGroup != groupingSize + 1) {
+ strictFail = true;
+ }
+ }
+ if (strictFail) {
+ // only set with strictParse and a leading zero error
+ // leading zeros are an error with strict parsing except
+ // immediately before nondigit (except group separator
+ // followed by digit), or end of text.
+
+ parsePosition.setIndex(oldStart);
+ parsePosition.setErrorIndex(position);
+ return false;
+ }
+
+ // Adjust for exponent, if any
+ exponent += digits.decimalAt;
+ if (exponent < -PARSE_MAX_EXPONENT) {
+ status[STATUS_UNDERFLOW] = true;
+ } else if (exponent > PARSE_MAX_EXPONENT) {
+ status[STATUS_INFINITE] = true;
+ } else {
+ digits.decimalAt = (int)exponent;
+ }
+
+ // If none of the text string was recognized. For example, parse
+ // "x" with pattern "#0.00" (return index and error index both 0)
+ // parse "$" with pattern "$#0.00". (return index 0 and error index
+ // 1).
+ if (!sawDigit && digitCount == 0) {
+ parsePosition.setIndex(oldStart);
+ parsePosition.setErrorIndex(oldStart);
+ return false;
+ }
+ }
+
+ // Match padding before suffix
+ if (formatWidth > 0 && padPosition == PAD_BEFORE_SUFFIX) {
+ position = skipPadding(text, position);
+ }
+
+ // Match positive and negative suffixes; prefer longest match.
+ if (posMatch >= 0) {
+ posMatch = compareAffix(text, position, false, false, posSuffix, type, currency);
+ }
+ if (negMatch >= 0) {
+ negMatch = compareAffix(text, position, true, false, negSuffix, type, currency);
+ }
+ if (posMatch >= 0 && negMatch >= 0) {
+ if (posMatch > negMatch) {
+ negMatch = -1;
+ } else if (negMatch > posMatch) {
+ posMatch = -1;
+ }
+ }
+
+ // Fail if neither or both
+ if ((posMatch >= 0) == (negMatch >= 0)) {
+ parsePosition.setErrorIndex(position);
+ return false;
+ }
+
+ position += (posMatch>=0 ? posMatch : negMatch);
+
+ // Match padding after suffix
+ if (formatWidth > 0 && padPosition == PAD_AFTER_SUFFIX) {
+ position = skipPadding(text, position);
+ }
+
+ parsePosition.setIndex(position);
+
+ status[STATUS_POSITIVE] = (posMatch >= 0);
+
+ if (parsePosition.getIndex() == oldStart) {
+ parsePosition.setErrorIndex(position);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return characters that are used where this decimal is used.
+ * @param decimal
+ * @param strictParse
+ * @return
+ */
+ private UnicodeSet getSimilarDecimals(char decimal, boolean strictParse) {
+ if (dotEquivalents.contains(decimal)) {
+ return strictParse ? strictDotEquivalents : dotEquivalents;
+ }
+ if (commaEquivalents.contains(decimal)) {
+ return strictParse ? strictCommaEquivalents : commaEquivalents;
+ }
+ // if there is no match, return the character itself
+ return new UnicodeSet().add(decimal);
+ }
+
+ /**
+ * Starting at position, advance past a run of pad characters, if any.
+ * Return the index of the first character after position that is not a pad
+ * character. Result is >= position.
+ */
+ private final int skipPadding(String text, int position) {
+ while (position < text.length() && text.charAt(position) == pad) {
+ ++position;
+ }
+ return position;
+ }
+
+ /*
+ * Return the length matched by the given affix, or -1 if none.
+ * Runs of white space in the affix, match runs of white space in
+ * the input. Pattern white space and input white space are
+ * determined differently; see code.
+ * @param text input text
+ * @param pos offset into input at which to begin matching
+ * @param isNegative
+ * @param isPrefix
+ * @param affixPat affix pattern used for currency affix comparison
+ * @param type compare against currency type, LONG_NAME only or not.
+ * @param currency return value for parsed currency, for generic
+ * currency parsing mode, or null for normal parsing. In generic
+ * currency parsing mode, any currency is parsed, not just the
+ * currency that this formatter is set to.
+ * @return length of input that matches, or -1 if match failure
+ */
+ private int compareAffix(String text, int pos,
+ boolean isNegative, boolean isPrefix,
+ String affixPat,
+ int type,
+ Currency[] currency) {
+ if (currency != null || currencyChoice != null ||
+ currencySignCount > 0) {
+ return compareComplexAffix(affixPat, text, pos, type, currency);
+ }
+ if (isPrefix) {
+ return compareSimpleAffix(isNegative ? negativePrefix : positivePrefix,
+ text, pos);
+ } else {
+ return compareSimpleAffix(isNegative ? negativeSuffix : positiveSuffix,
+ text, pos);
+ }
+
+ }
+
+
+
+ /**
+ * Return the length matched by the given affix, or -1 if none.
+ * Runs of white space in the affix, match runs of white space in
+ * the input. Pattern white space and input white space are
+ * determined differently; see code.
+ * @param affix pattern string, taken as a literal
+ * @param input input text
+ * @param pos offset into input at which to begin matching
+ * @return length of input that matches, or -1 if match failure
+ */
+ private static int compareSimpleAffix(String affix, String input, int pos) {
+ int start = pos;
+ for (int i=0; inull
if rounding
+ * is not in effect.
+ * @see #setRoundingIncrement
+ * @see #getRoundingMode
+ * @see #setRoundingMode
+ * @stable ICU 2.0
+ */
+//#if defined(FOUNDATION10) || defined(ECLIPSE_FRAGMENT)
+//## public BigDecimal getRoundingIncrement() {
+//## if (roundingIncrementICU == null) return null;
+//## return new BigDecimal(roundingIncrementICU.toString());
+//## }
+//#else
+ public java.math.BigDecimal getRoundingIncrement() {
+ if (roundingIncrementICU == null) return null;
+ return roundingIncrementICU.toBigDecimal();
+ }
+//#endif
+
+//#if defined(FOUNDATION10)
+//#else
+ // [NEW]
+ /**
+ * Set the rounding increment. This method also controls whether
+ * rounding is enabled.
+ * @param newValue A positive rounding increment, or null
or
+ * BigDecimal(0.0)
to disable rounding.
+ * @exception IllegalArgumentException if newValue
is < 0.0
+ * @see #getRoundingIncrement
+ * @see #getRoundingMode
+ * @see #setRoundingMode
+ * @stable ICU 2.0
+ */
+ public void setRoundingIncrement(java.math.BigDecimal newValue) {
+ if (newValue == null) {
+ setRoundingIncrement((BigDecimal)null);
+ } else {
+ setRoundingIncrement(new BigDecimal(newValue));
+ }
+ }
+//#endif
+
+ // [NEW]
+ /**
+ * Set the rounding increment. This method also controls whether
+ * rounding is enabled.
+ * @param newValue A positive rounding increment, or null
or
+ * BigDecimal(0.0)
to disable rounding.
+ * @exception IllegalArgumentException if newValue
is < 0.0
+ * @see #getRoundingIncrement
+ * @see #getRoundingMode
+ * @see #setRoundingMode
+ * @stable ICU 3.6
+ */
+ public void setRoundingIncrement(BigDecimal newValue) {
+ int i = newValue == null
+ ? 0 : newValue.compareTo(BigDecimal.ZERO);
+ if (i < 0) {
+ throw new IllegalArgumentException("Illegal rounding increment");
+ }
+ if (i == 0) {
+ setInternalRoundingIncrement(null);
+ } else {
+ setInternalRoundingIncrement(newValue);
+ }
+ setRoundingDouble();
+ }
+
+ // [NEW]
+ /**
+ * Set the rounding increment. This method also controls whether
+ * rounding is enabled.
+ * @param newValue A positive rounding increment, or 0.0 to disable
+ * rounding.
+ * @exception IllegalArgumentException if newValue
is < 0.0
+ * @see #getRoundingIncrement
+ * @see #getRoundingMode
+ * @see #setRoundingMode
+ * @stable ICU 2.0
+ */
+ public void setRoundingIncrement(double newValue) {
+ if (newValue < 0.0) {
+ throw new IllegalArgumentException("Illegal rounding increment");
+ }
+ roundingDouble = newValue;
+ roundingDoubleReciprocal = 0.0d;
+ if (newValue == 0.0d) {
+ setRoundingIncrement((BigDecimal)null);
+ } else {
+ roundingDouble = newValue;
+ if (roundingDouble < 1.0d) {
+ double rawRoundedReciprocal = 1.0d/roundingDouble;
+ setRoundingDoubleReciprocal(rawRoundedReciprocal);
+ }
+ setInternalRoundingIncrement(new BigDecimal(newValue));
+ }
+ }
+
+
+ private void setRoundingDoubleReciprocal(double rawRoundedReciprocal) {
+ roundingDoubleReciprocal = Math.rint(rawRoundedReciprocal);
+ if (Math.abs(rawRoundedReciprocal - roundingDoubleReciprocal) > roundingIncrementEpsilon) {
+ roundingDoubleReciprocal = 0.0d;
+ }
+ }
+
+ static final double roundingIncrementEpsilon = 0.000000001;
+
+ /**
+ * Get the rounding mode.
+ * @return A rounding mode, between BigDecimal.ROUND_UP
+ * and BigDecimal.ROUND_UNNECESSARY
.
+ * @see #setRoundingIncrement
+ * @see #getRoundingIncrement
+ * @see #setRoundingMode
+ * @see java.math.BigDecimal
+ * @stable ICU 2.0
+ */
+ public int getRoundingMode() {
+ return roundingMode;
+ }
+
+ /**
+ * Set the rounding mode. This has no effect unless the rounding
+ * increment is greater than zero.
+ * @param roundingMode A rounding mode, between
+ * BigDecimal.ROUND_UP
and
+ * BigDecimal.ROUND_UNNECESSARY
.
+ * @exception IllegalArgumentException if roundingMode
+ * is unrecognized.
+ * @see #setRoundingIncrement
+ * @see #getRoundingIncrement
+ * @see #getRoundingMode
+ * @see java.math.BigDecimal
+ * @stable ICU 2.0
+ */
+ public void setRoundingMode(int roundingMode) {
+ if (roundingMode < BigDecimal.ROUND_UP
+ || roundingMode > BigDecimal.ROUND_UNNECESSARY) {
+ throw new IllegalArgumentException("Invalid rounding mode: "
+ + roundingMode);
+ }
+
+ this.roundingMode = roundingMode;
+
+ if (getRoundingIncrement() == null) {
+ setRoundingIncrement(Math.pow(10.0,(double)-getMaximumFractionDigits()));
+ }
+ }
+
+ // [NEW]
+ /**
+ * Get the width to which the output of format()
is padded.
+ * The width is counted in 16-bit code units.
+ * @return the format width, or zero if no padding is in effect
+ * @see #setFormatWidth
+ * @see #getPadCharacter
+ * @see #setPadCharacter
+ * @see #getPadPosition
+ * @see #setPadPosition
+ * @stable ICU 2.0
+ */
+ public int getFormatWidth() {
+ return formatWidth;
+ }
+
+ // [NEW]
+ /**
+ * Set the width to which the output of format()
is padded.
+ * The width is counted in 16-bit code units.
+ * This method also controls whether padding is enabled.
+ * @param width the width to which to pad the result of
+ * format()
, or zero to disable padding
+ * @exception IllegalArgumentException if width
is < 0
+ * @see #getFormatWidth
+ * @see #getPadCharacter
+ * @see #setPadCharacter
+ * @see #getPadPosition
+ * @see #setPadPosition
+ * @stable ICU 2.0
+ */
+ public void setFormatWidth(int width) {
+ if (width < 0) {
+ throw new IllegalArgumentException("Illegal format width");
+ }
+ formatWidth = width;
+ }
+
+ // [NEW]
+ /**
+ * Get the character used to pad to the format width. The default is ' '.
+ * @return the pad character
+ * @see #setFormatWidth
+ * @see #getFormatWidth
+ * @see #setPadCharacter
+ * @see #getPadPosition
+ * @see #setPadPosition
+ * @stable ICU 2.0
+ */
+ public char getPadCharacter() {
+ return pad;
+ }
+
+ // [NEW]
+ /**
+ * Set the character used to pad to the format width. If padding
+ * is not enabled, then this will take effect if padding is later
+ * enabled.
+ * @param padChar the pad character
+ * @see #setFormatWidth
+ * @see #getFormatWidth
+ * @see #getPadCharacter
+ * @see #getPadPosition
+ * @see #setPadPosition
+ * @stable ICU 2.0
+ */
+ public void setPadCharacter(char padChar) {
+ pad = padChar;
+ }
+
+ // [NEW]
+ /**
+ * Get the position at which padding will take place. This is the location
+ * at which padding will be inserted if the result of format()
+ * is shorter than the format width.
+ * @return the pad position, one of PAD_BEFORE_PREFIX
,
+ * PAD_AFTER_PREFIX
, PAD_BEFORE_SUFFIX
, or
+ * PAD_AFTER_SUFFIX
.
+ * @see #setFormatWidth
+ * @see #getFormatWidth
+ * @see #setPadCharacter
+ * @see #getPadCharacter
+ * @see #setPadPosition
+ * @see #PAD_BEFORE_PREFIX
+ * @see #PAD_AFTER_PREFIX
+ * @see #PAD_BEFORE_SUFFIX
+ * @see #PAD_AFTER_SUFFIX
+ * @stable ICU 2.0
+ */
+ public int getPadPosition() {
+ return padPosition;
+ }
+
+ // [NEW]
+ /**
+ * Set the position at which padding will take place. This is the location
+ * at which padding will be inserted if the result of format()
+ * is shorter than the format width. This has no effect unless padding is
+ * enabled.
+ * @param padPos the pad position, one of PAD_BEFORE_PREFIX
,
+ * PAD_AFTER_PREFIX
, PAD_BEFORE_SUFFIX
, or
+ * PAD_AFTER_SUFFIX
.
+ * @exception IllegalArgumentException if the pad position in
+ * unrecognized
+ * @see #setFormatWidth
+ * @see #getFormatWidth
+ * @see #setPadCharacter
+ * @see #getPadCharacter
+ * @see #getPadPosition
+ * @see #PAD_BEFORE_PREFIX
+ * @see #PAD_AFTER_PREFIX
+ * @see #PAD_BEFORE_SUFFIX
+ * @see #PAD_AFTER_SUFFIX
+ * @stable ICU 2.0
+ */
+ public void setPadPosition(int padPos) {
+ if (padPos < PAD_BEFORE_PREFIX || padPos > PAD_AFTER_SUFFIX) {
+ throw new IllegalArgumentException("Illegal pad position");
+ }
+ padPosition = padPos;
+ }
+
+ // [NEW]
+ /**
+ * Return whether or not scientific notation is used.
+ * @return true if this object formats and parses scientific notation
+ * @see #setScientificNotation
+ * @see #getMinimumExponentDigits
+ * @see #setMinimumExponentDigits
+ * @see #isExponentSignAlwaysShown
+ * @see #setExponentSignAlwaysShown
+ * @stable ICU 2.0
+ */
+ public boolean isScientificNotation() {
+ return useExponentialNotation;
+ }
+
+ // [NEW]
+ /**
+ * Set whether or not scientific notation is used. When scientific notation
+ * is used, the effective maximum number of integer digits is <= 8. If the
+ * maximum number of integer digits is set to more than 8, the effective
+ * maximum will be 1. This allows this call to generate a 'default' scientific
+ * number format without additional changes.
+ * @param useScientific true if this object formats and parses scientific
+ * notation
+ * @see #isScientificNotation
+ * @see #getMinimumExponentDigits
+ * @see #setMinimumExponentDigits
+ * @see #isExponentSignAlwaysShown
+ * @see #setExponentSignAlwaysShown
+ * @stable ICU 2.0
+ */
+ public void setScientificNotation(boolean useScientific) {
+ useExponentialNotation = useScientific;
+ }
+
+ // [NEW]
+ /**
+ * Return the minimum exponent digits that will be shown.
+ * @return the minimum exponent digits that will be shown
+ * @see #setScientificNotation
+ * @see #isScientificNotation
+ * @see #setMinimumExponentDigits
+ * @see #isExponentSignAlwaysShown
+ * @see #setExponentSignAlwaysShown
+ * @stable ICU 2.0
+ */
+ public byte getMinimumExponentDigits() {
+ return minExponentDigits;
+ }
+
+ // [NEW]
+ /**
+ * Set the minimum exponent digits that will be shown. This has no
+ * effect unless scientific notation is in use.
+ * @param minExpDig a value >= 1 indicating the fewest exponent digits
+ * that will be shown
+ * @exception IllegalArgumentException if minExpDig
< 1
+ * @see #setScientificNotation
+ * @see #isScientificNotation
+ * @see #getMinimumExponentDigits
+ * @see #isExponentSignAlwaysShown
+ * @see #setExponentSignAlwaysShown
+ * @stable ICU 2.0
+ */
+ public void setMinimumExponentDigits(byte minExpDig) {
+ if (minExpDig < 1) {
+ throw new IllegalArgumentException("Exponent digits must be >= 1");
+ }
+ minExponentDigits = minExpDig;
+ }
+
+ // [NEW]
+ /**
+ * Return whether the exponent sign is always shown.
+ * @return true if the exponent is always prefixed with either the
+ * localized minus sign or the localized plus sign, false if only negative
+ * exponents are prefixed with the localized minus sign.
+ * @see #setScientificNotation
+ * @see #isScientificNotation
+ * @see #setMinimumExponentDigits
+ * @see #getMinimumExponentDigits
+ * @see #setExponentSignAlwaysShown
+ * @stable ICU 2.0
+ */
+ public boolean isExponentSignAlwaysShown() {
+ return exponentSignAlwaysShown;
+ }
+
+ // [NEW]
+ /**
+ * Set whether the exponent sign is always shown. This has no effect
+ * unless scientific notation is in use.
+ * @param expSignAlways true if the exponent is always prefixed with either
+ * the localized minus sign or the localized plus sign, false if only
+ * negative exponents are prefixed with the localized minus sign.
+ * @see #setScientificNotation
+ * @see #isScientificNotation
+ * @see #setMinimumExponentDigits
+ * @see #getMinimumExponentDigits
+ * @see #isExponentSignAlwaysShown
+ * @stable ICU 2.0
+ */
+ public void setExponentSignAlwaysShown(boolean expSignAlways) {
+ exponentSignAlwaysShown = expSignAlways;
+ }
+
+ /**
+ * Return the grouping size. Grouping size is the number of digits between
+ * grouping separators in the integer portion of a number. For example,
+ * in the number "123,456.78", the grouping size is 3.
+ * @see #setGroupingSize
+ * @see NumberFormat#isGroupingUsed
+ * @see DecimalFormatSymbols#getGroupingSeparator
+ * @stable ICU 2.0
+ */
+ public int getGroupingSize () {
+ return groupingSize;
+ }
+
+ /**
+ * Set the grouping size. Grouping size is the number of digits between
+ * grouping separators in the integer portion of a number. For example,
+ * in the number "123,456.78", the grouping size is 3.
+ * @see #getGroupingSize
+ * @see NumberFormat#setGroupingUsed
+ * @see DecimalFormatSymbols#setGroupingSeparator
+ * @stable ICU 2.0
+ */
+ public void setGroupingSize (int newValue) {
+ groupingSize = (byte)newValue;
+ }
+
+ // [NEW]
+ /**
+ * Return the secondary grouping size. In some locales one
+ * grouping interval is used for the least significant integer
+ * digits (the primary grouping size), and another is used for all
+ * others (the secondary grouping size). A formatter supporting a
+ * secondary grouping size will return a positive integer unequal
+ * to the primary grouping size returned by
+ * getGroupingSize()
. For example, if the primary
+ * grouping size is 4, and the secondary grouping size is 2, then
+ * the number 123456789 formats as "1,23,45,6789", and the pattern
+ * appears as "#,##,###0".
+ * @return the secondary grouping size, or a value less than
+ * one if there is none
+ * @see #setSecondaryGroupingSize
+ * @see NumberFormat#isGroupingUsed
+ * @see DecimalFormatSymbols#getGroupingSeparator
+ * @stable ICU 2.0
+ */
+ public int getSecondaryGroupingSize () {
+ return groupingSize2;
+ }
+
+ // [NEW]
+ /**
+ * Set the secondary grouping size. If set to a value less than 1,
+ * then secondary grouping is turned off, and the primary grouping
+ * size is used for all intervals, not just the least significant.
+ * @see #getSecondaryGroupingSize
+ * @see NumberFormat#setGroupingUsed
+ * @see DecimalFormatSymbols#setGroupingSeparator
+ * @stable ICU 2.0
+ */
+ public void setSecondaryGroupingSize (int newValue) {
+ groupingSize2 = (byte)newValue;
+ }
+
+ // [NEW]
+ /**
+ * Returns the MathContext
+ * used by this format.
+ * @return desired MathContext
+ * @see #mathContext
+ * @see #getMathContext
+ * @draft ICU 4.2
+ * @provisional This API might change or be removed in a future release.
+ */
+ public MathContext getMathContextICU() {
+ return mathContext;
+ }
+
+//#if defined(FOUNDATION10) || defined(J2SE13) || defined(J2SE14) || defined(ECLIPSE_FRAGMENT)
+//#else
+ // [NEW]
+ /**
+ * Returns the MathContext
+ * used by this format.
+ * @return desired MathContext
+ * @see #mathContext
+ * @see #getMathContext
+ * @draft ICU 4.2
+ * @provisional This API might change or be removed in a future release.
+ */
+ public java.math.MathContext getMathContext()
+ {
+ try
+ {
+ // don't allow multiple references
+ return mathContext == null ?
+ null :
+ new java.math.MathContext(mathContext.getDigits(),
+ java.math.RoundingMode.valueOf(mathContext.getRoundingMode()));
+ }
+ catch (Exception foo)
+ {
+ return null; // should never happen
+ }
+ }
+//#endif
+
+ // [NEW]
+ /**
+ * Sets the MathContext used by this format.
+ * @param newValue desired MathContext
+ * @see #mathContext
+ * @see #getMathContext
+ * @draft ICU 4.2
+ * @provisional This API might change or be removed in a future release.
+ */
+ public void setMathContextICU(MathContext newValue) {
+ mathContext = newValue;
+ }
+
+//#if defined(FOUNDATION10) || defined(J2SE13) || defined(J2SE14) || defined(ECLIPSE_FRAGMENT)
+//#else
+ // [NEW]
+ /**
+ * Sets the MathContext used by this format.
+ * @param newValue desired MathContext
+ * @see #mathContext
+ * @see #getMathContext
+ * @draft ICU 4.2
+ * @provisional This API might change or be removed in a future release.
+ */
+ public void setMathContext(java.math.MathContext newValue)
+ {
+ mathContext = new MathContext(newValue.getPrecision(),
+ MathContext.SCIENTIFIC,
+ false,
+ (newValue.getRoundingMode()).ordinal()
+ );
+ }
+//#endif
+
+ /**
+ * Allows you to get the behavior of the decimal separator with integers.
+ * (The decimal separator will always appear with decimals.)
+ * min
is less than one then it is set
+ * to one. If the maximum significant digits count is less than
+ * min
, then it is set to min
. This
+ * value has no effect unless areSignificantDigitsUsed() returns true.
+ * @param min the fewest significant digits to be shown
+ * @stable ICU 3.0
+ */
+ public void setMinimumSignificantDigits(int min) {
+ if (min < 1) {
+ min = 1;
+ }
+ // pin max sig dig to >= min
+ int max = Math.max(maxSignificantDigits, min);
+ minSignificantDigits = min;
+ maxSignificantDigits = max;
+ }
+
+ /**
+ * Sets the maximum number of significant digits that will be
+ * displayed. If max
is less than one then it is set
+ * to one. If the minimum significant digits count is greater
+ * than max
, then it is set to max
. This
+ * value has no effect unless areSignificantDigitsUsed() returns true.
+ * @param max the most significant digits to be shown
+ * @stable ICU 3.0
+ */
+ public void setMaximumSignificantDigits(int max) {
+ if (max < 1) {
+ max = 1;
+ }
+ // pin min sig dig to 1..max
+ int min = Math.min(minSignificantDigits, max);
+ minSignificantDigits = min;
+ maxSignificantDigits = max;
+ }
+
+ /**
+ * Returns true if significant digits are in use or false if
+ * integer and fraction digit counts are in use.
+ * @return true if significant digits are in use
+ * @stable ICU 3.0
+ */
+ public boolean areSignificantDigitsUsed() {
+ return useSignificantDigits;
+ }
+
+ /**
+ * Sets whether significant digits are in use, or integer and
+ * fraction digit counts are in use.
+ * @param useSignificantDigits true to use significant digits, or
+ * false to use integer and fraction digit counts
+ * @stable ICU 3.0
+ */
+ public void setSignificantDigitsUsed(boolean useSignificantDigits) {
+ this.useSignificantDigits = useSignificantDigits;
+ }
+
+ /**
+ * Sets the Currency object used to display currency
+ * amounts. This takes effect immediately, if this format is a
+ * currency format. If this format is not a currency format, then
+ * the currency object is used if and when this object becomes a
+ * currency format through the application of a new pattern.
+ * @param theCurrency new currency object to use. Must not be
+ * null.
+ * @stable ICU 2.2
+ */
+ public void setCurrency(Currency theCurrency) {
+ // If we are a currency format, then modify our affixes to
+ // encode the currency symbol for the given currency in our
+ // locale, and adjust the decimal digits and rounding for the
+ // given currency.
+
+ super.setCurrency(theCurrency);
+ if (theCurrency != null) {
+ boolean[] isChoiceFormat = new boolean[1];
+ String s = theCurrency.getName(symbols.getULocale(),
+ Currency.SYMBOL_NAME,
+ isChoiceFormat);
+ symbols.setCurrencySymbol(s);
+ symbols.setInternationalCurrencySymbol(theCurrency.getCurrencyCode());
+ }
+
+ if (currencySignCount > 0) {
+ if (theCurrency != null) {
+ setRoundingIncrement(theCurrency.getRoundingIncrement());
+ int d = theCurrency.getDefaultFractionDigits();
+ setMinimumFractionDigits(d);
+ setMaximumFractionDigits(d);
+ }
+ expandAffixes(null);
+ }
+ }
+
+ /**
+ * Returns the currency in effect for this formatter. Subclasses
+ * should override this method as needed. Unlike getCurrency(),
+ * this method should never return null.
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ protected Currency getEffectiveCurrency() {
+ Currency c = getCurrency();
+ if (c == null) {
+ c = Currency.getInstance(symbols.getInternationalCurrencySymbol());
+ }
+ return c;
+ }
+
+ /**
+ * Sets the maximum number of digits allowed in the fraction portion of a
+ * number. This override limits the fraction digit count to 340.
+ * @see NumberFormat#setMaximumFractionDigits
+ * @stable ICU 2.0
+ */
+ public void setMaximumFractionDigits(int newValue) {
+ super.setMaximumFractionDigits(Math.min(newValue, DOUBLE_FRACTION_DIGITS));
+ }
+
+ /**
+ * Sets the minimum number of digits allowed in the fraction portion of a
+ * number. This override limits the fraction digit count to 340.
+ * @see NumberFormat#setMinimumFractionDigits
+ * @stable ICU 2.0
+ */
+ public void setMinimumFractionDigits(int newValue) {
+ super.setMinimumFractionDigits(Math.min(newValue, DOUBLE_FRACTION_DIGITS));
+ }
+
+ /**
+ * Sets whether {@link #parse(String, ParsePosition)} method returns BigDecimal.
+ * The default value is false.
+ * @param value true if {@link #parse(String, ParsePosition)} method returns
+ * BigDecimal.
+ * @stable ICU 3.6
+ */
+ public void setParseBigDecimal(boolean value) {
+ parseBigDecimal = value;
+ }
+
+ /**
+ * Returns whether {@link #parse(String, ParsePosition)} method returns BigDecimal.
+ * @return true if {@link #parse(String, ParsePosition)} method returns BigDecimal.
+ * @stable ICU 3.6
+ */
+ public boolean isParseBigDecimal() {
+ return parseBigDecimal;
+ }
+
+//#if defined(FOUNDATION10) || defined(J2SE13)
+//#else
+ private void writeObject(ObjectOutputStream stream) throws IOException {
+// Doug, do we need this anymore?
+// if (roundingIncrementICU != null) {
+// roundingIncrement = roundingIncrementICU.toBigDecimal();
+// }
+
+ // Ticket#6449
+ // Format.Field instances are not serializable. When formatToCharacterIterator
+ // is called, attributes (ArrayList) stores FieldPosition instances with
+ // NumberFormat.Field. Because NumberFormat.Field is not serializable, we need
+ // to clear the contents of the list when writeObject is called. We could remove
+ // the field or make it transient, but it will break serialization compatibility.
+ attributes.clear();
+
+ stream.defaultWriteObject();
+ }
+//#endif
+
+ /**
+ * First, read the default serializable fields from the stream. Then
+ * if serialVersionOnStream
is less than 1, indicating that
+ * the stream was written by JDK 1.1, initialize useExponentialNotation
+ * to false, since it was not present in JDK 1.1.
+ * Finally, set serialVersionOnStream back to the maximum allowed value so that
+ * default serialization will work properly if this object is streamed out again.
+ */
+ private void readObject(ObjectInputStream stream)
+ throws IOException, ClassNotFoundException
+ {
+ stream.defaultReadObject();
+ /*Bug 4185761 validate fields
+ [Richard/GCL]
+ */
+ // We only need to check the maximum counts because NumberFormat
+ // .readObject has already ensured that the maximum is greater than the
+ // minimum count.
+ /*Commented for compatibility with previous version, and reserved for further use
+ if (getMaximumIntegerDigits() > DOUBLE_INTEGER_DIGITS ||
+ getMaximumFractionDigits() > DOUBLE_FRACTION_DIGITS) {
+ throw new InvalidObjectException("Digit count out of range");
+ }*/
+ /* Truncate the maximumIntegerDigits to DOUBLE_INTEGER_DIGITS and maximumFractionDigits
+ * to DOUBLE_FRACTION_DIGITS
+ */
+ if (getMaximumIntegerDigits() > DOUBLE_INTEGER_DIGITS) {
+ setMaximumIntegerDigits(DOUBLE_INTEGER_DIGITS);
+ }
+ if (getMaximumFractionDigits() > DOUBLE_FRACTION_DIGITS) {
+ setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS);
+ }
+ if (serialVersionOnStream < 2) {
+ exponentSignAlwaysShown = false;
+ setInternalRoundingIncrement(null);
+ setRoundingDouble();
+ roundingMode = BigDecimal.ROUND_HALF_EVEN;
+ formatWidth = 0;
+ pad = ' ';
+ padPosition = PAD_BEFORE_PREFIX;
+ if (serialVersionOnStream < 1) {
+ // Didn't have exponential fields
+ useExponentialNotation = false;
+ }
+ }
+ if (serialVersionOnStream < 3) {
+ // Versions prior to 3 do not store a currency object.
+ // Create one to match the DecimalFormatSymbols object.
+ setCurrencyForSymbols();
+ }
+ serialVersionOnStream = currentSerialVersion;
+ digitList = new DigitList();
+
+//#if defined(FOUNDATION10)
+//#else
+ if (roundingIncrement != null) {
+ setInternalRoundingIncrement(new BigDecimal(roundingIncrement));
+ setRoundingDouble();
+ }
+//#endif
+ }
+
+
+ private void setInternalRoundingIncrement(BigDecimal value) {
+ roundingIncrementICU = value;
+//#if defined(FOUNDATION10)
+//#else
+ roundingIncrement = value == null ? null : value.toBigDecimal();
+//#endif
+ }
+
+ //----------------------------------------------------------------------
+ // INSTANCE VARIABLES
+ //----------------------------------------------------------------------
+
+ private transient DigitList digitList = new DigitList();
+
+ /**
+ * The symbol used as a prefix when formatting positive numbers, e.g. "+".
+ *
+ * @serial
+ * @see #getPositivePrefix
+ */
+ private String positivePrefix = "";
+
+ /**
+ * The symbol used as a suffix when formatting positive numbers.
+ * This is often an empty string.
+ *
+ * @serial
+ * @see #getPositiveSuffix
+ */
+ private String positiveSuffix = "";
+
+ /**
+ * The symbol used as a prefix when formatting negative numbers, e.g. "-".
+ *
+ * @serial
+ * @see #getNegativePrefix
+ */
+ private String negativePrefix = "-";
+
+ /**
+ * The symbol used as a suffix when formatting negative numbers.
+ * This is often an empty string.
+ *
+ * @serial
+ * @see #getNegativeSuffix
+ */
+ private String negativeSuffix = "";
+
+ /**
+ * The prefix pattern for non-negative numbers. This variable corresponds
+ * to positivePrefix
.
+ *
+ * expandAffix()
to
+ * positivePrefix
to update the latter to reflect changes in
+ * symbols
. If this variable is null
then
+ * positivePrefix
is taken as a literal value that does not
+ * change when symbols
changes. This variable is always
+ * null
for DecimalFormat
objects older than
+ * stream version 2 restored from stream.
+ *
+ * @serial
+ */
+ //[Richard/GCL]
+ private String posPrefixPattern;
+
+ /**
+ * The suffix pattern for non-negative numbers. This variable corresponds
+ * to positiveSuffix
. This variable is analogous to
+ * posPrefixPattern
; see that variable for further
+ * documentation.
+ *
+ * @serial
+ */
+ //[Richard/GCL]
+ private String posSuffixPattern;
+
+ /**
+ * The prefix pattern for negative numbers. This variable corresponds
+ * to negativePrefix
. This variable is analogous to
+ * posPrefixPattern
; see that variable for further
+ * documentation.
+ *
+ * @serial
+ */
+ //[Richard/GCL]
+ private String negPrefixPattern;
+
+ /**
+ * The suffix pattern for negative numbers. This variable corresponds
+ * to negativeSuffix
. This variable is analogous to
+ * posPrefixPattern
; see that variable for further
+ * documentation.
+ *
+ * @serial
+ */
+ //[Richard/GCL]
+ private String negSuffixPattern;
+
+
+
+ /**
+ * Formatter for ChoiceFormat-based currency names. If this field
+ * is not null, then delegate to it to format currency symbols.
+ * @since ICU 2.6
+ */
+ private ChoiceFormat currencyChoice;
+
+ /**
+ * The multiplier for use in percent, permill, etc.
+ *
+ * @serial
+ * @see #getMultiplier
+ */
+ private int multiplier = 1;
+
+ /**
+ * The number of digits between grouping separators in the integer
+ * portion of a number. Must be greater than 0 if
+ * NumberFormat.groupingUsed
is true.
+ *
+ * @serial
+ * @see #getGroupingSize
+ * @see NumberFormat#isGroupingUsed
+ */
+ private byte groupingSize = 3; // invariant, > 0 if useThousands
+
+ // [NEW]
+ /**
+ * The secondary grouping size. This is only used for Hindi
+ * numerals, which use a primary grouping of 3 and a secondary
+ * grouping of 2, e.g., "12,34,567". If this value is less than
+ * 1, then secondary grouping is equal to the primary grouping.
+ *
+ */
+ private byte groupingSize2 = 0;
+
+ /**
+ * If true, forces the decimal separator to always appear in a formatted
+ * number, even if the fractional part of the number is zero.
+ *
+ * @serial
+ * @see #isDecimalSeparatorAlwaysShown
+ */
+ private boolean decimalSeparatorAlwaysShown = false;
+
+
+ /**
+ * The DecimalFormatSymbols
object used by this format.
+ * It contains the symbols used to format numbers, e.g. the grouping separator,
+ * decimal separator, and so on.
+ *
+ * @serial
+ * @see #setDecimalFormatSymbols
+ * @see DecimalFormatSymbols
+ */
+ private DecimalFormatSymbols symbols = null; // LIU new DecimalFormatSymbols();
+
+ /**
+ * True to use significant digits rather than integer and fraction
+ * digit counts.
+ * @serial
+ * @since ICU 3.0
+ */
+ private boolean useSignificantDigits = false;
+
+ /**
+ * The minimum number of significant digits to show. Must be >= 1
+ * and <= maxSignificantDigits. Ignored unless
+ * useSignificantDigits == true.
+ * @serial
+ * @since ICU 3.0
+ */
+ private int minSignificantDigits = 1;
+
+ /**
+ * The maximum number of significant digits to show. Must be >=
+ * minSignficantDigits. Ignored unless useSignificantDigits ==
+ * true.
+ * @serial
+ * @since ICU 3.0
+ */
+ private int maxSignificantDigits = 6;
+
+ /**
+ * True to force the use of exponential (i.e. scientific) notation when formatting
+ * numbers.
+ * useExponentialNotation
is not true.
+ * useExponentialNotation
is true.
+ * @serial
+ * @since AlphaWorks NumberFormat
+ */
+ private boolean exponentSignAlwaysShown = false;
+
+//#if defined(FOUNDATION10)
+//#else
+ // [NEW]
+ /**
+ * The value to which numbers are rounded during formatting. For example,
+ * if the rounding increment is 0.05, then 13.371 would be formatted as
+ * 13.350, assuming 3 fraction digits. Has the value null
if
+ * rounding is not in effect, or a positive value if rounding is in effect.
+ * Default value null
.
+ * @serial
+ * @since AlphaWorks NumberFormat
+ */
+ // Note: this is kept in sync with roundingIncrementICU.
+ // it is only kept around to avoid a conversion when formatting a java.math.BigDecimal
+ private java.math.BigDecimal roundingIncrement = null;
+//#endif
+
+ // [NEW]
+ /**
+ * The value to which numbers are rounded during formatting. For example,
+ * if the rounding increment is 0.05, then 13.371 would be formatted as
+ * 13.350, assuming 3 fraction digits. Has the value null
if
+ * rounding is not in effect, or a positive value if rounding is in effect.
+ * Default value null
.
+ * WARNING: the roundingIncrement value is the one serialized.
+ * @serial
+ * @since AlphaWorks NumberFormat
+ */
+ private transient BigDecimal roundingIncrementICU = null;
+
+ // [NEW]
+ /**
+ * The rounding increment as a double. If this value is <= 0, then no
+ * rounding is done. This value is
+ * roundingIncrementICU.doubleValue()
. Default value 0.0.
+ */
+ private transient double roundingDouble = 0.0;
+
+ // [NEW]
+ /**
+ * If the roundingDouble is the reciprocal of an integer (the most common case!),
+ * this is set to be that integer. Otherwise it is 0.0.
+ */
+ private transient double roundingDoubleReciprocal = 0.0;
+
+ // [NEW]
+ /**
+ * The rounding mode. This value controls any rounding operations which
+ * occur when applying a rounding increment or when reducing the number of
+ * fraction digits to satisfy a maximum fraction digits limit. The value
+ * may assume any of the BigDecimal
rounding mode values.
+ * Default value BigDecimal.ROUND_HALF_EVEN
.
+ * @serial
+ * @since AlphaWorks NumberFormat
+ */
+ private int roundingMode = BigDecimal.ROUND_HALF_EVEN;
+
+ // [NEW]
+ /**
+ * Operations on BigDecimal
numbers are controlled by a
+ * {@link MathContext} object, which provides the context (precision and
+ * other information) for the operation. The default MathContext
+ * settings are digits=0, form=PLAIN, lostDigits=false,
+ * roundingMode=ROUND_HALF_UP
; these settings perform fixed point
+ * arithmetic with unlimited precision, as defined for the original BigDecimal
+ * class in Java 1.1 and Java 1.2
+ */
+ private MathContext mathContext = new MathContext(0, MathContext.PLAIN); // context for plain unlimited math
+
+ // [NEW]
+ /**
+ * The padded format width, or zero if there is no padding. Must
+ * be >= 0. Default value zero.
+ * @serial
+ * @since AlphaWorks NumberFormat
+ */
+ private int formatWidth = 0;
+
+ // [NEW]
+ /**
+ * The character used to pad the result of format to
+ * formatWidth
, if padding is in effect. Default value ' '.
+ * @serial
+ * @since AlphaWorks NumberFormat
+ */
+ private char pad = ' ';
+
+ // [NEW]
+ /**
+ * The position in the string at which the pad
character
+ * will be inserted, if padding is in effect. Must have a value from
+ * PAD_BEFORE_PREFIX
to PAD_AFTER_SUFFIX
.
+ * Default value PAD_BEFORE_PREFIX
.
+ * @serial
+ * @since AlphaWorks NumberFormat
+ */
+ private int padPosition = PAD_BEFORE_PREFIX;
+
+ /**
+ * True if {@link #parse(String, ParsePosition)} to return BigDecimal
+ * rather than Long, Double or BigDecimal except special values.
+ * This property is introduced for J2SE 5 compatibility support.
+ * @serial
+ * @since ICU 3.6
+ * @see #setParseBigDecimal(boolean)
+ * @see #isParseBigDecimal()
+ */
+ private boolean parseBigDecimal = false;
+
+ //----------------------------------------------------------------------
+
+ static final int currentSerialVersion = 3;
+
+ /**
+ * The internal serial version which says which version was written
+ * Possible values are:
+ *
+ *
+ * @serial */
+ private int serialVersionOnStream = currentSerialVersion;
+
+ //----------------------------------------------------------------------
+ // CONSTANTS
+ //----------------------------------------------------------------------
+
+ // [NEW]
+ /**
+ * Constant for useExponentialNotation
and minExponentDigits
.
+ * getPadPosition()
and
+ * setPadPosition()
specifying pad characters inserted before
+ * the prefix.
+ * @see #setPadPosition
+ * @see #getPadPosition
+ * @see #PAD_AFTER_PREFIX
+ * @see #PAD_BEFORE_SUFFIX
+ * @see #PAD_AFTER_SUFFIX
+ * @stable ICU 2.0
+ */
+ public static final int PAD_BEFORE_PREFIX = 0;
+
+ // [NEW]
+ /**
+ * Constant for getPadPosition()
and
+ * setPadPosition()
specifying pad characters inserted after
+ * the prefix.
+ * @see #setPadPosition
+ * @see #getPadPosition
+ * @see #PAD_BEFORE_PREFIX
+ * @see #PAD_BEFORE_SUFFIX
+ * @see #PAD_AFTER_SUFFIX
+ * @stable ICU 2.0
+ */
+ public static final int PAD_AFTER_PREFIX = 1;
+
+ // [NEW]
+ /**
+ * Constant for getPadPosition()
and
+ * setPadPosition()
specifying pad characters inserted before
+ * the suffix.
+ * @see #setPadPosition
+ * @see #getPadPosition
+ * @see #PAD_BEFORE_PREFIX
+ * @see #PAD_AFTER_PREFIX
+ * @see #PAD_AFTER_SUFFIX
+ * @stable ICU 2.0
+ */
+ public static final int PAD_BEFORE_SUFFIX = 2;
+
+ // [NEW]
+ /**
+ * Constant for getPadPosition()
and
+ * setPadPosition()
specifying pad characters inserted after
+ * the suffix.
+ * @see #setPadPosition
+ * @see #getPadPosition
+ * @see #PAD_BEFORE_PREFIX
+ * @see #PAD_AFTER_PREFIX
+ * @see #PAD_BEFORE_SUFFIX
+ * @stable ICU 2.0
+ */
+ public static final int PAD_AFTER_SUFFIX = 3;
+
+ // Constants for characters used in programmatic (unlocalized) patterns.
+ private static final char PATTERN_ZERO_DIGIT = '0';
+ private static final char PATTERN_GROUPING_SEPARATOR = ',';
+ private static final char PATTERN_DECIMAL_SEPARATOR = '.';
+ private static final char PATTERN_DIGIT = '#';
+ static final char PATTERN_SIGNIFICANT_DIGIT = '@';
+ static final char PATTERN_EXPONENT = 'E'; // [NEW]
+ static final char PATTERN_PLUS_SIGN = '+'; // [NEW]
+
+ // Affix
+ private static final char PATTERN_PER_MILLE = '\u2030';
+ private static final char PATTERN_PERCENT = '%';
+ static final char PATTERN_PAD_ESCAPE = '*'; // [NEW]
+ /*Bug 4212072
+ To meet the need of expandAffix(String, StirngBuffer)
+ [Richard/GCL]
+ */
+ private static final char PATTERN_MINUS = '-'; //[Richard/GCL]
+
+ // Other
+ private static final char PATTERN_SEPARATOR = ';';
+
+ // Pad escape is package private to allow access by DecimalFormatSymbols.
+ // Also plus sign. Also exponent.
+
+ /**
+ * The CURRENCY_SIGN is the standard Unicode symbol for currency. It
+ * is used in patterns and substitued with either the currency symbol,
+ * or if it is doubled, with the international currency symbol. If the
+ * CURRENCY_SIGN is seen in a pattern, then the decimal separator is
+ * replaced with the monetary decimal separator.
+ *
+ * The CURRENCY_SIGN is not localized.
+ */
+ private static final char CURRENCY_SIGN = '\u00A4';
+
+ private static final char QUOTE = '\'';
+
+ /* Upper limit on integer and fraction digits for a Java double
+ [Richard/GCL]
+ */
+ static final int DOUBLE_INTEGER_DIGITS = 309;
+ static final int DOUBLE_FRACTION_DIGITS = 340;
+
+ /**
+ * When someone turns on scientific mode, we assume that more than this
+ * number of digits is due to flipping from some other mode that didn't
+ * restrict the maximum, and so we force 1 integer digit. We don't bother
+ * to track and see if someone is using exponential notation with more than
+ * this number, it wouldn't make sense anyway, and this is just to make sure
+ * that someone turning on scientific mode with default settings doesn't
+ * end up with lots of zeroes.
+ */
+ static final int MAX_SCIENTIFIC_INTEGER_DIGITS = 8;
+
+//#if defined(FOUNDATION10)
+//## // we're not compatible with other versions, since we have no java.math.BigDecimal field
+//## private static final long serialVersionUID = 2;
+//#else
+ // Proclaim JDK 1.1 serial compatibility.
+ private static final long serialVersionUID = 864413376551465018L;
+//#endif
+
+//#if defined(FOUNDATION10) || defined(J2SE13)
+//#else
+ private ArrayList attributes = new ArrayList();
+//#endif
+
+ /*
+ * Following are used in currency format
+ */
+/*
+ // triple currency sign char array
+ private static final char[] tripleCurrencySign = {0xA4, 0xA4, 0xA4};
+ // triple currency sign string
+ private static final String tripleCurrencyStr = new String(tripleCurrencySign);
+
+ // default currency plural pattern char array
+ private static final char[] defaultCurrencyPluralPatternChar = {0, '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4};
+ // default currency plural pattern string
+ private static final String defaultCurrencyPluralPattern = new String(defaultCurrencyPluralPatternChar);
+*/
+
+ // pattern used in this formatter
+ private String formatPattern = "";
+ // style is only valid when decimal formatter is constructed by
+ // DecimalFormat(pattern, decimalFormatSymbol, style)
+ private int style = NumberFormat.NUMBERSTYLE;
+ /*
+ * Represents whether this is a currency format, and which
+ * currency format style.
+ * 0: not currency format type;
+ * 1: currency style -- symbol name, such as "$" for US dollar.
+ * 2: currency style -- ISO name, such as USD for US dollar.
+ * 3: currency style -- plural long name, such as "US Dollar" for
+ * "1.00 US Dollar", or "US Dollars" for
+ * "3.00 US Dollars".
+ */
+ private int currencySignCount = 0;
+
+
+ /* For parsing purose,
+ * Need to remember all prefix patterns and suffix patterns of
+ * every currency format pattern,
+ * including the pattern of default currecny style, ISO currency style,
+ * and plural currency style. And the patterns are set through applyPattern.
+ * Following are used to represent the affix patterns in currency plural
+ * formats.
+ */
+ private static final class AffixForCurrency {
+ // negative prefix pattern
+ private String negPrefixPatternForCurrency = null;
+ // negative suffix pattern
+ private String negSuffixPatternForCurrency = null;
+ // positive prefix pattern
+ private String posPrefixPatternForCurrency = null;
+ // positive suffix pattern
+ private String posSuffixPatternForCurrency = null;
+ private int patternType;
+
+ public AffixForCurrency() {
+ patternType = Currency.SYMBOL_NAME;
+ }
+
+ public AffixForCurrency(String negPrefix, String negSuffix,
+ String posPrefix, String posSuffix,
+ int type) {
+ negPrefixPatternForCurrency = negPrefix;
+ negSuffixPatternForCurrency = negSuffix;
+ posPrefixPatternForCurrency = posPrefix;
+ posSuffixPatternForCurrency = posSuffix;
+ patternType = type;
+ }
+
+ public String getNegPrefix() {
+ return negPrefixPatternForCurrency;
+ }
+
+ public String getNegSuffix() {
+ return negSuffixPatternForCurrency;
+ }
+
+ public String getPosPrefix() {
+ return posPrefixPatternForCurrency;
+ }
+
+ public String getPosSuffix() {
+ return posSuffixPatternForCurrency;
+ }
+
+ public int getPatternType() {
+ return patternType;
+ }
+ }
+ // Affix patter set for currency.
+ // It is a set of AffixForCurrency,
+ // each element of the set saves the negative prefix,
+ // negative suffix, positive prefix, and positive suffix of a pattern.
+ private transient Set affixPatternsForCurrency = null;
+
+ // For currency parsing, since currency parsing need to parse
+ // against all currency patterns, before the parsing, need to set up
+ // the affix patterns for currency.
+ private transient boolean isReadyForParsing = false;
+
+ // Information needed for DecimalFormat to format/parse currency plural.
+ private CurrencyPluralInfo currencyPluralInfo = null;
+
+}
+
+//eof