Path: blob/master/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java
41161 views
/*1* Copyright (c) 2005, 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.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;4041/**42* An instance of this class holds a set of the third party implementations of a particular43* locale sensitive service, such as {@link java.util.spi.LocaleNameProvider}.44*45* @author Naoto Sato46* @author Masayoshi Okutsu47*/48public final class LocaleServiceProviderPool {4950/**51* A Map that holds singleton instances of this class. Each instance holds a52* set of provider implementations of a particular locale sensitive service.53*/54private static final ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProviderPool> poolOfPools =55new ConcurrentHashMap<>();5657/**58* A Map that retains Locale->provider mapping59*/60private final ConcurrentMap<Locale, List<LocaleServiceProvider>> providersCache =61new ConcurrentHashMap<>();6263/**64* Available locales for this locale sensitive service. This also contains65* JRE's available locales66*/67private Set<Locale> availableLocales = null;6869/**70* Provider class71*/72private final Class<? extends LocaleServiceProvider> providerClass;7374/**75* Array of all Locale Sensitive SPI classes.76*77* We know "spiClasses" contains classes that extends LocaleServiceProvider,78* but generic array creation is not allowed, thus the "unchecked" warning79* is suppressed here.80*/81@SuppressWarnings("unchecked")82static final Class<LocaleServiceProvider>[] spiClasses =83(Class<LocaleServiceProvider>[]) new Class<?>[] {84java.text.spi.BreakIteratorProvider.class,85java.text.spi.CollatorProvider.class,86java.text.spi.DateFormatProvider.class,87java.text.spi.DateFormatSymbolsProvider.class,88java.text.spi.DecimalFormatSymbolsProvider.class,89java.text.spi.NumberFormatProvider.class,90java.util.spi.CurrencyNameProvider.class,91java.util.spi.LocaleNameProvider.class,92java.util.spi.TimeZoneNameProvider.class,93java.util.spi.CalendarDataProvider.class94};9596/**97* A factory method that returns a singleton instance98*/99public static LocaleServiceProviderPool getPool(Class<? extends LocaleServiceProvider> providerClass) {100LocaleServiceProviderPool pool = poolOfPools.get(providerClass);101if (pool == null) {102LocaleServiceProviderPool newPool =103new LocaleServiceProviderPool(providerClass);104pool = poolOfPools.putIfAbsent(providerClass, newPool);105if (pool == null) {106pool = newPool;107}108}109110return pool;111}112113/**114* The sole constructor.115*116* @param c class of the locale sensitive service117*/118private LocaleServiceProviderPool (final Class<? extends LocaleServiceProvider> c) {119providerClass = c;120}121122/**123* Lazy loaded set of available locales.124* Loading all locales is a very long operation.125*/126private static class AllAvailableLocales {127/**128* Available locales for all locale sensitive services.129* This also contains JRE's available locales130*/131static final Locale[] allAvailableLocales;132133static {134Set<Locale> all = new HashSet<>();135for (Class<? extends LocaleServiceProvider> c : spiClasses) {136LocaleServiceProviderPool pool =137LocaleServiceProviderPool.getPool(c);138all.addAll(pool.getAvailableLocaleSet());139}140141allAvailableLocales = all.toArray(new Locale[0]);142}143144// No instantiation145private AllAvailableLocales() {146}147}148149/**150* Returns an array of available locales for all the provider classes.151* This array is a merged array of all the locales that are provided by each152* provider, including the JRE.153*154* @return an array of the available locales for all provider classes155*/156public static Locale[] getAllAvailableLocales() {157return AllAvailableLocales.allAvailableLocales.clone();158}159160/**161* Returns an array of available locales. This array is a162* merged array of all the locales that are provided by each163* provider, including the JRE.164*165* @return an array of the available locales166*/167public Locale[] getAvailableLocales() {168Set<Locale> locList = new HashSet<>();169locList.addAll(getAvailableLocaleSet());170// Make sure it all contains JRE's locales for compatibility.171locList.addAll(Arrays.asList(LocaleProviderAdapter.forJRE().getAvailableLocales()));172Locale[] tmp = new Locale[locList.size()];173locList.toArray(tmp);174return tmp;175}176177/**178* Returns the union of locale sets that are available from179* each service provider. This method does NOT return the180* defensive copy.181*182* @return a set of available locales183*/184private synchronized Set<Locale> getAvailableLocaleSet() {185if (availableLocales == null) {186availableLocales = new HashSet<>();187for (LocaleProviderAdapter.Type type : LocaleProviderAdapter.getAdapterPreference()) {188LocaleProviderAdapter lda = LocaleProviderAdapter.forType(type);189if (lda != null) {190LocaleServiceProvider lsp = lda.getLocaleServiceProvider(providerClass);191if (lsp != null) {192Locale[] locales = lsp.getAvailableLocales();193for (Locale locale: locales) {194availableLocales.add(getLookupLocale(locale));195}196}197}198}199}200201return availableLocales;202}203204/**205* Returns the provider's localized object for the specified206* locale.207*208* @param getter an object on which getObject() method209* is called to obtain the provider's instance.210* @param locale the given locale that is used as the starting one211* @param params provider specific parameters212* @return provider's instance, or null.213*/214public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,215Locale locale,216Object... params) {217return getLocalizedObjectImpl(getter, locale, true, null, params);218}219220/**221* Returns the provider's localized name for the specified222* locale.223*224* @param getter an object on which getObject() method225* is called to obtain the provider's instance.226* @param locale the given locale that is used as the starting one227* @param key the key string for name providers228* @param params provider specific parameters229* @return provider's instance, or null.230*/231public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,232Locale locale,233String key,234Object... params) {235return getLocalizedObjectImpl(getter, locale, false, key, params);236}237238/**239* Returns the provider's localized name for the specified240* locale.241*242* @param getter an object on which getObject() method243* is called to obtain the provider's instance.244* @param locale the given locale that is used as the starting one245* @param isObjectProvider flag designating object provder or not246* @param key the key string for name providers247* @param params provider specific parameters248* @return provider's instance, or null.249*/250public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,251Locale locale,252Boolean isObjectProvider,253String key,254Object... params) {255return getLocalizedObjectImpl(getter, locale, isObjectProvider, key, params);256}257258@SuppressWarnings("unchecked")259private <P extends LocaleServiceProvider, S> S getLocalizedObjectImpl(LocalizedObjectGetter<P, S> getter,260Locale locale,261boolean isObjectProvider,262String key,263Object... params) {264if (locale == null) {265throw new NullPointerException();266}267268List<Locale> lookupLocales = getLookupLocales(locale);269270for (Locale current : lookupLocales) {271S providersObj;272273for (LocaleServiceProvider lsp: findProviders(current, isObjectProvider)) {274providersObj = getter.getObject((P)lsp, locale, key, params);275if (providersObj != null) {276return providersObj;277} else if (isObjectProvider) {278System.getLogger(LocaleServiceProviderPool.class.getCanonicalName())279.log(System.Logger.Level.INFO,280"A locale sensitive service object provider returned null, " +281"which should not happen. Provider: " + lsp + " Locale: " + locale);282}283}284}285286// not found.287return null;288}289290/**291* Returns the list of locale service provider instances that support292* the specified locale.293*294* @param locale the given locale295* @return the list of locale data adapter types296*/297private List<LocaleServiceProvider> findProviders(Locale locale, boolean isObjectProvider) {298List<LocaleServiceProvider> providersList = providersCache.get(locale);299if (providersList == null) {300for (LocaleProviderAdapter.Type type : LocaleProviderAdapter.getAdapterPreference()) {301LocaleProviderAdapter lda = LocaleProviderAdapter.forType(type);302if (lda != null) {303LocaleServiceProvider lsp = lda.getLocaleServiceProvider(providerClass);304if (lsp != null) {305if (lsp.isSupportedLocale(locale)) {306if (providersList == null) {307providersList = new ArrayList<>(2);308}309providersList.add(lsp);310if (isObjectProvider) {311break;312}313}314}315}316}317if (providersList == null) {318providersList = NULL_LIST;319}320List<LocaleServiceProvider> val = providersCache.putIfAbsent(locale, providersList);321if (val != null) {322providersList = val;323}324}325return providersList;326}327328/**329* Returns a list of candidate locales for service look up.330* @param locale the input locale331* @return the list of candidate locales for the given locale332*/333static List<Locale> getLookupLocales(Locale locale) {334// Note: We currently use the default implementation of335// ResourceBundle.Control.getCandidateLocales. The result336// returned by getCandidateLocales are already normalized337// (no extensions) for service look up.338return Control.getNoFallbackControl(Control.FORMAT_DEFAULT)339.getCandidateLocales("", locale);340}341342/**343* Returns an instance of Locale used for service look up.344* The result Locale has no extensions except for ja_JP_JP345* and th_TH_TH346*347* @param locale the locale348* @return the locale used for service look up349*/350static Locale getLookupLocale(Locale locale) {351Locale lookupLocale = locale;352if (locale.hasExtensions()353&& !locale.equals(JRELocaleConstants.JA_JP_JP)354&& !locale.equals(JRELocaleConstants.TH_TH_TH)) {355// remove extensions356Builder locbld = new Builder();357try {358locbld.setLocale(locale);359locbld.clearExtensions();360lookupLocale = locbld.build();361} catch (IllformedLocaleException e) {362// A Locale with non-empty extensions363// should have well-formed fields except364// for ja_JP_JP and th_TH_TH. Therefore,365// it should never enter in this catch clause.366System.getLogger(LocaleServiceProviderPool.class.getCanonicalName())367.log(System.Logger.Level.INFO,368"A locale(" + locale + ") has non-empty extensions, but has illformed fields.");369370// Fallback - script field will be lost.371lookupLocale = new Locale(locale.getLanguage(), locale.getCountry(), locale.getVariant());372}373}374return lookupLocale;375}376377/**378* A dummy locale service provider list that indicates there is no379* provider available380*/381private static final List<LocaleServiceProvider> NULL_LIST =382Collections.emptyList();383384/**385* An interface to get a localized object for each locale sensitive386* service class.387*/388public interface LocalizedObjectGetter<P extends LocaleServiceProvider, S> {389/**390* Returns an object from the provider391*392* @param lsp the provider393* @param locale the locale394* @param key key string to localize, or null if the provider is not395* a name provider396* @param params provider specific params397* @return localized object from the provider398*/399S getObject(P lsp,400Locale locale,401String key,402Object... params);403}404}405406407