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.List;
33 import java.util.Map;
34
35 class Bundle {
36 static enum Type {
37 LOCALENAMES, CURRENCYNAMES, TIMEZONENAMES, CALENDARDATA, FORMATDATA;
38
39 static EnumSet<Type> ALL_TYPES = EnumSet.of(LOCALENAMES,
40 CURRENCYNAMES,
41 TIMEZONENAMES,
42 CALENDARDATA,
43 FORMATDATA);
44 }
45
46 private final static Map<String, Bundle> bundles = new HashMap<>();
47
48 private final static String[] NUMBER_PATTERN_KEYS = {
49 "NumberPatterns/decimal",
50 "NumberPatterns/currency",
51 "NumberPatterns/percent"
69 "DateTimePatterns/full-time",
70 "DateTimePatterns/long-time",
71 "DateTimePatterns/medium-time",
72 "DateTimePatterns/short-time",
73 };
74
75 private final static String[] DATE_PATTERN_KEYS = {
76 "DateTimePatterns/full-date",
77 "DateTimePatterns/long-date",
78 "DateTimePatterns/medium-date",
79 "DateTimePatterns/short-date",
80 };
81
82 private final static String[] DATETIME_PATTERN_KEYS = {
83 "DateTimePatterns/date-time"
84 };
85
86 private final static String[] ERA_KEYS = {
87 "long.Eras",
88 "Eras",
89 "short.Eras"
90 };
91
92 private final String id;
93 private final String cldrPath;
94 private final EnumSet<Type> bundleTypes;
95 private final String currencies;
96
97 static Bundle getBundle(String id) {
98 return bundles.get(id);
99 }
100
101 Bundle(String id, String cldrPath, String bundles, String currencies) {
102 this.id = id;
103 this.cldrPath = cldrPath;
104 if ("localenames".equals(bundles)) {
105 bundleTypes = EnumSet.of(Type.LOCALENAMES);
106 } else if ("currencynames".equals(bundles)) {
107 bundleTypes = EnumSet.of(Type.CURRENCYNAMES);
108 } else {
109 bundleTypes = Type.ALL_TYPES;
110 }
111 if (currencies == null) {
112 currencies = "local";
113 }
114 this.currencies = currencies;
115 addBundle();
116 }
117
118 private void addBundle() {
119 Bundle.bundles.put(id, this);
120 }
225 }
226 }
227 numberElements[i] = value;
228 }
229 myMap.put(script + "." + "NumberElements", numberElements);
230 break;
231 }
232 }
233 }
234
235 // another hack: parentsMap is not used for date-time resources.
236 if ("root".equals(id)) {
237 parentsMap = null;
238 }
239
240 for (CalendarType calendarType : CalendarType.values()) {
241 String calendarPrefix = calendarType.keyElementName();
242 // handle multiple inheritance for month and day names
243 handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "MonthNames");
244 handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "MonthAbbreviations");
245 handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "DayNames");
246 handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "DayAbbreviations");
247 handleMultipleInheritance(myMap, parentsMap, calendarPrefix + "AmPmMarkers");
248
249 adjustEraNames(myMap, calendarType);
250
251 handleDateTimeFormatPatterns(TIME_PATTERN_KEYS, myMap, parentsMap, calendarType, "TimePatterns");
252 handleDateTimeFormatPatterns(DATE_PATTERN_KEYS, myMap, parentsMap, calendarType, "DatePatterns");
253 handleDateTimeFormatPatterns(DATETIME_PATTERN_KEYS, myMap, parentsMap, calendarType, "DateTimePatterns");
254 }
255
256 return myMap;
257 }
258
259 private void handleMultipleInheritance(Map<String, Object> map, Map<String, Object> parents, String key) {
260 String formatKey = key + "/format";
261 Object format = map.get(formatKey);
262 if (format != null) {
263 map.remove(formatKey);
264 map.put(key, format);
265 if (fillInElements(parents, formatKey, format)) {
266 map.remove(key);
267 }
268 }
269 String standaloneKey = key + "/stand-alone";
270 Object standalone = map.get(standaloneKey);
271 if (standalone != null) {
272 map.remove(standaloneKey);
273 String realKey = key;
274 if (format != null) {
275 realKey = "standalone." + key;
335 } else {
336 newValue[0] = "";
337 }
338 System.arraycopy(value, 0, newValue, 1, value.length);
339 value = newValue;
340 }
341 break;
342
343 case BUDDHIST:
344 // Replace the value
345 value = new String[] {"BC", value[0]};
346 break;
347 }
348 if (!key.equals(realKey)) {
349 map.put(realKey, value);
350 }
351 }
352 realKeys[index] = realKey;
353 eraNames[index++] = value;
354 }
355 if (eraNames[0] != null) {
356 if (eraNames[1] != null) {
357 if (eraNames[2] == null) {
358 // Eras -> short.Eras
359 // long.Eras -> Eras
360 map.put(realKeys[2], map.get(realKeys[1]));
361 map.put(realKeys[1], map.get(realKeys[0]));
362 }
363 } else {
364 // long.Eras -> Eras
365 map.put(realKeys[1], map.get(realKeys[0]));
366 }
367 // remove long.Eras
368 map.remove(realKeys[0]);
369 }
370 }
371
372 private void handleDateTimeFormatPatterns(String[] patternKeys, Map<String, Object> myMap, Map<String, Object> parentsMap,
373 CalendarType calendarType, String name) {
374 String calendarPrefix = calendarType.keyElementName();
375 for (String k : patternKeys) {
376 if (myMap.containsKey(calendarPrefix + k)) {
377 int len = patternKeys.length;
378 List<String> patterns = new ArrayList<>();
379 for (int i = 0; i < len; i++) {
380 String key = calendarPrefix + patternKeys[i];
381 String pattern = (String) myMap.remove(key);
382 if (pattern == null) {
383 pattern = (String) parentsMap.remove(key);
384 }
385 if (pattern != null) {
386 patterns.add(i, translateDateFormatLetters(calendarType, pattern));
387 }
388 }
456 continue;
457 }
458 convert(calendarType, lastLetter, count, jrePattern);
459 lastLetter = c;
460 count = 1;
461 }
462
463 if (inQuote) {
464 throw new InternalError("Unterminated quote in date-time pattern: " + cldrFormat);
465 }
466
467 if (count != 0) {
468 convert(calendarType, lastLetter, count, jrePattern);
469 }
470 if (cldrFormat.contentEquals(jrePattern)) {
471 return cldrFormat;
472 }
473 return jrePattern.toString();
474 }
475
476 private void convert(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb) {
477 switch (cldrLetter) {
478 case 'G':
479 if (calendarType != CalendarType.GREGORIAN) {
480 // Adjust the number of 'G's for JRE SimpleDateFormat
481 if (count == 5) {
482 // CLDR narrow -> JRE short
483 count = 1;
484 } else if (count == 1) {
485 // CLDR abbr -> JRE long
486 count = 4;
487 }
488 }
489 appendN(cldrLetter, count, sb);
490 break;
491
492 // TODO: support 'c' and 'e' in JRE SimpleDateFormat
493 // Use 'u' and 'E' for now.
494 case 'c':
495 case 'e':
521 case 'u':
522 case 'U':
523 case 'q':
524 case 'Q':
525 case 'l':
526 case 'g':
527 case 'j':
528 case 'A':
529 throw new InternalError(String.format("Unsupported letter: '%c', count=%d%n",
530 cldrLetter, count));
531 default:
532 appendN(cldrLetter, count, sb);
533 break;
534 }
535 }
536
537 private void appendN(char c, int n, StringBuilder sb) {
538 for (int i = 0; i < n; i++) {
539 sb.append(c);
540 }
541 }
542 }
|
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"
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 }
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
270 adjustEraNames(myMap, calendarType);
271
272 handleDateTimeFormatPatterns(TIME_PATTERN_KEYS, myMap, parentsMap, calendarType, "TimePatterns");
273 handleDateTimeFormatPatterns(DATE_PATTERN_KEYS, myMap, parentsMap, calendarType, "DatePatterns");
274 handleDateTimeFormatPatterns(DATETIME_PATTERN_KEYS, myMap, parentsMap, calendarType, "DateTimePatterns");
275 }
276
277 // if myMap has any empty timezone or metazone names, weed out them.
278 // Fill in any missing abbreviations if locale is "en".
279 for (Iterator<String> it = myMap.keySet().iterator(); it.hasNext();) {
280 String key = it.next();
281 if (key.startsWith(CLDRConverter.TIMEZONE_ID_PREFIX)
282 || key.startsWith(CLDRConverter.METAZONE_ID_PREFIX)) {
283 @SuppressWarnings("unchecked")
284 Map<String, String> nameMap = (Map<String, String>) myMap.get(key);
285 if (nameMap.isEmpty()) {
286 // Some zones have only exemplarCity, which become empty.
287 // Remove those from the map.
288 it.remove();
289 continue;
290 }
291
292 if (id.startsWith("en")) {
293 fillInAbbrs(key, nameMap);
294 }
295 }
296 }
297 for (Iterator<String> it = myMap.keySet().iterator(); it.hasNext();) {
298 String key = it.next();
299 if (key.startsWith(CLDRConverter.TIMEZONE_ID_PREFIX)
300 || key.startsWith(CLDRConverter.METAZONE_ID_PREFIX)) {
301 @SuppressWarnings("unchecked")
302 Map<String, String> nameMap = (Map<String, String>) myMap.get(key);
303 // Convert key/value pairs to an array.
304 String[] names = new String[ZONE_NAME_KEYS.length];
305 int ix = 0;
306 for (String nameKey : ZONE_NAME_KEYS) {
307 String name = nameMap.get(nameKey);
308 if (name == null) {
309 @SuppressWarnings("unchecked")
310 Map<String, String> parentNames = (Map<String, String>) parentsMap.get(key);
311 if (parentNames != null) {
312 name = parentNames.get(nameKey);
313 }
314 }
315 names[ix++] = name;
316 }
317 if (hasNulls(names)) {
318 String metaKey = toMetaZoneKey(key);
319 if (metaKey != null) {
320 Object obj = myMap.get(metaKey);
321 if (obj instanceof String[]) {
322 String[] metaNames = (String[]) obj;
323 for (int i = 0; i < names.length; i++) {
324 if (names[i] == null) {
325 names[i] = metaNames[i];
326 }
327 }
328 } else if (obj instanceof Map) {
329 @SuppressWarnings("unchecked")
330 Map<String, String> m = (Map<String, String>) obj;
331 for (int i = 0; i < names.length; i++) {
332 if (names[i] == null) {
333 names[i] = m.get(ZONE_NAME_KEYS[i]);
334 }
335 }
336 }
337 }
338 // If there are still any nulls, try filling in them from en data.
339 if (hasNulls(names) && !id.equals("en")) {
340 @SuppressWarnings("unchecked")
341 String[] enNames = (String[]) Bundle.getBundle("en").getTargetMap().get(key);
342 if (enNames == null) {
343 if (metaKey != null) {
344 @SuppressWarnings("unchecked")
345 String[] metaNames = (String[]) Bundle.getBundle("en").getTargetMap().get(metaKey);
346 enNames = metaNames;
347 }
348 }
349 if (enNames != null) {
350 for (int i = 0; i < names.length; i++) {
351 if (names[i] == null) {
352 names[i] = enNames[i];
353 }
354 }
355 }
356 // If there are still nulls, give up names.
357 if (hasNulls(names)) {
358 names = null;
359 }
360 }
361 }
362 // replace the Map with the array
363 if (names != null) {
364 myMap.put(key, names);
365 } else {
366 it.remove();
367 }
368 }
369 }
370 return myMap;
371 }
372
373 private void handleMultipleInheritance(Map<String, Object> map, Map<String, Object> parents, String key) {
374 String formatKey = key + "/format";
375 Object format = map.get(formatKey);
376 if (format != null) {
377 map.remove(formatKey);
378 map.put(key, format);
379 if (fillInElements(parents, formatKey, format)) {
380 map.remove(key);
381 }
382 }
383 String standaloneKey = key + "/stand-alone";
384 Object standalone = map.get(standaloneKey);
385 if (standalone != null) {
386 map.remove(standaloneKey);
387 String realKey = key;
388 if (format != null) {
389 realKey = "standalone." + key;
449 } else {
450 newValue[0] = "";
451 }
452 System.arraycopy(value, 0, newValue, 1, value.length);
453 value = newValue;
454 }
455 break;
456
457 case BUDDHIST:
458 // Replace the value
459 value = new String[] {"BC", value[0]};
460 break;
461 }
462 if (!key.equals(realKey)) {
463 map.put(realKey, value);
464 }
465 }
466 realKeys[index] = realKey;
467 eraNames[index++] = value;
468 }
469 for (int i = 0; i < eraNames.length; i++) {
470 if (eraNames[i] == null) {
471 map.put(realKeys[i], null);
472 }
473 }
474 }
475
476 private void handleDateTimeFormatPatterns(String[] patternKeys, Map<String, Object> myMap, Map<String, Object> parentsMap,
477 CalendarType calendarType, String name) {
478 String calendarPrefix = calendarType.keyElementName();
479 for (String k : patternKeys) {
480 if (myMap.containsKey(calendarPrefix + k)) {
481 int len = patternKeys.length;
482 List<String> patterns = new ArrayList<>();
483 for (int i = 0; i < len; i++) {
484 String key = calendarPrefix + patternKeys[i];
485 String pattern = (String) myMap.remove(key);
486 if (pattern == null) {
487 pattern = (String) parentsMap.remove(key);
488 }
489 if (pattern != null) {
490 patterns.add(i, translateDateFormatLetters(calendarType, pattern));
491 }
492 }
560 continue;
561 }
562 convert(calendarType, lastLetter, count, jrePattern);
563 lastLetter = c;
564 count = 1;
565 }
566
567 if (inQuote) {
568 throw new InternalError("Unterminated quote in date-time pattern: " + cldrFormat);
569 }
570
571 if (count != 0) {
572 convert(calendarType, lastLetter, count, jrePattern);
573 }
574 if (cldrFormat.contentEquals(jrePattern)) {
575 return cldrFormat;
576 }
577 return jrePattern.toString();
578 }
579
580 private String toMetaZoneKey(String tzKey) {
581 if (tzKey.startsWith(CLDRConverter.TIMEZONE_ID_PREFIX)) {
582 String tz = tzKey.substring(CLDRConverter.TIMEZONE_ID_PREFIX.length());
583 String meta = CLDRConverter.handlerMetaZones.get(tz);
584 if (meta != null) {
585 return CLDRConverter.METAZONE_ID_PREFIX + meta;
586 }
587 }
588 return null;
589 }
590
591 private void fillInAbbrs(String key, Map<String, String> map) {
592 fillInAbbrs(TZ_STD_LONG_KEY, TZ_STD_SHORT_KEY, map);
593 fillInAbbrs(TZ_DST_LONG_KEY, TZ_DST_SHORT_KEY, map);
594 fillInAbbrs(TZ_GEN_LONG_KEY, TZ_GEN_SHORT_KEY, map);
595
596 // If the standard std is "Standard Time" and daylight std is "Summer Time",
597 // replace the standard std with the generic std to avoid using
598 // the same abbrivation except for Australia time zone names.
599 String std = map.get(TZ_STD_SHORT_KEY);
600 String dst = map.get(TZ_DST_SHORT_KEY);
601 String gen = map.get(TZ_GEN_SHORT_KEY);
602 if (std != null) {
603 if (dst == null) {
604 // if dst is null, create long and short names from the standard
605 // std. ("Something Standard Time" to "Something Daylight Time",
606 // or "Something Time" to "Something Summer Time")
607 String name = map.get(TZ_STD_LONG_KEY);
608 if (name != null) {
609 if (name.contains("Standard Time")) {
610 name = name.replace("Standard Time", "Daylight Time");
611 } else if (name.endsWith("Mean Time")) {
612 name = name.replace("Mean Time", "Summer Time");
613 } else if (name.endsWith(" Time")) {
614 name = name.replace(" Time", " Summer Time");
615 }
616 map.put(TZ_DST_LONG_KEY, name);
617 fillInAbbrs(TZ_DST_LONG_KEY, TZ_DST_SHORT_KEY, map);
618 }
619 }
620 if (gen == null) {
621 String name = map.get(TZ_STD_LONG_KEY);
622 if (name != null) {
623 if (name.endsWith("Standard Time")) {
624 name = name.replace("Standard Time", "Time");
625 } else if (name.endsWith("Mean Time")) {
626 name = name.replace("Mean Time", "Time");
627 }
628 map.put(TZ_GEN_LONG_KEY, name);
629 fillInAbbrs(TZ_GEN_LONG_KEY, TZ_GEN_SHORT_KEY, map);
630 }
631 }
632 }
633 }
634
635 private void fillInAbbrs(String longKey, String shortKey, Map<String, String> map) {
636 String abbr = map.get(shortKey);
637 if (abbr == null) {
638 String name = map.get(longKey);
639 if (name != null) {
640 abbr = toAbbr(name);
641 if (abbr != null) {
642 map.put(shortKey, abbr);
643 }
644 }
645 }
646 }
647
648 private String toAbbr(String name) {
649 String[] substrs = name.split("\\s+");
650 StringBuilder sb = new StringBuilder();
651 for (String s : substrs) {
652 char c = s.charAt(0);
653 if (c >= 'A' && c <= 'Z') {
654 sb.append(c);
655 }
656 }
657 return sb.length() > 0 ? sb.toString() : null;
658 }
659
660 private void convert(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb) {
661 switch (cldrLetter) {
662 case 'G':
663 if (calendarType != CalendarType.GREGORIAN) {
664 // Adjust the number of 'G's for JRE SimpleDateFormat
665 if (count == 5) {
666 // CLDR narrow -> JRE short
667 count = 1;
668 } else if (count == 1) {
669 // CLDR abbr -> JRE long
670 count = 4;
671 }
672 }
673 appendN(cldrLetter, count, sb);
674 break;
675
676 // TODO: support 'c' and 'e' in JRE SimpleDateFormat
677 // Use 'u' and 'E' for now.
678 case 'c':
679 case 'e':
705 case 'u':
706 case 'U':
707 case 'q':
708 case 'Q':
709 case 'l':
710 case 'g':
711 case 'j':
712 case 'A':
713 throw new InternalError(String.format("Unsupported letter: '%c', count=%d%n",
714 cldrLetter, count));
715 default:
716 appendN(cldrLetter, count, sb);
717 break;
718 }
719 }
720
721 private void appendN(char c, int n, StringBuilder sb) {
722 for (int i = 0; i < n; i++) {
723 sb.append(c);
724 }
725 }
726
727 private static boolean hasNulls(Object[] array) {
728 for (int i = 0; i < array.length; i++) {
729 if (array[i] == null) {
730 return true;
731 }
732 }
733 return false;
734 }
735 }
|