8136668: Default locale provider adapter incorrectly set to JRE
Reviewed-by: okutsu
This commit is contained in:
parent
b5a1becbda
commit
b6b38331a6
jdk
make/src/classes/build/tools/cldrconverter
src/java.base/share/classes/sun/util
@ -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),
|
||||
|
Loading…
x
Reference in New Issue
Block a user