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)

Split Close
Expand all
Collapse all
          --- old/src/share/classes/sun/util/LocaleServiceProviderPool.java
          +++ new/src/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java
   1    1  /*
   2      - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
        2 + * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
   3    3   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4    4   *
   5    5   * This code is free software; you can redistribute it and/or modify it
   6    6   * under the terms of the GNU General Public License version 2 only, as
   7    7   * published by the Free Software Foundation.  Oracle designates this
   8    8   * particular file as subject to the "Classpath" exception as provided
   9    9   * by Oracle in the LICENSE file that accompanied this code.
  10   10   *
  11   11   * This code is distributed in the hope that it will be useful, but WITHOUT
  12   12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
↓ open down ↓ 3 lines elided ↑ open up ↑
  16   16   *
  17   17   * You should have received a copy of the GNU General Public License version
  18   18   * 2 along with this work; if not, write to the Free Software Foundation,
  19   19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20   20   *
  21   21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22   22   * or visit www.oracle.com if you need additional information or have any
  23   23   * questions.
  24   24   */
  25   25  
  26      -package sun.util;
       26 +package sun.util.locale.provider;
  27   27  
  28      -import java.security.AccessController;
  29      -import java.security.PrivilegedActionException;
  30      -import java.security.PrivilegedExceptionAction;
  31   28  import java.util.ArrayList;
       29 +import java.util.Collections;
  32   30  import java.util.HashSet;
  33   31  import java.util.IllformedLocaleException;
  34      -import java.util.LinkedHashSet;
  35   32  import java.util.List;
  36   33  import java.util.Locale;
  37   34  import java.util.Locale.Builder;
  38      -import java.util.Map;
  39   35  import java.util.ResourceBundle.Control;
  40      -import java.util.ServiceLoader;
  41   36  import java.util.Set;
  42   37  import java.util.concurrent.ConcurrentHashMap;
  43   38  import java.util.concurrent.ConcurrentMap;
  44   39  import java.util.spi.LocaleServiceProvider;
  45      -
  46   40  import sun.util.logging.PlatformLogger;
  47      -import sun.util.resources.LocaleData;
  48      -import sun.util.resources.OpenListResourceBundle;
  49   41  
  50   42  /**
  51   43   * An instance of this class holds a set of the third party implementations of a particular
  52   44   * locale sensitive service, such as {@link java.util.spi.LocaleNameProvider}.
  53   45   *
       46 + * @author Naoto Sato
       47 + * @author Masayoshi Okutsu
  54   48   */
  55   49  public final class LocaleServiceProviderPool {
  56   50  
  57   51      /**
  58   52       * A Map that holds singleton instances of this class.  Each instance holds a
  59   53       * set of provider implementations of a particular locale sensitive service.
  60   54       */
  61   55      private static ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProviderPool> poolOfPools =
  62   56          new ConcurrentHashMap<>();
  63   57  
  64   58      /**
  65      -     * A Set containing locale service providers that implement the
  66      -     * specified provider SPI
       59 +     * A Map containing locale service providers that implement the
       60 +     * specified provider SPI, keyed by a LocaleProviderAdapter.Type
  67   61       */
  68      -    private Set<LocaleServiceProvider> providers =
  69      -        new LinkedHashSet<LocaleServiceProvider>();
       62 +    private ConcurrentMap<LocaleProviderAdapter.Type, LocaleServiceProvider> providers =
       63 +        new ConcurrentHashMap<>();
  70   64  
  71   65      /**
  72   66       * A Map that retains Locale->provider mapping
  73   67       */
  74      -    private Map<Locale, LocaleServiceProvider> providersCache =
  75      -        new ConcurrentHashMap<Locale, LocaleServiceProvider>();
       68 +    private ConcurrentMap<Locale, List<LocaleProviderAdapter.Type>> providersCache =
       69 +        new ConcurrentHashMap<>();
  76   70  
  77   71      /**
  78   72       * Available locales for this locale sensitive service.  This also contains
  79   73       * JRE's available locales
  80   74       */
  81   75      private Set<Locale> availableLocales = null;
  82   76  
  83   77      /**
  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.
       78 +     * Provider class
  87   79       */
  88      -    private static volatile List<Locale> availableJRELocales = null;
       80 +    private Class<? extends LocaleServiceProvider> providerClass;
  89   81  
  90   82      /**
  91      -     * Provider locales for this locale sensitive service.
       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.
  92   88       */
  93      -    private Set<Locale> providerLocales = null;
       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 +    };
  94  103  
  95  104      /**
  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  105       * A factory method that returns a singleton instance
 107  106       */
 108  107      public static LocaleServiceProviderPool getPool(Class<? extends LocaleServiceProvider> providerClass) {
 109  108          LocaleServiceProviderPool pool = poolOfPools.get(providerClass);
 110  109          if (pool == null) {
 111  110              LocaleServiceProviderPool newPool =
 112  111                  new LocaleServiceProviderPool(providerClass);
 113  112              pool = poolOfPools.putIfAbsent(providerClass, newPool);
 114  113              if (pool == null) {
 115  114                  pool = newPool;
↓ open down ↓ 2 lines elided ↑ open up ↑
 118  117  
 119  118          return pool;
 120  119      }
 121  120  
 122  121      /**
 123  122       * The sole constructor.
 124  123       *
 125  124       * @param c class of the locale sensitive service
 126  125       */
 127  126      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());
      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);
 139  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 +        }
 140  157      }
 141  158  
 142      -    private static void config(String message) {
 143      -        PlatformLogger logger = PlatformLogger.getLogger("sun.util.LocaleServiceProviderPool");
      159 +    static void config(Class<? extends Object> caller, String message) {
      160 +        PlatformLogger logger = PlatformLogger.getLogger(caller.getCanonicalName());
 144  161          logger.config(message);
 145  162      }
 146  163  
 147  164      /**
 148  165       * Lazy loaded set of available locales.
 149  166       * 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  167       */
 155  168      private static class AllAvailableLocales {
 156  169          /**
 157  170           * Available locales for all locale sensitive services.
 158  171           * This also contains JRE's available locales
 159  172           */
 160  173          static final Locale[] allAvailableLocales;
 161  174  
 162  175          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) {
      176 +            Set<Locale> all = new HashSet<>();
      177 +            for (Class<? extends LocaleServiceProvider> c : spiClasses) {
 184  178                  LocaleServiceProviderPool pool =
 185      -                    LocaleServiceProviderPool.getPool(providerClass);
 186      -                all.addAll(pool.getProviderLocales());
      179 +                    LocaleServiceProviderPool.getPool(c);
      180 +                all.addAll(pool.getAvailableLocaleList());
 187  181              }
 188  182  
 189  183              allAvailableLocales = all.toArray(new Locale[0]);
 190  184          }
      185 +
      186 +        // No instantiation
      187 +        private AllAvailableLocales() {
      188 +        }
 191  189      }
 192  190  
 193  191      /**
 194  192       * Returns an array of available locales for all the provider classes.
 195  193       * This array is a merged array of all the locales that are provided by each
 196  194       * provider, including the JRE.
 197  195       *
 198  196       * @return an array of the available locales for all provider classes
 199  197       */
 200  198      public static Locale[] getAllAvailableLocales() {
 201  199          return AllAvailableLocales.allAvailableLocales.clone();
 202  200      }
 203  201  
 204  202      /**
 205  203       * Returns an array of available locales.  This array is a
 206  204       * merged array of all the locales that are provided by each
 207  205       * provider, including the JRE.
 208  206       *
 209  207       * @return an array of the available locales
 210  208       */
 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);
      209 +    public Locale[] getAvailableLocales() {
      210 +        Set<Locale> locList = getAvailableLocaleList();
      211 +        Locale[] tmp = new Locale[locList.size()];
      212 +        locList.toArray(tmp);
 220  213          return tmp;
 221  214      }
 222  215  
 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      -                    }
      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));
 239  223                  }
 240  224              }
      225 +
      226 +            // Remove Locale.ROOT for the compatibility.
      227 +            availableLocales.remove(Locale.ROOT);
 241  228          }
 242      -        return providerLocales;
      229 +
      230 +        return availableLocales;
 243  231      }
 244  232  
 245  233      /**
 246  234       * Returns whether any provider for this locale sensitive
 247      -     * service is available or not.
      235 +     * service is available or not, excluding JRE's one.
 248  236       *
 249      -     * @return true if any provider is available
      237 +     * @return true if any provider (other than JRE) is available
 250  238       */
 251      -    public boolean hasProviders() {
 252      -        return !providers.isEmpty();
      239 +    boolean hasProviders() {
      240 +        return providers.size() != 1 ||
      241 +               providers.get(LocaleProviderAdapter.Type.JRE) == null;
 253  242      }
 254  243  
 255  244      /**
 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  245       * Returns the provider's localized object for the specified
 292  246       * locale.
 293  247       *
 294  248       * @param getter an object on which getObject() method
 295  249       *     is called to obtain the provider's instance.
 296  250       * @param locale the given locale that is used as the starting one
 297  251       * @param params provider specific parameters
 298  252       * @return provider's instance, or null.
 299  253       */
 300      -    public <P, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
      254 +    public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
 301  255                                       Locale locale,
 302  256                                       Object... params) {
 303      -        return getLocalizedObjectImpl(getter, locale, true, null, null, null, params);
      257 +        return getLocalizedObjectImpl(getter, locale, true, null, params);
 304  258      }
 305  259  
 306  260      /**
 307  261       * Returns the provider's localized name for the specified
 308  262       * locale.
 309  263       *
 310  264       * @param getter an object on which getObject() method
 311  265       *     is called to obtain the provider's instance.
 312  266       * @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.
      267 +     * @param key the key string for name providers
 316  268       * @param params provider specific parameters
 317  269       * @return provider's instance, or null.
 318  270       */
 319      -    public <P, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
      271 +    public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
 320  272                                       Locale locale,
 321      -                                     OpenListResourceBundle bundle,
 322  273                                       String key,
 323  274                                       Object... params) {
 324      -        return getLocalizedObjectImpl(getter, locale, false, null, bundle, key, params);
      275 +        return getLocalizedObjectImpl(getter, locale, false, key, params);
 325  276      }
 326  277  
 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,
      278 +    @SuppressWarnings("unchecked")
      279 +    private <P extends LocaleServiceProvider, S> S getLocalizedObjectImpl(LocalizedObjectGetter<P, S> getter,
 343  280                                       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  281                                       boolean isObjectProvider,
 354      -                                     String bundleKey,
 355      -                                     OpenListResourceBundle bundle,
 356  282                                       String key,
 357  283                                       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;
      284 +        if (locale == null) {
      285 +            throw new NullPointerException();
      286 +        }
 365  287  
 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      -            }
      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 +        }
 397  295  
 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();
      296 +        List<Locale> lookupLocales = getLookupLocales(locale);
 402  297  
 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      -                        }
      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);
 415  312                      }
 416  313                  }
 417      -
 418      -                // try parent bundle
 419      -                bundle = bundle.getParent();
 420  314              }
 421  315          }
 422  316  
 423  317          // not found.
 424  318          return null;
 425  319      }
 426  320  
 427  321      /**
 428      -     * Returns a locale service provider instance that supports
      322 +     * Returns the list of locale service provider instances that support
 429  323       * the specified locale.
 430  324       *
 431  325       * @param locale the given locale
 432      -     * @return the provider, or null if there is
 433      -     *     no provider available.
      326 +     * @return the list of locale data adapter types
 434  327       */
 435      -    private LocaleServiceProvider findProvider(Locale locale) {
 436      -        if (!hasProviders()) {
 437      -            return null;
 438      -        }
      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);
 439  339  
 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  340                      }
 458  341                  }
 459  342              }
 460      -            providersCache.put(locale, NullProvider.INSTANCE);
      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 +            }
 461  350          }
 462      -        return null;
 463      -    }
      351 +            return providersList;
      352 +        }
 464  353  
 465  354      /**
 466  355       * Returns a list of candidate locales for service look up.
 467  356       * @param locale the input locale
 468      -     * @return the list of candiate locales for the given locale
      357 +     * @return the list of candidate locales for the given locale
 469  358       */
 470  359      private static List<Locale> getLookupLocales(Locale locale) {
 471  360          // Note: We currently use the default implementation of
 472  361          // ResourceBundle.Control.getCandidateLocales. The result
 473  362          // returned by getCandidateLocales are already normalized
 474  363          // (no extensions) for service look up.
 475      -        List<Locale> lookupLocales = new Control(){}.getCandidateLocales("", locale);
      364 +        List<Locale> lookupLocales = Control.getNoFallbackControl(Control.FORMAT_DEFAULT)
      365 +                                            .getCandidateLocales("", locale);
 476  366          return lookupLocales;
 477  367      }
 478  368  
 479  369      /**
 480  370       * Returns an instance of Locale used for service look up.
 481  371       * The result Locale has no extensions except for ja_JP_JP
 482  372       * and th_TH_TH
 483  373       *
 484  374       * @param locale the locale
 485  375       * @return the locale used for service look up
 486  376       */
 487      -    private static Locale getLookupLocale(Locale locale) {
      377 +    static Locale getLookupLocale(Locale locale) {
 488  378          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)) {
      379 +        if (locale.hasExtensions()
      380 +                && !locale.equals(JRELocaleConstants.JA_JP_JP)
      381 +                && !locale.equals(JRELocaleConstants.TH_TH_TH)) {
 493  382              // remove extensions
 494  383              Builder locbld = new Builder();
 495  384              try {
 496  385                  locbld.setLocale(locale);
 497  386                  locbld.clearExtensions();
 498  387                  lookupLocale = locbld.build();
 499  388              } catch (IllformedLocaleException e) {
 500  389                  // A Locale with non-empty extensions
 501  390                  // should have well-formed fields except
 502  391                  // for ja_JP_JP and th_TH_TH. Therefore,
 503  392                  // it should never enter in this catch clause.
 504      -                config("A locale(" + locale + ") has non-empty extensions, but has illformed fields.");
      393 +                config(LocaleServiceProviderPool.class,
      394 +                       "A locale(" + locale + ") has non-empty extensions, but has illformed fields.");
 505  395  
 506  396                  // Fallback - script field will be lost.
 507  397                  lookupLocale = new Locale(locale.getLanguage(), locale.getCountry(), locale.getVariant());
 508  398              }
 509  399          }
 510  400          return lookupLocale;
 511  401      }
 512  402  
 513  403      /**
 514      -     * A dummy locale service provider that indicates there is no
      404 +     * A dummy locale service provider list that indicates there is no
 515  405       * provider available
 516  406       */
 517      -    private static class NullProvider extends LocaleServiceProvider {
 518      -        private static final NullProvider INSTANCE = new NullProvider();
      407 +    private static List<LocaleProviderAdapter.Type> NULL_LIST =
      408 +        Collections.emptyList();
 519  409  
 520      -        public Locale[] getAvailableLocales() {
 521      -            throw new RuntimeException("Should not get called.");
 522      -        }
 523      -    }
 524      -
 525  410      /**
 526      -     * An interface to get a localized object for each locale sensitve
      411 +     * An interface to get a localized object for each locale sensitive
 527  412       * service class.
 528  413       */
 529      -    public interface LocalizedObjectGetter<P, S> {
      414 +    public interface LocalizedObjectGetter<P extends LocaleServiceProvider, S> {
 530  415          /**
 531  416           * Returns an object from the provider
 532  417           *
 533  418           * @param lsp the provider
 534  419           * @param locale the locale
 535  420           * @param key key string to localize, or null if the provider is not
 536  421           *     a name provider
 537  422           * @param params provider specific params
 538  423           * @return localized object from the provider
 539  424           */
 540  425          public S getObject(P lsp,
 541      -                                Locale locale,
 542      -                                String key,
 543      -                                Object... params);
      426 +                           Locale locale,
      427 +                           String key,
      428 +                           Object... params);
 544  429      }
 545  430  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX