< 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 +1,7 @@
/*
- * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
+ * 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,28 +200,30 @@
* 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 thousands separator. Different for French, etc.
+ * 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 thousands separator. Different for French, etc.
+ * 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,10 +238,11 @@
* 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,49 +257,16 @@
* 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 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() {
@@ -307,49 +277,16 @@
* 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 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() {
@@ -360,10 +297,11 @@
* 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,10 +318,11 @@
* 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,10 +339,11 @@
* 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,10 +360,11 @@
* 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,54 +383,16 @@
* 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);
}
/**
- * 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
@@ -508,10 +411,11 @@
* @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,10 +447,11 @@
* @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,10 +489,11 @@
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,25 +515,14 @@
* @param sep the monetary decimal separator
* @since 1.2
*/
public void setMonetaryDecimalSeparator(char sep)
{
+ hashCode = 0;
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
@@ -638,18 +533,10 @@
{
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
@@ -659,13 +546,170 @@
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,23 +750,45 @@
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() {
- int result = zeroDigit;
- result = result * 37 + groupingSeparator;
- result = result * 37 + decimalSeparator;
- return result;
+ 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,18 +823,19 @@
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];
-
- // 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
*/
@@ -842,10 +909,12 @@
* {@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,10 +953,14 @@
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,11 +978,11 @@
* @see #getZeroDigit
*/
private char zeroDigit;
/**
- * Character used for thousands separator.
+ * Character used for grouping separator.
*
* @serial
* @see #getGroupingSeparator
*/
private char groupingSeparator;
@@ -1061,10 +1134,18 @@
* @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,11 +1158,12 @@
// 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;
+ // - 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,11 +1176,13 @@
* <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>
+ * <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 >