8136668: Default locale provider adapter incorrectly set to JRE

Reviewed-by: okutsu
This commit is contained in:
Naoto Sato 2015-10-22 21:41:57 -07:00
parent b5a1becbda
commit b6b38331a6
5 changed files with 135 additions and 116 deletions
jdk

@ -28,6 +28,7 @@ package build.tools.cldrconverter;
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 @@ class ResourceBundleGenerator implements BundleGenerator {
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;
}

@ -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.Objects;
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 @@ public class CLDRLocaleProviderAdapter extends JRELocaleProviderAdapter {
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 @@ public class CLDRLocaleProviderAdapter extends JRELocaleProviderAdapter {
}
}
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 @@ public class CLDRLocaleProviderAdapter extends JRELocaleProviderAdapter {
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 class CLDRLocaleProviderAdapter extends JRELocaleProviderAdapter {
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) ||

@ -70,5 +70,5 @@ public class FallbackLocaleProviderAdapter extends JRELocaleProviderAdapter {
@Override
public boolean isSupportedProviderLocale(Locale locale, Set<String>langtags) {
return Locale.ROOT.equals(locale);
}
}
}

@ -36,6 +36,7 @@ import java.util.ArrayList;
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 @@ public abstract class LocaleProviderAdapter {
* 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 @@ public abstract class LocaleProviderAdapter {
private static final List<Type> adapterPreference;
/**
* JRE Locale Data Adapter instance
* LocaleProviderAdapter instances
*/
private static LocaleProviderAdapter jreLocaleProviderAdapter = new JRELocaleProviderAdapter();
/**
* SPI Locale Data Adapter instance
*/
private static LocaleProviderAdapter spiLocaleProviderAdapter = new SPILocaleProviderAdapter();
/**
* CLDR Locale Data Adapter instance, if any.
*/
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 @@ public abstract class LocaleProviderAdapter {
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 @@ public abstract class LocaleProviderAdapter {
}
}
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 abstract class LocaleProviderAdapter {
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 @@ public abstract class LocaleProviderAdapter {
// Shouldn't happen.
throw new InternalError();
}
/**
* Returns the preference order of LocaleProviderAdapter.Type
*/
@ -281,18 +265,20 @@ public abstract class LocaleProviderAdapter {
}
// 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 class LocaleProviderAdapter {
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() {};
}
}

@ -221,14 +221,13 @@ public final class LocaleServiceProviderPool {
/**
* 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 @@ public final class LocaleServiceProviderPool {
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),