All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ibm.icu.text.DecimalFormat Maven / Gradle / Ivy

There is a newer version: 4.15.0-HBase-1.5
Show newest version
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
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.ParseException;
import java.text.ParsePosition;

import com.ibm.icu.impl.number.AffixUtils;
import com.ibm.icu.impl.number.DecimalFormatProperties;
import com.ibm.icu.impl.number.Padder.PadPosition;
import com.ibm.icu.impl.number.Parse;
import com.ibm.icu.impl.number.PatternStringParser;
import com.ibm.icu.impl.number.PatternStringUtils;
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_} 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: * *

    *
  1. Magnitude: Display a fixed number of fraction digits; this is the most * common form. *
  2. Increment: Round numbers to the closest multiple of a certain increment, * such as 0.05. This is common in currencies. *
  3. 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 bidi 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. * *

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; //=====================================================================================// // 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: * *

    *
  1. {@link #setDecimalSeparatorAlwaysShown} *
  2. {@link #setExponentSignAlwaysShown} *
  3. {@link #setFormatWidth} *
  4. {@link #setGroupingSize} *
  5. {@link #setMultiplier} (percent/permille) *
  6. {@link #setMaximumFractionDigits} *
  7. {@link #setMaximumIntegerDigits} *
  8. {@link #setMaximumSignificantDigits} *
  9. {@link #setMinimumExponentDigits} *
  10. {@link #setMinimumFractionDigits} *
  11. {@link #setMinimumIntegerDigits} *
  12. {@link #setMinimumSignificantDigits} *
  13. {@link #setPadPosition} *
  14. {@link #setPadCharacter} *
  15. {@link #setRoundingIncrement} *
  16. {@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) { FormattedNumber output = formatter.format(number); output.populateFieldPosition(fieldPosition, result.length()); output.appendTo(result); return result; } /** * {@inheritDoc} * * @stable ICU 2.0 */ @Override public StringBuffer format(long number, StringBuffer result, FieldPosition fieldPosition) { FormattedNumber output = formatter.format(number); output.populateFieldPosition(fieldPosition, result.length()); output.appendTo(result); return result; } /** * {@inheritDoc} * * @stable ICU 2.0 */ @Override public StringBuffer format(BigInteger number, StringBuffer result, FieldPosition fieldPosition) { FormattedNumber output = formatter.format(number); output.populateFieldPosition(fieldPosition, result.length()); output.appendTo(result); return result; } /** * {@inheritDoc} * * @stable ICU 2.0 */ @Override public StringBuffer format( java.math.BigDecimal number, StringBuffer result, FieldPosition fieldPosition) { FormattedNumber output = formatter.format(number); output.populateFieldPosition(fieldPosition, result.length()); output.appendTo(result); return result; } /** * {@inheritDoc} * * @stable ICU 2.0 */ @Override public StringBuffer format(BigDecimal number, StringBuffer result, FieldPosition fieldPosition) { FormattedNumber output = formatter.format(number); output.populateFieldPosition(fieldPosition, result.length()); output.appendTo(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.getFieldIterator(); } /** * {@inheritDoc} * * @stable ICU 3.0 */ @Override public StringBuffer format(CurrencyAmount currAmt, StringBuffer toAppendTo, FieldPosition pos) { FormattedNumber output = formatter.format(currAmt); output.populateFieldPosition(pos, toAppendTo.length()); output.appendTo(toAppendTo); return toAppendTo; } /** * {@inheritDoc} * * @stable ICU 2.0 */ @Override public Number parse(String text, ParsePosition parsePosition) { DecimalFormatProperties pprops = threadLocalProperties.get(); synchronized (this) { pprops.copyFrom(properties); } // Backwards compatibility: use currency parse mode if this is a currency instance Number result = Parse.parse(text, parsePosition, pprops, symbols); // Backwards compatibility: return com.ibm.icu.math.BigDecimal if (result instanceof java.math.BigDecimal) { result = safeConvertBigDecimal((java.math.BigDecimal) result); } return result; } /** * {@inheritDoc} * * @stable ICU 49 */ @Override public CurrencyAmount parseCurrency(CharSequence text, ParsePosition parsePosition) { try { DecimalFormatProperties pprops = threadLocalProperties.get(); synchronized (this) { pprops.copyFrom(properties); } CurrencyAmount result = Parse.parseCurrency(text, parsePosition, pprops, symbols); if (result == null) return null; Number number = result.getNumber(); // Backwards compatibility: return com.ibm.icu.math.BigDecimal if (number instanceof java.math.BigDecimal) { number = safeConvertBigDecimal((java.math.BigDecimal) number); result = new CurrencyAmount(number, result.getCurrency()); } return result; } catch (ParseException e) { 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.format(1).getPrefix(); } /** * 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.format(-1).getPrefix(); } /** * 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.format(1).getSuffix(); } /** * 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.format(-1).getSuffix(); } /** * 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. * * @see #setSignAlwaysShown * @category Affixes * @internal * @deprecated ICU 59: This API is a technical preview. It may change in an upcoming release. */ @Deprecated public synchronized boolean getSignAlwaysShown() { // 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: * *

    *
  1. Patterns without a negative subpattern: The locale's plus sign will be prepended * to the positive prefix. *
  2. 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. *
  3. 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. * @category Affixes * @internal * @deprecated ICU 59: This API is technical preview. It may change in an upcoming release. */ @Deprecated 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. * @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 (multiplier != 1) { delta++; int temp = value / 10; if (temp * 10 != value) { delta = -1; break; } value = temp; } if (delta != -1) { properties.setMagnitudeMultiplier(delta); } else { 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 { java.math.BigDecimal javaBigDecimal = java.math.BigDecimal.valueOf(increment); 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. * @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. * @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 maximium 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 maximium 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 maximium 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 maximium 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) { if (useSignificantDigits) { // These are the default values from the old implementation. properties.setMinimumSignificantDigits(1); properties.setMaximumSignificantDigits(6); } else { properties.setMinimumSignificantDigits(-1); properties.setMaximumSignificantDigits(-1); } 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 '.'; // TODO: Is this the correct behavior? } 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.getGroupingSize() > 0 || properties.getSecondaryGroupingSize() > 0; } /** * 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". * *

Calling df.setGroupingUsed(true) is functionally equivalent to setting grouping * size to 3, as in df.setGroupingSize(3). * * @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) { if (enabled) { // Set to a reasonable default value properties.setGroupingSize(3); } else { properties.setGroupingSize(0); properties.setSecondaryGroupingSize(0); } refreshFormatter(); } /** * Returns the primary grouping size in use. * * @see #setGroupingSize * @category Separators * @stable ICU 2.0 */ public synchronized int getGroupingSize() { 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 grouping1 = properties.getGroupingSize(); int grouping2 = properties.getSecondaryGroupingSize(); if (grouping1 == grouping2 || grouping2 < 0) { return 0; } return properties.getSecondaryGroupingSize(); } /** * {@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 * @internal * @deprecated ICU 59: This API is a technical preview. It may change in an upcoming release. */ @Deprecated public synchronized int getMinimumGroupingDigits() { // Only 1 and 2 are supported right now. if (properties.getMinimumGroupingDigits() == 2) { return 2; } else { 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". * * @param number The minimum number of digits before grouping is triggered. * @category Separators * @internal * @deprecated ICU 59: This API is a technical preview. It may change in an upcoming release. */ @Deprecated public synchronized void setMinimumGroupingDigits(int number) { properties.setMinimumGroupingDigits(number); refreshFormatter(); } /** * 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 user-specified currency. May be null. * * @see #setCurrency * @see DecimalFormatSymbols#getCurrency * @category Currency * @stable ICU 2.6 */ @Override public synchronized Currency getCurrency() { return properties.getCurrency(); } /** * Sets the currency to be used when formatting numbers. The effect is twofold: * *

    *
  1. Substitutions for currency symbols in the pattern string will use this currency *
  2. 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); // Backwards compatibility: also set the currency in the DecimalFormatSymbols if (currency != null) { symbols.setCurrency(currency); String symbol = currency.getName(symbols.getULocale(), Currency.SYMBOL_NAME, null); symbols.setCurrencySymbol(symbol); } 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() not needed } /** * 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() == Parse.ParseMode.STRICT; } /** * {@inheritDoc} * * @category Parsing * @stable ICU 3.6 */ @Override public synchronized void setParseStrict(boolean parseStrict) { Parse.ParseMode mode = parseStrict ? Parse.ParseMode.STRICT : Parse.ParseMode.LENIENT; properties.setParseMode(mode); // refreshFormatter() not needed } /** * {@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() not needed } /** * {@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 * @internal * @deprecated ICU 59: This API is a technical preview. It may change in an upcoming release. */ @Deprecated public synchronized boolean getParseNoExponent() { 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 * @internal * @deprecated ICU 59: This API is a technical preview. It may change in an upcoming release. */ @Deprecated 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 * @internal * @deprecated ICU 59: This API is a technical preview. It may change in an upcoming release. */ @Deprecated public synchronized boolean getParseCaseSensitive() { 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 * @internal * @deprecated ICU 59: This API is a technical preview. It may change in an upcoming release. */ @Deprecated 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 = threadLocalProperties.get().copyFrom(properties); if (useCurrency(properties)) { 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 * @provisional This API might change or be removed in a future release. * @draft 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(); } private static final ThreadLocal threadLocalProperties = new ThreadLocal() { @Override protected DecimalFormatProperties initialValue() { return new DecimalFormatProperties(); } }; /** 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); } /** * 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; } } } /** * Returns true if the currency is set in The property bag or if currency symbols are present in * the prefix/suffix pattern. */ private static boolean useCurrency(DecimalFormatProperties properties) { return ((properties.getCurrency() != null) || properties.getCurrencyPluralInfo() != null || properties.getCurrencyUsage() != null || AffixUtils.hasCurrencySymbols(properties.getPositivePrefixPattern()) || AffixUtils.hasCurrencySymbols(properties.getPositiveSuffixPattern()) || AffixUtils.hasCurrencySymbols(properties.getNegativePrefixPattern()) || AffixUtils.hasCurrencySymbols(properties.getNegativeSuffixPattern())); } /** * 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); } /** * @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; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy