|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  */ | 
|  |  | 
|  | 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)); | 
|  |     } | 
|  | } |