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