8136668: Default locale provider adapter incorrectly set to JRE
authornaoto
Thu, 22 Oct 2015 21:41:57 -0700
changeset 33309 3aecb54196d9
parent 33308 e8b39cf05704
child 33310 2701a9854bb5
8136668: Default locale provider adapter incorrectly set to JRE Reviewed-by: okutsu
jdk/make/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java
jdk/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java
jdk/src/java.base/share/classes/sun/util/locale/provider/FallbackLocaleProviderAdapter.java
jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java
jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java
--- a/jdk/make/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java	Thu Oct 22 11:14:00 2015 -0700
+++ b/jdk/make/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java	Thu Oct 22 21:41:57 2015 -0700
@@ -28,6 +28,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.Arrays;
 import java.util.Formatter;
 import java.util.HashSet;
 import java.util.HashMap;
@@ -285,15 +286,16 @@
                         out.printf("        parentLocalesMap.put(Locale.forLanguageTag(\"%s\"),\n",
                                    parentTag);
                     }
-                    String[] childlen = toLocaleList(metaInfo.get(key), true).split(" ");
+                    String[] children = toLocaleList(metaInfo.get(key), true).split(" ");
+                    Arrays.sort(children);
                     out.printf("             new String[] {\n" +
                                "                 ");
                     int count = 0;
-                    for (int i = 0; i < childlen.length; i++) {
-                        String child = childlen[i];
+                    for (int i = 0; i < children.length; i++) {
+                        String child = children[i];
                         out.printf("\"%s\", ", child);
                         count += child.length() + 4;
-                        if (i != childlen.length - 1 && count > 64) {
+                        if (i != children.length - 1 && count > 64) {
                             out.printf("\n                 ");
                             count = 0;
                         }
--- a/jdk/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java	Thu Oct 22 11:14:00 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java	Thu Oct 22 21:41:57 2015 -0700
@@ -26,13 +26,11 @@
 package sun.util.cldr;
 
 import java.security.AccessController;
-import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
 import java.text.spi.BreakIteratorProvider;
 import java.text.spi.CollatorProvider;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
@@ -41,7 +39,7 @@
 import java.util.ServiceLoader;
 import java.util.Set;
 import java.util.StringTokenizer;
-import java.util.stream.Stream;
+import java.util.concurrent.ConcurrentHashMap;
 import sun.util.locale.provider.JRELocaleProviderAdapter;
 import sun.util.locale.provider.LocaleProviderAdapter;
 import sun.util.locale.provider.LocaleDataMetaInfo;
@@ -59,12 +57,21 @@
     private final LocaleDataMetaInfo nonBaseMetaInfo;
 
     // parent locales map
-    private static volatile Map<Locale, Locale> parentLocalesMap = null;
+    private static volatile Map<Locale, Locale> parentLocalesMap;
+    static {
+        parentLocalesMap = new ConcurrentHashMap<>();
+        // Assuming these locales do NOT have irregular parent locales.
+        parentLocalesMap.put(Locale.ROOT, Locale.ROOT);
+        parentLocalesMap.put(Locale.ENGLISH, Locale.ENGLISH);
+        parentLocalesMap.put(Locale.US, Locale.US);
+    }
 
     public CLDRLocaleProviderAdapter() {
+        LocaleDataMetaInfo nbmi = null;
+
         try {
-            nonBaseMetaInfo = AccessController.doPrivileged(new PrivilegedExceptionAction<LocaleDataMetaInfo>() {
-                    @Override
+            nbmi = AccessController.doPrivileged(new PrivilegedExceptionAction<LocaleDataMetaInfo>() {
+                @Override
                 public LocaleDataMetaInfo run() {
                     for (LocaleDataMetaInfo ldmi : ServiceLoader.loadInstalled(LocaleDataMetaInfo.class)) {
                         if (ldmi.getType() == LocaleProviderAdapter.Type.CLDR) {
@@ -72,18 +79,13 @@
                         }
                     }
                     return null;
-                    }
-                });
+                }
+            });
         }  catch (Exception e) {
-            // Catch any exception, and fail gracefully as if CLDR locales do not exist.
-            // It's ok ignore it if something wrong happens because there always is the
-            // JRE or FALLBACK LocaleProviderAdapter that will do the right thing.
-            throw new UnsupportedOperationException(e);
+            // Catch any exception, and continue as if only CLDR's base locales exist.
         }
 
-        if (nonBaseMetaInfo == null) {
-            throw new UnsupportedOperationException("CLDR locale data could not be found.");
-        }
+        nonBaseMetaInfo = nbmi;
     }
 
     /**
@@ -120,7 +122,11 @@
     protected Set<String> createLanguageTagSet(String category) {
         // Directly call Base tags, as we know it's in the base module.
         String supportedLocaleString = baseMetaInfo.availableLanguageTags(category);
-        String nonBaseTags = nonBaseMetaInfo.availableLanguageTags(category);
+        String nonBaseTags = null;
+
+        if (nonBaseMetaInfo != null) {
+            nonBaseTags = nonBaseMetaInfo.availableLanguageTags(category);
+        }
         if (nonBaseTags != null) {
             if (supportedLocaleString != null) {
                 supportedLocaleString += " " + nonBaseTags;
@@ -144,35 +150,51 @@
     public List<Locale> getCandidateLocales(String baseName, Locale locale) {
         List<Locale> candidates = super.getCandidateLocales(baseName, locale);
         return applyParentLocales(baseName, candidates);
-}
+    }
 
     private List<Locale> applyParentLocales(String baseName, List<Locale> candidates) {
-        if (Objects.isNull(parentLocalesMap)) {
-            Map<Locale, Locale> map = new HashMap<>();
-            baseMetaInfo.parentLocales().forEach((parent, children) -> {
-                Stream.of(children).forEach(child -> {
-                    map.put(Locale.forLanguageTag(child), parent);
-                });
-            });
-            parentLocalesMap = Collections.unmodifiableMap(map);
-        }
-
         // check irregular parents
         for (int i = 0; i < candidates.size(); i++) {
             Locale l = candidates.get(i);
-            Locale p = parentLocalesMap.get(l);
-            if (!l.equals(Locale.ROOT) &&
-                Objects.nonNull(p) &&
-                !candidates.get(i+1).equals(p)) {
-                List<Locale> applied = candidates.subList(0, i+1);
-                applied.addAll(applyParentLocales(baseName, super.getCandidateLocales(baseName, p)));
-                return applied;
+            if (!l.equals(Locale.ROOT)) {
+                Locale p = getParentLocale(l);
+                if (p != null &&
+                    !candidates.get(i+1).equals(p)) {
+                    List<Locale> applied = candidates.subList(0, i+1);
+                    applied.addAll(applyParentLocales(baseName, super.getCandidateLocales(baseName, p)));
+                    return applied;
+                }
             }
         }
 
         return candidates;
     }
 
+    private static Locale getParentLocale(Locale locale) {
+        Locale parent = parentLocalesMap.get(locale);
+
+        if (parent == null) {
+            String tag = locale.toLanguageTag();
+            for (Map.Entry<Locale, String[]> entry : baseMetaInfo.parentLocales().entrySet()) {
+                if (Arrays.binarySearch(entry.getValue(), tag) >= 0) {
+                    parent = entry.getKey();
+                    break;
+                }
+            }
+            if (parent == null) {
+                parent = locale; // non existent marker
+            }
+            parentLocalesMap.putIfAbsent(locale, parent);
+        }
+
+        if (locale.equals(parent)) {
+            // means no irregular parent.
+            parent = null;
+        }
+
+        return parent;
+    }
+
     @Override
     public boolean isSupportedProviderLocale(Locale locale, Set<String> langtags) {
         return Locale.ROOT.equals(locale) ||
--- a/jdk/src/java.base/share/classes/sun/util/locale/provider/FallbackLocaleProviderAdapter.java	Thu Oct 22 11:14:00 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/FallbackLocaleProviderAdapter.java	Thu Oct 22 21:41:57 2015 -0700
@@ -70,5 +70,5 @@
     @Override
     public boolean isSupportedProviderLocale(Locale locale, Set<String>langtags) {
         return Locale.ROOT.equals(locale);
+    }
 }
-}
--- a/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java	Thu Oct 22 11:14:00 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java	Thu Oct 22 21:41:57 2015 -0700
@@ -36,6 +36,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.ResourceBundle;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -60,24 +61,30 @@
      * Adapter type.
      */
     public static enum Type {
-        JRE("sun.util.resources", "sun.text.resources"),
-        CLDR("sun.util.resources.cldr", "sun.text.resources.cldr"),
-        SPI,
-        HOST,
-        FALLBACK("sun.util.resources", "sun.text.resources");
+        JRE("sun.util.locale.provider.JRELocaleProviderAdapter", "sun.util.resources", "sun.text.resources"),
+        CLDR("sun.util.cldr.CLDRLocaleProviderAdapter", "sun.util.resources.cldr", "sun.text.resources.cldr"),
+        SPI("sun.util.locale.provider.SPILocaleProviderAdapter"),
+        HOST("sun.util.locale.provider.HostLocaleProviderAdapter"),
+        FALLBACK("sun.util.locale.provider.FallbackLocaleProviderAdapter", "sun.util.resources", "sun.text.resources");
 
+        private final String CLASSNAME;
         private final String UTIL_RESOURCES_PACKAGE;
         private final String TEXT_RESOURCES_PACKAGE;
 
-        private Type() {
-            this(null, null);
+        private Type(String className) {
+            this(className, null, null);
         }
 
-        private Type(String util, String text) {
+        private Type(String className, String util, String text) {
+            CLASSNAME = className;
             UTIL_RESOURCES_PACKAGE = util;
             TEXT_RESOURCES_PACKAGE = text;
         }
 
+        public String getAdapterClassName() {
+            return CLASSNAME;
+        }
+
         public String getUtilResourcesPackage() {
             return UTIL_RESOURCES_PACKAGE;
         }
@@ -93,36 +100,15 @@
     private static final List<Type> adapterPreference;
 
     /**
-     * JRE Locale Data Adapter instance
-     */
-    private static LocaleProviderAdapter jreLocaleProviderAdapter = new JRELocaleProviderAdapter();
-
-    /**
-     * SPI Locale Data Adapter instance
-     */
-    private static LocaleProviderAdapter spiLocaleProviderAdapter = new SPILocaleProviderAdapter();
-
-    /**
-     * CLDR Locale Data Adapter instance, if any.
+     * LocaleProviderAdapter instances
      */
-    private static LocaleProviderAdapter cldrLocaleProviderAdapter = null;
-
-    /**
-     * HOST Locale Data Adapter instance, if any.
-     */
-    private static LocaleProviderAdapter hostLocaleProviderAdapter = null;
-
-    /**
-     * FALLBACK Locale Data Adapter instance. It's basically the same with JRE, but only kicks
-     * in for the root locale.
-     */
-    private static LocaleProviderAdapter fallbackLocaleProviderAdapter = null;
+    private static final Map<Type, LocaleProviderAdapter> adapterInstances = new ConcurrentHashMap<>();
 
     /**
      * Default fallback adapter type, which should return something meaningful in any case.
-     * This is either JRE or FALLBACK.
+     * This is either CLDR or FALLBACK.
      */
-    static LocaleProviderAdapter.Type defaultLocaleProviderAdapter = null;
+    static volatile LocaleProviderAdapter.Type defaultLocaleProviderAdapter = null;
 
     /**
      * Adapter lookup cache.
@@ -141,20 +127,6 @@
             for (String type : types) {
                 try {
                     Type aType = Type.valueOf(type.trim().toUpperCase(Locale.ROOT));
-
-                    // load adapter if necessary
-                    switch (aType) {
-                        case CLDR:
-                            if (cldrLocaleProviderAdapter == null) {
-                                cldrLocaleProviderAdapter = new CLDRLocaleProviderAdapter();
-                            }
-                            break;
-                        case HOST:
-                            if (hostLocaleProviderAdapter == null) {
-                                hostLocaleProviderAdapter = new HostLocaleProviderAdapter();
-                            }
-                            break;
-                    }
                     if (!typeList.contains(aType)) {
                         typeList.add(aType);
                     }
@@ -166,28 +138,19 @@
             }
         }
 
+        defaultLocaleProviderAdapter = Type.CLDR;
         if (!typeList.isEmpty()) {
-            if (!typeList.contains(Type.JRE)) {
+            // bona fide preference exists
+            if (!typeList.contains(Type.CLDR)) {
                 // Append FALLBACK as the last resort.
-                fallbackLocaleProviderAdapter = new FallbackLocaleProviderAdapter();
                 typeList.add(Type.FALLBACK);
                 defaultLocaleProviderAdapter = Type.FALLBACK;
-            } else {
-                defaultLocaleProviderAdapter = Type.JRE;
             }
         } else {
             // Default preference list.
-            try {
-                cldrLocaleProviderAdapter = new CLDRLocaleProviderAdapter();
-                typeList.add(Type.CLDR);
-                defaultLocaleProviderAdapter = Type.CLDR;
-            } catch (UnsupportedOperationException e) {
-                LocaleServiceProviderPool.config(LocaleProviderAdapter.class, e.toString());
-            }
+            typeList.add(Type.CLDR);
             typeList.add(Type.JRE);
-            defaultLocaleProviderAdapter = Type.JRE;
         }
-
         adapterPreference = Collections.unmodifiableList(typeList);
     }
 
@@ -197,22 +160,42 @@
     public static LocaleProviderAdapter forType(Type type) {
         switch (type) {
         case JRE:
-            return jreLocaleProviderAdapter;
         case CLDR:
-            return cldrLocaleProviderAdapter;
         case SPI:
-            return spiLocaleProviderAdapter;
         case HOST:
-            return hostLocaleProviderAdapter;
         case FALLBACK:
-            return fallbackLocaleProviderAdapter;
+            LocaleProviderAdapter adapter = null;
+            LocaleProviderAdapter cached = adapterInstances.get(type);
+            if (cached == null) {
+                try {
+                    // lazily load adapters here
+                    adapter = (LocaleProviderAdapter)Class.forName(type.getAdapterClassName())
+                        .newInstance();
+                    cached = adapterInstances.putIfAbsent(type, adapter);
+                    if (cached != null) {
+                        adapter = cached;
+                    }
+                } catch (ClassNotFoundException |
+                         IllegalAccessException |
+                         InstantiationException |
+                         UnsupportedOperationException e) {
+                    LocaleServiceProviderPool.config(LocaleProviderAdapter.class, e.toString());
+                    adapterInstances.putIfAbsent(type, NONEXISTENT_ADAPTER);
+                    if (defaultLocaleProviderAdapter == type) {
+                        defaultLocaleProviderAdapter = Type.FALLBACK;
+                    }
+                }
+            } else if (cached != NONEXISTENT_ADAPTER) {
+                adapter = cached;
+            }
+            return adapter;
         default:
             throw new InternalError("unknown locale data adapter type");
         }
     }
 
     public static LocaleProviderAdapter forJRE() {
-        return jreLocaleProviderAdapter;
+        return forType(Type.JRE);
     }
 
     public static LocaleProviderAdapter getResourceBundleBased() {
@@ -227,6 +210,7 @@
         // Shouldn't happen.
         throw new InternalError();
     }
+
     /**
      * Returns the preference order of LocaleProviderAdapter.Type
      */
@@ -281,18 +265,20 @@
         }
 
         // returns the adapter for FALLBACK as the last resort
-        adapterMap.putIfAbsent(locale, fallbackLocaleProviderAdapter);
-        return fallbackLocaleProviderAdapter;
+        adapterMap.putIfAbsent(locale, forType(Type.FALLBACK));
+        return forType(Type.FALLBACK);
     }
 
     private static LocaleProviderAdapter findAdapter(Class<? extends LocaleServiceProvider> providerClass,
                                                  Locale locale) {
         for (Type type : getAdapterPreference()) {
             LocaleProviderAdapter adapter = forType(type);
-            LocaleServiceProvider provider = adapter.getLocaleServiceProvider(providerClass);
-            if (provider != null) {
-                if (provider.isSupportedLocale(locale)) {
-                    return adapter;
+            if (adapter != null) {
+                LocaleServiceProvider provider = adapter.getLocaleServiceProvider(providerClass);
+                if (provider != null) {
+                    if (provider.isSupportedLocale(locale)) {
+                        return adapter;
+                    }
                 }
             }
         }
@@ -442,4 +428,14 @@
     public abstract LocaleResources getLocaleResources(Locale locale);
 
     public abstract Locale[] getAvailableLocales();
+
+    private static final LocaleProviderAdapter NONEXISTENT_ADAPTER = new NonExistentAdapter();
+    private static final class NonExistentAdapter extends FallbackLocaleProviderAdapter {
+        @Override
+        public LocaleProviderAdapter.Type getAdapterType() {
+            return null;
+        }
+
+        private NonExistentAdapter() {};
+    }
 }
--- a/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java	Thu Oct 22 11:14:00 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java	Thu Oct 22 21:41:57 2015 -0700
@@ -221,14 +221,13 @@
 
     /**
      * Returns whether any provider for this locale sensitive
-     * service is available or not, excluding JRE's one.
+     * service is available or not, excluding JRE/CLDR's one.
      *
-     * @return true if any provider (other than JRE) is available
+     * @return true if any provider (other than JRE/CLDR) is available
      */
     boolean hasProviders() {
         return providers.size() != 1 ||
-               (providers.get(LocaleProviderAdapter.Type.JRE) == null &&
-                providers.get(LocaleProviderAdapter.Type.FALLBACK) == null);
+               providers.get(LocaleProviderAdapter.defaultLocaleProviderAdapter) == null;
     }
 
     /**
@@ -275,7 +274,7 @@
             throw new NullPointerException();
         }
 
-        // Check whether JRE is the sole locale data provider or not,
+        // Check whether JRE/CLDR is the sole locale data provider or not,
         // and directly call it if it is.
         if (!hasProviders()) {
             return getter.getObject((P)providers.get(LocaleProviderAdapter.defaultLocaleProviderAdapter),