Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/make/src/classes/build/tools/cldrconverter/Bundle.java
32287 views
1
/*
2
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package build.tools.cldrconverter;
27
28
import java.util.ArrayList;
29
import java.util.Arrays;
30
import java.util.EnumSet;
31
import java.util.HashMap;
32
import java.util.Iterator;
33
import java.util.List;
34
import java.util.Map;
35
36
class Bundle {
37
static enum Type {
38
LOCALENAMES, CURRENCYNAMES, TIMEZONENAMES, CALENDARDATA, FORMATDATA;
39
40
static EnumSet<Type> ALL_TYPES = EnumSet.of(LOCALENAMES,
41
CURRENCYNAMES,
42
TIMEZONENAMES,
43
CALENDARDATA,
44
FORMATDATA);
45
}
46
47
private final static Map<String, Bundle> bundles = new HashMap<>();
48
49
private final static String[] NUMBER_PATTERN_KEYS = {
50
"NumberPatterns/decimal",
51
"NumberPatterns/currency",
52
"NumberPatterns/percent"
53
};
54
55
private final static String[] NUMBER_ELEMENT_KEYS = {
56
"NumberElements/decimal",
57
"NumberElements/group",
58
"NumberElements/list",
59
"NumberElements/percent",
60
"NumberElements/zero",
61
"NumberElements/pattern",
62
"NumberElements/minus",
63
"NumberElements/exponential",
64
"NumberElements/permille",
65
"NumberElements/infinity",
66
"NumberElements/nan"
67
};
68
69
private final static String[] TIME_PATTERN_KEYS = {
70
"DateTimePatterns/full-time",
71
"DateTimePatterns/long-time",
72
"DateTimePatterns/medium-time",
73
"DateTimePatterns/short-time",
74
};
75
76
private final static String[] DATE_PATTERN_KEYS = {
77
"DateTimePatterns/full-date",
78
"DateTimePatterns/long-date",
79
"DateTimePatterns/medium-date",
80
"DateTimePatterns/short-date",
81
};
82
83
private final static String[] DATETIME_PATTERN_KEYS = {
84
"DateTimePatterns/date-time"
85
};
86
87
private final static String[] ERA_KEYS = {
88
"long.Eras",
89
"Eras",
90
"narrow.Eras"
91
};
92
93
// Keys for individual time zone names
94
private final static String TZ_GEN_LONG_KEY = "timezone.displayname.generic.long";
95
private final static String TZ_GEN_SHORT_KEY = "timezone.displayname.generic.short";
96
private final static String TZ_STD_LONG_KEY = "timezone.displayname.standard.long";
97
private final static String TZ_STD_SHORT_KEY = "timezone.displayname.standard.short";
98
private final static String TZ_DST_LONG_KEY = "timezone.displayname.daylight.long";
99
private final static String TZ_DST_SHORT_KEY = "timezone.displayname.daylight.short";
100
private final static String[] ZONE_NAME_KEYS = {
101
TZ_STD_LONG_KEY,
102
TZ_STD_SHORT_KEY,
103
TZ_DST_LONG_KEY,
104
TZ_DST_SHORT_KEY,
105
TZ_GEN_LONG_KEY,
106
TZ_GEN_SHORT_KEY
107
};
108
109
private final String id;
110
private final String cldrPath;
111
private final EnumSet<Type> bundleTypes;
112
private final String currencies;
113
114
static Bundle getBundle(String id) {
115
return bundles.get(id);
116
}
117
118
@SuppressWarnings("ConvertToStringSwitch")
119
Bundle(String id, String cldrPath, String bundles, String currencies) {
120
this.id = id;
121
this.cldrPath = cldrPath;
122
if ("localenames".equals(bundles)) {
123
bundleTypes = EnumSet.of(Type.LOCALENAMES);
124
} else if ("currencynames".equals(bundles)) {
125
bundleTypes = EnumSet.of(Type.CURRENCYNAMES);
126
} else {
127
bundleTypes = Type.ALL_TYPES;
128
}
129
if (currencies == null) {
130
currencies = "local";
131
}
132
this.currencies = currencies;
133
addBundle();
134
}
135
136
private void addBundle() {
137
Bundle.bundles.put(id, this);
138
}
139
140
String getID() {
141
return id;
142
}
143
144
boolean isRoot() {
145
return "root".equals(id);
146
}
147
148
String getCLDRPath() {
149
return cldrPath;
150
}
151
152
EnumSet<Type> getBundleTypes() {
153
return bundleTypes;
154
}
155
156
String getCurrencies() {
157
return currencies;
158
}
159
160
/**
161
* Generate a map that contains all the data that should be
162
* visible for the bundle's locale
163
*/
164
Map<String, Object> getTargetMap() throws Exception {
165
String[] cldrBundles = getCLDRPath().split(",");
166
167
// myMap contains resources for id.
168
Map<String, Object> myMap = new HashMap<>();
169
int index;
170
for (index = 0; index < cldrBundles.length; index++) {
171
if (cldrBundles[index].equals(id)) {
172
myMap.putAll(CLDRConverter.getCLDRBundle(cldrBundles[index]));
173
break;
174
}
175
}
176
177
// parentsMap contains resources from id's parents.
178
Map<String, Object> parentsMap = new HashMap<>();
179
for (int i = cldrBundles.length - 1; i > index; i--) {
180
if (!("no".equals(cldrBundles[i]) || cldrBundles[i].startsWith("no_"))) {
181
parentsMap.putAll(CLDRConverter.getCLDRBundle(cldrBundles[i]));
182
}
183
}
184
// Duplicate myMap as parentsMap for "root" so that the
185
// fallback works. This is a huck, though.
186
if ("root".equals(cldrBundles[0])) {
187
assert parentsMap.isEmpty();
188
parentsMap.putAll(myMap);
189
}
190
191
// merge individual strings into arrays
192
193
// if myMap has any of the NumberPatterns members
194
for (String k : NUMBER_PATTERN_KEYS) {
195
if (myMap.containsKey(k)) {
196
String[] numberPatterns = new String[NUMBER_PATTERN_KEYS.length];
197
for (int i = 0; i < NUMBER_PATTERN_KEYS.length; i++) {
198
String key = NUMBER_PATTERN_KEYS[i];
199
String value = (String) myMap.remove(key);
200
if (value == null) {
201
value = (String) parentsMap.remove(key);
202
}
203
if (value.length() == 0) {
204
CLDRConverter.warning("empty pattern for " + key);
205
}
206
numberPatterns[i] = value;
207
}
208
myMap.put("NumberPatterns", numberPatterns);
209
break;
210
}
211
}
212
213
// if myMap has any of NUMBER_ELEMENT_KEYS, create a complete NumberElements.
214
String defaultScript = (String) myMap.get("DefaultNumberingSystem");
215
@SuppressWarnings("unchecked")
216
List<String> scripts = (List<String>) myMap.get("numberingScripts");
217
if (defaultScript == null && scripts != null) {
218
// Some locale data has no default script for numbering even with mutiple scripts.
219
// Take the first one as default in that case.
220
defaultScript = scripts.get(0);
221
myMap.put("DefaultNumberingSystem", defaultScript);
222
}
223
if (scripts != null) {
224
for (String script : scripts) {
225
for (String k : NUMBER_ELEMENT_KEYS) {
226
String[] numberElements = new String[NUMBER_ELEMENT_KEYS.length];
227
for (int i = 0; i < NUMBER_ELEMENT_KEYS.length; i++) {
228
String key = script + "." + NUMBER_ELEMENT_KEYS[i];
229
String value = (String) myMap.remove(key);
230
if (value == null) {
231
if (key.endsWith("/pattern")) {
232
value = "#";
233
} else {
234
value = (String) parentsMap.get(key);
235
if (value == null) {
236
// the last resort is "latn"
237
key = "latn." + NUMBER_ELEMENT_KEYS[i];
238
value = (String) parentsMap.get(key);
239
if (value == null) {
240
throw new InternalError("NumberElements: null for " + key);
241
}
242
}
243
}
244
}
245
numberElements[i] = value;
246
}
247
myMap.put(script + "." + "NumberElements", numberElements);
248
break;
249
}
250
}
251
}
252
253
// another hack: parentsMap is not used for date-time resources.
254
if ("root".equals(id)) {
255
parentsMap = null;
256
}
257
258
for (CalendarType calendarType : CalendarType.values()) {
259
String calendarPrefix = calendarType.keyElementName();
260
// handle multiple inheritance for month and day names
261
handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "MonthNames");
262
handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "MonthAbbreviations");
263
handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "MonthNarrows");
264
handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "DayNames");
265
handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "DayAbbreviations");
266
handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "DayNarrows");
267
handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "AmPmMarkers");
268
handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "narrow.AmPmMarkers");
269
handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "QuarterNames");
270
handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "QuarterAbbreviations");
271
handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "QuarterNarrows");
272
273
adjustEraNames(myMap, calendarType);
274
275
handleDateTimeFormatPatterns(TIME_PATTERN_KEYS, myMap, parentsMap, calendarType, "TimePatterns");
276
handleDateTimeFormatPatterns(DATE_PATTERN_KEYS, myMap, parentsMap, calendarType, "DatePatterns");
277
handleDateTimeFormatPatterns(DATETIME_PATTERN_KEYS, myMap, parentsMap, calendarType, "DateTimePatterns");
278
}
279
280
// First, weed out any empty timezone or metazone names from myMap.
281
// Fill in any missing abbreviations if locale is "en".
282
for (Iterator<String> it = myMap.keySet().iterator(); it.hasNext();) {
283
String key = it.next();
284
if (key.startsWith(CLDRConverter.TIMEZONE_ID_PREFIX)
285
|| key.startsWith(CLDRConverter.METAZONE_ID_PREFIX)) {
286
@SuppressWarnings("unchecked")
287
Map<String, String> nameMap = (Map<String, String>) myMap.get(key);
288
if (nameMap.isEmpty()) {
289
// Some zones have only exemplarCity, which become empty.
290
// Remove those from the map.
291
it.remove();
292
continue;
293
}
294
295
if (id.startsWith("en")) {
296
fillInAbbrs(key, nameMap);
297
}
298
}
299
}
300
for (Iterator<String> it = myMap.keySet().iterator(); it.hasNext();) {
301
String key = it.next();
302
if (key.startsWith(CLDRConverter.TIMEZONE_ID_PREFIX)
303
|| key.startsWith(CLDRConverter.METAZONE_ID_PREFIX)) {
304
@SuppressWarnings("unchecked")
305
Map<String, String> nameMap = (Map<String, String>) myMap.get(key);
306
// Convert key/value pairs to an array.
307
String[] names = new String[ZONE_NAME_KEYS.length];
308
int ix = 0;
309
for (String nameKey : ZONE_NAME_KEYS) {
310
String name = nameMap.get(nameKey);
311
if (name == null) {
312
@SuppressWarnings("unchecked")
313
Map<String, String> parentNames = (Map<String, String>) parentsMap.get(key);
314
if (parentNames != null) {
315
name = parentNames.get(nameKey);
316
}
317
}
318
names[ix++] = name;
319
}
320
if (hasNulls(names)) {
321
String metaKey = toMetaZoneKey(key);
322
if (metaKey != null) {
323
Object obj = myMap.get(metaKey);
324
if (obj instanceof String[]) {
325
String[] metaNames = (String[]) obj;
326
for (int i = 0; i < names.length; i++) {
327
if (names[i] == null) {
328
names[i] = metaNames[i];
329
}
330
}
331
} else if (obj instanceof Map) {
332
@SuppressWarnings("unchecked")
333
Map<String, String> m = (Map<String, String>) obj;
334
for (int i = 0; i < names.length; i++) {
335
if (names[i] == null) {
336
names[i] = m.get(ZONE_NAME_KEYS[i]);
337
}
338
}
339
}
340
}
341
// If there are still any nulls, try filling in them from en data.
342
if (hasNulls(names) && !id.equals("en")) {
343
@SuppressWarnings("unchecked")
344
String[] enNames = (String[]) Bundle.getBundle("en").getTargetMap().get(key);
345
if (enNames == null) {
346
if (metaKey != null) {
347
@SuppressWarnings("unchecked")
348
String[] metaNames = (String[]) Bundle.getBundle("en").getTargetMap().get(metaKey);
349
enNames = metaNames;
350
}
351
}
352
if (enNames != null) {
353
for (int i = 0; i < names.length; i++) {
354
if (names[i] == null) {
355
names[i] = enNames[i];
356
}
357
}
358
}
359
// If there are still nulls, give up names.
360
if (hasNulls(names)) {
361
names = null;
362
}
363
}
364
}
365
// replace the Map with the array
366
if (names != null) {
367
myMap.put(key, names);
368
} else {
369
it.remove();
370
}
371
}
372
}
373
return myMap;
374
}
375
376
private void handleMultipleInheritance(Map<String, Object> map, Map<String, Object> parents, String key) {
377
String formatKey = key + "/format";
378
Object format = map.get(formatKey);
379
if (format != null) {
380
map.remove(formatKey);
381
map.put(key, format);
382
if (fillInElements(parents, formatKey, format)) {
383
map.remove(key);
384
}
385
}
386
String standaloneKey = key + "/stand-alone";
387
Object standalone = map.get(standaloneKey);
388
if (standalone != null) {
389
map.remove(standaloneKey);
390
String realKey = key;
391
if (format != null) {
392
realKey = "standalone." + key;
393
}
394
map.put(realKey, standalone);
395
if (fillInElements(parents, standaloneKey, standalone)) {
396
map.remove(realKey);
397
}
398
}
399
}
400
401
/**
402
* Fills in any empty elements with its parent element. Returns true if the resulting array is
403
* identical to its parent array.
404
*
405
* @param parents
406
* @param key
407
* @param value
408
* @return true if the resulting array is identical to its parent array.
409
*/
410
private boolean fillInElements(Map<String, Object> parents, String key, Object value) {
411
if (parents == null) {
412
return false;
413
}
414
if (value instanceof String[]) {
415
Object pvalue = parents.get(key);
416
if (pvalue != null && pvalue instanceof String[]) {
417
String[] strings = (String[]) value;
418
String[] pstrings = (String[]) pvalue;
419
for (int i = 0; i < strings.length; i++) {
420
if (strings[i] == null || strings[i].length() == 0) {
421
strings[i] = pstrings[i];
422
}
423
}
424
return Arrays.equals(strings, pstrings);
425
}
426
}
427
return false;
428
}
429
430
/*
431
* Adjusts String[] for era names because JRE's Calendars use different
432
* ERA value indexes in the Buddhist, Japanese Imperial, and Islamic calendars.
433
*/
434
private void adjustEraNames(Map<String, Object> map, CalendarType type) {
435
String[][] eraNames = new String[ERA_KEYS.length][];
436
String[] realKeys = new String[ERA_KEYS.length];
437
int index = 0;
438
for (String key : ERA_KEYS) {
439
String realKey = type.keyElementName() + key;
440
String[] value = (String[]) map.get(realKey);
441
if (value != null) {
442
switch (type) {
443
case GREGORIAN:
444
break;
445
446
case JAPANESE:
447
{
448
String[] newValue = new String[value.length + 1];
449
String[] julianEras = (String[]) map.get(key);
450
if (julianEras != null && julianEras.length >= 2) {
451
newValue[0] = julianEras[1];
452
} else {
453
newValue[0] = "";
454
}
455
System.arraycopy(value, 0, newValue, 1, value.length);
456
value = newValue;
457
}
458
break;
459
460
case BUDDHIST:
461
// Replace the value
462
value = new String[] {"BC", value[0]};
463
break;
464
465
case ISLAMIC:
466
// Replace the value
467
value = new String[] {"", value[0]};
468
break;
469
}
470
if (!key.equals(realKey)) {
471
map.put(realKey, value);
472
}
473
}
474
realKeys[index] = realKey;
475
eraNames[index++] = value;
476
}
477
for (int i = 0; i < eraNames.length; i++) {
478
if (eraNames[i] == null) {
479
map.put(realKeys[i], null);
480
}
481
}
482
}
483
484
private void handleDateTimeFormatPatterns(String[] patternKeys, Map<String, Object> myMap, Map<String, Object> parentsMap,
485
CalendarType calendarType, String name) {
486
String calendarPrefix = calendarType.keyElementName();
487
for (String k : patternKeys) {
488
if (myMap.containsKey(calendarPrefix + k)) {
489
int len = patternKeys.length;
490
List<String> rawPatterns = new ArrayList<>(len);
491
List<String> patterns = new ArrayList<>(len);
492
for (int i = 0; i < len; i++) {
493
String key = calendarPrefix + patternKeys[i];
494
String pattern = (String) myMap.remove(key);
495
if (pattern == null) {
496
pattern = (String) parentsMap.remove(key);
497
}
498
rawPatterns.add(i, pattern);
499
if (pattern != null) {
500
patterns.add(i, translateDateFormatLetters(calendarType, pattern));
501
} else {
502
patterns.add(i, null);
503
}
504
}
505
// If patterns is empty or has any nulls, discard patterns.
506
if (patterns.isEmpty()) {
507
return;
508
}
509
for (String p : patterns) {
510
if (p == null) {
511
return;
512
}
513
}
514
String key = calendarPrefix + name;
515
if (!rawPatterns.equals(patterns)) {
516
myMap.put("java.time." + key, rawPatterns.toArray(new String[len]));
517
}
518
myMap.put(key, patterns.toArray(new String[len]));
519
break;
520
}
521
}
522
}
523
524
private String translateDateFormatLetters(CalendarType calendarType, String cldrFormat) {
525
String pattern = cldrFormat;
526
int length = pattern.length();
527
boolean inQuote = false;
528
StringBuilder jrePattern = new StringBuilder(length);
529
int count = 0;
530
char lastLetter = 0;
531
532
for (int i = 0; i < length; i++) {
533
char c = pattern.charAt(i);
534
535
if (c == '\'') {
536
// '' is treated as a single quote regardless of being
537
// in a quoted section.
538
if ((i + 1) < length) {
539
char nextc = pattern.charAt(i + 1);
540
if (nextc == '\'') {
541
i++;
542
if (count != 0) {
543
convert(calendarType, lastLetter, count, jrePattern);
544
lastLetter = 0;
545
count = 0;
546
}
547
jrePattern.append("''");
548
continue;
549
}
550
}
551
if (!inQuote) {
552
if (count != 0) {
553
convert(calendarType, lastLetter, count, jrePattern);
554
lastLetter = 0;
555
count = 0;
556
}
557
inQuote = true;
558
} else {
559
inQuote = false;
560
}
561
jrePattern.append(c);
562
continue;
563
}
564
if (inQuote) {
565
jrePattern.append(c);
566
continue;
567
}
568
if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
569
if (count != 0) {
570
convert(calendarType, lastLetter, count, jrePattern);
571
lastLetter = 0;
572
count = 0;
573
}
574
jrePattern.append(c);
575
continue;
576
}
577
578
if (lastLetter == 0 || lastLetter == c) {
579
lastLetter = c;
580
count++;
581
continue;
582
}
583
convert(calendarType, lastLetter, count, jrePattern);
584
lastLetter = c;
585
count = 1;
586
}
587
588
if (inQuote) {
589
throw new InternalError("Unterminated quote in date-time pattern: " + cldrFormat);
590
}
591
592
if (count != 0) {
593
convert(calendarType, lastLetter, count, jrePattern);
594
}
595
if (cldrFormat.contentEquals(jrePattern)) {
596
return cldrFormat;
597
}
598
return jrePattern.toString();
599
}
600
601
private String toMetaZoneKey(String tzKey) {
602
if (tzKey.startsWith(CLDRConverter.TIMEZONE_ID_PREFIX)) {
603
String tz = tzKey.substring(CLDRConverter.TIMEZONE_ID_PREFIX.length());
604
String meta = CLDRConverter.handlerMetaZones.get(tz);
605
if (meta != null) {
606
return CLDRConverter.METAZONE_ID_PREFIX + meta;
607
}
608
}
609
return null;
610
}
611
612
private void fillInAbbrs(String key, Map<String, String> map) {
613
fillInAbbrs(TZ_STD_LONG_KEY, TZ_STD_SHORT_KEY, map);
614
fillInAbbrs(TZ_DST_LONG_KEY, TZ_DST_SHORT_KEY, map);
615
fillInAbbrs(TZ_GEN_LONG_KEY, TZ_GEN_SHORT_KEY, map);
616
617
// If the standard std is "Standard Time" and daylight std is "Summer Time",
618
// replace the standard std with the generic std to avoid using
619
// the same abbrivation except for Australia time zone names.
620
String std = map.get(TZ_STD_SHORT_KEY);
621
String dst = map.get(TZ_DST_SHORT_KEY);
622
String gen = map.get(TZ_GEN_SHORT_KEY);
623
if (std != null) {
624
if (dst == null) {
625
// if dst is null, create long and short names from the standard
626
// std. ("Something Standard Time" to "Something Daylight Time",
627
// or "Something Time" to "Something Summer Time")
628
String name = map.get(TZ_STD_LONG_KEY);
629
if (name != null) {
630
if (name.contains("Standard Time")) {
631
name = name.replace("Standard Time", "Daylight Time");
632
} else if (name.endsWith("Mean Time")) {
633
name = name.replace("Mean Time", "Summer Time");
634
} else if (name.endsWith(" Time")) {
635
name = name.replace(" Time", " Summer Time");
636
}
637
map.put(TZ_DST_LONG_KEY, name);
638
fillInAbbrs(TZ_DST_LONG_KEY, TZ_DST_SHORT_KEY, map);
639
}
640
}
641
if (gen == null) {
642
String name = map.get(TZ_STD_LONG_KEY);
643
if (name != null) {
644
if (name.endsWith("Standard Time")) {
645
name = name.replace("Standard Time", "Time");
646
} else if (name.endsWith("Mean Time")) {
647
name = name.replace("Mean Time", "Time");
648
}
649
map.put(TZ_GEN_LONG_KEY, name);
650
fillInAbbrs(TZ_GEN_LONG_KEY, TZ_GEN_SHORT_KEY, map);
651
}
652
}
653
}
654
}
655
656
private void fillInAbbrs(String longKey, String shortKey, Map<String, String> map) {
657
String abbr = map.get(shortKey);
658
if (abbr == null) {
659
String name = map.get(longKey);
660
if (name != null) {
661
abbr = toAbbr(name);
662
if (abbr != null) {
663
map.put(shortKey, abbr);
664
}
665
}
666
}
667
}
668
669
private String toAbbr(String name) {
670
String[] substrs = name.split("\\s+");
671
StringBuilder sb = new StringBuilder();
672
for (String s : substrs) {
673
char c = s.charAt(0);
674
if (c >= 'A' && c <= 'Z') {
675
sb.append(c);
676
}
677
}
678
return sb.length() > 0 ? sb.toString() : null;
679
}
680
681
private void convert(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb) {
682
switch (cldrLetter) {
683
case 'G':
684
if (calendarType != CalendarType.GREGORIAN) {
685
// Adjust the number of 'G's for JRE SimpleDateFormat
686
if (count == 5) {
687
// CLDR narrow -> JRE short
688
count = 1;
689
} else if (count == 1) {
690
// CLDR abbr -> JRE long
691
count = 4;
692
}
693
}
694
appendN(cldrLetter, count, sb);
695
break;
696
697
// TODO: support 'c' and 'e' in JRE SimpleDateFormat
698
// Use 'u' and 'E' for now.
699
case 'c':
700
case 'e':
701
switch (count) {
702
case 1:
703
sb.append('u');
704
break;
705
case 3:
706
case 4:
707
appendN('E', count, sb);
708
break;
709
case 5:
710
appendN('E', 3, sb);
711
break;
712
}
713
break;
714
715
case 'v':
716
case 'V':
717
appendN('z', count, sb);
718
break;
719
720
case 'Z':
721
if (count == 4 || count == 5) {
722
sb.append("XXX");
723
}
724
break;
725
726
case 'u':
727
case 'U':
728
case 'q':
729
case 'Q':
730
case 'l':
731
case 'g':
732
case 'j':
733
case 'A':
734
throw new InternalError(String.format("Unsupported letter: '%c', count=%d%n",
735
cldrLetter, count));
736
default:
737
appendN(cldrLetter, count, sb);
738
break;
739
}
740
}
741
742
private void appendN(char c, int n, StringBuilder sb) {
743
for (int i = 0; i < n; i++) {
744
sb.append(c);
745
}
746
}
747
748
private static boolean hasNulls(Object[] array) {
749
for (int i = 0; i < array.length; i++) {
750
if (array[i] == null) {
751
return true;
752
}
753
}
754
return false;
755
}
756
}
757
758