mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
8220224: With CLDR provider, NumberFormat.format could not handle locale with number extension correctly
Reviewed-by: darcy
This commit is contained in:
parent
954c66afed
commit
dc3c9c8439
4 changed files with 606 additions and 284 deletions
|
@ -836,7 +836,8 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||||
if (ch == QUOTE) {
|
if (ch == QUOTE) {
|
||||||
ch = pattern.charAt(index++);
|
ch = pattern.charAt(index++);
|
||||||
if (ch == MINUS_SIGN) {
|
if (ch == MINUS_SIGN) {
|
||||||
ch = symbols.getMinusSign();
|
sb.append(symbols.getMinusSignText());
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.append(ch);
|
sb.append(ch);
|
||||||
|
@ -859,11 +860,14 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||||
if (ch == QUOTE) {
|
if (ch == QUOTE) {
|
||||||
ch = pattern.charAt(index++);
|
ch = pattern.charAt(index++);
|
||||||
if (ch == MINUS_SIGN) {
|
if (ch == MINUS_SIGN) {
|
||||||
ch = symbols.getMinusSign();
|
String minusText = symbols.getMinusSignText();
|
||||||
FieldPosition fp = new FieldPosition(NumberFormat.Field.SIGN);
|
FieldPosition fp = new FieldPosition(NumberFormat.Field.SIGN);
|
||||||
fp.setBeginIndex(stringIndex);
|
fp.setBeginIndex(stringIndex);
|
||||||
fp.setEndIndex(stringIndex + 1);
|
fp.setEndIndex(stringIndex + minusText.length());
|
||||||
positions.add(fp);
|
positions.add(fp);
|
||||||
|
stringIndex += minusText.length();
|
||||||
|
affix.append(minusText);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stringIndex++;
|
stringIndex++;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -38,12 +38,14 @@
|
||||||
|
|
||||||
package java.text;
|
package java.text;
|
||||||
|
|
||||||
|
import java.io.InvalidObjectException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.text.spi.DecimalFormatSymbolsProvider;
|
import java.text.spi.DecimalFormatSymbolsProvider;
|
||||||
import java.util.Currency;
|
import java.util.Currency;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
import sun.util.locale.provider.CalendarDataUtility;
|
import sun.util.locale.provider.CalendarDataUtility;
|
||||||
import sun.util.locale.provider.LocaleProviderAdapter;
|
import sun.util.locale.provider.LocaleProviderAdapter;
|
||||||
import sun.util.locale.provider.LocaleServiceProviderPool;
|
import sun.util.locale.provider.LocaleServiceProviderPool;
|
||||||
|
@ -51,11 +53,11 @@ import sun.util.locale.provider.ResourceBundleBasedAdapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents the set of symbols (such as the decimal separator,
|
* This class represents the set of symbols (such as the decimal separator,
|
||||||
* the grouping separator, and so on) needed by <code>DecimalFormat</code>
|
* the grouping separator, and so on) needed by {@code DecimalFormat}
|
||||||
* to format numbers. <code>DecimalFormat</code> creates for itself an instance of
|
* to format numbers. {@code DecimalFormat} creates for itself an instance of
|
||||||
* <code>DecimalFormatSymbols</code> from its locale data. If you need to change any
|
* {@code DecimalFormatSymbols} from its locale data. If you need to change any
|
||||||
* of these symbols, you can get the <code>DecimalFormatSymbols</code> object from
|
* of these symbols, you can get the {@code DecimalFormatSymbols} object from
|
||||||
* your <code>DecimalFormat</code> and modify it.
|
* your {@code DecimalFormat} and modify it.
|
||||||
*
|
*
|
||||||
* <p>If the locale contains "rg" (region override)
|
* <p>If the locale contains "rg" (region override)
|
||||||
* <a href="../util/Locale.html#def_locale_extension">Unicode extension</a>,
|
* <a href="../util/Locale.html#def_locale_extension">Unicode extension</a>,
|
||||||
|
@ -107,7 +109,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
* instead of the Latin numbering system.
|
* instead of the Latin numbering system.
|
||||||
*
|
*
|
||||||
* @param locale the desired locale
|
* @param locale the desired locale
|
||||||
* @exception NullPointerException if <code>locale</code> is null
|
* @exception NullPointerException if {@code locale} is null
|
||||||
*/
|
*/
|
||||||
public DecimalFormatSymbols( Locale locale ) {
|
public DecimalFormatSymbols( Locale locale ) {
|
||||||
initialize( locale );
|
initialize( locale );
|
||||||
|
@ -115,16 +117,16 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of all locales for which the
|
* Returns an array of all locales for which the
|
||||||
* <code>getInstance</code> methods of this class can return
|
* {@code getInstance} methods of this class can return
|
||||||
* localized instances.
|
* localized instances.
|
||||||
* The returned array represents the union of locales supported by the Java
|
* The returned array represents the union of locales supported by the Java
|
||||||
* runtime and by installed
|
* runtime and by installed
|
||||||
* {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
|
* {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
|
||||||
* implementations. It must contain at least a <code>Locale</code>
|
* implementations. It must contain at least a {@code Locale}
|
||||||
* instance equal to {@link java.util.Locale#US Locale.US}.
|
* instance equal to {@link java.util.Locale#US Locale.US}.
|
||||||
*
|
*
|
||||||
* @return an array of locales for which localized
|
* @return an array of locales for which localized
|
||||||
* <code>DecimalFormatSymbols</code> instances are available.
|
* {@code DecimalFormatSymbols} instances are available.
|
||||||
* @since 1.6
|
* @since 1.6
|
||||||
*/
|
*/
|
||||||
public static Locale[] getAvailableLocales() {
|
public static Locale[] getAvailableLocales() {
|
||||||
|
@ -134,8 +136,8 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the <code>DecimalFormatSymbols</code> instance for the default
|
* Gets the {@code DecimalFormatSymbols} instance for the default
|
||||||
* locale. This method provides access to <code>DecimalFormatSymbols</code>
|
* locale. This method provides access to {@code DecimalFormatSymbols}
|
||||||
* instances for locales supported by the Java runtime itself as well
|
* instances for locales supported by the Java runtime itself as well
|
||||||
* as for those supported by installed
|
* as for those supported by installed
|
||||||
* {@link java.text.spi.DecimalFormatSymbolsProvider
|
* {@link java.text.spi.DecimalFormatSymbolsProvider
|
||||||
|
@ -145,7 +147,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
* getInstance(Locale.getDefault(Locale.Category.FORMAT))}.
|
* getInstance(Locale.getDefault(Locale.Category.FORMAT))}.
|
||||||
* @see java.util.Locale#getDefault(java.util.Locale.Category)
|
* @see java.util.Locale#getDefault(java.util.Locale.Category)
|
||||||
* @see java.util.Locale.Category#FORMAT
|
* @see java.util.Locale.Category#FORMAT
|
||||||
* @return a <code>DecimalFormatSymbols</code> instance.
|
* @return a {@code DecimalFormatSymbols} instance.
|
||||||
* @since 1.6
|
* @since 1.6
|
||||||
*/
|
*/
|
||||||
public static final DecimalFormatSymbols getInstance() {
|
public static final DecimalFormatSymbols getInstance() {
|
||||||
|
@ -153,8 +155,8 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the <code>DecimalFormatSymbols</code> instance for the specified
|
* Gets the {@code DecimalFormatSymbols} instance for the specified
|
||||||
* locale. This method provides access to <code>DecimalFormatSymbols</code>
|
* locale. This method provides access to {@code DecimalFormatSymbols}
|
||||||
* instances for locales supported by the Java runtime itself as well
|
* instances for locales supported by the Java runtime itself as well
|
||||||
* as for those supported by installed
|
* as for those supported by installed
|
||||||
* {@link java.text.spi.DecimalFormatSymbolsProvider
|
* {@link java.text.spi.DecimalFormatSymbolsProvider
|
||||||
|
@ -169,8 +171,8 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
* instead of the Latin numbering system.
|
* instead of the Latin numbering system.
|
||||||
*
|
*
|
||||||
* @param locale the desired locale.
|
* @param locale the desired locale.
|
||||||
* @return a <code>DecimalFormatSymbols</code> instance.
|
* @return a {@code DecimalFormatSymbols} instance.
|
||||||
* @exception NullPointerException if <code>locale</code> is null
|
* @exception NullPointerException if {@code locale} is null
|
||||||
* @since 1.6
|
* @since 1.6
|
||||||
*/
|
*/
|
||||||
public static final DecimalFormatSymbols getInstance(Locale locale) {
|
public static final DecimalFormatSymbols getInstance(Locale locale) {
|
||||||
|
@ -255,6 +257,41 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
*/
|
*/
|
||||||
public void setPerMill(char perMill) {
|
public void setPerMill(char perMill) {
|
||||||
this.perMill = 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');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -273,6 +310,41 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
*/
|
*/
|
||||||
public void setPercent(char percent) {
|
public void setPercent(char percent) {
|
||||||
this.percent = 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, '%');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -373,6 +445,46 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
*/
|
*/
|
||||||
public void setMinusSign(char minusSign) {
|
public void setMinusSign(char minusSign) {
|
||||||
this.minusSign = 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, '-');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -464,7 +576,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
* symbol attribute to the currency's ISO 4217 currency code.
|
* symbol attribute to the currency's ISO 4217 currency code.
|
||||||
*
|
*
|
||||||
* @param currency the new currency to be used
|
* @param currency the new currency to be used
|
||||||
* @exception NullPointerException if <code>currency</code> is null
|
* @exception NullPointerException if {@code currency} is null
|
||||||
* @since 1.4
|
* @since 1.4
|
||||||
* @see #setCurrencySymbol
|
* @see #setCurrencySymbol
|
||||||
* @see #setInternationalCurrencySymbol
|
* @see #setInternationalCurrencySymbol
|
||||||
|
@ -540,7 +652,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
* Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
|
* Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
|
||||||
*
|
*
|
||||||
* @param exp the exponent separator string
|
* @param exp the exponent separator string
|
||||||
* @exception NullPointerException if <code>exp</code> is null
|
* @exception NullPointerException if {@code exp} is null
|
||||||
* @see #getExponentSeparator()
|
* @see #getExponentSeparator()
|
||||||
* @since 1.6
|
* @since 1.6
|
||||||
*/
|
*/
|
||||||
|
@ -583,9 +695,12 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
groupingSeparator == other.groupingSeparator &&
|
groupingSeparator == other.groupingSeparator &&
|
||||||
decimalSeparator == other.decimalSeparator &&
|
decimalSeparator == other.decimalSeparator &&
|
||||||
percent == other.percent &&
|
percent == other.percent &&
|
||||||
|
percentText.equals(other.percentText) &&
|
||||||
perMill == other.perMill &&
|
perMill == other.perMill &&
|
||||||
|
perMillText.equals(other.perMillText) &&
|
||||||
digit == other.digit &&
|
digit == other.digit &&
|
||||||
minusSign == other.minusSign &&
|
minusSign == other.minusSign &&
|
||||||
|
minusSignText.equals(other.minusSignText) &&
|
||||||
patternSeparator == other.patternSeparator &&
|
patternSeparator == other.patternSeparator &&
|
||||||
infinity.equals(other.infinity) &&
|
infinity.equals(other.infinity) &&
|
||||||
NaN.equals(other.NaN) &&
|
NaN.equals(other.NaN) &&
|
||||||
|
@ -631,13 +746,16 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
decimalSeparator = numberElements[0].charAt(0);
|
decimalSeparator = numberElements[0].charAt(0);
|
||||||
groupingSeparator = numberElements[1].charAt(0);
|
groupingSeparator = numberElements[1].charAt(0);
|
||||||
patternSeparator = numberElements[2].charAt(0);
|
patternSeparator = numberElements[2].charAt(0);
|
||||||
percent = numberElements[3].charAt(0);
|
percentText = numberElements[3];
|
||||||
|
percent = findNonFormatChar(percentText, '%');
|
||||||
zeroDigit = numberElements[4].charAt(0); //different for Arabic,etc.
|
zeroDigit = numberElements[4].charAt(0); //different for Arabic,etc.
|
||||||
digit = numberElements[5].charAt(0);
|
digit = numberElements[5].charAt(0);
|
||||||
minusSign = numberElements[6].charAt(0);
|
minusSignText = numberElements[6];
|
||||||
|
minusSign = findNonFormatChar(minusSignText, '-');
|
||||||
exponential = numberElements[7].charAt(0);
|
exponential = numberElements[7].charAt(0);
|
||||||
exponentialSeparator = numberElements[7]; //string representation new since 1.6
|
exponentialSeparator = numberElements[7]; //string representation new since 1.6
|
||||||
perMill = numberElements[8].charAt(0);
|
perMillText = numberElements[8];
|
||||||
|
perMill = findNonFormatChar(perMillText, '\u2030');
|
||||||
infinity = numberElements[9];
|
infinity = numberElements[9];
|
||||||
NaN = numberElements[10];
|
NaN = numberElements[10];
|
||||||
|
|
||||||
|
@ -651,6 +769,16 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
monetarySeparator = decimalSeparator;
|
monetarySeparator = decimalSeparator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains non-format single character from String
|
||||||
|
*/
|
||||||
|
private char findNonFormatChar(String src, char defChar) {
|
||||||
|
return (char)src.chars()
|
||||||
|
.filter(c -> Character.getType(c) != Character.FORMAT)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(defChar);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazy initialization for currency related fields
|
* Lazy initialization for currency related fields
|
||||||
*/
|
*/
|
||||||
|
@ -704,18 +832,24 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
/**
|
/**
|
||||||
* Reads the default serializable fields, provides default values for objects
|
* Reads the default serializable fields, provides default values for objects
|
||||||
* in older serial versions, and initializes non-serializable fields.
|
* in older serial versions, and initializes non-serializable fields.
|
||||||
* If <code>serialVersionOnStream</code>
|
* If {@code serialVersionOnStream}
|
||||||
* is less than 1, initializes <code>monetarySeparator</code> to be
|
* is less than 1, initializes {@code monetarySeparator} to be
|
||||||
* the same as <code>decimalSeparator</code> and <code>exponential</code>
|
* the same as {@code decimalSeparator} and {@code exponential}
|
||||||
* to be 'E'.
|
* to be 'E'.
|
||||||
* If <code>serialVersionOnStream</code> is less than 2,
|
* If {@code serialVersionOnStream} is less than 2,
|
||||||
* initializes <code>locale</code>to the root locale, and initializes
|
* initializes {@code locale}to the root locale, and initializes
|
||||||
* If <code>serialVersionOnStream</code> is less than 3, it initializes
|
* If {@code serialVersionOnStream} is less than 3, it initializes
|
||||||
* <code>exponentialSeparator</code> using <code>exponential</code>.
|
* {@code exponentialSeparator} using {@code exponential}.
|
||||||
* Sets <code>serialVersionOnStream</code> back to the maximum allowed value so that
|
* 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.
|
||||||
|
* Sets {@code serialVersionOnStream} back to the maximum allowed value so that
|
||||||
* default serialization will work properly if this object is streamed out again.
|
* default serialization will work properly if this object is streamed out again.
|
||||||
* Initializes the currency from the intlCurrencySymbol field.
|
* Initializes the currency from the intlCurrencySymbol field.
|
||||||
*
|
*
|
||||||
|
* @throws InvalidObjectException if {@code char} and {@code String}
|
||||||
|
* representations of either percent, per mille, and/or minus sign disagree.
|
||||||
* @since 1.1.6
|
* @since 1.1.6
|
||||||
*/
|
*/
|
||||||
private void readObject(ObjectInputStream stream)
|
private void readObject(ObjectInputStream stream)
|
||||||
|
@ -735,6 +869,23 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
// didn't have exponentialSeparator. Create one using exponential
|
// didn't have exponentialSeparator. Create one using exponential
|
||||||
exponentialSeparator = Character.toString(exponential);
|
exponentialSeparator = Character.toString(exponential);
|
||||||
}
|
}
|
||||||
|
if (serialVersionOnStream < 4) {
|
||||||
|
// didn't have perMillText, percentText, and minusSignText.
|
||||||
|
// Create one using corresponding char variations.
|
||||||
|
perMillText = Character.toString(perMill);
|
||||||
|
percentText = Character.toString(percent);
|
||||||
|
minusSignText = Character.toString(minusSign);
|
||||||
|
} else {
|
||||||
|
// Check whether char and text fields agree
|
||||||
|
if (findNonFormatChar(perMillText, '\uFFFF') != perMill ||
|
||||||
|
findNonFormatChar(percentText, '\uFFFF') != percent ||
|
||||||
|
findNonFormatChar(minusSignText, '\uFFFF') != minusSign) {
|
||||||
|
throw new InvalidObjectException(
|
||||||
|
"'char' and 'String' representations of either percent, " +
|
||||||
|
"per mille, and/or minus sign disagree.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
serialVersionOnStream = currentSerialVersion;
|
serialVersionOnStream = currentSerialVersion;
|
||||||
|
|
||||||
if (intlCurrencySymbol != null) {
|
if (intlCurrencySymbol != null) {
|
||||||
|
@ -862,8 +1013,8 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
* The string used to separate the mantissa from the exponent.
|
* The string used to separate the mantissa from the exponent.
|
||||||
* Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
|
* Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
|
||||||
* <p>
|
* <p>
|
||||||
* If both <code>exponential</code> and <code>exponentialSeparator</code>
|
* If both {@code exponential} and {@code exponentialSeparator}
|
||||||
* exist, this <code>exponentialSeparator</code> has the precedence.
|
* exist, this {@code exponentialSeparator} has the precedence.
|
||||||
*
|
*
|
||||||
* @serial
|
* @serial
|
||||||
* @since 1.6
|
* @since 1.6
|
||||||
|
@ -878,6 +1029,39 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
*/
|
*/
|
||||||
private Locale locale;
|
private Locale locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String representation of per mille sign, which may include
|
||||||
|
* formatting characters, such as BiDi control characters.
|
||||||
|
* The first non-format character of this string is the same as
|
||||||
|
* {@code perMill}.
|
||||||
|
*
|
||||||
|
* @serial
|
||||||
|
* @since 13
|
||||||
|
*/
|
||||||
|
private String perMillText;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String representation of percent sign, which may include
|
||||||
|
* formatting characters, such as BiDi control characters.
|
||||||
|
* The first non-format character of this string is the same as
|
||||||
|
* {@code percent}.
|
||||||
|
*
|
||||||
|
* @serial
|
||||||
|
* @since 13
|
||||||
|
*/
|
||||||
|
private String percentText;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String representation of minus sign, which may include
|
||||||
|
* formatting characters, such as BiDi control characters.
|
||||||
|
* The first non-format character of this string is the same as
|
||||||
|
* {@code minusSign}.
|
||||||
|
*
|
||||||
|
* @serial
|
||||||
|
* @since 13
|
||||||
|
*/
|
||||||
|
private String minusSignText;
|
||||||
|
|
||||||
// currency; only the ISO code is serialized.
|
// currency; only the ISO code is serialized.
|
||||||
private transient Currency currency;
|
private transient Currency currency;
|
||||||
private transient volatile boolean currencyInitialized;
|
private transient volatile boolean currencyInitialized;
|
||||||
|
@ -891,23 +1075,28 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||||
// monetarySeparator and exponential.
|
// monetarySeparator and exponential.
|
||||||
// - 2 for version from J2SE 1.4, which includes locale field.
|
// - 2 for version from J2SE 1.4, which includes locale field.
|
||||||
// - 3 for version from J2SE 1.6, which includes exponentialSeparator field.
|
// - 3 for version from J2SE 1.6, which includes exponentialSeparator field.
|
||||||
private static final int currentSerialVersion = 3;
|
// - 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</code> present on the stream.
|
* Describes the version of {@code DecimalFormatSymbols} present on the stream.
|
||||||
* Possible values are:
|
* Possible values are:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li><b>0</b> (or uninitialized): versions prior to JDK 1.1.6.
|
* <li><b>0</b> (or uninitialized): versions prior to JDK 1.1.6.
|
||||||
*
|
*
|
||||||
* <li><b>1</b>: Versions written by JDK 1.1.6 or later, which include
|
* <li><b>1</b>: Versions written by JDK 1.1.6 or later, which include
|
||||||
* two new fields: <code>monetarySeparator</code> and <code>exponential</code>.
|
* two new fields: {@code monetarySeparator} and {@code exponential}.
|
||||||
* <li><b>2</b>: Versions written by J2SE 1.4 or later, which include a
|
* <li><b>2</b>: Versions written by J2SE 1.4 or later, which include a
|
||||||
* new <code>locale</code> field.
|
* new {@code locale} field.
|
||||||
* <li><b>3</b>: Versions written by J2SE 1.6 or later, which include a
|
* <li><b>3</b>: Versions written by J2SE 1.6 or later, which include a
|
||||||
* new <code>exponentialSeparator</code> field.
|
* 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>
|
* </ul>
|
||||||
* When streaming out a <code>DecimalFormatSymbols</code>, the most recent format
|
* When streaming out a {@code DecimalFormatSymbols}, the most recent format
|
||||||
* (corresponding to the highest allowable <code>serialVersionOnStream</code>)
|
* (corresponding to the highest allowable {@code serialVersionOnStream})
|
||||||
* is always written.
|
* is always written.
|
||||||
*
|
*
|
||||||
* @serial
|
* @serial
|
||||||
|
|
139
test/jdk/java/text/Format/NumberFormat/DFSMinusPerCentMill.java
Normal file
139
test/jdk/java/text/Format/NumberFormat/DFSMinusPerCentMill.java
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @bug 8220309
|
||||||
|
* @library /java/text/testlib
|
||||||
|
* @summary Test String representation of MinusSign/Percent/PerMill symbols.
|
||||||
|
* This test assumes CLDR has numbering systems for "arab" and
|
||||||
|
* "arabext", and their minus/percent representations include
|
||||||
|
* BiDi formatting control characters.
|
||||||
|
* @run testng/othervm DFSMinusPerCentMill
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.text.*;
|
||||||
|
|
||||||
|
import static org.testng.Assert.*;
|
||||||
|
import org.testng.annotations.DataProvider;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
public class DFSMinusPerCentMill {
|
||||||
|
private enum Type {
|
||||||
|
NUMBER, PERCENT, CURRENCY, INTEGER, COMPACT, PERMILL
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Locale US_ARAB = Locale.forLanguageTag("en-US-u-nu-arab");
|
||||||
|
private static final Locale US_ARABEXT = Locale.forLanguageTag("en-US-u-nu-arabext");
|
||||||
|
private static final double SRC_NUM = -1234.56;
|
||||||
|
|
||||||
|
@DataProvider
|
||||||
|
Object[][] formatData() {
|
||||||
|
return new Object[][] {
|
||||||
|
// Locale, FormatStyle, expected format, expected single char symbol
|
||||||
|
{US_ARAB, Type.NUMBER, "\u061c-\u0661\u066c\u0662\u0663\u0664\u066b\u0665\u0666"},
|
||||||
|
{US_ARAB, Type.PERCENT, "\u061c-\u0661\u0662\u0663\u066c\u0664\u0665\u0666\u066a\u061c"},
|
||||||
|
{US_ARAB, Type.CURRENCY, "\u061c-$\u0661\u066c\u0662\u0663\u0664\u066b\u0665\u0666"},
|
||||||
|
{US_ARAB, Type.INTEGER, "\u061c-\u0661\u066c\u0662\u0663\u0665"},
|
||||||
|
{US_ARAB, Type.COMPACT, "\u061c-\u0661K"},
|
||||||
|
{US_ARAB, Type.PERMILL, "\u061c-\u0661\u0662\u0663\u0664\u0665\u0666\u0660\u0609"},
|
||||||
|
|
||||||
|
{US_ARABEXT, Type.NUMBER, "\u200e-\u200e\u06f1\u066c\u06f2\u06f3\u06f4\u066b\u06f5\u06f6"},
|
||||||
|
{US_ARABEXT, Type.PERCENT, "\u200e-\u200e\u06f1\u06f2\u06f3\u066c\u06f4\u06f5\u06f6\u066a"},
|
||||||
|
{US_ARABEXT, Type.CURRENCY, "\u200e-\u200e$\u06f1\u066c\u06f2\u06f3\u06f4\u066b\u06f5\u06f6"},
|
||||||
|
{US_ARABEXT, Type.INTEGER, "\u200e-\u200e\u06f1\u066c\u06f2\u06f3\u06f5"},
|
||||||
|
{US_ARABEXT, Type.COMPACT, "\u200e-\u200e\u06f1K"},
|
||||||
|
{US_ARABEXT, Type.PERMILL, "\u200e-\u200e\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f0\u0609"},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@DataProvider
|
||||||
|
Object[][] charSymbols() {
|
||||||
|
return new Object[][]{
|
||||||
|
// Locale, percent, per mille, minus sign
|
||||||
|
{US_ARAB, '\u066a', '\u0609', '-'},
|
||||||
|
{US_ARABEXT, '\u066a', '\u0609', '-'},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(dataProvider="formatData")
|
||||||
|
public void testFormatData(Locale l, Type style, String expected) {
|
||||||
|
NumberFormat nf = null;
|
||||||
|
switch (style) {
|
||||||
|
case NUMBER:
|
||||||
|
nf = NumberFormat.getNumberInstance(l);
|
||||||
|
break;
|
||||||
|
case PERCENT:
|
||||||
|
nf = NumberFormat.getPercentInstance(l);
|
||||||
|
break;
|
||||||
|
case CURRENCY:
|
||||||
|
nf = NumberFormat.getCurrencyInstance(l);
|
||||||
|
break;
|
||||||
|
case INTEGER:
|
||||||
|
nf = NumberFormat.getIntegerInstance(l);
|
||||||
|
break;
|
||||||
|
case COMPACT:
|
||||||
|
nf = NumberFormat.getCompactNumberInstance(l, NumberFormat.Style.SHORT);
|
||||||
|
break;
|
||||||
|
case PERMILL:
|
||||||
|
nf = new DecimalFormat("#.#\u2030", DecimalFormatSymbols.getInstance(l));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(nf.format(SRC_NUM), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(dataProvider="charSymbols")
|
||||||
|
public void testCharSymbols(Locale l, char percent, char permill, char minus) {
|
||||||
|
DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
|
||||||
|
assertEquals(dfs.getPercent(), percent);
|
||||||
|
assertEquals(dfs.getPerMill(), permill);
|
||||||
|
assertEquals(dfs.getMinusSign(), minus);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSerialization() throws Exception {
|
||||||
|
DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance();
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
new ObjectOutputStream(bos).writeObject(dfs);
|
||||||
|
DecimalFormatSymbols dfsSerialized = (DecimalFormatSymbols)new ObjectInputStream(
|
||||||
|
new ByteArrayInputStream(bos.toByteArray())
|
||||||
|
).readObject();
|
||||||
|
|
||||||
|
assertEquals(dfs, dfsSerialized);
|
||||||
|
|
||||||
|
// set minus/percent/permille
|
||||||
|
dfs.setMinusSign('a');
|
||||||
|
dfs.setPercent('b');
|
||||||
|
dfs.setPerMill('c');
|
||||||
|
bos = new ByteArrayOutputStream();
|
||||||
|
new ObjectOutputStream(bos).writeObject(dfs);
|
||||||
|
dfsSerialized = (DecimalFormatSymbols)new ObjectInputStream(
|
||||||
|
new ByteArrayInputStream(bos.toByteArray())
|
||||||
|
).readObject();
|
||||||
|
|
||||||
|
assertEquals(dfs, dfsSerialized);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue