jdk/src/java.base/share/classes/sun/util/resources/LocaleData.java
changeset 36511 9d0388c6b336
parent 36014 3d50784f2dc2
child 36753 5977ba282e9b
--- a/jdk/src/java.base/share/classes/sun/util/resources/LocaleData.java	Tue Mar 15 13:48:26 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/util/resources/LocaleData.java	Thu Mar 17 19:04:16 2016 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -46,14 +46,17 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.MissingResourceException;
 import java.util.ResourceBundle;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.spi.ResourceBundleProvider;
 import sun.util.locale.provider.JRELocaleProviderAdapter;
 import sun.util.locale.provider.LocaleProviderAdapter;
-import sun.util.locale.provider.ResourceBundleBasedAdapter;
 import static sun.util.locale.provider.LocaleProviderAdapter.Type.CLDR;
 import static sun.util.locale.provider.LocaleProviderAdapter.Type.JRE;
+import sun.util.locale.provider.ResourceBundleBasedAdapter;
 
 /**
  * Provides information about and access to resource bundles in the
@@ -65,6 +68,14 @@
  */
 
 public class LocaleData {
+    private static final ResourceBundle.Control defaultControl
+        = ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT);
+
+    private static final String DOTCLDR      = ".cldr";
+
+    // Map of key (base name + locale) to candidates
+    private static final Map<String, List<Locale>> CANDIDATES_MAP = new ConcurrentHashMap<>();
+
     private final LocaleProviderAdapter.Type type;
 
     public LocaleData(LocaleProviderAdapter.Type type) {
@@ -163,8 +174,7 @@
         return AccessController.doPrivileged(new PrivilegedAction<>() {
             @Override
             public ResourceBundle run() {
-                return ResourceBundle
-                        .getBundle(baseName, locale, LocaleDataResourceBundleControl.INSTANCE);
+                return Bundles.of(baseName, locale, LocaleDataStrategy.INSTANCE);
             }
         });
     }
@@ -175,9 +185,8 @@
            public OpenListResourceBundle run() {
                OpenListResourceBundle rb = null;
                try {
-                   rb = (OpenListResourceBundle) ResourceBundle.getBundle(baseName,
-                           locale, SupplementaryResourceBundleControl.INSTANCE);
-
+                   rb = (OpenListResourceBundle) Bundles.of(baseName, locale,
+                                                            SupplementaryStrategy.INSTANCE);
                } catch (MissingResourceException e) {
                    // return null if no supplementary is available
                }
@@ -186,12 +195,51 @@
         });
     }
 
-    private static class LocaleDataResourceBundleControl extends ResourceBundle.Control {
-        /* Singlton instance of ResourceBundle.Control. */
-        private static final LocaleDataResourceBundleControl INSTANCE =
-            new LocaleDataResourceBundleControl();
+    private static abstract class LocaleDataResourceBundleProvider
+                                            implements ResourceBundleProvider {
+        abstract protected boolean isSupportedInModule(String baseName, Locale locale);
+
+        /**
+         * Changes baseName to its module dependent package name and
+         * calls the super class implementation. For example,
+         * if the baseName is "sun.text.resources.FormatData" and locale is ja_JP,
+         * the baseName is changed to "sun.text.resources.ext.FormatData". If
+         * baseName contains ".cldr", such as "sun.text.resources.cldr.FormatData",
+         * the name is changed to "sun.text.resources.cldr.ext.FormatData".
+         */
+        protected String toBundleName(String baseName, Locale locale) {
+            return LocaleDataStrategy.INSTANCE.toBundleName(baseName, locale);
+        }
+    }
 
-        private LocaleDataResourceBundleControl() {
+    /**
+     * A ResourceBundleProvider implementation for loading locale data
+     * resource bundles except for the java.time supplementary data.
+     */
+    public static abstract class CommonResourceBundleProvider extends LocaleDataResourceBundleProvider {
+        @Override
+        protected boolean isSupportedInModule(String baseName, Locale locale) {
+            return LocaleDataStrategy.INSTANCE.inJavaBaseModule(baseName, locale);
+        }
+    }
+
+    /**
+     * A ResourceBundleProvider implementation for loading supplementary
+     * resource bundles for java.time.
+     */
+    public static abstract class SupplementaryResourceBundleProvider extends LocaleDataResourceBundleProvider {
+        @Override
+        protected boolean isSupportedInModule(String baseName, Locale locale) {
+            return SupplementaryStrategy.INSTANCE.inJavaBaseModule(baseName, locale);
+        }
+    }
+
+    // Bundles.Strategy implementations
+
+    private static class LocaleDataStrategy implements Bundles.Strategy {
+        private static final LocaleDataStrategy INSTANCE = new LocaleDataStrategy();
+
+        private LocaleDataStrategy() {
         }
 
         /*
@@ -205,66 +253,49 @@
          * @exception NullPointerException if baseName or locale is null.
          */
         @Override
-         public List<Locale> getCandidateLocales(String baseName, Locale locale) {
-            LocaleProviderAdapter.Type type = baseName.contains(DOTCLDR) ? CLDR : JRE;
-            LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type);
-            List<Locale> candidates = adapter instanceof ResourceBundleBasedAdapter ?
-                ((ResourceBundleBasedAdapter)adapter).getCandidateLocales(baseName, locale) :
-                super.getCandidateLocales(baseName, locale);
+        public List<Locale> getCandidateLocales(String baseName, Locale locale) {
+            String key = baseName + '-' + locale.toLanguageTag();
+            List<Locale> candidates = CANDIDATES_MAP.get(key);
+            if (candidates == null) {
+                LocaleProviderAdapter.Type type = baseName.contains(DOTCLDR) ? CLDR : JRE;
+                LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type);
+                candidates = adapter instanceof ResourceBundleBasedAdapter ?
+                    ((ResourceBundleBasedAdapter)adapter).getCandidateLocales(baseName, locale) :
+                    defaultControl.getCandidateLocales(baseName, locale);
 
-            // Weed out Locales which are known to have no resource bundles
-            int lastDot = baseName.lastIndexOf('.');
-            String category = (lastDot >= 0) ? baseName.substring(lastDot + 1) : baseName;
-            Set<String> langtags = ((JRELocaleProviderAdapter)adapter).getLanguageTagSet(category);
-            if (!langtags.isEmpty()) {
-                for (Iterator<Locale> itr = candidates.iterator(); itr.hasNext();) {
-                    if (!adapter.isSupportedProviderLocale(itr.next(), langtags)) {
-                        itr.remove();
+                // Weed out Locales which are known to have no resource bundles
+                int lastDot = baseName.lastIndexOf('.');
+                String category = (lastDot >= 0) ? baseName.substring(lastDot + 1) : baseName;
+                Set<String> langtags = ((JRELocaleProviderAdapter)adapter).getLanguageTagSet(category);
+                if (!langtags.isEmpty()) {
+                    for (Iterator<Locale> itr = candidates.iterator(); itr.hasNext();) {
+                        if (!adapter.isSupportedProviderLocale(itr.next(), langtags)) {
+                            itr.remove();
+                        }
                     }
                 }
-            }
-
-            // Force fallback to Locale.ENGLISH for CLDR time zone names support
-            if (locale.getLanguage() != "en"
-                    && type == CLDR && category.equals("TimeZoneNames")) {
-                candidates.add(candidates.size() - 1, Locale.ENGLISH);
+                // Force fallback to Locale.ENGLISH for CLDR time zone names support
+                if (locale.getLanguage() != "en"
+                        && type == CLDR && category.equals("TimeZoneNames")) {
+                    candidates.add(candidates.size() - 1, Locale.ENGLISH);
+                }
+                CANDIDATES_MAP.putIfAbsent(key, candidates);
             }
             return candidates;
         }
 
-        /*
-         * Overrides "getFallbackLocale" to return null so
-         * that the fallback locale will be null.
-         * @param baseName the resource bundle base name.
-         *        locale   the requested locale for the resource bundle.
-         * @return null for the fallback locale.
-         * @exception NullPointerException if baseName or locale is null.
-         */
-        @Override
-        public Locale getFallbackLocale(String baseName, Locale locale) {
-            if (baseName == null || locale == null) {
-                throw new NullPointerException();
-            }
-            return null;
+        boolean inJavaBaseModule(String baseName, Locale locale) {
+            // TODO: avoid hard-coded Locales
+            return locale.equals(Locale.ROOT) ||
+                (locale.getLanguage() == "en" &&
+                    (locale.getCountry().isEmpty() ||
+                     locale.getCountry() == "US"));
         }
 
-        private static final String DOTCLDR      = ".cldr";
-
-        /**
-         * Changes baseName to its module dependent package name and
-         * calls the super class implementation. For example,
-         * if the baseName is "sun.text.resources.FormatData" and locale is ja_JP,
-         * the baseName is changed to "sun.text.resources.ext.FormatData". If
-         * baseName contains "cldr", such as "sun.text.resources.cldr.FormatData",
-         * the name is changed to "sun.text.resources.cldr.ext.FormatData".
-         */
         @Override
         public String toBundleName(String baseName, Locale locale) {
             String newBaseName = baseName;
-            String lang = locale.getLanguage();
-            String ctry = locale.getCountry();
-            if (lang.length() > 0 &&
-                (lang != "en" || (ctry.length() > 0 && ctry != "US"))) {
+            if (!inJavaBaseModule(baseName, locale)) {
                 if (baseName.startsWith(JRE.getUtilResourcesPackage())
                         || baseName.startsWith(JRE.getTextResourcesPackage())) {
                     // Assume the lengths are the same.
@@ -274,19 +305,26 @@
                     if (baseName.indexOf(DOTCLDR, index) > 0) {
                         index += DOTCLDR.length();
                     }
-                    newBaseName = baseName.substring(0, index + 1) + "ext" +
-                                      baseName.substring(index);
+                    newBaseName = baseName.substring(0, index + 1) + "ext"
+                                      + baseName.substring(index);
                 }
             }
-            return super.toBundleName(newBaseName, locale);
+            return defaultControl.toBundleName(newBaseName, locale);
+        }
+
+        @Override
+        public Class<? extends ResourceBundleProvider> getResourceBundleProviderType(String baseName,
+                                                                                     Locale locale) {
+            return inJavaBaseModule(baseName, locale) ?
+                        null : CommonResourceBundleProvider.class;
         }
     }
 
-    private static class SupplementaryResourceBundleControl extends LocaleDataResourceBundleControl {
-        private static final SupplementaryResourceBundleControl INSTANCE =
-                new SupplementaryResourceBundleControl();
+    private static class SupplementaryStrategy extends LocaleDataStrategy {
+        private static final SupplementaryStrategy INSTANCE
+                = new SupplementaryStrategy();
 
-        private SupplementaryResourceBundleControl() {
+        private SupplementaryStrategy() {
         }
 
         @Override
@@ -296,9 +334,16 @@
         }
 
         @Override
-        public long getTimeToLive(String baseName, Locale locale) {
-            assert baseName.contains("JavaTimeSupplementary");
-            return TTL_DONT_CACHE;
+        public Class<? extends ResourceBundleProvider> getResourceBundleProviderType(String baseName,
+                                                                                     Locale locale) {
+            return inJavaBaseModule(baseName, locale) ?
+                    null : SupplementaryResourceBundleProvider.class;
+        }
+
+        @Override
+        boolean inJavaBaseModule(String baseName, Locale locale) {
+            // TODO: avoid hard-coded Locales
+            return locale.equals(Locale.ROOT) || locale.getLanguage() == "en";
         }
     }
 }