< prev index next >

src/java.base/share/classes/java/text/DecimalFormatSymbols.java

Print this page
rev 57525 : 8227313: Support monetary grouping separator in DecimalFormat/DecimalFormatSymbols
Reviewed-by: joehw

*** 1,7 **** /* ! * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this --- 1,7 ---- /* ! * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this
*** 200,227 **** * Sets the character used for zero. Different for Arabic, etc. * * @param zeroDigit the character used for zero */ public void setZeroDigit(char zeroDigit) { this.zeroDigit = zeroDigit; } /** ! * Gets the character used for thousands separator. Different for French, etc. * * @return the grouping separator */ public char getGroupingSeparator() { return groupingSeparator; } /** ! * Sets the character used for thousands separator. Different for French, etc. * * @param groupingSeparator the grouping separator */ public void setGroupingSeparator(char groupingSeparator) { this.groupingSeparator = groupingSeparator; } /** * Gets the character used for decimal sign. Different for French, etc. --- 200,229 ---- * Sets the character used for zero. Different for Arabic, etc. * * @param zeroDigit the character used for zero */ public void setZeroDigit(char zeroDigit) { + hashCode = 0; this.zeroDigit = zeroDigit; } /** ! * Gets the character used for grouping separator. Different for French, etc. * * @return the grouping separator */ public char getGroupingSeparator() { return groupingSeparator; } /** ! * Sets the character used for grouping separator. Different for French, etc. * * @param groupingSeparator the grouping separator */ public void setGroupingSeparator(char groupingSeparator) { + hashCode = 0; this.groupingSeparator = groupingSeparator; } /** * Gets the character used for decimal sign. Different for French, etc.
*** 236,245 **** --- 238,248 ---- * Sets the character used for decimal sign. Different for French, etc. * * @param decimalSeparator the character used for decimal sign */ public void setDecimalSeparator(char decimalSeparator) { + hashCode = 0; this.decimalSeparator = decimalSeparator; } /** * Gets the character used for per mille sign. Different for Arabic, etc.
*** 254,302 **** * Sets the character used for per mille sign. Different for Arabic, etc. * * @param perMill the character used for per mille sign */ public void setPerMill(char perMill) { this.perMill = perMill; this.perMillText = Character.toString(perMill); } /** - * Gets the string used for per mille sign. Different for Arabic, etc. - * - * @return the string used for per mille sign - * @since 13 - */ - String getPerMillText() { - return perMillText; - } - - /** - * Sets the string used for per mille sign. Different for Arabic, etc. - * - * Setting the {@code perMillText} affects the return value of - * {@link #getPerMill()}, in which the first non-format character of - * {@code perMillText} is returned. - * - * @param perMillText the string used for per mille sign - * @throws NullPointerException if {@code perMillText} is null - * @throws IllegalArgumentException if {@code perMillText} is an empty string - * @see #getPerMill() - * @see #getPerMillText() - * @since 13 - */ - void setPerMillText(String perMillText) { - Objects.requireNonNull(perMillText); - if (perMillText.isEmpty()) { - throw new IllegalArgumentException("Empty argument string"); - } - - this.perMillText = perMillText; - this.perMill = findNonFormatChar(perMillText, '\u2030'); - } - - /** * Gets the character used for percent sign. Different for Arabic, etc. * * @return the character used for percent sign */ public char getPercent() { --- 257,272 ---- * Sets the character used for per mille sign. Different for Arabic, etc. * * @param perMill the character used for per mille sign */ public void setPerMill(char perMill) { + hashCode = 0; this.perMill = perMill; this.perMillText = Character.toString(perMill); } /** * Gets the character used for percent sign. Different for Arabic, etc. * * @return the character used for percent sign */ public char getPercent() {
*** 307,355 **** * Sets the character used for percent sign. Different for Arabic, etc. * * @param percent the character used for percent sign */ public void setPercent(char percent) { this.percent = percent; this.percentText = Character.toString(percent); } /** - * Gets the string used for percent sign. Different for Arabic, etc. - * - * @return the string used for percent sign - * @since 13 - */ - String getPercentText() { - return percentText; - } - - /** - * Sets the string used for percent sign. Different for Arabic, etc. - * - * Setting the {@code percentText} affects the return value of - * {@link #getPercent()}, in which the first non-format character of - * {@code percentText} is returned. - * - * @param percentText the string used for percent sign - * @throws NullPointerException if {@code percentText} is null - * @throws IllegalArgumentException if {@code percentText} is an empty string - * @see #getPercent() - * @see #getPercentText() - * @since 13 - */ - void setPercentText(String percentText) { - Objects.requireNonNull(percentText); - if (percentText.isEmpty()) { - throw new IllegalArgumentException("Empty argument string"); - } - - this.percentText = percentText; - this.percent = findNonFormatChar(percentText, '%'); - } - - /** * Gets the character used for a digit in a pattern. * * @return the character used for a digit in a pattern */ public char getDigit() { --- 277,292 ---- * Sets the character used for percent sign. Different for Arabic, etc. * * @param percent the character used for percent sign */ public void setPercent(char percent) { + hashCode = 0; this.percent = percent; this.percentText = Character.toString(percent); } /** * Gets the character used for a digit in a pattern. * * @return the character used for a digit in a pattern */ public char getDigit() {
*** 360,369 **** --- 297,307 ---- * Sets the character used for a digit in a pattern. * * @param digit the character used for a digit in a pattern */ public void setDigit(char digit) { + hashCode = 0; this.digit = digit; } /** * Gets the character used to separate positive and negative subpatterns
*** 380,389 **** --- 318,328 ---- * in a pattern. * * @param patternSeparator the pattern separator */ public void setPatternSeparator(char patternSeparator) { + hashCode = 0; this.patternSeparator = patternSeparator; } /** * Gets the string used to represent infinity. Almost always left
*** 400,409 **** --- 339,349 ---- * unchanged. * * @param infinity the string representing infinity */ public void setInfinity(String infinity) { + hashCode = 0; this.infinity = infinity; } /** * Gets the string used to represent "not a number". Almost always left
*** 420,429 **** --- 360,370 ---- * unchanged. * * @param NaN the string representing "not a number" */ public void setNaN(String NaN) { + hashCode = 0; this.NaN = NaN; } /** * Gets the character used to represent minus sign. If no explicit
*** 442,495 **** * minusSign to the positive format. * * @param minusSign the character representing minus sign */ public void setMinusSign(char minusSign) { this.minusSign = minusSign; this.minusSignText = Character.toString(minusSign); } /** - * Gets the string used to represent minus sign. If no explicit - * negative format is specified, one is formed by prefixing - * minusSignText to the positive format. - * - * @return the string representing minus sign - * @since 13 - */ - String getMinusSignText() { - return minusSignText; - } - - /** - * Sets the string used to represent minus sign. If no explicit - * negative format is specified, one is formed by prefixing - * minusSignText to the positive format. - * - * Setting the {@code minusSignText} affects the return value of - * {@link #getMinusSign()}, in which the first non-format character of - * {@code minusSignText} is returned. - * - * @param minusSignText the character representing minus sign - * @throws NullPointerException if {@code minusSignText} is null - * @throws IllegalArgumentException if {@code minusSignText} is an - * empty string - * @see #getMinusSign() - * @see #getMinusSignText() - * @since 13 - */ - void setMinusSignText(String minusSignText) { - Objects.requireNonNull(minusSignText); - if (minusSignText.isEmpty()) { - throw new IllegalArgumentException("Empty argument string"); - } - - this.minusSignText = minusSignText; - this.minusSign = findNonFormatChar(minusSignText, '-'); - } - - /** * Returns the currency symbol for the currency of these * DecimalFormatSymbols in their locale. * * @return the currency symbol * @since 1.2 --- 383,398 ---- * minusSign to the positive format. * * @param minusSign the character representing minus sign */ public void setMinusSign(char minusSign) { + hashCode = 0; this.minusSign = minusSign; this.minusSignText = Character.toString(minusSign); } /** * Returns the currency symbol for the currency of these * DecimalFormatSymbols in their locale. * * @return the currency symbol * @since 1.2
*** 508,517 **** --- 411,421 ---- * @since 1.2 */ public void setCurrencySymbol(String currency) { initializeCurrency(locale); + hashCode = 0; currencySymbol = currency; } /** * Returns the ISO 4217 currency code of the currency of these
*** 543,552 **** --- 447,457 ---- * @since 1.2 */ public void setInternationalCurrencySymbol(String currencyCode) { initializeCurrency(locale); + hashCode = 0; intlCurrencySymbol = currencyCode; currency = null; if (currencyCode != null) { try { currency = Currency.getInstance(currencyCode);
*** 584,593 **** --- 489,499 ---- public void setCurrency(Currency currency) { if (currency == null) { throw new NullPointerException(); } initializeCurrency(locale); + hashCode = 0; this.currency = currency; intlCurrencySymbol = currency.getCurrencyCode(); currencySymbol = currency.getSymbol(locale); }
*** 609,633 **** * @param sep the monetary decimal separator * @since 1.2 */ public void setMonetaryDecimalSeparator(char sep) { monetarySeparator = sep; } - //------------------------------------------------------------ - // BEGIN Package Private methods ... to be made public later - //------------------------------------------------------------ - - /** - * Returns the character used to separate the mantissa from the exponent. - */ - char getExponentialSymbol() - { - return exponential; - } - /** * Returns the string used to separate the mantissa from the exponent. * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4. * * @return the exponent separator string --- 515,528 ---- * @param sep the monetary decimal separator * @since 1.2 */ public void setMonetaryDecimalSeparator(char sep) { + hashCode = 0; monetarySeparator = sep; } /** * Returns the string used to separate the mantissa from the exponent. * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4. * * @return the exponent separator string
*** 638,655 **** { return exponentialSeparator; } /** - * Sets the character used to separate the mantissa from the exponent. - */ - void setExponentialSymbol(char exp) - { - exponential = exp; - } - - /** * Sets the string used to separate the mantissa from the exponent. * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4. * * @param exp the exponent separator string * @throws NullPointerException if {@code exp} is null --- 533,542 ----
*** 659,671 **** --- 546,715 ---- public void setExponentSeparator(String exp) { if (exp == null) { throw new NullPointerException(); } + hashCode = 0; exponentialSeparator = exp; } + /** + * Gets the character used for grouping separator for currencies. + * May be different from {@code grouping separator} in some locales, + * e.g, German in Austria. + * + * @return the monetary grouping separator + * @since 15 + */ + public char getMonetaryGroupingSeparator() { + return monetaryGroupingSeparator; + } + + /** + * Sets the character used for grouping separator for currencies. + * Invocation of this method will not affect the normal + * {@code grouping separator}. + * + * @param monetaryGroupingSeparator the monetary grouping separator + * @see #setGroupingSeparator(char) + * @since 15 + */ + public void setMonetaryGroupingSeparator(char monetaryGroupingSeparator) + { + hashCode = 0; + this.monetaryGroupingSeparator = monetaryGroupingSeparator; + } + + //------------------------------------------------------------ + // BEGIN Package Private methods ... to be made public later + //------------------------------------------------------------ + + /** + * Returns the character used to separate the mantissa from the exponent. + */ + char getExponentialSymbol() + { + return exponential; + } + + /** + * Sets the character used to separate the mantissa from the exponent. + */ + void setExponentialSymbol(char exp) + { + exponential = exp; + } + + /** + * Gets the string used for per mille sign. Different for Arabic, etc. + * + * @return the string used for per mille sign + * @since 13 + */ + String getPerMillText() { + return perMillText; + } + + /** + * Sets the string used for per mille sign. Different for Arabic, etc. + * + * Setting the {@code perMillText} affects the return value of + * {@link #getPerMill()}, in which the first non-format character of + * {@code perMillText} is returned. + * + * @param perMillText the string used for per mille sign + * @throws NullPointerException if {@code perMillText} is null + * @throws IllegalArgumentException if {@code perMillText} is an empty string + * @see #getPerMill() + * @see #getPerMillText() + * @since 13 + */ + void setPerMillText(String perMillText) { + Objects.requireNonNull(perMillText); + if (perMillText.isEmpty()) { + throw new IllegalArgumentException("Empty argument string"); + } + + hashCode = 0; + this.perMillText = perMillText; + this.perMill = findNonFormatChar(perMillText, '\u2030'); + } + + /** + * Gets the string used for percent sign. Different for Arabic, etc. + * + * @return the string used for percent sign + * @since 13 + */ + String getPercentText() { + return percentText; + } + + /** + * Sets the string used for percent sign. Different for Arabic, etc. + * + * Setting the {@code percentText} affects the return value of + * {@link #getPercent()}, in which the first non-format character of + * {@code percentText} is returned. + * + * @param percentText the string used for percent sign + * @throws NullPointerException if {@code percentText} is null + * @throws IllegalArgumentException if {@code percentText} is an empty string + * @see #getPercent() + * @see #getPercentText() + * @since 13 + */ + void setPercentText(String percentText) { + Objects.requireNonNull(percentText); + if (percentText.isEmpty()) { + throw new IllegalArgumentException("Empty argument string"); + } + + hashCode = 0; + this.percentText = percentText; + this.percent = findNonFormatChar(percentText, '%'); + } + + /** + * Gets the string used to represent minus sign. If no explicit + * negative format is specified, one is formed by prefixing + * minusSignText to the positive format. + * + * @return the string representing minus sign + * @since 13 + */ + String getMinusSignText() { + return minusSignText; + } + + /** + * Sets the string used to represent minus sign. If no explicit + * negative format is specified, one is formed by prefixing + * minusSignText to the positive format. + * + * Setting the {@code minusSignText} affects the return value of + * {@link #getMinusSign()}, in which the first non-format character of + * {@code minusSignText} is returned. + * + * @param minusSignText the character representing minus sign + * @throws NullPointerException if {@code minusSignText} is null + * @throws IllegalArgumentException if {@code minusSignText} is an + * empty string + * @see #getMinusSign() + * @see #getMinusSignText() + * @since 13 + */ + void setMinusSignText(String minusSignText) { + Objects.requireNonNull(minusSignText); + if (minusSignText.isEmpty()) { + throw new IllegalArgumentException("Empty argument string"); + } + + hashCode = 0; + this.minusSignText = minusSignText; + this.minusSign = findNonFormatChar(minusSignText, '-'); + } //------------------------------------------------------------ // END Package Private methods ... to be made public later //------------------------------------------------------------
*** 706,728 **** NaN.equals(other.NaN) && getCurrencySymbol().equals(other.getCurrencySymbol()) && // possible currency init occurs here intlCurrencySymbol.equals(other.intlCurrencySymbol) && currency == other.currency && monetarySeparator == other.monetarySeparator && exponentialSeparator.equals(other.exponentialSeparator) && locale.equals(other.locale)); } /** * Override hashCode. */ @Override public int hashCode() { ! int result = zeroDigit; ! result = result * 37 + groupingSeparator; ! result = result * 37 + decimalSeparator; ! return result; } /** * Initializes the symbols from the FormatData resource bundle. */ --- 750,794 ---- NaN.equals(other.NaN) && getCurrencySymbol().equals(other.getCurrencySymbol()) && // possible currency init occurs here intlCurrencySymbol.equals(other.intlCurrencySymbol) && currency == other.currency && monetarySeparator == other.monetarySeparator && + monetaryGroupingSeparator == other.monetaryGroupingSeparator && exponentialSeparator.equals(other.exponentialSeparator) && locale.equals(other.locale)); } /** * Override hashCode. */ + private volatile int hashCode; @Override public int hashCode() { ! if (hashCode == 0) { ! hashCode = Objects.hash( ! zeroDigit, ! groupingSeparator, ! decimalSeparator, ! percent, ! percentText, ! perMill, ! perMillText, ! digit, ! minusSign, ! minusSignText, ! patternSeparator, ! infinity, ! NaN, ! getCurrencySymbol(), // possible currency init occurs here ! intlCurrencySymbol, ! currency, ! monetarySeparator, ! monetaryGroupingSeparator, ! exponentialSeparator, ! locale); ! } ! return hashCode; } /** * Initializes the symbols from the FormatData resource bundle. */
*** 757,774 **** perMillText = numberElements[8]; perMill = findNonFormatChar(perMillText, '\u2030'); infinity = numberElements[9]; NaN = numberElements[10]; // maybe filled with previously cached values, or null. intlCurrencySymbol = (String) data[1]; currencySymbol = (String) data[2]; - - // Currently the monetary decimal separator is the same as the - // standard decimal separator for all locales that we support. - // If that changes, add a new entry to NumberElements. - monetarySeparator = decimalSeparator; } /** * Obtains non-format single character from String */ --- 823,841 ---- perMillText = numberElements[8]; perMill = findNonFormatChar(perMillText, '\u2030'); infinity = numberElements[9]; NaN = numberElements[10]; + // monetary decimal/grouping separators may be missing in resource bundles + monetarySeparator = numberElements.length < 12 || numberElements[11].isEmpty() ? + decimalSeparator : numberElements[11].charAt(0); + monetaryGroupingSeparator = numberElements.length < 13 || numberElements[12].isEmpty() ? + groupingSeparator : numberElements[12].charAt(0); + // maybe filled with previously cached values, or null. intlCurrencySymbol = (String) data[1]; currencySymbol = (String) data[2]; } /** * Obtains non-format single character from String */
*** 842,851 **** --- 909,920 ---- * {@code exponentialSeparator} using {@code exponential}. * If {@code serialVersionOnStream} is less than 4, it initializes * {@code perMillText}, {@code percentText}, and * {@code minusSignText} using {@code perMill}, {@code percent}, and * {@code minusSign} respectively. + * If {@code serialVersionOnStream} is less than 5, it initializes + * {@code monetaryGroupingSeparator} using {@code groupingSeparator}. * Sets {@code serialVersionOnStream} back to the maximum allowed value so that * default serialization will work properly if this object is streamed out again. * Initializes the currency from the intlCurrencySymbol field. * * @throws InvalidObjectException if {@code char} and {@code String}
*** 884,893 **** --- 953,966 ---- throw new InvalidObjectException( "'char' and 'String' representations of either percent, " + "per mille, and/or minus sign disagree."); } } + if (serialVersionOnStream < 5) { + // didn't have monetaryGroupingSeparator. Create one using groupingSeparator + monetaryGroupingSeparator = groupingSeparator; + } serialVersionOnStream = currentSerialVersion; if (intlCurrencySymbol != null) { try {
*** 905,915 **** * @see #getZeroDigit */ private char zeroDigit; /** ! * Character used for thousands separator. * * @serial * @see #getGroupingSeparator */ private char groupingSeparator; --- 978,988 ---- * @see #getZeroDigit */ private char zeroDigit; /** ! * Character used for grouping separator. * * @serial * @see #getGroupingSeparator */ private char groupingSeparator;
*** 1061,1070 **** --- 1134,1151 ---- * @serial * @since 13 */ private String minusSignText; + /** + * The grouping separator used when formatting currency values. + * + * @serial + * @since 15 + */ + private char monetaryGroupingSeparator; + // currency; only the ISO code is serialized. private transient Currency currency; private transient volatile boolean currencyInitialized; // Proclaim JDK 1.1 FCS compatibility
*** 1077,1087 **** // monetarySeparator and exponential. // - 2 for version from J2SE 1.4, which includes locale field. // - 3 for version from J2SE 1.6, which includes exponentialSeparator field. // - 4 for version from Java SE 13, which includes perMillText, percentText, // and minusSignText field. ! private static final int currentSerialVersion = 4; /** * Describes the version of {@code DecimalFormatSymbols} present on the stream. * Possible values are: * <ul> --- 1158,1169 ---- // monetarySeparator and exponential. // - 2 for version from J2SE 1.4, which includes locale field. // - 3 for version from J2SE 1.6, which includes exponentialSeparator field. // - 4 for version from Java SE 13, which includes perMillText, percentText, // and minusSignText field. ! // - 5 for version from Java SE 15, which includes monetaryGroupingSeparator. ! private static final int currentSerialVersion = 5; /** * Describes the version of {@code DecimalFormatSymbols} present on the stream. * Possible values are: * <ul>
*** 1094,1104 **** * <li><b>3</b>: Versions written by J2SE 1.6 or later, which include a * new {@code exponentialSeparator} field. * <li><b>4</b>: Versions written by Java SE 13 or later, which include * new {@code perMillText}, {@code percentText}, and * {@code minusSignText} field. ! * </ul> * When streaming out a {@code DecimalFormatSymbols}, the most recent format * (corresponding to the highest allowable {@code serialVersionOnStream}) * is always written. * * @serial --- 1176,1188 ---- * <li><b>3</b>: Versions written by J2SE 1.6 or later, which include a * new {@code exponentialSeparator} field. * <li><b>4</b>: Versions written by Java SE 13 or later, which include * new {@code perMillText}, {@code percentText}, and * {@code minusSignText} field. ! * <li><b>5</b>: Versions written by Java SE 15 or later, which include ! * new {@code monetaryGroupingSeparator} field. ! * * </ul> * When streaming out a {@code DecimalFormatSymbols}, the most recent format * (corresponding to the highest allowable {@code serialVersionOnStream}) * is always written. * * @serial
< prev index next >