|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.util.locale.provider; |
|
|
|
import java.security.AccessController; |
|
import java.security.AccessControlException; |
|
import java.security.PrivilegedAction; |
|
import java.security.PrivilegedActionException; |
|
import java.security.PrivilegedExceptionAction; |
|
import java.text.spi.BreakIteratorProvider; |
|
import java.text.spi.CollatorProvider; |
|
import java.text.spi.DateFormatProvider; |
|
import java.text.spi.DateFormatSymbolsProvider; |
|
import java.text.spi.DecimalFormatSymbolsProvider; |
|
import java.text.spi.NumberFormatProvider; |
|
import java.util.Collections; |
|
import java.util.HashSet; |
|
import java.util.List; |
|
import java.util.Locale; |
|
import java.util.ResourceBundle; |
|
import java.util.ServiceLoader; |
|
import java.util.ServiceConfigurationError; |
|
import java.util.Set; |
|
import java.util.StringTokenizer; |
|
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; |
|
import java.util.spi.LocaleNameProvider; |
|
import java.util.spi.LocaleServiceProvider; |
|
import java.util.spi.TimeZoneNameProvider; |
|
import sun.text.spi.JavaTimeDateTimePatternProvider; |
|
import sun.util.resources.LocaleData; |
|
import sun.util.spi.CalendarProvider; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class JRELocaleProviderAdapter extends LocaleProviderAdapter implements ResourceBundleBasedAdapter { |
|
|
|
private final ConcurrentMap<String, Set<String>> langtagSets |
|
= new ConcurrentHashMap<>(); |
|
|
|
private final ConcurrentMap<Locale, LocaleResources> localeResourcesMap |
|
= new ConcurrentHashMap<>(); |
|
|
|
|
|
private volatile LocaleData localeData; |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public LocaleProviderAdapter.Type getAdapterType() { |
|
return Type.JRE; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
@SuppressWarnings("unchecked") |
|
public <P extends LocaleServiceProvider> P getLocaleServiceProvider(Class<P> c) { |
|
switch (c.getSimpleName()) { |
|
case "BreakIteratorProvider": |
|
return (P) getBreakIteratorProvider(); |
|
case "CollatorProvider": |
|
return (P) getCollatorProvider(); |
|
case "DateFormatProvider": |
|
return (P) getDateFormatProvider(); |
|
case "DateFormatSymbolsProvider": |
|
return (P) getDateFormatSymbolsProvider(); |
|
case "DecimalFormatSymbolsProvider": |
|
return (P) getDecimalFormatSymbolsProvider(); |
|
case "NumberFormatProvider": |
|
return (P) getNumberFormatProvider(); |
|
case "CurrencyNameProvider": |
|
return (P) getCurrencyNameProvider(); |
|
case "LocaleNameProvider": |
|
return (P) getLocaleNameProvider(); |
|
case "TimeZoneNameProvider": |
|
return (P) getTimeZoneNameProvider(); |
|
case "CalendarDataProvider": |
|
return (P) getCalendarDataProvider(); |
|
case "CalendarNameProvider": |
|
return (P) getCalendarNameProvider(); |
|
case "CalendarProvider": |
|
return (P) getCalendarProvider(); |
|
case "JavaTimeDateTimePatternProvider": |
|
return (P) getJavaTimeDateTimePatternProvider(); |
|
default: |
|
throw new InternalError("should not come down here"); |
|
} |
|
} |
|
|
|
private volatile BreakIteratorProvider breakIteratorProvider; |
|
private volatile CollatorProvider collatorProvider; |
|
private volatile DateFormatProvider dateFormatProvider; |
|
private volatile DateFormatSymbolsProvider dateFormatSymbolsProvider; |
|
private volatile DecimalFormatSymbolsProvider decimalFormatSymbolsProvider; |
|
private volatile NumberFormatProvider numberFormatProvider; |
|
|
|
private volatile CurrencyNameProvider currencyNameProvider; |
|
private volatile LocaleNameProvider localeNameProvider; |
|
protected volatile TimeZoneNameProvider timeZoneNameProvider; |
|
protected volatile CalendarDataProvider calendarDataProvider; |
|
protected volatile CalendarNameProvider calendarNameProvider; |
|
|
|
private volatile CalendarProvider calendarProvider; |
|
private volatile JavaTimeDateTimePatternProvider javaTimeDateTimePatternProvider; |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public BreakIteratorProvider getBreakIteratorProvider() { |
|
if (breakIteratorProvider == null) { |
|
@SuppressWarnings("removal") |
|
BreakIteratorProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<BreakIteratorProvider>) () -> |
|
new BreakIteratorProviderImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("FormatData"))); |
|
|
|
synchronized (this) { |
|
if (breakIteratorProvider == null) { |
|
breakIteratorProvider = provider; |
|
} |
|
} |
|
} |
|
return breakIteratorProvider; |
|
} |
|
|
|
@Override |
|
public CollatorProvider getCollatorProvider() { |
|
if (collatorProvider == null) { |
|
@SuppressWarnings("removal") |
|
CollatorProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<CollatorProvider>) () -> |
|
new CollatorProviderImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("CollationData"))); |
|
|
|
synchronized (this) { |
|
if (collatorProvider == null) { |
|
collatorProvider = provider; |
|
} |
|
} |
|
} |
|
return collatorProvider; |
|
} |
|
|
|
@Override |
|
public DateFormatProvider getDateFormatProvider() { |
|
if (dateFormatProvider == null) { |
|
@SuppressWarnings("removal") |
|
DateFormatProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<DateFormatProvider>) () -> |
|
new DateFormatProviderImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("FormatData"))); |
|
|
|
synchronized (this) { |
|
if (dateFormatProvider == null) { |
|
dateFormatProvider = provider; |
|
} |
|
} |
|
} |
|
return dateFormatProvider; |
|
} |
|
|
|
@Override |
|
public DateFormatSymbolsProvider getDateFormatSymbolsProvider() { |
|
if (dateFormatSymbolsProvider == null) { |
|
@SuppressWarnings("removal") |
|
DateFormatSymbolsProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<DateFormatSymbolsProvider>) () -> |
|
new DateFormatSymbolsProviderImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("FormatData"))); |
|
|
|
synchronized (this) { |
|
if (dateFormatSymbolsProvider == null) { |
|
dateFormatSymbolsProvider = provider; |
|
} |
|
} |
|
} |
|
return dateFormatSymbolsProvider; |
|
} |
|
|
|
@Override |
|
public DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() { |
|
if (decimalFormatSymbolsProvider == null) { |
|
@SuppressWarnings("removal") |
|
DecimalFormatSymbolsProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<DecimalFormatSymbolsProvider>) () -> |
|
new DecimalFormatSymbolsProviderImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("FormatData"))); |
|
|
|
synchronized (this) { |
|
if (decimalFormatSymbolsProvider == null) { |
|
decimalFormatSymbolsProvider = provider; |
|
} |
|
} |
|
} |
|
return decimalFormatSymbolsProvider; |
|
} |
|
|
|
@Override |
|
public NumberFormatProvider getNumberFormatProvider() { |
|
if (numberFormatProvider == null) { |
|
@SuppressWarnings("removal") |
|
NumberFormatProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<NumberFormatProvider>) () -> |
|
new NumberFormatProviderImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("FormatData"))); |
|
|
|
synchronized (this) { |
|
if (numberFormatProvider == null) { |
|
numberFormatProvider = provider; |
|
} |
|
} |
|
} |
|
return numberFormatProvider; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public CurrencyNameProvider getCurrencyNameProvider() { |
|
if (currencyNameProvider == null) { |
|
@SuppressWarnings("removal") |
|
CurrencyNameProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<CurrencyNameProvider>) () -> |
|
new CurrencyNameProviderImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("CurrencyNames"))); |
|
|
|
synchronized (this) { |
|
if (currencyNameProvider == null) { |
|
currencyNameProvider = provider; |
|
} |
|
} |
|
} |
|
return currencyNameProvider; |
|
} |
|
|
|
@Override |
|
public LocaleNameProvider getLocaleNameProvider() { |
|
if (localeNameProvider == null) { |
|
@SuppressWarnings("removal") |
|
LocaleNameProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<LocaleNameProvider>) () -> |
|
new LocaleNameProviderImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("LocaleNames"))); |
|
|
|
synchronized (this) { |
|
if (localeNameProvider == null) { |
|
localeNameProvider = provider; |
|
} |
|
} |
|
} |
|
return localeNameProvider; |
|
} |
|
|
|
@Override |
|
public TimeZoneNameProvider getTimeZoneNameProvider() { |
|
if (timeZoneNameProvider == null) { |
|
@SuppressWarnings("removal") |
|
TimeZoneNameProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<TimeZoneNameProvider>) () -> |
|
new TimeZoneNameProviderImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("TimeZoneNames"))); |
|
|
|
synchronized (this) { |
|
if (timeZoneNameProvider == null) { |
|
timeZoneNameProvider = provider; |
|
} |
|
} |
|
} |
|
return timeZoneNameProvider; |
|
} |
|
|
|
@Override |
|
public CalendarDataProvider getCalendarDataProvider() { |
|
if (calendarDataProvider == null) { |
|
@SuppressWarnings("removal") |
|
CalendarDataProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<CalendarDataProvider>) () -> |
|
new CalendarDataProviderImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("CalendarData"))); |
|
|
|
synchronized (this) { |
|
if (calendarDataProvider == null) { |
|
calendarDataProvider = provider; |
|
} |
|
} |
|
} |
|
return calendarDataProvider; |
|
} |
|
|
|
@Override |
|
public CalendarNameProvider getCalendarNameProvider() { |
|
if (calendarNameProvider == null) { |
|
@SuppressWarnings("removal") |
|
CalendarNameProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<CalendarNameProvider>) () -> |
|
new CalendarNameProviderImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("FormatData"))); |
|
|
|
synchronized (this) { |
|
if (calendarNameProvider == null) { |
|
calendarNameProvider = provider; |
|
} |
|
} |
|
} |
|
return calendarNameProvider; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public CalendarProvider getCalendarProvider() { |
|
if (calendarProvider == null) { |
|
@SuppressWarnings("removal") |
|
CalendarProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<CalendarProvider>) () -> |
|
new CalendarProviderImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("CalendarData"))); |
|
|
|
synchronized (this) { |
|
if (calendarProvider == null) { |
|
calendarProvider = provider; |
|
} |
|
} |
|
} |
|
return calendarProvider; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public JavaTimeDateTimePatternProvider getJavaTimeDateTimePatternProvider() { |
|
if (javaTimeDateTimePatternProvider == null) { |
|
@SuppressWarnings("removal") |
|
JavaTimeDateTimePatternProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<JavaTimeDateTimePatternProvider>) () |
|
-> new JavaTimeDateTimePatternImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("FormatData"))); |
|
|
|
synchronized (this) { |
|
if (javaTimeDateTimePatternProvider == null) { |
|
javaTimeDateTimePatternProvider = provider; |
|
} |
|
} |
|
} |
|
return javaTimeDateTimePatternProvider; |
|
} |
|
|
|
@Override |
|
public LocaleResources getLocaleResources(Locale locale) { |
|
LocaleResources lr = localeResourcesMap.get(locale); |
|
if (lr == null) { |
|
lr = new LocaleResources(this, locale); |
|
LocaleResources lrc = localeResourcesMap.putIfAbsent(locale, lr); |
|
if (lrc != null) { |
|
lr = lrc; |
|
} |
|
} |
|
return lr; |
|
} |
|
|
|
// ResourceBundleBasedAdapter method implementation |
|
|
|
@Override |
|
public LocaleData getLocaleData() { |
|
if (localeData == null) { |
|
synchronized (this) { |
|
if (localeData == null) { |
|
localeData = new LocaleData(getAdapterType()); |
|
} |
|
} |
|
} |
|
return localeData; |
|
} |
|
|
|
@Override |
|
public List<Locale> getCandidateLocales(String baseName, Locale locale) { |
|
return ResourceBundle.Control |
|
.getNoFallbackControl(ResourceBundle.Control.FORMAT_DEFAULT) |
|
.getCandidateLocales(baseName, locale); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public Locale[] getAvailableLocales() { |
|
return AvailableJRELocales.localeList.clone(); |
|
} |
|
|
|
public Set<String> getLanguageTagSet(String category) { |
|
Set<String> tagset = langtagSets.get(category); |
|
if (tagset == null) { |
|
tagset = createLanguageTagSet(category); |
|
Set<String> ts = langtagSets.putIfAbsent(category, tagset); |
|
if (ts != null) { |
|
tagset = ts; |
|
} |
|
} |
|
return tagset; |
|
} |
|
|
|
protected Set<String> createLanguageTagSet(String category) { |
|
String supportedLocaleString = createSupportedLocaleString(category); |
|
if (supportedLocaleString == null) { |
|
return Collections.emptySet(); |
|
} |
|
StringTokenizer tokens = new StringTokenizer(supportedLocaleString); |
|
Set<String> tagset = new HashSet<>((tokens.countTokens() * 4 + 2) / 3); |
|
while (tokens.hasMoreTokens()) { |
|
tagset.add(tokens.nextToken()); |
|
} |
|
|
|
return tagset; |
|
} |
|
|
|
private static String createSupportedLocaleString(String category) { |
|
|
|
String supportedLocaleString = BaseLocaleDataMetaInfo.getSupportedLocaleString(category); |
|
|
|
|
|
try { |
|
@SuppressWarnings("removal") |
|
String nonBaseTags = AccessController.doPrivileged((PrivilegedExceptionAction<String>) () -> { |
|
StringBuilder tags = new StringBuilder(); |
|
for (LocaleDataMetaInfo ldmi : |
|
ServiceLoader.loadInstalled(LocaleDataMetaInfo.class)) { |
|
if (ldmi.getType() == LocaleProviderAdapter.Type.JRE) { |
|
String t = ldmi.availableLanguageTags(category); |
|
if (t != null) { |
|
if (tags.length() > 0) { |
|
tags.append(' '); |
|
} |
|
tags.append(t); |
|
} |
|
} |
|
} |
|
return tags.toString(); |
|
}); |
|
|
|
if (nonBaseTags != null) { |
|
supportedLocaleString += " " + nonBaseTags; |
|
} |
|
} catch (PrivilegedActionException pae) { |
|
throw new InternalError(pae.getCause()); |
|
} |
|
|
|
return supportedLocaleString; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static class AvailableJRELocales { |
|
private static final Locale[] localeList = createAvailableLocales(); |
|
private AvailableJRELocales() {} |
|
} |
|
|
|
private static Locale[] createAvailableLocales() { |
|
|
|
|
|
|
|
|
|
*/ |
|
String supportedLocaleString = createSupportedLocaleString("AvailableLocales"); |
|
|
|
if (supportedLocaleString.isEmpty()) { |
|
throw new InternalError("No available locales for JRE"); |
|
} |
|
|
|
StringTokenizer localeStringTokenizer = new StringTokenizer(supportedLocaleString); |
|
|
|
int length = localeStringTokenizer.countTokens(); |
|
Locale[] locales = new Locale[length + 1]; |
|
locales[0] = Locale.ROOT; |
|
for (int i = 1; i <= length; i++) { |
|
String currentToken = localeStringTokenizer.nextToken(); |
|
switch (currentToken) { |
|
case "ja-JP-JP": |
|
locales[i] = JRELocaleConstants.JA_JP_JP; |
|
break; |
|
case "no-NO-NY": |
|
locales[i] = JRELocaleConstants.NO_NO_NY; |
|
break; |
|
case "th-TH-TH": |
|
locales[i] = JRELocaleConstants.TH_TH_TH; |
|
break; |
|
default: |
|
locales[i] = Locale.forLanguageTag(currentToken); |
|
} |
|
} |
|
return locales; |
|
} |
|
|
|
@Override |
|
public boolean isSupportedProviderLocale(Locale locale, Set<String> langtags) { |
|
if (Locale.ROOT.equals(locale)) { |
|
return true; |
|
} |
|
|
|
locale = locale.stripExtensions(); |
|
if (langtags.contains(locale.toLanguageTag())) { |
|
return true; |
|
} |
|
|
|
String oldname = locale.toString().replace('_', '-'); |
|
return langtags.contains(oldname) || |
|
"ja-JP-JP".equals(oldname) || |
|
"th-TH-TH".equals(oldname) || |
|
"no-NO-NY".equals(oldname); |
|
} |
|
} |