changeset 15260:7af2d7a87806

7162007: Clean up i18n related caches Reviewed-by: okutsu, ohair
author naoto
date Mon, 14 Jan 2013 11:09:53 -0800
parents 33fec5f9630b
children c5b882836677
files jdk/make/java/java/FILES_java.gmk jdk/src/share/classes/java/text/DateFormatSymbols.java jdk/src/share/classes/java/text/DecimalFormat.java jdk/src/share/classes/java/text/DecimalFormatSymbols.java jdk/src/share/classes/java/text/NumberFormat.java jdk/src/share/classes/java/util/Locale.java jdk/src/share/classes/java/util/TimeZone.java jdk/src/share/classes/sun/text/resources/zh/CollationData_zh_HK.java jdk/src/share/classes/sun/text/resources/zh/FormatData_zh_HK.java jdk/src/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java jdk/src/share/classes/sun/util/locale/provider/BreakIteratorProviderImpl.java jdk/src/share/classes/sun/util/locale/provider/CalendarDataProviderImpl.java jdk/src/share/classes/sun/util/locale/provider/CalendarNameProviderImpl.java jdk/src/share/classes/sun/util/locale/provider/CollatorProviderImpl.java jdk/src/share/classes/sun/util/locale/provider/CurrencyNameProviderImpl.java jdk/src/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java jdk/src/share/classes/sun/util/locale/provider/LocaleNameProviderImpl.java jdk/src/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java jdk/src/share/classes/sun/util/locale/provider/LocaleResources.java jdk/src/share/classes/sun/util/locale/provider/ResourceBundleBasedAdapter.java jdk/src/share/classes/sun/util/locale/provider/TimeZoneNameProviderImpl.java jdk/src/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java jdk/src/share/classes/sun/util/resources/LocaleData.java jdk/src/share/classes/sun/util/resources/zh/CurrencyNames_zh_HK.java jdk/src/share/classes/sun/util/resources/zh/CurrencyNames_zh_SG.java jdk/src/share/classes/sun/util/resources/zh/LocaleNames_zh_HK.java jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_HK.java jdk/test/java/util/PluggableLocale/BreakIteratorProviderTest.java jdk/test/java/util/PluggableLocale/CollatorProviderTest.java jdk/test/java/util/PluggableLocale/CurrencyNameProviderTest.java jdk/test/java/util/PluggableLocale/DateFormatProviderTest.java jdk/test/java/util/PluggableLocale/DateFormatSymbolsProviderTest.java jdk/test/java/util/PluggableLocale/DecimalFormatSymbolsProviderTest.java jdk/test/java/util/PluggableLocale/LocaleNameProviderTest.java jdk/test/java/util/PluggableLocale/NumberFormatProviderTest.java jdk/test/java/util/PluggableLocale/TimeZoneNameProviderTest.java
diffstat 36 files changed, 491 insertions(+), 311 deletions(-) [+]
line wrap: on
line diff
--- a/jdk/make/java/java/FILES_java.gmk	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/make/java/java/FILES_java.gmk	Mon Jan 14 11:09:53 2013 -0800
@@ -227,6 +227,7 @@
             sun/util/locale/provider/LocaleResources.java \
             sun/util/locale/provider/NumberFormatProviderImpl.java \
             sun/util/locale/provider/RuleBasedBreakIterator.java \
+            sun/util/locale/provider/ResourceBundleBasedAdapter.java \
             sun/util/locale/provider/SPILocaleProviderAdapter.java \
             sun/util/locale/provider/TimeZoneNameProviderImpl.java \
             sun/util/locale/provider/TimeZoneNameUtility.java \
--- a/jdk/src/share/classes/java/text/DateFormatSymbols.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/java/text/DateFormatSymbols.java	Mon Jan 14 11:09:53 2013 -0800
@@ -52,6 +52,7 @@
 import java.util.concurrent.ConcurrentMap;
 import sun.util.locale.provider.LocaleProviderAdapter;
 import sun.util.locale.provider.LocaleServiceProviderPool;
+import sun.util.locale.provider.ResourceBundleBasedAdapter;
 import sun.util.locale.provider.TimeZoneNameUtility;
 
 /**
@@ -680,13 +681,10 @@
         // Initialize the fields from the ResourceBundle for locale.
         LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(DateFormatSymbolsProvider.class, locale);
         // Avoid any potential recursions
-        switch (adapter.getAdapterType()) {
-        case HOST:
-        case SPI:
+        if (!(adapter instanceof ResourceBundleBasedAdapter)) {
             adapter = LocaleProviderAdapter.getResourceBundleBased();
-            break;
         }
-        ResourceBundle resource = adapter.getLocaleData().getDateFormatData(locale);
+        ResourceBundle resource = ((ResourceBundleBasedAdapter)adapter).getLocaleData().getDateFormatData(locale);
 
         // JRE and CLDR use different keys
         // JRE: Eras, short.Eras and narrow.Eras
--- a/jdk/src/share/classes/java/text/DecimalFormat.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/java/text/DecimalFormat.java	Mon Jan 14 11:09:53 2013 -0800
@@ -54,6 +54,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 import sun.util.locale.provider.LocaleProviderAdapter;
+import sun.util.locale.provider.ResourceBundleBasedAdapter;
 
 /**
  * <code>DecimalFormat</code> is a concrete subclass of
@@ -394,28 +395,17 @@
      * @see java.text.NumberFormat#getPercentInstance
      */
     public DecimalFormat() {
+        // Get the pattern for the default locale.
         Locale def = Locale.getDefault(Locale.Category.FORMAT);
-        // try to get the pattern from the cache
-        String pattern = cachedLocaleData.get(def);
-        if (pattern == null) {  /* cache miss */
-            // Get the pattern for the default locale.
-            LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(NumberFormatProvider.class, def);
-            switch (adapter.getAdapterType()) {
-            case HOST:
-            case SPI:
-                adapter = LocaleProviderAdapter.getResourceBundleBased();
-                break;
-            }
-            ResourceBundle rb = adapter.getLocaleData().getNumberFormatData(def);
-            String[] all = rb.getStringArray("NumberPatterns");
-            pattern = all[0];
-            /* update cache */
-            cachedLocaleData.putIfAbsent(def, pattern);
+        LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(NumberFormatProvider.class, def);
+        if (!(adapter instanceof ResourceBundleBasedAdapter)) {
+            adapter = LocaleProviderAdapter.getResourceBundleBased();
         }
+        String[] all = adapter.getLocaleResources(def).getNumberPatterns();
 
         // Always applyPattern after the symbols are set
         this.symbols = DecimalFormatSymbols.getInstance(def);
-        applyPattern(pattern, false);
+        applyPattern(all[0], false);
     }
 
 
@@ -4154,10 +4144,4 @@
 
     // Proclaim JDK 1.1 serial compatibility.
     static final long serialVersionUID = 864413376551465018L;
-
-    /**
-     * Cache to hold the NumberPattern of a Locale.
-     */
-    private static final ConcurrentMap<Locale, String> cachedLocaleData
-        = new ConcurrentHashMap<>(3);
 }
--- a/jdk/src/share/classes/java/text/DecimalFormatSymbols.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/java/text/DecimalFormatSymbols.java	Mon Jan 14 11:09:53 2013 -0800
@@ -52,6 +52,7 @@
 import java.util.concurrent.ConcurrentMap;
 import sun.util.locale.provider.LocaleProviderAdapter;
 import sun.util.locale.provider.LocaleServiceProviderPool;
+import sun.util.locale.provider.ResourceBundleBasedAdapter;
 
 /**
  * This class represents the set of symbols (such as the decimal separator,
@@ -542,48 +543,13 @@
     private void initialize( Locale locale ) {
         this.locale = locale;
 
-        // get resource bundle data - try the cache first
-        boolean needCacheUpdate = false;
-        Object[] data = cachedLocaleData.get(locale);
-        if (data == null) {  /* cache miss */
-            LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(DecimalFormatSymbolsProvider.class, locale);
-            // Avoid potential recursions
-            switch (adapter.getAdapterType()) {
-            case HOST:
-            case SPI:
-                adapter = LocaleProviderAdapter.getResourceBundleBased();
-                break;
-            }
-            ResourceBundle rb = adapter.getLocaleData().getNumberFormatData(locale);
-            data = new Object[3];
-
-            // NumberElements look up. First, try the Unicode extension
-            String numElemKey;
-            String numberType = locale.getUnicodeLocaleType("nu");
-            if (numberType != null) {
-                numElemKey = numberType + ".NumberElements";
-                if (rb.containsKey(numElemKey)) {
-                    data[0] = rb.getStringArray(numElemKey);
-                }
-            }
-
-            // Next, try DefaultNumberingSystem value
-            if (data[0] == null && rb.containsKey("DefaultNumberingSystem")) {
-                numElemKey = rb.getString("DefaultNumberingSystem") + ".NumberElements";
-                if (rb.containsKey(numElemKey)) {
-                    data[0] = rb.getStringArray(numElemKey);
-                }
-            }
-
-            // Last resort. No need to check the availability.
-            // Just let it throw MissingResourceException when needed.
-            if (data[0] == null) {
-                data[0] = rb.getStringArray("NumberElements");
-            }
-
-            needCacheUpdate = true;
+        // get resource bundle data
+        LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(DecimalFormatSymbolsProvider.class, locale);
+        // Avoid potential recursions
+        if (!(adapter instanceof ResourceBundleBasedAdapter)) {
+            adapter = LocaleProviderAdapter.getResourceBundleBased();
         }
-
+        Object[] data = adapter.getLocaleResources(locale).getDecimalFormatSymbolsData();
         String[] numberElements = (String[]) data[0];
 
         decimalSeparator = numberElements[0].charAt(0);
@@ -618,7 +584,6 @@
                 currencySymbol = currency.getSymbol(locale);
                 data[1] = intlCurrencySymbol;
                 data[2] = currencySymbol;
-                needCacheUpdate = true;
             }
         } else {
             // default values
@@ -633,10 +598,6 @@
         // standard decimal separator for all locales that we support.
         // If that changes, add a new entry to NumberElements.
         monetarySeparator = decimalSeparator;
-
-        if (needCacheUpdate) {
-            cachedLocaleData.putIfAbsent(locale, data);
-        }
     }
 
     /**
@@ -850,11 +811,4 @@
      * @since JDK 1.1.6
      */
     private int serialVersionOnStream = currentSerialVersion;
-
-    /**
-     * cache to hold the NumberElements and the Currency
-     * of a Locale.
-     */
-    private static final ConcurrentMap<Locale, Object[]> cachedLocaleData
-            = new ConcurrentHashMap<>(3);
 }
--- a/jdk/src/share/classes/java/text/NumberFormat.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/java/text/NumberFormat.java	Mon Jan 14 11:09:53 2013 -0800
@@ -56,7 +56,6 @@
 import java.util.spi.LocaleServiceProvider;
 import sun.util.locale.provider.LocaleProviderAdapter;
 import sun.util.locale.provider.LocaleServiceProviderPool;
-import sun.util.resources.LocaleData;
 
 /**
  * <code>NumberFormat</code> is the abstract base class for all number
--- a/jdk/src/share/classes/java/util/Locale.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/java/util/Locale.java	Mon Jan 14 11:09:53 2013 -0800
@@ -50,7 +50,6 @@
 import java.util.spi.LocaleNameProvider;
 
 import sun.security.action.GetPropertyAction;
-import sun.util.locale.provider.LocaleServiceProviderPool;
 import sun.util.locale.BaseLocale;
 import sun.util.locale.InternalLocaleBuilder;
 import sun.util.locale.LanguageTag;
@@ -61,7 +60,9 @@
 import sun.util.locale.LocaleUtils;
 import sun.util.locale.ParseStatus;
 import sun.util.locale.provider.LocaleProviderAdapter;
-import sun.util.resources.OpenListResourceBundle;
+import sun.util.locale.provider.LocaleResources;
+import sun.util.locale.provider.LocaleServiceProviderPool;
+import sun.util.locale.provider.ResourceBundleBasedAdapter;
 
 /**
  * A <code>Locale</code> object represents a specific geographical, political,
@@ -1779,20 +1780,15 @@
         if (baseLocale.getVariant().length() == 0)
             return "";
 
-        OpenListResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getLocaleNames(inLocale);
+        LocaleResources lr = LocaleProviderAdapter.forJRE().getLocaleResources(inLocale);
 
-        String names[] = getDisplayVariantArray(bundle, inLocale);
+        String names[] = getDisplayVariantArray(inLocale);
 
         // Get the localized patterns for formatting a list, and use
         // them to format the list.
-        String listPattern = null;
-        String listCompositionPattern = null;
-        try {
-            listPattern = bundle.getString("ListPattern");
-            listCompositionPattern = bundle.getString("ListCompositionPattern");
-        } catch (MissingResourceException e) {
-        }
-        return formatList(names, listPattern, listCompositionPattern);
+        return formatList(names,
+                          lr.getLocaleName("ListPattern"),
+                          lr.getLocaleName("ListCompositionPattern"));
     }
 
     /**
@@ -1837,23 +1833,17 @@
      * @throws NullPointerException if <code>inLocale</code> is <code>null</code>
      */
     public String getDisplayName(Locale inLocale) {
-        OpenListResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getLocaleNames(inLocale);
+        LocaleResources lr =  LocaleProviderAdapter.forJRE().getLocaleResources(inLocale);
 
         String languageName = getDisplayLanguage(inLocale);
         String scriptName = getDisplayScript(inLocale);
         String countryName = getDisplayCountry(inLocale);
-        String[] variantNames = getDisplayVariantArray(bundle, inLocale);
+        String[] variantNames = getDisplayVariantArray(inLocale);
 
         // Get the localized patterns for formatting a display name.
-        String displayNamePattern = null;
-        String listPattern = null;
-        String listCompositionPattern = null;
-        try {
-            displayNamePattern = bundle.getString("DisplayNamePattern");
-            listPattern = bundle.getString("ListPattern");
-            listCompositionPattern = bundle.getString("ListCompositionPattern");
-        } catch (MissingResourceException e) {
-        }
+        String displayNamePattern = lr.getLocaleName("DisplayNamePattern");
+        String listPattern = lr.getLocaleName("ListPattern");
+        String listCompositionPattern = lr.getLocaleName("ListCompositionPattern");
 
         // The display name consists of a main name, followed by qualifiers.
         // Typically, the format is "MainName (Qualifier, Qualifier)" but this
@@ -2005,7 +1995,7 @@
      * @param bundle the ResourceBundle to use to get the display names
      * @return an array of display names, possible of zero length.
      */
-    private String[] getDisplayVariantArray(OpenListResourceBundle bundle, Locale inLocale) {
+    private String[] getDisplayVariantArray(Locale inLocale) {
         // Split the variant name into tokens separated by '_'.
         StringTokenizer tokenizer = new StringTokenizer(baseLocale.getVariant(), "_");
         String[] names = new String[tokenizer.countTokens()];
--- a/jdk/src/share/classes/java/util/TimeZone.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/java/util/TimeZone.java	Mon Jan 14 11:09:53 2013 -0800
@@ -430,32 +430,7 @@
     }
 
     private static String[] getDisplayNames(String id, Locale locale) {
-        Map<String, SoftReference<Map<Locale, String[]>>> displayNames = DisplayNames.CACHE;
-
-        SoftReference<Map<Locale, String[]>> ref = displayNames.get(id);
-        if (ref != null) {
-            Map<Locale, String[]> perLocale = ref.get();
-            if (perLocale != null) {
-                String[] names = perLocale.get(locale);
-                if (names != null) {
-                    return names;
-                }
-                names = TimeZoneNameUtility.retrieveDisplayNames(id, locale);
-                if (names != null) {
-                    perLocale.put(locale, names);
-                }
-                return names;
-            }
-        }
-
-        String[] names = TimeZoneNameUtility.retrieveDisplayNames(id, locale);
-        if (names != null) {
-            Map<Locale, String[]> perLocale = new ConcurrentHashMap<>();
-            perLocale.put(locale, names);
-            ref = new SoftReference<>(perLocale);
-            displayNames.put(id, ref);
-        }
-        return names;
+        return TimeZoneNameUtility.retrieveDisplayNames(id, locale);
     }
 
     /**
--- a/jdk/src/share/classes/sun/text/resources/zh/CollationData_zh_HK.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/text/resources/zh/CollationData_zh_HK.java	Mon Jan 14 11:09:53 2013 -0800
@@ -47,12 +47,13 @@
 import java.util.Locale;
 import java.util.ResourceBundle;
 import sun.util.locale.provider.LocaleProviderAdapter;
+import sun.util.locale.provider.ResourceBundleBasedAdapter;
 
 public class CollationData_zh_HK extends ListResourceBundle {
 
     // reparent to zh_TW for traditional Chinese collation sequence
     public CollationData_zh_HK() {
-        ResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getCollationData(Locale.TAIWAN);
+        ResourceBundle bundle = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getCollationData(Locale.TAIWAN);
         setParent(bundle);
     }
 
--- a/jdk/src/share/classes/sun/text/resources/zh/FormatData_zh_HK.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/text/resources/zh/FormatData_zh_HK.java	Mon Jan 14 11:09:53 2013 -0800
@@ -44,12 +44,14 @@
 import java.util.Locale;
 import java.util.ResourceBundle;
 import sun.util.locale.provider.LocaleProviderAdapter;
+import sun.util.locale.provider.ResourceBundleBasedAdapter;
 
 public class FormatData_zh_HK extends ListResourceBundle {
 
     // reparent to zh_TW for traditional Chinese names
     public FormatData_zh_HK() {
-        ResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getDateFormatData(Locale.TAIWAN);
+        ResourceBundle bundle = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE())
+            .getLocaleData().getDateFormatData(Locale.TAIWAN);
         setParent(bundle);
     }
 
--- a/jdk/src/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java	Mon Jan 14 11:09:53 2013 -0800
@@ -43,7 +43,6 @@
 import java.util.spi.LocaleNameProvider;
 import java.util.spi.LocaleServiceProvider;
 import java.util.spi.TimeZoneNameProvider;
-import sun.util.resources.LocaleData;
 
 /**
  * An abstract parent class for the
@@ -146,11 +145,6 @@
         return null;
     }
 
-    @Override
-    public LocaleData getLocaleData() {
-        return null;
-    }
-
     private static Locale[] availableLocales = null;
 
     @Override
--- a/jdk/src/share/classes/sun/util/locale/provider/BreakIteratorProviderImpl.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/locale/provider/BreakIteratorProviderImpl.java	Mon Jan 14 11:09:53 2013 -0800
@@ -25,12 +25,12 @@
 
 package sun.util.locale.provider;
 
+import java.io.IOException;
 import java.text.BreakIterator;
 import java.text.spi.BreakIteratorProvider;
 import java.util.Locale;
-import java.util.ResourceBundle;
+import java.util.MissingResourceException;
 import java.util.Set;
-import sun.util.resources.LocaleData;
 
 /**
  * Concrete implementation of the  {@link java.text.spi.BreakIteratorProvider
@@ -159,24 +159,22 @@
             throw new NullPointerException();
         }
 
-        ResourceBundle bundle = LocaleData.getBundle(
-                LocaleProviderAdapter.Type.JRE.getTextResourcesPackage() + ".BreakIteratorInfo", locale);
-        String[] classNames = bundle.getStringArray("BreakIteratorClasses");
-
-        String dataFile = bundle.getString(dataName);
+        LocaleResources lr = LocaleProviderAdapter.forJRE().getLocaleResources(locale);
+        String[] classNames = (String[]) lr.getBreakIteratorInfo("BreakIteratorClasses");
+        String dataFile = (String) lr.getBreakIteratorInfo(dataName);
 
         try {
             switch (classNames[type]) {
             case "RuleBasedBreakIterator":
                 return new RuleBasedBreakIterator(dataFile);
             case "DictionaryBasedBreakIterator":
-                String dictionaryFile = bundle.getString(dictionaryName);
+                String dictionaryFile = (String) lr.getBreakIteratorInfo(dictionaryName);
                 return new DictionaryBasedBreakIterator(dataFile, dictionaryFile);
             default:
                 throw new IllegalArgumentException("Invalid break iterator class \"" +
                                 classNames[type] + "\"");
             }
-        } catch (Exception e) {
+        } catch (IOException | MissingResourceException | IllegalArgumentException e) {
             throw new InternalError(e.toString(), e);
         }
     }
--- a/jdk/src/share/classes/sun/util/locale/provider/CalendarDataProviderImpl.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/locale/provider/CalendarDataProviderImpl.java	Mon Jan 14 11:09:53 2013 -0800
@@ -24,10 +24,7 @@
  */
 package sun.util.locale.provider;
 
-import java.util.Calendar;
-import static java.util.Calendar.*;
 import java.util.Locale;
-import java.util.ResourceBundle;
 import java.util.Set;
 import java.util.spi.CalendarDataProvider;
 
@@ -49,12 +46,14 @@
 
     @Override
     public int getFirstDayOfWeek(Locale locale) {
-        return getIntData(CalendarDataUtility.FIRST_DAY_OF_WEEK, locale);
+        return LocaleProviderAdapter.forType(type).getLocaleResources(locale)
+                   .getCalendarData(CalendarDataUtility.FIRST_DAY_OF_WEEK);
     }
 
     @Override
     public int getMinimalDaysInFirstWeek(Locale locale) {
-        return getIntData(CalendarDataUtility.MINIMAL_DAYS_IN_FIRST_WEEK, locale);
+        return LocaleProviderAdapter.forType(type).getLocaleResources(locale)
+                   .getCalendarData(CalendarDataUtility.MINIMAL_DAYS_IN_FIRST_WEEK);
     }
 
     @Override
@@ -66,13 +65,4 @@
     public Set<String> getAvailableLanguageTags() {
         return langtags;
     }
-
-    private int getIntData(String key, Locale locale) {
-        ResourceBundle rb = LocaleProviderAdapter.forType(type).getLocaleData().getCalendarData(locale);
-        if (rb.containsKey(key)) {
-            String firstday = rb.getString(key);
-            return Integer.parseInt(firstday);
-        }
-        return 0;
-    }
 }
--- a/jdk/src/share/classes/sun/util/locale/provider/CalendarNameProviderImpl.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/locale/provider/CalendarNameProviderImpl.java	Mon Jan 14 11:09:53 2013 -0800
@@ -28,7 +28,6 @@
 import java.util.Comparator;
 import java.util.Locale;
 import java.util.Map;
-import java.util.ResourceBundle;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.spi.CalendarNameProvider;
@@ -54,22 +53,19 @@
         String name = null;
         String key = getResourceKey(calendarType, field, style);
         if (key != null) {
-            ResourceBundle rb = LocaleProviderAdapter.forType(type).getLocaleData().getDateFormatData(locale);
-            if (rb.containsKey(key)) {
-                String[] strings = rb.getStringArray(key);
-                if (strings.length > 0) {
-                    if (field == DAY_OF_WEEK || field == YEAR) {
-                        --value;
-                    }
-                    name = strings[value];
-                    // If name is empty in standalone, try its `format' style.
-                    if (name.length() == 0
-                            && (style == SHORT_STANDALONE || style == LONG_STANDALONE
-                                || style == NARROW_STANDALONE)) {
-                        name = getDisplayName(calendarType, field, value,
-                                              getBaseStyle(style),
-                                              locale);
-                    }
+            String[] strings = LocaleProviderAdapter.forType(type).getLocaleResources(locale).getCalendarNames(key);
+            if (strings != null && strings.length > 0) {
+                if (field == DAY_OF_WEEK || field == YEAR) {
+                    --value;
+                }
+                name = strings[value];
+                // If name is empty in standalone, try its `format' style.
+                if (name.length() == 0
+                        && (style == SHORT_STANDALONE || style == LONG_STANDALONE
+                            || style == NARROW_STANDALONE)) {
+                    name = getDisplayName(calendarType, field, value,
+                                          getBaseStyle(style),
+                                          locale);
                 }
             }
         }
@@ -100,9 +96,8 @@
         String key = getResourceKey(calendarType, field, style);
         Map<String, Integer> map = new TreeMap<>(LengthBasedComparator.INSTANCE);
         if (key != null) {
-            ResourceBundle rb = LocaleProviderAdapter.forType(type).getLocaleData().getDateFormatData(locale);
-            if (rb.containsKey(key)) {
-                String[] strings = rb.getStringArray(key);
+            String[] strings = LocaleProviderAdapter.forType(type).getLocaleResources(locale).getCalendarNames(key);
+            if (strings != null) {
                 if (!hasDuplicates(strings)) {
                     if (field == YEAR) {
                         if (strings.length > 0) {
--- a/jdk/src/share/classes/sun/util/locale/provider/CollatorProviderImpl.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/locale/provider/CollatorProviderImpl.java	Mon Jan 14 11:09:53 2013 -0800
@@ -45,8 +45,6 @@
 import java.text.RuleBasedCollator;
 import java.text.spi.CollatorProvider;
 import java.util.Locale;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
 import java.util.Set;
 
 /**
@@ -102,14 +100,7 @@
 
         // Load the resource of the desired locale from resource
         // manager.
-        String colString = "";
-        try {
-            ResourceBundle resource = LocaleProviderAdapter.forType(type).getLocaleData().getCollationData(locale);
-
-            colString = resource.getString("Rule");
-        } catch (MissingResourceException e) {
-            // Use default values
-        }
+        String colString = LocaleProviderAdapter.forType(type).getLocaleResources(locale).getCollationData();
         try
         {
             result = new RuleBasedCollator(CollationRules.DEFAULTRULES +
--- a/jdk/src/share/classes/sun/util/locale/provider/CurrencyNameProviderImpl.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/locale/provider/CurrencyNameProviderImpl.java	Mon Jan 14 11:09:53 2013 -0800
@@ -26,7 +26,6 @@
 package sun.util.locale.provider;
 
 import java.util.Locale;
-import java.util.ResourceBundle;
 import java.util.Set;
 import java.util.spi.CurrencyNameProvider;
 
@@ -120,11 +119,6 @@
             throw new NullPointerException();
         }
 
-        ResourceBundle bundle = LocaleProviderAdapter.forType(type).getLocaleData().getCurrencyNames(locale);
-        if (bundle.containsKey(key)) {
-                return bundle.getString(key);
-            }
-
-        return null;
+        return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getCurrencyName(key);
     }
 }
--- a/jdk/src/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java	Mon Jan 14 11:09:53 2013 -0800
@@ -54,7 +54,7 @@
  * @author Naoto Sato
  * @author Masayoshi Okutsu
  */
-public class JRELocaleProviderAdapter extends LocaleProviderAdapter {
+public class JRELocaleProviderAdapter extends LocaleProviderAdapter implements ResourceBundleBasedAdapter {
 
     private static final String LOCALE_DATA_JAR_NAME = "localedata.jar";
 
@@ -296,6 +296,7 @@
         return lr;
     }
 
+    // ResourceBundleBasedAdapter method implementation
     @Override
     public LocaleData getLocaleData() {
         if (localeData == null) {
--- a/jdk/src/share/classes/sun/util/locale/provider/LocaleNameProviderImpl.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/locale/provider/LocaleNameProviderImpl.java	Mon Jan 14 11:09:53 2013 -0800
@@ -26,7 +26,6 @@
 package sun.util.locale.provider;
 
 import java.util.Locale;
-import java.util.ResourceBundle;
 import java.util.Set;
 import java.util.spi.LocaleNameProvider;
 
@@ -174,12 +173,7 @@
             throw new NullPointerException();
         }
 
-        ResourceBundle rb = LocaleProviderAdapter.forType(type).getLocaleData().getLocaleNames(locale);
-        if (rb.containsKey(key)) {
-                return rb.getString(key);
-            }
-
-        return null;
+        return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getLocaleName(key);
     }
 
     @Override
--- a/jdk/src/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java	Mon Jan 14 11:09:53 2013 -0800
@@ -37,6 +37,8 @@
 import java.util.Locale;
 import java.util.ResourceBundle;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.spi.CalendarDataProvider;
 import java.util.spi.CalendarNameProvider;
 import java.util.spi.CurrencyNameProvider;
@@ -44,7 +46,6 @@
 import java.util.spi.LocaleServiceProvider;
 import java.util.spi.TimeZoneNameProvider;
 import sun.util.cldr.CLDRLocaleProviderAdapter;
-import sun.util.resources.LocaleData;
 
 /**
  * The LocaleProviderAdapter abstract class.
@@ -119,6 +120,12 @@
      */
     private static LocaleProviderAdapter fallbackLocaleProviderAdapter = null;
 
+    /**
+     * Adapter lookup cache.
+     */
+    private static ConcurrentMap<Class<? extends LocaleServiceProvider>, ConcurrentMap<Locale, LocaleProviderAdapter>>
+        adapterCache = new ConcurrentHashMap<>();
+
     static {
         String order = AccessController.doPrivileged(
                            new sun.security.action.GetPropertyAction("java.locale.providers"));
@@ -210,9 +217,23 @@
      */
     public static LocaleProviderAdapter getAdapter(Class<? extends LocaleServiceProvider> providerClass,
                                                Locale locale) {
+        LocaleProviderAdapter adapter;
+
+        // cache lookup
+        ConcurrentMap<Locale, LocaleProviderAdapter> adapterMap = adapterCache.get(providerClass);
+        if (adapterMap != null) {
+            if ((adapter = adapterMap.get(locale)) != null) {
+                return adapter;
+            }
+        } else {
+            adapterMap = new ConcurrentHashMap<>();
+            adapterCache.putIfAbsent(providerClass, adapterMap);
+        }
+
         // Fast look-up for the given locale
-        LocaleProviderAdapter adapter = findAdapter(providerClass, locale);
+        adapter = findAdapter(providerClass, locale);
         if (adapter != null) {
+            adapterMap.putIfAbsent(locale, adapter);
             return adapter;
         }
 
@@ -226,11 +247,13 @@
             }
             adapter = findAdapter(providerClass, loc);
             if (adapter != null) {
+                adapterMap.putIfAbsent(locale, adapter);
                 return adapter;
             }
         }
 
         // returns the adapter for FALLBACK as the last resort
+        adapterMap.putIfAbsent(locale, fallbackLocaleProviderAdapter);
         return fallbackLocaleProviderAdapter;
     }
 
@@ -398,7 +421,5 @@
 
     public abstract LocaleResources getLocaleResources(Locale locale);
 
-    public abstract LocaleData getLocaleData();
-
     public abstract Locale[] getAvailableLocales();
 }
--- a/jdk/src/share/classes/sun/util/locale/provider/LocaleResources.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/locale/provider/LocaleResources.java	Mon Jan 14 11:09:53 2013 -0800
@@ -40,43 +40,295 @@
 
 package sun.util.locale.provider;
 
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
 import java.text.MessageFormat;
 import java.util.Calendar;
+import java.util.LinkedHashSet;
 import java.util.Locale;
+import java.util.Map;
 import java.util.ResourceBundle;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import sun.util.calendar.ZoneInfo;
+import sun.util.resources.LocaleData;
+import sun.util.resources.OpenListResourceBundle;
 import sun.util.resources.TimeZoneNamesBundle;
 
 /**
- * Central accessor to locale-dependent resources.
+ * Central accessor to locale-dependent resources for JRE/CLDR provider adapters.
  *
  * @author Masayoshi Okutsu
+ * @author Naoto Sato
  */
 public class LocaleResources {
 
-    private final LocaleProviderAdapter adapter;
     private final Locale locale;
+    private final LocaleData localeData;
+    private final LocaleProviderAdapter.Type type;
 
     // Resource cache
-    private ConcurrentMap<String, Object> cache = new ConcurrentHashMap<>();
+    private ConcurrentMap<String, ResourceReference> cache = new ConcurrentHashMap<>();
+    private ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
 
+    // cache key prefixes
+    private static final String BREAK_ITERATOR_INFO = "BII.";
+    private static final String CALENDAR_DATA = "CALD.";
+    private static final String COLLATION_DATA_CACHEKEY = "COLD";
+    private static final String DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY = "DFSD";
+    private static final String CURRENCY_NAMES = "CN.";
+    private static final String LOCALE_NAMES = "LN.";
+    private static final String TIME_ZONE_NAMES = "TZN.";
+    private static final String ZONE_IDS_CACHEKEY = "ZID";
+    private static final String CALENDAR_NAMES = "CALN.";
+    private static final String NUMBER_PATTERNS_CACHEKEY = "NP";
+    private static final String DATE_TIME_PATTERN = "DTP.";
 
-    LocaleResources(LocaleProviderAdapter adapter, Locale locale) {
-        this.adapter = adapter;
+    // null singleton cache value
+    private static final Object NULLOBJECT = new Object();
+
+    LocaleResources(ResourceBundleBasedAdapter adapter, Locale locale) {
         this.locale = locale;
+        this.localeData = adapter.getLocaleData();
+        type = ((LocaleProviderAdapter)adapter).getAdapterType();
     }
 
-    public TimeZoneNamesBundle getTimeZoneNames() {
-        TimeZoneNamesBundle tznames = (TimeZoneNamesBundle) cache.get("TimeZoneNames");
-        if (tznames == null) {
-            tznames = adapter.getLocaleData().getTimeZoneNames(locale);
-            TimeZoneNamesBundle tznb = (TimeZoneNamesBundle) cache.putIfAbsent("TimeZoneNames", tznames);
-            if (tznb != null) {
-                tznames = tznb;
+    private void removeEmptyReferences() {
+        Object ref;
+        while ((ref = referenceQueue.poll()) != null) {
+            cache.remove(((ResourceReference)ref).getCacheKey());
+        }
+    }
+
+    Object getBreakIteratorInfo(String key) {
+        Object biInfo;
+        String cacheKey = BREAK_ITERATOR_INFO + key;
+
+        removeEmptyReferences();
+        ResourceReference data = cache.get(cacheKey);
+        if (data == null || ((biInfo = data.get()) == null)) {
+           biInfo = localeData.getBreakIteratorInfo(locale).getObject(key);
+           cache.put(cacheKey, new ResourceReference(cacheKey, biInfo, referenceQueue));
+       }
+
+       return biInfo;
+    }
+
+    int getCalendarData(String key) {
+        Integer caldata;
+        String cacheKey = CALENDAR_DATA  + key;
+
+        removeEmptyReferences();
+
+        ResourceReference data = cache.get(cacheKey);
+        if (data == null || ((caldata = (Integer) data.get()) == null)) {
+            ResourceBundle rb = localeData.getCalendarData(locale);
+            if (rb.containsKey(key)) {
+                caldata = Integer.parseInt(rb.getString(key));
+            } else {
+                caldata = 0;
+            }
+
+            cache.put(cacheKey,
+                      new ResourceReference(cacheKey, (Object) caldata, referenceQueue));
+        }
+
+        return caldata;
+    }
+
+    public String getCollationData() {
+        String key = "Rule";
+        String coldata = "";
+
+        removeEmptyReferences();
+        ResourceReference data = cache.get(COLLATION_DATA_CACHEKEY);
+        if (data == null || ((coldata = (String) data.get()) == null)) {
+            ResourceBundle rb = localeData.getCollationData(locale);
+            if (rb.containsKey(key)) {
+                coldata = rb.getString(key);
+            }
+            cache.put(COLLATION_DATA_CACHEKEY,
+                      new ResourceReference(COLLATION_DATA_CACHEKEY, (Object) coldata, referenceQueue));
+        }
+
+        return coldata;
+    }
+
+    public Object[] getDecimalFormatSymbolsData() {
+        Object[] dfsdata;
+
+        removeEmptyReferences();
+        ResourceReference data = cache.get(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY);
+        if (data == null || ((dfsdata = (Object[]) data.get()) == null)) {
+            // Note that only dfsdata[0] is prepared here in this method. Other
+            // elements are provided by the caller, yet they are cached here.
+            ResourceBundle rb = localeData.getNumberFormatData(locale);
+            dfsdata = new Object[3];
+
+            // NumberElements look up. First, try the Unicode extension
+            String numElemKey;
+            String numberType = locale.getUnicodeLocaleType("nu");
+            if (numberType != null) {
+                numElemKey = numberType + ".NumberElements";
+                if (rb.containsKey(numElemKey)) {
+                    dfsdata[0] = rb.getStringArray(numElemKey);
+                }
+            }
+
+            // Next, try DefaultNumberingSystem value
+            if (dfsdata[0] == null && rb.containsKey("DefaultNumberingSystem")) {
+                numElemKey = rb.getString("DefaultNumberingSystem") + ".NumberElements";
+                if (rb.containsKey(numElemKey)) {
+                    dfsdata[0] = rb.getStringArray(numElemKey);
+                }
+            }
+
+            // Last resort. No need to check the availability.
+            // Just let it throw MissingResourceException when needed.
+            if (dfsdata[0] == null) {
+                dfsdata[0] = rb.getStringArray("NumberElements");
+            }
+
+            cache.put(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY,
+                      new ResourceReference(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY, (Object) dfsdata, referenceQueue));
+        }
+
+        return dfsdata;
+    }
+
+    public String getCurrencyName(String key) {
+        Object currencyName = null;
+        String cacheKey = CURRENCY_NAMES + key;
+
+        removeEmptyReferences();
+        ResourceReference data = cache.get(cacheKey);
+
+        if (data != null && ((currencyName = data.get()) != null)) {
+            if (currencyName.equals(NULLOBJECT)) {
+                currencyName = null;
+            }
+
+            return (String) currencyName;
+        }
+
+        OpenListResourceBundle olrb = localeData.getCurrencyNames(locale);
+
+        if (olrb.containsKey(key)) {
+            currencyName = olrb.getObject(key);
+            cache.put(cacheKey,
+                      new ResourceReference(cacheKey, currencyName, referenceQueue));
+        }
+
+        return (String) currencyName;
+    }
+
+    public String getLocaleName(String key) {
+        Object localeName = null;
+        String cacheKey = LOCALE_NAMES + key;
+
+        removeEmptyReferences();
+        ResourceReference data = cache.get(cacheKey);
+
+        if (data != null && ((localeName = data.get()) != null)) {
+            if (localeName.equals(NULLOBJECT)) {
+                localeName = null;
+            }
+
+            return (String) localeName;
+        }
+
+        OpenListResourceBundle olrb = localeData.getLocaleNames(locale);
+
+        if (olrb.containsKey(key)) {
+            localeName = olrb.getObject(key);
+            cache.put(cacheKey,
+                      new ResourceReference(cacheKey, localeName, referenceQueue));
+        }
+
+        return (String) localeName;
+    }
+
+    String[] getTimeZoneNames(String key, int size) {
+        String[] names = null;
+        String cacheKey = TIME_ZONE_NAMES + key;
+
+        removeEmptyReferences();
+        ResourceReference data = cache.get(cacheKey);
+
+        if (data == null || ((names = (String[]) data.get()) == null)) {
+            TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale);
+            if (tznb.containsKey(key)) {
+                names = tznb.getStringArray(key, size);
+                cache.put(cacheKey,
+                          new ResourceReference(cacheKey, (Object) names, referenceQueue));
             }
         }
-        return tznames;
+
+        return names;
+    }
+
+    @SuppressWarnings("unchecked")
+    Set<String> getZoneIDs() {
+        Set<String> zoneIDs = null;
+
+        removeEmptyReferences();
+        ResourceReference data = cache.get(ZONE_IDS_CACHEKEY);
+        if (data == null || ((zoneIDs = (Set<String>) data.get()) == null)) {
+            TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);
+            zoneIDs = rb.keySet();
+            cache.put(ZONE_IDS_CACHEKEY,
+                      new ResourceReference(ZONE_IDS_CACHEKEY, (Object) zoneIDs, referenceQueue));
+        }
+
+        return zoneIDs;
+    }
+
+    // zoneStrings are cached separately in TimeZoneNameUtility.
+    String[][] getZoneStrings() {
+        TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);
+        Set<String> keyset = getZoneIDs();
+        // Use a LinkedHashSet to preseve the order
+        Set<String[]> value = new LinkedHashSet<>();
+        for (String key : keyset) {
+            value.add(rb.getStringArray(key));
+        }
+
+        // Add aliases data for CLDR
+        if (type == LocaleProviderAdapter.Type.CLDR) {
+            // Note: TimeZoneNamesBundle creates a String[] on each getStringArray call.
+            Map<String, String> aliases = ZoneInfo.getAliasTable();
+            for (String alias : aliases.keySet()) {
+                if (!keyset.contains(alias)) {
+                    String tzid = aliases.get(alias);
+                    if (keyset.contains(tzid)) {
+                        String[] val = rb.getStringArray(tzid);
+                        val[0] = alias;
+                        value.add(val);
+                    }
+                }
+            }
+        }
+        return value.toArray(new String[0][]);
+    }
+
+    String[] getCalendarNames(String key) {
+        String[] names = null;
+        String cacheKey = CALENDAR_NAMES + key;
+
+        removeEmptyReferences();
+        ResourceReference data = cache.get(cacheKey);
+
+        if (data == null || ((names = (String[]) data.get()) == null)) {
+            ResourceBundle rb = localeData.getDateFormatData(locale);
+            if (rb.containsKey(key)) {
+                names = rb.getStringArray(key);
+                cache.put(cacheKey,
+                          new ResourceReference(cacheKey, (Object) names, referenceQueue));
+            }
+        }
+
+        return names;
     }
 
     public String getDateTimePattern(int timeStyle, int dateStyle, Calendar cal) {
@@ -120,32 +372,54 @@
     }
 
     public String[] getNumberPatterns() {
-        /* try the cache first */
-        String[] numberPatterns = (String[]) cache.get("NumberPatterns");
-        if (numberPatterns == null) { /* cache miss */
-            ResourceBundle resource = adapter.getLocaleData().getNumberFormatData(locale);
+        String[] numberPatterns = null;
+
+        removeEmptyReferences();
+        ResourceReference data = cache.get(NUMBER_PATTERNS_CACHEKEY);
+
+        if (data == null || ((numberPatterns = (String[]) data.get()) == null)) {
+            ResourceBundle resource = localeData.getNumberFormatData(locale);
             numberPatterns = resource.getStringArray("NumberPatterns");
-            /* update cache */
-            cache.put("NumberPatterns", numberPatterns);
+            cache.put(NUMBER_PATTERNS_CACHEKEY,
+                      new ResourceReference(NUMBER_PATTERNS_CACHEKEY, (Object) numberPatterns, referenceQueue));
         }
+
         return numberPatterns;
     }
 
     private String getDateTimePattern(String key, int styleIndex, String calendarType) {
         String resourceKey = "gregory".equals(calendarType) ? key : calendarType + "." + key;
-        /* try the cache first */
-        String[] patterns = (String[]) cache.get(resourceKey);
-        if (patterns == null) { /* cache miss */
-            ResourceBundle r = adapter.getLocaleData().getDateFormatData(locale);
+        String cacheKey = DATE_TIME_PATTERN + resourceKey;
+        String[] patterns = null;
+
+        removeEmptyReferences();
+        ResourceReference data = cache.get(cacheKey);
+
+        if (data == null || ((patterns = (String[]) data.get()) == null)) {
+            ResourceBundle r = localeData.getDateFormatData(locale);
             if (r.containsKey(resourceKey)) {
                 patterns = r.getStringArray(resourceKey);
             } else {
                 assert !resourceKey.equals(key);
                 patterns = r.getStringArray(key);
             }
-            /* update cache */
-            cache.putIfAbsent(resourceKey, patterns);
+            cache.put(cacheKey,
+                      new ResourceReference(cacheKey, (Object) patterns, referenceQueue));
         }
+
         return patterns[styleIndex];
     }
+
+    private static class ResourceReference extends SoftReference<Object> {
+        private final String cacheKey;
+
+        ResourceReference(String cacheKey, Object o, ReferenceQueue<Object> q) {
+            super(o, q);
+            this.cacheKey = cacheKey;
+        }
+
+        String getCacheKey() {
+            return cacheKey;
+        }
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/util/locale/provider/ResourceBundleBasedAdapter.java	Mon Jan 14 11:09:53 2013 -0800
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.util.locale.provider;
+
+import sun.util.resources.LocaleData;
+
+/**
+ * Accessor for LocaleData
+ *
+ * @author Naoto Sato
+ */
+public interface ResourceBundleBasedAdapter {
+    public LocaleData getLocaleData();
+}
--- a/jdk/src/share/classes/sun/util/locale/provider/TimeZoneNameProviderImpl.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/locale/provider/TimeZoneNameProviderImpl.java	Mon Jan 14 11:09:53 2013 -0800
@@ -25,14 +25,10 @@
 
 package sun.util.locale.provider;
 
-import java.util.LinkedHashSet;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Set;
 import java.util.TimeZone;
 import java.util.spi.TimeZoneNameProvider;
-import sun.util.calendar.ZoneInfo;
-import sun.util.resources.TimeZoneNamesBundle;
 
 /**
  * Concrete implementation of the
@@ -123,9 +119,7 @@
         if (id == null || locale == null) {
             throw new NullPointerException();
         }
-        LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type);
-        TimeZoneNamesBundle rb = adapter.getLocaleResources(locale).getTimeZoneNames();
-        return rb.containsKey(id) ? rb.getStringArray(id, n) : null;
+        return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getTimeZoneNames(id, n);
     }
 
     /**
@@ -136,30 +130,6 @@
      * @return an array of time zone names arrays
      */
     String[][] getZoneStrings(Locale locale) {
-        LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type);
-        TimeZoneNamesBundle rb = adapter.getLocaleResources(locale).getTimeZoneNames();
-        Set<String> keyset = rb.keySet();
-        // Use a LinkedHashSet to preseve the order
-        Set<String[]> value = new LinkedHashSet<>();
-        for (String key : keyset) {
-            value.add(rb.getStringArray(key));
-        }
-
-        // Add aliases data for CLDR
-        if (type == LocaleProviderAdapter.Type.CLDR) {
-            // Note: TimeZoneNamesBundle creates a String[] on each getStringArray call.
-            Map<String, String> aliases = ZoneInfo.getAliasTable();
-            for (String alias : aliases.keySet()) {
-                if (!keyset.contains(alias)) {
-                    String tzid = aliases.get(alias);
-                    if (keyset.contains(tzid)) {
-                        String[] val = rb.getStringArray(tzid);
-                        val[0] = alias;
-                        value.add(val);
-                    }
-                }
-            }
-        }
-        return value.toArray(new String[0][]);
+        return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getZoneStrings();
     }
 }
--- a/jdk/src/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java	Mon Jan 14 11:09:53 2013 -0800
@@ -30,11 +30,10 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.spi.TimeZoneNameProvider;
 import sun.util.calendar.ZoneInfo;
-import sun.util.resources.OpenListResourceBundle;
-import sun.util.resources.TimeZoneNamesBundle;
 
 /**
  * Utility class that deals with the localized time zone names
@@ -45,15 +44,17 @@
 public final class TimeZoneNameUtility {
 
     /**
-     * cache to hold time zone resource bundles. Keyed by Locale
+     * cache to hold time zone localized strings. Keyed by Locale
      */
-    private static ConcurrentHashMap<Locale, SoftReference<TimeZoneNamesBundle>> cachedBundles =
+    private static ConcurrentHashMap<Locale, SoftReference<String[][]>> cachedZoneData =
         new ConcurrentHashMap<>();
 
     /**
-     * cache to hold time zone localized strings. Keyed by Locale
+     * Cache for managing display names per timezone per locale
+     * The structure is:
+     *     Map(key=id, value=SoftReference(Map(key=locale, value=displaynames)))
      */
-    private static ConcurrentHashMap<Locale, SoftReference<String[][]>> cachedZoneData =
+    private static final Map<String, SoftReference<Map<Locale, String[]>>> cachedDisplayNames =
         new ConcurrentHashMap<>();
 
     /**
@@ -82,9 +83,9 @@
         }
 
         // Performs per-ID retrieval.
+        Set<String> zoneIDs = LocaleProviderAdapter.forJRE().getLocaleResources(locale).getZoneIDs();
         List<String[]> zones = new LinkedList<>();
-        OpenListResourceBundle rb = getBundle(locale);
-        for (String key : rb.keySet()) {
+        for (String key : zoneIDs) {
             String[] names = retrieveDisplayNamesImpl(key, locale);
             if (names != null) {
                 zones.add(names);
@@ -137,20 +138,31 @@
     private static String[] retrieveDisplayNamesImpl(String id, Locale locale) {
         LocaleServiceProviderPool pool =
             LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class);
-        return pool.getLocalizedObject(TimeZoneNameArrayGetter.INSTANCE, locale, id);
-    }
 
-    private static TimeZoneNamesBundle getBundle(Locale locale) {
-        TimeZoneNamesBundle rb;
-        SoftReference<TimeZoneNamesBundle> data = cachedBundles.get(locale);
-
-        if (data == null || ((rb = data.get()) == null)) {
-            rb = LocaleProviderAdapter.forJRE().getLocaleData().getTimeZoneNames(locale);
-            data = new SoftReference<>(rb);
-            cachedBundles.put(locale, data);
+        SoftReference<Map<Locale, String[]>> ref = cachedDisplayNames.get(id);
+        if (ref != null) {
+            Map<Locale, String[]> perLocale = ref.get();
+            if (perLocale != null) {
+                String[] names = perLocale.get(locale);
+                if (names != null) {
+                    return names;
+                }
+                names = pool.getLocalizedObject(TimeZoneNameArrayGetter.INSTANCE, locale, id);
+                if (names != null) {
+                    perLocale.put(locale, names);
+                }
+                return names;
+            }
         }
 
-        return rb;
+        String[] names = pool.getLocalizedObject(TimeZoneNameArrayGetter.INSTANCE, locale, id);
+        if (names != null) {
+            Map<Locale, String[]> perLocale = new ConcurrentHashMap<>();
+            perLocale.put(locale, names);
+            ref = new SoftReference<>(perLocale);
+            cachedDisplayNames.put(id, ref);
+        }
+        return names;
     }
 
     /**
--- a/jdk/src/share/classes/sun/util/resources/LocaleData.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/resources/LocaleData.java	Mon Jan 14 11:09:53 2013 -0800
@@ -99,6 +99,14 @@
     }
 
     /**
+     * Gets a break iterator info resource bundle, using privileges
+     * to allow accessing a sun.* package.
+     */
+    public ResourceBundle getBreakIteratorInfo(Locale locale) {
+        return getBundle(type.getTextResourcesPackage() + ".BreakIteratorInfo", locale);
+    }
+
+    /**
      * Gets a collation data resource bundle, using privileges
      * to allow accessing a sun.* package.
      */
--- a/jdk/src/share/classes/sun/util/resources/zh/CurrencyNames_zh_HK.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/resources/zh/CurrencyNames_zh_HK.java	Mon Jan 14 11:09:53 2013 -0800
@@ -80,13 +80,14 @@
 import java.util.Locale;
 import java.util.ResourceBundle;
 import sun.util.locale.provider.LocaleProviderAdapter;
+import sun.util.locale.provider.ResourceBundleBasedAdapter;
 import sun.util.resources.OpenListResourceBundle;
 
 public final class CurrencyNames_zh_HK extends OpenListResourceBundle {
 
     // reparent to zh_TW for traditional Chinese names
     public CurrencyNames_zh_HK() {
-        ResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getCurrencyNames(Locale.TAIWAN);
+        ResourceBundle bundle = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getCurrencyNames(Locale.TAIWAN);
         setParent(bundle);
     }
 
--- a/jdk/src/share/classes/sun/util/resources/zh/CurrencyNames_zh_SG.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/resources/zh/CurrencyNames_zh_SG.java	Mon Jan 14 11:09:53 2013 -0800
@@ -28,13 +28,14 @@
 import java.util.Locale;
 import java.util.ResourceBundle;
 import sun.util.locale.provider.LocaleProviderAdapter;
+import sun.util.locale.provider.ResourceBundleBasedAdapter;
 import sun.util.resources.OpenListResourceBundle;
 
 public final class CurrencyNames_zh_SG extends OpenListResourceBundle {
 
     // reparent to zh_CN for simplified Chinese names
     public CurrencyNames_zh_SG() {
-        ResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getCurrencyNames(Locale.CHINA);
+        ResourceBundle bundle = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getCurrencyNames(Locale.CHINA);
         setParent(bundle);
     }
 
--- a/jdk/src/share/classes/sun/util/resources/zh/LocaleNames_zh_HK.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/resources/zh/LocaleNames_zh_HK.java	Mon Jan 14 11:09:53 2013 -0800
@@ -28,13 +28,14 @@
 import java.util.Locale;
 import java.util.ResourceBundle;
 import sun.util.locale.provider.LocaleProviderAdapter;
+import sun.util.locale.provider.ResourceBundleBasedAdapter;
 import sun.util.resources.OpenListResourceBundle;
 
 public final class LocaleNames_zh_HK extends OpenListResourceBundle {
 
     // reparent to zh_TW for traditional Chinese names
     public LocaleNames_zh_HK() {
-        ResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getLocaleNames(Locale.TAIWAN);
+        ResourceBundle bundle = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getLocaleNames(Locale.TAIWAN);
         setParent(bundle);
     }
 
--- a/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_HK.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_HK.java	Mon Jan 14 11:09:53 2013 -0800
@@ -41,13 +41,14 @@
 import java.util.Locale;
 import java.util.ResourceBundle;
 import sun.util.locale.provider.LocaleProviderAdapter;
+import sun.util.locale.provider.ResourceBundleBasedAdapter;
 import sun.util.resources.TimeZoneNamesBundle;
 
 public final class TimeZoneNames_zh_HK extends TimeZoneNamesBundle {
 
     // reparent to zh_TW for traditional Chinese names
     public TimeZoneNames_zh_HK() {
-        ResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getTimeZoneNames(Locale.TAIWAN);
+        ResourceBundle bundle = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getTimeZoneNames(Locale.TAIWAN);
         setParent(bundle);
     }
 
--- a/jdk/test/java/util/PluggableLocale/BreakIteratorProviderTest.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/test/java/util/PluggableLocale/BreakIteratorProviderTest.java	Mon Jan 14 11:09:53 2013 -0800
@@ -67,8 +67,7 @@
 
         for (Locale target: availloc) {
             // pure JRE implementation
-            ResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getBundle(
-                        "sun.text.resources.BreakIteratorInfo", target);
+            ResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getBreakIteratorInfo(target);
             String[] classNames = rb.getStringArray("BreakIteratorClasses");
             boolean jreSupportsLocale = jreimplloc.contains(target);
 
--- a/jdk/test/java/util/PluggableLocale/CollatorProviderTest.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/test/java/util/PluggableLocale/CollatorProviderTest.java	Mon Jan 14 11:09:53 2013 -0800
@@ -67,7 +67,7 @@
             for (String tag : ((AvailableLanguageTags)LocaleProviderAdapter.forJRE().getCollatorProvider()).getAvailableLanguageTags()) {
                 jreimplloc.add(Locale.forLanguageTag(tag));
             }
-            ResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getCollationData(target);
+            ResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getCollationData(target);
             boolean jreSupportsLocale = jreimplloc.contains(target);
 
             // result object
--- a/jdk/test/java/util/PluggableLocale/CurrencyNameProviderTest.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/test/java/util/PluggableLocale/CurrencyNameProviderTest.java	Mon Jan 14 11:09:53 2013 -0800
@@ -58,7 +58,7 @@
 
         for (Locale target: availloc) {
             // pure JRE implementation
-            OpenListResourceBundle rb = (OpenListResourceBundle)LocaleProviderAdapter.forJRE().getLocaleData().getCurrencyNames(target);
+            OpenListResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getCurrencyNames(target);
             boolean jreSupportsTarget = jreimplloc.contains(target);
 
             for (Locale test: testloc) {
--- a/jdk/test/java/util/PluggableLocale/DateFormatProviderTest.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/test/java/util/PluggableLocale/DateFormatProviderTest.java	Mon Jan 14 11:09:53 2013 -0800
@@ -84,7 +84,7 @@
                     break;
             }
             // pure JRE implementation
-            ResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getDateFormatData(target);
+            ResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getDateFormatData(target);
             boolean jreSupportsLocale = jreimplloc.contains(target);
 
             // JRE string arrays
--- a/jdk/test/java/util/PluggableLocale/DateFormatSymbolsProviderTest.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/test/java/util/PluggableLocale/DateFormatSymbolsProviderTest.java	Mon Jan 14 11:09:53 2013 -0800
@@ -62,7 +62,7 @@
 
         for (Locale target: availloc) {
             // pure JRE implementation
-            ResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getDateFormatData(target);
+            ResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getDateFormatData(target);
             boolean jreSupportsLocale = jreimplloc.contains(target);
 
             // JRE string arrays
--- a/jdk/test/java/util/PluggableLocale/DecimalFormatSymbolsProviderTest.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/test/java/util/PluggableLocale/DecimalFormatSymbolsProviderTest.java	Mon Jan 14 11:09:53 2013 -0800
@@ -61,17 +61,15 @@
 
         for (Locale target: availloc) {
             // pure JRE implementation
-            ResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getNumberFormatData(target);
+            Object[] data = LocaleProviderAdapter.forJRE().getLocaleResources(target).getDecimalFormatSymbolsData();
             boolean jreSupportsLocale = jreimplloc.contains(target);
 
             // JRE string arrays
             String[] jres = new String[2];
             if (jreSupportsLocale) {
-                try {
-                    String[] tmp = rb.getStringArray("NumberElements");
-                    jres[0] = tmp[9]; // infinity
-                    jres[1] = tmp[10]; // NaN
-                } catch (MissingResourceException mre) {}
+                String[] tmp = (String[]) data[0];
+                jres[0] = tmp[9]; // infinity
+                jres[1] = tmp[10]; // NaN
             }
 
             // result object
--- a/jdk/test/java/util/PluggableLocale/LocaleNameProviderTest.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/test/java/util/PluggableLocale/LocaleNameProviderTest.java	Mon Jan 14 11:09:53 2013 -0800
@@ -49,7 +49,7 @@
 
         for (Locale target: availloc) {
             // pure JRE implementation
-            OpenListResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getLocaleNames(target);
+            OpenListResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getLocaleNames(target);
             boolean jreSupportsTarget = jreimplloc.contains(target);
 
             for (Locale test: testloc) {
--- a/jdk/test/java/util/PluggableLocale/NumberFormatProviderTest.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/test/java/util/PluggableLocale/NumberFormatProviderTest.java	Mon Jan 14 11:09:53 2013 -0800
@@ -63,16 +63,12 @@
     void objectValidityTest() {
 
         for (Locale target: availloc) {
-            // pure JRE implementation
-            ResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getNumberFormatData(target);
             boolean jreSupportsLocale = jreimplloc.contains(target);
 
             // JRE string arrays
             String[] jreNumberPatterns = null;
             if (jreSupportsLocale) {
-                try {
-                    jreNumberPatterns = rb.getStringArray("NumberPatterns");
-                } catch (MissingResourceException mre) {}
+                jreNumberPatterns = LocaleProviderAdapter.forJRE().getLocaleResources(target).getNumberPatterns();
             }
 
             // result object
--- a/jdk/test/java/util/PluggableLocale/TimeZoneNameProviderTest.java	Sun Jan 13 19:57:06 2013 -0500
+++ b/jdk/test/java/util/PluggableLocale/TimeZoneNameProviderTest.java	Mon Jan 14 11:09:53 2013 -0800
@@ -52,7 +52,7 @@
 
         for (Locale target: available) {
             // pure JRE implementation
-            OpenListResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getTimeZoneNames(target);
+            OpenListResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getTimeZoneNames(target);
             boolean jreSupportsTarget = jreimplloc.contains(target);
 
             for (String id: ids) {