From f60798a30e9a3e0b666fed5384b6ac92a8a283dd Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Tue, 23 Apr 2024 21:10:46 +0000 Subject: [PATCH] 8329222: java.text.NumberFormat (and subclasses) spec updates Reviewed-by: naoto --- .../java/text/CompactNumberFormat.java | 214 +++++---- .../classes/java/text/DecimalFormat.java | 417 +++++++++--------- .../share/classes/java/text/NumberFormat.java | 181 ++++---- 3 files changed, 421 insertions(+), 391 deletions(-) diff --git a/src/java.base/share/classes/java/text/CompactNumberFormat.java b/src/java.base/share/classes/java/text/CompactNumberFormat.java index a477d9c2fd8..115a21ee662 100644 --- a/src/java.base/share/classes/java/text/CompactNumberFormat.java +++ b/src/java.base/share/classes/java/text/CompactNumberFormat.java @@ -47,80 +47,113 @@ import java.util.stream.Collectors; /** *

* {@code CompactNumberFormat} is a concrete subclass of {@code NumberFormat} - * that formats a decimal number in its compact form. - * - * The compact number formatting is designed for the environment where the space - * is limited, and the formatted string can be displayed in that limited space. - * It is defined by LDML's specification for + * that formats a decimal number in a localized compact form. + * Compact number formatting is designed for an environment with limited space. + * For example, displaying the formatted number {@code 7M} instead of {@code + * 7,000,000.00} in the {@link java.util.Locale#US US locale}. The {@code + * CompactNumberFormat} class is defined by LDML's specification for * - * Compact Number Formats. A compact number formatting refers - * to the representation of a number in a shorter form, based on the patterns - * provided for a given locale. + * Compact Number Formats. * - *

- * For example: - *
In the {@link java.util.Locale#US US locale}, {@code 1000} can be formatted - * as {@code "1K"}, and {@code 1000000} as {@code "1M"}, depending upon the - * {@linkplain ##compact_number_style style} used. - *
In the {@code "hi_IN"} locale, {@code 1000} can be formatted as - * "1 \u0939\u091C\u093C\u093E\u0930", and {@code 50000000} as "5 \u0915.", - * depending upon the {@linkplain ##compact_number_style style} used. - * - *

- * To obtain a {@code CompactNumberFormat} for a locale, use one - * of the factory methods given by {@code NumberFormat} for compact number - * formatting. For example, - * {@link NumberFormat#getCompactNumberInstance(Locale, Style)}. - * - *

{@snippet lang=java : - * NumberFormat fmt = NumberFormat.getCompactNumberInstance( - * Locale.forLanguageTag("hi-IN"), NumberFormat.Style.SHORT); - * String result = fmt.format(1000); - * }
+ *

Getting a CompactNumberFormat

+ * To get a compact number format, use one of the ways listed below. + * + *

If a standard compact format for a given locale and {@link + * ##compact_number_style style} is desired, it is recommended to use one of the + * NumberFormat factory methods listed above. To use an instance method + * defined by {@code CompactNumberFormat}, the {@code NumberFormat} returned by + * these factory methods should be type checked before converted to {@code CompactNumberFormat}. + * If the installed locale-sensitive service implementation does not support + * the given {@code Locale}, the parent locale chain will be looked up, and + * a {@code Locale} used that is supported. * *

Style

- *

- * A number can be formatted in the compact forms with two different - * styles, {@link NumberFormat.Style#SHORT SHORT} - * and {@link NumberFormat.Style#LONG LONG}. Use - * {@link NumberFormat#getCompactNumberInstance(Locale, Style)} for formatting and - * parsing a number in {@link NumberFormat.Style#SHORT SHORT} or - * {@link NumberFormat.Style#LONG LONG} compact form, - * where the given {@code Style} parameter requests the desired - * format. A {@link NumberFormat.Style#SHORT SHORT} style - * compact number instance in the {@link java.util.Locale#US US locale} formats - * {@code 10000} as {@code "10K"}. However, a - * {@link NumberFormat.Style#LONG LONG} style instance in same locale - * formats {@code 10000} as {@code "10 thousand"}. + * When using {@link NumberFormat#getCompactNumberInstance(Locale, Style)}, a + * compact form can be retrieved with either a {@link NumberFormat.Style#SHORT + * SHORT} or {@link NumberFormat.Style#LONG LONG} style. + * For example, a {@link NumberFormat.Style#SHORT SHORT} style compact number instance in + * the {@link java.util.Locale#US US locale} formats {@code 10000} as {@code + * "10K"}. However, a {@link NumberFormat.Style#LONG LONG} style instance in + * the same locale formats {@code 10000} as {@code "10 thousand"}. + * + *

Using CompactNumberFormat

+ * The following is an example of formatting and parsing in a localized manner, + * + * {@snippet lang=java : + * NumberFormat compactFormat = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT); + * compactFormat.format(1000); // returns "1K" + * compactFormat.parse("1K"); // returns 1000 + * } + * + *

Formatting

+ * The default formatting behavior returns a formatted string with no fractional + * digits, however users can use the {@link #setMinimumFractionDigits(int)} + * method to include the fractional part. + * The number {@code 1000.0} or {@code 1000} is formatted as {@code "1K"} + * not {@code "1.00K"} (in the {@link java.util.Locale#US US locale}). For this + * reason, the patterns provided for formatting contain only the minimum + * integer digits, prefix and/or suffix, but no fractional part. + * For example, patterns used are {@code {"", "", "", 0K, 00K, ...}}. If the pattern + * selected for formatting a number is {@code "0"} (special pattern), + * either explicit or defaulted, then the general number formatting provided by + * {@link java.text.DecimalFormat DecimalFormat} + * for the specified locale is used. + * + *

Rounding

+ * {@code CompactNumberFormat} provides rounding modes defined in + * {@link java.math.RoundingMode} for formatting. By default, it uses + * {@link java.math.RoundingMode#HALF_EVEN RoundingMode.HALF_EVEN}. + * + *

Parsing

+ * The default parsing behavior does not allow a grouping separator until + * grouping used is set to {@code true} by using + * {@link #setGroupingUsed(boolean)}. The parsing of the fractional part + * depends on the {@link #isParseIntegerOnly()}. For example, if the + * parse integer only is set to true, then the fractional part is skipped. * *

Compact Number Patterns

*

- * The compact number patterns are represented in a series of patterns where each - * pattern is used to format a range of numbers. An example of - * {@link NumberFormat.Style#SHORT SHORT} styled compact number patterns + * The {@code compactPatterns} in {@link + * CompactNumberFormat#CompactNumberFormat(String, DecimalFormatSymbols, String[]) + * CompactNumberFormat(decimalPattern, symbols, compactPatterns)} are represented + * as a series of strings, where each string is a {@link ##compact_number_syntax + * pattern} that is used to format a range of numbers. + * + *

An example of the {@link NumberFormat.Style#SHORT SHORT} styled compact number patterns * for the {@link java.util.Locale#US US locale} is {@code {"", "", "", "0K", * "00K", "000K", "0M", "00M", "000M", "0B", "00B", "000B", "0T", "00T", "000T"}}, * ranging from {@code 10}{@code 0} to {@code 10}{@code 14}. * There can be any number of patterns and they are * strictly index based starting from the range {@code 10}{@code 0}. - * For example, in the above patterns, pattern at index 3 - * ({@code "0K"}) is used for formatting {@code number >= 1000 and number < 10000}, - * pattern at index 4 ({@code "00K"}) is used for formatting - * {@code number >= 10000 and number < 100000} and so on. In most of the locales, - * patterns with the range + * For example, in the above patterns, the pattern at index 3 + * ({@code "0K"}) is used for formatting a number in the range: {@code 1000 <= number < 10000}, + * index 4 ({@code "00K"}) for formatting a number the range: {@code 10000 <= + * number < 100000}, and so forth. + *

In most locales, patterns with the range * {@code 10}{@code 0}-{@code 10}{@code 2} are empty * strings, which implicitly means a special pattern {@code "0"}. * A special pattern {@code "0"} is used for any range which does not contain * a compact pattern. This special pattern can appear explicitly for any specific * range, or considered as a default pattern for an empty string. * - *

+ *

Negative Subpatterns

* A compact pattern contains a positive and negative subpattern - * separated by a subpattern boundary character {@code ';' (U+003B)}, + * separated by a subpattern boundary character {@code ';'}, * for example, {@code "0K;-0K"}. Each subpattern has a prefix, * minimum integer digits, and suffix. The negative subpattern * is optional, if absent, then the positive subpattern prefixed with the - * minus sign ({@code '-' U+002D HYPHEN-MINUS}) is used as the negative + * minus sign {@code '-' (U+002D HYPHEN-MINUS)} is used as the negative * subpattern. That is, {@code "0K"} alone is equivalent to {@code "0K;-0K"}. * If there is an explicit negative subpattern, it serves only to specify * the negative prefix and suffix. The number of minimum integer digits, @@ -128,31 +161,35 @@ import java.util.stream.Collectors; * That means that {@code "0K;-00K"} produces precisely the same behavior * as {@code "0K;-0K"}. * - *

+ *

Escaping Special Characters

* Many characters in a compact pattern are taken literally, they are matched * during parsing and output unchanged during formatting. * {@linkplain DecimalFormat##special_pattern_character Special characters}, * on the other hand, stand for other characters, strings, or classes of - * characters. They must be quoted, using single quote {@code ' (U+0027)} + * characters. These characters must be quoted using single quotes {@code ' (U+0027)} * unless noted otherwise, if they are to appear in the prefix or suffix * as literals. For example, 0\u0915'.'. * *

Plurals

+ *

{@code CompactNumberFormat} support patterns for both singular and plural + * compact forms. For the plural form, the {@code Pattern} should consist + * of {@code PluralPattern}(s) separated by a space ' ' (U+0020) that are enumerated + * within a pair of curly brackets '{' (U+007B) and '}' (U+007D). + * In this format, each {@code PluralPattern} consists of its {@code count}, + * followed by a single colon {@code ':' (U+003A)} and a {@code SimplePattern}. + * As a space is reserved for separating subsequent {@code PluralPattern}s, it must + * be quoted to be used literally in either the {@code prefix} or {@code suffix}. *

- * In case some localization requires compact number patterns to be different for - * plurals, each singular and plural pattern can be enumerated within a pair of - * curly brackets '{' (U+007B) and '}' (U+007D), separated - * by a space {@code ' ' (U+0020)}. If this format is used, each pattern needs to be - * prepended by its {@code count}, followed by a single colon {@code ':' (U+003A)}. - * If the pattern includes spaces literally, they must be quoted. + * For example, while the pattern representing millions ({@code 10}{@code 6} + * ) in the US locale can be specified as the SimplePattern: {@code "0 Million"}, for the + * German locale it can be specified as the PluralPattern: + * {@code "{one:0' 'Million other:0' 'Millionen}"}. + * *

- * For example, the compact number pattern representing millions in German locale can be - * specified as {@code "{one:0' 'Million other:0' 'Millionen}"}. The {@code count} - * follows LDML's + * A compact pattern has the following syntax, with {@code count} + * following LDML's * - * Language Plural Rules. - *

- * A compact pattern has the following syntax: + * Language Plural Rules: *

  * Pattern:
  *         SimplePattern
@@ -179,37 +216,12 @@ import java.util.stream.Collectors;
  *      0 MinimumInteger
  * 
* - *

Formatting

- * The default formatting behavior returns a formatted string with no fractional - * digits, however users can use the {@link #setMinimumFractionDigits(int)} - * method to include the fractional part. - * The number {@code 1000.0} or {@code 1000} is formatted as {@code "1K"} - * not {@code "1.00K"} (in the {@link java.util.Locale#US US locale}). For this - * reason, the patterns provided for formatting contain only the minimum - * integer digits, prefix and/or suffix, but no fractional part. - * For example, patterns used are {@code {"", "", "", 0K, 00K, ...}}. If the pattern - * selected for formatting a number is {@code "0"} (special pattern), - * either explicit or defaulted, then the general number formatting provided by - * {@link java.text.DecimalFormat DecimalFormat} - * for the specified locale is used. - * - *

Parsing

- * The default parsing behavior does not allow a grouping separator until - * grouping used is set to {@code true} by using - * {@link #setGroupingUsed(boolean)}. The parsing of the fractional part - * depends on the {@link #isParseIntegerOnly()}. For example, if the - * parse integer only is set to true, then the fractional part is skipped. - * - *

Rounding

- * {@code CompactNumberFormat} provides rounding modes defined in - * {@link java.math.RoundingMode} for formatting. By default, it uses - * {@link java.math.RoundingMode#HALF_EVEN RoundingMode.HALF_EVEN}. - * * @spec https://www.unicode.org/reports/tr35 * Unicode Locale Data Markup Language (LDML) * @see NumberFormat.Style * @see NumberFormat * @see DecimalFormat + * @see Locale * @since 12 */ public final class CompactNumberFormat extends NumberFormat { @@ -389,10 +401,19 @@ public final class CompactNumberFormat extends NumberFormat { * To obtain the instance of {@code CompactNumberFormat} with the standard * compact patterns for a {@code Locale} and {@code Style}, * it is recommended to use the factory methods given by - * {@code NumberFormat} for compact number formatting. For example, - * {@link NumberFormat#getCompactNumberInstance(Locale, Style)}. + * {@code NumberFormat} for compact number formatting. * - * @param decimalPattern a decimal pattern for general number formatting + *

Below is an example of using the constructor, + * + * {@snippet lang=java : + * String[] compactPatterns = {"", "", "", "a lot"}; + * NumberFormat fmt = new CompactNumberFormat("00", DecimalFormatSymbols.getInstance(Locale.US), compactPatterns); + * fmt.format(1); // returns "01" + * fmt.format(1000); // returns "a lot" + * } + * + * @param decimalPattern a {@linkplain DecimalFormat##patterns decimal pattern} + * for general number formatting * @param symbols the set of symbols to be used * @param compactPatterns an array of * {@linkplain ##compact_number_patterns compact number patterns} @@ -419,7 +440,8 @@ public final class CompactNumberFormat extends NumberFormat { * {@code NumberFormat} for compact number formatting. For example, * {@link NumberFormat#getCompactNumberInstance(Locale, Style)}. * - * @param decimalPattern a decimal pattern for general number formatting + * @param decimalPattern a {@linkplain DecimalFormat##patterns decimal pattern} + * for general number formatting * @param symbols the set of symbols to be used * @param compactPatterns an array of * {@linkplain ##compact_number_patterns compact number patterns} diff --git a/src/java.base/share/classes/java/text/DecimalFormat.java b/src/java.base/share/classes/java/text/DecimalFormat.java index 752c3924855..2fc64d8e63e 100644 --- a/src/java.base/share/classes/java/text/DecimalFormat.java +++ b/src/java.base/share/classes/java/text/DecimalFormat.java @@ -56,45 +56,104 @@ import sun.util.locale.provider.ResourceBundleBasedAdapter; /** * {@code DecimalFormat} is a concrete subclass of - * {@code NumberFormat} that formats decimal numbers. It has a variety of - * features designed to make it possible to parse and format numbers in any - * locale, including support for Western, Arabic, and Indic digits. It also - * supports different kinds of numbers, including integers (123), fixed-point + * {@code NumberFormat} that formats decimal numbers in a localized manner. + * It has a variety of features designed to make it possible to parse and format + * numbers in any locale, including support for Western, Arabic, and Indic digits. + * It also supports different kinds of numbers, including integers (123), fixed-point * numbers (123.4), scientific notation (1.23E4), percentages (12%), and - * currency amounts ($123). All of these can be localized. + * currency amounts ($123). * - *

To obtain a {@code NumberFormat} for a specific locale, including the - * default locale, call one of {@code NumberFormat}'s factory methods, such - * as {@code getInstance()}. In general, do not call the - * {@code DecimalFormat} constructors directly, since the - * {@code NumberFormat} factory methods may return subclasses other than - * {@code DecimalFormat}. If you need to customize the format object, do - * something like this: + *

Getting a DecimalFormat

* - *
{@snippet lang=java : - * NumberFormat numFormat = NumberFormat.getInstance(loc); - * if (numFormat instanceof DecimalFormat decFormat) { - * decFormat.setDecimalSeparatorAlwaysShown(true); + * To obtain a standard decimal format for a specific locale, including the default locale, + * it is recommended to call one of the {@code NumberFormat} + * {@link NumberFormat##factory_methods factory methods}, such as {@link NumberFormat#getInstance()}. + * These factory methods may not always return a {@code DecimalFormat} + * depending on the locale-service provider implementation + * installed. Thus, to use an instance method defined by {@code DecimalFormat}, + * the {@code NumberFormat} returned by the factory method should be + * type checked before converted to {@code DecimalFormat}. If the installed locale-sensitive + * service implementation does not support the given {@code Locale}, the parent + * locale chain will be looked up, and a {@code Locale} used that is supported. + * + *

If the factory methods are not desired, use one of the constructors such + * as {@link #DecimalFormat(String) DecimalFormat(String pattern)}. See the {@link + * ##patterns Pattern} section for more information on the {@code pattern} parameter. + * + *

Using DecimalFormat

+ * The following is an example of formatting and parsing, + * {@snippet lang=java : + * NumberFormat nFmt = NumberFormat.getCurrencyInstance(Locale.US); + * if (nFmt instanceof DecimalFormat dFmt) { + * // pattern match to DecimalFormat to use setPositiveSuffix(String) + * dFmt.setPositiveSuffix(" dollars"); + * dFmt.format(100000); // returns "$100,000.00 dollars" + * dFmt.parse("$100,000.00 dollars"); // returns 100000 + * } * } - * }
* - *

A {@code DecimalFormat} comprises a pattern and a set of - * symbols. The pattern may be set directly using - * {@code applyPattern()}, or indirectly using the API methods. The - * symbols are stored in a {@code DecimalFormatSymbols} object. When using - * the {@code NumberFormat} factory methods, the pattern and symbols are - * read from localized {@code ResourceBundle}s. * - *

Patterns

+ *

Formatting and Parsing

+ *

Rounding

* - * Note: For any given {@code DecimalFormat} pattern, if the pattern is not - * in scientific notation, the maximum number of integer digits will not be - * derived from the pattern, and instead set to {@link Integer#MAX_VALUE}. - * Otherwise, if the pattern is in scientific notation, the maximum number of - * integer digits will be derived from the pattern. This derivation is detailed - * in the {@link ##scientific_notation Scientific Notation} section. This behavior - * is the typical end-user desire; {@link #setMaximumIntegerDigits(int)} can be - * used to manually adjust the maximum integer digits. + * When formatting, {@code DecimalFormat} can adjust its rounding using {@link + * #setRoundingMode(RoundingMode)}. By default, it uses + * {@link java.math.RoundingMode#HALF_EVEN RoundingMode.HALF_EVEN}. + * + *

Digits

+ * + * When formatting, {@code DecimalFormat} uses the ten consecutive + * characters starting with the localized zero digit defined in the + * {@code DecimalFormatSymbols} object as digits. + *

When parsing, these digits as well as all Unicode decimal digits, as + * defined by {@link Character#digit Character.digit}, are recognized. + * + *

Integer and Fraction Digit Limits

+ * @implSpec + * When formatting a {@code Number} other than {@code BigInteger} and + * {@code BigDecimal}, {@code 309} is used as the upper limit for integer digits, + * and {@code 340} as the upper limit for fraction digits. This occurs, even if + * one of the {@code DecimalFormat} getter methods, for example, {@link #getMinimumFractionDigits()} + * returns a numerically greater value. + * + *

Special Values

+ * + * + *

Synchronization

+ * + *

+ * Decimal formats are generally not synchronized. + * It is recommended to create separate format instances for each thread. + * If multiple threads access a format concurrently, it must be synchronized + * externally. + * + *

DecimalFormat Pattern

+ * + * A {@code DecimalFormat} comprises a pattern and a set of + * symbols. The pattern may be set directly using {@code applyPattern()}, + * or indirectly using the various API methods. The symbols are stored in a {@code + * DecimalFormatSymbols} object. When using the {@code NumberFormat} factory + * methods, the pattern and symbols are created from the locale-sensitive service + * implementation installed. * *

{@code DecimalFormat} patterns have the following syntax: *

@@ -135,11 +194,115 @@ import sun.util.locale.provider.ResourceBundleBasedAdapter;
  *         0 MinimumExponentopt
  * 
* - *

A {@code DecimalFormat} pattern contains a positive and negative + *

Special Pattern Characters

+ * + *

The special characters in the table below are interpreted syntactically when + * used in the DecimalFormat pattern. + * They must be quoted, unless noted otherwise, if they are to appear in the + * prefix or suffix as literals. + * + *

The characters in the {@code Symbol} column are used in non-localized + * patterns. The corresponding characters in the {@code Localized Symbol} column are used + * in localized patterns, with the characters in {@code Symbol} losing their + * syntactical meaning. Two exceptions are the currency sign ({@code U+00A4}) and + * quote ({@code U+0027}), which are not localized. + *

+ * Non-localized patterns should be used when calling {@link #applyPattern(String)}. + * Localized patterns should be used when calling {@link #applyLocalizedPattern(String)}. + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Chart showing symbol, location, localized, and meaning.
Symbol + * Localized Symbol + * Location + * Meaning + *
{@code 0} + * {@link DecimalFormatSymbols#getZeroDigit()} + * Number + * Digit + *
{@code #} + * {@link DecimalFormatSymbols#getDigit()} + * Number + * Digit, zero shows as absent + *
{@code .} + * {@link DecimalFormatSymbols#getDecimalSeparator()} + * Number + * Decimal separator or monetary decimal separator + *
{@code - (U+002D)} + * {@link DecimalFormatSymbols#getMinusSign()} + * Number + * Minus sign + *
{@code ,} + * {@link DecimalFormatSymbols#getGroupingSeparator()} + * Number + * Grouping separator or monetary grouping separator + *
{@code E} + * {@link DecimalFormatSymbols#getExponentSeparator()} + * Number + * Separates mantissa and exponent in scientific notation. This value + * is case sensistive. Need not be quoted in prefix or suffix. + *
{@code ;} + * {@link DecimalFormatSymbols#getPatternSeparator()} + * Subpattern boundary + * Separates positive and negative subpatterns + *
{@code %} + * {@link DecimalFormatSymbols#getPercent()} + * Prefix or suffix + * Multiply by 100 and show as percentage + *
‰ ({@code U+2030}) + * {@link DecimalFormatSymbols#getPerMill()} + * Prefix or suffix + * Multiply by 1000 and show as per mille value + *
¤ ({@code U+00A4}) + * n/a (not localized) + * Prefix or suffix + * Currency sign, replaced by currency symbol. If + * doubled, replaced by international currency symbol. + * If present in a pattern, the monetary decimal/grouping separators + * are used instead of the decimal/grouping separators. + *
{@code ' (U+0027)} + * n/a (not localized) + * Prefix or suffix + * Used to quote special characters in a prefix or suffix, + * for example, {@code "'#'#"} formats 123 to + * {@code "#123"}. To create a single quote + * itself, use two in a row: {@code "# o''clock"}. + *
+ *
+ * + *

Maximum Digits Derivation

+ * For any given {@code DecimalFormat} pattern, if the pattern is not + * in scientific notation, the maximum number of integer digits will not be + * derived from the pattern, and instead set to {@link Integer#MAX_VALUE}. + * Otherwise, if the pattern is in scientific notation, the maximum number of + * integer digits will be derived from the pattern. This derivation is detailed + * in the {@link ##scientific_notation Scientific Notation} section. {@link + * #setMaximumIntegerDigits(int)} can be used to manually adjust the maximum + * integer digits. + * + *

Negative Subpatterns

+ * A {@code DecimalFormat} pattern contains a positive and negative * subpattern, for example, {@code "#,##0.00;(#,##0.00)"}. Each * subpattern has a prefix, numeric part, and suffix. The negative subpattern * is optional; if absent, then the positive subpattern prefixed with the - * minus sign ({@code '-' U+002D HYPHEN-MINUS}) is used as the + * minus sign {@code '-' (U+002D HYPHEN-MINUS)} is used as the * negative subpattern. That is, {@code "0.00"} alone is equivalent to * {@code "0.00;-0.00"}. If there is an explicit negative subpattern, it * serves only to specify the negative prefix and suffix; the number of digits, @@ -158,105 +321,15 @@ import sun.util.locale.provider.ResourceBundleBasedAdapter; * specified.) Another example is that the decimal separator and grouping * separator should be distinct characters, or parsing will be impossible. * + *

Grouping Separator

*

The grouping separator is commonly used for thousands, but in some - * countries it separates ten-thousands. The grouping size is a constant number + * locales it separates ten-thousands. The grouping size is a constant number * of digits between the grouping characters, such as 3 for 100,000,000 or 4 for - * 1,0000,0000. If you supply a pattern with multiple grouping characters, the + * 1,0000,0000. If you supply a pattern with multiple grouping characters, the * interval between the last one and the end of the integer is the one that is - * used. So {@code "#,##,###,####"} == {@code "######,####"} == + * used. For example, {@code "#,##,###,####"} == {@code "######,####"} == * {@code "##,####,####"}. * - *

Special Pattern Characters

- * - *

Many characters in a pattern are taken literally; they are matched during - * parsing and output unchanged during formatting. Special characters, on the - * other hand, stand for other characters, strings, or classes of characters. - * They must be quoted, unless noted otherwise, if they are to appear in the - * prefix or suffix as literals. - * - *

The characters listed here are used in non-localized patterns. Localized - * patterns use the corresponding characters taken from this formatter's - * {@code DecimalFormatSymbols} object instead, and these characters lose - * their special status. Two exceptions are the currency sign and quote, which - * are not localized. - * - *

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Chart showing symbol, location, localized, and meaning.
Symbol - * Location - * Localized? - * Meaning - *
{@code 0} - * Number - * Yes - * Digit - *
{@code #} - * Number - * Yes - * Digit, zero shows as absent - *
{@code .} - * Number - * Yes - * Decimal separator or monetary decimal separator - *
{@code -} - * Number - * Yes - * Minus sign - *
{@code ,} - * Number - * Yes - * Grouping separator or monetary grouping separator - *
{@code E} - * Number - * Yes - * Separates mantissa and exponent in scientific notation. - * Need not be quoted in prefix or suffix. - *
{@code ;} - * Subpattern boundary - * Yes - * Separates positive and negative subpatterns - *
{@code %} - * Prefix or suffix - * Yes - * Multiply by 100 and show as percentage - *
{@code U+2030} - * Prefix or suffix - * Yes - * Multiply by 1000 and show as per mille value - *
¤ ({@code U+00A4}) - * Prefix or suffix - * No - * Currency sign, replaced by currency symbol. If - * doubled, replaced by international currency symbol. - * If present in a pattern, the monetary decimal/grouping separators - * are used instead of the decimal/grouping separators. - *
{@code '} - * Prefix or suffix - * No - * Used to quote special characters in a prefix or suffix, - * for example, {@code "'#'#"} formats 123 to - * {@code "#123"}. To create a single quote - * itself, use two in a row: {@code "# o''clock"}. - *
- *
- * *

Scientific Notation

* *

Numbers in scientific notation are expressed as the product of a mantissa @@ -339,95 +412,13 @@ import sun.util.locale.provider.ResourceBundleBasedAdapter; *

  • Exponential patterns may not contain grouping separators. * * - *

    Rounding

    - * - * {@code DecimalFormat} provides rounding modes defined in - * {@link java.math.RoundingMode} for formatting. By default, it uses - * {@link java.math.RoundingMode#HALF_EVEN RoundingMode.HALF_EVEN}. - * - *

    Digits

    - * - * For formatting, {@code DecimalFormat} uses the ten consecutive - * characters starting with the localized zero digit defined in the - * {@code DecimalFormatSymbols} object as digits. For parsing, these - * digits as well as all Unicode decimal digits, as defined by - * {@link Character#digit Character.digit}, are recognized. - * - *

    Integer and Fraction Digit Limits

    - * - * @implSpec - * When formatting a {@code Number} other than {@code BigInteger} and - * {@code BigDecimal}, {@code 309} is used as the upper limit for integer digits, - * and {@code 340} as the upper limit for fraction digits. This occurs, even if - * one of the {@code DecimalFormat} getter methods, for example, {@link #getMinimumFractionDigits()} - * returns a numerically greater value. - * - *

    Special Values

    - * - *

    Not a Number({@code NaN}) is formatted as a string, which typically has a - * single character {@code U+FFFD}. This string is determined by the - * {@code DecimalFormatSymbols} object. This is the only value for which - * the prefixes and suffixes are not used. - * - *

    Infinity is formatted as a string, which typically has a single character - * {@code U+221E}, with the positive or negative prefixes and suffixes - * applied. The infinity string is determined by the - * {@code DecimalFormatSymbols} object. - * - *

    Negative zero ({@code "-0"}) parses to - *

    - * - *

    Synchronization

    - * - *

    - * Decimal formats are generally not synchronized. - * It is recommended to create separate format instances for each thread. - * If multiple threads access a format concurrently, it must be synchronized - * externally. - * - *

    Example

    - * - *
    {@snippet lang=java : - * // Print out a number using the localized number, integer, currency, - * // and percent format for each locale - * Locale[] locales = NumberFormat.getAvailableLocales(); - * double myNumber = -1234.56; - * NumberFormat form; - * for (int j = 0; j < 4; ++j) { - * System.out.println("FORMAT"); - * for (Locale locale : locales) { - * if (locale.getCountry().length() == 0) { - * continue; // Skip language-only locales - * } - * System.out.print(locale.getDisplayName()); - * form = switch (j) { - * case 0 -> NumberFormat.getInstance(locale); - * case 1 -> NumberFormat.getIntegerInstance(locale); - * case 2 -> NumberFormat.getCurrencyInstance(locale); - * default -> NumberFormat.getPercentInstance(locale); - * }; - * if (form instanceof DecimalFormat decForm) { - * System.out.print(": " + decForm.toPattern()); - * } - * System.out.print(" -> " + form.format(myNumber)); - * try { - * System.out.println(" -> " + form.parse(form.format(myNumber))); - * } catch (ParseException e) {} - * } - * } - * }
    - * + * @spec https://www.unicode.org/reports/tr35 + * Unicode Locale Data Markup Language (LDML) * @see Java Tutorial * @see NumberFormat * @see DecimalFormatSymbols * @see ParsePosition + * @see Locale * @author Mark Davis * @author Alan Liu * @since 1.1 diff --git a/src/java.base/share/classes/java/text/NumberFormat.java b/src/java.base/share/classes/java/text/NumberFormat.java index bd36ad6dc25..0409efc2da0 100644 --- a/src/java.base/share/classes/java/text/NumberFormat.java +++ b/src/java.base/share/classes/java/text/NumberFormat.java @@ -59,102 +59,110 @@ import sun.util.locale.provider.LocaleServiceProviderPool; /** * {@code NumberFormat} is the abstract base class for all number * formats. This class provides the interface for formatting and parsing - * numbers. {@code NumberFormat} also provides methods for determining - * which locales have number formats, and what their names are. + * numbers in a localized manner. This enables code that can be completely + * independent of the locale conventions for decimal points, thousands-separators, + * the particular decimal digits used, or whether the number format is even + * decimal. For example, this class could be used within an application to + * produce a number in a currency format according to the conventions of the desired + * locale. * - *

    - * {@code NumberFormat} helps you to format and parse numbers for any locale. - * Your code can be completely independent of the locale conventions for - * decimal points, thousands-separators, or even the particular decimal - * digits used, or whether the number format is even decimal. + *

    Getting a NumberFormat

    + * To get a {@code NumberFormat} for the default Locale, use one of the static + * factory methods that return a concrete subclass of {@code NumberFormat}. + * The following formats all provide an example of formatting the {@code Number} + * "2000.50" with the {@link java.util.Locale#US US} locale as the default locale. + * * - *

    - * To format a number for the current Locale, use one of the factory - * class methods: - *

    - * {@snippet lang=java : - * myString = NumberFormat.getInstance().format(myNumber); - * } - *
    - * If you are formatting multiple numbers, it is - * more efficient to get the format and use it multiple times so that - * the system doesn't have to fetch the information about the local - * language and country conventions multiple times. - *
    - * {@snippet lang=java : - * NumberFormat nf = NumberFormat.getInstance(); - * for (var myNumber : numbers) { - * output.println(nf.format(myNumber) + "; "); - * } - * } - *
    - * To format a number for a different Locale, specify it in the - * call to {@code getInstance}. - *
    - * {@snippet lang=java : - * NumberFormat nf = NumberFormat.getInstance(Locale.FRENCH); - * } - *
    + * Alternatively, if a {@code NumberFormat} for a different locale is required, use + * one of the overloaded factory methods that take {@code Locale} as a parameter, + * for example, {@link #getIntegerInstance(Locale)}. If the installed locale-sensitive + * service implementation does not support the given {@code Locale}, the parent + * locale chain will be looked up, and a {@code Locale} used that is supported. * - *

    If the locale contains "nu" (numbers) and/or "rg" (region override) + *

    Locale Extensions

    + * Formatting behavior can be changed when using a locale that contains any of the following * Unicode extensions, - * the decimal digits, and/or the country used for formatting are overridden. + * + *

    * If both "nu" and "rg" are specified, the decimal digits from the "nu" * extension supersedes the implicit one from the "rg" extension. + * Although Unicode extensions + * defines various keys and values, actual locale-sensitive service implementations + * in a Java Runtime Environment might not support any particular Unicode locale + * attributes or key/type pairs. + *

    Below is an example of a "US" locale currency format with accounting style, + *

    {@code NumberFormat.getCurrencyInstance(Locale.forLanguageTag("en-US-u-cf-account"));}
    + * With this style, a negative value is formatted enclosed in parentheses, instead + * of being prepended with a minus sign. * - *

    You can also use a {@code NumberFormat} to parse numbers: - *

    + *

    Using NumberFormat

    + * The following is an example of formatting and parsing in a localized fashion, * {@snippet lang=java : - * myNumber = nf.parse(myString); + * NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(Locale.US); + * currencyFormat.format(100000); // returns "$100,000.00" + * currencyFormat.parse("$100,000.00"); // returns 100000 * } - *
    - * Use {@code getInstance} or {@code getNumberInstance} to get the - * normal number format. Use {@code getIntegerInstance} to get an - * integer number format. Use {@code getCurrencyInstance} to get the - * currency number format. Use {@code getCompactNumberInstance} to get the - * compact number format to format a number in shorter form. For example, - * {@code 2000} can be formatted as {@code "2K"} in - * {@link java.util.Locale#US US locale}. Use {@code getPercentInstance} - * to get a format for displaying percentages. With this format, a fraction - * like 0.53 is displayed as 53%. + * + *

    Customizing NumberFormat

    + * {@code NumberFormat} provides API to customize formatting and parsing behavior, + * * *

    - * You can also control the display of numbers with such methods as - * {@code setMinimumFractionDigits}. - * If you want even more control over the format or parsing, - * or want to give your users more control, - * you can try casting the {@code NumberFormat} you get from the factory methods - * to a {@code DecimalFormat} or {@code CompactNumberFormat} depending on - * the factory method used. This will work for the vast majority of locales; - * just remember to put it in a {@code try} block in case you encounter - * an unusual one. + * To provide more control over formatting or parsing behavior, type checking can + * be done to safely convert to an implementing subclass of {@code NumberFormat}; this + * provides additional methods defined by the subclass. + * For example, + * {@snippet lang=java : + * NumberFormat nFmt = NumberFormat.getInstance(Locale.US); + * if (nFmt instanceof DecimalFormat dFmt) { + * dFmt.setDecimalSeparatorAlwaysShown(true); + * dFmt.format(100); // returns "100." + * } + * } + * The {@code NumberFormat} subclass returned by the factory methods is dependent + * on the locale-service provider implementation installed, and may not always + * be {@link DecimalFormat} or {@link CompactNumberFormat}. * *

    - * NumberFormat and DecimalFormat are designed such that some controls - * work for formatting and others work for parsing. The following is - * the detailed description for each these control methods, - *

    - * setParseIntegerOnly : only affects parsing, e.g. - * if true, "3456.78" → 3456 (and leaves the parse position just after index 6) - * if false, "3456.78" → 3456.78 (and leaves the parse position just after index 8) - * This is independent of formatting. If you want to not show a decimal point - * where there might be no digits after the decimal point, use - * setDecimalSeparatorAlwaysShown. - *

    - * setDecimalSeparatorAlwaysShown : only affects formatting, and only where - * there might be no digits after the decimal point, such as with a pattern - * like "#,##0.##", e.g., - * if true, 3456.00 → "3,456." - * if false, 3456.00 → "3456" - * This is independent of parsing. If you want parsing to stop at the decimal - * point, use setParseIntegerOnly. - *

    * You can also use forms of the {@code parse} and {@code format} * methods with {@code ParsePosition} and {@code FieldPosition} to * allow you to: *

    * For example, you can align numbers in two ways: *
      @@ -197,15 +205,20 @@ import sun.util.locale.provider.LocaleServiceProviderPool; * If multiple threads access a format concurrently, it must be synchronized * externally. * - * @implSpec The {@link #format(double, StringBuffer, FieldPosition)}, + * @implSpec + * Null Parameter Handling + *
        + *
      • The {@link #format(double, StringBuffer, FieldPosition)}, * {@link #format(long, StringBuffer, FieldPosition)} and * {@link #parse(String, ParsePosition)} methods may throw * {@code NullPointerException}, if any of their parameter is {@code null}. * The subclass may provide its own implementation and specification about * {@code NullPointerException}. + *
      * - *

      - * The default implementation provides rounding modes defined + * Default RoundingMode + *

        + *
      • The default implementation provides rounding modes defined * in {@link java.math.RoundingMode} for formatting numbers. It * uses the {@linkplain java.math.RoundingMode#HALF_EVEN * round half-even algorithm}. To change the rounding mode use @@ -214,10 +227,14 @@ import sun.util.locale.provider.LocaleServiceProviderPool; * configured to round floating point numbers using half-even * rounding (see {@link java.math.RoundingMode#HALF_EVEN * RoundingMode.HALF_EVEN}) for formatting. + *
      * + * @spec https://www.unicode.org/reports/tr35 + * Unicode Locale Data Markup Language (LDML) * @see DecimalFormat * @see ChoiceFormat * @see CompactNumberFormat + * @see Locale * @author Mark Davis * @author Helena Shih * @since 1.1