|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.util.cldr; |
|
|
|
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.util.Arrays; |
|
import java.util.Collections; |
|
import java.util.HashSet; |
|
import java.util.List; |
|
import java.util.Locale; |
|
import java.util.Map; |
|
import java.util.Optional; |
|
import java.util.ServiceLoader; |
|
import java.util.ServiceConfigurationError; |
|
import java.util.Set; |
|
import java.util.StringTokenizer; |
|
import java.util.concurrent.ConcurrentHashMap; |
|
import java.util.spi.CalendarDataProvider; |
|
import java.util.spi.CalendarNameProvider; |
|
import java.util.spi.TimeZoneNameProvider; |
|
import sun.util.locale.provider.JRELocaleProviderAdapter; |
|
import sun.util.locale.provider.LocaleDataMetaInfo; |
|
import sun.util.locale.provider.LocaleProviderAdapter; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class CLDRLocaleProviderAdapter extends JRELocaleProviderAdapter { |
|
|
|
private static final CLDRBaseLocaleDataMetaInfo baseMetaInfo = new CLDRBaseLocaleDataMetaInfo(); |
|
|
|
private final LocaleDataMetaInfo nonBaseMetaInfo; |
|
|
|
|
|
private static volatile Map<Locale, Locale> parentLocalesMap; |
|
|
|
private static volatile Map<String,String> langAliasesMap; |
|
|
|
private static final Map<Locale, Locale> langAliasesCache; |
|
static { |
|
parentLocalesMap = new ConcurrentHashMap<>(); |
|
langAliasesMap = new ConcurrentHashMap<>(); |
|
langAliasesCache = new ConcurrentHashMap<>(); |
|
|
|
parentLocalesMap.put(Locale.ROOT, Locale.ROOT); |
|
parentLocalesMap.put(Locale.ENGLISH, Locale.ENGLISH); |
|
parentLocalesMap.put(Locale.US, Locale.US); |
|
} |
|
|
|
public CLDRLocaleProviderAdapter() { |
|
LocaleDataMetaInfo nbmi = null; |
|
|
|
try { |
|
nbmi = AccessController.doPrivileged(new PrivilegedExceptionAction<LocaleDataMetaInfo>() { |
|
@Override |
|
public LocaleDataMetaInfo run() { |
|
for (LocaleDataMetaInfo ldmi : ServiceLoader.loadInstalled(LocaleDataMetaInfo.class)) { |
|
if (ldmi.getType() == LocaleProviderAdapter.Type.CLDR) { |
|
return ldmi; |
|
} |
|
} |
|
return null; |
|
} |
|
}); |
|
} catch (PrivilegedActionException pae) { |
|
throw new InternalError(pae.getCause()); |
|
} |
|
|
|
nonBaseMetaInfo = nbmi; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public LocaleProviderAdapter.Type getAdapterType() { |
|
return LocaleProviderAdapter.Type.CLDR; |
|
} |
|
|
|
@Override |
|
public BreakIteratorProvider getBreakIteratorProvider() { |
|
return null; |
|
} |
|
|
|
@Override |
|
public CalendarDataProvider getCalendarDataProvider() { |
|
if (calendarDataProvider == null) { |
|
CalendarDataProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<CalendarDataProvider>) () -> |
|
new CLDRCalendarDataProviderImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("CalendarData"))); |
|
|
|
synchronized (this) { |
|
if (calendarDataProvider == null) { |
|
calendarDataProvider = provider; |
|
} |
|
} |
|
} |
|
return calendarDataProvider; |
|
} |
|
|
|
@Override |
|
public CalendarNameProvider getCalendarNameProvider() { |
|
if (calendarNameProvider == null) { |
|
CalendarNameProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<CalendarNameProvider>) () |
|
-> new CLDRCalendarNameProviderImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("FormatData"))); |
|
|
|
synchronized (this) { |
|
if (calendarNameProvider == null) { |
|
calendarNameProvider = provider; |
|
} |
|
} |
|
} |
|
return calendarNameProvider; |
|
} |
|
|
|
@Override |
|
public CollatorProvider getCollatorProvider() { |
|
return null; |
|
} |
|
|
|
@Override |
|
public TimeZoneNameProvider getTimeZoneNameProvider() { |
|
if (timeZoneNameProvider == null) { |
|
TimeZoneNameProvider provider = AccessController.doPrivileged( |
|
(PrivilegedAction<TimeZoneNameProvider>) () -> |
|
new CLDRTimeZoneNameProviderImpl( |
|
getAdapterType(), |
|
getLanguageTagSet("TimeZoneNames"))); |
|
|
|
synchronized (this) { |
|
if (timeZoneNameProvider == null) { |
|
timeZoneNameProvider = provider; |
|
} |
|
} |
|
} |
|
return timeZoneNameProvider; |
|
} |
|
|
|
@Override |
|
public Locale[] getAvailableLocales() { |
|
Set<String> all = createLanguageTagSet("AvailableLocales"); |
|
Locale[] locs = new Locale[all.size()]; |
|
int index = 0; |
|
for (String tag : all) { |
|
locs[index++] = Locale.forLanguageTag(tag); |
|
} |
|
return locs; |
|
} |
|
|
|
private static Locale applyAliases(Locale loc) { |
|
if (langAliasesMap.isEmpty()) { |
|
langAliasesMap = baseMetaInfo.getLanguageAliasMap(); |
|
} |
|
Locale locale = langAliasesCache.get(loc); |
|
if (locale == null) { |
|
String locTag = loc.toLanguageTag(); |
|
Locale aliasLocale = langAliasesMap.containsKey(locTag) |
|
? Locale.forLanguageTag(langAliasesMap.get(locTag)) : loc; |
|
langAliasesCache.putIfAbsent(loc, aliasLocale); |
|
return aliasLocale; |
|
} else { |
|
return locale; |
|
} |
|
} |
|
|
|
@Override |
|
protected Set<String> createLanguageTagSet(String category) { |
|
// Assume all categories support the same set as AvailableLocales |
|
|
|
category = "AvailableLocales"; |
|
|
|
|
|
String supportedLocaleString = baseMetaInfo.availableLanguageTags(category); |
|
String nonBaseTags = null; |
|
|
|
if (nonBaseMetaInfo != null) { |
|
nonBaseTags = nonBaseMetaInfo.availableLanguageTags(category); |
|
} |
|
if (nonBaseTags != null) { |
|
if (supportedLocaleString != null) { |
|
supportedLocaleString += " " + nonBaseTags; |
|
} else { |
|
supportedLocaleString = nonBaseTags; |
|
} |
|
} |
|
if (supportedLocaleString == null) { |
|
return Collections.emptySet(); |
|
} |
|
Set<String> tagset = new HashSet<>(); |
|
StringTokenizer tokens = new StringTokenizer(supportedLocaleString); |
|
while (tokens.hasMoreTokens()) { |
|
tagset.add(tokens.nextToken()); |
|
} |
|
return tagset; |
|
} |
|
|
|
|
|
@Override |
|
public List<Locale> getCandidateLocales(String baseName, Locale locale) { |
|
List<Locale> candidates = super.getCandidateLocales(baseName, applyAliases(locale)); |
|
return applyParentLocales(baseName, candidates); |
|
} |
|
|
|
private List<Locale> applyParentLocales(String baseName, List<Locale> candidates) { |
|
|
|
for (int i = 0; i < candidates.size(); i++) { |
|
Locale l = candidates.get(i); |
|
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; |
|
} |
|
parentLocalesMap.putIfAbsent(locale, parent); |
|
} |
|
|
|
if (locale.equals(parent)) { |
|
|
|
parent = null; |
|
} |
|
|
|
return parent; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static Locale getEquivalentLoc(Locale locale) { |
|
switch (locale.toString()) { |
|
case "no": |
|
case "no_NO": |
|
return Locale.forLanguageTag("nb"); |
|
} |
|
return applyAliases(locale); |
|
} |
|
|
|
@Override |
|
public boolean isSupportedProviderLocale(Locale locale, Set<String> langtags) { |
|
return Locale.ROOT.equals(locale) |
|
|| langtags.contains(locale.stripExtensions().toLanguageTag()) |
|
|| langtags.contains(getEquivalentLoc(locale).toLanguageTag()); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Optional<String> canonicalTZID(String id) { |
|
return Optional.ofNullable(baseMetaInfo.tzCanonicalIDs().get(id)); |
|
} |
|
} |