src/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java

Print this page
rev 5696 : 6336885: RFE: Locale Data Deployment Enhancements
4609153: Provide locale data for Indic locales
5104387: Support for gl_ES locale (galician language)
6337471: desktop/system locale preferences support
7056139: (cal) SPI support for locale-dependent Calendar parameters
7058206: Provide CalendarData SPI for week params and display field value names
7073852: Support multiple scripts for digits and decimal symbols per locale
7079560: [Fmt-Da] Context dependent month names support in SimpleDateFormat
7171324: getAvailableLocales() of locale sensitive services should return the actual availability of locales
7151414: (cal) Support calendar type identification
7168528: LocaleServiceProvider needs to be aware of Locale extensions
7171372: (cal) locale's default Calendar should be created if unknown calendar is specified
Summary: JEP 127: Improve Locale Data Packaging and Adopt Unicode CLDR Data (part 1 w/o packaging changes. by Naoto Sato and Masayoshi Okutsu)
   1 /*
   2  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.util;
  27 
  28 import java.security.AccessController;
  29 import java.security.PrivilegedActionException;
  30 import java.security.PrivilegedExceptionAction;
  31 import java.util.ArrayList;

  32 import java.util.HashSet;
  33 import java.util.IllformedLocaleException;
  34 import java.util.LinkedHashSet;
  35 import java.util.List;
  36 import java.util.Locale;
  37 import java.util.Locale.Builder;
  38 import java.util.Map;
  39 import java.util.ResourceBundle.Control;
  40 import java.util.ServiceLoader;
  41 import java.util.Set;
  42 import java.util.concurrent.ConcurrentHashMap;
  43 import java.util.concurrent.ConcurrentMap;
  44 import java.util.spi.LocaleServiceProvider;
  45 
  46 import sun.util.logging.PlatformLogger;
  47 import sun.util.resources.LocaleData;
  48 import sun.util.resources.OpenListResourceBundle;
  49 
  50 /**
  51  * An instance of this class holds a set of the third party implementations of a particular
  52  * locale sensitive service, such as {@link java.util.spi.LocaleNameProvider}.
  53  *


  54  */
  55 public final class LocaleServiceProviderPool {
  56 
  57     /**
  58      * A Map that holds singleton instances of this class.  Each instance holds a
  59      * set of provider implementations of a particular locale sensitive service.
  60      */
  61     private static ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProviderPool> poolOfPools =
  62         new ConcurrentHashMap<>();
  63 
  64     /**
  65      * A Set containing locale service providers that implement the
  66      * specified provider SPI
  67      */
  68     private Set<LocaleServiceProvider> providers =
  69         new LinkedHashSet<LocaleServiceProvider>();
  70 
  71     /**
  72      * A Map that retains Locale->provider mapping
  73      */
  74     private Map<Locale, LocaleServiceProvider> providersCache =
  75         new ConcurrentHashMap<Locale, LocaleServiceProvider>();
  76 
  77     /**
  78      * Available locales for this locale sensitive service.  This also contains
  79      * JRE's available locales
  80      */
  81     private Set<Locale> availableLocales = null;
  82 
  83     /**
  84      * Available locales within this JRE.  Currently this is declared as
  85      * static.  This could be non-static later, so that they could have
  86      * different sets for each locale sensitive services.
  87      */
  88     private static volatile List<Locale> availableJRELocales = null;
  89 
  90     /**
  91      * Provider locales for this locale sensitive service.




  92      */
  93     private Set<Locale> providerLocales = null;













  94 
  95     /**
  96      * Special locale for ja_JP with Japanese calendar
  97      */
  98     private static Locale locale_ja_JP_JP = new Locale("ja", "JP", "JP");
  99 
 100     /**
 101      * Special locale for th_TH with Thai numbering system
 102      */
 103     private static Locale locale_th_TH_TH = new Locale("th", "TH", "TH");
 104 
 105     /**
 106      * A factory method that returns a singleton instance
 107      */
 108     public static LocaleServiceProviderPool getPool(Class<? extends LocaleServiceProvider> providerClass) {
 109         LocaleServiceProviderPool pool = poolOfPools.get(providerClass);
 110         if (pool == null) {
 111             LocaleServiceProviderPool newPool =
 112                 new LocaleServiceProviderPool(providerClass);
 113             pool = poolOfPools.putIfAbsent(providerClass, newPool);
 114             if (pool == null) {
 115                 pool = newPool;
 116             }
 117         }
 118 
 119         return pool;
 120     }
 121 
 122     /**
 123      * The sole constructor.
 124      *
 125      * @param c class of the locale sensitive service
 126      */
 127     private LocaleServiceProviderPool (final Class<? extends LocaleServiceProvider> c) {
 128         try {
 129             AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
 130                 public Object run() {
 131                     for (LocaleServiceProvider provider : ServiceLoader.loadInstalled(c)) {
 132                         providers.add(provider);






 133                     }
 134                     return null;






 135                 }
 136             });
 137         }  catch (PrivilegedActionException e) {
 138             config(e.toString());
 139         }







 140     }


 141 
 142     private static void config(String message) {
 143         PlatformLogger logger = PlatformLogger.getLogger("sun.util.LocaleServiceProviderPool");
 144         logger.config(message);
 145     }
 146 
 147     /**
 148      * Lazy loaded set of available locales.
 149      * Loading all locales is a very long operation.
 150      *
 151      * We know "providerClasses" contains classes that extends LocaleServiceProvider,
 152      * but generic array creation is not allowed, thus the "unchecked" warning
 153      * is suppressed here.
 154      */
 155     private static class AllAvailableLocales {
 156         /**
 157          * Available locales for all locale sensitive services.
 158          * This also contains JRE's available locales
 159          */
 160         static final Locale[] allAvailableLocales;
 161 
 162         static {
 163             @SuppressWarnings("unchecked")
 164             Class<LocaleServiceProvider>[] providerClasses =
 165                         (Class<LocaleServiceProvider>[]) new Class<?>[] {
 166                 java.text.spi.BreakIteratorProvider.class,
 167                 java.text.spi.CollatorProvider.class,
 168                 java.text.spi.DateFormatProvider.class,
 169                 java.text.spi.DateFormatSymbolsProvider.class,
 170                 java.text.spi.DecimalFormatSymbolsProvider.class,
 171                 java.text.spi.NumberFormatProvider.class,
 172                 java.util.spi.CurrencyNameProvider.class,
 173                 java.util.spi.LocaleNameProvider.class,
 174                 java.util.spi.TimeZoneNameProvider.class };
 175 
 176             // Normalize locales for look up
 177             Locale[] allLocales = LocaleData.getAvailableLocales();
 178             Set<Locale> all = new HashSet<Locale>(allLocales.length);
 179             for (Locale locale : allLocales) {
 180                 all.add(getLookupLocale(locale));
 181             }
 182 
 183             for (Class<LocaleServiceProvider> providerClass : providerClasses) {
 184                 LocaleServiceProviderPool pool =
 185                     LocaleServiceProviderPool.getPool(providerClass);
 186                 all.addAll(pool.getProviderLocales());
 187             }
 188 
 189             allAvailableLocales = all.toArray(new Locale[0]);
 190         }



 191     }

 192 
 193     /**
 194      * Returns an array of available locales for all the provider classes.
 195      * This array is a merged array of all the locales that are provided by each
 196      * provider, including the JRE.
 197      *
 198      * @return an array of the available locales for all provider classes
 199      */
 200     public static Locale[] getAllAvailableLocales() {
 201         return AllAvailableLocales.allAvailableLocales.clone();
 202     }
 203 
 204     /**
 205      * Returns an array of available locales.  This array is a
 206      * merged array of all the locales that are provided by each
 207      * provider, including the JRE.
 208      *
 209      * @return an array of the available locales
 210      */
 211     public synchronized Locale[] getAvailableLocales() {
 212         if (availableLocales == null) {
 213             availableLocales = new HashSet<Locale>(getJRELocales());
 214             if (hasProviders()) {
 215                 availableLocales.addAll(getProviderLocales());
 216             }
 217         }
 218         Locale[] tmp = new Locale[availableLocales.size()];
 219         availableLocales.toArray(tmp);
 220         return tmp;
 221     }
 222 
 223     /**
 224      * Returns an array of available locales (already normalized
 225      * for service lookup) from providers.
 226      * Note that this method does not return a defensive copy.
 227      *
 228      * @return list of the provider locales
 229      */
 230     private synchronized Set<Locale> getProviderLocales() {
 231         if (providerLocales == null) {
 232             providerLocales = new HashSet<Locale>();
 233             if (hasProviders()) {
 234                 for (LocaleServiceProvider lsp : providers) {
 235                     Locale[] locales = lsp.getAvailableLocales();
 236                     for (Locale locale: locales) {
 237                         providerLocales.add(getLookupLocale(locale));
 238                     }
 239                 }
 240             }
 241         }
 242         return providerLocales;
 243     }
 244 
 245     /**
 246      * Returns whether any provider for this locale sensitive
 247      * service is available or not.
 248      *
 249      * @return true if any provider is available
 250      */
 251     public boolean hasProviders() {
 252         return !providers.isEmpty();
 253     }
 254 
 255     /**
 256      * Returns an array of available locales (already normalized for
 257      * service lookup) supported by the JRE.
 258      * Note that this method does not return a defensive copy.
 259      *
 260      * @return list of the available JRE locales
 261      */
 262     private List<Locale> getJRELocales() {
 263         if (availableJRELocales == null) {
 264             synchronized (LocaleServiceProviderPool.class) {
 265                 if (availableJRELocales == null) {
 266                     Locale[] allLocales = LocaleData.getAvailableLocales();
 267                     List<Locale> tmpList = new ArrayList<>(allLocales.length);
 268                     for (Locale locale : allLocales) {
 269                         tmpList.add(getLookupLocale(locale));
 270                     }
 271                     availableJRELocales = tmpList;
 272                 }
 273             }
 274         }
 275         return availableJRELocales;
 276     }
 277 
 278     /**
 279      * Returns whether the given locale is supported by the JRE.

 280      *
 281      * @param locale the locale to test.
 282      * @return true, if the locale is supported by the JRE. false
 283      *     otherwise.
 284      */
 285     private boolean isJRESupported(Locale locale) {
 286         List<Locale> locales = getJRELocales();
 287         return locales.contains(getLookupLocale(locale));
 288     }
 289 
 290     /**
 291      * Returns the provider's localized object for the specified
 292      * locale.
 293      *
 294      * @param getter an object on which getObject() method
 295      *     is called to obtain the provider's instance.
 296      * @param locale the given locale that is used as the starting one
 297      * @param params provider specific parameters
 298      * @return provider's instance, or null.
 299      */
 300     public <P, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
 301                                      Locale locale,
 302                                      Object... params) {
 303         return getLocalizedObjectImpl(getter, locale, true, null, null, null, params);
 304     }
 305 
 306     /**
 307      * Returns the provider's localized name for the specified
 308      * locale.
 309      *
 310      * @param getter an object on which getObject() method
 311      *     is called to obtain the provider's instance.
 312      * @param locale the given locale that is used as the starting one
 313      * @param bundle JRE resource bundle that contains
 314      *     the localized names, or null for localized objects.
 315      * @param key the key string if bundle is supplied, otherwise null.
 316      * @param params provider specific parameters
 317      * @return provider's instance, or null.
 318      */
 319     public <P, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
 320                                      Locale locale,
 321                                      OpenListResourceBundle bundle,
 322                                      String key,
 323                                      Object... params) {
 324         return getLocalizedObjectImpl(getter, locale, false, null, bundle, key, params);
 325     }
 326 
 327     /**
 328      * Returns the provider's localized name for the specified
 329      * locale.
 330      *
 331      * @param getter an object on which getObject() method
 332      *     is called to obtain the provider's instance.
 333      * @param locale the given locale that is used as the starting one
 334      * @param bundleKey JRE specific bundle key. e.g., "USD" is for currency
 335            symbol and "usd" is for currency display name in the JRE bundle.
 336      * @param bundle JRE resource bundle that contains
 337      *     the localized names, or null for localized objects.
 338      * @param key the key string if bundle is supplied, otherwise null.
 339      * @param params provider specific parameters
 340      * @return provider's instance, or null.
 341      */
 342     public <P, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
 343                                      Locale locale,
 344                                      String bundleKey,
 345                                      OpenListResourceBundle bundle,
 346                                      String key,
 347                                      Object... params) {
 348         return getLocalizedObjectImpl(getter, locale, false, bundleKey, bundle, key, params);

 349     }
 350 
 351     private <P, S> S getLocalizedObjectImpl(LocalizedObjectGetter<P, S> getter,
 352                                      Locale locale,
 353                                      boolean isObjectProvider,
 354                                      String bundleKey,
 355                                      OpenListResourceBundle bundle,
 356                                      String key,
 357                                      Object... params) {
 358         if (hasProviders()) {
 359             if (bundleKey == null) {
 360                 bundleKey = key;
 361             }
 362             Locale bundleLocale = (bundle != null ? bundle.getLocale() : null);
 363             List<Locale> lookupLocales = getLookupLocales(locale);
 364             S providersObj = null;
 365 
 366             // check whether a provider has an implementation that's closer
 367             // to the requested locale than the bundle we've found (for
 368             // localized names), or Java runtime's supported locale
 369             // (for localized objects)
 370             Set<Locale> provLoc = getProviderLocales();
 371             for (int i = 0; i < lookupLocales.size(); i++) {
 372                 Locale current = lookupLocales.get(i);
 373                 if (bundleLocale != null) {
 374                     if (current.equals(bundleLocale)) {
 375                         break;
 376                     }
 377                 } else {
 378                     if (isJRESupported(current)) {
 379                         break;
 380                     }
 381                 }
 382                 if (provLoc.contains(current)) {
 383                     // It is safe to assume that findProvider() returns the instance of type P.
 384                     @SuppressWarnings("unchecked")
 385                     P lsp = (P)findProvider(current);
 386                     if (lsp != null) {
 387                         providersObj = getter.getObject(lsp, locale, key, params);
 388                         if (providersObj != null) {
 389                             return providersObj;
 390                         } else if (isObjectProvider) {
 391                             config(
 392                                 "A locale sensitive service provider returned null for a localized objects,  which should not happen.  provider: " + lsp + " locale: " + locale);

 393                         }
 394                     }
 395                 }
 396             }
 397 
 398             // look up the JRE bundle and its parent chain.  Only
 399             // providers for localized names are checked hereafter.
 400             while (bundle != null) {
 401                 bundleLocale = bundle.getLocale();
 402 
 403                 if (bundle.handleGetKeys().contains(bundleKey)) {
 404                     // JRE has it.
 405                     return null;
 406                 } else {
 407                     // It is safe to assume that findProvider() returns the instance of type P.
 408                     @SuppressWarnings("unchecked")
 409                     P lsp = (P)findProvider(bundleLocale);
 410                     if (lsp != null) {
 411                         providersObj = getter.getObject(lsp, locale, key, params);
 412                         if (providersObj != null) {
 413                             return providersObj;
 414                         }
 415                     }
 416                 }
 417 
 418                 // try parent bundle
 419                 bundle = bundle.getParent();
 420             }
 421         }
 422 
 423         // not found.
 424         return null;
 425     }
 426 
 427     /**
 428      * Returns a locale service provider instance that supports
 429      * the specified locale.
 430      *
 431      * @param locale the given locale
 432      * @return the provider, or null if there is
 433      *     no provider available.
 434      */
 435     private LocaleServiceProvider findProvider(Locale locale) {
 436         if (!hasProviders()) {
 437             return null;






 438         }

 439 
 440         if (providersCache.containsKey(locale)) {
 441             LocaleServiceProvider provider = providersCache.get(locale);
 442             if (provider != NullProvider.INSTANCE) {
 443                 return provider;
 444             }
 445         } else {
 446             for (LocaleServiceProvider lsp : providers) {
 447                 Locale[] locales = lsp.getAvailableLocales();
 448                 for (Locale available: locales) {
 449                     // normalize
 450                     available = getLookupLocale(available);
 451                     if (locale.equals(available)) {
 452                         LocaleServiceProvider providerInCache =
 453                             providersCache.put(locale, lsp);
 454                         return (providerInCache != null ?
 455                                 providerInCache :
 456                                 lsp);
 457                     }
 458                 }


 459             }
 460             providersCache.put(locale, NullProvider.INSTANCE);


 461         }
 462         return null;
 463     }


 464 
 465     /**
 466      * Returns a list of candidate locales for service look up.
 467      * @param locale the input locale
 468      * @return the list of candiate locales for the given locale
 469      */
 470     private static List<Locale> getLookupLocales(Locale locale) {
 471         // Note: We currently use the default implementation of
 472         // ResourceBundle.Control.getCandidateLocales. The result
 473         // returned by getCandidateLocales are already normalized
 474         // (no extensions) for service look up.
 475         List<Locale> lookupLocales = new Control(){}.getCandidateLocales("", locale);

 476         return lookupLocales;
 477     }
 478 
 479     /**
 480      * Returns an instance of Locale used for service look up.
 481      * The result Locale has no extensions except for ja_JP_JP
 482      * and th_TH_TH
 483      *
 484      * @param locale the locale
 485      * @return the locale used for service look up
 486      */
 487     private static Locale getLookupLocale(Locale locale) {
 488         Locale lookupLocale = locale;
 489         Set<Character> extensions = locale.getExtensionKeys();
 490         if (!extensions.isEmpty()
 491                 && !locale.equals(locale_ja_JP_JP)
 492                 && !locale.equals(locale_th_TH_TH)) {
 493             // remove extensions
 494             Builder locbld = new Builder();
 495             try {
 496                 locbld.setLocale(locale);
 497                 locbld.clearExtensions();
 498                 lookupLocale = locbld.build();
 499             } catch (IllformedLocaleException e) {
 500                 // A Locale with non-empty extensions
 501                 // should have well-formed fields except
 502                 // for ja_JP_JP and th_TH_TH. Therefore,
 503                 // it should never enter in this catch clause.
 504                 config("A locale(" + locale + ") has non-empty extensions, but has illformed fields.");

 505 
 506                 // Fallback - script field will be lost.
 507                 lookupLocale = new Locale(locale.getLanguage(), locale.getCountry(), locale.getVariant());
 508             }
 509         }
 510         return lookupLocale;
 511     }
 512 
 513     /**
 514      * A dummy locale service provider that indicates there is no
 515      * provider available
 516      */
 517     private static class NullProvider extends LocaleServiceProvider {
 518         private static final NullProvider INSTANCE = new NullProvider();
 519 
 520         public Locale[] getAvailableLocales() {
 521             throw new RuntimeException("Should not get called.");
 522         }
 523     }
 524 
 525     /**
 526      * An interface to get a localized object for each locale sensitve
 527      * service class.
 528      */
 529     public interface LocalizedObjectGetter<P, S> {
 530         /**
 531          * Returns an object from the provider
 532          *
 533          * @param lsp the provider
 534          * @param locale the locale
 535          * @param key key string to localize, or null if the provider is not
 536          *     a name provider
 537          * @param params provider specific params
 538          * @return localized object from the provider
 539          */
 540         public S getObject(P lsp,
 541                                 Locale locale,
 542                                 String key,
 543                                 Object... params);
 544     }
 545 }
   1 /*
   2  * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.util.locale.provider;
  27 



  28 import java.util.ArrayList;
  29 import java.util.Collections;
  30 import java.util.HashSet;
  31 import java.util.IllformedLocaleException;

  32 import java.util.List;
  33 import java.util.Locale;
  34 import java.util.Locale.Builder;

  35 import java.util.ResourceBundle.Control;

  36 import java.util.Set;
  37 import java.util.concurrent.ConcurrentHashMap;
  38 import java.util.concurrent.ConcurrentMap;
  39 import java.util.spi.LocaleServiceProvider;

  40 import sun.util.logging.PlatformLogger;


  41 
  42 /**
  43  * An instance of this class holds a set of the third party implementations of a particular
  44  * locale sensitive service, such as {@link java.util.spi.LocaleNameProvider}.
  45  *
  46  * @author Naoto Sato
  47  * @author Masayoshi Okutsu
  48  */
  49 public final class LocaleServiceProviderPool {
  50 
  51     /**
  52      * A Map that holds singleton instances of this class.  Each instance holds a
  53      * set of provider implementations of a particular locale sensitive service.
  54      */
  55     private static ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProviderPool> poolOfPools =
  56         new ConcurrentHashMap<>();
  57 
  58     /**
  59      * A Map containing locale service providers that implement the
  60      * specified provider SPI, keyed by a LocaleProviderAdapter.Type
  61      */
  62     private ConcurrentMap<LocaleProviderAdapter.Type, LocaleServiceProvider> providers =
  63         new ConcurrentHashMap<>();
  64 
  65     /**
  66      * A Map that retains Locale->provider mapping
  67      */
  68     private ConcurrentMap<Locale, List<LocaleProviderAdapter.Type>> providersCache =
  69         new ConcurrentHashMap<>();
  70 
  71     /**
  72      * Available locales for this locale sensitive service.  This also contains
  73      * JRE's available locales
  74      */
  75     private Set<Locale> availableLocales = null;
  76 
  77     /**
  78      * Provider class


  79      */
  80     private Class<? extends LocaleServiceProvider> providerClass;
  81 
  82     /**
  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" warning
  87      * is suppressed here.
  88      */
  89     @SuppressWarnings("unchecked")
  90     static final Class<LocaleServiceProvider>[] spiClasses =
  91                 (Class<LocaleServiceProvider>[]) new Class<?>[] {
  92         java.text.spi.BreakIteratorProvider.class,
  93         java.text.spi.CollatorProvider.class,
  94         java.text.spi.DateFormatProvider.class,
  95         java.text.spi.DateFormatSymbolsProvider.class,
  96         java.text.spi.DecimalFormatSymbolsProvider.class,
  97         java.text.spi.NumberFormatProvider.class,
  98         java.util.spi.CurrencyNameProvider.class,
  99         java.util.spi.LocaleNameProvider.class,
 100         java.util.spi.TimeZoneNameProvider.class,
 101         java.util.spi.CalendarDataProvider.class
 102     };
 103 
 104     /**










 105      * A factory method that returns a singleton instance
 106      */
 107     public static LocaleServiceProviderPool getPool(Class<? extends LocaleServiceProvider> providerClass) {
 108         LocaleServiceProviderPool pool = poolOfPools.get(providerClass);
 109         if (pool == null) {
 110             LocaleServiceProviderPool newPool =
 111                 new LocaleServiceProviderPool(providerClass);
 112             pool = poolOfPools.putIfAbsent(providerClass, newPool);
 113             if (pool == null) {
 114                 pool = newPool;
 115             }
 116         }
 117 
 118         return pool;
 119     }
 120 
 121     /**
 122      * The sole constructor.
 123      *
 124      * @param c class of the locale sensitive service
 125      */
 126     private LocaleServiceProviderPool (final Class<? extends LocaleServiceProvider> c) {
 127         providerClass = c;
 128 
 129         // Add the JRE Locale Data Adapter implementation.
 130         providers.putIfAbsent(LocaleProviderAdapter.Type.JRE,
 131             LocaleProviderAdapter.forJRE().getLocaleServiceProvider(c));
 132 
 133         // Add the SPI Locale Data Adapter implementation.
 134         LocaleProviderAdapter lda = LocaleProviderAdapter.forType(LocaleProviderAdapter.Type.SPI);
 135         LocaleServiceProvider provider = lda.getLocaleServiceProvider(c);
 136         if (provider != null) {
 137             providers.putIfAbsent(LocaleProviderAdapter.Type.SPI, provider);
 138         }
 139 
 140         // Add the CLDR Locale Data Adapter implementation, if needed.
 141         lda =  LocaleProviderAdapter.forType(LocaleProviderAdapter.Type.CLDR);
 142         if (lda != null) {
 143             provider = lda.getLocaleServiceProvider(c);
 144             if (provider != null) {
 145                 providers.putIfAbsent(LocaleProviderAdapter.Type.CLDR, provider);
 146             }



 147         }
 148 
 149         // Add the Host Locale Data Adapter implementation, if needed.
 150         lda =  LocaleProviderAdapter.forType(LocaleProviderAdapter.Type.HOST);
 151         if (lda != null) {
 152             provider = lda.getLocaleServiceProvider(c);
 153             if (provider != null) {
 154                 providers.putIfAbsent(LocaleProviderAdapter.Type.HOST, provider);
 155             }
 156         }
 157     }
 158 
 159     static void config(Class<? extends Object> caller, String message) {
 160         PlatformLogger logger = PlatformLogger.getLogger(caller.getCanonicalName());
 161         logger.config(message);
 162     }
 163 
 164     /**
 165      * Lazy loaded set of available locales.
 166      * Loading all locales is a very long operation.




 167      */
 168     private static class AllAvailableLocales {
 169         /**
 170          * Available locales for all locale sensitive services.
 171          * This also contains JRE's available locales
 172          */
 173         static final Locale[] allAvailableLocales;
 174 
 175         static {
 176             Set<Locale> all = new HashSet<>();
 177             for (Class<? extends LocaleServiceProvider> c : spiClasses) {



















 178                 LocaleServiceProviderPool pool =
 179                     LocaleServiceProviderPool.getPool(c);
 180                 all.addAll(pool.getAvailableLocaleList());
 181             }
 182 
 183             allAvailableLocales = all.toArray(new Locale[0]);
 184         }
 185 
 186         // No instantiation
 187         private AllAvailableLocales() {
 188         }
 189     }
 190 
 191     /**
 192      * Returns an array of available locales for all the provider classes.
 193      * This array is a merged array of all the locales that are provided by each
 194      * provider, including the JRE.
 195      *
 196      * @return an array of the available locales for all provider classes
 197      */
 198     public static Locale[] getAllAvailableLocales() {
 199         return AllAvailableLocales.allAvailableLocales.clone();
 200     }
 201 
 202     /**
 203      * Returns an array of available locales.  This array is a
 204      * merged array of all the locales that are provided by each
 205      * provider, including the JRE.
 206      *
 207      * @return an array of the available locales
 208      */
 209     public Locale[] getAvailableLocales() {
 210         Set<Locale> locList = getAvailableLocaleList();
 211         Locale[] tmp = new Locale[locList.size()];
 212         locList.toArray(tmp);





 213         return tmp;
 214     }
 215 
 216     private synchronized Set<Locale> getAvailableLocaleList() {
 217         if (availableLocales == null) {
 218             availableLocales = new HashSet<>();
 219             for (LocaleServiceProvider lsp : providers.values()) {








 220                 Locale[] locales = lsp.getAvailableLocales();
 221                 for (Locale locale: locales) {
 222                     availableLocales.add(getLookupLocale(locale));
 223                 }
 224             }




 225 
 226             // Remove Locale.ROOT for the compatibility.
 227             availableLocales.remove(Locale.ROOT);






 228         }
 229 
 230         return availableLocales;














 231     }






 232 
 233     /**
 234      * Returns whether any provider for this locale sensitive
 235      * service is available or not, excluding JRE's one.
 236      *
 237      * @return true if any provider (other than JRE) is available


 238      */
 239     boolean hasProviders() {
 240         return providers.size() != 1 ||
 241                providers.get(LocaleProviderAdapter.Type.JRE) == null;
 242     }
 243 
 244     /**
 245      * Returns the provider's localized object for the specified
 246      * locale.
 247      *
 248      * @param getter an object on which getObject() method
 249      *     is called to obtain the provider's instance.
 250      * @param locale the given locale that is used as the starting one
 251      * @param params provider specific parameters
 252      * @return provider's instance, or null.
 253      */
 254     public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
 255                                      Locale locale,
 256                                      Object... params) {
 257         return getLocalizedObjectImpl(getter, locale, true, null, params);
 258     }
 259 
 260     /**
 261      * Returns the provider's localized name for the specified
 262      * locale.
 263      *
 264      * @param getter an object on which getObject() method
 265      *     is called to obtain the provider's instance.
 266      * @param locale the given locale that is used as the starting one
 267      * @param key the key string for name providers


 268      * @param params provider specific parameters
 269      * @return provider's instance, or null.
 270      */
 271     public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
 272                                      Locale locale,

 273                                      String key,
 274                                      Object... params) {
 275         return getLocalizedObjectImpl(getter, locale, false, key, params);
 276     }
 277 
 278     @SuppressWarnings("unchecked")
 279     private <P extends LocaleServiceProvider, S> S getLocalizedObjectImpl(LocalizedObjectGetter<P, S> getter,














 280                                      Locale locale,
 281                                      boolean isObjectProvider,

 282                                      String key,
 283                                      Object... params) {
 284         if (locale == null) {
 285             throw new NullPointerException();
 286         }
 287 
 288         // Check whether JRE is the sole locale data provider or not,
 289         // and directly call it if it is.
 290         if (!hasProviders()) {
 291             return getter.getObject(
 292                 (P)providers.get(LocaleProviderAdapter.Type.JRE),
 293                 locale, key, params);




 294         }
 295 
 296         List<Locale> lookupLocales = getLookupLocales(locale);

 297 
 298         Set<Locale> available = getAvailableLocaleList();
 299         for (Locale current : lookupLocales) {
 300             if (available.contains(current)) {
 301                 S providersObj;
 302 
 303                 for (LocaleProviderAdapter.Type type: findProviders(current)) {
 304                     LocaleServiceProvider lsp = providers.get(type);
 305                     providersObj = getter.getObject((P)lsp, current, key, params);














 306                     if (providersObj != null) {
 307                         return providersObj;
 308                     } else if (isObjectProvider) {
 309                         config(LocaleServiceProviderPool.class,
 310                             "A locale sensitive service provider returned null for a localized objects,  which should not happen.  provider: "
 311                                 + lsp + " locale: " + locale);
 312                     }
 313                 }
 314             }
 315         }
 316 

























 317         // not found.
 318         return null;
 319     }
 320 
 321     /**
 322      * Returns the list of locale service provider instances that support
 323      * the specified locale.
 324      *
 325      * @param locale the given locale
 326      * @return the list of locale data adapter types

 327      */
 328     private List<LocaleProviderAdapter.Type> findProviders(Locale locale) {
 329         List<LocaleProviderAdapter.Type> providersList = providersCache.get(locale);
 330         if (providersList == null) {
 331             for (LocaleProviderAdapter.Type type : LocaleProviderAdapter.getAdapterPreference()) {
 332                 LocaleServiceProvider lsp = providers.get(type);
 333                 if (lsp != null) {
 334                     if (lsp.isSupportedLocale(locale)) {
 335                         if (providersList == null) {
 336                             providersList = new ArrayList<>(2);
 337                         }
 338                         providersList.add(type);
 339 




 340                     }












 341                 }
 342             }
 343             if (providersList == null) {
 344                 providersList = NULL_LIST;
 345             }
 346             List<LocaleProviderAdapter.Type> val = providersCache.putIfAbsent(locale, providersList);
 347             if (val != null) {
 348                 providersList = val;
 349             }

 350         }
 351             return providersList;
 352         }
 353 
 354     /**
 355      * Returns a list of candidate locales for service look up.
 356      * @param locale the input locale
 357      * @return the list of candidate locales for the given locale
 358      */
 359     private static List<Locale> getLookupLocales(Locale locale) {
 360         // Note: We currently use the default implementation of
 361         // ResourceBundle.Control.getCandidateLocales. The result
 362         // returned by getCandidateLocales are already normalized
 363         // (no extensions) for service look up.
 364         List<Locale> lookupLocales = Control.getNoFallbackControl(Control.FORMAT_DEFAULT)
 365                                             .getCandidateLocales("", locale);
 366         return lookupLocales;
 367     }
 368 
 369     /**
 370      * Returns an instance of Locale used for service look up.
 371      * The result Locale has no extensions except for ja_JP_JP
 372      * and th_TH_TH
 373      *
 374      * @param locale the locale
 375      * @return the locale used for service look up
 376      */
 377     static Locale getLookupLocale(Locale locale) {
 378         Locale lookupLocale = locale;
 379         if (locale.hasExtensions()
 380                 && !locale.equals(JRELocaleConstants.JA_JP_JP)
 381                 && !locale.equals(JRELocaleConstants.TH_TH_TH)) {

 382             // remove extensions
 383             Builder locbld = new Builder();
 384             try {
 385                 locbld.setLocale(locale);
 386                 locbld.clearExtensions();
 387                 lookupLocale = locbld.build();
 388             } catch (IllformedLocaleException e) {
 389                 // A Locale with non-empty extensions
 390                 // should have well-formed fields except
 391                 // for ja_JP_JP and th_TH_TH. Therefore,
 392                 // it should never enter in this catch clause.
 393                 config(LocaleServiceProviderPool.class,
 394                        "A locale(" + locale + ") has non-empty extensions, but has illformed fields.");
 395 
 396                 // Fallback - script field will be lost.
 397                 lookupLocale = new Locale(locale.getLanguage(), locale.getCountry(), locale.getVariant());
 398             }
 399         }
 400         return lookupLocale;
 401     }
 402 
 403     /**
 404      * A dummy locale service provider list that indicates there is no
 405      * provider available
 406      */
 407     private static List<LocaleProviderAdapter.Type> NULL_LIST =
 408         Collections.emptyList();
 409 





 410     /**
 411      * An interface to get a localized object for each locale sensitive
 412      * service class.
 413      */
 414     public interface LocalizedObjectGetter<P extends LocaleServiceProvider, S> {
 415         /**
 416          * Returns an object from the provider
 417          *
 418          * @param lsp the provider
 419          * @param locale the locale
 420          * @param key key string to localize, or null if the provider is not
 421          *     a name provider
 422          * @param params provider specific params
 423          * @return localized object from the provider
 424          */
 425         public S getObject(P lsp,
 426                            Locale locale,
 427                            String key,
 428                            Object... params);
 429     }
 430 }