jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java
changeset 25859 3317bb8137f4
parent 23010 6dadb192ad81
child 25996 4aa9eb909760
equal deleted inserted replaced
25858:836adbf7a2cd 25859:3317bb8137f4
       
     1 /*
       
     2  * Copyright (c) 2012, 2013, 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.security.AccessController;
       
    29 import java.text.spi.BreakIteratorProvider;
       
    30 import java.text.spi.CollatorProvider;
       
    31 import java.text.spi.DateFormatProvider;
       
    32 import java.text.spi.DateFormatSymbolsProvider;
       
    33 import java.text.spi.DecimalFormatSymbolsProvider;
       
    34 import java.text.spi.NumberFormatProvider;
       
    35 import java.util.ArrayList;
       
    36 import java.util.Collections;
       
    37 import java.util.List;
       
    38 import java.util.Locale;
       
    39 import java.util.ResourceBundle;
       
    40 import java.util.Set;
       
    41 import java.util.concurrent.ConcurrentHashMap;
       
    42 import java.util.concurrent.ConcurrentMap;
       
    43 import java.util.spi.CalendarDataProvider;
       
    44 import java.util.spi.CalendarNameProvider;
       
    45 import java.util.spi.CurrencyNameProvider;
       
    46 import java.util.spi.LocaleNameProvider;
       
    47 import java.util.spi.LocaleServiceProvider;
       
    48 import java.util.spi.TimeZoneNameProvider;
       
    49 import sun.util.cldr.CLDRLocaleProviderAdapter;
       
    50 import sun.util.spi.CalendarProvider;
       
    51 
       
    52 /**
       
    53  * The LocaleProviderAdapter abstract class.
       
    54  *
       
    55  * @author Naoto Sato
       
    56  * @author Masayoshi Okutsu
       
    57  */
       
    58 public abstract class LocaleProviderAdapter {
       
    59     /**
       
    60      * Adapter type.
       
    61      */
       
    62     public static enum Type {
       
    63         JRE("sun.util.resources", "sun.text.resources"),
       
    64         CLDR("sun.util.resources.cldr", "sun.text.resources.cldr"),
       
    65         SPI,
       
    66         HOST,
       
    67         FALLBACK("sun.util.resources", "sun.text.resources");
       
    68 
       
    69         private final String UTIL_RESOURCES_PACKAGE;
       
    70         private final String TEXT_RESOURCES_PACKAGE;
       
    71 
       
    72         private Type() {
       
    73             this(null, null);
       
    74         }
       
    75 
       
    76         private Type(String util, String text) {
       
    77             UTIL_RESOURCES_PACKAGE = util;
       
    78             TEXT_RESOURCES_PACKAGE = text;
       
    79         }
       
    80 
       
    81         public String getUtilResourcesPackage() {
       
    82             return UTIL_RESOURCES_PACKAGE;
       
    83         }
       
    84 
       
    85         public String getTextResourcesPackage() {
       
    86             return TEXT_RESOURCES_PACKAGE;
       
    87         }
       
    88     }
       
    89 
       
    90     /**
       
    91      * LocaleProviderAdapter preference list. The default list is intended
       
    92      * to behave the same manner in JDK7.
       
    93      */
       
    94     private static final List<Type> adapterPreference;
       
    95 
       
    96     /**
       
    97      * JRE Locale Data Adapter instance
       
    98      */
       
    99     private static LocaleProviderAdapter jreLocaleProviderAdapter = new JRELocaleProviderAdapter();
       
   100 
       
   101     /**
       
   102      * SPI Locale Data Adapter instance
       
   103      */
       
   104     private static LocaleProviderAdapter spiLocaleProviderAdapter = new SPILocaleProviderAdapter();
       
   105 
       
   106     /**
       
   107      * CLDR Locale Data Adapter instance, if any.
       
   108      */
       
   109     private static LocaleProviderAdapter cldrLocaleProviderAdapter = null;
       
   110 
       
   111     /**
       
   112      * HOST Locale Data Adapter instance, if any.
       
   113      */
       
   114     private static LocaleProviderAdapter hostLocaleProviderAdapter = null;
       
   115 
       
   116     /**
       
   117      * FALLBACK Locale Data Adapter instance. It's basically the same with JRE, but only kicks
       
   118      * in for the root locale.
       
   119      */
       
   120     private static LocaleProviderAdapter fallbackLocaleProviderAdapter = null;
       
   121 
       
   122     /**
       
   123      * Default fallback adapter type, which should return something meaningful in any case.
       
   124      * This is either JRE or FALLBACK.
       
   125      */
       
   126     static LocaleProviderAdapter.Type defaultLocaleProviderAdapter = null;
       
   127 
       
   128     /**
       
   129      * Adapter lookup cache.
       
   130      */
       
   131     private static ConcurrentMap<Class<? extends LocaleServiceProvider>, ConcurrentMap<Locale, LocaleProviderAdapter>>
       
   132         adapterCache = new ConcurrentHashMap<>();
       
   133 
       
   134     static {
       
   135         String order = AccessController.doPrivileged(
       
   136                            new sun.security.action.GetPropertyAction("java.locale.providers"));
       
   137         List<Type> typeList = new ArrayList<>();
       
   138 
       
   139         // Check user specified adapter preference
       
   140         if (order != null && order.length() != 0) {
       
   141             String[] types = order.split(",");
       
   142             for (String type : types) {
       
   143                 try {
       
   144                     Type aType = Type.valueOf(type.trim().toUpperCase(Locale.ROOT));
       
   145 
       
   146                     // load adapter if necessary
       
   147                     switch (aType) {
       
   148                         case CLDR:
       
   149                             if (cldrLocaleProviderAdapter == null) {
       
   150                                 cldrLocaleProviderAdapter = new CLDRLocaleProviderAdapter();
       
   151                             }
       
   152                             break;
       
   153                         case HOST:
       
   154                             if (hostLocaleProviderAdapter == null) {
       
   155                                 hostLocaleProviderAdapter = new HostLocaleProviderAdapter();
       
   156                             }
       
   157                             break;
       
   158                     }
       
   159                     if (!typeList.contains(aType)) {
       
   160                         typeList.add(aType);
       
   161                     }
       
   162                 } catch (IllegalArgumentException | UnsupportedOperationException e) {
       
   163                     // could be caused by the user specifying wrong
       
   164                     // provider name or format in the system property
       
   165                     LocaleServiceProviderPool.config(LocaleProviderAdapter.class, e.toString());
       
   166                 }
       
   167             }
       
   168         }
       
   169 
       
   170         if (!typeList.isEmpty()) {
       
   171             if (!typeList.contains(Type.JRE)) {
       
   172                 // Append FALLBACK as the last resort.
       
   173                 fallbackLocaleProviderAdapter = new FallbackLocaleProviderAdapter();
       
   174                 typeList.add(Type.FALLBACK);
       
   175                 defaultLocaleProviderAdapter = Type.FALLBACK;
       
   176             } else {
       
   177                 defaultLocaleProviderAdapter = Type.JRE;
       
   178             }
       
   179         } else {
       
   180             // Default preference list
       
   181             typeList.add(Type.JRE);
       
   182             typeList.add(Type.SPI);
       
   183             defaultLocaleProviderAdapter = Type.JRE;
       
   184         }
       
   185 
       
   186         adapterPreference = Collections.unmodifiableList(typeList);
       
   187     }
       
   188 
       
   189     /**
       
   190      * Returns the singleton instance for each adapter type
       
   191      */
       
   192     public static LocaleProviderAdapter forType(Type type) {
       
   193         switch (type) {
       
   194         case JRE:
       
   195             return jreLocaleProviderAdapter;
       
   196         case CLDR:
       
   197             return cldrLocaleProviderAdapter;
       
   198         case SPI:
       
   199             return spiLocaleProviderAdapter;
       
   200         case HOST:
       
   201             return hostLocaleProviderAdapter;
       
   202         case FALLBACK:
       
   203             return fallbackLocaleProviderAdapter;
       
   204         default:
       
   205             throw new InternalError("unknown locale data adapter type");
       
   206         }
       
   207     }
       
   208 
       
   209     public static LocaleProviderAdapter forJRE() {
       
   210         return jreLocaleProviderAdapter;
       
   211     }
       
   212 
       
   213     public static LocaleProviderAdapter getResourceBundleBased() {
       
   214         for (Type type : getAdapterPreference()) {
       
   215             if (type == Type.JRE || type == Type.CLDR || type == Type.FALLBACK) {
       
   216                 return forType(type);
       
   217             }
       
   218         }
       
   219         // Shouldn't happen.
       
   220         throw new InternalError();
       
   221     }
       
   222     /**
       
   223      * Returns the preference order of LocaleProviderAdapter.Type
       
   224      */
       
   225     public static List<Type> getAdapterPreference() {
       
   226         return adapterPreference;
       
   227     }
       
   228 
       
   229     /**
       
   230      * Returns a LocaleProviderAdapter for the given locale service provider that
       
   231      * best matches the given locale. This method returns the LocaleProviderAdapter
       
   232      * for JRE if none is found for the given locale.
       
   233      *
       
   234      * @param providerClass the class for the locale service provider
       
   235      * @param locale the desired locale.
       
   236      * @return a LocaleProviderAdapter
       
   237      */
       
   238     public static LocaleProviderAdapter getAdapter(Class<? extends LocaleServiceProvider> providerClass,
       
   239                                                Locale locale) {
       
   240         LocaleProviderAdapter adapter;
       
   241 
       
   242         // cache lookup
       
   243         ConcurrentMap<Locale, LocaleProviderAdapter> adapterMap = adapterCache.get(providerClass);
       
   244         if (adapterMap != null) {
       
   245             if ((adapter = adapterMap.get(locale)) != null) {
       
   246                 return adapter;
       
   247             }
       
   248         } else {
       
   249             adapterMap = new ConcurrentHashMap<>();
       
   250             adapterCache.putIfAbsent(providerClass, adapterMap);
       
   251         }
       
   252 
       
   253         // Fast look-up for the given locale
       
   254         adapter = findAdapter(providerClass, locale);
       
   255         if (adapter != null) {
       
   256             adapterMap.putIfAbsent(locale, adapter);
       
   257             return adapter;
       
   258         }
       
   259 
       
   260         // Try finding an adapter in the normal candidate locales path of the given locale.
       
   261         List<Locale> lookupLocales = ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT)
       
   262                                         .getCandidateLocales("", locale);
       
   263         for (Locale loc : lookupLocales) {
       
   264             if (loc.equals(locale)) {
       
   265                 // We've already done with this loc.
       
   266                 continue;
       
   267             }
       
   268             adapter = findAdapter(providerClass, loc);
       
   269             if (adapter != null) {
       
   270                 adapterMap.putIfAbsent(locale, adapter);
       
   271                 return adapter;
       
   272             }
       
   273         }
       
   274 
       
   275         // returns the adapter for FALLBACK as the last resort
       
   276         adapterMap.putIfAbsent(locale, fallbackLocaleProviderAdapter);
       
   277         return fallbackLocaleProviderAdapter;
       
   278     }
       
   279 
       
   280     private static LocaleProviderAdapter findAdapter(Class<? extends LocaleServiceProvider> providerClass,
       
   281                                                  Locale locale) {
       
   282         for (Type type : getAdapterPreference()) {
       
   283             LocaleProviderAdapter adapter = forType(type);
       
   284             LocaleServiceProvider provider = adapter.getLocaleServiceProvider(providerClass);
       
   285             if (provider != null) {
       
   286                 if (provider.isSupportedLocale(locale)) {
       
   287                     return adapter;
       
   288                 }
       
   289             }
       
   290         }
       
   291         return null;
       
   292     }
       
   293 
       
   294     /**
       
   295      * A utility method for implementing the default LocaleServiceProvider.isSupportedLocale
       
   296      * for the JRE, CLDR, and FALLBACK adapters.
       
   297      */
       
   298     static boolean isSupportedLocale(Locale locale, LocaleProviderAdapter.Type type, Set<String> langtags) {
       
   299         assert type == Type.JRE || type == Type.CLDR || type == Type.FALLBACK;
       
   300         if (Locale.ROOT.equals(locale)) {
       
   301             return true;
       
   302         }
       
   303 
       
   304         if (type == Type.FALLBACK) {
       
   305             // no other locales except ROOT are supported for FALLBACK
       
   306             return false;
       
   307         }
       
   308 
       
   309         locale = locale.stripExtensions();
       
   310         if (langtags.contains(locale.toLanguageTag())) {
       
   311             return true;
       
   312         }
       
   313         if (type == Type.JRE) {
       
   314             String oldname = locale.toString().replace('_', '-');
       
   315             return langtags.contains(oldname) ||
       
   316                    "ja-JP-JP".equals(oldname) ||
       
   317                    "th-TH-TH".equals(oldname) ||
       
   318                    "no-NO-NY".equals(oldname);
       
   319         }
       
   320         return false;
       
   321     }
       
   322 
       
   323     public static Locale[] toLocaleArray(Set<String> tags) {
       
   324         Locale[] locs = new Locale[tags.size() + 1];
       
   325         int index = 0;
       
   326         locs[index++] = Locale.ROOT;
       
   327         for (String tag : tags) {
       
   328             switch (tag) {
       
   329             case "ja-JP-JP":
       
   330                 locs[index++] = JRELocaleConstants.JA_JP_JP;
       
   331                 break;
       
   332             case "th-TH-TH":
       
   333                 locs[index++] = JRELocaleConstants.TH_TH_TH;
       
   334                 break;
       
   335             default:
       
   336                 locs[index++] = Locale.forLanguageTag(tag);
       
   337                 break;
       
   338             }
       
   339         }
       
   340         return locs;
       
   341     }
       
   342 
       
   343     /**
       
   344      * Returns the type of this LocaleProviderAdapter
       
   345      */
       
   346     public abstract LocaleProviderAdapter.Type getAdapterType();
       
   347 
       
   348     /**
       
   349      * Getter method for Locale Service Providers.
       
   350      */
       
   351     public abstract <P extends LocaleServiceProvider> P getLocaleServiceProvider(Class<P> c);
       
   352 
       
   353     /**
       
   354      * Returns a BreakIteratorProvider for this LocaleProviderAdapter, or null if no
       
   355      * BreakIteratorProvider is available.
       
   356      *
       
   357      * @return a BreakIteratorProvider
       
   358      */
       
   359     public abstract BreakIteratorProvider getBreakIteratorProvider();
       
   360 
       
   361     /**
       
   362      * Returns a ollatorProvider for this LocaleProviderAdapter, or null if no
       
   363      * ollatorProvider is available.
       
   364      *
       
   365      * @return a ollatorProvider
       
   366      */
       
   367     public abstract CollatorProvider getCollatorProvider();
       
   368 
       
   369     /**
       
   370      * Returns a DateFormatProvider for this LocaleProviderAdapter, or null if no
       
   371      * DateFormatProvider is available.
       
   372      *
       
   373      * @return a DateFormatProvider
       
   374      */
       
   375     public abstract DateFormatProvider getDateFormatProvider();
       
   376 
       
   377     /**
       
   378      * Returns a DateFormatSymbolsProvider for this LocaleProviderAdapter, or null if no
       
   379      * DateFormatSymbolsProvider is available.
       
   380      *
       
   381      * @return a DateFormatSymbolsProvider
       
   382      */
       
   383     public abstract DateFormatSymbolsProvider getDateFormatSymbolsProvider();
       
   384 
       
   385     /**
       
   386      * Returns a DecimalFormatSymbolsProvider for this LocaleProviderAdapter, or null if no
       
   387      * DecimalFormatSymbolsProvider is available.
       
   388      *
       
   389      * @return a DecimalFormatSymbolsProvider
       
   390      */
       
   391     public abstract DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider();
       
   392 
       
   393     /**
       
   394      * Returns a NumberFormatProvider for this LocaleProviderAdapter, or null if no
       
   395      * NumberFormatProvider is available.
       
   396      *
       
   397      * @return a NumberFormatProvider
       
   398      */
       
   399     public abstract NumberFormatProvider getNumberFormatProvider();
       
   400 
       
   401     /*
       
   402      * Getter methods for java.util.spi.* providers
       
   403      */
       
   404 
       
   405     /**
       
   406      * Returns a CurrencyNameProvider for this LocaleProviderAdapter, or null if no
       
   407      * CurrencyNameProvider is available.
       
   408      *
       
   409      * @return a CurrencyNameProvider
       
   410      */
       
   411     public abstract CurrencyNameProvider getCurrencyNameProvider();
       
   412 
       
   413     /**
       
   414      * Returns a LocaleNameProvider for this LocaleProviderAdapter, or null if no
       
   415      * LocaleNameProvider is available.
       
   416      *
       
   417      * @return a LocaleNameProvider
       
   418      */
       
   419     public abstract LocaleNameProvider getLocaleNameProvider();
       
   420 
       
   421     /**
       
   422      * Returns a TimeZoneNameProvider for this LocaleProviderAdapter, or null if no
       
   423      * TimeZoneNameProvider is available.
       
   424      *
       
   425      * @return a TimeZoneNameProvider
       
   426      */
       
   427     public abstract TimeZoneNameProvider getTimeZoneNameProvider();
       
   428 
       
   429     /**
       
   430      * Returns a CalendarDataProvider for this LocaleProviderAdapter, or null if no
       
   431      * CalendarDataProvider is available.
       
   432      *
       
   433      * @return a CalendarDataProvider
       
   434      */
       
   435     public abstract CalendarDataProvider getCalendarDataProvider();
       
   436 
       
   437     /**
       
   438      * Returns a CalendarNameProvider for this LocaleProviderAdapter, or null if no
       
   439      * CalendarNameProvider is available.
       
   440      *
       
   441      * @return a CalendarNameProvider
       
   442      */
       
   443     public abstract CalendarNameProvider getCalendarNameProvider();
       
   444 
       
   445     /**
       
   446      * Returns a CalendarProvider for this LocaleProviderAdapter, or null if no
       
   447      * CalendarProvider is available.
       
   448      *
       
   449      * @return a CalendarProvider
       
   450      */
       
   451     public abstract CalendarProvider getCalendarProvider();
       
   452 
       
   453     public abstract LocaleResources getLocaleResources(Locale locale);
       
   454 
       
   455     public abstract Locale[] getAvailableLocales();
       
   456 }