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.HashSet;
36 import java.util.Locale;
37 import java.util.Map;
38 import java.util.ResourceBundle.Control;
39 import java.util.Set;
40 import java.util.concurrent.ConcurrentHashMap;
41 import java.util.concurrent.ConcurrentMap;
42 import java.util.concurrent.atomic.AtomicReferenceArray;
43 import java.util.spi.CalendarDataProvider;
44 import java.util.spi.CalendarNameProvider;
45 import java.util.spi.CurrencyNameProvider;
46 import java.util.spi.LocaleNameProvider;
47 import java.util.spi.TimeZoneNameProvider;
48
49 /**
50 * LocaleProviderAdapter implementation for the Mac OS X locale data
51 *
52 * @author Naoto Sato
53 */
54 public class HostLocaleProviderAdapterImpl {
55
56 // per supported locale instances
57 private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> dateFormatPatternsMap =
58 new ConcurrentHashMap<>(2);
59 private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> numberFormatPatternsMap =
60 new ConcurrentHashMap<>(2);
61 private static ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> dateFormatSymbolsMap =
62 new ConcurrentHashMap<>(2);
63 private static ConcurrentMap<Locale, SoftReference<DecimalFormatSymbols>> decimalFormatSymbolsMap =
64 new ConcurrentHashMap<>(2);
65
66 // locale categories
67 private static final int CAT_DISPLAY = 0;
77 // CalendarData value types
78 private static final int CD_FIRSTDAYOFWEEK = 0;
79 private static final int CD_MINIMALDAYSINFIRSTWEEK = 1;
80
81 // Locale/Currency display name types
82 private static final int DN_LOCALE_LANGUAGE = 0;
83 private static final int DN_LOCALE_SCRIPT = 1;
84 private static final int DN_LOCALE_REGION = 2;
85 private static final int DN_LOCALE_VARIANT = 3;
86 private static final int DN_CURRENCY_CODE = 4;
87 private static final int DN_CURRENCY_SYMBOL = 5;
88
89 // TimeZone display name types
90 private static final int DN_TZ_SHORT_STANDARD = 0;
91 private static final int DN_TZ_SHORT_DST = 1;
92 private static final int DN_TZ_LONG_STANDARD = 2;
93 private static final int DN_TZ_LONG_DST = 3;
94
95 private static final Set<Locale> supportedLocaleSet;
96 static {
97 Set<Locale> tmpSet = new HashSet<Locale>();
98 // Assuming the default locales do not include any extensions, so
99 // no stripping is needed here.
100 Locale l = Locale.forLanguageTag(getDefaultLocale(CAT_FORMAT).replaceAll("_","-"));
101 tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
102 l = Locale.forLanguageTag(getDefaultLocale(CAT_DISPLAY).replaceAll("_","-"));
103 tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
104 supportedLocaleSet = Collections.unmodifiableSet(tmpSet);
105 }
106 private final static Locale[] supportedLocale = supportedLocaleSet.toArray(new Locale[0]);
107
108 public static DateFormatProvider getDateFormatProvider() {
109 return new DateFormatProvider() {
110
111 @Override
112 public Locale[] getAvailableLocales() {
113 return getSupportedCalendarLocales();
114 }
115
116 @Override
117 public boolean isSupportedLocale(Locale locale) {
118 return isSupportedCalendarLocale(locale);
119 }
120
121 @Override
122 public DateFormat getDateInstance(int style, Locale locale) {
123 return new SimpleDateFormat(getDateTimePattern(style, -1, locale),
124 getCalendarLocale(locale));
125 }
126
127 @Override
153 String langTag = locale.toLanguageTag();
154 pattern = translateDateFormatLetters(getCalendarID(langTag),
155 getDateTimePatternNative(dateStyle, timeStyle, langTag));
156 if (!dateFormatPatterns.compareAndSet(index, null, pattern)) {
157 pattern = dateFormatPatterns.get(index);
158 }
159 }
160
161 return pattern;
162 }
163 };
164 }
165
166 public static DateFormatSymbolsProvider getDateFormatSymbolsProvider() {
167 return new DateFormatSymbolsProvider() {
168 @Override
169 public Locale[] getAvailableLocales() {
170 if (isSupportedLocale(Locale.getDefault(Locale.Category.FORMAT))) {
171 return supportedLocale;
172 }
173
174 return new Locale[0];
175 }
176
177 @Override
178 public boolean isSupportedLocale(Locale locale) {
179 // Only supports the locale with Gregorian calendar
180 Locale base = locale.stripExtensions();
181 if (supportedLocaleSet.contains(base)) {
182 return getCalendarID(locale.toLanguageTag()).equals("gregorian");
183 }
184 return false;
185 }
186
187 @Override
188 public DateFormatSymbols getInstance(Locale locale) {
189 DateFormatSymbols dateFormatSymbols;
190 SoftReference<DateFormatSymbols> ref = dateFormatSymbolsMap.get(locale);
191
192 if (ref == null || (dateFormatSymbols = ref.get()) == null) {
193 dateFormatSymbols = new DateFormatSymbols(locale);
345
346 @Override
347 public boolean isSupportedLocale(Locale locale) {
348 return isSupportedCalendarLocale(locale);
349 }
350
351 @Override
352 public String getDisplayName(String calType, int field, int value,
353 int style, Locale locale) {
354 return null;
355 }
356
357 @Override
358 public Map<String, Integer> getDisplayNames(String calType,
359 int field, int style, Locale locale) {
360 return null;
361 }
362 };
363 }
364
365 public static CurrencyNameProvider getCurrencyNameProvider() {
366 return new CurrencyNameProvider() {
367 @Override
368 public Locale[] getAvailableLocales() {
369 return supportedLocale;
370 }
371
372 @Override
373 public boolean isSupportedLocale(Locale locale) {
374 // Ignore the extensions for now
375 return supportedLocaleSet.contains(locale.stripExtensions());
376 }
377
378 @Override
379 public String getDisplayName(String code, Locale locale) {
380 return getDisplayString(locale.toLanguageTag(), DN_CURRENCY_CODE, code);
381 }
382
383 @Override
384 public String getSymbol(String code, Locale locale) {
438 @Override
439 public String getDisplayName(String ID, boolean daylight, int style, Locale locale) {
440 return getTimeZoneDisplayString(locale.toLanguageTag(), style * 2 + (daylight ? 1 : 0), ID);
441 }
442 };
443 }
444
445 private static Locale[] getSupportedCalendarLocales() {
446 if (supportedLocale.length != 0 &&
447 supportedLocaleSet.contains(Locale.JAPAN) &&
448 isJapaneseCalendar()) {
449 Locale[] sup = new Locale[supportedLocale.length+1];
450 sup[0] = JRELocaleConstants.JA_JP_JP;
451 System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length);
452 return sup;
453 }
454 return supportedLocale;
455 }
456
457 private static boolean isSupportedCalendarLocale(Locale locale) {
458 // special case for ja_JP_JP
459 if (JRELocaleConstants.JA_JP_JP.equals(locale)) {
460 return isJapaneseCalendar();
461 }
462
463 Locale base = locale.stripExtensions();
464 if (!supportedLocaleSet.contains(base)) {
465 return false;
466 }
467
468 String caltype = locale.getUnicodeLocaleType("ca");
469 if (caltype == null) {
470 return true;
471 }
472
473 return caltype.replaceFirst("gregory", "gregorian").equals(
474 getCalendarID(locale.toLanguageTag()));
475 }
476
477 private static boolean isJapaneseCalendar() {
478 return getCalendarID("ja-JP").equals("japanese");
479 }
480
481 private static Locale getCalendarLocale(Locale locale) {
482 Locale.Builder lb = new Locale.Builder().setLocale(locale);
483 String calid = getCalendarID(locale.toLanguageTag());
484 switch (calid) {
485 case "gregorian":
486 calid = "gregory";
487 // FALL THROUGH!
488 case "japanese":
489 case "buddhist":
490 lb.setUnicodeLocaleKeyword("ca", calid);
491 return lb.build();
492 default:
493 return locale;
494 }
495 }
496
497 // The following methods are copied from CLDRConverter build tool.
498 private static String translateDateFormatLetters(String calendarType, String cldrFormat) {
499 String pattern = cldrFormat;
500 int length = pattern.length();
501 boolean inQuote = false;
502 StringBuilder jrePattern = new StringBuilder(length);
503 int count = 0;
504 char lastLetter = 0;
505
506 for (int i = 0; i < length; i++) {
507 char c = pattern.charAt(i);
508
509 if (c == '\'') {
510 // '' is treated as a single quote regardless of being
511 // in a quoted section.
512 if ((i + 1) < length) {
|
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.HashSet;
37 import java.util.Locale;
38 import java.util.Map;
39 import java.util.ResourceBundle.Control;
40 import java.util.Set;
41 import java.util.TimeZone;
42 import java.util.concurrent.ConcurrentHashMap;
43 import java.util.concurrent.ConcurrentMap;
44 import java.util.concurrent.atomic.AtomicReferenceArray;
45 import java.util.spi.CalendarDataProvider;
46 import java.util.spi.CalendarNameProvider;
47 import java.util.spi.CurrencyNameProvider;
48 import java.util.spi.LocaleNameProvider;
49 import java.util.spi.TimeZoneNameProvider;
50 import sun.util.spi.CalendarProvider;
51
52 /**
53 * LocaleProviderAdapter implementation for the Mac OS X locale data
54 *
55 * @author Naoto Sato
56 */
57 public class HostLocaleProviderAdapterImpl {
58
59 // per supported locale instances
60 private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> dateFormatPatternsMap =
61 new ConcurrentHashMap<>(2);
62 private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> numberFormatPatternsMap =
63 new ConcurrentHashMap<>(2);
64 private static ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> dateFormatSymbolsMap =
65 new ConcurrentHashMap<>(2);
66 private static ConcurrentMap<Locale, SoftReference<DecimalFormatSymbols>> decimalFormatSymbolsMap =
67 new ConcurrentHashMap<>(2);
68
69 // locale categories
70 private static final int CAT_DISPLAY = 0;
80 // CalendarData value types
81 private static final int CD_FIRSTDAYOFWEEK = 0;
82 private static final int CD_MINIMALDAYSINFIRSTWEEK = 1;
83
84 // Locale/Currency display name types
85 private static final int DN_LOCALE_LANGUAGE = 0;
86 private static final int DN_LOCALE_SCRIPT = 1;
87 private static final int DN_LOCALE_REGION = 2;
88 private static final int DN_LOCALE_VARIANT = 3;
89 private static final int DN_CURRENCY_CODE = 4;
90 private static final int DN_CURRENCY_SYMBOL = 5;
91
92 // TimeZone display name types
93 private static final int DN_TZ_SHORT_STANDARD = 0;
94 private static final int DN_TZ_SHORT_DST = 1;
95 private static final int DN_TZ_LONG_STANDARD = 2;
96 private static final int DN_TZ_LONG_DST = 3;
97
98 private static final Set<Locale> supportedLocaleSet;
99 static {
100 Set<Locale> tmpSet = new HashSet<>();
101 // Assuming the default locales do not include any extensions, so
102 // no stripping is needed here.
103 Locale l = convertPosixLocaleToJavaLocale(getDefaultLocale(CAT_FORMAT));
104 tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
105 l = convertPosixLocaleToJavaLocale(getDefaultLocale(CAT_DISPLAY));
106 tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
107 supportedLocaleSet = Collections.unmodifiableSet(tmpSet);
108 }
109 private final static Locale[] supportedLocale = supportedLocaleSet.toArray(new Locale[0]);
110
111 @SuppressWarnings("fallthrough")
112 private static Locale convertPosixLocaleToJavaLocale(String posix) {
113 // MacOSX may return ICU notation, here is the quote from CFLocale doc:
114 // "The corresponding value is a CFString containing the POSIX locale
115 // identifier as used by ICU, such as "ja_JP". If you have a variant
116 // locale or a different currency or calendar, it can be as complex as
117 // "en_US_POSIX@calendar=japanese;currency=EUR" or
118 // "az_Cyrl_AZ@calendar=buddhist;currency=JPY".
119 String[] tmp = posix.split("@");
120 String langTag = tmp[0].replaceAll("_","-");
121 if (tmp.length > 1) {
122 String[] ext = tmp[1].split(";");
123 for (String keyval : ext) {
124 // We are only interested in "calendar" value for now.
125 if (keyval.startsWith("calendar=")) {
126 String calid = keyval.substring(keyval.indexOf('=')+1);
127 switch (calid) {
128 case "gregorian":
129 langTag += "-u-ca-gregory";
130 break;
131 case "japanese":
132 // Tweak for ja_JP_JP
133 if (tmp[0].equals("ja_JP")) {
134 return JRELocaleConstants.JA_JP_JP;
135 }
136
137 // fall through
138
139 default:
140 langTag += "-u-ca-" + calid;
141 break;
142 }
143 }
144 }
145 }
146
147 return Locale.forLanguageTag(langTag);
148 }
149
150 public static DateFormatProvider getDateFormatProvider() {
151 return new DateFormatProvider() {
152
153 @Override
154 public Locale[] getAvailableLocales() {
155 return getSupportedCalendarLocales();
156 }
157
158 @Override
159 public boolean isSupportedLocale(Locale locale) {
160 return isSupportedCalendarLocale(locale);
161 }
162
163 @Override
164 public DateFormat getDateInstance(int style, Locale locale) {
165 return new SimpleDateFormat(getDateTimePattern(style, -1, locale),
166 getCalendarLocale(locale));
167 }
168
169 @Override
195 String langTag = locale.toLanguageTag();
196 pattern = translateDateFormatLetters(getCalendarID(langTag),
197 getDateTimePatternNative(dateStyle, timeStyle, langTag));
198 if (!dateFormatPatterns.compareAndSet(index, null, pattern)) {
199 pattern = dateFormatPatterns.get(index);
200 }
201 }
202
203 return pattern;
204 }
205 };
206 }
207
208 public static DateFormatSymbolsProvider getDateFormatSymbolsProvider() {
209 return new DateFormatSymbolsProvider() {
210 @Override
211 public Locale[] getAvailableLocales() {
212 if (isSupportedLocale(Locale.getDefault(Locale.Category.FORMAT))) {
213 return supportedLocale;
214 }
215 return new Locale[0];
216 }
217
218 @Override
219 public boolean isSupportedLocale(Locale locale) {
220 // Only supports the locale with Gregorian calendar
221 Locale base = locale.stripExtensions();
222 if (supportedLocaleSet.contains(base)) {
223 return getCalendarID(locale.toLanguageTag()).equals("gregorian");
224 }
225 return false;
226 }
227
228 @Override
229 public DateFormatSymbols getInstance(Locale locale) {
230 DateFormatSymbols dateFormatSymbols;
231 SoftReference<DateFormatSymbols> ref = dateFormatSymbolsMap.get(locale);
232
233 if (ref == null || (dateFormatSymbols = ref.get()) == null) {
234 dateFormatSymbols = new DateFormatSymbols(locale);
386
387 @Override
388 public boolean isSupportedLocale(Locale locale) {
389 return isSupportedCalendarLocale(locale);
390 }
391
392 @Override
393 public String getDisplayName(String calType, int field, int value,
394 int style, Locale locale) {
395 return null;
396 }
397
398 @Override
399 public Map<String, Integer> getDisplayNames(String calType,
400 int field, int style, Locale locale) {
401 return null;
402 }
403 };
404 }
405
406 public static CalendarProvider getCalendarProvider() {
407 return new CalendarProvider() {
408 @Override
409 public Locale[] getAvailableLocales() {
410 return getSupportedCalendarLocales();
411 }
412
413 @Override
414 public boolean isSupportedLocale(Locale locale) {
415 return isSupportedCalendarLocale(locale);
416 }
417
418 @Override
419 public Calendar getInstance(TimeZone zone, Locale locale) {
420 return new Calendar.Builder()
421 .setLocale(locale)
422 .setCalendarType(getCalendarID(locale.toLanguageTag())
423 .replaceFirst("gregorian", "gregory"))
424 .setTimeZone(zone)
425 .build();
426 }
427 };
428 }
429
430 public static CurrencyNameProvider getCurrencyNameProvider() {
431 return new CurrencyNameProvider() {
432 @Override
433 public Locale[] getAvailableLocales() {
434 return supportedLocale;
435 }
436
437 @Override
438 public boolean isSupportedLocale(Locale locale) {
439 // Ignore the extensions for now
440 return supportedLocaleSet.contains(locale.stripExtensions());
441 }
442
443 @Override
444 public String getDisplayName(String code, Locale locale) {
445 return getDisplayString(locale.toLanguageTag(), DN_CURRENCY_CODE, code);
446 }
447
448 @Override
449 public String getSymbol(String code, Locale locale) {
503 @Override
504 public String getDisplayName(String ID, boolean daylight, int style, Locale locale) {
505 return getTimeZoneDisplayString(locale.toLanguageTag(), style * 2 + (daylight ? 1 : 0), ID);
506 }
507 };
508 }
509
510 private static Locale[] getSupportedCalendarLocales() {
511 if (supportedLocale.length != 0 &&
512 supportedLocaleSet.contains(Locale.JAPAN) &&
513 isJapaneseCalendar()) {
514 Locale[] sup = new Locale[supportedLocale.length+1];
515 sup[0] = JRELocaleConstants.JA_JP_JP;
516 System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length);
517 return sup;
518 }
519 return supportedLocale;
520 }
521
522 private static boolean isSupportedCalendarLocale(Locale locale) {
523 Locale base = locale.stripExtensions();
524 if (!supportedLocaleSet.contains(base)) {
525 return false;
526 }
527
528 String requestedCalType = locale.getUnicodeLocaleType("ca");
529 String nativeCalType =
530 getCalendarID(locale.toLanguageTag()).replaceFirst("gregorian", "gregory");
531
532 if (requestedCalType == null) {
533 return Calendar.getAvailableCalendarTypes().contains(nativeCalType);
534 } else {
535 return requestedCalType.equals(nativeCalType);
536 }
537 }
538
539 private static boolean isJapaneseCalendar() {
540 return getCalendarID("ja-JP").equals("japanese");
541 }
542
543 private static Locale getCalendarLocale(Locale locale) {
544 String nativeCalType = getCalendarID(locale.toLanguageTag())
545 .replaceFirst("gregorian", "gregory");
546 if (Calendar.getAvailableCalendarTypes().contains(nativeCalType)) {
547 return new Locale.Builder()
548 .setLocale(locale)
549 .setUnicodeLocaleKeyword("ca", nativeCalType)
550 .build();
551 } else {
552 return locale;
553 }
554 }
555
556 // The following methods are copied from CLDRConverter build tool.
557 private static String translateDateFormatLetters(String calendarType, String cldrFormat) {
558 String pattern = cldrFormat;
559 int length = pattern.length();
560 boolean inQuote = false;
561 StringBuilder jrePattern = new StringBuilder(length);
562 int count = 0;
563 char lastLetter = 0;
564
565 for (int i = 0; i < length; i++) {
566 char c = pattern.charAt(i);
567
568 if (c == '\'') {
569 // '' is treated as a single quote regardless of being
570 // in a quoted section.
571 if ((i + 1) < length) {
|