Skip to content

Commit ea0fbbc

Browse files
committed
8230284: Accounting currency format support does not cope with explicit number system
Reviewed-by: rriggs
1 parent 6794a68 commit ea0fbbc

File tree

8 files changed

+675
-606
lines changed

8 files changed

+675
-606
lines changed

make/jdk/src/classes/build/tools/cldrconverter/Bundle.java

Lines changed: 52 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.List;
3434
import java.util.Map;
3535
import java.util.Objects;
36+
import java.util.Optional;
3637

3738
class Bundle {
3839
static enum Type {
@@ -213,27 +214,16 @@ Map<String, Object> getTargetMap() throws Exception {
213214

214215
// merge individual strings into arrays
215216

216-
// if myMap has any of the NumberPatterns members
217-
for (String k : NUMBER_PATTERN_KEYS) {
218-
if (myMap.containsKey(k)) {
219-
String[] numberPatterns = new String[NUMBER_PATTERN_KEYS.length];
220-
for (int i = 0; i < NUMBER_PATTERN_KEYS.length; i++) {
221-
String key = NUMBER_PATTERN_KEYS[i];
222-
String value = (String) myMap.remove(key);
223-
if (value == null) {
224-
value = (String) parentsMap.remove(key);
225-
}
226-
if (value == null || value.isEmpty()) {
227-
if (!key.endsWith("accounting")) {
228-
// print warning unless it is for "accounting",
229-
// which may be missing.
230-
CLDRConverter.warning("empty pattern for " + key);
231-
}
232-
}
233-
numberPatterns[i] = value;
234-
}
235-
myMap.put("NumberPatterns", numberPatterns);
236-
break;
217+
// if myMap has any of the NumberPatterns/NumberElements members, create a
218+
// complete array of patterns/elements.
219+
@SuppressWarnings("unchecked")
220+
List<String> scripts = (List<String>) myMap.get("numberingScripts");
221+
if (scripts != null) {
222+
for (String script : scripts) {
223+
myMap.put(script + ".NumberPatterns",
224+
createNumberArray(myMap, parentsMap, NUMBER_PATTERN_KEYS, script));
225+
myMap.put(script + ".NumberElements",
226+
createNumberArray(myMap, parentsMap, NUMBER_ELEMENT_KEYS, script));
237227
}
238228
}
239229

@@ -247,40 +237,6 @@ Map<String, Object> getTargetMap() throws Exception {
247237
}
248238
}
249239

250-
// if myMap has any of NUMBER_ELEMENT_KEYS, create a complete NumberElements.
251-
String defaultScript = (String) myMap.get("DefaultNumberingSystem");
252-
@SuppressWarnings("unchecked")
253-
List<String> scripts = (List<String>) myMap.get("numberingScripts");
254-
if (scripts != null) {
255-
for (String script : scripts) {
256-
for (String k : NUMBER_ELEMENT_KEYS) {
257-
String[] numberElements = new String[NUMBER_ELEMENT_KEYS.length];
258-
for (int i = 0; i < NUMBER_ELEMENT_KEYS.length; i++) {
259-
String key = script + "." + NUMBER_ELEMENT_KEYS[i];
260-
String value = (String) myMap.remove(key);
261-
if (value == null) {
262-
if (key.endsWith("/pattern")) {
263-
value = "#";
264-
} else {
265-
value = (String) parentsMap.get(key);
266-
if (value == null) {
267-
// the last resort is "latn"
268-
key = "latn." + NUMBER_ELEMENT_KEYS[i];
269-
value = (String) parentsMap.get(key);
270-
if (value == null) {
271-
throw new InternalError("NumberElements: null for " + key);
272-
}
273-
}
274-
}
275-
}
276-
numberElements[i] = value;
277-
}
278-
myMap.put(script + "." + "NumberElements", numberElements);
279-
break;
280-
}
281-
}
282-
}
283-
284240
// another hack: parentsMap is not used for date-time resources.
285241
if ("root".equals(id)) {
286242
parentsMap = null;
@@ -798,4 +754,45 @@ private static boolean hasNulls(Object[] array) {
798754
private interface ConvertDateTimeLetters {
799755
void convert(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb);
800756
}
757+
758+
/**
759+
* Returns a complete string array for NumberElements or NumberPatterns. If any
760+
* array element is missing, it will fall back to parents map, as well as
761+
* numbering script fallback.
762+
*/
763+
private String[] createNumberArray(Map<String, Object> myMap, Map<String, Object>parentsMap,
764+
String[] keys, String script) {
765+
String[] numArray = new String[keys.length];
766+
for (int i = 0; i < keys.length; i++) {
767+
String key = script + "." + keys[i];
768+
final int idx = i;
769+
Optional.ofNullable(
770+
myMap.getOrDefault(key,
771+
// if value not found in myMap, search for parentsMap
772+
parentsMap.getOrDefault(key,
773+
parentsMap.getOrDefault(keys[i],
774+
// the last resort is "latn"
775+
parentsMap.get("latn." + keys[i])))))
776+
.ifPresentOrElse(v -> numArray[idx] = (String)v, () -> {
777+
if (keys == NUMBER_PATTERN_KEYS) {
778+
// NumberPatterns
779+
if (!key.endsWith("accounting")) {
780+
// throw error unless it is for "accounting",
781+
// which may be missing.
782+
throw new InternalError("NumberPatterns: null for " +
783+
key + ", id: " + id);
784+
}
785+
} else {
786+
// NumberElements
787+
assert keys == NUMBER_ELEMENT_KEYS;
788+
if (key.endsWith("/pattern")) {
789+
numArray[idx] = "#";
790+
} else {
791+
throw new InternalError("NumberElements: null for " +
792+
key + ", id: " + id);
793+
}
794+
}});
795+
}
796+
return numArray;
797+
}
801798
}

make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -864,7 +864,7 @@ private static Map<String, Object> extractFormatData(Map<String, Object> map, St
864864
}
865865

866866
for (String key : map.keySet()) {
867-
// Copy available calendar names
867+
// Copy available calendar names
868868
if (key.startsWith(CLDRConverter.LOCALE_TYPE_PREFIX_CA)) {
869869
String type = key.substring(CLDRConverter.LOCALE_TYPE_PREFIX_CA.length());
870870
for (CalendarType calendarType : CalendarType.values()) {
@@ -891,12 +891,13 @@ private static Map<String, Object> extractFormatData(Map<String, Object> map, St
891891
List<String> numberingScripts = (List<String>) map.remove("numberingScripts");
892892
if (numberingScripts != null) {
893893
for (String script : numberingScripts) {
894-
copyIfPresent(map, script + "." + "NumberElements", formatData);
894+
copyIfPresent(map, script + ".NumberElements", formatData);
895+
copyIfPresent(map, script + ".NumberPatterns", formatData);
895896
}
896897
} else {
897898
copyIfPresent(map, "NumberElements", formatData);
899+
copyIfPresent(map, "NumberPatterns", formatData);
898900
}
899-
copyIfPresent(map, "NumberPatterns", formatData);
900901
copyIfPresent(map, "short.CompactNumberPatterns", formatData);
901902
copyIfPresent(map, "long.CompactNumberPatterns", formatData);
902903

@@ -1159,4 +1160,22 @@ public int compare(String t1, String t2) {
11591160
.collect(Collectors.toList()),
11601161
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
11611162
}
1163+
1164+
// for debug
1165+
private static void dumpMap(Map<String, Object> map) {
1166+
map.entrySet().stream()
1167+
.sorted(Map.Entry.comparingByKey())
1168+
.map(e -> {
1169+
Object val = e.getValue();
1170+
String valStr = null;
1171+
1172+
if (val instanceof String[]) {
1173+
valStr = Arrays.asList((String[])val).toString();
1174+
} else if (val != null) {
1175+
valStr = val.toString();
1176+
}
1177+
return e.getKey() + " = " + valStr;
1178+
})
1179+
.forEach(System.out::println);
1180+
}
11621181
}

make/jdk/src/classes/build/tools/cldrconverter/LDMLParseHandler.java

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,8 @@ public void startElement(String uri, String localName, String qName, Attributes
508508
String type = attributes.getValue("type");
509509
if (null == type) {
510510
// format data for decimal number format
511-
pushStringEntry(qName, attributes, "NumberPatterns/decimal");
511+
pushStringEntry(qName, attributes,
512+
currentNumberingSystem + "NumberPatterns/decimal");
512513
currentStyle = type;
513514
} else {
514515
switch (type) {
@@ -586,6 +587,18 @@ public void startElement(String uri, String localName, String qName, Attributes
586587
pushContainer(qName, attributes);
587588
}
588589
break;
590+
case "currencyFormats":
591+
case "decimalFormats":
592+
case "percentFormats":
593+
{
594+
String script = attributes.getValue("numberSystem");
595+
if (script != null) {
596+
addNumberingScript(script);
597+
currentNumberingSystem = script + ".";
598+
}
599+
pushContainer(qName, attributes);
600+
}
601+
break;
589602
case "currencyFormatLength":
590603
if (attributes.getValue("type") == null) {
591604
// skipping type="short" data
@@ -601,9 +614,11 @@ public void startElement(String uri, String localName, String qName, Attributes
601614
// copy string for later assembly into NumberPatterns
602615
String cfStyle = attributes.getValue("type");
603616
if (cfStyle.equals("standard")) {
604-
pushStringEntry(qName, attributes, "NumberPatterns/currency");
617+
pushStringEntry(qName, attributes,
618+
currentNumberingSystem + "NumberPatterns/currency");
605619
} else if (cfStyle.equals("accounting")) {
606-
pushStringEntry(qName, attributes, "NumberPatterns/accounting");
620+
pushStringEntry(qName, attributes,
621+
currentNumberingSystem + "NumberPatterns/accounting");
607622
} else {
608623
pushIgnoredContainer(qName);
609624
}
@@ -613,7 +628,8 @@ public void startElement(String uri, String localName, String qName, Attributes
613628
// for FormatData
614629
// copy string for later assembly into NumberPatterns
615630
if (attributes.getValue("type").equals("standard")) {
616-
pushStringEntry(qName, attributes, "NumberPatterns/percent");
631+
pushStringEntry(qName, attributes,
632+
currentNumberingSystem + "NumberPatterns/percent");
617633
} else {
618634
pushIgnoredContainer(qName);
619635
}
@@ -641,13 +657,7 @@ public void startElement(String uri, String localName, String qName, Attributes
641657
break;
642658
}
643659

644-
@SuppressWarnings("unchecked")
645-
List<String> numberingScripts = (List<String>) get("numberingScripts");
646-
if (numberingScripts == null) {
647-
numberingScripts = new ArrayList<>();
648-
put("numberingScripts", numberingScripts);
649-
}
650-
numberingScripts.add(script);
660+
addNumberingScript(script);
651661
put(currentNumberingSystem + "NumberElements/zero", digits.substring(0, 1));
652662
pushContainer(qName, attributes);
653663
}
@@ -1020,6 +1030,13 @@ public void endElement(String uri, String localName, String qName) throws SAXExc
10201030
compactCount = "";
10211031
putIfEntry();
10221032
break;
1033+
case "currencyFormats":
1034+
case "decimalFormats":
1035+
case "percentFormats":
1036+
case "symbols":
1037+
currentNumberingSystem = "";
1038+
putIfEntry();
1039+
break;
10231040
default:
10241041
putIfEntry();
10251042
}
@@ -1086,4 +1103,16 @@ public String convertOldKeyName(String key) {
10861103
return key;
10871104
}
10881105
}
1106+
1107+
private void addNumberingScript(String script) {
1108+
@SuppressWarnings("unchecked")
1109+
List<String> numberingScripts = (List<String>) get("numberingScripts");
1110+
if (numberingScripts == null) {
1111+
numberingScripts = new ArrayList<>();
1112+
put("numberingScripts", numberingScripts);
1113+
}
1114+
if (!numberingScripts.contains(script)) {
1115+
numberingScripts.add(script);
1116+
}
1117+
}
10891118
}

src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -178,36 +178,44 @@ public Object[] getDecimalFormatSymbolsData() {
178178
// elements are provided by the caller, yet they are cached here.
179179
ResourceBundle rb = localeData.getNumberFormatData(locale);
180180
dfsdata = new Object[3];
181+
dfsdata[0] = getNumberStrings(rb, "NumberElements");
181182

182-
// NumberElements look up. First, try the Unicode extension
183-
String numElemKey;
184-
String numberType = locale.getUnicodeLocaleType("nu");
185-
if (numberType != null) {
186-
numElemKey = numberType + ".NumberElements";
187-
if (rb.containsKey(numElemKey)) {
188-
dfsdata[0] = rb.getStringArray(numElemKey);
189-
}
190-
}
183+
cache.put(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY,
184+
new ResourceReference(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY, (Object) dfsdata, referenceQueue));
185+
}
191186

192-
// Next, try DefaultNumberingSystem value
193-
if (dfsdata[0] == null && rb.containsKey("DefaultNumberingSystem")) {
194-
numElemKey = rb.getString("DefaultNumberingSystem") + ".NumberElements";
195-
if (rb.containsKey(numElemKey)) {
196-
dfsdata[0] = rb.getStringArray(numElemKey);
197-
}
187+
return dfsdata;
188+
}
189+
190+
private String[] getNumberStrings(ResourceBundle rb, String type) {
191+
String[] ret = null;
192+
String key;
193+
String numSys;
194+
195+
// Number strings look up. First, try the Unicode extension
196+
numSys = locale.getUnicodeLocaleType("nu");
197+
if (numSys != null) {
198+
key = numSys + "." + type;
199+
if (rb.containsKey(key)) {
200+
ret = rb.getStringArray(key);
198201
}
202+
}
199203

200-
// Last resort. No need to check the availability.
201-
// Just let it throw MissingResourceException when needed.
202-
if (dfsdata[0] == null) {
203-
dfsdata[0] = rb.getStringArray("NumberElements");
204+
// Next, try DefaultNumberingSystem value
205+
if (ret == null && rb.containsKey("DefaultNumberingSystem")) {
206+
key = rb.getString("DefaultNumberingSystem") + "." + type;
207+
if (rb.containsKey(key)) {
208+
ret = rb.getStringArray(key);
204209
}
210+
}
205211

206-
cache.put(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY,
207-
new ResourceReference(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY, (Object) dfsdata, referenceQueue));
212+
// Last resort. No need to check the availability.
213+
// Just let it throw MissingResourceException when needed.
214+
if (ret == null) {
215+
ret = rb.getStringArray(type);
208216
}
209217

210-
return dfsdata;
218+
return ret;
211219
}
212220

213221
public String getCurrencyName(String key) {
@@ -485,7 +493,7 @@ public String[] getNumberPatterns() {
485493

486494
if (data == null || ((numberPatterns = (String[]) data.get()) == null)) {
487495
ResourceBundle resource = localeData.getNumberFormatData(locale);
488-
numberPatterns = resource.getStringArray("NumberPatterns");
496+
numberPatterns = getNumberStrings(resource, "NumberPatterns");
489497
cache.put(NUMBER_PATTERNS_CACHEKEY,
490498
new ResourceReference(NUMBER_PATTERNS_CACHEKEY, (Object) numberPatterns, referenceQueue));
491499
}

test/jdk/java/text/Format/NumberFormat/DFSMinusPerCentMill.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
/**
2525
* @test
26-
* @bug 8220309
26+
* @bug 8220309 8230284
2727
* @library /java/text/testlib
2828
* @summary Test String representation of MinusSign/Percent/PerMill symbols.
2929
* This test assumes CLDR has numbering systems for "arab" and
@@ -55,14 +55,14 @@ Object[][] formatData() {
5555
// Locale, FormatStyle, expected format, expected single char symbol
5656
{US_ARAB, Type.NUMBER, "\u061c-\u0661\u066c\u0662\u0663\u0664\u066b\u0665\u0666"},
5757
{US_ARAB, Type.PERCENT, "\u061c-\u0661\u0662\u0663\u066c\u0664\u0665\u0666\u066a\u061c"},
58-
{US_ARAB, Type.CURRENCY, "\u061c-$\u0661\u066c\u0662\u0663\u0664\u066b\u0665\u0666"},
58+
{US_ARAB, Type.CURRENCY, "\u061c-\u0661\u066c\u0662\u0663\u0664\u066b\u0665\u0666\u00a0$"},
5959
{US_ARAB, Type.INTEGER, "\u061c-\u0661\u066c\u0662\u0663\u0665"},
6060
{US_ARAB, Type.COMPACT, "\u061c-\u0661K"},
6161
{US_ARAB, Type.PERMILL, "\u061c-\u0661\u0662\u0663\u0664\u0665\u0666\u0660\u0609"},
6262

6363
{US_ARABEXT, Type.NUMBER, "\u200e-\u200e\u06f1\u066c\u06f2\u06f3\u06f4\u066b\u06f5\u06f6"},
6464
{US_ARABEXT, Type.PERCENT, "\u200e-\u200e\u06f1\u06f2\u06f3\u066c\u06f4\u06f5\u06f6\u066a"},
65-
{US_ARABEXT, Type.CURRENCY, "\u200e-\u200e$\u06f1\u066c\u06f2\u06f3\u06f4\u066b\u06f5\u06f6"},
65+
{US_ARABEXT, Type.CURRENCY, "\u200e-\u200e$\u00a0\u06f1\u066c\u06f2\u06f3\u06f4\u066b\u06f5\u06f6"},
6666
{US_ARABEXT, Type.INTEGER, "\u200e-\u200e\u06f1\u066c\u06f2\u06f3\u06f5"},
6767
{US_ARABEXT, Type.COMPACT, "\u200e-\u200e\u06f1K"},
6868
{US_ARABEXT, Type.PERMILL, "\u200e-\u200e\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f0\u0609"},

0 commit comments

Comments
 (0)