/*
 * Copyright (c) 2012, 2018, 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, Masayoshi Okutsu
/** * LocaleProviderAdapter implementation for the legacy JRE locale data. * * @author Naoto Sato * @author Masayoshi Okutsu */
public class JRELocaleProviderAdapter extends LocaleProviderAdapter implements ResourceBundleBasedAdapter { private final ConcurrentMap<String, Set<String>> langtagSets = new ConcurrentHashMap<>(); private final ConcurrentMap<Locale, LocaleResources> localeResourcesMap = new ConcurrentHashMap<>(); // LocaleData specific to this LocaleProviderAdapter. private volatile LocaleData localeData;
Returns the type of this LocaleProviderAdapter
/** * Returns the type of this LocaleProviderAdapter */
@Override public LocaleProviderAdapter.Type getAdapterType() { return Type.JRE; }
Getter method for Locale Service Providers
/** * Getter method for Locale Service Providers */
@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; /* * Getter methods for java.text.spi.* providers */ @Override public BreakIteratorProvider getBreakIteratorProvider() { if (breakIteratorProvider == null) { 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) { 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) { 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) { 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) { 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) { NumberFormatProvider provider = AccessController.doPrivileged( (PrivilegedAction<NumberFormatProvider>) () -> new NumberFormatProviderImpl( getAdapterType(), getLanguageTagSet("FormatData"))); synchronized (this) { if (numberFormatProvider == null) { numberFormatProvider = provider; } } } return numberFormatProvider; }
Getter methods for java.util.spi.* providers
/** * Getter methods for java.util.spi.* providers */
@Override public CurrencyNameProvider getCurrencyNameProvider() { if (currencyNameProvider == null) { 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) { 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) { 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) { 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) { CalendarNameProvider provider = AccessController.doPrivileged( (PrivilegedAction<CalendarNameProvider>) () -> new CalendarNameProviderImpl( getAdapterType(), getLanguageTagSet("FormatData"))); synchronized (this) { if (calendarNameProvider == null) { calendarNameProvider = provider; } } } return calendarNameProvider; }
Getter methods for sun.util.spi.* providers
/** * Getter methods for sun.util.spi.* providers */
@Override public CalendarProvider getCalendarProvider() { if (calendarProvider == null) { CalendarProvider provider = AccessController.doPrivileged( (PrivilegedAction<CalendarProvider>) () -> new CalendarProviderImpl( getAdapterType(), getLanguageTagSet("CalendarData"))); synchronized (this) { if (calendarProvider == null) { calendarProvider = provider; } } } return calendarProvider; }
Getter methods for sun.text.spi.JavaTimeDateTimePatternProvider provider
/** * Getter methods for sun.text.spi.JavaTimeDateTimePatternProvider provider */
@Override public JavaTimeDateTimePatternProvider getJavaTimeDateTimePatternProvider() { if (javaTimeDateTimePatternProvider == null) { 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); }
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.
/** * 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<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(); } Set<String> 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<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; }
Lazy load available locales.
/** * 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.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); } }