Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java
41139 views
1
/*
2
* Copyright (c) 2012, 2019, 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 sun.util.locale.provider;
27
28
import java.lang.ref.SoftReference;
29
import java.text.*;
30
import java.text.spi.DateFormatProvider;
31
import java.text.spi.DateFormatSymbolsProvider;
32
import java.text.spi.DecimalFormatSymbolsProvider;
33
import java.text.spi.NumberFormatProvider;
34
import java.util.Collections;
35
import java.util.Calendar;
36
import java.util.HashMap;
37
import java.util.HashSet;
38
import java.util.Locale;
39
import java.util.Map;
40
import java.util.ResourceBundle.Control;
41
import java.util.Set;
42
import java.util.TimeZone;
43
import java.util.concurrent.ConcurrentHashMap;
44
import java.util.concurrent.ConcurrentMap;
45
import java.util.concurrent.atomic.AtomicReferenceArray;
46
import java.util.spi.CalendarDataProvider;
47
import java.util.spi.CalendarNameProvider;
48
import java.util.spi.CurrencyNameProvider;
49
import java.util.spi.LocaleNameProvider;
50
import java.util.spi.TimeZoneNameProvider;
51
import sun.text.spi.JavaTimeDateTimePatternProvider;
52
import sun.util.spi.CalendarProvider;
53
54
/**
55
* LocaleProviderAdapter implementation for the Mac OS X locale data
56
*
57
* @author Naoto Sato
58
*/
59
public class HostLocaleProviderAdapterImpl {
60
61
// per supported locale instances
62
private static final ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> dateFormatPatternsMap =
63
new ConcurrentHashMap<>(2);
64
private static final ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> numberFormatPatternsMap =
65
new ConcurrentHashMap<>(2);
66
private static final ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> dateFormatSymbolsMap =
67
new ConcurrentHashMap<>(2);
68
private static final ConcurrentMap<Locale, SoftReference<DecimalFormatSymbols>> decimalFormatSymbolsMap =
69
new ConcurrentHashMap<>(2);
70
71
// locale categories
72
private static final int CAT_DISPLAY = 0;
73
private static final int CAT_FORMAT = 1;
74
75
// NumberFormat styles
76
private static final int NF_NUMBER = 0;
77
private static final int NF_CURRENCY = 1;
78
private static final int NF_PERCENT = 2;
79
private static final int NF_INTEGER = 3;
80
private static final int NF_MAX = NF_INTEGER;
81
82
// CalendarData value types
83
private static final int CD_FIRSTDAYOFWEEK = 0;
84
private static final int CD_MINIMALDAYSINFIRSTWEEK = 1;
85
86
// Locale/Currency display name types
87
private static final int DN_LOCALE_LANGUAGE = 0;
88
private static final int DN_LOCALE_SCRIPT = 1;
89
private static final int DN_LOCALE_REGION = 2;
90
private static final int DN_LOCALE_VARIANT = 3;
91
private static final int DN_CURRENCY_CODE = 4;
92
private static final int DN_CURRENCY_SYMBOL = 5;
93
94
// TimeZone display name types
95
private static final int DN_TZ_SHORT_STANDARD = 0;
96
private static final int DN_TZ_SHORT_DST = 1;
97
private static final int DN_TZ_LONG_STANDARD = 2;
98
private static final int DN_TZ_LONG_DST = 3;
99
100
private static final Set<Locale> supportedLocaleSet;
101
static {
102
Set<Locale> tmpSet = new HashSet<>();
103
// Assuming the default locales do not include any extensions, so
104
// no stripping is needed here.
105
Locale l = convertMacOSXLocaleToJavaLocale(getDefaultLocale(CAT_FORMAT));
106
tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
107
l = convertMacOSXLocaleToJavaLocale(getDefaultLocale(CAT_DISPLAY));
108
tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
109
supportedLocaleSet = Collections.unmodifiableSet(tmpSet);
110
}
111
private static final Locale[] supportedLocale = supportedLocaleSet.toArray(new Locale[0]);
112
113
@SuppressWarnings("fallthrough")
114
private static Locale convertMacOSXLocaleToJavaLocale(String macosxloc) {
115
// MacOSX may return ICU notation, here is the quote from CFLocale doc:
116
// "The corresponding value is a CFString containing the POSIX locale
117
// identifier as used by ICU, such as "ja_JP". If you have a variant
118
// locale or a different currency or calendar, it can be as complex as
119
// "en_US_POSIX@calendar=japanese;currency=EUR" or
120
// "az_Cyrl_AZ@calendar=buddhist;currency=JPY".
121
String[] tmp = macosxloc.split("@");
122
String langTag = tmp[0].replace('_', '-');
123
if (tmp.length > 1) {
124
String[] ext = tmp[1].split(";");
125
for (String keyval : ext) {
126
// We are only interested in "calendar" value for now.
127
if (keyval.startsWith("calendar=")) {
128
String calid = keyval.substring(keyval.indexOf('=')+1);
129
switch (calid) {
130
case "gregorian":
131
langTag += "-u-ca-gregory";
132
break;
133
case "japanese":
134
// Tweak for ja_JP_JP
135
if (tmp[0].equals("ja_JP")) {
136
return JRELocaleConstants.JA_JP_JP;
137
}
138
139
// fall through
140
141
default:
142
langTag += "-u-ca-" + calid;
143
break;
144
}
145
}
146
}
147
}
148
149
return Locale.forLanguageTag(langTag);
150
}
151
152
public static JavaTimeDateTimePatternProvider getJavaTimeDateTimePatternProvider() {
153
return new JavaTimeDateTimePatternProvider() {
154
@Override
155
public Locale[] getAvailableLocales() {
156
return getSupportedCalendarLocales();
157
}
158
159
@Override
160
public boolean isSupportedLocale(Locale locale) {
161
return isSupportedCalendarLocale(locale);
162
}
163
164
@Override
165
public String getJavaTimeDateTimePattern(int timeStyle, int dateStyle, String calType, Locale locale) {
166
return toJavaTimeDateTimePattern(calType, getDateTimePattern(dateStyle, timeStyle, locale));
167
168
}
169
170
private String getDateTimePattern(int dateStyle, int timeStyle, Locale locale) {
171
AtomicReferenceArray<String> dateFormatPatterns;
172
SoftReference<AtomicReferenceArray<String>> ref = dateFormatPatternsMap.get(locale);
173
174
if (ref == null || (dateFormatPatterns = ref.get()) == null) {
175
dateFormatPatterns = new AtomicReferenceArray<>(5 * 5);
176
ref = new SoftReference<>(dateFormatPatterns);
177
dateFormatPatternsMap.put(locale, ref);
178
}
179
int index = (dateStyle + 1) * 5 + timeStyle + 1;
180
String pattern = dateFormatPatterns.get(index);
181
if (pattern == null) {
182
String langTag = locale.toLanguageTag();
183
pattern = translateDateFormatLetters(getCalendarID(langTag),
184
getDateTimePatternNative(dateStyle, timeStyle, langTag));
185
if (!dateFormatPatterns.compareAndSet(index, null, pattern)) {
186
pattern = dateFormatPatterns.get(index);
187
}
188
}
189
return pattern;
190
}
191
192
/**
193
* This method will convert JRE Date/time Pattern String to JSR310
194
* type Date/Time Pattern
195
*/
196
private String toJavaTimeDateTimePattern(String calendarType, String jrePattern) {
197
int length = jrePattern.length();
198
StringBuilder sb = new StringBuilder(length);
199
boolean inQuote = false;
200
int count = 0;
201
char lastLetter = 0;
202
for (int i = 0; i < length; i++) {
203
char c = jrePattern.charAt(i);
204
if (c == '\'') {
205
// '' is treated as a single quote regardless of being
206
// in a quoted section.
207
if ((i + 1) < length) {
208
char nextc = jrePattern.charAt(i + 1);
209
if (nextc == '\'') {
210
i++;
211
if (count != 0) {
212
convert(calendarType, lastLetter, count, sb);
213
lastLetter = 0;
214
count = 0;
215
}
216
sb.append("''");
217
continue;
218
}
219
}
220
if (!inQuote) {
221
if (count != 0) {
222
convert(calendarType, lastLetter, count, sb);
223
lastLetter = 0;
224
count = 0;
225
}
226
inQuote = true;
227
} else {
228
inQuote = false;
229
}
230
sb.append(c);
231
continue;
232
}
233
if (inQuote) {
234
sb.append(c);
235
continue;
236
}
237
if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
238
if (count != 0) {
239
convert(calendarType, lastLetter, count, sb);
240
lastLetter = 0;
241
count = 0;
242
}
243
sb.append(c);
244
continue;
245
}
246
if (lastLetter == 0 || lastLetter == c) {
247
lastLetter = c;
248
count++;
249
continue;
250
}
251
convert(calendarType, lastLetter, count, sb);
252
lastLetter = c;
253
count = 1;
254
}
255
if (inQuote) {
256
// should not come here.
257
// returning null so that FALLBACK provider will kick in.
258
return null;
259
}
260
if (count != 0) {
261
convert(calendarType, lastLetter, count, sb);
262
}
263
return sb.toString();
264
}
265
266
private void convert(String calendarType, char letter, int count, StringBuilder sb) {
267
switch (letter) {
268
case 'G':
269
if (calendarType.equals("japanese")) {
270
if (count >= 4) {
271
count = 1;
272
} else {
273
count = 5;
274
}
275
} else if (!calendarType.equals("iso8601")) {
276
// Gregorian calendar is iso8601 for java.time
277
// Adjust the number of 'G's
278
if (count >= 4) {
279
// JRE full -> JavaTime full
280
count = 4;
281
} else {
282
// JRE short -> JavaTime short
283
count = 1;
284
}
285
}
286
break;
287
case 'y':
288
if (calendarType.equals("japanese") && count >= 4) {
289
// JRE specific "gan-nen" support
290
count = 1;
291
}
292
break;
293
default:
294
// JSR 310 and CLDR define 5-letter patterns for narrow text.
295
if (count > 4) {
296
count = 4;
297
}
298
break;
299
}
300
appendN(letter, count, sb);
301
}
302
303
private void appendN(char c, int n, StringBuilder sb) {
304
for (int i = 0; i < n; i++) {
305
sb.append(c);
306
}
307
}
308
};
309
}
310
311
public static DateFormatProvider getDateFormatProvider() {
312
return new DateFormatProvider() {
313
314
@Override
315
public Locale[] getAvailableLocales() {
316
return getSupportedCalendarLocales();
317
}
318
319
@Override
320
public boolean isSupportedLocale(Locale locale) {
321
return isSupportedCalendarLocale(locale);
322
}
323
324
@Override
325
public DateFormat getDateInstance(int style, Locale locale) {
326
return new SimpleDateFormat(getDateTimePattern(style, -1, locale),
327
getCalendarLocale(locale));
328
}
329
330
@Override
331
public DateFormat getTimeInstance(int style, Locale locale) {
332
return new SimpleDateFormat(getDateTimePattern(-1, style, locale),
333
getCalendarLocale(locale));
334
}
335
336
@Override
337
public DateFormat getDateTimeInstance(int dateStyle,
338
int timeStyle, Locale locale) {
339
return new SimpleDateFormat(getDateTimePattern(dateStyle, timeStyle, locale),
340
getCalendarLocale(locale));
341
}
342
343
private String getDateTimePattern(int dateStyle, int timeStyle, Locale locale) {
344
AtomicReferenceArray<String> dateFormatPatterns;
345
SoftReference<AtomicReferenceArray<String>> ref = dateFormatPatternsMap.get(locale);
346
347
if (ref == null || (dateFormatPatterns = ref.get()) == null) {
348
dateFormatPatterns = new AtomicReferenceArray<>(5 * 5);
349
ref = new SoftReference<>(dateFormatPatterns);
350
dateFormatPatternsMap.put(locale, ref);
351
}
352
353
int index = (dateStyle + 1) * 5 + timeStyle + 1;
354
String pattern = dateFormatPatterns.get(index);
355
if (pattern == null) {
356
String langTag = locale.toLanguageTag();
357
pattern = translateDateFormatLetters(getCalendarID(langTag),
358
getDateTimePatternNative(dateStyle, timeStyle, langTag));
359
if (!dateFormatPatterns.compareAndSet(index, null, pattern)) {
360
pattern = dateFormatPatterns.get(index);
361
}
362
}
363
364
return pattern;
365
}
366
};
367
}
368
369
public static DateFormatSymbolsProvider getDateFormatSymbolsProvider() {
370
return new DateFormatSymbolsProvider() {
371
@Override
372
public Locale[] getAvailableLocales() {
373
if (isSupportedLocale(Locale.getDefault(Locale.Category.FORMAT))) {
374
return supportedLocale;
375
}
376
return new Locale[0];
377
}
378
379
@Override
380
public boolean isSupportedLocale(Locale locale) {
381
// Only supports the locale with Gregorian calendar
382
Locale base = locale.stripExtensions();
383
if (supportedLocaleSet.contains(base)) {
384
return getCalendarID(locale.toLanguageTag()).equals("gregorian");
385
}
386
return false;
387
}
388
389
@Override
390
public DateFormatSymbols getInstance(Locale locale) {
391
DateFormatSymbols dateFormatSymbols;
392
SoftReference<DateFormatSymbols> ref = dateFormatSymbolsMap.get(locale);
393
394
if (ref == null || (dateFormatSymbols = ref.get()) == null) {
395
dateFormatSymbols = new DateFormatSymbols(locale);
396
String langTag = locale.toLanguageTag();
397
dateFormatSymbols.setAmPmStrings(getAmPmStrings(langTag, dateFormatSymbols.getAmPmStrings()));
398
dateFormatSymbols.setEras(getEras(langTag, dateFormatSymbols.getEras()));
399
dateFormatSymbols.setMonths(getMonths(langTag, dateFormatSymbols.getMonths()));
400
dateFormatSymbols.setShortMonths(getShortMonths(langTag, dateFormatSymbols.getShortMonths()));
401
dateFormatSymbols.setWeekdays(getWeekdays(langTag, dateFormatSymbols.getWeekdays()));
402
dateFormatSymbols.setShortWeekdays(getShortWeekdays(langTag, dateFormatSymbols.getShortWeekdays()));
403
ref = new SoftReference<>(dateFormatSymbols);
404
dateFormatSymbolsMap.put(locale, ref);
405
}
406
return (DateFormatSymbols)dateFormatSymbols.clone();
407
}
408
};
409
}
410
411
public static NumberFormatProvider getNumberFormatProvider() {
412
return new NumberFormatProvider() {
413
@Override
414
public Locale[] getAvailableLocales() {
415
return supportedLocale;
416
}
417
418
@Override
419
public boolean isSupportedLocale(Locale locale) {
420
// Ignore the extensions for now
421
return supportedLocaleSet.contains(locale.stripExtensions());
422
}
423
424
@Override
425
public NumberFormat getCurrencyInstance(Locale locale) {
426
return new DecimalFormat(getNumberPattern(NF_CURRENCY, locale),
427
DecimalFormatSymbols.getInstance(locale));
428
}
429
430
@Override
431
public NumberFormat getIntegerInstance(Locale locale) {
432
DecimalFormat format = new DecimalFormat(getNumberPattern(NF_INTEGER, locale),
433
DecimalFormatSymbols.getInstance(locale));
434
return HostLocaleProviderAdapter.makeIntegerFormatter(format);
435
}
436
437
@Override
438
public NumberFormat getNumberInstance(Locale locale) {
439
return new DecimalFormat(getNumberPattern(NF_NUMBER, locale),
440
DecimalFormatSymbols.getInstance(locale));
441
}
442
443
@Override
444
public NumberFormat getPercentInstance(Locale locale) {
445
return new DecimalFormat(getNumberPattern(NF_PERCENT, locale),
446
DecimalFormatSymbols.getInstance(locale));
447
}
448
449
private String getNumberPattern(int style, Locale locale) {
450
AtomicReferenceArray<String> numberFormatPatterns;
451
SoftReference<AtomicReferenceArray<String>> ref = numberFormatPatternsMap.get(locale);
452
453
if (ref == null || (numberFormatPatterns = ref.get()) == null) {
454
numberFormatPatterns = new AtomicReferenceArray<>(4);
455
ref = new SoftReference<>(numberFormatPatterns);
456
numberFormatPatternsMap.put(locale, ref);
457
}
458
459
String pattern = numberFormatPatterns.get(style);
460
if (pattern == null) {
461
pattern = getNumberPatternNative(style, locale.toLanguageTag());
462
if (!numberFormatPatterns.compareAndSet(style, null, pattern)) {
463
pattern = numberFormatPatterns.get(style);
464
}
465
}
466
467
return pattern;
468
}
469
};
470
}
471
472
public static DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() {
473
return new DecimalFormatSymbolsProvider() {
474
475
@Override
476
public Locale[] getAvailableLocales() {
477
return supportedLocale;
478
}
479
480
@Override
481
public boolean isSupportedLocale(Locale locale) {
482
// Ignore the extensions for now
483
return supportedLocaleSet.contains(locale.stripExtensions());
484
}
485
486
@Override
487
public DecimalFormatSymbols getInstance(Locale locale) {
488
DecimalFormatSymbols decimalFormatSymbols;
489
SoftReference<DecimalFormatSymbols> ref = decimalFormatSymbolsMap.get(locale);
490
491
if (ref == null || (decimalFormatSymbols = ref.get()) == null) {
492
decimalFormatSymbols = new DecimalFormatSymbols(locale);
493
String langTag = locale.toLanguageTag();
494
495
// DecimalFormatSymbols.setInternationalCurrencySymbol() has
496
// a side effect of setting the currency symbol as well. So
497
// the calling order is relevant here.
498
decimalFormatSymbols.setInternationalCurrencySymbol(getInternationalCurrencySymbol(langTag, decimalFormatSymbols.getInternationalCurrencySymbol()));
499
decimalFormatSymbols.setCurrencySymbol(getCurrencySymbol(langTag, decimalFormatSymbols.getCurrencySymbol()));
500
decimalFormatSymbols.setDecimalSeparator(getDecimalSeparator(langTag, decimalFormatSymbols.getDecimalSeparator()));
501
decimalFormatSymbols.setGroupingSeparator(getGroupingSeparator(langTag, decimalFormatSymbols.getGroupingSeparator()));
502
decimalFormatSymbols.setInfinity(getInfinity(langTag, decimalFormatSymbols.getInfinity()));
503
decimalFormatSymbols.setMinusSign(getMinusSign(langTag, decimalFormatSymbols.getMinusSign()));
504
decimalFormatSymbols.setMonetaryDecimalSeparator(getMonetaryDecimalSeparator(langTag, decimalFormatSymbols.getMonetaryDecimalSeparator()));
505
decimalFormatSymbols.setNaN(getNaN(langTag, decimalFormatSymbols.getNaN()));
506
decimalFormatSymbols.setPercent(getPercent(langTag, decimalFormatSymbols.getPercent()));
507
decimalFormatSymbols.setPerMill(getPerMill(langTag, decimalFormatSymbols.getPerMill()));
508
decimalFormatSymbols.setZeroDigit(getZeroDigit(langTag, decimalFormatSymbols.getZeroDigit()));
509
decimalFormatSymbols.setExponentSeparator(getExponentSeparator(langTag, decimalFormatSymbols.getExponentSeparator()));
510
ref = new SoftReference<>(decimalFormatSymbols);
511
decimalFormatSymbolsMap.put(locale, ref);
512
}
513
return (DecimalFormatSymbols)decimalFormatSymbols.clone();
514
}
515
};
516
}
517
518
public static CalendarDataProvider getCalendarDataProvider() {
519
return new CalendarDataProvider() {
520
@Override
521
public Locale[] getAvailableLocales() {
522
return getSupportedCalendarLocales();
523
}
524
525
@Override
526
public boolean isSupportedLocale(Locale locale) {
527
return isSupportedCalendarLocale(locale);
528
}
529
530
@Override
531
public int getFirstDayOfWeek(Locale locale) {
532
return getCalendarInt(locale.toLanguageTag(), CD_FIRSTDAYOFWEEK);
533
}
534
535
@Override
536
public int getMinimalDaysInFirstWeek(Locale locale) {
537
return getCalendarInt(locale.toLanguageTag(), CD_MINIMALDAYSINFIRSTWEEK);
538
}
539
};
540
}
541
542
public static CalendarNameProvider getCalendarNameProvider() {
543
return new CalendarNameProvider() {
544
@Override
545
public Locale[] getAvailableLocales() {
546
return getSupportedCalendarLocales();
547
}
548
549
@Override
550
public boolean isSupportedLocale(Locale locale) {
551
return isSupportedCalendarLocale(locale);
552
}
553
554
@Override
555
public String getDisplayName(String calendarType, int field,
556
int value, int style, Locale locale) {
557
String[] names = getCalendarDisplayStrings(locale.toLanguageTag(),
558
field, style);
559
if (names != null && value >= 0 && value < names.length) {
560
return names[value];
561
} else {
562
return null;
563
}
564
}
565
566
@Override
567
public Map<String, Integer> getDisplayNames(String calendarType,
568
int field, int style, Locale locale) {
569
Map<String, Integer> map = null;
570
String[] names = getCalendarDisplayStrings(locale.toLanguageTag(),
571
field, style);
572
if (names != null) {
573
map = new HashMap<>((int)Math.ceil(names.length / 0.75));
574
for (int value = 0; value < names.length; value++) {
575
if (names[value] != null) {
576
map.put(names[value], value);
577
}
578
}
579
map = map.isEmpty() ? null : map;
580
}
581
return map;
582
}
583
};
584
}
585
586
public static CalendarProvider getCalendarProvider() {
587
return new CalendarProvider() {
588
@Override
589
public Locale[] getAvailableLocales() {
590
return getSupportedCalendarLocales();
591
}
592
593
@Override
594
public boolean isSupportedLocale(Locale locale) {
595
return isSupportedCalendarLocale(locale);
596
}
597
598
@Override
599
public Calendar getInstance(TimeZone zone, Locale locale) {
600
return new Calendar.Builder()
601
.setLocale(locale)
602
.setCalendarType(getCalendarID(locale.toLanguageTag()))
603
.setTimeZone(zone)
604
.setInstant(System.currentTimeMillis())
605
.build();
606
}
607
};
608
}
609
610
public static CurrencyNameProvider getCurrencyNameProvider() {
611
return new CurrencyNameProvider() {
612
@Override
613
public Locale[] getAvailableLocales() {
614
return supportedLocale;
615
}
616
617
@Override
618
public boolean isSupportedLocale(Locale locale) {
619
// Ignore the extensions for now
620
return supportedLocaleSet.contains(locale.stripExtensions());
621
}
622
623
@Override
624
public String getDisplayName(String code, Locale locale) {
625
return getDisplayString(locale.toLanguageTag(), DN_CURRENCY_CODE, code);
626
}
627
628
@Override
629
public String getSymbol(String code, Locale locale) {
630
return getDisplayString(locale.toLanguageTag(), DN_CURRENCY_SYMBOL, code);
631
}
632
};
633
}
634
635
public static LocaleNameProvider getLocaleNameProvider() {
636
return new LocaleNameProvider() {
637
@Override
638
public Locale[] getAvailableLocales() {
639
return supportedLocale;
640
}
641
642
@Override
643
public boolean isSupportedLocale(Locale locale) {
644
// Ignore the extensions for now
645
return supportedLocaleSet.contains(locale.stripExtensions());
646
}
647
648
@Override
649
public String getDisplayLanguage(String languageCode, Locale locale) {
650
return getDisplayString(locale.toLanguageTag(), DN_LOCALE_LANGUAGE, languageCode);
651
}
652
653
@Override
654
public String getDisplayCountry(String countryCode, Locale locale) {
655
return getDisplayString(locale.toLanguageTag(), DN_LOCALE_REGION, countryCode);
656
}
657
658
@Override
659
public String getDisplayScript(String scriptCode, Locale locale) {
660
return getDisplayString(locale.toLanguageTag(), DN_LOCALE_SCRIPT, scriptCode);
661
}
662
663
@Override
664
public String getDisplayVariant(String variantCode, Locale locale) {
665
return getDisplayString(locale.toLanguageTag(), DN_LOCALE_VARIANT, variantCode);
666
}
667
};
668
}
669
670
public static TimeZoneNameProvider getTimeZoneNameProvider() {
671
return new TimeZoneNameProvider() {
672
@Override
673
public Locale[] getAvailableLocales() {
674
return supportedLocale;
675
}
676
677
@Override
678
public boolean isSupportedLocale(Locale locale) {
679
// Ignore the extensions for now
680
return supportedLocaleSet.contains(locale.stripExtensions());
681
}
682
683
@Override
684
public String getDisplayName(String ID, boolean daylight, int style, Locale locale) {
685
return getTimeZoneDisplayString(locale.toLanguageTag(), style * 2 + (daylight ? 1 : 0), ID);
686
}
687
};
688
}
689
690
private static Locale[] getSupportedCalendarLocales() {
691
if (supportedLocale.length != 0 &&
692
supportedLocaleSet.contains(Locale.JAPAN) &&
693
isJapaneseCalendar()) {
694
Locale[] sup = new Locale[supportedLocale.length+1];
695
sup[0] = JRELocaleConstants.JA_JP_JP;
696
System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length);
697
return sup;
698
}
699
return supportedLocale;
700
}
701
702
private static boolean isSupportedCalendarLocale(Locale locale) {
703
Locale base = locale;
704
705
if (base.hasExtensions() || base.getVariant() != "") {
706
base = new Locale.Builder()
707
.setLocale(locale)
708
.clearExtensions()
709
.build();
710
}
711
712
if (!supportedLocaleSet.contains(base)) {
713
return false;
714
}
715
716
String requestedCalType = locale.getUnicodeLocaleType("ca");
717
String nativeCalType =
718
getCalendarID(base.toLanguageTag()).replaceFirst("gregorian", "gregory");
719
720
if (requestedCalType == null) {
721
return Calendar.getAvailableCalendarTypes().contains(nativeCalType);
722
} else {
723
return requestedCalType.equals(nativeCalType);
724
}
725
}
726
727
private static boolean isJapaneseCalendar() {
728
return getCalendarID("ja-JP").equals("japanese");
729
}
730
731
private static Locale getCalendarLocale(Locale locale) {
732
String nativeCalType = getCalendarID(locale.toLanguageTag())
733
.replaceFirst("gregorian", "gregory");
734
if (Calendar.getAvailableCalendarTypes().contains(nativeCalType)) {
735
return new Locale.Builder()
736
.setLocale(locale)
737
.setUnicodeLocaleKeyword("ca", nativeCalType)
738
.build();
739
} else {
740
return locale;
741
}
742
}
743
744
// The following methods are copied from CLDRConverter build tool.
745
private static String translateDateFormatLetters(String calendarType, String cldrFormat) {
746
String pattern = cldrFormat;
747
int length = pattern.length();
748
boolean inQuote = false;
749
StringBuilder jrePattern = new StringBuilder(length);
750
int count = 0;
751
char lastLetter = 0;
752
753
for (int i = 0; i < length; i++) {
754
char c = pattern.charAt(i);
755
756
if (c == '\'') {
757
// '' is treated as a single quote regardless of being
758
// in a quoted section.
759
if ((i + 1) < length) {
760
char nextc = pattern.charAt(i + 1);
761
if (nextc == '\'') {
762
i++;
763
if (count != 0) {
764
convert(calendarType, lastLetter, count, jrePattern);
765
lastLetter = 0;
766
count = 0;
767
}
768
jrePattern.append("''");
769
continue;
770
}
771
}
772
if (!inQuote) {
773
if (count != 0) {
774
convert(calendarType, lastLetter, count, jrePattern);
775
lastLetter = 0;
776
count = 0;
777
}
778
inQuote = true;
779
} else {
780
inQuote = false;
781
}
782
jrePattern.append(c);
783
continue;
784
}
785
if (inQuote) {
786
jrePattern.append(c);
787
continue;
788
}
789
if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
790
if (count != 0) {
791
convert(calendarType, lastLetter, count, jrePattern);
792
lastLetter = 0;
793
count = 0;
794
}
795
jrePattern.append(c);
796
continue;
797
}
798
799
if (lastLetter == 0 || lastLetter == c) {
800
lastLetter = c;
801
count++;
802
continue;
803
}
804
convert(calendarType, lastLetter, count, jrePattern);
805
lastLetter = c;
806
count = 1;
807
}
808
809
if (count != 0) {
810
convert(calendarType, lastLetter, count, jrePattern);
811
}
812
if (cldrFormat.contentEquals(jrePattern)) {
813
return cldrFormat;
814
}
815
return jrePattern.toString();
816
}
817
818
private static void convert(String calendarType, char cldrLetter, int count, StringBuilder sb) {
819
switch (cldrLetter) {
820
case 'G':
821
if (!calendarType.equals("gregorian")) {
822
// Adjust the number of 'G's for JRE SimpleDateFormat
823
if (count == 5) {
824
// CLDR narrow -> JRE short
825
count = 1;
826
} else if (count == 1) {
827
// CLDR abbr -> JRE long
828
count = 4;
829
}
830
}
831
appendN(cldrLetter, count, sb);
832
break;
833
834
// TODO: support 'c' and 'e' in JRE SimpleDateFormat
835
// Use 'u' and 'E' for now.
836
case 'c':
837
case 'e':
838
switch (count) {
839
case 1:
840
sb.append('u');
841
break;
842
case 3:
843
case 4:
844
appendN('E', count, sb);
845
break;
846
case 5:
847
appendN('E', 3, sb);
848
break;
849
}
850
break;
851
852
case 'v':
853
case 'V':
854
appendN('z', count, sb);
855
break;
856
857
case 'Z':
858
if (count == 4 || count == 5) {
859
sb.append("XXX");
860
}
861
break;
862
863
case 'u':
864
case 'U':
865
case 'q':
866
case 'Q':
867
case 'l':
868
case 'g':
869
case 'j':
870
case 'A':
871
// Unsupported letter. Just append it within quotes
872
sb.append('\'');
873
sb.append(cldrLetter);
874
sb.append('\'');
875
break;
876
877
default:
878
appendN(cldrLetter, count, sb);
879
break;
880
}
881
}
882
883
private static void appendN(char c, int n, StringBuilder sb) {
884
for (int i = 0; i < n; i++) {
885
sb.append(c);
886
}
887
}
888
889
// initialize
890
private static native String getDefaultLocale(int cat);
891
892
// For DateFormatProvider
893
private static native String getDateTimePatternNative(int dateStyle, int timeStyle, String langtag);
894
private static native String getCalendarID(String langTag);
895
896
// For NumberFormatProvider
897
private static native String getNumberPatternNative(int style, String langtag);
898
899
// For DateFormatSymbolsProvider
900
private static native String[] getAmPmStrings(String langTag, String[] ampm);
901
private static native String[] getEras(String langTag, String[] eras);
902
private static native String[] getMonths(String langTag, String[] months);
903
private static native String[] getShortMonths(String langTag, String[] smonths);
904
private static native String[] getWeekdays(String langTag, String[] wdays);
905
private static native String[] getShortWeekdays(String langTag, String[] swdays);
906
907
// For DecimalFormatSymbolsProvider
908
private static native String getCurrencySymbol(String langTag, String currencySymbol);
909
private static native char getDecimalSeparator(String langTag, char decimalSeparator);
910
private static native char getGroupingSeparator(String langTag, char groupingSeparator);
911
private static native String getInfinity(String langTag, String infinity);
912
private static native String getInternationalCurrencySymbol(String langTag, String internationalCurrencySymbol);
913
private static native char getMinusSign(String langTag, char minusSign);
914
private static native char getMonetaryDecimalSeparator(String langTag, char monetaryDecimalSeparator);
915
private static native String getNaN(String langTag, String nan);
916
private static native char getPercent(String langTag, char percent);
917
private static native char getPerMill(String langTag, char perMill);
918
private static native char getZeroDigit(String langTag, char zeroDigit);
919
private static native String getExponentSeparator(String langTag, String exponent);
920
921
// For CalendarDataProvider
922
private static native int getCalendarInt(String langTag, int type);
923
924
// For CalendarNameProvider
925
private static native String[] getCalendarDisplayStrings(String langTag, int field, int style);
926
927
// For Locale/CurrencyNameProvider
928
private static native String getDisplayString(String langTag, int key, String value);
929
930
// For TimeZoneNameProvider
931
private static native String getTimeZoneDisplayString(String langTag, int style, String value);
932
}
933
934