Path: blob/master/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.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*/2425package sun.util.locale.provider;2627import java.lang.reflect.InvocationTargetException;28import java.text.spi.BreakIteratorProvider;29import java.text.spi.CollatorProvider;30import java.text.spi.DateFormatProvider;31import java.text.spi.DateFormatSymbolsProvider;32import java.text.spi.DecimalFormatSymbolsProvider;33import java.text.spi.NumberFormatProvider;34import java.util.ArrayList;35import java.util.Collections;36import java.util.List;37import java.util.Locale;38import java.util.Map;39import java.util.ResourceBundle;40import java.util.ServiceConfigurationError;41import java.util.Set;42import java.util.concurrent.ConcurrentHashMap;43import java.util.concurrent.ConcurrentMap;44import java.util.spi.CalendarDataProvider;45import java.util.spi.CalendarNameProvider;46import java.util.spi.CurrencyNameProvider;47import java.util.spi.LocaleNameProvider;48import java.util.spi.LocaleServiceProvider;49import java.util.spi.TimeZoneNameProvider;50import sun.security.action.GetPropertyAction;51import sun.text.spi.JavaTimeDateTimePatternProvider;52import sun.util.spi.CalendarProvider;5354import static java.lang.System.*;5556/**57* The LocaleProviderAdapter abstract class.58*59* @author Naoto Sato60* @author Masayoshi Okutsu61*/62public abstract class LocaleProviderAdapter {63/**64* Adapter type.65*/66public enum Type {67JRE("sun.util.locale.provider.JRELocaleProviderAdapter", "sun.util.resources", "sun.text.resources"),68CLDR("sun.util.cldr.CLDRLocaleProviderAdapter", "sun.util.resources.cldr", "sun.text.resources.cldr"),69SPI("sun.util.locale.provider.SPILocaleProviderAdapter"),70HOST("sun.util.locale.provider.HostLocaleProviderAdapter"),71FALLBACK("sun.util.locale.provider.FallbackLocaleProviderAdapter", "sun.util.resources", "sun.text.resources");7273private final String CLASSNAME;74private final String UTIL_RESOURCES_PACKAGE;75private final String TEXT_RESOURCES_PACKAGE;7677Type(String className) {78this(className, null, null);79}8081Type(String className, String util, String text) {82CLASSNAME = className;83UTIL_RESOURCES_PACKAGE = util;84TEXT_RESOURCES_PACKAGE = text;85}8687public String getAdapterClassName() {88return CLASSNAME;89}9091public String getUtilResourcesPackage() {92return UTIL_RESOURCES_PACKAGE;93}9495public String getTextResourcesPackage() {96return TEXT_RESOURCES_PACKAGE;97}98}99100/**101* LocaleProviderAdapter preference list.102*/103private static final List<Type> adapterPreference;104105/**106* LocaleProviderAdapter instances107*/108private static final Map<Type, LocaleProviderAdapter> adapterInstances = new ConcurrentHashMap<>();109110/**111* Default fallback adapter type, which should return something meaningful in any case.112* This is either CLDR or FALLBACK.113*/114static volatile LocaleProviderAdapter.Type defaultLocaleProviderAdapter;115116/**117* Adapter lookup cache.118*/119private static final ConcurrentMap<Class<? extends LocaleServiceProvider>, ConcurrentMap<Locale, LocaleProviderAdapter>>120adapterCache = new ConcurrentHashMap<>();121122static {123String order = GetPropertyAction.privilegedGetProperty("java.locale.providers");124ArrayList<Type> typeList = new ArrayList<>();125String invalidTypeMessage = null;126127// Check user specified adapter preference128if (order != null && !order.isEmpty()) {129String[] types = order.split(",");130for (String type : types) {131type = type.trim().toUpperCase(Locale.ROOT);132if (type.equals("COMPAT")) {133type = "JRE";134}135try {136Type aType = Type.valueOf(type.trim().toUpperCase(Locale.ROOT));137if (!typeList.contains(aType)) {138typeList.add(aType);139}140} catch (IllegalArgumentException e) {141// construct a log message.142invalidTypeMessage = "Invalid locale provider adapter \"" + type + "\" ignored.";143}144}145}146147defaultLocaleProviderAdapter = Type.CLDR;148if (!typeList.isEmpty()) {149// bona fide preference exists150if (!(typeList.contains(Type.CLDR) || typeList.contains(Type.JRE))) {151// Append FALLBACK as the last resort when no ResourceBundleBasedAdapter is available.152typeList.add(Type.FALLBACK);153defaultLocaleProviderAdapter = Type.FALLBACK;154}155} else {156// Default preference list.157typeList.add(Type.CLDR);158typeList.add(Type.JRE);159}160adapterPreference = Collections.unmodifiableList(typeList);161162// Emit logs, if any, after 'adapterPreference' is initialized which is needed163// for logging.164if (invalidTypeMessage != null) {165// could be caused by the user specifying wrong166// provider name or format in the system property167getLogger(LocaleProviderAdapter.class.getCanonicalName())168.log(Logger.Level.INFO, invalidTypeMessage);169}170}171172/**173* Returns the singleton instance for each adapter type174*/175public static LocaleProviderAdapter forType(Type type) {176switch (type) {177case JRE:178case CLDR:179case SPI:180case HOST:181case FALLBACK:182LocaleProviderAdapter adapter = adapterInstances.get(type);183if (adapter == null) {184try {185// lazily load adapters here186adapter = (LocaleProviderAdapter)Class.forName(type.getAdapterClassName())187.getDeclaredConstructor().newInstance();188LocaleProviderAdapter cached = adapterInstances.putIfAbsent(type, adapter);189if (cached != null) {190adapter = cached;191}192} catch (NoSuchMethodException |193InvocationTargetException |194ClassNotFoundException |195IllegalAccessException |196InstantiationException |197UnsupportedOperationException e) {198throw new ServiceConfigurationError("Locale provider adapter \"" +199type + "\"cannot be instantiated.", e);200}201}202return adapter;203default:204throw new InternalError("unknown locale data adapter type");205}206}207208public static LocaleProviderAdapter forJRE() {209return forType(Type.JRE);210}211212public static LocaleProviderAdapter getResourceBundleBased() {213for (Type type : getAdapterPreference()) {214if (type == Type.JRE || type == Type.CLDR || type == Type.FALLBACK) {215LocaleProviderAdapter adapter = forType(type);216if (adapter != null) {217return adapter;218}219}220}221// Shouldn't happen.222throw new InternalError();223}224225/**226* Returns the preference order of LocaleProviderAdapter.Type227*/228public static List<Type> getAdapterPreference() {229return adapterPreference;230}231232/**233* Returns a LocaleProviderAdapter for the given locale service provider that234* best matches the given locale. This method returns the LocaleProviderAdapter235* for JRE if none is found for the given locale.236*237* @param providerClass the class for the locale service provider238* @param locale the desired locale.239* @return a LocaleProviderAdapter240*/241public static LocaleProviderAdapter getAdapter(Class<? extends LocaleServiceProvider> providerClass,242Locale locale) {243LocaleProviderAdapter adapter;244245// cache lookup246ConcurrentMap<Locale, LocaleProviderAdapter> adapterMap = adapterCache.get(providerClass);247if (adapterMap != null) {248if ((adapter = adapterMap.get(locale)) != null) {249return adapter;250}251} else {252adapterMap = new ConcurrentHashMap<>();253adapterCache.putIfAbsent(providerClass, adapterMap);254}255256// Fast look-up for the given locale257adapter = findAdapter(providerClass, locale);258if (adapter != null) {259adapterMap.putIfAbsent(locale, adapter);260return adapter;261}262263// Try finding an adapter in the normal candidate locales path of the given locale.264List<Locale> lookupLocales = ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT)265.getCandidateLocales("", locale);266for (Locale loc : lookupLocales) {267if (loc.equals(locale)) {268// We've already done with this loc.269continue;270}271adapter = findAdapter(providerClass, loc);272if (adapter != null) {273adapterMap.putIfAbsent(locale, adapter);274return adapter;275}276}277278// returns the adapter for FALLBACK as the last resort279adapterMap.putIfAbsent(locale, forType(Type.FALLBACK));280return forType(Type.FALLBACK);281}282283private static LocaleProviderAdapter findAdapter(Class<? extends LocaleServiceProvider> providerClass,284Locale locale) {285for (Type type : getAdapterPreference()) {286LocaleProviderAdapter adapter = forType(type);287if (adapter != null) {288LocaleServiceProvider provider = adapter.getLocaleServiceProvider(providerClass);289if (provider != null) {290if (provider.isSupportedLocale(locale)) {291return adapter;292}293}294}295}296return null;297}298299/**300* A utility method for implementing the default LocaleServiceProvider.isSupportedLocale301* for the JRE, CLDR, and FALLBACK adapters.302*/303public boolean isSupportedProviderLocale(Locale locale, Set<String> langtags) {304LocaleProviderAdapter.Type type = getAdapterType();305assert type == Type.JRE || type == Type.CLDR || type == Type.FALLBACK;306return false;307}308309public static Locale[] toLocaleArray(Set<String> tags) {310Locale[] locs = new Locale[tags.size() + 1];311int index = 0;312locs[index++] = Locale.ROOT;313for (String tag : tags) {314switch (tag) {315case "ja-JP-JP":316locs[index++] = JRELocaleConstants.JA_JP_JP;317break;318case "th-TH-TH":319locs[index++] = JRELocaleConstants.TH_TH_TH;320break;321default:322locs[index++] = Locale.forLanguageTag(tag);323break;324}325}326return locs;327}328329/**330* Returns the type of this LocaleProviderAdapter331*/332public abstract LocaleProviderAdapter.Type getAdapterType();333334/**335* Getter method for Locale Service Providers.336*/337public abstract <P extends LocaleServiceProvider> P getLocaleServiceProvider(Class<P> c);338339/**340* Returns a BreakIteratorProvider for this LocaleProviderAdapter, or null if no341* BreakIteratorProvider is available.342*343* @return a BreakIteratorProvider344*/345public abstract BreakIteratorProvider getBreakIteratorProvider();346347/**348* Returns a ollatorProvider for this LocaleProviderAdapter, or null if no349* ollatorProvider is available.350*351* @return a ollatorProvider352*/353public abstract CollatorProvider getCollatorProvider();354355/**356* Returns a DateFormatProvider for this LocaleProviderAdapter, or null if no357* DateFormatProvider is available.358*359* @return a DateFormatProvider360*/361public abstract DateFormatProvider getDateFormatProvider();362363/**364* Returns a DateFormatSymbolsProvider for this LocaleProviderAdapter, or null if no365* DateFormatSymbolsProvider is available.366*367* @return a DateFormatSymbolsProvider368*/369public abstract DateFormatSymbolsProvider getDateFormatSymbolsProvider();370371/**372* Returns a DecimalFormatSymbolsProvider for this LocaleProviderAdapter, or null if no373* DecimalFormatSymbolsProvider is available.374*375* @return a DecimalFormatSymbolsProvider376*/377public abstract DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider();378379/**380* Returns a NumberFormatProvider for this LocaleProviderAdapter, or null if no381* NumberFormatProvider is available.382*383* @return a NumberFormatProvider384*/385public abstract NumberFormatProvider getNumberFormatProvider();386387/*388* Getter methods for java.util.spi.* providers389*/390391/**392* Returns a CurrencyNameProvider for this LocaleProviderAdapter, or null if no393* CurrencyNameProvider is available.394*395* @return a CurrencyNameProvider396*/397public abstract CurrencyNameProvider getCurrencyNameProvider();398399/**400* Returns a LocaleNameProvider for this LocaleProviderAdapter, or null if no401* LocaleNameProvider is available.402*403* @return a LocaleNameProvider404*/405public abstract LocaleNameProvider getLocaleNameProvider();406407/**408* Returns a TimeZoneNameProvider for this LocaleProviderAdapter, or null if no409* TimeZoneNameProvider is available.410*411* @return a TimeZoneNameProvider412*/413public abstract TimeZoneNameProvider getTimeZoneNameProvider();414415/**416* Returns a CalendarDataProvider for this LocaleProviderAdapter, or null if no417* CalendarDataProvider is available.418*419* @return a CalendarDataProvider420*/421public abstract CalendarDataProvider getCalendarDataProvider();422423/**424* Returns a CalendarNameProvider for this LocaleProviderAdapter, or null if no425* CalendarNameProvider is available.426*427* @return a CalendarNameProvider428*/429public abstract CalendarNameProvider getCalendarNameProvider();430431/**432* Returns a CalendarProvider for this LocaleProviderAdapter, or null if no433* CalendarProvider is available.434*435* @return a CalendarProvider436*/437public abstract CalendarProvider getCalendarProvider();438439/**440* Returns a JavaTimeDateTimePatternProvider for this LocaleProviderAdapter,441* or null if no JavaTimeDateTimePatternProvider is available.442*443* @return a JavaTimeDateTimePatternProvider444*/445public abstract JavaTimeDateTimePatternProvider getJavaTimeDateTimePatternProvider();446447public abstract LocaleResources getLocaleResources(Locale locale);448449public abstract Locale[] getAvailableLocales();450}451452453