Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java
38918 views
/*1* Copyright (c) 2005, 2013, 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.util.ArrayList;28import java.util.Arrays;29import java.util.Collections;30import java.util.HashSet;31import java.util.IllformedLocaleException;32import java.util.List;33import java.util.Locale;34import java.util.Locale.Builder;35import java.util.ResourceBundle.Control;36import java.util.Set;37import java.util.concurrent.ConcurrentHashMap;38import java.util.concurrent.ConcurrentMap;39import java.util.spi.LocaleServiceProvider;40import sun.util.logging.PlatformLogger;4142/**43* An instance of this class holds a set of the third party implementations of a particular44* locale sensitive service, such as {@link java.util.spi.LocaleNameProvider}.45*46* @author Naoto Sato47* @author Masayoshi Okutsu48*/49public final class LocaleServiceProviderPool {5051/**52* A Map that holds singleton instances of this class. Each instance holds a53* set of provider implementations of a particular locale sensitive service.54*/55private static ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProviderPool> poolOfPools =56new ConcurrentHashMap<>();5758/**59* A Map containing locale service providers that implement the60* specified provider SPI, keyed by a LocaleProviderAdapter.Type61*/62private ConcurrentMap<LocaleProviderAdapter.Type, LocaleServiceProvider> providers =63new ConcurrentHashMap<>();6465/**66* A Map that retains Locale->provider mapping67*/68private ConcurrentMap<Locale, List<LocaleProviderAdapter.Type>> providersCache =69new ConcurrentHashMap<>();7071/**72* Available locales for this locale sensitive service. This also contains73* JRE's available locales74*/75private Set<Locale> availableLocales = null;7677/**78* Provider class79*/80private Class<? extends LocaleServiceProvider> providerClass;8182/**83* Array of all Locale Sensitive SPI classes.84*85* We know "spiClasses" contains classes that extends LocaleServiceProvider,86* but generic array creation is not allowed, thus the "unchecked" warning87* is suppressed here.88*/89@SuppressWarnings("unchecked")90static final Class<LocaleServiceProvider>[] spiClasses =91(Class<LocaleServiceProvider>[]) new Class<?>[] {92java.text.spi.BreakIteratorProvider.class,93java.text.spi.CollatorProvider.class,94java.text.spi.DateFormatProvider.class,95java.text.spi.DateFormatSymbolsProvider.class,96java.text.spi.DecimalFormatSymbolsProvider.class,97java.text.spi.NumberFormatProvider.class,98java.util.spi.CurrencyNameProvider.class,99java.util.spi.LocaleNameProvider.class,100java.util.spi.TimeZoneNameProvider.class,101java.util.spi.CalendarDataProvider.class102};103104/**105* A factory method that returns a singleton instance106*/107public static LocaleServiceProviderPool getPool(Class<? extends LocaleServiceProvider> providerClass) {108LocaleServiceProviderPool pool = poolOfPools.get(providerClass);109if (pool == null) {110LocaleServiceProviderPool newPool =111new LocaleServiceProviderPool(providerClass);112pool = poolOfPools.putIfAbsent(providerClass, newPool);113if (pool == null) {114pool = newPool;115}116}117118return pool;119}120121/**122* The sole constructor.123*124* @param c class of the locale sensitive service125*/126private LocaleServiceProviderPool (final Class<? extends LocaleServiceProvider> c) {127providerClass = c;128129for (LocaleProviderAdapter.Type type : LocaleProviderAdapter.getAdapterPreference()) {130LocaleProviderAdapter lda = LocaleProviderAdapter.forType(type);131if (lda != null) {132LocaleServiceProvider provider = lda.getLocaleServiceProvider(c);133if (provider != null) {134providers.putIfAbsent(type, provider);135}136}137}138}139140static void config(Class<? extends Object> caller, String message) {141PlatformLogger logger = PlatformLogger.getLogger(caller.getCanonicalName());142logger.config(message);143}144145/**146* Lazy loaded set of available locales.147* Loading all locales is a very long operation.148*/149private static class AllAvailableLocales {150/**151* Available locales for all locale sensitive services.152* This also contains JRE's available locales153*/154static final Locale[] allAvailableLocales;155156static {157Set<Locale> all = new HashSet<>();158for (Class<? extends LocaleServiceProvider> c : spiClasses) {159LocaleServiceProviderPool pool =160LocaleServiceProviderPool.getPool(c);161all.addAll(pool.getAvailableLocaleSet());162}163164allAvailableLocales = all.toArray(new Locale[0]);165}166167// No instantiation168private AllAvailableLocales() {169}170}171172/**173* Returns an array of available locales for all the provider classes.174* This array is a merged array of all the locales that are provided by each175* provider, including the JRE.176*177* @return an array of the available locales for all provider classes178*/179public static Locale[] getAllAvailableLocales() {180return AllAvailableLocales.allAvailableLocales.clone();181}182183/**184* Returns an array of available locales. This array is a185* merged array of all the locales that are provided by each186* provider, including the JRE.187*188* @return an array of the available locales189*/190public Locale[] getAvailableLocales() {191Set<Locale> locList = new HashSet<>();192locList.addAll(getAvailableLocaleSet());193// Make sure it all contains JRE's locales for compatibility.194locList.addAll(Arrays.asList(LocaleProviderAdapter.forJRE().getAvailableLocales()));195Locale[] tmp = new Locale[locList.size()];196locList.toArray(tmp);197return tmp;198}199200/**201* Returns the union of locale sets that are available from202* each service provider. This method does NOT return the203* defensive copy.204*205* @return a set of available locales206*/207private synchronized Set<Locale> getAvailableLocaleSet() {208if (availableLocales == null) {209availableLocales = new HashSet<>();210for (LocaleServiceProvider lsp : providers.values()) {211Locale[] locales = lsp.getAvailableLocales();212for (Locale locale: locales) {213availableLocales.add(getLookupLocale(locale));214}215}216}217218return availableLocales;219}220221/**222* Returns whether any provider for this locale sensitive223* service is available or not, excluding JRE's one.224*225* @return true if any provider (other than JRE) is available226*/227boolean hasProviders() {228return providers.size() != 1 ||229(providers.get(LocaleProviderAdapter.Type.JRE) == null &&230providers.get(LocaleProviderAdapter.Type.FALLBACK) == null);231}232233/**234* Returns the provider's localized object for the specified235* locale.236*237* @param getter an object on which getObject() method238* is called to obtain the provider's instance.239* @param locale the given locale that is used as the starting one240* @param params provider specific parameters241* @return provider's instance, or null.242*/243public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,244Locale locale,245Object... params) {246return getLocalizedObjectImpl(getter, locale, true, null, params);247}248249/**250* Returns the provider's localized name for the specified251* locale.252*253* @param getter an object on which getObject() method254* is called to obtain the provider's instance.255* @param locale the given locale that is used as the starting one256* @param key the key string for name providers257* @param params provider specific parameters258* @return provider's instance, or null.259*/260public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,261Locale locale,262String key,263Object... params) {264return getLocalizedObjectImpl(getter, locale, false, key, params);265}266267@SuppressWarnings("unchecked")268private <P extends LocaleServiceProvider, S> S getLocalizedObjectImpl(LocalizedObjectGetter<P, S> getter,269Locale locale,270boolean isObjectProvider,271String key,272Object... params) {273if (locale == null) {274throw new NullPointerException();275}276277// Check whether JRE is the sole locale data provider or not,278// and directly call it if it is.279if (!hasProviders()) {280return getter.getObject((P)providers.get(LocaleProviderAdapter.defaultLocaleProviderAdapter),281locale, key, params);282}283284List<Locale> lookupLocales = getLookupLocales(locale);285286Set<Locale> available = getAvailableLocaleSet();287for (Locale current : lookupLocales) {288if (available.contains(current)) {289S providersObj;290291for (LocaleProviderAdapter.Type type: findProviders(current)) {292LocaleServiceProvider lsp = providers.get(type);293providersObj = getter.getObject((P)lsp, locale, key, params);294if (providersObj != null) {295return providersObj;296} else if (isObjectProvider) {297config(LocaleServiceProviderPool.class,298"A locale sensitive service provider returned null for a localized objects, which should not happen. provider: "299+ lsp + " locale: " + locale);300}301}302}303}304305// not found.306return null;307}308309/**310* Returns the list of locale service provider instances that support311* the specified locale.312*313* @param locale the given locale314* @return the list of locale data adapter types315*/316private List<LocaleProviderAdapter.Type> findProviders(Locale locale) {317List<LocaleProviderAdapter.Type> providersList = providersCache.get(locale);318if (providersList == null) {319for (LocaleProviderAdapter.Type type : LocaleProviderAdapter.getAdapterPreference()) {320LocaleServiceProvider lsp = providers.get(type);321if (lsp != null) {322if (lsp.isSupportedLocale(locale)) {323if (providersList == null) {324providersList = new ArrayList<>(2);325}326providersList.add(type);327328}329}330}331if (providersList == null) {332providersList = NULL_LIST;333}334List<LocaleProviderAdapter.Type> val = providersCache.putIfAbsent(locale, providersList);335if (val != null) {336providersList = val;337}338}339return providersList;340}341342/**343* Returns a list of candidate locales for service look up.344* @param locale the input locale345* @return the list of candidate locales for the given locale346*/347static List<Locale> getLookupLocales(Locale locale) {348// Note: We currently use the default implementation of349// ResourceBundle.Control.getCandidateLocales. The result350// returned by getCandidateLocales are already normalized351// (no extensions) for service look up.352List<Locale> lookupLocales = Control.getNoFallbackControl(Control.FORMAT_DEFAULT)353.getCandidateLocales("", locale);354return lookupLocales;355}356357/**358* Returns an instance of Locale used for service look up.359* The result Locale has no extensions except for ja_JP_JP360* and th_TH_TH361*362* @param locale the locale363* @return the locale used for service look up364*/365static Locale getLookupLocale(Locale locale) {366Locale lookupLocale = locale;367if (locale.hasExtensions()368&& !locale.equals(JRELocaleConstants.JA_JP_JP)369&& !locale.equals(JRELocaleConstants.TH_TH_TH)) {370// remove extensions371Builder locbld = new Builder();372try {373locbld.setLocale(locale);374locbld.clearExtensions();375lookupLocale = locbld.build();376} catch (IllformedLocaleException e) {377// A Locale with non-empty extensions378// should have well-formed fields except379// for ja_JP_JP and th_TH_TH. Therefore,380// it should never enter in this catch clause.381config(LocaleServiceProviderPool.class,382"A locale(" + locale + ") has non-empty extensions, but has illformed fields.");383384// Fallback - script field will be lost.385lookupLocale = new Locale(locale.getLanguage(), locale.getCountry(), locale.getVariant());386}387}388return lookupLocale;389}390391/**392* A dummy locale service provider list that indicates there is no393* provider available394*/395private static List<LocaleProviderAdapter.Type> NULL_LIST =396Collections.emptyList();397398/**399* An interface to get a localized object for each locale sensitive400* service class.401*/402public interface LocalizedObjectGetter<P extends LocaleServiceProvider, S> {403/**404* Returns an object from the provider405*406* @param lsp the provider407* @param locale the locale408* @param key key string to localize, or null if the provider is not409* a name provider410* @param params provider specific params411* @return localized object from the provider412*/413public S getObject(P lsp,414Locale locale,415String key,416Object... params);417}418}419420421