# HG changeset patch # User naoto # Date 1445575317 25200 # Node ID 3aecb54196d9af1ee39082a2a3fd4960682414f6 # Parent e8b39cf0570437d5eac00793086625ca67f3d4b6 8136668: Default locale provider adapter incorrectly set to JRE Reviewed-by: okutsu diff -r e8b39cf05704 -r 3aecb54196d9 jdk/make/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.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; } diff -r e8b39cf05704 -r 3aecb54196d9 jdk/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java --- 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 parentLocalesMap = null; + private static volatile Map 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() { - @Override + nbmi = AccessController.doPrivileged(new PrivilegedExceptionAction() { + @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 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 getCandidateLocales(String baseName, Locale locale) { List candidates = super.getCandidateLocales(baseName, locale); return applyParentLocales(baseName, candidates); -} + } private List applyParentLocales(String baseName, List candidates) { - if (Objects.isNull(parentLocalesMap)) { - Map 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 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 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 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 langtags) { return Locale.ROOT.equals(locale) || diff -r e8b39cf05704 -r 3aecb54196d9 jdk/src/java.base/share/classes/sun/util/locale/provider/FallbackLocaleProviderAdapter.java --- 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, Setlangtags) { return Locale.ROOT.equals(locale); + } } -} diff -r e8b39cf05704 -r 3aecb54196d9 jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java --- 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 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 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 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() {}; + } } diff -r e8b39cf05704 -r 3aecb54196d9 jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java --- 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),