Path: blob/master/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java
41161 views
/*1* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.util.locale.provider;2627import java.security.AccessController;28import java.security.AccessControlException;29import java.security.PrivilegedAction;30import java.security.PrivilegedActionException;31import java.security.PrivilegedExceptionAction;32import java.text.spi.BreakIteratorProvider;33import java.text.spi.CollatorProvider;34import java.text.spi.DateFormatProvider;35import java.text.spi.DateFormatSymbolsProvider;36import java.text.spi.DecimalFormatSymbolsProvider;37import java.text.spi.NumberFormatProvider;38import java.util.Collections;39import java.util.HashSet;40import java.util.List;41import java.util.Locale;42import java.util.ResourceBundle;43import java.util.ServiceLoader;44import java.util.ServiceConfigurationError;45import java.util.Set;46import java.util.StringTokenizer;47import java.util.concurrent.ConcurrentHashMap;48import java.util.concurrent.ConcurrentMap;49import java.util.spi.CalendarDataProvider;50import java.util.spi.CalendarNameProvider;51import java.util.spi.CurrencyNameProvider;52import java.util.spi.LocaleNameProvider;53import java.util.spi.LocaleServiceProvider;54import java.util.spi.TimeZoneNameProvider;55import sun.text.spi.JavaTimeDateTimePatternProvider;56import sun.util.resources.LocaleData;57import sun.util.spi.CalendarProvider;5859/**60* LocaleProviderAdapter implementation for the legacy JRE locale data.61*62* @author Naoto Sato63* @author Masayoshi Okutsu64*/65public class JRELocaleProviderAdapter extends LocaleProviderAdapter implements ResourceBundleBasedAdapter {6667private final ConcurrentMap<String, Set<String>> langtagSets68= new ConcurrentHashMap<>();6970private final ConcurrentMap<Locale, LocaleResources> localeResourcesMap71= new ConcurrentHashMap<>();7273// LocaleData specific to this LocaleProviderAdapter.74private volatile LocaleData localeData;7576/**77* Returns the type of this LocaleProviderAdapter78*/79@Override80public LocaleProviderAdapter.Type getAdapterType() {81return Type.JRE;82}8384/**85* Getter method for Locale Service Providers86*/87@Override88@SuppressWarnings("unchecked")89public <P extends LocaleServiceProvider> P getLocaleServiceProvider(Class<P> c) {90switch (c.getSimpleName()) {91case "BreakIteratorProvider":92return (P) getBreakIteratorProvider();93case "CollatorProvider":94return (P) getCollatorProvider();95case "DateFormatProvider":96return (P) getDateFormatProvider();97case "DateFormatSymbolsProvider":98return (P) getDateFormatSymbolsProvider();99case "DecimalFormatSymbolsProvider":100return (P) getDecimalFormatSymbolsProvider();101case "NumberFormatProvider":102return (P) getNumberFormatProvider();103case "CurrencyNameProvider":104return (P) getCurrencyNameProvider();105case "LocaleNameProvider":106return (P) getLocaleNameProvider();107case "TimeZoneNameProvider":108return (P) getTimeZoneNameProvider();109case "CalendarDataProvider":110return (P) getCalendarDataProvider();111case "CalendarNameProvider":112return (P) getCalendarNameProvider();113case "CalendarProvider":114return (P) getCalendarProvider();115case "JavaTimeDateTimePatternProvider":116return (P) getJavaTimeDateTimePatternProvider();117default:118throw new InternalError("should not come down here");119}120}121122private volatile BreakIteratorProvider breakIteratorProvider;123private volatile CollatorProvider collatorProvider;124private volatile DateFormatProvider dateFormatProvider;125private volatile DateFormatSymbolsProvider dateFormatSymbolsProvider;126private volatile DecimalFormatSymbolsProvider decimalFormatSymbolsProvider;127private volatile NumberFormatProvider numberFormatProvider;128129private volatile CurrencyNameProvider currencyNameProvider;130private volatile LocaleNameProvider localeNameProvider;131protected volatile TimeZoneNameProvider timeZoneNameProvider;132protected volatile CalendarDataProvider calendarDataProvider;133protected volatile CalendarNameProvider calendarNameProvider;134135private volatile CalendarProvider calendarProvider;136private volatile JavaTimeDateTimePatternProvider javaTimeDateTimePatternProvider;137138/*139* Getter methods for java.text.spi.* providers140*/141@Override142public BreakIteratorProvider getBreakIteratorProvider() {143if (breakIteratorProvider == null) {144@SuppressWarnings("removal")145BreakIteratorProvider provider = AccessController.doPrivileged(146(PrivilegedAction<BreakIteratorProvider>) () ->147new BreakIteratorProviderImpl(148getAdapterType(),149getLanguageTagSet("FormatData")));150151synchronized (this) {152if (breakIteratorProvider == null) {153breakIteratorProvider = provider;154}155}156}157return breakIteratorProvider;158}159160@Override161public CollatorProvider getCollatorProvider() {162if (collatorProvider == null) {163@SuppressWarnings("removal")164CollatorProvider provider = AccessController.doPrivileged(165(PrivilegedAction<CollatorProvider>) () ->166new CollatorProviderImpl(167getAdapterType(),168getLanguageTagSet("CollationData")));169170synchronized (this) {171if (collatorProvider == null) {172collatorProvider = provider;173}174}175}176return collatorProvider;177}178179@Override180public DateFormatProvider getDateFormatProvider() {181if (dateFormatProvider == null) {182@SuppressWarnings("removal")183DateFormatProvider provider = AccessController.doPrivileged(184(PrivilegedAction<DateFormatProvider>) () ->185new DateFormatProviderImpl(186getAdapterType(),187getLanguageTagSet("FormatData")));188189synchronized (this) {190if (dateFormatProvider == null) {191dateFormatProvider = provider;192}193}194}195return dateFormatProvider;196}197198@Override199public DateFormatSymbolsProvider getDateFormatSymbolsProvider() {200if (dateFormatSymbolsProvider == null) {201@SuppressWarnings("removal")202DateFormatSymbolsProvider provider = AccessController.doPrivileged(203(PrivilegedAction<DateFormatSymbolsProvider>) () ->204new DateFormatSymbolsProviderImpl(205getAdapterType(),206getLanguageTagSet("FormatData")));207208synchronized (this) {209if (dateFormatSymbolsProvider == null) {210dateFormatSymbolsProvider = provider;211}212}213}214return dateFormatSymbolsProvider;215}216217@Override218public DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() {219if (decimalFormatSymbolsProvider == null) {220@SuppressWarnings("removal")221DecimalFormatSymbolsProvider provider = AccessController.doPrivileged(222(PrivilegedAction<DecimalFormatSymbolsProvider>) () ->223new DecimalFormatSymbolsProviderImpl(224getAdapterType(),225getLanguageTagSet("FormatData")));226227synchronized (this) {228if (decimalFormatSymbolsProvider == null) {229decimalFormatSymbolsProvider = provider;230}231}232}233return decimalFormatSymbolsProvider;234}235236@Override237public NumberFormatProvider getNumberFormatProvider() {238if (numberFormatProvider == null) {239@SuppressWarnings("removal")240NumberFormatProvider provider = AccessController.doPrivileged(241(PrivilegedAction<NumberFormatProvider>) () ->242new NumberFormatProviderImpl(243getAdapterType(),244getLanguageTagSet("FormatData")));245246synchronized (this) {247if (numberFormatProvider == null) {248numberFormatProvider = provider;249}250}251}252return numberFormatProvider;253}254255/**256* Getter methods for java.util.spi.* providers257*/258@Override259public CurrencyNameProvider getCurrencyNameProvider() {260if (currencyNameProvider == null) {261@SuppressWarnings("removal")262CurrencyNameProvider provider = AccessController.doPrivileged(263(PrivilegedAction<CurrencyNameProvider>) () ->264new CurrencyNameProviderImpl(265getAdapterType(),266getLanguageTagSet("CurrencyNames")));267268synchronized (this) {269if (currencyNameProvider == null) {270currencyNameProvider = provider;271}272}273}274return currencyNameProvider;275}276277@Override278public LocaleNameProvider getLocaleNameProvider() {279if (localeNameProvider == null) {280@SuppressWarnings("removal")281LocaleNameProvider provider = AccessController.doPrivileged(282(PrivilegedAction<LocaleNameProvider>) () ->283new LocaleNameProviderImpl(284getAdapterType(),285getLanguageTagSet("LocaleNames")));286287synchronized (this) {288if (localeNameProvider == null) {289localeNameProvider = provider;290}291}292}293return localeNameProvider;294}295296@Override297public TimeZoneNameProvider getTimeZoneNameProvider() {298if (timeZoneNameProvider == null) {299@SuppressWarnings("removal")300TimeZoneNameProvider provider = AccessController.doPrivileged(301(PrivilegedAction<TimeZoneNameProvider>) () ->302new TimeZoneNameProviderImpl(303getAdapterType(),304getLanguageTagSet("TimeZoneNames")));305306synchronized (this) {307if (timeZoneNameProvider == null) {308timeZoneNameProvider = provider;309}310}311}312return timeZoneNameProvider;313}314315@Override316public CalendarDataProvider getCalendarDataProvider() {317if (calendarDataProvider == null) {318@SuppressWarnings("removal")319CalendarDataProvider provider = AccessController.doPrivileged(320(PrivilegedAction<CalendarDataProvider>) () ->321new CalendarDataProviderImpl(322getAdapterType(),323getLanguageTagSet("CalendarData")));324325synchronized (this) {326if (calendarDataProvider == null) {327calendarDataProvider = provider;328}329}330}331return calendarDataProvider;332}333334@Override335public CalendarNameProvider getCalendarNameProvider() {336if (calendarNameProvider == null) {337@SuppressWarnings("removal")338CalendarNameProvider provider = AccessController.doPrivileged(339(PrivilegedAction<CalendarNameProvider>) () ->340new CalendarNameProviderImpl(341getAdapterType(),342getLanguageTagSet("FormatData")));343344synchronized (this) {345if (calendarNameProvider == null) {346calendarNameProvider = provider;347}348}349}350return calendarNameProvider;351}352353/**354* Getter methods for sun.util.spi.* providers355*/356@Override357public CalendarProvider getCalendarProvider() {358if (calendarProvider == null) {359@SuppressWarnings("removal")360CalendarProvider provider = AccessController.doPrivileged(361(PrivilegedAction<CalendarProvider>) () ->362new CalendarProviderImpl(363getAdapterType(),364getLanguageTagSet("CalendarData")));365366synchronized (this) {367if (calendarProvider == null) {368calendarProvider = provider;369}370}371}372return calendarProvider;373}374375/**376* Getter methods for sun.text.spi.JavaTimeDateTimePatternProvider provider377*/378@Override379public JavaTimeDateTimePatternProvider getJavaTimeDateTimePatternProvider() {380if (javaTimeDateTimePatternProvider == null) {381@SuppressWarnings("removal")382JavaTimeDateTimePatternProvider provider = AccessController.doPrivileged(383(PrivilegedAction<JavaTimeDateTimePatternProvider>) ()384-> new JavaTimeDateTimePatternImpl(385getAdapterType(),386getLanguageTagSet("FormatData")));387388synchronized (this) {389if (javaTimeDateTimePatternProvider == null) {390javaTimeDateTimePatternProvider = provider;391}392}393}394return javaTimeDateTimePatternProvider;395}396397@Override398public LocaleResources getLocaleResources(Locale locale) {399LocaleResources lr = localeResourcesMap.get(locale);400if (lr == null) {401lr = new LocaleResources(this, locale);402LocaleResources lrc = localeResourcesMap.putIfAbsent(locale, lr);403if (lrc != null) {404lr = lrc;405}406}407return lr;408}409410// ResourceBundleBasedAdapter method implementation411412@Override413public LocaleData getLocaleData() {414if (localeData == null) {415synchronized (this) {416if (localeData == null) {417localeData = new LocaleData(getAdapterType());418}419}420}421return localeData;422}423424@Override425public List<Locale> getCandidateLocales(String baseName, Locale locale) {426return ResourceBundle.Control427.getNoFallbackControl(ResourceBundle.Control.FORMAT_DEFAULT)428.getCandidateLocales(baseName, locale);429}430431/**432* Returns a list of the installed locales. Currently, this simply returns433* the list of locales for which a sun.text.resources.FormatData bundle434* exists. This bundle family happens to be the one with the broadest435* locale coverage in the JRE.436*/437@Override438public Locale[] getAvailableLocales() {439return AvailableJRELocales.localeList.clone();440}441442public Set<String> getLanguageTagSet(String category) {443Set<String> tagset = langtagSets.get(category);444if (tagset == null) {445tagset = createLanguageTagSet(category);446Set<String> ts = langtagSets.putIfAbsent(category, tagset);447if (ts != null) {448tagset = ts;449}450}451return tagset;452}453454protected Set<String> createLanguageTagSet(String category) {455String supportedLocaleString = createSupportedLocaleString(category);456if (supportedLocaleString == null) {457return Collections.emptySet();458}459StringTokenizer tokens = new StringTokenizer(supportedLocaleString);460Set<String> tagset = new HashSet<>((tokens.countTokens() * 4 + 2) / 3);461while (tokens.hasMoreTokens()) {462tagset.add(tokens.nextToken());463}464465return tagset;466}467468private static String createSupportedLocaleString(String category) {469// Directly call Base tags, as we know it's in the base module.470String supportedLocaleString = BaseLocaleDataMetaInfo.getSupportedLocaleString(category);471472// Use ServiceLoader to dynamically acquire installed locales' tags.473try {474@SuppressWarnings("removal")475String nonBaseTags = AccessController.doPrivileged((PrivilegedExceptionAction<String>) () -> {476StringBuilder tags = new StringBuilder();477for (LocaleDataMetaInfo ldmi :478ServiceLoader.loadInstalled(LocaleDataMetaInfo.class)) {479if (ldmi.getType() == LocaleProviderAdapter.Type.JRE) {480String t = ldmi.availableLanguageTags(category);481if (t != null) {482if (tags.length() > 0) {483tags.append(' ');484}485tags.append(t);486}487}488}489return tags.toString();490});491492if (nonBaseTags != null) {493supportedLocaleString += " " + nonBaseTags;494}495} catch (PrivilegedActionException pae) {496throw new InternalError(pae.getCause());497}498499return supportedLocaleString;500}501502/**503* Lazy load available locales.504*/505private static class AvailableJRELocales {506private static final Locale[] localeList = createAvailableLocales();507private AvailableJRELocales() {}508}509510private static Locale[] createAvailableLocales() {511/*512* Gets the locale string list from LocaleDataMetaInfo classes and then513* contructs the Locale array and a set of language tags based on the514* locale string returned above.515*/516String supportedLocaleString = createSupportedLocaleString("AvailableLocales");517518if (supportedLocaleString.isEmpty()) {519throw new InternalError("No available locales for JRE");520}521522StringTokenizer localeStringTokenizer = new StringTokenizer(supportedLocaleString);523524int length = localeStringTokenizer.countTokens();525Locale[] locales = new Locale[length + 1];526locales[0] = Locale.ROOT;527for (int i = 1; i <= length; i++) {528String currentToken = localeStringTokenizer.nextToken();529switch (currentToken) {530case "ja-JP-JP":531locales[i] = JRELocaleConstants.JA_JP_JP;532break;533case "no-NO-NY":534locales[i] = JRELocaleConstants.NO_NO_NY;535break;536case "th-TH-TH":537locales[i] = JRELocaleConstants.TH_TH_TH;538break;539default:540locales[i] = Locale.forLanguageTag(currentToken);541}542}543return locales;544}545546@Override547public boolean isSupportedProviderLocale(Locale locale, Set<String> langtags) {548if (Locale.ROOT.equals(locale)) {549return true;550}551552locale = locale.stripExtensions();553if (langtags.contains(locale.toLanguageTag())) {554return true;555}556557String oldname = locale.toString().replace('_', '-');558return langtags.contains(oldname) ||559"ja-JP-JP".equals(oldname) ||560"th-TH-TH".equals(oldname) ||561"no-NO-NY".equals(oldname);562}563}564565566