/*
 * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.util;

import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.ServiceConfigurationError;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import java.util.spi.LocaleServiceProvider;
import sun.util.resources.LocaleData;
import sun.util.resources.OpenListResourceBundle;

An instance of this class holds a set of the third party implementations of a particular locale sensitive service, such as LocaleNameProvider.
/** * An instance of this class holds a set of the third party implementations of a particular * locale sensitive service, such as {@link java.util.spi.LocaleNameProvider}. * */
public final class LocaleServiceProviderPool {
A Map that holds singleton instances of this class. Each instance holds a set of provider implementations of a particular locale sensitive service.
/** * A Map that holds singleton instances of this class. Each instance holds a * set of provider implementations of a particular locale sensitive service. */
private static Map<Class, LocaleServiceProviderPool> poolOfPools = new ConcurrentHashMap<Class, LocaleServiceProviderPool>();
A Set containing locale service providers that implement the specified provider SPI
/** * A Set containing locale service providers that implement the * specified provider SPI */
private Set<LocaleServiceProvider> providers = new LinkedHashSet<LocaleServiceProvider>();
A Map that retains Locale->provider mapping
/** * A Map that retains Locale->provider mapping */
private Map<Locale, LocaleServiceProvider> providersCache = new ConcurrentHashMap<Locale, LocaleServiceProvider>();
Available locales for this locale sensitive service. This also contains JRE's available locales
/** * Available locales for this locale sensitive service. This also contains * JRE's available locales */
private Set<Locale> availableLocales = null;
Available locales within this JRE. Currently this is declared as static. This could be non-static later, so that they could have different sets for each locale sensitive services.
/** * Available locales within this JRE. Currently this is declared as * static. This could be non-static later, so that they could have * different sets for each locale sensitive services. */
private static List<Locale> availableJRELocales = null;
Provider locales for this locale sensitive service.
/** * Provider locales for this locale sensitive service. */
private Set<Locale> providerLocales = null;
A factory method that returns a singleton instance
/** * A factory method that returns a singleton instance */
public static LocaleServiceProviderPool getPool(Class<? extends LocaleServiceProvider> providerClass) { LocaleServiceProviderPool pool = poolOfPools.get(providerClass); if (pool == null) { LocaleServiceProviderPool newPool = new LocaleServiceProviderPool(providerClass); pool = poolOfPools.put(providerClass, newPool); if (pool == null) { pool = newPool; } } return pool; }
The sole constructor.
Params:
  • c – class of the locale sensitive service
/** * The sole constructor. * * @param c class of the locale sensitive service */
private LocaleServiceProviderPool (final Class<? extends LocaleServiceProvider> c) { try { AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { public Object run() { for (LocaleServiceProvider provider : ServiceLoader.loadInstalled(c)) { providers.add(provider); } return null; } }); } catch (PrivilegedActionException e) { Logger.getLogger("sun.util.LocaleServiceProviderPool").config(e.toString()); } }
Lazy loaded set of available locales. Loading all locales is a very long operation.
/** * Lazy loaded set of available locales. * Loading all locales is a very long operation. */
private static class AllAvailableLocales {
Available locales for all locale sensitive services. This also contains JRE's available locales
/** * Available locales for all locale sensitive services. * This also contains JRE's available locales */
static final Locale[] allAvailableLocales; static { Class[] providerClasses = { 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 }; Set<Locale> all = new HashSet<Locale>(Arrays.asList( LocaleData.getAvailableLocales()) ); for (Class providerClass : providerClasses) { LocaleServiceProviderPool pool = LocaleServiceProviderPool.getPool(providerClass); all.addAll(pool.getProviderLocales()); } allAvailableLocales = all.toArray(new Locale[0]); } }
Returns an array of available locales for all the provider classes. This array is a merged array of all the locales that are provided by each provider, including the JRE.
Returns:an array of the available locales for all provider classes
/** * Returns an array of available locales for all the provider classes. * This array is a merged array of all the locales that are provided by each * provider, including the JRE. * * @return an array of the available locales for all provider classes */
public static Locale[] getAllAvailableLocales() { return AllAvailableLocales.allAvailableLocales.clone(); }
Returns an array of available locales. This array is a merged array of all the locales that are provided by each provider, including the JRE.
Returns:an array of the available locales
/** * Returns an array of available locales. This array is a * merged array of all the locales that are provided by each * provider, including the JRE. * * @return an array of the available locales */
public synchronized Locale[] getAvailableLocales() { if (availableLocales == null) { availableLocales = new HashSet<Locale>(getJRELocales()); if (hasProviders()) { availableLocales.addAll(getProviderLocales()); } } Locale[] tmp = new Locale[availableLocales.size()]; availableLocales.toArray(tmp); return tmp; }
Returns an array of available locales from providers. Note that this method does not return a defensive copy.
Returns:list of the provider locales
/** * Returns an array of available locales from providers. * Note that this method does not return a defensive copy. * * @return list of the provider locales */
private synchronized Set<Locale> getProviderLocales() { if (providerLocales == null) { providerLocales = new HashSet<Locale>(); if (hasProviders()) { for (LocaleServiceProvider lsp : providers) { Locale[] locales = lsp.getAvailableLocales(); for (Locale locale: locales) { providerLocales.add(locale); } } } } return providerLocales; }
Returns whether any provider for this locale sensitive service is available or not.
Returns:true if any provider is available
/** * Returns whether any provider for this locale sensitive * service is available or not. * * @return true if any provider is available */
public boolean hasProviders() { return !providers.isEmpty(); }
Returns an array of available locales supported by the JRE. Note that this method does not return a defensive copy.
Returns:list of the available JRE locales
/** * Returns an array of available locales supported by the JRE. * Note that this method does not return a defensive copy. * * @return list of the available JRE locales */
private synchronized List<Locale> getJRELocales() { if (availableJRELocales == null) { availableJRELocales = Arrays.asList(LocaleData.getAvailableLocales()); } return availableJRELocales; }
Returns whether the given locale is supported by the JRE.
Params:
  • locale – the locale to test.
Returns:true, if the locale is supported by the JRE. false otherwise.
/** * Returns whether the given locale is supported by the JRE. * * @param locale the locale to test. * @return true, if the locale is supported by the JRE. false * otherwise. */
private boolean isJRESupported(Locale locale) { List<Locale> locales = getJRELocales(); return locales.contains(locale); }
Returns the provider's localized object for the specified locale.
Params:
  • getter – an object on which getObject() method is called to obtain the provider's instance.
  • locale – the given locale that is used as the starting one
  • params – provider specific parameters
Returns:provider's instance, or null.
/** * Returns the provider's localized object for the specified * locale. * * @param getter an object on which getObject() method * is called to obtain the provider's instance. * @param locale the given locale that is used as the starting one * @param params provider specific parameters * @return provider's instance, or null. */
public <P, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter, Locale locale, Object... params) { return getLocalizedObjectImpl(getter, locale, true, null, null, null, params); }
Returns the provider's localized name for the specified locale.
Params:
  • getter – an object on which getObject() method is called to obtain the provider's instance.
  • locale – the given locale that is used as the starting one
  • bundle – JRE resource bundle that contains the localized names, or null for localized objects.
  • key – the key string if bundle is supplied, otherwise null.
  • params – provider specific parameters
Returns:provider's instance, or null.
/** * Returns the provider's localized name for the specified * locale. * * @param getter an object on which getObject() method * is called to obtain the provider's instance. * @param locale the given locale that is used as the starting one * @param bundle JRE resource bundle that contains * the localized names, or null for localized objects. * @param key the key string if bundle is supplied, otherwise null. * @param params provider specific parameters * @return provider's instance, or null. */
public <P, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter, Locale locale, OpenListResourceBundle bundle, String key, Object... params) { return getLocalizedObjectImpl(getter, locale, false, null, bundle, key, params); }
Returns the provider's localized name for the specified locale.
Params:
  • getter – an object on which getObject() method is called to obtain the provider's instance.
  • locale – the given locale that is used as the starting one
  • bundleKey – JRE specific bundle key. e.g., "USD" is for currency symbol and "usd" is for currency display name in the JRE bundle.
  • bundle – JRE resource bundle that contains the localized names, or null for localized objects.
  • key – the key string if bundle is supplied, otherwise null.
  • params – provider specific parameters
Returns:provider's instance, or null.
/** * Returns the provider's localized name for the specified * locale. * * @param getter an object on which getObject() method * is called to obtain the provider's instance. * @param locale the given locale that is used as the starting one * @param bundleKey JRE specific bundle key. e.g., "USD" is for currency symbol and "usd" is for currency display name in the JRE bundle. * @param bundle JRE resource bundle that contains * the localized names, or null for localized objects. * @param key the key string if bundle is supplied, otherwise null. * @param params provider specific parameters * @return provider's instance, or null. */
public <P, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter, Locale locale, String bundleKey, OpenListResourceBundle bundle, String key, Object... params) { return getLocalizedObjectImpl(getter, locale, false, bundleKey, bundle, key, params); } private <P, S> S getLocalizedObjectImpl(LocalizedObjectGetter<P, S> getter, Locale locale, boolean isObjectProvider, String bundleKey, OpenListResourceBundle bundle, String key, Object... params) { if (hasProviders()) { if (bundleKey == null) { bundleKey = key; } Locale bundleLocale = (bundle != null ? bundle.getLocale() : null); Locale requested = locale; P lsp; S providersObj = null; // check whether a provider has an implementation that's closer // to the requested locale than the bundle we've found (for // localized names), or Java runtime's supported locale // (for localized objects) while ((locale = findProviderLocale(locale, bundleLocale)) != null) { lsp = (P)findProvider(locale); if (lsp != null) { providersObj = getter.getObject(lsp, requested, key, params); if (providersObj != null) { return providersObj; } else if (isObjectProvider) { Logger.getLogger("sun.util.LocaleServiceProviderPool").config( "A locale sensitive service provider returned null for a localized objects, which should not happen. provider: " + lsp + " locale: " + requested); } } locale = getParentLocale(locale); } // look up the JRE bundle and its parent chain. Only // providers for localized names are checked hereafter. while (bundle != null) { bundleLocale = bundle.getLocale(); if (bundle.handleGetKeys().contains(bundleKey)) { // JRE has it. return null; } else { lsp = (P)findProvider(bundleLocale); if (lsp != null) { providersObj = getter.getObject(lsp, requested, key, params); if (providersObj != null) { return providersObj; } } } // try parent bundle bundle = bundle.getParent(); } } // not found. return null; }
Returns a locale service provider instance that supports the specified locale.
Params:
  • locale – the given locale
Returns:the provider, or null if there is no provider available.
/** * Returns a locale service provider instance that supports * the specified locale. * * @param locale the given locale * @return the provider, or null if there is * no provider available. */
private LocaleServiceProvider findProvider(Locale locale) { if (!hasProviders()) { return null; } if (providersCache.containsKey(locale)) { LocaleServiceProvider provider = providersCache.get(locale); if (provider != NullProvider.INSTANCE) { return provider; } } else { for (LocaleServiceProvider lsp : providers) { Locale[] locales = lsp.getAvailableLocales(); for (Locale available: locales) { if (locale.equals(available)) { LocaleServiceProvider providerInCache = providersCache.put(locale, lsp); return (providerInCache != null ? providerInCache : lsp); } } } providersCache.put(locale, NullProvider.INSTANCE); } return null; }
Returns the provider's locale that is the most appropriate within the range
Params:
  • start – the given locale that is used as the starting one
  • end – the given locale that is used as the end one (exclusive), or null if it reaching any of the JRE supported locale should terminate the look up.
Returns:the most specific locale within the range, or null if no provider locale found in that range.
/** * Returns the provider's locale that is the most appropriate * within the range * * @param start the given locale that is used as the starting one * @param end the given locale that is used as the end one (exclusive), * or null if it reaching any of the JRE supported locale should * terminate the look up. * @return the most specific locale within the range, or null * if no provider locale found in that range. */
private Locale findProviderLocale(Locale start, Locale end) { Set<Locale> provLoc = getProviderLocales(); Locale current = start; while (current != null) { if (end != null) { if (current.equals(end)) { current = null; break; } } else { if (isJRESupported(current)) { current = null; break; } } if (provLoc.contains(current)) { break; } current = getParentLocale(current); } return current; }
Returns the parent locale.
Params:
  • locale – the locale
Returns:the parent locale
/** * Returns the parent locale. * * @param locale the locale * @return the parent locale */
private static Locale getParentLocale(Locale locale) { String variant = locale.getVariant(); if (variant != "") { int underscoreIndex = variant.lastIndexOf('_'); if (underscoreIndex != (-1)) { return new Locale(locale.getLanguage(), locale.getCountry(), variant.substring(0, underscoreIndex)); } else { return new Locale(locale.getLanguage(), locale.getCountry()); } } else if (locale.getCountry() != "") { return new Locale(locale.getLanguage()); } else if (locale.getLanguage() != "") { return Locale.ROOT; } else { return null; } }
A dummy locale service provider that indicates there is no provider available
/** * A dummy locale service provider that indicates there is no * provider available */
private static class NullProvider extends LocaleServiceProvider { private static final NullProvider INSTANCE = new NullProvider(); public Locale[] getAvailableLocales() { throw new RuntimeException("Should not get called."); } }
An interface to get a localized object for each locale sensitve service class.
/** * An interface to get a localized object for each locale sensitve * service class. */
public interface LocalizedObjectGetter<P, S> {
Returns an object from the provider
Params:
  • lsp – the provider
  • locale – the locale
  • key – key string to localize, or null if the provider is not a name provider
  • params – provider specific params
Returns:localized object from the provider
/** * Returns an object from the provider * * @param lsp the provider * @param locale the locale * @param key key string to localize, or null if the provider is not * a name provider * @param params provider specific params * @return localized object from the provider */
public S getObject(P lsp, Locale locale, String key, Object... params); } }