HostLocaleProviderAdapterImpl.java revision 12745:f068a4ffddd2
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 */
25package sun.util.locale.provider;
26
27import java.lang.ref.SoftReference;
28import java.text.DateFormat;
29import java.text.DateFormatSymbols;
30import java.text.DecimalFormat;
31import java.text.DecimalFormatSymbols;
32import java.text.NumberFormat;
33import java.text.SimpleDateFormat;
34import java.text.spi.DateFormatProvider;
35import java.text.spi.DateFormatSymbolsProvider;
36import java.text.spi.DecimalFormatSymbolsProvider;
37import java.text.spi.NumberFormatProvider;
38import java.util.Calendar;
39import java.util.Collections;
40import java.util.Currency;
41import java.util.HashSet;
42import java.util.Locale;
43import java.util.Map;
44import java.util.ResourceBundle.Control;
45import java.util.Set;
46import java.util.TimeZone;
47import java.util.concurrent.ConcurrentHashMap;
48import java.util.concurrent.ConcurrentMap;
49import java.util.concurrent.atomic.AtomicReferenceArray;
50import java.util.spi.CalendarDataProvider;
51import java.util.spi.CurrencyNameProvider;
52import java.util.spi.LocaleNameProvider;
53import sun.util.spi.CalendarProvider;
54
55/**
56 * LocaleProviderdapter implementation for the Windows locale data.
57 *
58 * @author Naoto Sato
59 */
60public class HostLocaleProviderAdapterImpl {
61
62    // locale categories
63    private static final int CAT_DISPLAY = 0;
64    private static final int CAT_FORMAT  = 1;
65
66    // NumberFormat styles
67    private static final int NF_NUMBER   = 0;
68    private static final int NF_CURRENCY = 1;
69    private static final int NF_PERCENT  = 2;
70    private static final int NF_INTEGER  = 3;
71    private static final int NF_MAX = NF_INTEGER;
72
73    // CalendarData value types
74    private static final int CD_FIRSTDAYOFWEEK = 0;
75    private static final int CD_MINIMALDAYSINFIRSTWEEK = 1;
76
77    // Currency/Locale display name types
78    private static final int DN_CURRENCY_NAME   = 0;
79    private static final int DN_CURRENCY_SYMBOL = 1;
80    private static final int DN_LOCALE_LANGUAGE = 2;
81    private static final int DN_LOCALE_SCRIPT   = 3;
82    private static final int DN_LOCALE_REGION   = 4;
83    private static final int DN_LOCALE_VARIANT  = 5;
84
85    // Native Calendar ID to LDML calendar type map
86    private static final String[] calIDToLDML = {
87        "",
88        "gregory",
89        "gregory_en-US",
90        "japanese",
91        "roc",
92        "",          // No appropriate type for CAL_KOREA
93        "islamic",
94        "buddhist",
95        "hebrew",
96        "gregory_fr",
97        "gregory_ar",
98        "gregory_en",
99        "gregory_fr",
100    };
101
102    // Caches
103    private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> dateFormatCache = new ConcurrentHashMap<>();
104    private static ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> dateFormatSymbolsCache = new ConcurrentHashMap<>();
105    private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> numberFormatCache = new ConcurrentHashMap<>();
106    private static ConcurrentMap<Locale, SoftReference<DecimalFormatSymbols>> decimalFormatSymbolsCache = new ConcurrentHashMap<>();
107
108    private static final Set<Locale> supportedLocaleSet;
109    private static final String nativeDisplayLanguage;
110    static {
111        Set<Locale> tmpSet = new HashSet<>();
112        if (initialize()) {
113            // Assuming the default locales do not include any extensions, so
114            // no stripping is needed here.
115            Control c = Control.getNoFallbackControl(Control.FORMAT_DEFAULT);
116            String displayLocale = getDefaultLocale(CAT_DISPLAY);
117            Locale l = Locale.forLanguageTag(displayLocale.replace('_', '-'));
118            tmpSet.addAll(c.getCandidateLocales("", l));
119            nativeDisplayLanguage = l.getLanguage();
120
121            String formatLocale = getDefaultLocale(CAT_FORMAT);
122            if (!formatLocale.equals(displayLocale)) {
123                l = Locale.forLanguageTag(formatLocale.replace('_', '-'));
124                tmpSet.addAll(c.getCandidateLocales("", l));
125            }
126        } else {
127            nativeDisplayLanguage = "";
128        }
129        supportedLocaleSet = Collections.unmodifiableSet(tmpSet);
130    }
131    private static final Locale[] supportedLocale = supportedLocaleSet.toArray(new Locale[0]);
132
133    public static DateFormatProvider getDateFormatProvider() {
134        return new DateFormatProvider() {
135            @Override
136            public Locale[] getAvailableLocales() {
137                return getSupportedCalendarLocales();
138            }
139
140            @Override
141            public boolean isSupportedLocale(Locale locale) {
142                return isSupportedCalendarLocale(locale);
143            }
144
145            @Override
146            public DateFormat getDateInstance(int style, Locale locale) {
147                AtomicReferenceArray<String> patterns = getDateTimePatterns(locale);
148                return new SimpleDateFormat(patterns.get(style/2),
149                                            getCalendarLocale(locale));
150            }
151
152            @Override
153            public DateFormat getTimeInstance(int style, Locale locale) {
154                AtomicReferenceArray<String> patterns = getDateTimePatterns(locale);
155                return new SimpleDateFormat(patterns.get(style/2+2),
156                                            getCalendarLocale(locale));
157            }
158
159            @Override
160            public DateFormat getDateTimeInstance(int dateStyle,
161                    int timeStyle, Locale locale) {
162                AtomicReferenceArray<String> patterns = getDateTimePatterns(locale);
163                String pattern = new StringBuilder(patterns.get(dateStyle/2))
164                                       .append(" ")
165                                       .append(patterns.get(timeStyle/2+2))
166                                       .toString();
167                return new SimpleDateFormat(pattern, getCalendarLocale(locale));
168            }
169
170            private AtomicReferenceArray<String> getDateTimePatterns(Locale locale) {
171                AtomicReferenceArray<String> patterns;
172                SoftReference<AtomicReferenceArray<String>> ref = dateFormatCache.get(locale);
173
174                if (ref == null || (patterns = ref.get()) == null) {
175                    String langtag = removeExtensions(locale).toLanguageTag();
176                    patterns = new AtomicReferenceArray<>(4);
177                    patterns.compareAndSet(0, null, convertDateTimePattern(
178                        getDateTimePattern(DateFormat.LONG, -1, langtag)));
179                    patterns.compareAndSet(1, null, convertDateTimePattern(
180                        getDateTimePattern(DateFormat.SHORT, -1, langtag)));
181                    patterns.compareAndSet(2, null, convertDateTimePattern(
182                        getDateTimePattern(-1, DateFormat.LONG, langtag)));
183                    patterns.compareAndSet(3, null, convertDateTimePattern(
184                        getDateTimePattern(-1, DateFormat.SHORT, langtag)));
185                    ref = new SoftReference<>(patterns);
186                    dateFormatCache.put(locale, ref);
187                }
188
189                return patterns;
190            }
191        };
192    }
193
194    public static DateFormatSymbolsProvider getDateFormatSymbolsProvider() {
195        return new DateFormatSymbolsProvider() {
196
197            @Override
198            public Locale[] getAvailableLocales() {
199                return getSupportedCalendarLocales();
200            }
201
202            @Override
203            public boolean isSupportedLocale(Locale locale) {
204                return isSupportedCalendarLocale(locale);
205            }
206
207            @Override
208            public DateFormatSymbols getInstance(Locale locale) {
209                DateFormatSymbols dfs;
210                SoftReference<DateFormatSymbols> ref =
211                    dateFormatSymbolsCache.get(locale);
212
213                if (ref == null || (dfs = ref.get()) == null) {
214                    dfs = new DateFormatSymbols(locale);
215                    String langTag = removeExtensions(locale).toLanguageTag();
216
217                    dfs.setAmPmStrings(getAmPmStrings(langTag, dfs.getAmPmStrings()));
218                    dfs.setEras(getEras(langTag, dfs.getEras()));
219                    dfs.setMonths(getMonths(langTag, dfs.getMonths()));
220                    dfs.setShortMonths(getShortMonths(langTag, dfs.getShortMonths()));
221                    dfs.setWeekdays(getWeekdays(langTag, dfs.getWeekdays()));
222                    dfs.setShortWeekdays(getShortWeekdays(langTag, dfs.getShortWeekdays()));
223                    ref = new SoftReference<>(dfs);
224                    dateFormatSymbolsCache.put(locale, ref);
225                }
226                return (DateFormatSymbols)dfs.clone();
227            }
228        };
229    }
230
231    public static NumberFormatProvider getNumberFormatProvider() {
232        return new NumberFormatProvider() {
233
234            @Override
235            public Locale[] getAvailableLocales() {
236                return getSupportedNativeDigitLocales();
237            }
238
239            @Override
240            public boolean isSupportedLocale(Locale locale) {
241                return isSupportedNativeDigitLocale(locale);
242            }
243
244            @Override
245            public NumberFormat getCurrencyInstance(Locale locale) {
246                AtomicReferenceArray<String> patterns = getNumberPatterns(locale);
247                return new DecimalFormat(patterns.get(NF_CURRENCY),
248                    DecimalFormatSymbols.getInstance(locale));
249            }
250
251            @Override
252            public NumberFormat getIntegerInstance(Locale locale) {
253                AtomicReferenceArray<String> patterns = getNumberPatterns(locale);
254                return new DecimalFormat(patterns.get(NF_INTEGER),
255                    DecimalFormatSymbols.getInstance(locale));
256            }
257
258            @Override
259            public NumberFormat getNumberInstance(Locale locale) {
260                AtomicReferenceArray<String> patterns = getNumberPatterns(locale);
261                return new DecimalFormat(patterns.get(NF_NUMBER),
262                    DecimalFormatSymbols.getInstance(locale));
263            }
264
265            @Override
266            public NumberFormat getPercentInstance(Locale locale) {
267                AtomicReferenceArray<String> patterns = getNumberPatterns(locale);
268                return new DecimalFormat(patterns.get(NF_PERCENT),
269                    DecimalFormatSymbols.getInstance(locale));
270            }
271
272            private AtomicReferenceArray<String> getNumberPatterns(Locale locale) {
273                AtomicReferenceArray<String> patterns;
274                SoftReference<AtomicReferenceArray<String>> ref = numberFormatCache.get(locale);
275
276                if (ref == null || (patterns = ref.get()) == null) {
277                    String langtag = locale.toLanguageTag();
278                    patterns = new AtomicReferenceArray<>(NF_MAX+1);
279                    for (int i = 0; i <= NF_MAX; i++) {
280                        patterns.compareAndSet(i, null, getNumberPattern(i, langtag));
281                    }
282                    ref = new SoftReference<>(patterns);
283                    numberFormatCache.put(locale, ref);
284                }
285                return patterns;
286            }
287        };
288    }
289
290    public static DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() {
291        return new DecimalFormatSymbolsProvider() {
292
293            @Override
294            public Locale[] getAvailableLocales() {
295                return getSupportedNativeDigitLocales();
296            }
297
298            @Override
299            public boolean isSupportedLocale(Locale locale) {
300                return isSupportedNativeDigitLocale(locale);
301            }
302
303            @Override
304            public DecimalFormatSymbols getInstance(Locale locale) {
305                DecimalFormatSymbols dfs;
306                SoftReference<DecimalFormatSymbols> ref =
307                    decimalFormatSymbolsCache.get(locale);
308
309                if (ref == null || (dfs = ref.get()) == null) {
310                    dfs = new DecimalFormatSymbols(getNumberLocale(locale));
311                    String langTag = removeExtensions(locale).toLanguageTag();
312
313                    // DecimalFormatSymbols.setInternationalCurrencySymbol() has
314                    // a side effect of setting the currency symbol as well. So
315                    // the calling order is relevant here.
316                    dfs.setInternationalCurrencySymbol(getInternationalCurrencySymbol(langTag, dfs.getInternationalCurrencySymbol()));
317                    dfs.setCurrencySymbol(getCurrencySymbol(langTag, dfs.getCurrencySymbol()));
318                    dfs.setDecimalSeparator(getDecimalSeparator(langTag, dfs.getDecimalSeparator()));
319                    dfs.setGroupingSeparator(getGroupingSeparator(langTag, dfs.getGroupingSeparator()));
320                    dfs.setInfinity(getInfinity(langTag, dfs.getInfinity()));
321                    dfs.setMinusSign(getMinusSign(langTag, dfs.getMinusSign()));
322                    dfs.setMonetaryDecimalSeparator(getMonetaryDecimalSeparator(langTag, dfs.getMonetaryDecimalSeparator()));
323                    dfs.setNaN(getNaN(langTag, dfs.getNaN()));
324                    dfs.setPercent(getPercent(langTag, dfs.getPercent()));
325                    dfs.setPerMill(getPerMill(langTag, dfs.getPerMill()));
326                    dfs.setZeroDigit(getZeroDigit(langTag, dfs.getZeroDigit()));
327                    ref = new SoftReference<>(dfs);
328                    decimalFormatSymbolsCache.put(locale, ref);
329                }
330                return (DecimalFormatSymbols)dfs.clone();
331            }
332        };
333    }
334
335    public static CalendarDataProvider getCalendarDataProvider() {
336        return new CalendarDataProvider() {
337            @Override
338            public Locale[] getAvailableLocales() {
339                return getSupportedCalendarLocales();
340            }
341
342            @Override
343            public boolean isSupportedLocale(Locale locale) {
344                return isSupportedCalendarLocale(locale);
345            }
346
347            @Override
348            public int getFirstDayOfWeek(Locale locale) {
349                int first = getCalendarDataValue(
350                                 removeExtensions(locale).toLanguageTag(),
351                                 CD_FIRSTDAYOFWEEK);
352                if (first != -1) {
353                    return (first + 1) % 7 + 1;
354                } else {
355                    return 0;
356                }
357            }
358
359            @Override
360            public int getMinimalDaysInFirstWeek(Locale locale) {
361                return 0;
362            }
363        };
364    }
365
366    public static CalendarProvider getCalendarProvider() {
367        return new CalendarProvider() {
368            @Override
369            public Locale[] getAvailableLocales() {
370                return getSupportedCalendarLocales();
371            }
372
373            @Override
374            public boolean isSupportedLocale(Locale locale) {
375                return isSupportedCalendarLocale(locale);
376            }
377
378            @Override
379            public Calendar getInstance(TimeZone zone, Locale locale) {
380                return new Calendar.Builder()
381                             .setLocale(getCalendarLocale(locale))
382                             .setTimeZone(zone)
383                             .setInstant(System.currentTimeMillis())
384                             .build();
385            }
386        };
387    }
388
389    public static CurrencyNameProvider getCurrencyNameProvider() {
390        return new CurrencyNameProvider() {
391            @Override
392            public Locale[] getAvailableLocales() {
393                return supportedLocale;
394            }
395
396            @Override
397            public boolean isSupportedLocale(Locale locale) {
398                // Ignore the extensions for now
399                return supportedLocaleSet.contains(locale.stripExtensions()) &&
400                       locale.getLanguage().equals(nativeDisplayLanguage);
401            }
402
403            @Override
404            public String getSymbol(String currencyCode, Locale locale) {
405                // Retrieves the currency symbol by calling
406                // GetLocaleInfoEx(LOCALE_SCURRENCY).
407                // It only works with the "locale"'s currency in its native
408                // language.
409                try {
410                    if (Currency.getInstance(locale).getCurrencyCode()
411                        .equals(currencyCode)) {
412                        return getDisplayString(locale.toLanguageTag(),
413                                DN_CURRENCY_SYMBOL, currencyCode);
414                    }
415                } catch (IllegalArgumentException iae) {}
416                return null;
417            }
418
419            @Override
420            public String getDisplayName(String currencyCode, Locale locale) {
421                // Retrieves the display name by calling
422                // GetLocaleInfoEx(LOCALE_SNATIVECURRNAME).
423                // It only works with the "locale"'s currency in its native
424                // language.
425                try {
426                    if (Currency.getInstance(locale).getCurrencyCode()
427                        .equals(currencyCode)) {
428                        return getDisplayString(locale.toLanguageTag(),
429                                DN_CURRENCY_NAME, currencyCode);
430                    }
431                } catch (IllegalArgumentException iae) {}
432                return null;
433            }
434        };
435    }
436
437    public static LocaleNameProvider getLocaleNameProvider() {
438        return new LocaleNameProvider() {
439            @Override
440            public Locale[] getAvailableLocales() {
441                return supportedLocale;
442            }
443
444            @Override
445            public boolean isSupportedLocale(Locale locale) {
446                return supportedLocaleSet.contains(locale.stripExtensions()) &&
447                       locale.getLanguage().equals(nativeDisplayLanguage);
448            }
449
450            @Override
451            public String getDisplayLanguage(String languageCode, Locale locale) {
452                // Retrieves the display language name by calling
453                // GetLocaleInfoEx(LOCALE_SLOCALIZEDLANGUAGENAME).
454                return getDisplayString(locale.toLanguageTag(),
455                            DN_LOCALE_LANGUAGE, languageCode);
456            }
457
458            @Override
459            public String getDisplayCountry(String countryCode, Locale locale) {
460                // Retrieves the display country name by calling
461                // GetLocaleInfoEx(LOCALE_SLOCALIZEDCOUNTRYNAME).
462                return getDisplayString(locale.toLanguageTag(),
463                            DN_LOCALE_REGION, nativeDisplayLanguage+"-"+countryCode);
464            }
465
466            @Override
467            public String getDisplayScript(String scriptCode, Locale locale) {
468                return null;
469            }
470
471            @Override
472            public String getDisplayVariant(String variantCode, Locale locale) {
473                return null;
474            }
475        };
476    }
477
478
479    private static String convertDateTimePattern(String winPattern) {
480        String ret = winPattern.replaceAll("dddd", "EEEE");
481        ret = ret.replaceAll("ddd", "EEE");
482        ret = ret.replaceAll("tt", "aa");
483        ret = ret.replaceAll("g", "GG");
484        return ret;
485    }
486
487    private static Locale[] getSupportedCalendarLocales() {
488        if (supportedLocale.length != 0 &&
489            supportedLocaleSet.contains(Locale.JAPAN) &&
490            isJapaneseCalendar()) {
491            Locale[] sup = new Locale[supportedLocale.length+1];
492            sup[0] = JRELocaleConstants.JA_JP_JP;
493            System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length);
494            return sup;
495        }
496        return supportedLocale;
497    }
498
499    private static boolean isSupportedCalendarLocale(Locale locale) {
500        Locale base = locale;
501
502        if (base.hasExtensions() || base.getVariant() != "") {
503            // strip off extensions and variant.
504            base = new Locale.Builder()
505                            .setLocale(locale)
506                            .clearExtensions()
507                            .build();
508        }
509
510        if (!supportedLocaleSet.contains(base)) {
511            return false;
512        }
513
514        int calid = getCalendarID(base.toLanguageTag());
515        if (calid <= 0 || calid >= calIDToLDML.length) {
516            return false;
517        }
518
519        String requestedCalType = locale.getUnicodeLocaleType("ca");
520        String nativeCalType = calIDToLDML[calid]
521                .replaceFirst("_.*", ""); // remove locale part.
522
523        if (requestedCalType == null) {
524            return Calendar.getAvailableCalendarTypes().contains(nativeCalType);
525        } else {
526            return requestedCalType.equals(nativeCalType);
527        }
528    }
529
530    private static Locale[] getSupportedNativeDigitLocales() {
531        if (supportedLocale.length != 0 &&
532            supportedLocaleSet.contains(JRELocaleConstants.TH_TH) &&
533            isNativeDigit("th-TH")) {
534            Locale[] sup = new Locale[supportedLocale.length+1];
535            sup[0] = JRELocaleConstants.TH_TH_TH;
536            System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length);
537            return sup;
538        }
539        return supportedLocale;
540    }
541
542    private static boolean isSupportedNativeDigitLocale(Locale locale) {
543        // special case for th_TH_TH
544        if (JRELocaleConstants.TH_TH_TH.equals(locale)) {
545            return isNativeDigit("th-TH");
546        }
547
548        String numtype = null;
549        Locale base = locale;
550        if (locale.hasExtensions()) {
551            numtype = locale.getUnicodeLocaleType("nu");
552            base = locale.stripExtensions();
553        }
554
555        if (supportedLocaleSet.contains(base)) {
556            // Only supports Latin or Thai (in thai locales) digits.
557            if (numtype == null || numtype.equals("latn")) {
558                return true;
559            } else if (locale.getLanguage().equals("th")) {
560                return "thai".equals(numtype) &&
561                       isNativeDigit(locale.toLanguageTag());
562            }
563        }
564
565        return false;
566    }
567
568    private static Locale removeExtensions(Locale src) {
569        return new Locale.Builder().setLocale(src).clearExtensions().build();
570    }
571
572    private static boolean isJapaneseCalendar() {
573        return getCalendarID("ja-JP") == 3; // 3: CAL_JAPAN
574    }
575
576    private static Locale getCalendarLocale(Locale locale) {
577        int calid = getCalendarID(locale.toLanguageTag());
578        if (calid > 0 && calid < calIDToLDML.length) {
579            Locale.Builder lb = new Locale.Builder();
580            String[] caltype = calIDToLDML[calid].split("_");
581            if (caltype.length > 1) {
582                lb.setLocale(Locale.forLanguageTag(caltype[1]));
583            } else {
584                lb.setLocale(locale);
585            }
586            lb.setUnicodeLocaleKeyword("ca", caltype[0]);
587            return lb.build();
588        }
589
590        return locale;
591    }
592
593    private static Locale getNumberLocale(Locale src) {
594        if (JRELocaleConstants.TH_TH.equals(src)) {
595            if (isNativeDigit("th-TH")) {
596                Locale.Builder lb = new Locale.Builder().setLocale(src);
597                lb.setUnicodeLocaleKeyword("nu", "thai");
598                return lb.build();
599            }
600        }
601
602        return src;
603    }
604
605    // native methods
606
607    // initialize
608    private static native boolean initialize();
609    private static native String getDefaultLocale(int cat);
610
611    // For DateFormatProvider
612    private static native String getDateTimePattern(int dateStyle, int timeStyle, String langTag);
613    private static native int getCalendarID(String langTag);
614
615    // For DateFormatSymbolsProvider
616    private static native String[] getAmPmStrings(String langTag, String[] ampm);
617    private static native String[] getEras(String langTag, String[] eras);
618    private static native String[] getMonths(String langTag, String[] months);
619    private static native String[] getShortMonths(String langTag, String[] smonths);
620    private static native String[] getWeekdays(String langTag, String[] wdays);
621    private static native String[] getShortWeekdays(String langTag, String[] swdays);
622
623    // For NumberFormatProvider
624    private static native String getNumberPattern(int numberStyle, String langTag);
625    private static native boolean isNativeDigit(String langTag);
626
627    // For DecimalFormatSymbolsProvider
628    private static native String getCurrencySymbol(String langTag, String currencySymbol);
629    private static native char getDecimalSeparator(String langTag, char decimalSeparator);
630    private static native char getGroupingSeparator(String langTag, char groupingSeparator);
631    private static native String getInfinity(String langTag, String infinity);
632    private static native String getInternationalCurrencySymbol(String langTag, String internationalCurrencySymbol);
633    private static native char getMinusSign(String langTag, char minusSign);
634    private static native char getMonetaryDecimalSeparator(String langTag, char monetaryDecimalSeparator);
635    private static native String getNaN(String langTag, String nan);
636    private static native char getPercent(String langTag, char percent);
637    private static native char getPerMill(String langTag, char perMill);
638    private static native char getZeroDigit(String langTag, char zeroDigit);
639
640    // For CalendarDataProvider
641    private static native int getCalendarDataValue(String langTag, int type);
642
643    // For Locale/CurrencyNameProvider
644    private static native String getDisplayString(String langTag, int key, String value);
645}
646