151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski/*
22c87ad3a45cecf9e344487cad1abfdebe79f2c7cNarayan Kamath * Copyright (C) 2014 The Android Open Source Project
351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski *
651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * This code is free software; you can redistribute it and/or modify it
751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * under the terms of the GNU General Public License version 2 only, as
851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * published by the Free Software Foundation.  Oracle designates this
951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * particular file as subject to the "Classpath" exception as provided
1051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * by Oracle in the LICENSE file that accompanied this code.
1151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski *
1251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * This code is distributed in the hope that it will be useful, but WITHOUT
1351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * version 2 for more details (a copy is included in the LICENSE file that
1651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * accompanied this code).
1751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski *
1851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * You should have received a copy of the GNU General Public License version
1951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * 2 along with this work; if not, write to the Free Software Foundation,
2051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski *
2251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * or visit www.oracle.com if you need additional information or have any
2451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * questions.
2551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski */
2651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
2751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskipackage sun.util;
2851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
2951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.security.AccessController;
3051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.security.PrivilegedActionException;
3151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.security.PrivilegedExceptionAction;
3251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.ArrayList;
3351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.HashSet;
3451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.IllformedLocaleException;
3551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.LinkedHashSet;
3651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.List;
3751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.Locale;
3851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.Locale.Builder;
3951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.Map;
4051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.ResourceBundle.Control;
4151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.ServiceLoader;
4251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.Set;
4351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.concurrent.ConcurrentHashMap;
4451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.concurrent.ConcurrentMap;
4551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.spi.LocaleServiceProvider;
469c853c5b9ebbb0ef60a013ae10ee411d70dfa832Piotr Jastrzebskiimport libcore.icu.ICU;
4751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
4851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport sun.util.logging.PlatformLogger;
4951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport sun.util.resources.OpenListResourceBundle;
5051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
5151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski/**
5251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * An instance of this class holds a set of the third party implementations of a particular
5351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * locale sensitive service, such as {@link java.util.spi.LocaleNameProvider}.
5451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski *
5551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski */
5651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskipublic final class LocaleServiceProviderPool {
5751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
5851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
5951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * A Map that holds singleton instances of this class.  Each instance holds a
6051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * set of provider implementations of a particular locale sensitive service.
6151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
6251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProviderPool> poolOfPools =
6351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        new ConcurrentHashMap<>();
6451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
6551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
6651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * A Set containing locale service providers that implement the
6751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * specified provider SPI
6851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
6951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private Set<LocaleServiceProvider> providers =
7051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        new LinkedHashSet<LocaleServiceProvider>();
7151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
7251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
7351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * A Map that retains Locale->provider mapping
7451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
7551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private Map<Locale, LocaleServiceProvider> providersCache =
7651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        new ConcurrentHashMap<Locale, LocaleServiceProvider>();
7751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
7851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
7951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Available locales for this locale sensitive service.  This also contains
8051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * JRE's available locales
8151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
8251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private Set<Locale> availableLocales = null;
8351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
8451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
8551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Available locales within this JRE.  Currently this is declared as
8651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * static.  This could be non-static later, so that they could have
8751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * different sets for each locale sensitive services.
8851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
8951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static volatile List<Locale> availableJRELocales = null;
9051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
9151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
9251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Provider locales for this locale sensitive service.
9351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
9451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private Set<Locale> providerLocales = null;
9551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
9651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
9751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Special locale for ja_JP with Japanese calendar
9851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
9951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static Locale locale_ja_JP_JP = new Locale("ja", "JP", "JP");
10051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
10151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
10251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Special locale for th_TH with Thai numbering system
10351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
10451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static Locale locale_th_TH_TH = new Locale("th", "TH", "TH");
10551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
10651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
10751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * A factory method that returns a singleton instance
10851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
10951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    public static LocaleServiceProviderPool getPool(Class<? extends LocaleServiceProvider> providerClass) {
11051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        LocaleServiceProviderPool pool = poolOfPools.get(providerClass);
11151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (pool == null) {
11251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            LocaleServiceProviderPool newPool =
11351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                new LocaleServiceProviderPool(providerClass);
11451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            pool = poolOfPools.putIfAbsent(providerClass, newPool);
11551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (pool == null) {
11651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                pool = newPool;
11751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
11851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
11951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
12051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return pool;
12151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
12251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
12351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
12451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * The sole constructor.
12551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
12651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param c class of the locale sensitive service
12751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
12851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private LocaleServiceProviderPool (final Class<? extends LocaleServiceProvider> c) {
12951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        try {
13051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
13151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                public Object run() {
13251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    for (LocaleServiceProvider provider : ServiceLoader.loadInstalled(c)) {
13351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        providers.add(provider);
13451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    }
13551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    return null;
13651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
13751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            });
13851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }  catch (PrivilegedActionException e) {
13951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            config(e.toString());
14051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
14151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
14251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
14351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static void config(String message) {
14451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        PlatformLogger logger = PlatformLogger.getLogger("sun.util.LocaleServiceProviderPool");
14551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        logger.config(message);
14651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
14751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
14851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
14951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Lazy loaded set of available locales.
15051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Loading all locales is a very long operation.
15151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
15251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * We know "providerClasses" contains classes that extends LocaleServiceProvider,
15351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * but generic array creation is not allowed, thus the "unchecked" warning
15451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * is suppressed here.
15551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
15651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static class AllAvailableLocales {
15751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        /**
15851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski         * Available locales for all locale sensitive services.
15951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski         * This also contains JRE's available locales
16051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski         */
16151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        static final Locale[] allAvailableLocales;
16251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
16351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        static {
16451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            @SuppressWarnings("unchecked")
16551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            Class<LocaleServiceProvider>[] providerClasses =
16651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        (Class<LocaleServiceProvider>[]) new Class<?>[] {
16751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                java.text.spi.BreakIteratorProvider.class,
16851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                java.text.spi.CollatorProvider.class,
16951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                java.text.spi.DateFormatProvider.class,
17051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                java.text.spi.DateFormatSymbolsProvider.class,
17151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                java.text.spi.DecimalFormatSymbolsProvider.class,
17251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                java.text.spi.NumberFormatProvider.class,
173983b2c6ff9ea6d35adf7ab6398dccf870b7e180aPiotr Jastrzebski                java.util.spi.CurrencyNameProvider.class,
17451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                java.util.spi.LocaleNameProvider.class,
1759c853c5b9ebbb0ef60a013ae10ee411d70dfa832Piotr Jastrzebski                java.util.spi.TimeZoneNameProvider.class,
1769c853c5b9ebbb0ef60a013ae10ee411d70dfa832Piotr Jastrzebski            };
17751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
17851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            // Normalize locales for look up
1799c853c5b9ebbb0ef60a013ae10ee411d70dfa832Piotr Jastrzebski            Locale[] allLocales = ICU.getAvailableLocales();
18051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            Set<Locale> all = new HashSet<Locale>(allLocales.length);
18151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            for (Locale locale : allLocales) {
18251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                all.add(getLookupLocale(locale));
18351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
18451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
18551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            for (Class<LocaleServiceProvider> providerClass : providerClasses) {
18651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                LocaleServiceProviderPool pool =
18751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    LocaleServiceProviderPool.getPool(providerClass);
18851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                all.addAll(pool.getProviderLocales());
18951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
19051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
19151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            allAvailableLocales = all.toArray(new Locale[0]);
19251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
19351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
19451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
19551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
19651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Returns an array of available locales for all the provider classes.
19751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * This array is a merged array of all the locales that are provided by each
19851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * provider, including the JRE.
19951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
20051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @return an array of the available locales for all provider classes
20151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
20251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    public static Locale[] getAllAvailableLocales() {
20351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return AllAvailableLocales.allAvailableLocales.clone();
20451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
20551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
20651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
20751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Returns an array of available locales.  This array is a
20851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * merged array of all the locales that are provided by each
20951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * provider, including the JRE.
21051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
21151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @return an array of the available locales
21251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
21351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    public synchronized Locale[] getAvailableLocales() {
21451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (availableLocales == null) {
21551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            availableLocales = new HashSet<Locale>(getJRELocales());
21651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (hasProviders()) {
21751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                availableLocales.addAll(getProviderLocales());
21851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
21951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
22051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        Locale[] tmp = new Locale[availableLocales.size()];
22151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        availableLocales.toArray(tmp);
22251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return tmp;
22351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
22451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
22551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
22651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Returns an array of available locales (already normalized
22751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * for service lookup) from providers.
22851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Note that this method does not return a defensive copy.
22951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
23051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @return list of the provider locales
23151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
23251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private synchronized Set<Locale> getProviderLocales() {
23351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (providerLocales == null) {
23451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            providerLocales = new HashSet<Locale>();
23551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (hasProviders()) {
23651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                for (LocaleServiceProvider lsp : providers) {
23751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    Locale[] locales = lsp.getAvailableLocales();
23851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    for (Locale locale: locales) {
23951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        providerLocales.add(getLookupLocale(locale));
24051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    }
24151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
24251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
24351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
24451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return providerLocales;
24551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
24651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
24751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
24851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Returns whether any provider for this locale sensitive
24951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * service is available or not.
25051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
25151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @return true if any provider is available
25251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
25351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    public boolean hasProviders() {
25451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return !providers.isEmpty();
25551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
25651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
25751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
25851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Returns an array of available locales (already normalized for
25951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * service lookup) supported by the JRE.
26051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Note that this method does not return a defensive copy.
26151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
26251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @return list of the available JRE locales
26351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
26451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private List<Locale> getJRELocales() {
26551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (availableJRELocales == null) {
26651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            synchronized (LocaleServiceProviderPool.class) {
26751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (availableJRELocales == null) {
2689c853c5b9ebbb0ef60a013ae10ee411d70dfa832Piotr Jastrzebski                    Locale[] allLocales = ICU.getAvailableLocales();
26951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    List<Locale> tmpList = new ArrayList<>(allLocales.length);
27051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    for (Locale locale : allLocales) {
27151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        tmpList.add(getLookupLocale(locale));
27251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    }
27351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    availableJRELocales = tmpList;
27451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
27551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
27651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
27751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return availableJRELocales;
27851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
27951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
28051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
28151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Returns whether the given locale is supported by the JRE.
28251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
28351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param locale the locale to test.
28451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @return true, if the locale is supported by the JRE. false
28551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *     otherwise.
28651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
28751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private boolean isJRESupported(Locale locale) {
28851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        List<Locale> locales = getJRELocales();
28951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return locales.contains(getLookupLocale(locale));
29051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
29151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
29251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
29351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Returns the provider's localized object for the specified
29451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * locale.
29551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
29651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param getter an object on which getObject() method
29751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *     is called to obtain the provider's instance.
29851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param locale the given locale that is used as the starting one
29951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param params provider specific parameters
30051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @return provider's instance, or null.
30151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
30251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    public <P, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
30351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     Locale locale,
30451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     Object... params) {
30551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return getLocalizedObjectImpl(getter, locale, true, null, null, null, params);
30651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
30751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
30851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
30951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Returns the provider's localized name for the specified
31051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * locale.
31151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
31251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param getter an object on which getObject() method
31351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *     is called to obtain the provider's instance.
31451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param locale the given locale that is used as the starting one
31551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param bundle JRE resource bundle that contains
31651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *     the localized names, or null for localized objects.
31751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param key the key string if bundle is supplied, otherwise null.
31851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param params provider specific parameters
31951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @return provider's instance, or null.
32051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
32151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    public <P, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
32251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     Locale locale,
32351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     OpenListResourceBundle bundle,
32451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     String key,
32551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     Object... params) {
32651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return getLocalizedObjectImpl(getter, locale, false, null, bundle, key, params);
32751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
32851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
32951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
33051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Returns the provider's localized name for the specified
33151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * locale.
33251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
33351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param getter an object on which getObject() method
33451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *     is called to obtain the provider's instance.
33551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param locale the given locale that is used as the starting one
33651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param bundleKey JRE specific bundle key. e.g., "USD" is for currency
33751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski           symbol and "usd" is for currency display name in the JRE bundle.
33851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param bundle JRE resource bundle that contains
33951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *     the localized names, or null for localized objects.
34051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param key the key string if bundle is supplied, otherwise null.
34151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param params provider specific parameters
34251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @return provider's instance, or null.
34351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
34451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    public <P, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
34551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     Locale locale,
34651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     String bundleKey,
34751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     OpenListResourceBundle bundle,
34851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     String key,
34951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     Object... params) {
35051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return getLocalizedObjectImpl(getter, locale, false, bundleKey, bundle, key, params);
35151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
35251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
35351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private <P, S> S getLocalizedObjectImpl(LocalizedObjectGetter<P, S> getter,
35451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     Locale locale,
35551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     boolean isObjectProvider,
35651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     String bundleKey,
35751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     OpenListResourceBundle bundle,
35851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     String key,
35951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     Object... params) {
36051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (hasProviders()) {
36151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (bundleKey == null) {
36251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                bundleKey = key;
36351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
36451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            Locale bundleLocale = (bundle != null ? bundle.getLocale() : null);
36551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            List<Locale> lookupLocales = getLookupLocales(locale);
36651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            S providersObj = null;
36751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
36851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            // check whether a provider has an implementation that's closer
36951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            // to the requested locale than the bundle we've found (for
37051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            // localized names), or Java runtime's supported locale
37151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            // (for localized objects)
37251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            Set<Locale> provLoc = getProviderLocales();
37351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            for (int i = 0; i < lookupLocales.size(); i++) {
37451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                Locale current = lookupLocales.get(i);
37551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (bundleLocale != null) {
37651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    if (current.equals(bundleLocale)) {
37751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        break;
37851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    }
37951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                } else {
38051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    if (isJRESupported(current)) {
38151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        break;
38251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    }
38351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
38451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (provLoc.contains(current)) {
38551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    // It is safe to assume that findProvider() returns the instance of type P.
38651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    @SuppressWarnings("unchecked")
38751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    P lsp = (P)findProvider(current);
38851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    if (lsp != null) {
38951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        providersObj = getter.getObject(lsp, locale, key, params);
39051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        if (providersObj != null) {
39151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            return providersObj;
39251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        } else if (isObjectProvider) {
39351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            config(
39451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                "A locale sensitive service provider returned null for a localized objects,  which should not happen.  provider: " + lsp + " locale: " + locale);
39551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        }
39651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    }
39751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
39851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
39951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
40051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            // look up the JRE bundle and its parent chain.  Only
40151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            // providers for localized names are checked hereafter.
40251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            while (bundle != null) {
40351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                bundleLocale = bundle.getLocale();
40451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
40551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (bundle.handleGetKeys().contains(bundleKey)) {
40651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    // JRE has it.
40751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    return null;
40851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                } else {
40951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    // It is safe to assume that findProvider() returns the instance of type P.
41051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    @SuppressWarnings("unchecked")
41151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    P lsp = (P)findProvider(bundleLocale);
41251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    if (lsp != null) {
41351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        providersObj = getter.getObject(lsp, locale, key, params);
41451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        if (providersObj != null) {
41551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            return providersObj;
41651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        }
41751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    }
41851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
41951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
42051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                // try parent bundle
42151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                bundle = bundle.getParent();
42251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
42351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
42451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
42551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // not found.
42651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return null;
42751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
42851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
42951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
43051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Returns a locale service provider instance that supports
43151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * the specified locale.
43251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
43351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param locale the given locale
43451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @return the provider, or null if there is
43551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *     no provider available.
43651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
43751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private LocaleServiceProvider findProvider(Locale locale) {
43851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (!hasProviders()) {
43951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            return null;
44051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
44151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
44251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (providersCache.containsKey(locale)) {
44351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            LocaleServiceProvider provider = providersCache.get(locale);
44451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (provider != NullProvider.INSTANCE) {
44551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                return provider;
44651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
44751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        } else {
44851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            for (LocaleServiceProvider lsp : providers) {
44951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                Locale[] locales = lsp.getAvailableLocales();
45051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                for (Locale available: locales) {
45151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    // normalize
45251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    available = getLookupLocale(available);
45351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    if (locale.equals(available)) {
45451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        LocaleServiceProvider providerInCache =
45551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            providersCache.put(locale, lsp);
45651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        return (providerInCache != null ?
45751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                providerInCache :
45851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                lsp);
45951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    }
46051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
46151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
46251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            providersCache.put(locale, NullProvider.INSTANCE);
46351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
46451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return null;
46551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
46651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
46751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
46851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Returns a list of candidate locales for service look up.
46951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param locale the input locale
47051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @return the list of candiate locales for the given locale
47151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
47251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static List<Locale> getLookupLocales(Locale locale) {
47351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // Note: We currently use the default implementation of
47451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // ResourceBundle.Control.getCandidateLocales. The result
47551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // returned by getCandidateLocales are already normalized
47651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // (no extensions) for service look up.
47751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        List<Locale> lookupLocales = new Control(){}.getCandidateLocales("", locale);
47851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return lookupLocales;
47951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
48051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
48151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
48251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Returns an instance of Locale used for service look up.
48351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * The result Locale has no extensions except for ja_JP_JP
48451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * and th_TH_TH
48551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
48651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @param locale the locale
48751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @return the locale used for service look up
48851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
48951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static Locale getLookupLocale(Locale locale) {
49051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        Locale lookupLocale = locale;
49151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        Set<Character> extensions = locale.getExtensionKeys();
49251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (!extensions.isEmpty()
49351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                && !locale.equals(locale_ja_JP_JP)
49451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                && !locale.equals(locale_th_TH_TH)) {
49551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            // remove extensions
49651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            Builder locbld = new Builder();
49751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            try {
49851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                locbld.setLocale(locale);
49951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                locbld.clearExtensions();
50051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                lookupLocale = locbld.build();
50151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            } catch (IllformedLocaleException e) {
50251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                // A Locale with non-empty extensions
50351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                // should have well-formed fields except
50451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                // for ja_JP_JP and th_TH_TH. Therefore,
50551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                // it should never enter in this catch clause.
50651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                config("A locale(" + locale + ") has non-empty extensions, but has illformed fields.");
50751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
50851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                // Fallback - script field will be lost.
50951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                lookupLocale = new Locale(locale.getLanguage(), locale.getCountry(), locale.getVariant());
51051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
51151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
51251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return lookupLocale;
51351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
51451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
51551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
51651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * A dummy locale service provider that indicates there is no
51751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * provider available
51851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
51951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static class NullProvider extends LocaleServiceProvider {
52051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        private static final NullProvider INSTANCE = new NullProvider();
52151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
52251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        public Locale[] getAvailableLocales() {
52351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            throw new RuntimeException("Should not get called.");
52451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
52551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
52651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
52751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
52851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * An interface to get a localized object for each locale sensitve
52951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * service class.
53051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
53151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    public interface LocalizedObjectGetter<P, S> {
53251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        /**
53351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski         * Returns an object from the provider
53451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski         *
53551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski         * @param lsp the provider
53651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski         * @param locale the locale
53751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski         * @param key key string to localize, or null if the provider is not
53851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski         *     a name provider
53951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski         * @param params provider specific params
54051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski         * @return localized object from the provider
54151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski         */
54251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        public S getObject(P lsp,
54351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                Locale locale,
54451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                String key,
54551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                Object... params);
54651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
54751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski}
548