/* * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ 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; /** * LocaleProviderAdapter implementation for the legacy JRE locale data. * * @author Naoto Sato * @author Masayoshi Okutsu */ public class JRELocaleProviderAdapter extends LocaleProviderAdapter implements ResourceBundleBasedAdapter { private final ConcurrentMap> langtagSets = new ConcurrentHashMap<>(); private final ConcurrentMap localeResourcesMap = new ConcurrentHashMap<>(); // LocaleData specific to this LocaleProviderAdapter. private volatile LocaleData localeData; /** * Returns the type of this LocaleProviderAdapter */ @Override public LocaleProviderAdapter.Type getAdapterType() { return Type.JRE; } /** * Getter method for Locale Service Providers */ @Override @SuppressWarnings("unchecked") public

P getLocaleServiceProvider(Class

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; private volatile TimeZoneNameProvider timeZoneNameProvider; protected volatile CalendarDataProvider calendarDataProvider; private volatile CalendarNameProvider calendarNameProvider; private volatile CalendarProvider calendarProvider; private volatile JavaTimeDateTimePatternProvider javaTimeDateTimePatternProvider; /* * Getter methods for java.text.spi.* providers */ @Override public BreakIteratorProvider getBreakIteratorProvider() { if (breakIteratorProvider == null) { BreakIteratorProvider provider = AccessController.doPrivileged( (PrivilegedAction) () -> new BreakIteratorProviderImpl( getAdapterType(), getLanguageTagSet("FormatData"))); synchronized (this) { if (breakIteratorProvider == null) { breakIteratorProvider = provider; } } } return breakIteratorProvider; } @Override public CollatorProvider getCollatorProvider() { if (collatorProvider == null) { CollatorProvider provider = AccessController.doPrivileged( (PrivilegedAction) () -> new CollatorProviderImpl( getAdapterType(), getLanguageTagSet("CollationData"))); synchronized (this) { if (collatorProvider == null) { collatorProvider = provider; } } } return collatorProvider; } @Override public DateFormatProvider getDateFormatProvider() { if (dateFormatProvider == null) { DateFormatProvider provider = AccessController.doPrivileged( (PrivilegedAction) () -> new DateFormatProviderImpl( getAdapterType(), getLanguageTagSet("FormatData"))); synchronized (this) { if (dateFormatProvider == null) { dateFormatProvider = provider; } } } return dateFormatProvider; } @Override public DateFormatSymbolsProvider getDateFormatSymbolsProvider() { if (dateFormatSymbolsProvider == null) { DateFormatSymbolsProvider provider = AccessController.doPrivileged( (PrivilegedAction) () -> new DateFormatSymbolsProviderImpl( getAdapterType(), getLanguageTagSet("FormatData"))); synchronized (this) { if (dateFormatSymbolsProvider == null) { dateFormatSymbolsProvider = provider; } } } return dateFormatSymbolsProvider; } @Override public DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() { if (decimalFormatSymbolsProvider == null) { DecimalFormatSymbolsProvider provider = AccessController.doPrivileged( (PrivilegedAction) () -> new DecimalFormatSymbolsProviderImpl( getAdapterType(), getLanguageTagSet("FormatData"))); synchronized (this) { if (decimalFormatSymbolsProvider == null) { decimalFormatSymbolsProvider = provider; } } } return decimalFormatSymbolsProvider; } @Override public NumberFormatProvider getNumberFormatProvider() { if (numberFormatProvider == null) { NumberFormatProvider provider = AccessController.doPrivileged( (PrivilegedAction) () -> new NumberFormatProviderImpl( getAdapterType(), getLanguageTagSet("FormatData"))); synchronized (this) { if (numberFormatProvider == null) { numberFormatProvider = provider; } } } return numberFormatProvider; } /** * Getter methods for java.util.spi.* providers */ @Override public CurrencyNameProvider getCurrencyNameProvider() { if (currencyNameProvider == null) { CurrencyNameProvider provider = AccessController.doPrivileged( (PrivilegedAction) () -> new CurrencyNameProviderImpl( getAdapterType(), getLanguageTagSet("CurrencyNames"))); synchronized (this) { if (currencyNameProvider == null) { currencyNameProvider = provider; } } } return currencyNameProvider; } @Override public LocaleNameProvider getLocaleNameProvider() { if (localeNameProvider == null) { LocaleNameProvider provider = AccessController.doPrivileged( (PrivilegedAction) () -> new LocaleNameProviderImpl( getAdapterType(), getLanguageTagSet("LocaleNames"))); synchronized (this) { if (localeNameProvider == null) { localeNameProvider = provider; } } } return localeNameProvider; } @Override public TimeZoneNameProvider getTimeZoneNameProvider() { if (timeZoneNameProvider == null) { TimeZoneNameProvider provider = AccessController.doPrivileged( (PrivilegedAction) () -> new TimeZoneNameProviderImpl( getAdapterType(), getLanguageTagSet("TimeZoneNames"))); synchronized (this) { if (timeZoneNameProvider == null) { timeZoneNameProvider = provider; } } } return timeZoneNameProvider; } @Override public CalendarDataProvider getCalendarDataProvider() { if (calendarDataProvider == null) { CalendarDataProvider provider = AccessController.doPrivileged( (PrivilegedAction) () -> new CalendarDataProviderImpl( getAdapterType(), getLanguageTagSet("CalendarData"))); synchronized (this) { if (calendarDataProvider == null) { calendarDataProvider = provider; } } } return calendarDataProvider; } @Override public CalendarNameProvider getCalendarNameProvider() { if (calendarNameProvider == null) { CalendarNameProvider provider = AccessController.doPrivileged( (PrivilegedAction) () -> new CalendarNameProviderImpl( getAdapterType(), getLanguageTagSet("FormatData"))); synchronized (this) { if (calendarNameProvider == null) { calendarNameProvider = provider; } } } return calendarNameProvider; } /** * Getter methods for sun.util.spi.* providers */ @Override public CalendarProvider getCalendarProvider() { if (calendarProvider == null) { CalendarProvider provider = AccessController.doPrivileged( (PrivilegedAction) () -> new CalendarProviderImpl( getAdapterType(), getLanguageTagSet("CalendarData"))); synchronized (this) { if (calendarProvider == null) { calendarProvider = provider; } } } return calendarProvider; } /** * Getter methods for sun.text.spi.JavaTimeDateTimePatternProvider provider */ @Override public JavaTimeDateTimePatternProvider getJavaTimeDateTimePatternProvider() { if (javaTimeDateTimePatternProvider == null) { JavaTimeDateTimePatternProvider provider = AccessController.doPrivileged( (PrivilegedAction) () -> 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 getCandidateLocales(String baseName, Locale locale) { return ResourceBundle.Control .getNoFallbackControl(ResourceBundle.Control.FORMAT_DEFAULT) .getCandidateLocales(baseName, locale); } /** * Returns a list of the installed locales. Currently, this simply returns * the list of locales for which a sun.text.resources.FormatData bundle * exists. This bundle family happens to be the one with the broadest * locale coverage in the JRE. */ @Override public Locale[] getAvailableLocales() { return AvailableJRELocales.localeList.clone(); } public Set getLanguageTagSet(String category) { Set tagset = langtagSets.get(category); if (tagset == null) { tagset = createLanguageTagSet(category); Set ts = langtagSets.putIfAbsent(category, tagset); if (ts != null) { tagset = ts; } } return tagset; } protected Set createLanguageTagSet(String category) { String supportedLocaleString = createSupportedLocaleString(category); if (supportedLocaleString == null) { return Collections.emptySet(); } Set tagset = new HashSet<>(); StringTokenizer tokens = new StringTokenizer(supportedLocaleString); while (tokens.hasMoreTokens()) { tagset.add(tokens.nextToken()); } return tagset; } private static String createSupportedLocaleString(String category) { // Directly call Base tags, as we know it's in the base module. String supportedLocaleString = BaseLocaleDataMetaInfo.getSupportedLocaleString(category); // Use ServiceLoader to dynamically acquire installed locales' tags. try { String nonBaseTags = AccessController.doPrivileged((PrivilegedExceptionAction) () -> { 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; } /** * Lazy load available locales. */ private static class AvailableJRELocales { private static final Locale[] localeList = createAvailableLocales(); private AvailableJRELocales() { } } private static Locale[] createAvailableLocales() { /* * Gets the locale string list from LocaleDataMetaInfo classes and then * contructs the Locale array and a set of language tags based on the * locale string returned above. */ String supportedLocaleString = createSupportedLocaleString("AvailableLocales"); if (supportedLocaleString.length() == 0) { 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 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); } }