com.ibm.icu.text.DecimalFormat Maven / Gradle / Ivy
Show all versions of icu4j Show documentation
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
package com.ibm.icu.text;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.AttributedCharacterIterator;
import java.text.FieldPosition;
import java.text.ParsePosition;
import com.ibm.icu.impl.FormattedStringBuilder;
import com.ibm.icu.impl.FormattedValueStringBuilderImpl;
import com.ibm.icu.impl.Utility;
import com.ibm.icu.impl.number.AffixUtils;
import com.ibm.icu.impl.number.DecimalFormatProperties;
import com.ibm.icu.impl.number.DecimalFormatProperties.ParseMode;
import com.ibm.icu.impl.number.DecimalQuantity;
import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
import com.ibm.icu.impl.number.Padder;
import com.ibm.icu.impl.number.Padder.PadPosition;
import com.ibm.icu.impl.number.PatternStringParser;
import com.ibm.icu.impl.number.PatternStringUtils;
import com.ibm.icu.impl.number.parse.NumberParserImpl;
import com.ibm.icu.impl.number.parse.ParsedNumber;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.math.BigDecimal;
import com.ibm.icu.math.MathContext;
import com.ibm.icu.number.FormattedNumber;
import com.ibm.icu.number.LocalizedNumberFormatter;
import com.ibm.icu.number.NumberFormatter;
import com.ibm.icu.text.PluralRules.IFixedDecimal;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.Currency.CurrencyUsage;
import com.ibm.icu.util.CurrencyAmount;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.ULocale.Category;
/**
* {@icuenhanced java.text.DecimalFormat}.{@icu _usage_}
*
*
* IMPORTANT: New users are strongly encouraged to see if
* {@link NumberFormatter} fits their use case. Although not deprecated, this
* class, DecimalFormat, is only provided for java.text.DecimalFormat compatibility.
*
*
* DecimalFormat
is the primary
* concrete subclass of {@link NumberFormat}. 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 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 number formatter for a specific locale (including the default locale), call one of
* NumberFormat's factory methods such as {@link NumberFormat#getInstance}. Do not call
* DecimalFormat constructors directly unless you know what you are doing.
*
*
DecimalFormat aims to comply with the specification UTS #35. Read
* the specification for more information on how all the properties in DecimalFormat fit together.
*
*
NOTE: Starting in ICU 60, there is a new set of APIs for localized number
* formatting that are designed to be an improvement over DecimalFormat. New users are discouraged
* from using DecimalFormat. For more information, see the package com.ibm.icu.number.
*
*
Example Usage
*
* Customize settings on a DecimalFormat instance from the NumberFormat factory:
*
*
*
*
* NumberFormat f = NumberFormat.getInstance(loc);
* if (f instanceof DecimalFormat) {
* ((DecimalFormat) f).setDecimalSeparatorAlwaysShown(true);
* ((DecimalFormat) f).setMinimumGroupingDigits(2);
* }
*
*
*
*
* Quick and dirty print out a number using the localized number, currency, and percent format
* for each locale:
*
*
*
*
* for (ULocale uloc : ULocale.getAvailableLocales()) {
* System.out.print(uloc + ":\t");
* System.out.print(NumberFormat.getInstance(uloc).format(1.23));
* System.out.print("\t");
* System.out.print(NumberFormat.getCurrencyInstance(uloc).format(1.23));
* System.out.print("\t");
* System.out.print(NumberFormat.getPercentInstance(uloc).format(1.23));
* System.out.println();
* }
*
*
*
*
* Properties and Symbols
*
* A DecimalFormat object encapsulates a set of properties and a set of
* symbols. Grouping size, rounding mode, and affixes are examples of properties. Locale
* digits and the characters used for grouping and decimal separators are examples of symbols.
*
*
To set a custom set of symbols, use {@link #setDecimalFormatSymbols}. Use the various other
* setters in this class to set custom values for the properties.
*
*
Rounding
*
* DecimalFormat provides three main strategies to specify the position at which numbers should
* be rounded:
*
*
* - Magnitude: Display a fixed number of fraction digits; this is the most
* common form.
*
- Increment: Round numbers to the closest multiple of a certain increment,
* such as 0.05. This is common in currencies.
*
- Significant Digits: Round numbers such that a fixed number of nonzero
* digits are shown. This is most common in scientific notation.
*
*
* It is not possible to specify more than one rounding strategy. For example, setting a rounding
* increment in conjunction with significant digits results in undefined behavior.
*
*
It is also possible to specify the rounding mode to use. The default rounding mode is
* "half even", which rounds numbers to their closest increment, with ties broken in favor of
* trailing numbers being even. For more information, see {@link #setRoundingMode} and the ICU
* User Guide.
*
*
Pattern Strings
*
* A pattern string is a way to serialize some of the available properties for decimal
* formatting. However, not all properties are capable of being serialized into a pattern string;
* see {@link #applyPattern} for more information.
*
*
Most users should not need to interface with pattern strings directly.
*
*
ICU DecimalFormat aims to follow the specification for pattern strings in UTS #35.
* Refer to that specification for more information on pattern string syntax.
*
*
Pattern String BNF
*
* The following BNF is used when parsing the pattern string into property values:
*
*
* 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:
*
*
* - The grouping separator ',' can occur inside the integer and sigDigits elements, between any
* two pattern characters of that element, as long as the integer or sigDigits element is not
* followed by the exponent element.
*
- Two grouping intervals are recognized: That between the decimal point and the first
* grouping symbol, and that between the first and second grouping symbols. These intervals
* are identical in most locales, but in some locales they differ. For example, the pattern
* "#,##,###" formats the number 123456789 as "12,34,56,789".
*
- The pad specifier
padSpec
may appear before the prefix, after the prefix,
* before the suffix, after the suffix, or not at all.
* - In place of '0', the digits '1' through '9' may be used to indicate a rounding increment.
*
*
* Parsing
*
* DecimalFormat aims to be able to parse anything that it can output as a formatted string.
*
*
There are two primary parse modes: lenient and strict. Lenient mode should
* be used if the goal is to parse user input to a number; strict mode should be used if the goal is
* validation. The default is lenient mode. For more information, see {@link #setParseStrict}.
*
*
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.
*
*
Grouping separators are ignored in lenient mode (default). In strict mode, grouping separators
* must match the locale-specified grouping sizes.
*
*
When using {@link #parseCurrency}, all currencies are accepted, not just the currency
* currently set in the formatter. In addition, the formatter is able to parse every currency style
* format for a particular locale no matter which style the formatter is constructed with. For
* example, a formatter instance gotten from NumberFormat.getInstance(ULocale,
* NumberFormat.CURRENCYSTYLE) can parse both "USD1.00" and "3.00 US dollars".
*
*
Whitespace characters (lenient mode) and control characters (lenient and strict mode),
* collectively called "ignorables", do not need to match in identity or quantity between the
* pattern string and the input string. For example, the pattern "# %" matches "35 %" (with a single
* space), "35%" (with no space), "35 %" (with a non-breaking space), and "35 %" (with
* multiple spaces). Arbitrary ignorables are also allowed at boundaries between the parts of the
* number: prefix, number, exponent separator, and suffix. Ignorable whitespace characters are those
* having the Unicode "blank" property for regular expressions, defined in UTS #18 Annex C, which is
* "horizontal" whitespace, like spaces and tabs, but not "vertical" whitespace, like line breaks.
* Ignorable control characters are those in the Unicode set [:Default_Ignorable_Code_Point:].
*
*
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}.
*
*
Under the hood, a state table parsing engine is used. To debug a parsing failure during
* development, use the following pattern to print details about the state table transitions:
*
*
* com.ibm.icu.impl.number.Parse.DEBUGGING = true;
* df.parse("123.45", ppos);
* com.ibm.icu.impl.number.Parse.DEBUGGING = false;
*
*
* Thread Safety and Best Practices
*
* Starting with ICU 59, instances of DecimalFormat are thread-safe.
*
*
Under the hood, DecimalFormat maintains an immutable formatter object that is rebuilt whenever
* any of the property setters are called. It is therefore best practice to call property setters
* only during construction and not when formatting numbers online.
*
* @see java.text.Format
* @see NumberFormat
* @stable ICU 2.0
*/
public class DecimalFormat extends NumberFormat {
/** New serialization in ICU 59: declare different version from ICU 58. */
private static final long serialVersionUID = 864413376551465018L;
/**
* One non-transient field such that deserialization can determine the version of the class. This
* field has existed since the very earliest versions of DecimalFormat.
*/
@SuppressWarnings("unused")
private final int serialVersionOnStream = 5;
//=====================================================================================//
// INSTANCE FIELDS //
//=====================================================================================//
// Fields are package-private, so that subclasses can use them.
// properties should be final, but clone won't work if we make it final.
// All fields are transient because custom serialization is used.
/**
* The property bag corresponding to user-specified settings and settings from the pattern string.
* In principle this should be final, but serialize and clone won't work if it is final. Does not
* need to be volatile because the reference never changes.
*/
/* final */ transient DecimalFormatProperties properties;
/**
* The symbols for the current locale. Volatile because threads may read and write at the same
* time.
*/
transient volatile DecimalFormatSymbols symbols;
/**
* The pre-computed formatter object. Setters cause this to be re-computed atomically. The {@link
* #format} method uses the formatter directly without needing to synchronize. Volatile because
* threads may read and write at the same time.
*/
transient volatile LocalizedNumberFormatter formatter;
/**
* The effective properties as exported from the formatter object. Volatile because threads may
* read and write at the same time.
*/
transient volatile DecimalFormatProperties exportedProperties;
transient volatile NumberParserImpl parser;
transient volatile NumberParserImpl currencyParser;
//=====================================================================================//
// CONSTRUCTORS //
//=====================================================================================//
/**
* Creates a DecimalFormat based on the number pattern and symbols for the default locale. This is
* a convenient way to obtain a DecimalFormat instance when internationalization is not the main
* concern.
*
*
Most users should call the factory methods on NumberFormat, such as {@link
* NumberFormat#getNumberInstance}, which return localized formatter objects, instead of the
* DecimalFormat constructors.
*
* @see NumberFormat#getInstance
* @see NumberFormat#getNumberInstance
* @see NumberFormat#getCurrencyInstance
* @see NumberFormat#getPercentInstance
* @see Category#FORMAT
* @stable ICU 2.0
*/
public DecimalFormat() {
// Use the locale's default pattern
ULocale def = ULocale.getDefault(ULocale.Category.FORMAT);
String pattern = getPattern(def, NumberFormat.NUMBERSTYLE);
symbols = getDefaultSymbols();
properties = new DecimalFormatProperties();
exportedProperties = new DecimalFormatProperties();
// Regression: ignore pattern rounding information if the pattern has currency symbols.
setPropertiesFromPattern(pattern, PatternStringParser.IGNORE_ROUNDING_IF_CURRENCY);
refreshFormatter();
}
/**
* Creates a DecimalFormat based on the given pattern, using symbols for the default locale. This
* is a convenient way to obtain a DecimalFormat instance when internationalization is not the
* main concern.
*
*
Most users should call the factory methods on NumberFormat, such as {@link
* NumberFormat#getNumberInstance}, which return localized formatter objects, instead of the
* DecimalFormat constructors.
*
* @param pattern A pattern string such as "#,##0.00" conforming to UTS
* #35.
* @throws IllegalArgumentException if the given pattern is invalid.
* @see NumberFormat#getInstance
* @see NumberFormat#getNumberInstance
* @see NumberFormat#getCurrencyInstance
* @see NumberFormat#getPercentInstance
* @see Category#FORMAT
* @stable ICU 2.0
*/
public DecimalFormat(String pattern) {
symbols = getDefaultSymbols();
properties = new DecimalFormatProperties();
exportedProperties = new DecimalFormatProperties();
// Regression: ignore pattern rounding information if the pattern has currency symbols.
setPropertiesFromPattern(pattern, PatternStringParser.IGNORE_ROUNDING_IF_CURRENCY);
refreshFormatter();
}
/**
* Creates a DecimalFormat based on the given pattern and symbols. Use this constructor if you
* want complete control over the behavior of the formatter.
*
*
Most users should call the factory methods on NumberFormat, such as {@link
* NumberFormat#getNumberInstance}, which return localized formatter objects, instead of the
* DecimalFormat constructors.
*
* @param pattern A pattern string such as "#,##0.00" conforming to UTS
* #35.
* @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) {
this.symbols = (DecimalFormatSymbols) symbols.clone();
properties = new DecimalFormatProperties();
exportedProperties = new DecimalFormatProperties();
// Regression: ignore pattern rounding information if the pattern has currency symbols.
setPropertiesFromPattern(pattern, PatternStringParser.IGNORE_ROUNDING_IF_CURRENCY);
refreshFormatter();
}
/**
* Creates a DecimalFormat based on the given pattern and symbols, with additional control over
* the behavior of currency. The style argument determines whether currency rounding rules should
* override the pattern, and the {@link CurrencyPluralInfo} object is used for customizing the
* plural forms used for currency long names.
*
*
Most users should call the factory methods on NumberFormat, such as {@link
* NumberFormat#getNumberInstance}, which return localized formatter objects, instead of the
* DecimalFormat constructors.
*
* @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;
* @stable ICU 4.2
*/
public DecimalFormat(
String pattern, DecimalFormatSymbols symbols, CurrencyPluralInfo infoInput, int style) {
this(pattern, symbols, style);
properties.setCurrencyPluralInfo(infoInput);
refreshFormatter();
}
/** Package-private constructor used by NumberFormat. */
DecimalFormat(String pattern, DecimalFormatSymbols symbols, int choice) {
this.symbols = (DecimalFormatSymbols) symbols.clone();
properties = new DecimalFormatProperties();
exportedProperties = new DecimalFormatProperties();
// If choice is a currency type, ignore the rounding information.
if (choice == CURRENCYSTYLE
|| choice == ISOCURRENCYSTYLE
|| choice == ACCOUNTINGCURRENCYSTYLE
|| choice == CASHCURRENCYSTYLE
|| choice == STANDARDCURRENCYSTYLE
|| choice == PLURALCURRENCYSTYLE) {
setPropertiesFromPattern(pattern, PatternStringParser.IGNORE_ROUNDING_ALWAYS);
} else {
setPropertiesFromPattern(pattern, PatternStringParser.IGNORE_ROUNDING_IF_CURRENCY);
}
refreshFormatter();
}
private static DecimalFormatSymbols getDefaultSymbols() {
return DecimalFormatSymbols.getInstance();
}
/**
* Parses the given pattern string and overwrites the settings specified in the pattern string.
* The properties corresponding to the following setters are overwritten, either with their
* default values or with the value specified in the pattern string:
*
*
* - {@link #setDecimalSeparatorAlwaysShown}
*
- {@link #setExponentSignAlwaysShown}
*
- {@link #setFormatWidth}
*
- {@link #setGroupingSize}
*
- {@link #setMultiplier} (percent/permille)
*
- {@link #setMaximumFractionDigits}
*
- {@link #setMaximumIntegerDigits}
*
- {@link #setMaximumSignificantDigits}
*
- {@link #setMinimumExponentDigits}
*
- {@link #setMinimumFractionDigits}
*
- {@link #setMinimumIntegerDigits}
*
- {@link #setMinimumSignificantDigits}
*
- {@link #setPadPosition}
*
- {@link #setPadCharacter}
*
- {@link #setRoundingIncrement}
*
- {@link #setSecondaryGroupingSize}
*
*
* All other settings remain untouched.
*
* For more information on pattern strings, see UTS #35.
*
* @stable ICU 2.0
*/
public synchronized void applyPattern(String pattern) {
setPropertiesFromPattern(pattern, PatternStringParser.IGNORE_ROUNDING_NEVER);
// Backwards compatibility: clear out user-specified prefix and suffix,
// as well as CurrencyPluralInfo.
properties.setPositivePrefix(null);
properties.setNegativePrefix(null);
properties.setPositiveSuffix(null);
properties.setNegativeSuffix(null);
properties.setCurrencyPluralInfo(null);
refreshFormatter();
}
/**
* Converts the given string to standard notation and then parses it using {@link #applyPattern}.
* This method is provided for backwards compatibility and should not be used in new projects.
*
*
Localized notation means that instead of using generic placeholders in the pattern, you use
* the corresponding locale-specific characters instead. For example, in locale fr-FR,
* the period in the pattern "0.000" means "decimal" in standard notation (as it does in every
* other locale), but it means "grouping" in localized notation.
*
* @param localizedPattern The pattern string in localized notation.
* @stable ICU 2.0
*/
public synchronized void applyLocalizedPattern(String localizedPattern) {
String pattern = PatternStringUtils.convertLocalized(localizedPattern, symbols, false);
applyPattern(pattern);
}
//=====================================================================================//
// CLONE AND SERIALIZE //
//=====================================================================================//
/** @stable ICU 2.0 */
@Override
public Object clone() {
DecimalFormat other = (DecimalFormat) super.clone();
other.symbols = (DecimalFormatSymbols) symbols.clone();
other.properties = properties.clone();
other.exportedProperties = new DecimalFormatProperties();
other.refreshFormatter();
return other;
}
/**
* Custom serialization: save property bag and symbols; the formatter object can be re-created
* from just that amount of information.
*/
private synchronized void writeObject(ObjectOutputStream oos) throws IOException {
// ICU 59 custom serialization.
// Write class metadata and serialVersionOnStream field:
oos.defaultWriteObject();
// Extra int for possible future use:
oos.writeInt(0);
// 1) Property Bag
oos.writeObject(properties);
// 2) DecimalFormatSymbols
oos.writeObject(symbols);
}
/**
* Custom serialization: re-create object from serialized property bag and symbols. Also supports
* reading from the legacy (pre-ICU4J 59) format and converting it to the new form.
*/
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ObjectInputStream.GetField fieldGetter = ois.readFields();
ObjectStreamField[] serializedFields = fieldGetter.getObjectStreamClass().getFields();
int serialVersion = fieldGetter.get("serialVersionOnStream", -1);
if (serialVersion > 5) {
throw new IOException(
"Cannot deserialize newer com.ibm.icu.text.DecimalFormat (v" + serialVersion + ")");
} else if (serialVersion == 5) {
///// ICU 59+ SERIALIZATION FORMAT /////
// We expect this field and no other fields:
if (serializedFields.length > 1) {
throw new IOException("Too many fields when reading serial version 5");
}
// Extra int for possible future use:
ois.readInt();
// 1) Property Bag
Object serializedProperties = ois.readObject();
if (serializedProperties instanceof DecimalFormatProperties) {
// ICU 60+
properties = (DecimalFormatProperties) serializedProperties;
} else {
// ICU 59
properties = ((com.ibm.icu.impl.number.Properties) serializedProperties).getInstance();
}
// 2) DecimalFormatSymbols
symbols = (DecimalFormatSymbols) ois.readObject();
// Re-build transient fields
exportedProperties = new DecimalFormatProperties();
refreshFormatter();
} else {
///// LEGACY SERIALIZATION FORMAT /////
properties = new DecimalFormatProperties();
// Loop through the fields. Not all fields necessarily exist in the serialization.
String pp = null, ppp = null, ps = null, psp = null;
String np = null, npp = null, ns = null, nsp = null;
for (ObjectStreamField field : serializedFields) {
String name = field.getName();
if (name.equals("decimalSeparatorAlwaysShown")) {
setDecimalSeparatorAlwaysShown(fieldGetter.get("decimalSeparatorAlwaysShown", false));
} else if (name.equals("exponentSignAlwaysShown")) {
setExponentSignAlwaysShown(fieldGetter.get("exponentSignAlwaysShown", false));
} else if (name.equals("formatWidth")) {
setFormatWidth(fieldGetter.get("formatWidth", 0));
} else if (name.equals("groupingSize")) {
setGroupingSize(fieldGetter.get("groupingSize", (byte) 3));
} else if (name.equals("groupingSize2")) {
setSecondaryGroupingSize(fieldGetter.get("groupingSize2", (byte) 0));
} else if (name.equals("maxSignificantDigits")) {
setMaximumSignificantDigits(fieldGetter.get("maxSignificantDigits", 6));
} else if (name.equals("minExponentDigits")) {
setMinimumExponentDigits(fieldGetter.get("minExponentDigits", (byte) 0));
} else if (name.equals("minSignificantDigits")) {
setMinimumSignificantDigits(fieldGetter.get("minSignificantDigits", 1));
} else if (name.equals("multiplier")) {
setMultiplier(fieldGetter.get("multiplier", 1));
} else if (name.equals("pad")) {
setPadCharacter(fieldGetter.get("pad", '\u0020'));
} else if (name.equals("padPosition")) {
setPadPosition(fieldGetter.get("padPosition", 0));
} else if (name.equals("parseBigDecimal")) {
setParseBigDecimal(fieldGetter.get("parseBigDecimal", false));
} else if (name.equals("parseRequireDecimalPoint")) {
setDecimalPatternMatchRequired(fieldGetter.get("parseRequireDecimalPoint", false));
} else if (name.equals("roundingMode")) {
setRoundingMode(fieldGetter.get("roundingMode", 0));
} else if (name.equals("useExponentialNotation")) {
setScientificNotation(fieldGetter.get("useExponentialNotation", false));
} else if (name.equals("useSignificantDigits")) {
setSignificantDigitsUsed(fieldGetter.get("useSignificantDigits", false));
} else if (name.equals("currencyPluralInfo")) {
setCurrencyPluralInfo((CurrencyPluralInfo) fieldGetter.get("currencyPluralInfo", null));
} else if (name.equals("mathContext")) {
setMathContextICU((MathContext) fieldGetter.get("mathContext", null));
} else if (name.equals("negPrefixPattern")) {
npp = (String) fieldGetter.get("negPrefixPattern", null);
} else if (name.equals("negSuffixPattern")) {
nsp = (String) fieldGetter.get("negSuffixPattern", null);
} else if (name.equals("negativePrefix")) {
np = (String) fieldGetter.get("negativePrefix", null);
} else if (name.equals("negativeSuffix")) {
ns = (String) fieldGetter.get("negativeSuffix", null);
} else if (name.equals("posPrefixPattern")) {
ppp = (String) fieldGetter.get("posPrefixPattern", null);
} else if (name.equals("posSuffixPattern")) {
psp = (String) fieldGetter.get("posSuffixPattern", null);
} else if (name.equals("positivePrefix")) {
pp = (String) fieldGetter.get("positivePrefix", null);
} else if (name.equals("positiveSuffix")) {
ps = (String) fieldGetter.get("positiveSuffix", null);
} else if (name.equals("roundingIncrement")) {
setRoundingIncrement((java.math.BigDecimal) fieldGetter.get("roundingIncrement", null));
} else if (name.equals("symbols")) {
setDecimalFormatSymbols((DecimalFormatSymbols) fieldGetter.get("symbols", null));
} else {
// The following fields are ignored:
// "PARSE_MAX_EXPONENT"
// "currencySignCount"
// "style"
// "attributes"
// "currencyChoice"
// "formatPattern"
// "currencyUsage" => ignore this because the old code puts currencyUsage directly into min/max fraction.
}
}
// Resolve affixes
if (npp == null) {
properties.setNegativePrefix(np);
} else {
properties.setNegativePrefixPattern(npp);
}
if (nsp == null) {
properties.setNegativeSuffix(ns);
} else {
properties.setNegativeSuffixPattern(nsp);
}
if (ppp == null) {
properties.setPositivePrefix(pp);
} else {
properties.setPositivePrefixPattern(ppp);
}
if (psp == null) {
properties.setPositiveSuffix(ps);
} else {
properties.setPositiveSuffixPattern(psp);
}
// Extract values from parent NumberFormat class. Have to use reflection here.
java.lang.reflect.Field getter;
try {
getter = NumberFormat.class.getDeclaredField("groupingUsed");
getter.setAccessible(true);
setGroupingUsed((Boolean) getter.get(this));
getter = NumberFormat.class.getDeclaredField("parseIntegerOnly");
getter.setAccessible(true);
setParseIntegerOnly((Boolean) getter.get(this));
getter = NumberFormat.class.getDeclaredField("maximumIntegerDigits");
getter.setAccessible(true);
setMaximumIntegerDigits((Integer) getter.get(this));
getter = NumberFormat.class.getDeclaredField("minimumIntegerDigits");
getter.setAccessible(true);
setMinimumIntegerDigits((Integer) getter.get(this));
getter = NumberFormat.class.getDeclaredField("maximumFractionDigits");
getter.setAccessible(true);
setMaximumFractionDigits((Integer) getter.get(this));
getter = NumberFormat.class.getDeclaredField("minimumFractionDigits");
getter.setAccessible(true);
setMinimumFractionDigits((Integer) getter.get(this));
getter = NumberFormat.class.getDeclaredField("currency");
getter.setAccessible(true);
setCurrency((Currency) getter.get(this));
getter = NumberFormat.class.getDeclaredField("parseStrict");
getter.setAccessible(true);
setParseStrict((Boolean) getter.get(this));
} catch (IllegalArgumentException e) {
throw new IOException(e);
} catch (IllegalAccessException e) {
throw new IOException(e);
} catch (NoSuchFieldException e) {
throw new IOException(e);
} catch (SecurityException e) {
throw new IOException(e);
}
// Finish initialization
if (symbols == null) {
symbols = getDefaultSymbols();
}
exportedProperties = new DecimalFormatProperties();
refreshFormatter();
}
}
//=====================================================================================//
// FORMAT AND PARSE APIS //
//=====================================================================================//
/**
* {@inheritDoc}
*
* @stable ICU 2.0
*/
@Override
public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) {
DecimalQuantity dq = new DecimalQuantity_DualStorageBCD(number);
FormattedStringBuilder string = new FormattedStringBuilder();
formatter.formatImpl(dq, string);
fieldPositionHelper(dq, string, fieldPosition, result.length());
Utility.appendTo(string, result);
return result;
}
/**
* {@inheritDoc}
*
* @stable ICU 2.0
*/
@Override
public StringBuffer format(long number, StringBuffer result, FieldPosition fieldPosition) {
DecimalQuantity dq = new DecimalQuantity_DualStorageBCD(number);
FormattedStringBuilder string = new FormattedStringBuilder();
formatter.formatImpl(dq, string);
fieldPositionHelper(dq, string, fieldPosition, result.length());
Utility.appendTo(string, result);
return result;
}
/**
* {@inheritDoc}
*
* @stable ICU 2.0
*/
@Override
public StringBuffer format(BigInteger number, StringBuffer result, FieldPosition fieldPosition) {
DecimalQuantity dq = new DecimalQuantity_DualStorageBCD(number);
FormattedStringBuilder string = new FormattedStringBuilder();
formatter.formatImpl(dq, string);
fieldPositionHelper(dq, string, fieldPosition, result.length());
Utility.appendTo(string, result);
return result;
}
/**
* {@inheritDoc}
*
* @stable ICU 2.0
*/
@Override
public StringBuffer format(
java.math.BigDecimal number, StringBuffer result, FieldPosition fieldPosition) {
DecimalQuantity dq = new DecimalQuantity_DualStorageBCD(number);
FormattedStringBuilder string = new FormattedStringBuilder();
formatter.formatImpl(dq, string);
fieldPositionHelper(dq, string, fieldPosition, result.length());
Utility.appendTo(string, result);
return result;
}
/**
* {@inheritDoc}
*
* @stable ICU 2.0
*/
@Override
public StringBuffer format(BigDecimal number, StringBuffer result, FieldPosition fieldPosition) {
DecimalQuantity dq = new DecimalQuantity_DualStorageBCD(number);
FormattedStringBuilder string = new FormattedStringBuilder();
formatter.formatImpl(dq, string);
fieldPositionHelper(dq, string, fieldPosition, result.length());
Utility.appendTo(string, result);
return result;
}
/**
* {@inheritDoc}
*
* @stable ICU 3.6
*/
@Override
public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
if (!(obj instanceof Number)) throw new IllegalArgumentException();
Number number = (Number) obj;
FormattedNumber output = formatter.format(number);
return output.toCharacterIterator();
}
/**
* {@inheritDoc}
*
* @stable ICU 3.0
*/
@Override
public StringBuffer format(CurrencyAmount currAmt, StringBuffer result, FieldPosition fieldPosition) {
// We need to make localSymbols in order for monetary symbols to be initialized.
// Also, bypass the CurrencyAmount override of LocalizedNumberFormatter#format,
// because its caching mechanism will not provide any benefit here.
DecimalFormatSymbols localSymbols = (DecimalFormatSymbols) symbols.clone();
localSymbols.setCurrency(currAmt.getCurrency());
DecimalQuantity dq = new DecimalQuantity_DualStorageBCD(currAmt.getNumber());
FormattedStringBuilder string = new FormattedStringBuilder();
formatter.symbols(localSymbols)
.unit(currAmt.getCurrency())
.formatImpl(dq, string);
fieldPositionHelper(dq, string, fieldPosition, result.length());
Utility.appendTo(string, result);
return result;
}
/**
* {@inheritDoc}
*
* @stable ICU 2.0
*/
@Override
public Number parse(String text, ParsePosition parsePosition) {
if (text == null) {
throw new IllegalArgumentException("Text cannot be null");
}
if (parsePosition == null) {
parsePosition = new ParsePosition(0);
}
if (parsePosition.getIndex() < 0) {
throw new IllegalArgumentException("Cannot start parsing at a negative offset");
}
if (parsePosition.getIndex() >= text.length()) {
// For backwards compatibility, this is not an exception, just an empty result.
return null;
}
ParsedNumber result = new ParsedNumber();
// Note: if this is a currency instance, currencies will be matched despite the fact that we are not in the
// parseCurrency method (backwards compatibility)
int startIndex = parsePosition.getIndex();
NumberParserImpl parser = getParser();
parser.parse(text, startIndex, true, result);
if (result.success()) {
parsePosition.setIndex(result.charEnd);
// TODO: Accessing properties here is technically not thread-safe
Number number = result.getNumber(parser.getParseFlags());
// Backwards compatibility: return com.ibm.icu.math.BigDecimal
if (number instanceof java.math.BigDecimal) {
number = safeConvertBigDecimal((java.math.BigDecimal) number);
}
return number;
} else {
parsePosition.setErrorIndex(startIndex + result.charEnd);
return null;
}
}
/**
* {@inheritDoc}
*
* @stable ICU 49
*/
@Override
public CurrencyAmount parseCurrency(CharSequence text, ParsePosition parsePosition) {
if (text == null) {
throw new IllegalArgumentException("Text cannot be null");
}
if (parsePosition == null) {
parsePosition = new ParsePosition(0);
}
if (parsePosition.getIndex() < 0) {
throw new IllegalArgumentException("Cannot start parsing at a negative offset");
}
if (parsePosition.getIndex() >= text.length()) {
// For backwards compatibility, this is not an exception, just an empty result.
return null;
}
ParsedNumber result = new ParsedNumber();
int startIndex = parsePosition.getIndex();
NumberParserImpl parser = getCurrencyParser();
parser.parse(text.toString(), startIndex, true, result);
if (result.success()) {
parsePosition.setIndex(result.charEnd);
// TODO: Accessing properties here is technically not thread-safe
Number number = result.getNumber(parser.getParseFlags());
// Backwards compatibility: return com.ibm.icu.math.BigDecimal
if (number instanceof java.math.BigDecimal) {
number = safeConvertBigDecimal((java.math.BigDecimal) number);
}
Currency currency = Currency.getInstance(result.currencyCode);
return new CurrencyAmount(number, currency);
} else {
parsePosition.setErrorIndex(startIndex + result.charEnd);
return null;
}
}
//=====================================================================================//
// GETTERS AND SETTERS //
//=====================================================================================//
/**
* Returns a copy of the decimal format symbols used by this formatter.
*
* @return desired DecimalFormatSymbols
* @see DecimalFormatSymbols
* @stable ICU 2.0
*/
public synchronized DecimalFormatSymbols getDecimalFormatSymbols() {
return (DecimalFormatSymbols) symbols.clone();
}
/**
* Sets the decimal format symbols used by this formatter. The formatter uses a copy of the
* provided symbols.
*
* @param newSymbols desired DecimalFormatSymbols
* @see DecimalFormatSymbols
* @stable ICU 2.0
*/
public synchronized void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) {
symbols = (DecimalFormatSymbols) newSymbols.clone();
refreshFormatter();
}
/**
* Affixes: Gets the positive prefix string currently being used to format
* numbers.
*
*
If the affix was specified via the pattern, the string returned by this method will have
* locale symbols substituted in place of special characters according to the LDML specification.
* If the affix was specified via {@link #setPositivePrefix}, the string will be returned
* literally.
*
* @return The string being prepended to positive numbers.
* @category Affixes
* @stable ICU 2.0
*/
public synchronized String getPositivePrefix() {
return formatter.getAffixImpl(true, false);
}
/**
* Affixes: Sets the string to prepend to positive numbers. For example, if you
* set the value "#", then the number 123 will be formatted as "#123" in the locale
* en-US.
*
*
Using this method overrides the affix specified via the pattern, and unlike the pattern, the
* string given to this method will be interpreted literally WITHOUT locale symbol substitutions.
*
* @param prefix The literal string to prepend to positive numbers.
* @category Affixes
* @stable ICU 2.0
*/
public synchronized void setPositivePrefix(String prefix) {
if (prefix == null) {
throw new NullPointerException();
}
properties.setPositivePrefix(prefix);
refreshFormatter();
}
/**
* Affixes: Gets the negative prefix string currently being used to format
* numbers.
*
*
If the affix was specified via the pattern, the string returned by this method will have
* locale symbols substituted in place of special characters according to the LDML specification.
* If the affix was specified via {@link #setNegativePrefix}, the string will be returned
* literally.
*
* @return The string being prepended to negative numbers.
* @category Affixes
* @stable ICU 2.0
*/
public synchronized String getNegativePrefix() {
return formatter.getAffixImpl(true, true);
}
/**
* Affixes: Sets the string to prepend to negative numbers. For example, if you
* set the value "#", then the number -123 will be formatted as "#123" in the locale
* en-US (overriding the implicit default '-' in the pattern).
*
*
Using this method overrides the affix specified via the pattern, and unlike the pattern, the
* string given to this method will be interpreted literally WITHOUT locale symbol substitutions.
*
* @param prefix The literal string to prepend to negative numbers.
* @category Affixes
* @stable ICU 2.0
*/
public synchronized void setNegativePrefix(String prefix) {
if (prefix == null) {
throw new NullPointerException();
}
properties.setNegativePrefix(prefix);
refreshFormatter();
}
/**
* Affixes: Gets the positive suffix string currently being used to format
* numbers.
*
*
If the affix was specified via the pattern, the string returned by this method will have
* locale symbols substituted in place of special characters according to the LDML specification.
* If the affix was specified via {@link #setPositiveSuffix}, the string will be returned
* literally.
*
* @return The string being appended to positive numbers.
* @category Affixes
* @stable ICU 2.0
*/
public synchronized String getPositiveSuffix() {
return formatter.getAffixImpl(false, false);
}
/**
* Affixes: Sets the string to append to positive numbers. For example, if you
* set the value "#", then the number 123 will be formatted as "123#" in the locale
* en-US.
*
*
Using this method overrides the affix specified via the pattern, and unlike the pattern, the
* string given to this method will be interpreted literally WITHOUT locale symbol substitutions.
*
* @param suffix The literal string to append to positive numbers.
* @category Affixes
* @stable ICU 2.0
*/
public synchronized void setPositiveSuffix(String suffix) {
if (suffix == null) {
throw new NullPointerException();
}
properties.setPositiveSuffix(suffix);
refreshFormatter();
}
/**
* Affixes: Gets the negative suffix string currently being used to format
* numbers.
*
*
If the affix was specified via the pattern, the string returned by this method will have
* locale symbols substituted in place of special characters according to the LDML specification.
* If the affix was specified via {@link #setNegativeSuffix}, the string will be returned
* literally.
*
* @return The string being appended to negative numbers.
* @category Affixes
* @stable ICU 2.0
*/
public synchronized String getNegativeSuffix() {
return formatter.getAffixImpl(false, true);
}
/**
* Affixes: Sets the string to append to negative numbers. For example, if you
* set the value "#", then the number 123 will be formatted as "123#" in the locale
* en-US.
*
*
Using this method overrides the affix specified via the pattern, and unlike the pattern, the
* string given to this method will be interpreted literally WITHOUT locale symbol substitutions.
*
* @param suffix The literal string to append to negative numbers.
* @category Affixes
* @stable ICU 2.0
*/
public synchronized void setNegativeSuffix(String suffix) {
if (suffix == null) {
throw new NullPointerException();
}
properties.setNegativeSuffix(suffix);
refreshFormatter();
}
/**
* {@icu} Returns whether the sign is being shown on positive numbers.
*
* @return Whether the sign is shown on positive numbers and zero.
* @see #setSignAlwaysShown
* @category Affixes
* @stable ICU 64
*/
public synchronized boolean isSignAlwaysShown() {
// This is not in the exported properties
return properties.getSignAlwaysShown();
}
/**
* Sets whether to always shown the plus sign ('+' in en) on positive numbers. The rules
* in UTS #35 section 3.2.1 will be followed to ensure a locale-aware placement of the sign.
*
*
More specifically, the following strategy will be used to place the plus sign:
*
*
* - Patterns without a negative subpattern: The locale's plus sign will be prepended
* to the positive prefix.
*
- Patterns with a negative subpattern without a '-' sign (e.g., accounting): The
* locale's plus sign will be prepended to the positive prefix, as in case 1.
*
- Patterns with a negative subpattern that has a '-' sign: The locale's plus sign
* will substitute the '-' in the negative subpattern. The positive subpattern will be
* unused.
*
*
* This method is designed to be used instead of applying a pattern containing an
* explicit plus sign, such as "+0;-0". The behavior when combining this method with explicit plus
* signs in the pattern is undefined.
*
* @param value true to always show a sign; false to hide the sign on positive numbers and zero.
* @category Affixes
* @stable ICU 64
*/
public synchronized void setSignAlwaysShown(boolean value) {
properties.setSignAlwaysShown(value);
refreshFormatter();
}
/**
* Returns the multiplier being applied to numbers before they are formatted.
*
* @see #setMultiplier
* @category Multipliers
* @stable ICU 2.0
*/
public synchronized int getMultiplier() {
if (properties.getMultiplier() != null) {
return properties.getMultiplier().intValue();
} else {
return (int) Math.pow(10, properties.getMagnitudeMultiplier());
}
}
/**
* Sets a number that will be used to multiply all numbers prior to formatting. For example, when
* formatting percents, a multiplier of 100 can be used.
*
* If a percent or permille sign is specified in the pattern, the multiplier is automatically
* set to 100 or 1000, respectively.
*
*
If the number specified here is a power of 10, a more efficient code path will be used.
*
* @param multiplier The number by which all numbers passed to {@link #format} will be multiplied.
* @throws IllegalArgumentException If the given multiplier is zero.
* @throws ArithmeticException when inverting multiplier produces a non-terminating decimal result
* in conjunction with MathContext of unlimited precision.
* @category Multipliers
* @stable ICU 2.0
*/
public synchronized void setMultiplier(int multiplier) {
if (multiplier == 0) {
throw new IllegalArgumentException("Multiplier must be nonzero.");
}
// Try to convert to a magnitude multiplier first
int delta = 0;
int value = multiplier;
while (value != 1) {
delta++;
int temp = value / 10;
if (temp * 10 != value) {
delta = -1;
break;
}
value = temp;
}
if (delta != -1) {
properties.setMagnitudeMultiplier(delta);
properties.setMultiplier(null);
} else {
properties.setMagnitudeMultiplier(0);
properties.setMultiplier(java.math.BigDecimal.valueOf(multiplier));
}
refreshFormatter();
}
/**
* {@icu} Returns the increment to which numbers are being rounded.
*
* @see #setRoundingIncrement
* @category Rounding
* @stable ICU 2.0
*/
public synchronized java.math.BigDecimal getRoundingIncrement() {
return exportedProperties.getRoundingIncrement();
}
/**
* {@icu} Rounding and Digit Limits: Sets an increment, or interval, to which
* numbers are rounded. For example, a rounding increment of 0.05 will cause the number 1.23 to be
* rounded to 1.25 in the default rounding mode.
*
*
The rounding increment can be specified via the pattern string: for example, the pattern
* "#,##0.05" encodes a rounding increment of 0.05.
*
*
The rounding increment is applied after any multipliers might take effect; for
* example, in scientific notation or when {@link #setMultiplier} is used.
*
*
See {@link #setMaximumFractionDigits} and {@link #setMaximumSignificantDigits} for two other
* ways of specifying rounding strategies.
*
* @param increment The increment to which numbers are to be rounded.
* @see #setRoundingMode
* @see #setMaximumFractionDigits
* @see #setMaximumSignificantDigits
* @category Rounding
* @stable ICU 2.0
*/
public synchronized void setRoundingIncrement(java.math.BigDecimal increment) {
// Backwards compatibility: ignore rounding increment if zero,
// and instead set maximum fraction digits.
if (increment != null && increment.compareTo(java.math.BigDecimal.ZERO) == 0) {
properties.setMaximumFractionDigits(Integer.MAX_VALUE);
return;
}
properties.setRoundingIncrement(increment);
refreshFormatter();
}
/**
* {@icu} Rounding and Digit Limits: Overload of {@link
* #setRoundingIncrement(java.math.BigDecimal)}.
*
* @param increment The increment to which numbers are to be rounded.
* @see #setRoundingIncrement
* @category Rounding
* @stable ICU 3.6
*/
public synchronized void setRoundingIncrement(BigDecimal increment) {
java.math.BigDecimal javaBigDecimal = (increment == null) ? null : increment.toBigDecimal();
setRoundingIncrement(javaBigDecimal);
}
/**
* {@icu} Rounding and Digit Limits: Overload of {@link
* #setRoundingIncrement(java.math.BigDecimal)}.
*
* @param increment The increment to which numbers are to be rounded.
* @see #setRoundingIncrement
* @category Rounding
* @stable ICU 2.0
*/
public synchronized void setRoundingIncrement(double increment) {
if (increment == 0) {
setRoundingIncrement((java.math.BigDecimal) null);
} else {
// ICU-20425: Since doubles have no concept of trailing zeros, we should strip
// trailing zeros from the BigDecimal.
java.math.BigDecimal javaBigDecimal = java.math.BigDecimal.valueOf(increment)
.stripTrailingZeros();
setRoundingIncrement(javaBigDecimal);
}
}
/**
* Returns the rounding mode being used to round numbers.
*
* @see #setRoundingMode
* @category Rounding
* @stable ICU 2.0
*/
@Override
public synchronized int getRoundingMode() {
RoundingMode mode = exportedProperties.getRoundingMode();
return (mode == null) ? 0 : mode.ordinal();
}
/**
* Rounding and Digit Limits: Sets the {@link RoundingMode} used to round
* numbers. The default rounding mode is HALF_EVEN, which rounds decimals to their closest whole
* number, and rounds to the closest even number if at the midpoint.
*
*
For more detail on rounding modes, see the ICU
* User Guide.
*
*
For backwards compatibility, the rounding mode is specified as an int argument, which can be
* from either the constants in {@link BigDecimal} or the ordinal value of {@link RoundingMode}.
* The following two calls are functionally equivalent.
*
*
* df.setRoundingMode(BigDecimal.ROUND_CEILING);
* df.setRoundingMode(RoundingMode.CEILING.ordinal());
*
*
* @param roundingMode The integer constant rounding mode to use when formatting numbers.
* @category Rounding
* @stable ICU 2.0
*/
@Override
public synchronized void setRoundingMode(int roundingMode) {
properties.setRoundingMode(RoundingMode.valueOf(roundingMode));
refreshFormatter();
}
/**
* {@icu} Returns the {@link java.math.MathContext} being used to round numbers.
*
* @see #setMathContext
* @category Rounding
* @stable ICU 4.2
*/
public synchronized java.math.MathContext getMathContext() {
java.math.MathContext mathContext = exportedProperties.getMathContext();
assert mathContext != null;
return mathContext;
}
/**
* {@icu} Rounding and Digit Limits: Sets the {@link java.math.MathContext} used
* to round numbers. A "math context" encodes both a rounding mode and a number of significant
* digits. Most users should call {@link #setRoundingMode} and/or {@link
* #setMaximumSignificantDigits} instead of this method.
*
* When formatting, since no division is ever performed, the default MathContext is unlimited
* significant digits. However, when division occurs during parsing to correct for percentages and
* multipliers, a MathContext of 34 digits, the IEEE 754R Decimal128 standard, is used by default.
* If you require more than 34 digits when parsing, you can set a custom MathContext using this
* method.
*
* @param mathContext The MathContext to use when rounding numbers.
* @throws ArithmeticException when inverting multiplier produces a non-terminating decimal result
* in conjunction with MathContext of unlimited precision.
* @see java.math.MathContext
* @category Rounding
* @stable ICU 4.2
*/
public synchronized void setMathContext(java.math.MathContext mathContext) {
properties.setMathContext(mathContext);
refreshFormatter();
}
// Remember the ICU math context form in order to be able to return it from the API.
// NOTE: This value is not serialized. (should it be?)
private transient int icuMathContextForm = MathContext.PLAIN;
/**
* {@icu} Returns the {@link com.ibm.icu.math.MathContext} being used to round numbers.
*
* @see #setMathContext
* @category Rounding
* @stable ICU 4.2
*/
public synchronized MathContext getMathContextICU() {
java.math.MathContext mathContext = getMathContext();
return new MathContext(
mathContext.getPrecision(),
icuMathContextForm,
false,
mathContext.getRoundingMode().ordinal());
}
/**
* {@icu} Rounding and Digit Limits: Overload of {@link #setMathContext} for
* {@link com.ibm.icu.math.MathContext}.
*
* @param mathContextICU The MathContext to use when rounding numbers.
* @throws ArithmeticException when inverting multiplier produces a non-terminating decimal result
* in conjunction with MathContext of unlimited precision.
* @see #setMathContext(java.math.MathContext)
* @category Rounding
* @stable ICU 4.2
*/
public synchronized void setMathContextICU(MathContext mathContextICU) {
icuMathContextForm = mathContextICU.getForm();
java.math.MathContext mathContext;
if (mathContextICU.getLostDigits()) {
// The getLostDigits() feature in ICU MathContext means "throw an ArithmeticException if
// rounding causes digits to be lost". That feature is called RoundingMode.UNNECESSARY in
// Java MathContext.
mathContext = new java.math.MathContext(mathContextICU.getDigits(), RoundingMode.UNNECESSARY);
} else {
mathContext =
new java.math.MathContext(
mathContextICU.getDigits(), RoundingMode.valueOf(mathContextICU.getRoundingMode()));
}
setMathContext(mathContext);
}
/**
* Returns the effective minimum number of digits before the decimal separator.
*
* @see #setMinimumIntegerDigits
* @category Rounding
* @stable ICU 2.0
*/
@Override
public synchronized int getMinimumIntegerDigits() {
return exportedProperties.getMinimumIntegerDigits();
}
/**
* Rounding and Digit Limits: Sets the minimum number of digits to display before
* the decimal separator. If the number has fewer than this many digits, the number is padded with
* zeros.
*
*
For example, if minimum integer digits is 3, the number 12.3 will be printed as "001.23".
*
*
Minimum integer and minimum and maximum fraction digits can be specified via the pattern
* string. For example, "#,#00.00#" has 2 minimum integer digits, 2 minimum fraction digits, and 3
* maximum fraction digits. Note that it is not possible to specify maximum integer digits in the
* pattern except in scientific notation.
*
*
If minimum and maximum integer, fraction, or significant digits conflict with each other,
* the most recently specified value is used. For example, if there is a formatter with minInt=5,
* and then you set maxInt=3, then minInt will be changed to 3.
*
* @param value The minimum number of digits before the decimal separator.
* @category Rounding
* @stable ICU 2.0
*/
@Override
public synchronized void setMinimumIntegerDigits(int value) {
// For backwards compatibility, conflicting min/max need to keep the most recent setting.
int max = properties.getMaximumIntegerDigits();
if (max >= 0 && max < value) {
properties.setMaximumIntegerDigits(value);
}
properties.setMinimumIntegerDigits(value);
refreshFormatter();
}
/**
* Returns the effective maximum number of digits before the decimal separator.
*
* @see #setMaximumIntegerDigits
* @category Rounding
* @stable ICU 2.0
*/
@Override
public synchronized int getMaximumIntegerDigits() {
return exportedProperties.getMaximumIntegerDigits();
}
/**
* Rounding and Digit Limits: Sets the maximum number of digits to display before
* the decimal separator. If the number has more than this many digits, the number is truncated.
*
*
For example, if maximum integer digits is 3, the number 12345 will be printed as "345".
*
*
Minimum integer and minimum and maximum fraction digits can be specified via the pattern
* string. For example, "#,#00.00#" has 2 minimum integer digits, 2 minimum fraction digits, and 3
* maximum fraction digits. Note that it is not possible to specify maximum integer digits in the
* pattern except in scientific notation.
*
*
If minimum and maximum integer, fraction, or significant digits conflict with each other,
* the most recently specified value is used. For example, if there is a formatter with minInt=5,
* and then you set maxInt=3, then minInt will be changed to 3.
*
* @param value The maximum number of digits before the decimal separator.
* @category Rounding
* @stable ICU 2.0
*/
@Override
public synchronized void setMaximumIntegerDigits(int value) {
int min = properties.getMinimumIntegerDigits();
if (min >= 0 && min > value) {
properties.setMinimumIntegerDigits(value);
}
properties.setMaximumIntegerDigits(value);
refreshFormatter();
}
/**
* Returns the effective minimum number of integer digits after the decimal separator.
*
* @see #setMaximumIntegerDigits
* @category Rounding
* @stable ICU 2.0
*/
@Override
public synchronized int getMinimumFractionDigits() {
return exportedProperties.getMinimumFractionDigits();
}
/**
* Rounding and Digit Limits: Sets the minimum number of digits to display after
* the decimal separator. If the number has fewer than this many digits, the number is padded with
* zeros.
*
*
For example, if minimum fraction digits is 2, the number 123.4 will be printed as "123.40".
*
*
Minimum integer and minimum and maximum fraction digits can be specified via the pattern
* string. For example, "#,#00.00#" has 2 minimum integer digits, 2 minimum fraction digits, and 3
* maximum fraction digits. Note that it is not possible to specify maximum integer digits in the
* pattern except in scientific notation.
*
*
If minimum and maximum integer, fraction, or significant digits conflict with each other,
* the most recently specified value is used. For example, if there is a formatter with minInt=5,
* and then you set maxInt=3, then minInt will be changed to 3.
*
*
See {@link #setRoundingIncrement} and {@link #setMaximumSignificantDigits} for two other
* ways of specifying rounding strategies.
*
* @param value The minimum number of integer digits after the decimal separator.
* @see #setRoundingMode
* @see #setRoundingIncrement
* @see #setMaximumSignificantDigits
* @category Rounding
* @stable ICU 2.0
*/
@Override
public synchronized void setMinimumFractionDigits(int value) {
int max = properties.getMaximumFractionDigits();
if (max >= 0 && max < value) {
properties.setMaximumFractionDigits(value);
}
properties.setMinimumFractionDigits(value);
refreshFormatter();
}
/**
* Returns the effective maximum number of integer digits after the decimal separator.
*
* @see #setMaximumIntegerDigits
* @category Rounding
* @stable ICU 2.0
*/
@Override
public synchronized int getMaximumFractionDigits() {
return exportedProperties.getMaximumFractionDigits();
}
/**
* Rounding and Digit Limits: Sets the maximum number of digits to display after
* the decimal separator. If the number has more than this many digits, the number is rounded
* according to the rounding mode.
*
*
For example, if maximum fraction digits is 2, the number 123.456 will be printed as
* "123.46".
*
*
Minimum integer and minimum and maximum fraction digits can be specified via the pattern
* string. For example, "#,#00.00#" has 2 minimum integer digits, 2 minimum fraction digits, and 3
* maximum fraction digits. Note that it is not possible to specify maximum integer digits in the
* pattern except in scientific notation.
*
*
If minimum and maximum integer, fraction, or significant digits conflict with each other,
* the most recently specified value is used. For example, if there is a formatter with minInt=5,
* and then you set maxInt=3, then minInt will be changed to 3.
*
* @param value The maximum number of integer digits after the decimal separator.
* @see #setRoundingMode
* @category Rounding
* @stable ICU 2.0
*/
@Override
public synchronized void setMaximumFractionDigits(int value) {
int min = properties.getMinimumFractionDigits();
if (min >= 0 && min > value) {
properties.setMinimumFractionDigits(value);
}
properties.setMaximumFractionDigits(value);
refreshFormatter();
}
/**
* {@icu} Returns whether significant digits are being used in rounding.
*
* @see #setSignificantDigitsUsed
* @category Rounding
* @stable ICU 3.0
*/
public synchronized boolean areSignificantDigitsUsed() {
return properties.getMinimumSignificantDigits() != -1
|| properties.getMaximumSignificantDigits() != -1;
}
/**
* {@icu} Rounding and Digit Limits: Sets whether significant digits are to be
* used in rounding.
*
*
Calling df.setSignificantDigitsUsed(true)
is functionally equivalent to:
*
*
* df.setMinimumSignificantDigits(1);
* df.setMaximumSignificantDigits(6);
*
*
* @param useSignificantDigits true to enable significant digit rounding; false to disable it.
* @category Rounding
* @stable ICU 3.0
*/
public synchronized void setSignificantDigitsUsed(boolean useSignificantDigits) {
int oldMinSig = properties.getMinimumSignificantDigits();
int oldMaxSig = properties.getMaximumSignificantDigits();
// These are the default values from the old implementation.
if (useSignificantDigits) {
if (oldMinSig != -1 || oldMaxSig != -1) {
return;
}
} else {
if (oldMinSig == -1 && oldMaxSig == -1) {
return;
}
}
int minSig = useSignificantDigits ? 1 : -1;
int maxSig = useSignificantDigits ? 6 : -1;
properties.setMinimumSignificantDigits(minSig);
properties.setMaximumSignificantDigits(maxSig);
refreshFormatter();
}
/**
* {@icu} Returns the effective minimum number of significant digits displayed.
*
* @see #setMinimumSignificantDigits
* @category Rounding
* @stable ICU 3.0
*/
public synchronized int getMinimumSignificantDigits() {
return exportedProperties.getMinimumSignificantDigits();
}
/**
* {@icu} Rounding and Digit Limits: Sets the minimum number of significant
* digits to be displayed. If the number of significant digits is less than this value, the number
* will be padded with zeros as necessary.
*
* For example, if minimum significant digits is 3 and the number is 1.2, the number will be
* printed as "1.20".
*
*
If minimum and maximum integer, fraction, or significant digits conflict with each other,
* the most recently specified value is used. For example, if there is a formatter with minInt=5,
* and then you set maxInt=3, then minInt will be changed to 3.
*
* @param value The minimum number of significant digits to display.
* @category Rounding
* @stable ICU 3.0
*/
public synchronized void setMinimumSignificantDigits(int value) {
int max = properties.getMaximumSignificantDigits();
if (max >= 0 && max < value) {
properties.setMaximumSignificantDigits(value);
}
properties.setMinimumSignificantDigits(value);
refreshFormatter();
}
/**
* {@icu} Returns the effective maximum number of significant digits displayed.
*
* @see #setMaximumSignificantDigits
* @category Rounding
* @stable ICU 3.0
*/
public synchronized int getMaximumSignificantDigits() {
return exportedProperties.getMaximumSignificantDigits();
}
/**
* {@icu} Rounding and Digit Limits: Sets the maximum number of significant
* digits to be displayed. If the number of significant digits in the number exceeds this value,
* the number will be rounded according to the current rounding mode.
*
*
For example, if maximum significant digits is 3 and the number is 12345, the number will be
* printed as "12300".
*
*
If minimum and maximum integer, fraction, or significant digits conflict with each other,
* the most recently specified value is used. For example, if there is a formatter with minInt=5,
* and then you set maxInt=3, then minInt will be changed to 3.
*
*
See {@link #setRoundingIncrement} and {@link #setMaximumFractionDigits} for two other ways
* of specifying rounding strategies.
*
* @param value The maximum number of significant digits to display.
* @see #setRoundingMode
* @see #setRoundingIncrement
* @see #setMaximumFractionDigits
* @category Rounding
* @stable ICU 3.0
*/
public synchronized void setMaximumSignificantDigits(int value) {
int min = properties.getMinimumSignificantDigits();
if (min >= 0 && min > value) {
properties.setMinimumSignificantDigits(value);
}
properties.setMaximumSignificantDigits(value);
refreshFormatter();
}
/**
* Returns the minimum number of characters in formatted output.
*
* @see #setFormatWidth
* @category Padding
* @stable ICU 2.0
*/
public synchronized int getFormatWidth() {
return properties.getFormatWidth();
}
/**
* Padding: Sets the minimum width of the string output by the formatting
* pipeline. For example, if padding is enabled and paddingWidth is set to 6, formatting the
* number "3.14159" with the pattern "0.00" will result in "··3.14" if '·' is your padding string.
*
*
If the number is longer than your padding width, the number will display as if no padding
* width had been specified, which may result in strings longer than the padding width.
*
*
Padding can be specified in the pattern string using the '*' symbol. For example, the format
* "*x######0" has a format width of 7 and a pad character of 'x'.
*
*
Padding is currently counted in UTF-16 code units; see ticket #13034 for more information.
*
* @param width The minimum number of characters in the output.
* @see #setPadCharacter
* @see #setPadPosition
* @category Padding
* @stable ICU 2.0
*/
public synchronized void setFormatWidth(int width) {
properties.setFormatWidth(width);
refreshFormatter();
}
/**
* {@icu} Returns the character used for padding.
*
* @see #setPadCharacter
* @category Padding
* @stable ICU 2.0
*/
public synchronized char getPadCharacter() {
CharSequence paddingString = properties.getPadString();
if (paddingString == null) {
return Padder.FALLBACK_PADDING_STRING.charAt(0);
} else {
return paddingString.charAt(0);
}
}
/**
* {@icu} Padding: Sets the character used to pad numbers that are narrower than
* the width specified in {@link #setFormatWidth}.
*
*
In the pattern string, the padding character is the token that follows '*' before or after
* the prefix or suffix.
*
* @param padChar The character used for padding.
* @see #setFormatWidth
* @category Padding
* @stable ICU 2.0
*/
public synchronized void setPadCharacter(char padChar) {
properties.setPadString(Character.toString(padChar));
refreshFormatter();
}
/**
* {@icu} Returns the position used for padding.
*
* @see #setPadPosition
* @category Padding
* @stable ICU 2.0
*/
public synchronized int getPadPosition() {
PadPosition loc = properties.getPadPosition();
return (loc == null) ? PAD_BEFORE_PREFIX : loc.toOld();
}
/**
* {@icu} Padding: Sets the position where to insert the pad character when
* narrower than the width specified in {@link #setFormatWidth}. For example, consider the pattern
* "P123S" with padding width 8 and padding char "*". The four positions are:
*
*
* - {@link DecimalFormat#PAD_BEFORE_PREFIX} ⇒ "***P123S"
*
- {@link DecimalFormat#PAD_AFTER_PREFIX} ⇒ "P***123S"
*
- {@link DecimalFormat#PAD_BEFORE_SUFFIX} ⇒ "P123***S"
*
- {@link DecimalFormat#PAD_AFTER_SUFFIX} ⇒ "P123S***"
*
*
* @param padPos The position used for padding.
* @see #setFormatWidth
* @category Padding
* @stable ICU 2.0
*/
public synchronized void setPadPosition(int padPos) {
properties.setPadPosition(PadPosition.fromOld(padPos));
refreshFormatter();
}
/**
* {@icu} Returns whether scientific (exponential) notation is enabled on this formatter.
*
* @see #setScientificNotation
* @category ScientificNotation
* @stable ICU 2.0
*/
public synchronized boolean isScientificNotation() {
return properties.getMinimumExponentDigits() != -1;
}
/**
* {@icu} Scientific Notation: Sets whether this formatter should print in
* scientific (exponential) notation. For example, if scientific notation is enabled, the number
* 123000 will be printed as "1.23E5" in locale en-US. A locale-specific symbol is used
* as the exponent separator.
*
* Calling df.setScientificNotation(true)
is functionally equivalent to calling
* df.setMinimumExponentDigits(1)
.
*
* @param useScientific true to enable scientific notation; false to disable it.
* @see #setMinimumExponentDigits
* @category ScientificNotation
* @stable ICU 2.0
*/
public synchronized void setScientificNotation(boolean useScientific) {
if (useScientific) {
properties.setMinimumExponentDigits(1);
} else {
properties.setMinimumExponentDigits(-1);
}
refreshFormatter();
}
/**
* {@icu} Returns the minimum number of digits printed in the exponent in scientific notation.
*
* @see #setMinimumExponentDigits
* @category ScientificNotation
* @stable ICU 2.0
*/
public synchronized byte getMinimumExponentDigits() {
return (byte) properties.getMinimumExponentDigits();
}
/**
* {@icu} Scientific Notation: Sets the minimum number of digits to be printed in
* the exponent. For example, if minimum exponent digits is 3, the number 123000 will be printed
* as "1.23E005".
*
*
This setting corresponds to the number of zeros after the 'E' in a pattern string such as
* "0.00E000".
*
* @param minExpDig The minimum number of digits in the exponent.
* @category ScientificNotation
* @stable ICU 2.0
*/
public synchronized void setMinimumExponentDigits(byte minExpDig) {
properties.setMinimumExponentDigits(minExpDig);
refreshFormatter();
}
/**
* {@icu} Returns whether the sign (plus or minus) is always printed in scientific notation.
*
* @see #setExponentSignAlwaysShown
* @category ScientificNotation
* @stable ICU 2.0
*/
public synchronized boolean isExponentSignAlwaysShown() {
return properties.getExponentSignAlwaysShown();
}
/**
* {@icu} Scientific Notation: Sets whether the sign (plus or minus) is always to
* be shown in the exponent in scientific notation. For example, if this setting is enabled, the
* number 123000 will be printed as "1.23E+5" in locale en-US. The number 0.0000123 will
* always be printed as "1.23E-5" in locale en-US whether or not this setting is enabled.
*
*
This setting corresponds to the '+' in a pattern such as "0.00E+0".
*
* @param expSignAlways true to always shown the sign in the exponent; false to show it for
* negatives but not positives.
* @category ScientificNotation
* @stable ICU 2.0
*/
public synchronized void setExponentSignAlwaysShown(boolean expSignAlways) {
properties.setExponentSignAlwaysShown(expSignAlways);
refreshFormatter();
}
/**
* Returns whether or not grouping separators are being printed in the output.
*
* @see #setGroupingUsed
* @category Separators
* @stable ICU 2.0
*/
@Override
public synchronized boolean isGroupingUsed() {
return properties.getGroupingUsed();
}
/**
* Grouping: Sets whether grouping is to be used when formatting numbers.
* Grouping means whether the thousands, millions, billions, and larger powers of ten should be
* separated by a grouping separator (a comma in en-US).
*
*
For example, if grouping is enabled, 12345 will be printed as "12,345" in en-US. If
* grouping were disabled, it would instead be printed as simply "12345".
*
* @param enabled true to enable grouping separators; false to disable them.
* @see #setGroupingSize
* @see #setSecondaryGroupingSize
* @category Separators
* @stable ICU 2.0
*/
@Override
public synchronized void setGroupingUsed(boolean enabled) {
properties.setGroupingUsed(enabled);
refreshFormatter();
}
/**
* Returns the primary grouping size in use.
*
* @see #setGroupingSize
* @category Separators
* @stable ICU 2.0
*/
public synchronized int getGroupingSize() {
if (properties.getGroupingSize() < 0) {
return 0;
}
return properties.getGroupingSize();
}
/**
* Grouping: Sets the primary grouping size (distance between grouping
* separators) used when formatting large numbers. For most locales, this defaults to 3: the
* number of digits between the ones and thousands place, between thousands and millions, and so
* forth.
*
*
For example, with a grouping size of 3, the number 1234567 will be formatted as "1,234,567".
*
*
Grouping size can also be specified in the pattern: for example, "#,##0" corresponds to a
* grouping size of 3.
*
* @param width The grouping size to use.
* @see #setSecondaryGroupingSize
* @category Separators
* @stable ICU 2.0
*/
public synchronized void setGroupingSize(int width) {
properties.setGroupingSize(width);
refreshFormatter();
}
/**
* {@icu} Returns the secondary grouping size in use.
*
* @see #setSecondaryGroupingSize
* @category Separators
* @stable ICU 2.0
*/
public synchronized int getSecondaryGroupingSize() {
int grouping2 = properties.getSecondaryGroupingSize();
if (grouping2 < 0) {
return 0;
}
return grouping2;
}
/**
* {@icu} Grouping: Sets the secondary grouping size (distance between grouping
* separators after the first separator) used when formatting large numbers. In many south Asian
* locales, this is set to 2.
*
*
For example, with primary grouping size 3 and secondary grouping size 2, the number 1234567
* will be formatted as "12,34,567".
*
*
Grouping size can also be specified in the pattern: for example, "#,##,##0" corresponds to a
* primary grouping size of 3 and a secondary grouping size of 2.
*
* @param width The secondary grouping size to use.
* @see #setGroupingSize
* @category Separators
* @stable ICU 2.0
*/
public synchronized void setSecondaryGroupingSize(int width) {
properties.setSecondaryGroupingSize(width);
refreshFormatter();
}
/**
* {@icu} Returns the minimum number of digits before grouping is triggered.
*
* @see #setMinimumGroupingDigits
* @category Separators
* @stable ICU 64
*/
public synchronized int getMinimumGroupingDigits() {
if (properties.getMinimumGroupingDigits() > 0) {
return properties.getMinimumGroupingDigits();
}
return 1;
}
/**
* {@icu} Sets the minimum number of digits that must be before the first grouping separator in
* order for the grouping separator to be printed. For example, if minimum grouping digits is set
* to 2, in en-US, 1234 will be printed as "1234" and 12345 will be printed as "12,345".
*
* Set the value to:
*
* - 1 to turn off minimum grouping digits.
* - MINIMUM_GROUPING_DIGITS_AUTO to display grouping using the default
* strategy for all locales.
* - MINIMUM_GROUPING_DIGITS_MIN2 to display grouping using locale defaults,
* except do not show grouping on values smaller than 10000 (such that there is a minimum of
* two digits before the first separator).
*
*
* @param number The minimum number of digits before grouping is triggered.
* @category Separators
* @stable ICU 64
*/
public synchronized void setMinimumGroupingDigits(int number) {
properties.setMinimumGroupingDigits(number);
refreshFormatter();
}
/**
* {@icu} Constant for {@link #setMinimumGroupingDigits(int)} to specify display
* grouping using the default strategy for all locales.
*
* @see #setMinimumGroupingDigits(int)
* @see #MINIMUM_GROUPING_DIGITS_MIN2
* @category Separators
* @stable ICU 68
*/
public static final int MINIMUM_GROUPING_DIGITS_AUTO = -2;
/**
* {@icu} Constant for {@link #setMinimumGroupingDigits(int)} to specify display
* grouping using locale defaults, except do not show grouping on values smaller than
* 10000 (such that there is a minimum of two digits before the first separator).
*
* @see #setMinimumGroupingDigits(int)
* @see #MINIMUM_GROUPING_DIGITS_AUTO
* @category Separators
* @stable ICU 68
*/
public static final int MINIMUM_GROUPING_DIGITS_MIN2 = -3;
/**
* Returns whether the decimal separator is shown on integers.
*
* @see #setDecimalSeparatorAlwaysShown
* @category Separators
* @stable ICU 2.0
*/
public synchronized boolean isDecimalSeparatorAlwaysShown() {
return properties.getDecimalSeparatorAlwaysShown();
}
/**
* Separators: Sets whether the decimal separator (a period in en-US) is
* shown on integers. For example, if this setting is turned on, formatting 123 will result in
* "123." with the decimal separator.
*
* This setting can be specified in the pattern for integer formats: "#,##0." is an example.
*
* @param value true to always show the decimal separator; false to show it only when there is a
* fraction part of the number.
* @category Separators
* @stable ICU 2.0
*/
public synchronized void setDecimalSeparatorAlwaysShown(boolean value) {
properties.setDecimalSeparatorAlwaysShown(value);
refreshFormatter();
}
/**
* Returns the currency used to display currency amounts. May be null.
*
* @see #setCurrency
* @see DecimalFormatSymbols#getCurrency
* @category Currency
* @stable ICU 2.6
*/
@Override
public synchronized Currency getCurrency() {
return exportedProperties.getCurrency();
}
/**
* Sets the currency to be used when formatting numbers. The effect is twofold:
*
*
* - Substitutions for currency symbols in the pattern string will use this currency
*
- The rounding mode will obey the rules for this currency (see {@link #setCurrencyUsage})
*
*
* Important: Displaying the currency in the output requires that the patter
* associated with this formatter contains a currency symbol '¤'. This will be the case if the
* instance was created via {@link #getCurrencyInstance} or one of its friends.
*
* @param currency The currency to use.
* @category Currency
* @stable ICU 2.2
*/
@Override
public synchronized void setCurrency(Currency currency) {
properties.setCurrency(currency);
if (currency != null) {
symbols.setCurrency(currency);
}
refreshFormatter();
}
/**
* {@icu} Returns the strategy for rounding currency amounts.
*
* @see #setCurrencyUsage
* @category Currency
* @stable ICU 54
*/
public synchronized CurrencyUsage getCurrencyUsage() {
// CurrencyUsage is not exported, so we have to get it from the input property bag.
// TODO: Should we export CurrencyUsage instead?
CurrencyUsage usage = properties.getCurrencyUsage();
if (usage == null) {
usage = CurrencyUsage.STANDARD;
}
return usage;
}
/**
* {@icu} Sets the currency-dependent strategy to use when rounding numbers. There are two
* strategies:
*
*
* - STANDARD: When the amount displayed is intended for banking statements or electronic
* transfer.
*
- CASH: When the amount displayed is intended to be representable in physical currency,
* like at a cash register.
*
*
* CASH mode is relevant in currencies that do not have tender down to the penny. For more
* information on the two rounding strategies, see UTS
* #35. If omitted, the strategy defaults to STANDARD. To override currency rounding
* altogether, use {@link #setMinimumFractionDigits} and {@link #setMaximumFractionDigits} or
* {@link #setRoundingIncrement}.
*
* @param usage The strategy to use when rounding in the current currency.
* @category Currency
* @stable ICU 54
*/
public synchronized void setCurrencyUsage(CurrencyUsage usage) {
properties.setCurrencyUsage(usage);
refreshFormatter();
}
/**
* {@icu} Returns the current instance of CurrencyPluralInfo.
*
* @see #setCurrencyPluralInfo
* @category Currency
* @stable ICU 4.2
*/
public synchronized CurrencyPluralInfo getCurrencyPluralInfo() {
// CurrencyPluralInfo also is not exported.
return properties.getCurrencyPluralInfo();
}
/**
* {@icu} Sets a custom instance of CurrencyPluralInfo. CurrencyPluralInfo generates pattern
* strings for printing currency long names.
*
* Most users should not call this method directly. You should instead create
* your formatter via NumberFormat.getInstance(NumberFormat.PLURALCURRENCYSTYLE)
.
*
* @param newInfo The CurrencyPluralInfo to use when printing currency long names.
* @category Currency
* @stable ICU 4.2
*/
public synchronized void setCurrencyPluralInfo(CurrencyPluralInfo newInfo) {
properties.setCurrencyPluralInfo(newInfo);
refreshFormatter();
}
/**
* Returns whether {@link #parse} will always return a BigDecimal.
*
* @see #setParseBigDecimal
* @category Parsing
* @stable ICU 3.6
*/
public synchronized boolean isParseBigDecimal() {
return properties.getParseToBigDecimal();
}
/**
* Whether to make {@link #parse} prefer returning a {@link com.ibm.icu.math.BigDecimal} when
* possible. For strings corresponding to return values of Infinity, -Infinity, NaN, and -0.0, a
* Double will be returned even if ParseBigDecimal is enabled.
*
* @param value true to cause {@link #parse} to prefer BigDecimal; false to let {@link #parse}
* return additional data types like Long or BigInteger.
* @category Parsing
* @stable ICU 3.6
*/
public synchronized void setParseBigDecimal(boolean value) {
properties.setParseToBigDecimal(value);
refreshFormatter();
}
/**
* Always returns 1000, the default prior to ICU 59.
*
* @category Parsing
* @deprecated Setting max parse digits has no effect since ICU4J 59.
*/
@Deprecated
public int getParseMaxDigits() {
return 1000;
}
/**
* @param maxDigits Prior to ICU 59, the maximum number of digits in the output number after
* exponential notation is applied.
* @category Parsing
* @deprecated Setting max parse digits has no effect since ICU4J 59.
*/
@Deprecated
public void setParseMaxDigits(int maxDigits) {}
/**
* {@inheritDoc}
*
* @category Parsing
* @stable ICU 3.6
*/
@Override
public synchronized boolean isParseStrict() {
return properties.getParseMode() == ParseMode.STRICT;
}
/**
* {@inheritDoc}
*
* @category Parsing
* @stable ICU 3.6
*/
@Override
public synchronized void setParseStrict(boolean parseStrict) {
ParseMode mode = parseStrict ? ParseMode.STRICT : ParseMode.LENIENT;
properties.setParseMode(mode);
refreshFormatter();
}
/**
* Android libcore uses this internal method to set {@link ParseMode#JAVA_COMPATIBILITY}.
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public synchronized void setParseStrictMode(ParseMode parseMode) {
properties.setParseMode(parseMode);
refreshFormatter();
}
/**
* {@inheritDoc}
*
* @see #setParseIntegerOnly
* @category Parsing
* @stable ICU 2.0
*/
@Override
public synchronized boolean isParseIntegerOnly() {
return properties.getParseIntegerOnly();
}
/**
* Parsing: {@inheritDoc}
*
*
This is functionally equivalent to calling {@link #setDecimalPatternMatchRequired} and a
* pattern without a decimal point.
*
* @param parseIntegerOnly true to ignore fractional parts of numbers when parsing; false to
* consume fractional parts.
* @category Parsing
* @stable ICU 2.0
*/
@Override
public synchronized void setParseIntegerOnly(boolean parseIntegerOnly) {
properties.setParseIntegerOnly(parseIntegerOnly);
refreshFormatter();
}
/**
* {@icu} Returns whether the presence of a decimal point must match the pattern.
*
* @see #setDecimalPatternMatchRequired
* @category Parsing
* @stable ICU 54
*/
public synchronized boolean isDecimalPatternMatchRequired() {
return properties.getDecimalPatternMatchRequired();
}
/**
* {@icu} Parsing: This method is used to either require or
* forbid the presence of a decimal point in the string being parsed (disabled by
* default). This feature was designed to be an extra layer of strictness on top of strict
* parsing, although it can be used in either lenient mode or strict mode.
*
*
To require a decimal point, call this method in combination with either a pattern
* containing a decimal point or with {@link #setDecimalSeparatorAlwaysShown}.
*
*
* // Require a decimal point in the string being parsed:
* df.applyPattern("#.");
* df.setDecimalPatternMatchRequired(true);
*
* // Alternatively:
* df.setDecimalSeparatorAlwaysShown(true);
* df.setDecimalPatternMatchRequired(true);
*
*
* To forbid a decimal point, call this method in combination with a pattern containing
* no decimal point. Alternatively, use {@link #setParseIntegerOnly} for the same behavior without
* depending on the contents of the pattern string.
*
*
* // Forbid a decimal point in the string being parsed:
* df.applyPattern("#");
* df.setDecimalPatternMatchRequired(true);
*
*
* @param value true to either require or forbid the decimal point according to the pattern; false
* to disable this feature.
* @see #setParseIntegerOnly
* @category Parsing
* @stable ICU 54
*/
public synchronized void setDecimalPatternMatchRequired(boolean value) {
properties.setDecimalPatternMatchRequired(value);
refreshFormatter();
}
/**
* {@icu} Returns whether to ignore exponents when parsing.
*
* @see #setParseNoExponent
* @category Parsing
* @stable ICU 64
*/
public synchronized boolean isParseNoExponent() {
return properties.getParseNoExponent();
}
/**
* {@icu} Specifies whether to stop parsing when an exponent separator is encountered. For
* example, parses "123E4" to 123 (with parse position 3) instead of 1230000 (with parse position
* 5).
*
* @param value true to prevent exponents from being parsed; false to allow them to be parsed.
* @category Parsing
* @stable ICU 64
*/
public synchronized void setParseNoExponent(boolean value) {
properties.setParseNoExponent(value);
refreshFormatter();
}
/**
* {@icu} Returns whether to force case (uppercase/lowercase) to match when parsing.
*
* @see #setParseNoExponent
* @category Parsing
* @stable ICU 64
*/
public synchronized boolean isParseCaseSensitive() {
return properties.getParseCaseSensitive();
}
/**
* {@icu} Specifies whether parsing should require cases to match in affixes, exponent separators,
* and currency codes. Case mapping is performed for each code point using {@link
* UCharacter#foldCase}.
*
* @param value true to force case (uppercase/lowercase) to match when parsing; false to ignore
* case and perform case folding.
* @category Parsing
* @stable ICU 64
*/
public synchronized void setParseCaseSensitive(boolean value) {
properties.setParseCaseSensitive(value);
refreshFormatter();
}
// TODO(sffc): Uncomment for ICU 60 API proposal.
//
// /**
// * {@icu} Returns the strategy used for choosing between grouping and decimal separators when
// * parsing.
// *
// * @see #setParseGroupingMode
// * @category Parsing
// */
// public synchronized GroupingMode getParseGroupingMode() {
// return properties.getParseGroupingMode();
// }
//
// /**
// * {@icu} Sets the strategy used during parsing when a code point needs to be interpreted as
// * either a decimal separator or a grouping separator.
// *
// * The comma, period, space, and apostrophe have different meanings in different locales. For
// * example, in en-US and most American locales, the period is used as a decimal
// * separator, but in es-PY and most European locales, it is used as a grouping separator.
// *
// * Suppose you are in fr-FR the parser encounters the string "1.234". In fr-FR,
// * the grouping is a space and the decimal is a comma. The grouping mode is a mechanism
// * to let you specify whether to accept the string as 1234 (GroupingMode.DEFAULT) or whether to reject it since the separators
// * don't match (GroupingMode.RESTRICTED).
// *
// * When resolving grouping separators, it is the equivalence class of separators that is considered.
// * For example, a period is seen as equal to a fixed set of other period-like characters.
// *
// * @param groupingMode The strategy to use; either DEFAULT or RESTRICTED.
// * @category Parsing
// */
// public synchronized void setParseGroupingMode(GroupingMode groupingMode) {
// properties.setParseGroupingMode(groupingMode);
// refreshFormatter();
// }
//=====================================================================================//
// UTILITIES //
//=====================================================================================//
/**
* Tests for equality between this formatter and another formatter.
*
*
If two DecimalFormat instances are equal, then they will always produce the same output.
* However, the reverse is not necessarily true: if two DecimalFormat instances always produce the
* same output, they are not necessarily equal.
*
* @stable ICU 2.0
*/
@Override
public synchronized boolean equals(Object obj) {
if (obj == null) return false;
if (obj == this) return true;
if (!(obj instanceof DecimalFormat)) return false;
DecimalFormat other = (DecimalFormat) obj;
return properties.equals(other.properties) && symbols.equals(other.symbols);
}
/**
* {@inheritDoc}
*
* @stable ICU 2.0
*/
@Override
public synchronized int hashCode() {
return properties.hashCode() ^ symbols.hashCode();
}
/**
* Returns the default value of toString() with extra DecimalFormat-specific information appended
* to the end of the string. This extra information is intended for debugging purposes, and the
* format is not guaranteed to be stable.
*
* @stable ICU 2.0
*/
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append(getClass().getName());
result.append("@");
result.append(Integer.toHexString(hashCode()));
result.append(" { symbols@");
result.append(Integer.toHexString(symbols.hashCode()));
synchronized (this) {
properties.toStringBare(result);
}
result.append(" }");
return result.toString();
}
/**
* Serializes this formatter object to a decimal format pattern string. The result of this method
* is guaranteed to be functionally equivalent to the pattern string used to create this
* instance after incorporating values from the setter methods.
*
*
For more information on decimal format pattern strings, see UTS #35.
*
*
Important: Not all properties are capable of being encoded in a pattern
* string. See a list of properties in {@link #applyPattern}.
*
* @return A decimal format pattern string.
* @stable ICU 2.0
*/
public synchronized String toPattern() {
// Pull some properties from exportedProperties and others from properties
// to keep affix patterns intact. In particular, pull rounding properties
// so that CurrencyUsage is reflected properly.
// TODO: Consider putting this logic in PatternString.java instead.
DecimalFormatProperties tprops = new DecimalFormatProperties().copyFrom(properties);
boolean useCurrency = ((tprops.getCurrency() != null)
|| tprops.getCurrencyPluralInfo() != null
|| tprops.getCurrencyUsage() != null
|| tprops.getCurrencyAsDecimal()
|| AffixUtils.hasCurrencySymbols(tprops.getPositivePrefixPattern())
|| AffixUtils.hasCurrencySymbols(tprops.getPositiveSuffixPattern())
|| AffixUtils.hasCurrencySymbols(tprops.getNegativePrefixPattern())
|| AffixUtils.hasCurrencySymbols(tprops.getNegativeSuffixPattern()));
if (useCurrency) {
tprops.setMinimumFractionDigits(exportedProperties.getMinimumFractionDigits());
tprops.setMaximumFractionDigits(exportedProperties.getMaximumFractionDigits());
tprops.setRoundingIncrement(exportedProperties.getRoundingIncrement());
}
return PatternStringUtils.propertiesToPatternString(tprops);
}
/**
* Calls {@link #toPattern} and converts the string to localized notation. For more information on
* localized notation, see {@link #applyLocalizedPattern}. This method is provided for backwards
* compatibility and should not be used in new projects.
*
* @return A decimal format pattern string in localized notation.
* @stable ICU 2.0
*/
public synchronized String toLocalizedPattern() {
String pattern = toPattern();
return PatternStringUtils.convertLocalized(pattern, symbols, true);
}
/**
* Converts this DecimalFormat to a NumberFormatter. Starting in ICU 60,
* NumberFormatter is the recommended way to format numbers.
*
* @return An instance of {@link LocalizedNumberFormatter} with the same behavior as this instance of
* DecimalFormat.
* @see NumberFormatter
* @stable ICU 60
*/
public LocalizedNumberFormatter toNumberFormatter() {
return formatter;
}
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public IFixedDecimal getFixedDecimal(double number) {
return formatter.format(number).getFixedDecimal();
}
/** Rebuilds the formatter object from the property bag. */
void refreshFormatter() {
if (exportedProperties == null) {
// exportedProperties is null only when the formatter is not ready yet.
// The only time when this happens is during legacy deserialization.
return;
}
ULocale locale = this.getLocale(ULocale.ACTUAL_LOCALE);
if (locale == null) {
// Constructor
locale = symbols.getLocale(ULocale.ACTUAL_LOCALE);
}
if (locale == null) {
// Deserialization
locale = symbols.getULocale();
}
assert locale != null;
formatter = NumberFormatter.fromDecimalFormat(properties, symbols, exportedProperties).locale(locale);
// Lazy-initialize the parsers only when we need them.
parser = null;
currencyParser = null;
}
NumberParserImpl getParser() {
if (parser == null) {
parser = NumberParserImpl.createParserFromProperties(properties, symbols, false);
}
return parser;
}
NumberParserImpl getCurrencyParser() {
if (currencyParser == null) {
currencyParser = NumberParserImpl.createParserFromProperties(properties, symbols, true);
}
return currencyParser;
}
/**
* Converts a java.math.BigDecimal to a com.ibm.icu.math.BigDecimal with fallback for numbers
* outside of the range supported by com.ibm.icu.math.BigDecimal.
*
* @param number
* @return
*/
private Number safeConvertBigDecimal(java.math.BigDecimal number) {
try {
return new com.ibm.icu.math.BigDecimal(number);
} catch (NumberFormatException e) {
if (number.signum() > 0 && number.scale() < 0) {
return Double.POSITIVE_INFINITY;
} else if (number.scale() < 0) {
return Double.NEGATIVE_INFINITY;
} else if (number.signum() < 0) {
return -0.0;
} else {
return 0.0;
}
}
}
/**
* Updates the property bag with settings from the given pattern.
*
* @param pattern The pattern string to parse.
* @param ignoreRounding Whether to leave out rounding information (minFrac, maxFrac, and rounding
* increment) when parsing the pattern. This may be desirable if a custom rounding mode, such
* as CurrencyUsage, is to be used instead. One of {@link
* PatternStringParser#IGNORE_ROUNDING_ALWAYS}, {@link PatternStringParser#IGNORE_ROUNDING_IF_CURRENCY},
* or {@link PatternStringParser#IGNORE_ROUNDING_NEVER}.
* @see PatternAndPropertyUtils#parseToExistingProperties
*/
void setPropertiesFromPattern(String pattern, int ignoreRounding) {
if (pattern == null) {
throw new NullPointerException();
}
PatternStringParser.parseToExistingProperties(pattern, properties, ignoreRounding);
}
static void fieldPositionHelper(
DecimalQuantity dq, FormattedStringBuilder string, FieldPosition fieldPosition, int offset) {
// always return first occurrence:
fieldPosition.setBeginIndex(0);
fieldPosition.setEndIndex(0);
dq.populateUFieldPosition(fieldPosition);
boolean found = FormattedValueStringBuilderImpl.nextFieldPosition(string, fieldPosition);;
if (found && offset != 0) {
fieldPosition.setBeginIndex(fieldPosition.getBeginIndex() + offset);
fieldPosition.setEndIndex(fieldPosition.getEndIndex() + offset);
}
}
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public synchronized void setProperties(PropertySetter func) {
func.set(properties);
refreshFormatter();
}
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static interface PropertySetter {
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public void set(DecimalFormatProperties props);
}
/**
* {@icu} Constant for {@link #getPadPosition()} and {@link #setPadPosition(int)} to specify 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;
/**
* {@icu} Constant for {@link #getPadPosition()} and {@link #setPadPosition(int)} to specify 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;
/**
* {@icu} Constant for {@link #getPadPosition()} and {@link #setPadPosition(int)} to specify 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;
/**
* {@icu} Constant for {@link #getPadPosition()} and {@link #setPadPosition(int)} to specify 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;
}