8230284: Accounting currency format support does not cope with explicit number system
Reviewed-by: rriggs
This commit is contained in:
parent
6794a68681
commit
ea0fbbca51
@ -33,6 +33,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
class Bundle {
|
||||
static enum Type {
|
||||
@ -213,27 +214,16 @@ class Bundle {
|
||||
|
||||
// merge individual strings into arrays
|
||||
|
||||
// if myMap has any of the NumberPatterns members
|
||||
for (String k : NUMBER_PATTERN_KEYS) {
|
||||
if (myMap.containsKey(k)) {
|
||||
String[] numberPatterns = new String[NUMBER_PATTERN_KEYS.length];
|
||||
for (int i = 0; i < NUMBER_PATTERN_KEYS.length; i++) {
|
||||
String key = NUMBER_PATTERN_KEYS[i];
|
||||
String value = (String) myMap.remove(key);
|
||||
if (value == null) {
|
||||
value = (String) parentsMap.remove(key);
|
||||
}
|
||||
if (value == null || value.isEmpty()) {
|
||||
if (!key.endsWith("accounting")) {
|
||||
// print warning unless it is for "accounting",
|
||||
// which may be missing.
|
||||
CLDRConverter.warning("empty pattern for " + key);
|
||||
}
|
||||
}
|
||||
numberPatterns[i] = value;
|
||||
}
|
||||
myMap.put("NumberPatterns", numberPatterns);
|
||||
break;
|
||||
// if myMap has any of the NumberPatterns/NumberElements members, create a
|
||||
// complete array of patterns/elements.
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> scripts = (List<String>) myMap.get("numberingScripts");
|
||||
if (scripts != null) {
|
||||
for (String script : scripts) {
|
||||
myMap.put(script + ".NumberPatterns",
|
||||
createNumberArray(myMap, parentsMap, NUMBER_PATTERN_KEYS, script));
|
||||
myMap.put(script + ".NumberElements",
|
||||
createNumberArray(myMap, parentsMap, NUMBER_ELEMENT_KEYS, script));
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,40 +237,6 @@ class Bundle {
|
||||
}
|
||||
}
|
||||
|
||||
// if myMap has any of NUMBER_ELEMENT_KEYS, create a complete NumberElements.
|
||||
String defaultScript = (String) myMap.get("DefaultNumberingSystem");
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> scripts = (List<String>) myMap.get("numberingScripts");
|
||||
if (scripts != null) {
|
||||
for (String script : scripts) {
|
||||
for (String k : NUMBER_ELEMENT_KEYS) {
|
||||
String[] numberElements = new String[NUMBER_ELEMENT_KEYS.length];
|
||||
for (int i = 0; i < NUMBER_ELEMENT_KEYS.length; i++) {
|
||||
String key = script + "." + NUMBER_ELEMENT_KEYS[i];
|
||||
String value = (String) myMap.remove(key);
|
||||
if (value == null) {
|
||||
if (key.endsWith("/pattern")) {
|
||||
value = "#";
|
||||
} else {
|
||||
value = (String) parentsMap.get(key);
|
||||
if (value == null) {
|
||||
// the last resort is "latn"
|
||||
key = "latn." + NUMBER_ELEMENT_KEYS[i];
|
||||
value = (String) parentsMap.get(key);
|
||||
if (value == null) {
|
||||
throw new InternalError("NumberElements: null for " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
numberElements[i] = value;
|
||||
}
|
||||
myMap.put(script + "." + "NumberElements", numberElements);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// another hack: parentsMap is not used for date-time resources.
|
||||
if ("root".equals(id)) {
|
||||
parentsMap = null;
|
||||
@ -798,4 +754,45 @@ class Bundle {
|
||||
private interface ConvertDateTimeLetters {
|
||||
void convert(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a complete string array for NumberElements or NumberPatterns. If any
|
||||
* array element is missing, it will fall back to parents map, as well as
|
||||
* numbering script fallback.
|
||||
*/
|
||||
private String[] createNumberArray(Map<String, Object> myMap, Map<String, Object>parentsMap,
|
||||
String[] keys, String script) {
|
||||
String[] numArray = new String[keys.length];
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
String key = script + "." + keys[i];
|
||||
final int idx = i;
|
||||
Optional.ofNullable(
|
||||
myMap.getOrDefault(key,
|
||||
// if value not found in myMap, search for parentsMap
|
||||
parentsMap.getOrDefault(key,
|
||||
parentsMap.getOrDefault(keys[i],
|
||||
// the last resort is "latn"
|
||||
parentsMap.get("latn." + keys[i])))))
|
||||
.ifPresentOrElse(v -> numArray[idx] = (String)v, () -> {
|
||||
if (keys == NUMBER_PATTERN_KEYS) {
|
||||
// NumberPatterns
|
||||
if (!key.endsWith("accounting")) {
|
||||
// throw error unless it is for "accounting",
|
||||
// which may be missing.
|
||||
throw new InternalError("NumberPatterns: null for " +
|
||||
key + ", id: " + id);
|
||||
}
|
||||
} else {
|
||||
// NumberElements
|
||||
assert keys == NUMBER_ELEMENT_KEYS;
|
||||
if (key.endsWith("/pattern")) {
|
||||
numArray[idx] = "#";
|
||||
} else {
|
||||
throw new InternalError("NumberElements: null for " +
|
||||
key + ", id: " + id);
|
||||
}
|
||||
}});
|
||||
}
|
||||
return numArray;
|
||||
}
|
||||
}
|
||||
|
@ -864,7 +864,7 @@ public class CLDRConverter {
|
||||
}
|
||||
|
||||
for (String key : map.keySet()) {
|
||||
// Copy available calendar names
|
||||
// Copy available calendar names
|
||||
if (key.startsWith(CLDRConverter.LOCALE_TYPE_PREFIX_CA)) {
|
||||
String type = key.substring(CLDRConverter.LOCALE_TYPE_PREFIX_CA.length());
|
||||
for (CalendarType calendarType : CalendarType.values()) {
|
||||
@ -891,12 +891,13 @@ public class CLDRConverter {
|
||||
List<String> numberingScripts = (List<String>) map.remove("numberingScripts");
|
||||
if (numberingScripts != null) {
|
||||
for (String script : numberingScripts) {
|
||||
copyIfPresent(map, script + "." + "NumberElements", formatData);
|
||||
copyIfPresent(map, script + ".NumberElements", formatData);
|
||||
copyIfPresent(map, script + ".NumberPatterns", formatData);
|
||||
}
|
||||
} else {
|
||||
copyIfPresent(map, "NumberElements", formatData);
|
||||
copyIfPresent(map, "NumberPatterns", formatData);
|
||||
}
|
||||
copyIfPresent(map, "NumberPatterns", formatData);
|
||||
copyIfPresent(map, "short.CompactNumberPatterns", formatData);
|
||||
copyIfPresent(map, "long.CompactNumberPatterns", formatData);
|
||||
|
||||
@ -1159,4 +1160,22 @@ public class CLDRConverter {
|
||||
.collect(Collectors.toList()),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
}
|
||||
|
||||
// for debug
|
||||
private static void dumpMap(Map<String, Object> map) {
|
||||
map.entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.map(e -> {
|
||||
Object val = e.getValue();
|
||||
String valStr = null;
|
||||
|
||||
if (val instanceof String[]) {
|
||||
valStr = Arrays.asList((String[])val).toString();
|
||||
} else if (val != null) {
|
||||
valStr = val.toString();
|
||||
}
|
||||
return e.getKey() + " = " + valStr;
|
||||
})
|
||||
.forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
|
@ -508,7 +508,8 @@ class LDMLParseHandler extends AbstractLDMLHandler<Object> {
|
||||
String type = attributes.getValue("type");
|
||||
if (null == type) {
|
||||
// format data for decimal number format
|
||||
pushStringEntry(qName, attributes, "NumberPatterns/decimal");
|
||||
pushStringEntry(qName, attributes,
|
||||
currentNumberingSystem + "NumberPatterns/decimal");
|
||||
currentStyle = type;
|
||||
} else {
|
||||
switch (type) {
|
||||
@ -586,6 +587,18 @@ class LDMLParseHandler extends AbstractLDMLHandler<Object> {
|
||||
pushContainer(qName, attributes);
|
||||
}
|
||||
break;
|
||||
case "currencyFormats":
|
||||
case "decimalFormats":
|
||||
case "percentFormats":
|
||||
{
|
||||
String script = attributes.getValue("numberSystem");
|
||||
if (script != null) {
|
||||
addNumberingScript(script);
|
||||
currentNumberingSystem = script + ".";
|
||||
}
|
||||
pushContainer(qName, attributes);
|
||||
}
|
||||
break;
|
||||
case "currencyFormatLength":
|
||||
if (attributes.getValue("type") == null) {
|
||||
// skipping type="short" data
|
||||
@ -601,9 +614,11 @@ class LDMLParseHandler extends AbstractLDMLHandler<Object> {
|
||||
// copy string for later assembly into NumberPatterns
|
||||
String cfStyle = attributes.getValue("type");
|
||||
if (cfStyle.equals("standard")) {
|
||||
pushStringEntry(qName, attributes, "NumberPatterns/currency");
|
||||
pushStringEntry(qName, attributes,
|
||||
currentNumberingSystem + "NumberPatterns/currency");
|
||||
} else if (cfStyle.equals("accounting")) {
|
||||
pushStringEntry(qName, attributes, "NumberPatterns/accounting");
|
||||
pushStringEntry(qName, attributes,
|
||||
currentNumberingSystem + "NumberPatterns/accounting");
|
||||
} else {
|
||||
pushIgnoredContainer(qName);
|
||||
}
|
||||
@ -613,7 +628,8 @@ class LDMLParseHandler extends AbstractLDMLHandler<Object> {
|
||||
// for FormatData
|
||||
// copy string for later assembly into NumberPatterns
|
||||
if (attributes.getValue("type").equals("standard")) {
|
||||
pushStringEntry(qName, attributes, "NumberPatterns/percent");
|
||||
pushStringEntry(qName, attributes,
|
||||
currentNumberingSystem + "NumberPatterns/percent");
|
||||
} else {
|
||||
pushIgnoredContainer(qName);
|
||||
}
|
||||
@ -641,13 +657,7 @@ class LDMLParseHandler extends AbstractLDMLHandler<Object> {
|
||||
break;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> numberingScripts = (List<String>) get("numberingScripts");
|
||||
if (numberingScripts == null) {
|
||||
numberingScripts = new ArrayList<>();
|
||||
put("numberingScripts", numberingScripts);
|
||||
}
|
||||
numberingScripts.add(script);
|
||||
addNumberingScript(script);
|
||||
put(currentNumberingSystem + "NumberElements/zero", digits.substring(0, 1));
|
||||
pushContainer(qName, attributes);
|
||||
}
|
||||
@ -1020,6 +1030,13 @@ class LDMLParseHandler extends AbstractLDMLHandler<Object> {
|
||||
compactCount = "";
|
||||
putIfEntry();
|
||||
break;
|
||||
case "currencyFormats":
|
||||
case "decimalFormats":
|
||||
case "percentFormats":
|
||||
case "symbols":
|
||||
currentNumberingSystem = "";
|
||||
putIfEntry();
|
||||
break;
|
||||
default:
|
||||
putIfEntry();
|
||||
}
|
||||
@ -1086,4 +1103,16 @@ class LDMLParseHandler extends AbstractLDMLHandler<Object> {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
private void addNumberingScript(String script) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> numberingScripts = (List<String>) get("numberingScripts");
|
||||
if (numberingScripts == null) {
|
||||
numberingScripts = new ArrayList<>();
|
||||
put("numberingScripts", numberingScripts);
|
||||
}
|
||||
if (!numberingScripts.contains(script)) {
|
||||
numberingScripts.add(script);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -178,30 +178,7 @@ public class LocaleResources {
|
||||
// elements are provided by the caller, yet they are cached here.
|
||||
ResourceBundle rb = localeData.getNumberFormatData(locale);
|
||||
dfsdata = new Object[3];
|
||||
|
||||
// NumberElements look up. First, try the Unicode extension
|
||||
String numElemKey;
|
||||
String numberType = locale.getUnicodeLocaleType("nu");
|
||||
if (numberType != null) {
|
||||
numElemKey = numberType + ".NumberElements";
|
||||
if (rb.containsKey(numElemKey)) {
|
||||
dfsdata[0] = rb.getStringArray(numElemKey);
|
||||
}
|
||||
}
|
||||
|
||||
// Next, try DefaultNumberingSystem value
|
||||
if (dfsdata[0] == null && rb.containsKey("DefaultNumberingSystem")) {
|
||||
numElemKey = rb.getString("DefaultNumberingSystem") + ".NumberElements";
|
||||
if (rb.containsKey(numElemKey)) {
|
||||
dfsdata[0] = rb.getStringArray(numElemKey);
|
||||
}
|
||||
}
|
||||
|
||||
// Last resort. No need to check the availability.
|
||||
// Just let it throw MissingResourceException when needed.
|
||||
if (dfsdata[0] == null) {
|
||||
dfsdata[0] = rb.getStringArray("NumberElements");
|
||||
}
|
||||
dfsdata[0] = getNumberStrings(rb, "NumberElements");
|
||||
|
||||
cache.put(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY,
|
||||
new ResourceReference(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY, (Object) dfsdata, referenceQueue));
|
||||
@ -210,6 +187,37 @@ public class LocaleResources {
|
||||
return dfsdata;
|
||||
}
|
||||
|
||||
private String[] getNumberStrings(ResourceBundle rb, String type) {
|
||||
String[] ret = null;
|
||||
String key;
|
||||
String numSys;
|
||||
|
||||
// Number strings look up. First, try the Unicode extension
|
||||
numSys = locale.getUnicodeLocaleType("nu");
|
||||
if (numSys != null) {
|
||||
key = numSys + "." + type;
|
||||
if (rb.containsKey(key)) {
|
||||
ret = rb.getStringArray(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Next, try DefaultNumberingSystem value
|
||||
if (ret == null && rb.containsKey("DefaultNumberingSystem")) {
|
||||
key = rb.getString("DefaultNumberingSystem") + "." + type;
|
||||
if (rb.containsKey(key)) {
|
||||
ret = rb.getStringArray(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Last resort. No need to check the availability.
|
||||
// Just let it throw MissingResourceException when needed.
|
||||
if (ret == null) {
|
||||
ret = rb.getStringArray(type);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public String getCurrencyName(String key) {
|
||||
Object currencyName = null;
|
||||
String cacheKey = CURRENCY_NAMES + key;
|
||||
@ -485,7 +493,7 @@ public class LocaleResources {
|
||||
|
||||
if (data == null || ((numberPatterns = (String[]) data.get()) == null)) {
|
||||
ResourceBundle resource = localeData.getNumberFormatData(locale);
|
||||
numberPatterns = resource.getStringArray("NumberPatterns");
|
||||
numberPatterns = getNumberStrings(resource, "NumberPatterns");
|
||||
cache.put(NUMBER_PATTERNS_CACHEKEY,
|
||||
new ResourceReference(NUMBER_PATTERNS_CACHEKEY, (Object) numberPatterns, referenceQueue));
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8220309
|
||||
* @bug 8220309 8230284
|
||||
* @library /java/text/testlib
|
||||
* @summary Test String representation of MinusSign/Percent/PerMill symbols.
|
||||
* This test assumes CLDR has numbering systems for "arab" and
|
||||
@ -55,14 +55,14 @@ public class DFSMinusPerCentMill {
|
||||
// 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.CURRENCY, "\u061c-\u0661\u066c\u0662\u0663\u0664\u066b\u0665\u0666\u00a0$"},
|
||||
{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.CURRENCY, "\u200e-\u200e$\u00a0\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"},
|
||||
|
@ -24,10 +24,10 @@
|
||||
/*
|
||||
*
|
||||
* @test
|
||||
* @bug 8215181
|
||||
* @bug 8215181 8230284
|
||||
* @summary Tests the "u-cf" extension
|
||||
* @modules jdk.localedata
|
||||
* @run testng/othervm CurrencyFormatTests
|
||||
* @run testng/othervm -Djava.locale.providers=CLDR CurrencyFormatTests
|
||||
*/
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
@ -78,6 +78,22 @@ public class CurrencyFormatTests {
|
||||
{Locale.forLanguageTag("en-US-u-rg-CHZZZZ-cf-standard"), -100, "CHF-100.00"},
|
||||
{Locale.forLanguageTag("en-US-u-rg-CHZZZZ-cf-account"), -100, "CHF-100.00"},
|
||||
{Locale.forLanguageTag("en-US-u-rg-CHZZZZ-cf-bogus"), -100, "CHF-100.00"},
|
||||
|
||||
// Numbering systems
|
||||
// explicit
|
||||
{Locale.forLanguageTag("zh-CN-u-nu-arab"), -100, "\u061c-\uffe5\u0661\u0660\u0660\u066b\u0660\u0660"},
|
||||
{Locale.forLanguageTag("zh-CN-u-nu-arab-cf-standard"), -100, "\u061c-\uffe5\u0661\u0660\u0660\u066b\u0660\u0660"},
|
||||
{Locale.forLanguageTag("zh-CN-u-nu-arab-cf-account"), -100, "\u061c-\uffe5\u0661\u0660\u0660\u066b\u0660\u0660"},
|
||||
{Locale.forLanguageTag("zh-CN-u-nu-arab-cf-bogus"), -100, "\u061c-\uffe5\u0661\u0660\u0660\u066b\u0660\u0660"},
|
||||
// implicit
|
||||
{Locale.forLanguageTag("zh-CN"), -100, "-\uffe5100.00"},
|
||||
{Locale.forLanguageTag("zh-CN-u-cf-standard"), -100, "-\uffe5100.00"},
|
||||
{Locale.forLanguageTag("zh-CN-u-cf-account"), -100, "(\uffe5100.00)"},
|
||||
{Locale.forLanguageTag("zh-CN-u-cf-bogus"), -100, "-\uffe5100.00"},
|
||||
{Locale.forLanguageTag("ar-SA"), -100, "\u061c-\u0661\u0660\u0660\u066b\u0660\u0660\u00a0\u0631.\u0633.\u200f"},
|
||||
{Locale.forLanguageTag("ar-SA-u-cf-standard"), -100, "\u061c-\u0661\u0660\u0660\u066b\u0660\u0660\u00a0\u0631.\u0633.\u200f"},
|
||||
{Locale.forLanguageTag("ar-SA-u-cf-account"), -100, "\u061c-\u0661\u0660\u0660\u066b\u0660\u0660\u00a0\u0631.\u0633.\u200f"},
|
||||
{Locale.forLanguageTag("ar-SA-u-cf-bogus"), -100, "\u061c-\u0661\u0660\u0660\u066b\u0660\u0660\u00a0\u0631.\u0633.\u200f"},
|
||||
};
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -39,7 +39,7 @@
|
||||
* 8017142 8037343 8055222 8042126 8074791 8075173 8080774 8129361 8134916
|
||||
* 8145136 8145952 8164784 8037111 8081643 7037368 8178872 8185841 8190918
|
||||
* 8187946 8195478 8181157 8179071 8193552 8202026 8204269 8202537 8208746
|
||||
* 8209775 8221432 8227127
|
||||
* 8209775 8221432 8227127 8230284
|
||||
* @summary Verify locale data
|
||||
* @modules java.base/sun.util.resources
|
||||
* @modules jdk.localedata
|
||||
|
Loading…
Reference in New Issue
Block a user