|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.util.locale.provider; |
|
|
|
import java.util.ArrayList; |
|
import java.util.Arrays; |
|
import java.util.Collections; |
|
import java.util.HashSet; |
|
import java.util.IllformedLocaleException; |
|
import java.util.List; |
|
import java.util.Locale; |
|
import java.util.Locale.Builder; |
|
import java.util.ResourceBundle.Control; |
|
import java.util.Set; |
|
import java.util.concurrent.ConcurrentHashMap; |
|
import java.util.concurrent.ConcurrentMap; |
|
import java.util.spi.LocaleServiceProvider; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final class LocaleServiceProviderPool { |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProviderPool> poolOfPools = |
|
new ConcurrentHashMap<>(); |
|
|
|
|
|
|
|
*/ |
|
private final ConcurrentMap<Locale, List<LocaleServiceProvider>> providersCache = |
|
new ConcurrentHashMap<>(); |
|
|
|
|
|
|
|
|
|
*/ |
|
private Set<Locale> availableLocales = null; |
|
|
|
|
|
|
|
*/ |
|
private final Class<? extends LocaleServiceProvider> providerClass; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("unchecked") |
|
static final Class<LocaleServiceProvider>[] spiClasses = |
|
(Class<LocaleServiceProvider>[]) new Class<?>[] { |
|
java.text.spi.BreakIteratorProvider.class, |
|
java.text.spi.CollatorProvider.class, |
|
java.text.spi.DateFormatProvider.class, |
|
java.text.spi.DateFormatSymbolsProvider.class, |
|
java.text.spi.DecimalFormatSymbolsProvider.class, |
|
java.text.spi.NumberFormatProvider.class, |
|
java.util.spi.CurrencyNameProvider.class, |
|
java.util.spi.LocaleNameProvider.class, |
|
java.util.spi.TimeZoneNameProvider.class, |
|
java.util.spi.CalendarDataProvider.class |
|
}; |
|
|
|
|
|
|
|
*/ |
|
public static LocaleServiceProviderPool getPool(Class<? extends LocaleServiceProvider> providerClass) { |
|
LocaleServiceProviderPool pool = poolOfPools.get(providerClass); |
|
if (pool == null) { |
|
LocaleServiceProviderPool newPool = |
|
new LocaleServiceProviderPool(providerClass); |
|
pool = poolOfPools.putIfAbsent(providerClass, newPool); |
|
if (pool == null) { |
|
pool = newPool; |
|
} |
|
} |
|
|
|
return pool; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private LocaleServiceProviderPool (final Class<? extends LocaleServiceProvider> c) { |
|
providerClass = c; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static class AllAvailableLocales { |
|
|
|
|
|
|
|
*/ |
|
static final Locale[] allAvailableLocales; |
|
|
|
static { |
|
Set<Locale> all = new HashSet<>(); |
|
for (Class<? extends LocaleServiceProvider> c : spiClasses) { |
|
LocaleServiceProviderPool pool = |
|
LocaleServiceProviderPool.getPool(c); |
|
all.addAll(pool.getAvailableLocaleSet()); |
|
} |
|
|
|
allAvailableLocales = all.toArray(new Locale[0]); |
|
} |
|
|
|
|
|
private AllAvailableLocales() { |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Locale[] getAllAvailableLocales() { |
|
return AllAvailableLocales.allAvailableLocales.clone(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Locale[] getAvailableLocales() { |
|
Set<Locale> locList = new HashSet<>(); |
|
locList.addAll(getAvailableLocaleSet()); |
|
|
|
locList.addAll(Arrays.asList(LocaleProviderAdapter.forJRE().getAvailableLocales())); |
|
Locale[] tmp = new Locale[locList.size()]; |
|
locList.toArray(tmp); |
|
return tmp; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private synchronized Set<Locale> getAvailableLocaleSet() { |
|
if (availableLocales == null) { |
|
availableLocales = new HashSet<>(); |
|
for (LocaleProviderAdapter.Type type : LocaleProviderAdapter.getAdapterPreference()) { |
|
LocaleProviderAdapter lda = LocaleProviderAdapter.forType(type); |
|
if (lda != null) { |
|
LocaleServiceProvider lsp = lda.getLocaleServiceProvider(providerClass); |
|
if (lsp != null) { |
|
Locale[] locales = lsp.getAvailableLocales(); |
|
for (Locale locale: locales) { |
|
availableLocales.add(getLookupLocale(locale)); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
return availableLocales; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter, |
|
Locale locale, |
|
Object... params) { |
|
return getLocalizedObjectImpl(getter, locale, true, null, params); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter, |
|
Locale locale, |
|
String key, |
|
Object... params) { |
|
return getLocalizedObjectImpl(getter, locale, false, key, params); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter, |
|
Locale locale, |
|
Boolean isObjectProvider, |
|
String key, |
|
Object... params) { |
|
return getLocalizedObjectImpl(getter, locale, isObjectProvider, key, params); |
|
} |
|
|
|
@SuppressWarnings("unchecked") |
|
private <P extends LocaleServiceProvider, S> S getLocalizedObjectImpl(LocalizedObjectGetter<P, S> getter, |
|
Locale locale, |
|
boolean isObjectProvider, |
|
String key, |
|
Object... params) { |
|
if (locale == null) { |
|
throw new NullPointerException(); |
|
} |
|
|
|
List<Locale> lookupLocales = getLookupLocales(locale); |
|
|
|
for (Locale current : lookupLocales) { |
|
S providersObj; |
|
|
|
for (LocaleServiceProvider lsp: findProviders(current, isObjectProvider)) { |
|
providersObj = getter.getObject((P)lsp, locale, key, params); |
|
if (providersObj != null) { |
|
return providersObj; |
|
} else if (isObjectProvider) { |
|
System.getLogger(LocaleServiceProviderPool.class.getCanonicalName()) |
|
.log(System.Logger.Level.INFO, |
|
"A locale sensitive service object provider returned null, " + |
|
"which should not happen. Provider: " + lsp + " Locale: " + locale); |
|
} |
|
} |
|
} |
|
|
|
|
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private List<LocaleServiceProvider> findProviders(Locale locale, boolean isObjectProvider) { |
|
List<LocaleServiceProvider> providersList = providersCache.get(locale); |
|
if (providersList == null) { |
|
for (LocaleProviderAdapter.Type type : LocaleProviderAdapter.getAdapterPreference()) { |
|
LocaleProviderAdapter lda = LocaleProviderAdapter.forType(type); |
|
if (lda != null) { |
|
LocaleServiceProvider lsp = lda.getLocaleServiceProvider(providerClass); |
|
if (lsp != null) { |
|
if (lsp.isSupportedLocale(locale)) { |
|
if (providersList == null) { |
|
providersList = new ArrayList<>(2); |
|
} |
|
providersList.add(lsp); |
|
if (isObjectProvider) { |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
if (providersList == null) { |
|
providersList = NULL_LIST; |
|
} |
|
List<LocaleServiceProvider> val = providersCache.putIfAbsent(locale, providersList); |
|
if (val != null) { |
|
providersList = val; |
|
} |
|
} |
|
return providersList; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static List<Locale> getLookupLocales(Locale locale) { |
|
// Note: We currently use the default implementation of |
|
// ResourceBundle.Control.getCandidateLocales. The result |
|
// returned by getCandidateLocales are already normalized |
|
|
|
return Control.getNoFallbackControl(Control.FORMAT_DEFAULT) |
|
.getCandidateLocales("", locale); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static Locale getLookupLocale(Locale locale) { |
|
Locale lookupLocale = locale; |
|
if (locale.hasExtensions() |
|
&& !locale.equals(JRELocaleConstants.JA_JP_JP) |
|
&& !locale.equals(JRELocaleConstants.TH_TH_TH)) { |
|
|
|
Builder locbld = new Builder(); |
|
try { |
|
locbld.setLocale(locale); |
|
locbld.clearExtensions(); |
|
lookupLocale = locbld.build(); |
|
} catch (IllformedLocaleException e) { |
|
// A Locale with non-empty extensions |
|
// should have well-formed fields except |
|
// for ja_JP_JP and th_TH_TH. Therefore, |
|
|
|
System.getLogger(LocaleServiceProviderPool.class.getCanonicalName()) |
|
.log(System.Logger.Level.INFO, |
|
"A locale(" + locale + ") has non-empty extensions, but has illformed fields."); |
|
|
|
|
|
lookupLocale = new Locale(locale.getLanguage(), locale.getCountry(), locale.getVariant()); |
|
} |
|
} |
|
return lookupLocale; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final List<LocaleServiceProvider> NULL_LIST = |
|
Collections.emptyList(); |
|
|
|
|
|
|
|
|
|
*/ |
|
public interface LocalizedObjectGetter<P extends LocaleServiceProvider, S> { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
S getObject(P lsp, |
|
Locale locale, |
|
String key, |
|
Object... params); |
|
} |
|
} |