1 /*
2 * Copyright (c) 2012, 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 package sun.util.locale.provider;
26
27 import java.lang.ref.SoftReference;
28 import java.text.DateFormat;
29 import java.text.DateFormatSymbols;
30 import java.text.DecimalFormat;
31 import java.text.DecimalFormatSymbols;
32 import java.text.NumberFormat;
33 import java.text.SimpleDateFormat;
34 import java.text.spi.DateFormatProvider;
35 import java.text.spi.DateFormatSymbolsProvider;
36 import java.text.spi.DecimalFormatSymbolsProvider;
37 import java.text.spi.NumberFormatProvider;
38 import java.util.Calendar;
39 import java.util.Collections;
40 import java.util.HashSet;
41 import java.util.Locale;
42 import java.util.Map;
43 import java.util.ResourceBundle.Control;
44 import java.util.Set;
45 import java.util.TimeZone;
46 import java.util.concurrent.ConcurrentHashMap;
47 import java.util.concurrent.ConcurrentMap;
48 import java.util.concurrent.atomic.AtomicReferenceArray;
49 import java.util.spi.CalendarDataProvider;
50 import java.util.spi.CalendarNameProvider;
51 import sun.util.spi.CalendarProvider;
52
53 /**
54 * LocaleProviderdapter implementation for the Windows locale data.
55 *
56 * @author Naoto Sato
57 */
58 public class HostLocaleProviderAdapterImpl {
59
60 // locale categories
61 private static final int CAT_DISPLAY = 0;
62 private static final int CAT_FORMAT = 1;
63
64 // NumberFormat styles
65 private static final int NF_NUMBER = 0;
66 private static final int NF_CURRENCY = 1;
67 private static final int NF_PERCENT = 2;
68 private static final int NF_INTEGER = 3;
69 private static final int NF_MAX = NF_INTEGER;
70
71 // CalendarData value types
72 private static final int CD_FIRSTDAYOFWEEK = 0;
73 private static final int CD_MINIMALDAYSINFIRSTWEEK = 1;
74
75 // Native Calendar ID to LDML calendar type map
76 private static final String[] calIDToLDML = {
77 "",
78 "gregory",
79 "gregory_en-US",
80 "japanese",
81 "roc",
82 "", // No appropriate type for CAL_KOREA
83 "islamic",
84 "buddhist",
85 "hebrew",
86 "gregory_fr",
87 "gregory_ar",
88 "gregory_en",
89 "gregory_fr",
90 };
91
92 // Caches
93 private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> dateFormatCache = new ConcurrentHashMap<>();
94 private static ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> dateFormatSymbolsCache = new ConcurrentHashMap<>();
95 private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> numberFormatCache = new ConcurrentHashMap<>();
96 private static ConcurrentMap<Locale, SoftReference<DecimalFormatSymbols>> decimalFormatSymbolsCache = new ConcurrentHashMap<>();
97
98 private static final Set<Locale> supportedLocaleSet;
99 static {
100 Set<Locale> tmpSet = new HashSet<>();
101 if (initialize()) {
102 // Assuming the default locales do not include any extensions, so
103 // no stripping is needed here.
104 Locale l = Locale.forLanguageTag(getDefaultLocale(CAT_FORMAT).replace('_', '-'));
105 tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
106 l = Locale.forLanguageTag(getDefaultLocale(CAT_DISPLAY).replace('_', '-'));
107 tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
108 }
109 supportedLocaleSet = Collections.unmodifiableSet(tmpSet);
110 }
111 private final static Locale[] supportedLocale = supportedLocaleSet.toArray(new Locale[0]);
112
113 public static DateFormatProvider getDateFormatProvider() {
114 return new DateFormatProvider() {
115 @Override
116 public Locale[] getAvailableLocales() {
117 return getSupportedCalendarLocales();
118 }
119
120 @Override
121 public boolean isSupportedLocale(Locale locale) {
122 return isSupportedCalendarLocale(locale);
123 }
124
125 @Override
126 public DateFormat getDateInstance(int style, Locale locale) {
127 AtomicReferenceArray<String> patterns = getDateTimePatterns(locale);
128 return new SimpleDateFormat(patterns.get(style/2),
375 public Locale[] getAvailableLocales() {
376 return getSupportedCalendarLocales();
377 }
378
379 @Override
380 public boolean isSupportedLocale(Locale locale) {
381 return isSupportedCalendarLocale(locale);
382 }
383
384 @Override
385 public Calendar getInstance(TimeZone zone, Locale locale) {
386 return new Calendar.Builder()
387 .setLocale(getCalendarLocale(locale))
388 .setTimeZone(zone)
389 .setInstant(System.currentTimeMillis())
390 .build();
391 }
392 };
393 }
394
395 private static String convertDateTimePattern(String winPattern) {
396 String ret = winPattern.replaceAll("dddd", "EEEE");
397 ret = ret.replaceAll("ddd", "EEE");
398 ret = ret.replaceAll("tt", "aa");
399 ret = ret.replaceAll("g", "GG");
400 return ret;
401 }
402
403 private static Locale[] getSupportedCalendarLocales() {
404 if (supportedLocale.length != 0 &&
405 supportedLocaleSet.contains(Locale.JAPAN) &&
406 isJapaneseCalendar()) {
407 Locale[] sup = new Locale[supportedLocale.length+1];
408 sup[0] = JRELocaleConstants.JA_JP_JP;
409 System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length);
410 return sup;
411 }
412 return supportedLocale;
413 }
414
415 private static boolean isSupportedCalendarLocale(Locale locale) {
416 Locale base = locale.stripExtensions();
417 if (!supportedLocaleSet.contains(base)) {
418 return false;
419 }
420
421 int calid = getCalendarID(locale.toLanguageTag());
422 if (calid <= 0 || calid >= calIDToLDML.length) {
423 return false;
424 }
425
426 String requestedCalType = locale.getUnicodeLocaleType("ca");
427 String nativeCalType = calIDToLDML[calid]
428 .replaceFirst("_.*", ""); // remove locale part.
429
430 if (requestedCalType == null) {
431 return Calendar.getAvailableCalendarTypes().contains(nativeCalType);
432 } else {
433 return requestedCalType.equals(nativeCalType);
434 }
435 }
436
437 private static Locale[] getSupportedNativeDigitLocales() {
438 if (supportedLocale.length != 0 &&
439 supportedLocaleSet.contains(JRELocaleConstants.TH_TH) &&
440 isNativeDigit("th-TH")) {
441 Locale[] sup = new Locale[supportedLocale.length+1];
529
530 // For NumberFormatProvider
531 private static native String getNumberPattern(int numberStyle, String langTag);
532 private static native boolean isNativeDigit(String langTag);
533
534 // For DecimalFormatSymbolsProvider
535 private static native String getCurrencySymbol(String langTag, String currencySymbol);
536 private static native char getDecimalSeparator(String langTag, char decimalSeparator);
537 private static native char getGroupingSeparator(String langTag, char groupingSeparator);
538 private static native String getInfinity(String langTag, String infinity);
539 private static native String getInternationalCurrencySymbol(String langTag, String internationalCurrencySymbol);
540 private static native char getMinusSign(String langTag, char minusSign);
541 private static native char getMonetaryDecimalSeparator(String langTag, char monetaryDecimalSeparator);
542 private static native String getNaN(String langTag, String nan);
543 private static native char getPercent(String langTag, char percent);
544 private static native char getPerMill(String langTag, char perMill);
545 private static native char getZeroDigit(String langTag, char zeroDigit);
546
547 // For CalendarDataProvider
548 private static native int getCalendarDataValue(String langTag, int type);
549 }
|
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 package sun.util.locale.provider;
26
27 import java.lang.ref.SoftReference;
28 import java.text.DateFormat;
29 import java.text.DateFormatSymbols;
30 import java.text.DecimalFormat;
31 import java.text.DecimalFormatSymbols;
32 import java.text.NumberFormat;
33 import java.text.SimpleDateFormat;
34 import java.text.spi.DateFormatProvider;
35 import java.text.spi.DateFormatSymbolsProvider;
36 import java.text.spi.DecimalFormatSymbolsProvider;
37 import java.text.spi.NumberFormatProvider;
38 import java.util.Calendar;
39 import java.util.Collections;
40 import java.util.Currency;
41 import java.util.HashSet;
42 import java.util.Locale;
43 import java.util.Map;
44 import java.util.ResourceBundle.Control;
45 import java.util.Set;
46 import java.util.TimeZone;
47 import java.util.concurrent.ConcurrentHashMap;
48 import java.util.concurrent.ConcurrentMap;
49 import java.util.concurrent.atomic.AtomicReferenceArray;
50 import java.util.spi.CalendarDataProvider;
51 import java.util.spi.CalendarNameProvider;
52 import java.util.spi.CurrencyNameProvider;
53 import java.util.spi.LocaleNameProvider;
54 import sun.util.spi.CalendarProvider;
55
56 /**
57 * LocaleProviderdapter implementation for the Windows locale data.
58 *
59 * @author Naoto Sato
60 */
61 public class HostLocaleProviderAdapterImpl {
62
63 // locale categories
64 private static final int CAT_DISPLAY = 0;
65 private static final int CAT_FORMAT = 1;
66
67 // NumberFormat styles
68 private static final int NF_NUMBER = 0;
69 private static final int NF_CURRENCY = 1;
70 private static final int NF_PERCENT = 2;
71 private static final int NF_INTEGER = 3;
72 private static final int NF_MAX = NF_INTEGER;
73
74 // CalendarData value types
75 private static final int CD_FIRSTDAYOFWEEK = 0;
76 private static final int CD_MINIMALDAYSINFIRSTWEEK = 1;
77
78 // Currency/Locale display name types
79 private static final int DN_CURRENCY_NAME = 0;
80 private static final int DN_CURRENCY_SYMBOL = 1;
81 private static final int DN_LOCALE_LANGUAGE = 2;
82 private static final int DN_LOCALE_SCRIPT = 3;
83 private static final int DN_LOCALE_REGION = 4;
84 private static final int DN_LOCALE_VARIANT = 5;
85
86 // Native Calendar ID to LDML calendar type map
87 private static final String[] calIDToLDML = {
88 "",
89 "gregory",
90 "gregory_en-US",
91 "japanese",
92 "roc",
93 "", // No appropriate type for CAL_KOREA
94 "islamic",
95 "buddhist",
96 "hebrew",
97 "gregory_fr",
98 "gregory_ar",
99 "gregory_en",
100 "gregory_fr",
101 };
102
103 // Caches
104 private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> dateFormatCache = new ConcurrentHashMap<>();
105 private static ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> dateFormatSymbolsCache = new ConcurrentHashMap<>();
106 private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> numberFormatCache = new ConcurrentHashMap<>();
107 private static ConcurrentMap<Locale, SoftReference<DecimalFormatSymbols>> decimalFormatSymbolsCache = new ConcurrentHashMap<>();
108
109 private static final Set<Locale> supportedLocaleSet;
110 private static final String nativeDisplayLanguage;
111 static {
112 Set<Locale> tmpSet = new HashSet<>();
113 if (initialize()) {
114 // Assuming the default locales do not include any extensions, so
115 // no stripping is needed here.
116 Control c = Control.getNoFallbackControl(Control.FORMAT_DEFAULT);
117 String displayLocale = getDefaultLocale(CAT_DISPLAY);
118 Locale l = Locale.forLanguageTag(displayLocale.replace('_', '-'));
119 tmpSet.addAll(c.getCandidateLocales("", l));
120 nativeDisplayLanguage = l.getLanguage();
121
122 String formatLocale = getDefaultLocale(CAT_FORMAT);
123 if (!formatLocale.equals(displayLocale)) {
124 l = Locale.forLanguageTag(formatLocale.replace('_', '-'));
125 tmpSet.addAll(c.getCandidateLocales("", l));
126 }
127 } else {
128 nativeDisplayLanguage = "";
129 }
130 supportedLocaleSet = Collections.unmodifiableSet(tmpSet);
131 }
132 private final static Locale[] supportedLocale = supportedLocaleSet.toArray(new Locale[0]);
133
134 public static DateFormatProvider getDateFormatProvider() {
135 return new DateFormatProvider() {
136 @Override
137 public Locale[] getAvailableLocales() {
138 return getSupportedCalendarLocales();
139 }
140
141 @Override
142 public boolean isSupportedLocale(Locale locale) {
143 return isSupportedCalendarLocale(locale);
144 }
145
146 @Override
147 public DateFormat getDateInstance(int style, Locale locale) {
148 AtomicReferenceArray<String> patterns = getDateTimePatterns(locale);
149 return new SimpleDateFormat(patterns.get(style/2),
396 public Locale[] getAvailableLocales() {
397 return getSupportedCalendarLocales();
398 }
399
400 @Override
401 public boolean isSupportedLocale(Locale locale) {
402 return isSupportedCalendarLocale(locale);
403 }
404
405 @Override
406 public Calendar getInstance(TimeZone zone, Locale locale) {
407 return new Calendar.Builder()
408 .setLocale(getCalendarLocale(locale))
409 .setTimeZone(zone)
410 .setInstant(System.currentTimeMillis())
411 .build();
412 }
413 };
414 }
415
416 public static CurrencyNameProvider getCurrencyNameProvider() {
417 return new CurrencyNameProvider() {
418 @Override
419 public Locale[] getAvailableLocales() {
420 return supportedLocale;
421 }
422
423 @Override
424 public boolean isSupportedLocale(Locale locale) {
425 // Ignore the extensions for now
426 return supportedLocaleSet.contains(locale.stripExtensions()) &&
427 locale.getLanguage().equals(nativeDisplayLanguage);
428 }
429
430 @Override
431 public String getSymbol(String currencyCode, Locale locale) {
432 // Retrieves the currency symbol by calling
433 // GetLocaleInfoEx(LOCALE_SCURRENCY).
434 // It only works with the "locale"'s currency in its native
435 // language.
436 try {
437 if (Currency.getInstance(locale).getCurrencyCode()
438 .equals(currencyCode)) {
439 return getDisplayString(locale.toLanguageTag(),
440 DN_CURRENCY_SYMBOL, currencyCode);
441 }
442 } catch (IllegalArgumentException iae) {}
443 return null;
444 }
445
446 @Override
447 public String getDisplayName(String currencyCode, Locale locale) {
448 // Retrieves the display name by calling
449 // GetLocaleInfoEx(LOCALE_SNATIVECURRNAME).
450 // It only works with the "locale"'s currency in its native
451 // language.
452 try {
453 if (Currency.getInstance(locale).getCurrencyCode()
454 .equals(currencyCode)) {
455 return getDisplayString(locale.toLanguageTag(),
456 DN_CURRENCY_NAME, currencyCode);
457 }
458 } catch (IllegalArgumentException iae) {}
459 return null;
460 }
461 };
462 }
463
464 public static LocaleNameProvider getLocaleNameProvider() {
465 return new LocaleNameProvider() {
466 @Override
467 public Locale[] getAvailableLocales() {
468 return supportedLocale;
469 }
470
471 @Override
472 public boolean isSupportedLocale(Locale locale) {
473 return supportedLocaleSet.contains(locale.stripExtensions()) &&
474 locale.getLanguage().equals(nativeDisplayLanguage);
475 }
476
477 @Override
478 public String getDisplayLanguage(String languageCode, Locale locale) {
479 // Retrieves the display language name by calling
480 // GetLocaleInfoEx(LOCALE_SLOCALIZEDLANGUAGENAME).
481 return getDisplayString(locale.toLanguageTag(),
482 DN_LOCALE_LANGUAGE, languageCode);
483 }
484
485 @Override
486 public String getDisplayCountry(String countryCode, Locale locale) {
487 // Retrieves the display country name by calling
488 // GetLocaleInfoEx(LOCALE_SLOCALIZEDCOUNTRYNAME).
489 return getDisplayString(locale.toLanguageTag(),
490 DN_LOCALE_REGION, nativeDisplayLanguage+"-"+countryCode);
491 }
492
493 @Override
494 public String getDisplayScript(String scriptCode, Locale locale) {
495 return null;
496 }
497
498 @Override
499 public String getDisplayVariant(String variantCode, Locale locale) {
500 return null;
501 }
502 };
503 }
504
505
506 private static String convertDateTimePattern(String winPattern) {
507 String ret = winPattern.replaceAll("dddd", "EEEE");
508 ret = ret.replaceAll("ddd", "EEE");
509 ret = ret.replaceAll("tt", "aa");
510 ret = ret.replaceAll("g", "GG");
511 return ret;
512 }
513
514 private static Locale[] getSupportedCalendarLocales() {
515 if (supportedLocale.length != 0 &&
516 supportedLocaleSet.contains(Locale.JAPAN) &&
517 isJapaneseCalendar()) {
518 Locale[] sup = new Locale[supportedLocale.length+1];
519 sup[0] = JRELocaleConstants.JA_JP_JP;
520 System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length);
521 return sup;
522 }
523 return supportedLocale;
524 }
525
526 private static boolean isSupportedCalendarLocale(Locale locale) {
527 Locale base = locale;
528
529 if (base.hasExtensions() || base.getVariant() != "") {
530 // strip off extensions and variant.
531 base = new Locale.Builder()
532 .setLocale(locale)
533 .clearExtensions()
534 .build();
535 }
536
537 if (!supportedLocaleSet.contains(base)) {
538 return false;
539 }
540
541 int calid = getCalendarID(base.toLanguageTag());
542 if (calid <= 0 || calid >= calIDToLDML.length) {
543 return false;
544 }
545
546 String requestedCalType = locale.getUnicodeLocaleType("ca");
547 String nativeCalType = calIDToLDML[calid]
548 .replaceFirst("_.*", ""); // remove locale part.
549
550 if (requestedCalType == null) {
551 return Calendar.getAvailableCalendarTypes().contains(nativeCalType);
552 } else {
553 return requestedCalType.equals(nativeCalType);
554 }
555 }
556
557 private static Locale[] getSupportedNativeDigitLocales() {
558 if (supportedLocale.length != 0 &&
559 supportedLocaleSet.contains(JRELocaleConstants.TH_TH) &&
560 isNativeDigit("th-TH")) {
561 Locale[] sup = new Locale[supportedLocale.length+1];
649
650 // For NumberFormatProvider
651 private static native String getNumberPattern(int numberStyle, String langTag);
652 private static native boolean isNativeDigit(String langTag);
653
654 // For DecimalFormatSymbolsProvider
655 private static native String getCurrencySymbol(String langTag, String currencySymbol);
656 private static native char getDecimalSeparator(String langTag, char decimalSeparator);
657 private static native char getGroupingSeparator(String langTag, char groupingSeparator);
658 private static native String getInfinity(String langTag, String infinity);
659 private static native String getInternationalCurrencySymbol(String langTag, String internationalCurrencySymbol);
660 private static native char getMinusSign(String langTag, char minusSign);
661 private static native char getMonetaryDecimalSeparator(String langTag, char monetaryDecimalSeparator);
662 private static native String getNaN(String langTag, String nan);
663 private static native char getPercent(String langTag, char percent);
664 private static native char getPerMill(String langTag, char perMill);
665 private static native char getZeroDigit(String langTag, char zeroDigit);
666
667 // For CalendarDataProvider
668 private static native int getCalendarDataValue(String langTag, int type);
669
670 // For Locale/CurrencyNameProvider
671 private static native String getDisplayString(String langTag, int key, String value);
672 }
|