Path: blob/master/src/java.base/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java
41161 views
/*1* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package sun.util.locale.provider;2526import java.lang.ref.SoftReference;27import java.text.DateFormat;28import java.text.DateFormatSymbols;29import java.text.DecimalFormat;30import java.text.DecimalFormatSymbols;31import java.text.NumberFormat;32import java.text.SimpleDateFormat;33import java.text.spi.DateFormatProvider;34import java.text.spi.DateFormatSymbolsProvider;35import java.text.spi.DecimalFormatSymbolsProvider;36import java.text.spi.NumberFormatProvider;37import java.util.Calendar;38import java.util.Collections;39import java.util.Currency;40import java.util.HashMap;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.CalendarNameProvider;52import java.util.spi.CurrencyNameProvider;53import java.util.spi.LocaleNameProvider;54import sun.text.spi.JavaTimeDateTimePatternProvider;55import sun.util.spi.CalendarProvider;5657/**58* LocaleProviderdapter implementation for the Windows locale data.59*60* @author Naoto Sato61*/62public class HostLocaleProviderAdapterImpl {6364// locale categories65private static final int CAT_DISPLAY = 0;66private static final int CAT_FORMAT = 1;6768// NumberFormat styles69private static final int NF_NUMBER = 0;70private static final int NF_CURRENCY = 1;71private static final int NF_PERCENT = 2;72private static final int NF_INTEGER = 3;73private static final int NF_MAX = NF_INTEGER;7475// CalendarData value types76private static final int CD_FIRSTDAYOFWEEK = 0;77private static final int CD_FIRSTWEEKOFYEAR = 1;7879// Currency/Locale display name types80private static final int DN_CURRENCY_NAME = 0;81private static final int DN_CURRENCY_SYMBOL = 1;82private static final int DN_LOCALE_LANGUAGE = 2;83private static final int DN_LOCALE_SCRIPT = 3;84private static final int DN_LOCALE_REGION = 4;85private static final int DN_LOCALE_VARIANT = 5;8687// Windows Calendar IDs88private static final int CAL_JAPAN = 3;8990// Native Calendar ID to LDML calendar type map91private static final String[] calIDToLDML = {92"",93"gregory",94"gregory_en-US",95"japanese",96"roc",97"", // No appropriate type for CAL_KOREA98"islamic",99"buddhist",100"hebrew",101"gregory_fr",102"gregory_ar",103"gregory_en",104"gregory_fr", "", "", "", "", "", "", "", "", "", "",105"islamic-umalqura",106};107108// Caches109private static final ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> dateFormatCache = new ConcurrentHashMap<>();110private static final ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> dateFormatSymbolsCache = new ConcurrentHashMap<>();111private static final ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> numberFormatCache = new ConcurrentHashMap<>();112private static final ConcurrentMap<Locale, SoftReference<DecimalFormatSymbols>> decimalFormatSymbolsCache = new ConcurrentHashMap<>();113114private static final Set<Locale> supportedLocaleSet;115private static final String nativeDisplayLanguage;116static {117Set<Locale> tmpSet = new HashSet<>();118if (initialize()) {119// Assuming the default locales do not include any extensions, so120// no stripping is needed here.121Control c = Control.getNoFallbackControl(Control.FORMAT_DEFAULT);122String displayLocale = getDefaultLocale(CAT_DISPLAY);123Locale l = Locale.forLanguageTag(displayLocale.replace('_', '-'));124tmpSet.addAll(c.getCandidateLocales("", l));125nativeDisplayLanguage = l.getLanguage();126127String formatLocale = getDefaultLocale(CAT_FORMAT);128if (!formatLocale.equals(displayLocale)) {129l = Locale.forLanguageTag(formatLocale.replace('_', '-'));130tmpSet.addAll(c.getCandidateLocales("", l));131}132} else {133nativeDisplayLanguage = "";134}135supportedLocaleSet = Collections.unmodifiableSet(tmpSet);136}137private static final Locale[] supportedLocale = supportedLocaleSet.toArray(new Locale[0]);138139public static DateFormatProvider getDateFormatProvider() {140return new DateFormatProvider() {141@Override142public Locale[] getAvailableLocales() {143return getSupportedCalendarLocales();144}145146@Override147public boolean isSupportedLocale(Locale locale) {148return isSupportedCalendarLocale(locale);149}150151@Override152public DateFormat getDateInstance(int style, Locale locale) {153AtomicReferenceArray<String> patterns = getDateTimePatterns(locale);154return new SimpleDateFormat(patterns.get(style/2),155getCalendarLocale(locale));156}157158@Override159public DateFormat getTimeInstance(int style, Locale locale) {160AtomicReferenceArray<String> patterns = getDateTimePatterns(locale);161return new SimpleDateFormat(patterns.get(style/2+2),162getCalendarLocale(locale));163}164165@Override166public DateFormat getDateTimeInstance(int dateStyle,167int timeStyle, Locale locale) {168AtomicReferenceArray<String> patterns = getDateTimePatterns(locale);169String pattern = new StringBuilder(patterns.get(dateStyle/2))170.append(" ")171.append(patterns.get(timeStyle/2+2))172.toString();173return new SimpleDateFormat(pattern, getCalendarLocale(locale));174}175176private AtomicReferenceArray<String> getDateTimePatterns(Locale locale) {177AtomicReferenceArray<String> patterns;178SoftReference<AtomicReferenceArray<String>> ref = dateFormatCache.get(locale);179180if (ref == null || (patterns = ref.get()) == null) {181String langtag = removeExtensions(locale).toLanguageTag();182patterns = new AtomicReferenceArray<>(4);183patterns.compareAndSet(0, null, convertDateTimePattern(184getDateTimePattern(DateFormat.LONG, -1, langtag)));185patterns.compareAndSet(1, null, convertDateTimePattern(186getDateTimePattern(DateFormat.SHORT, -1, langtag)));187patterns.compareAndSet(2, null, convertDateTimePattern(188getDateTimePattern(-1, DateFormat.LONG, langtag)));189patterns.compareAndSet(3, null, convertDateTimePattern(190getDateTimePattern(-1, DateFormat.SHORT, langtag)));191ref = new SoftReference<>(patterns);192dateFormatCache.put(locale, ref);193}194195return patterns;196}197};198}199200public static DateFormatSymbolsProvider getDateFormatSymbolsProvider() {201return new DateFormatSymbolsProvider() {202203@Override204public Locale[] getAvailableLocales() {205return getSupportedCalendarLocales();206}207208@Override209public boolean isSupportedLocale(Locale locale) {210return isSupportedCalendarLocale(locale);211}212213@Override214public DateFormatSymbols getInstance(Locale locale) {215DateFormatSymbols dfs;216SoftReference<DateFormatSymbols> ref =217dateFormatSymbolsCache.get(locale);218219if (ref == null || (dfs = ref.get()) == null) {220dfs = new DateFormatSymbols(locale);221String langTag = removeExtensions(locale).toLanguageTag();222223dfs.setAmPmStrings(getAmPmStrings(langTag, dfs.getAmPmStrings()));224dfs.setEras(getEras(langTag, dfs.getEras()));225dfs.setMonths(getMonths(langTag, dfs.getMonths()));226dfs.setShortMonths(getShortMonths(langTag, dfs.getShortMonths()));227dfs.setWeekdays(getWeekdays(langTag, dfs.getWeekdays()));228dfs.setShortWeekdays(getShortWeekdays(langTag, dfs.getShortWeekdays()));229ref = new SoftReference<>(dfs);230dateFormatSymbolsCache.put(locale, ref);231}232return (DateFormatSymbols)dfs.clone();233}234};235}236237public static NumberFormatProvider getNumberFormatProvider() {238return new NumberFormatProvider() {239240@Override241public Locale[] getAvailableLocales() {242return getSupportedNativeDigitLocales();243}244245@Override246public boolean isSupportedLocale(Locale locale) {247return isSupportedNativeDigitLocale(locale);248}249250@Override251public NumberFormat getCurrencyInstance(Locale locale) {252AtomicReferenceArray<String> patterns = getNumberPatterns(locale);253return new DecimalFormat(patterns.get(NF_CURRENCY),254DecimalFormatSymbols.getInstance(locale));255}256257@Override258public NumberFormat getIntegerInstance(Locale locale) {259AtomicReferenceArray<String> patterns = getNumberPatterns(locale);260DecimalFormat format = new DecimalFormat(patterns.get(NF_INTEGER),261DecimalFormatSymbols.getInstance(locale));262return HostLocaleProviderAdapter.makeIntegerFormatter(format);263}264265@Override266public NumberFormat getNumberInstance(Locale locale) {267AtomicReferenceArray<String> patterns = getNumberPatterns(locale);268return new DecimalFormat(patterns.get(NF_NUMBER),269DecimalFormatSymbols.getInstance(locale));270}271272@Override273public NumberFormat getPercentInstance(Locale locale) {274AtomicReferenceArray<String> patterns = getNumberPatterns(locale);275return new DecimalFormat(patterns.get(NF_PERCENT),276DecimalFormatSymbols.getInstance(locale));277}278279private AtomicReferenceArray<String> getNumberPatterns(Locale locale) {280AtomicReferenceArray<String> patterns;281SoftReference<AtomicReferenceArray<String>> ref = numberFormatCache.get(locale);282283if (ref == null || (patterns = ref.get()) == null) {284String langtag = locale.toLanguageTag();285patterns = new AtomicReferenceArray<>(NF_MAX+1);286for (int i = 0; i <= NF_MAX; i++) {287patterns.compareAndSet(i, null, getNumberPattern(i, langtag));288}289ref = new SoftReference<>(patterns);290numberFormatCache.put(locale, ref);291}292return patterns;293}294};295}296297public static DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() {298return new DecimalFormatSymbolsProvider() {299300@Override301public Locale[] getAvailableLocales() {302return getSupportedNativeDigitLocales();303}304305@Override306public boolean isSupportedLocale(Locale locale) {307return isSupportedNativeDigitLocale(locale);308}309310@Override311public DecimalFormatSymbols getInstance(Locale locale) {312DecimalFormatSymbols dfs;313SoftReference<DecimalFormatSymbols> ref =314decimalFormatSymbolsCache.get(locale);315316if (ref == null || (dfs = ref.get()) == null) {317dfs = new DecimalFormatSymbols(getNumberLocale(locale));318String langTag = removeExtensions(locale).toLanguageTag();319320// DecimalFormatSymbols.setInternationalCurrencySymbol() has321// a side effect of setting the currency symbol as well. So322// the calling order is relevant here.323dfs.setInternationalCurrencySymbol(getInternationalCurrencySymbol(langTag, dfs.getInternationalCurrencySymbol()));324dfs.setCurrencySymbol(getCurrencySymbol(langTag, dfs.getCurrencySymbol()));325dfs.setDecimalSeparator(getDecimalSeparator(langTag, dfs.getDecimalSeparator()));326dfs.setGroupingSeparator(getGroupingSeparator(langTag, dfs.getGroupingSeparator()));327dfs.setInfinity(getInfinity(langTag, dfs.getInfinity()));328dfs.setMinusSign(getMinusSign(langTag, dfs.getMinusSign()));329dfs.setMonetaryDecimalSeparator(getMonetaryDecimalSeparator(langTag, dfs.getMonetaryDecimalSeparator()));330dfs.setNaN(getNaN(langTag, dfs.getNaN()));331dfs.setPercent(getPercent(langTag, dfs.getPercent()));332dfs.setPerMill(getPerMill(langTag, dfs.getPerMill()));333dfs.setZeroDigit(getZeroDigit(langTag, dfs.getZeroDigit()));334ref = new SoftReference<>(dfs);335decimalFormatSymbolsCache.put(locale, ref);336}337return (DecimalFormatSymbols)dfs.clone();338}339};340}341342public static CalendarDataProvider getCalendarDataProvider() {343return new CalendarDataProvider() {344@Override345public Locale[] getAvailableLocales() {346return getSupportedCalendarLocales();347}348349@Override350public boolean isSupportedLocale(Locale locale) {351return isSupportedCalendarLocale(locale);352}353354@Override355public int getFirstDayOfWeek(Locale locale) {356int first = getCalendarDataValue(357removeExtensions(locale).toLanguageTag(),358CD_FIRSTDAYOFWEEK);359if (first != -1) {360return (first + 1) % 7 + 1;361} else {362return 0;363}364}365366@Override367public int getMinimalDaysInFirstWeek(Locale locale) {368int firstWeek = getCalendarDataValue(369removeExtensions(locale).toLanguageTag(),370CD_FIRSTWEEKOFYEAR);371// Interpret the value from Windows LOCALE_IFIRSTWEEKOFYEAR setting372return switch (firstWeek) {373case 1 -> 7; // First full week following 1/1 is the first week of the year.374case 2 -> 4; // First week containing at least four days is the first week of the year.375default -> 1; // First week can be a single day, if 1/1 falls on the last day of the week.376};377}378};379}380381public static CalendarNameProvider getCalendarNameProvider() {382return new CalendarNameProvider() {383@Override384public Locale[] getAvailableLocales() {385return getSupportedCalendarLocales();386}387388@Override389public boolean isSupportedLocale(Locale locale) {390return isSupportedCalendarLocale(locale);391}392393@Override394public String getDisplayName(String calendarType, int field,395int value, int style, Locale locale) {396String[] names = getCalendarDisplayStrings(removeExtensions(locale).toLanguageTag(),397getCalendarIDFromLDMLType(calendarType), field, style);398if (field == Calendar.DAY_OF_WEEK) {399// Align value to array index400value--;401}402if (names != null && value >= 0 && value < names.length) {403return names[value];404} else {405return null;406}407}408409@Override410public Map<String, Integer> getDisplayNames(String calendarType,411int field, int style, Locale locale) {412Map<String, Integer> map = null;413String[] names = getCalendarDisplayStrings(removeExtensions(locale).toLanguageTag(),414getCalendarIDFromLDMLType(calendarType), field, style);415if (names != null) {416map = new HashMap<>();417for (int value = 0; value < names.length; value++) {418if (names[value] != null) {419map.put(names[value],420// Align array index to field value421field == Calendar.DAY_OF_WEEK ? value + 1 : value);422}423}424map = map.isEmpty() ? null : map;425}426return map;427}428};429}430431public static CalendarProvider getCalendarProvider() {432return new CalendarProvider() {433@Override434public Locale[] getAvailableLocales() {435return getSupportedCalendarLocales();436}437438@Override439public boolean isSupportedLocale(Locale locale) {440return isSupportedCalendarLocale(locale);441}442443@Override444public Calendar getInstance(TimeZone zone, Locale locale) {445return new Calendar.Builder()446.setLocale(getCalendarLocale(locale))447.setTimeZone(zone)448.setInstant(System.currentTimeMillis())449.build();450}451};452}453454public static CurrencyNameProvider getCurrencyNameProvider() {455return new CurrencyNameProvider() {456@Override457public Locale[] getAvailableLocales() {458return supportedLocale;459}460461@Override462public boolean isSupportedLocale(Locale locale) {463// Ignore the extensions for now464return supportedLocaleSet.contains(locale.stripExtensions()) &&465locale.getLanguage().equals(nativeDisplayLanguage);466}467468@Override469public String getSymbol(String currencyCode, Locale locale) {470// Retrieves the currency symbol by calling471// GetLocaleInfoEx(LOCALE_SCURRENCY).472// It only works with the "locale"'s currency in its native473// language.474try {475if (Currency.getInstance(locale).getCurrencyCode()476.equals(currencyCode)) {477return getDisplayString(locale.toLanguageTag(),478DN_CURRENCY_SYMBOL, currencyCode);479}480} catch (IllegalArgumentException iae) {}481return null;482}483484@Override485public String getDisplayName(String currencyCode, Locale locale) {486// Retrieves the display name by calling487// GetLocaleInfoEx(LOCALE_SNATIVECURRNAME).488// It only works with the "locale"'s currency in its native489// language.490try {491if (Currency.getInstance(locale).getCurrencyCode()492.equals(currencyCode)) {493return getDisplayString(locale.toLanguageTag(),494DN_CURRENCY_NAME, currencyCode);495}496} catch (IllegalArgumentException iae) {}497return null;498}499};500}501502public static LocaleNameProvider getLocaleNameProvider() {503return new LocaleNameProvider() {504@Override505public Locale[] getAvailableLocales() {506return supportedLocale;507}508509@Override510public boolean isSupportedLocale(Locale locale) {511return supportedLocaleSet.contains(locale.stripExtensions()) &&512locale.getLanguage().equals(nativeDisplayLanguage);513}514515@Override516public String getDisplayLanguage(String languageCode, Locale locale) {517// Retrieves the display language name by calling518// GetLocaleInfoEx(LOCALE_SLOCALIZEDLANGUAGENAME).519return getDisplayString(locale.toLanguageTag(),520DN_LOCALE_LANGUAGE, languageCode);521}522523@Override524public String getDisplayCountry(String countryCode, Locale locale) {525// Retrieves the display country name by calling526// GetLocaleInfoEx(LOCALE_SLOCALIZEDCOUNTRYNAME).527String str = getDisplayString(locale.toLanguageTag(),528DN_LOCALE_REGION,529nativeDisplayLanguage+"-"+countryCode);530// Hack: Windows 10 returns translated "Unknown Region (XX)"531// for localized XX region name. Take that as not known.532if (str != null && str.endsWith("("+countryCode+")")) {533return null;534}535return str;536}537538@Override539public String getDisplayScript(String scriptCode, Locale locale) {540return null;541}542543@Override544public String getDisplayVariant(String variantCode, Locale locale) {545return null;546}547};548}549550public static JavaTimeDateTimePatternProvider getJavaTimeDateTimePatternProvider() {551return new JavaTimeDateTimePatternProvider() {552@Override553public Locale[] getAvailableLocales() {554return getSupportedCalendarLocales();555}556557@Override558public boolean isSupportedLocale(Locale locale) {559return isSupportedCalendarLocale(locale);560}561562@Override563public String getJavaTimeDateTimePattern(int timeStyle, int dateStyle, String calType, Locale locale) {564AtomicReferenceArray<String> patterns = getDateTimePatterns(locale);565String datePattern = dateStyle != - 1 ? patterns.get(dateStyle / 2) : "";566String timePattern = timeStyle != - 1 ? patterns.get(timeStyle / 2 + 2) : "";567String delim = dateStyle != -1 && timeStyle != - 1 ? " " : "";568return toJavaTimeDateTimePattern(calType, datePattern + delim + timePattern);569}570571private AtomicReferenceArray<String> getDateTimePatterns(Locale locale) {572AtomicReferenceArray<String> patterns;573SoftReference<AtomicReferenceArray<String>> ref = dateFormatCache.get(locale);574575if (ref == null || (patterns = ref.get()) == null) {576String langtag = removeExtensions(locale).toLanguageTag();577patterns = new AtomicReferenceArray<>(4);578patterns.compareAndSet(0, null, convertDateTimePattern(579getDateTimePattern(DateFormat.LONG, -1, langtag)));580patterns.compareAndSet(1, null, convertDateTimePattern(581getDateTimePattern(DateFormat.SHORT, -1, langtag)));582patterns.compareAndSet(2, null, convertDateTimePattern(583getDateTimePattern(-1, DateFormat.LONG, langtag)));584patterns.compareAndSet(3, null, convertDateTimePattern(585getDateTimePattern(-1, DateFormat.SHORT, langtag)));586ref = new SoftReference<>(patterns);587dateFormatCache.put(locale, ref);588}589return patterns;590}591/**592* This method will convert JRE Date/time Pattern String to JSR310593* type Date/Time Pattern594*/595private String toJavaTimeDateTimePattern(String calendarType, String jrePattern) {596int length = jrePattern.length();597StringBuilder sb = new StringBuilder(length);598boolean inQuote = false;599int count = 0;600char lastLetter = 0;601for (int i = 0; i < length; i++) {602char c = jrePattern.charAt(i);603if (c == '\'') {604// '' is treated as a single quote regardless of being605// in a quoted section.606if ((i + 1) < length) {607char nextc = jrePattern.charAt(i + 1);608if (nextc == '\'') {609i++;610if (count != 0) {611convert(calendarType, lastLetter, count, sb);612lastLetter = 0;613count = 0;614}615sb.append("''");616continue;617}618}619if (!inQuote) {620if (count != 0) {621convert(calendarType, lastLetter, count, sb);622lastLetter = 0;623count = 0;624}625inQuote = true;626} else {627inQuote = false;628}629sb.append(c);630continue;631}632if (inQuote) {633sb.append(c);634continue;635}636if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {637if (count != 0) {638convert(calendarType, lastLetter, count, sb);639lastLetter = 0;640count = 0;641}642sb.append(c);643continue;644}645if (lastLetter == 0 || lastLetter == c) {646lastLetter = c;647count++;648continue;649}650convert(calendarType, lastLetter, count, sb);651lastLetter = c;652count = 1;653}654if (inQuote) {655// should not come here.656// returning null so that FALLBACK provider will kick in.657return null;658}659if (count != 0) {660convert(calendarType, lastLetter, count, sb);661}662return sb.toString();663}664665private void convert(String calendarType, char letter, int count, StringBuilder sb) {666switch (letter) {667case 'G':668if (calendarType.equals("japanese")) {669if (count >= 4) {670count = 1;671} else {672count = 5;673}674} else if (!calendarType.equals("iso8601")) {675// Adjust the number of 'G's676// Gregorian calendar is iso8601 for java.time677if (count >= 4) {678// JRE full -> JavaTime full679count = 4;680} else {681// JRE short -> JavaTime short682count = 1;683}684}685break;686case 'y':687if (calendarType.equals("japanese") && count >= 4) {688// JRE specific "gan-nen" support689count = 1;690}691break;692default:693// JSR 310 and CLDR define 5-letter patterns for narrow text.694if (count > 4) {695count = 4;696}697break;698}699appendN(letter, count, sb);700}701702private void appendN(char c, int n, StringBuilder sb) {703for (int i = 0; i < n; i++) {704sb.append(c);705}706}707};708}709710private static String convertDateTimePattern(String winPattern) {711String ret = winPattern.replaceAll("dddd", "EEEE");712ret = ret.replaceAll("ddd", "EEE");713ret = ret.replaceAll("tt", "a");714ret = ret.replaceAll("g", "GG");715return ret;716}717718private static Locale[] getSupportedCalendarLocales() {719if (supportedLocale.length != 0 &&720supportedLocaleSet.contains(Locale.JAPAN) &&721isJapaneseCalendar()) {722Locale[] sup = new Locale[supportedLocale.length+1];723sup[0] = JRELocaleConstants.JA_JP_JP;724System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length);725return sup;726}727return supportedLocale;728}729730private static boolean isSupportedCalendarLocale(Locale locale) {731Locale base = stripVariantAndExtensions(locale);732733if (!supportedLocaleSet.contains(base)) {734return false;735}736737int calid = getCalendarID(base.toLanguageTag());738if (calid <= 0 || calid >= calIDToLDML.length) {739return false;740}741742String requestedCalType = locale.getUnicodeLocaleType("ca");743String nativeCalType = calIDToLDML[calid]744.replaceFirst("_.*", ""); // remove locale part.745746if (requestedCalType == null) {747return Calendar.getAvailableCalendarTypes().contains(nativeCalType);748} else {749return requestedCalType.equals(nativeCalType);750}751}752753private static Locale[] getSupportedNativeDigitLocales() {754if (supportedLocale.length != 0 &&755supportedLocaleSet.contains(JRELocaleConstants.TH_TH) &&756isNativeDigit("th-TH")) {757Locale[] sup = new Locale[supportedLocale.length+1];758sup[0] = JRELocaleConstants.TH_TH_TH;759System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length);760return sup;761}762return supportedLocale;763}764765private static boolean isSupportedNativeDigitLocale(Locale locale) {766// special case for th_TH_TH767if (JRELocaleConstants.TH_TH_TH.equals(locale)) {768return isNativeDigit("th-TH");769}770771String numtype = null;772Locale base = locale;773if (locale.hasExtensions()) {774numtype = locale.getUnicodeLocaleType("nu");775base = locale.stripExtensions();776}777778if (supportedLocaleSet.contains(base)) {779// Only supports Latin or Thai (in thai locales) digits.780if (numtype == null || numtype.equals("latn")) {781return true;782} else if (locale.getLanguage().equals("th")) {783return "thai".equals(numtype) &&784isNativeDigit(locale.toLanguageTag());785}786}787788return false;789}790791private static Locale removeExtensions(Locale src) {792return new Locale.Builder().setLocale(src).clearExtensions().build();793}794795private static boolean isJapaneseCalendar() {796return getCalendarID("ja-JP") == CAL_JAPAN;797}798799private static Locale stripVariantAndExtensions(Locale locale) {800if (locale.hasExtensions() || locale.getVariant() != "") {801// strip off extensions and variant.802locale = new Locale.Builder()803.setLocale(locale)804.clearExtensions()805.build();806}807808return locale;809}810811private static Locale getCalendarLocale(Locale locale) {812int calid = getCalendarID(stripVariantAndExtensions(locale).toLanguageTag());813if (calid > 0 && calid < calIDToLDML.length) {814Locale.Builder lb = new Locale.Builder();815String[] caltype = calIDToLDML[calid].split("_");816if (caltype.length > 1) {817lb.setLocale(Locale.forLanguageTag(caltype[1]));818} else {819lb.setLocale(locale);820}821lb.setUnicodeLocaleKeyword("ca", caltype[0]);822return lb.build();823}824825return locale;826}827828private static int getCalendarIDFromLDMLType(String ldmlType) {829for (int i = 0; i < calIDToLDML.length; i++) {830if (calIDToLDML[i].startsWith(ldmlType)) {831return i;832}833}834return -1;835}836837private static Locale getNumberLocale(Locale src) {838if (JRELocaleConstants.TH_TH.equals(src)) {839if (isNativeDigit("th-TH")) {840Locale.Builder lb = new Locale.Builder().setLocale(src);841lb.setUnicodeLocaleKeyword("nu", "thai");842return lb.build();843}844}845846return src;847}848849// native methods850851// initialize852private static native boolean initialize();853private static native String getDefaultLocale(int cat);854855// For DateFormatProvider856private static native String getDateTimePattern(int dateStyle, int timeStyle, String langTag);857private static native int getCalendarID(String langTag);858859// For DateFormatSymbolsProvider860private static native String[] getAmPmStrings(String langTag, String[] ampm);861private static native String[] getEras(String langTag, String[] eras);862private static native String[] getMonths(String langTag, String[] months);863private static native String[] getShortMonths(String langTag, String[] smonths);864private static native String[] getWeekdays(String langTag, String[] wdays);865private static native String[] getShortWeekdays(String langTag, String[] swdays);866867// For NumberFormatProvider868private static native String getNumberPattern(int numberStyle, String langTag);869private static native boolean isNativeDigit(String langTag);870871// For DecimalFormatSymbolsProvider872private static native String getCurrencySymbol(String langTag, String currencySymbol);873private static native char getDecimalSeparator(String langTag, char decimalSeparator);874private static native char getGroupingSeparator(String langTag, char groupingSeparator);875private static native String getInfinity(String langTag, String infinity);876private static native String getInternationalCurrencySymbol(String langTag, String internationalCurrencySymbol);877private static native char getMinusSign(String langTag, char minusSign);878private static native char getMonetaryDecimalSeparator(String langTag, char monetaryDecimalSeparator);879private static native String getNaN(String langTag, String nan);880private static native char getPercent(String langTag, char percent);881private static native char getPerMill(String langTag, char perMill);882private static native char getZeroDigit(String langTag, char zeroDigit);883884// For CalendarDataProvider885private static native int getCalendarDataValue(String langTag, int type);886887// For CalendarNameProvider888private static native String[] getCalendarDisplayStrings(String langTag, int calid, int field, int style);889890// For Locale/CurrencyNameProvider891private static native String getDisplayString(String langTag, int key, String value);892}893894895