Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/util/locale/provider/LocaleResources.java
38918 views
/*1* Copyright (c) 2012, 2013, 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*/2425/*26* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved27* (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved28*29* The original version of this source code and documentation30* is copyrighted and owned by Taligent, Inc., a wholly-owned31* subsidiary of IBM. These materials are provided under terms32* of a License Agreement between Taligent and Sun. This technology33* is protected by multiple US and International patents.34*35* This notice and attribution to Taligent may not be removed.36* Taligent is a registered trademark of Taligent, Inc.37*38*/3940package sun.util.locale.provider;4142import java.lang.ref.ReferenceQueue;43import java.lang.ref.SoftReference;44import java.text.MessageFormat;45import java.util.Calendar;46import java.util.LinkedHashSet;47import java.util.Locale;48import java.util.Map;49import java.util.Objects;50import java.util.ResourceBundle;51import java.util.Set;52import java.util.concurrent.ConcurrentHashMap;53import java.util.concurrent.ConcurrentMap;54import sun.util.calendar.ZoneInfo;55import sun.util.resources.LocaleData;56import sun.util.resources.OpenListResourceBundle;57import sun.util.resources.ParallelListResourceBundle;58import sun.util.resources.TimeZoneNamesBundle;5960/**61* Central accessor to locale-dependent resources for JRE/CLDR provider adapters.62*63* @author Masayoshi Okutsu64* @author Naoto Sato65*/66public class LocaleResources {6768private final Locale locale;69private final LocaleData localeData;70private final LocaleProviderAdapter.Type type;7172// Resource cache73private ConcurrentMap<String, ResourceReference> cache = new ConcurrentHashMap<>();74private ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();7576// cache key prefixes77private static final String BREAK_ITERATOR_INFO = "BII.";78private static final String CALENDAR_DATA = "CALD.";79private static final String COLLATION_DATA_CACHEKEY = "COLD";80private static final String DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY = "DFSD";81private static final String CURRENCY_NAMES = "CN.";82private static final String LOCALE_NAMES = "LN.";83private static final String TIME_ZONE_NAMES = "TZN.";84private static final String ZONE_IDS_CACHEKEY = "ZID";85private static final String CALENDAR_NAMES = "CALN.";86private static final String NUMBER_PATTERNS_CACHEKEY = "NP";87private static final String DATE_TIME_PATTERN = "DTP.";8889// null singleton cache value90private static final Object NULLOBJECT = new Object();9192LocaleResources(ResourceBundleBasedAdapter adapter, Locale locale) {93this.locale = locale;94this.localeData = adapter.getLocaleData();95type = ((LocaleProviderAdapter)adapter).getAdapterType();96}9798private void removeEmptyReferences() {99Object ref;100while ((ref = referenceQueue.poll()) != null) {101cache.remove(((ResourceReference)ref).getCacheKey());102}103}104105Object getBreakIteratorInfo(String key) {106Object biInfo;107String cacheKey = BREAK_ITERATOR_INFO + key;108109removeEmptyReferences();110ResourceReference data = cache.get(cacheKey);111if (data == null || ((biInfo = data.get()) == null)) {112biInfo = localeData.getBreakIteratorInfo(locale).getObject(key);113cache.put(cacheKey, new ResourceReference(cacheKey, biInfo, referenceQueue));114}115116return biInfo;117}118119int getCalendarData(String key) {120Integer caldata;121String cacheKey = CALENDAR_DATA + key;122123removeEmptyReferences();124125ResourceReference data = cache.get(cacheKey);126if (data == null || ((caldata = (Integer) data.get()) == null)) {127ResourceBundle rb = localeData.getCalendarData(locale);128if (rb.containsKey(key)) {129caldata = Integer.parseInt(rb.getString(key));130} else {131caldata = 0;132}133134cache.put(cacheKey,135new ResourceReference(cacheKey, (Object) caldata, referenceQueue));136}137138return caldata;139}140141public String getCollationData() {142String key = "Rule";143String coldata = "";144145removeEmptyReferences();146ResourceReference data = cache.get(COLLATION_DATA_CACHEKEY);147if (data == null || ((coldata = (String) data.get()) == null)) {148ResourceBundle rb = localeData.getCollationData(locale);149if (rb.containsKey(key)) {150coldata = rb.getString(key);151}152cache.put(COLLATION_DATA_CACHEKEY,153new ResourceReference(COLLATION_DATA_CACHEKEY, (Object) coldata, referenceQueue));154}155156return coldata;157}158159public Object[] getDecimalFormatSymbolsData() {160Object[] dfsdata;161162removeEmptyReferences();163ResourceReference data = cache.get(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY);164if (data == null || ((dfsdata = (Object[]) data.get()) == null)) {165// Note that only dfsdata[0] is prepared here in this method. Other166// elements are provided by the caller, yet they are cached here.167ResourceBundle rb = localeData.getNumberFormatData(locale);168dfsdata = new Object[3];169170// NumberElements look up. First, try the Unicode extension171String numElemKey;172String numberType = locale.getUnicodeLocaleType("nu");173if (numberType != null) {174numElemKey = numberType + ".NumberElements";175if (rb.containsKey(numElemKey)) {176dfsdata[0] = rb.getStringArray(numElemKey);177}178}179180// Next, try DefaultNumberingSystem value181if (dfsdata[0] == null && rb.containsKey("DefaultNumberingSystem")) {182numElemKey = rb.getString("DefaultNumberingSystem") + ".NumberElements";183if (rb.containsKey(numElemKey)) {184dfsdata[0] = rb.getStringArray(numElemKey);185}186}187188// Last resort. No need to check the availability.189// Just let it throw MissingResourceException when needed.190if (dfsdata[0] == null) {191dfsdata[0] = rb.getStringArray("NumberElements");192}193194cache.put(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY,195new ResourceReference(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY, (Object) dfsdata, referenceQueue));196}197198return dfsdata;199}200201public String getCurrencyName(String key) {202Object currencyName = null;203String cacheKey = CURRENCY_NAMES + key;204205removeEmptyReferences();206ResourceReference data = cache.get(cacheKey);207208if (data != null && ((currencyName = data.get()) != null)) {209if (currencyName.equals(NULLOBJECT)) {210currencyName = null;211}212213return (String) currencyName;214}215216OpenListResourceBundle olrb = localeData.getCurrencyNames(locale);217218if (olrb.containsKey(key)) {219currencyName = olrb.getObject(key);220cache.put(cacheKey,221new ResourceReference(cacheKey, currencyName, referenceQueue));222}223224return (String) currencyName;225}226227public String getLocaleName(String key) {228Object localeName = null;229String cacheKey = LOCALE_NAMES + key;230231removeEmptyReferences();232ResourceReference data = cache.get(cacheKey);233234if (data != null && ((localeName = data.get()) != null)) {235if (localeName.equals(NULLOBJECT)) {236localeName = null;237}238239return (String) localeName;240}241242OpenListResourceBundle olrb = localeData.getLocaleNames(locale);243244if (olrb.containsKey(key)) {245localeName = olrb.getObject(key);246cache.put(cacheKey,247new ResourceReference(cacheKey, localeName, referenceQueue));248}249250return (String) localeName;251}252253String[] getTimeZoneNames(String key) {254String[] names = null;255String cacheKey = TIME_ZONE_NAMES + '.' + key;256257removeEmptyReferences();258ResourceReference data = cache.get(cacheKey);259260if (Objects.isNull(data) || Objects.isNull((names = (String[]) data.get()))) {261TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale);262if (tznb.containsKey(key)) {263names = tznb.getStringArray(key);264cache.put(cacheKey,265new ResourceReference(cacheKey, (Object) names, referenceQueue));266}267}268269return names;270}271272@SuppressWarnings("unchecked")273Set<String> getZoneIDs() {274Set<String> zoneIDs = null;275276removeEmptyReferences();277ResourceReference data = cache.get(ZONE_IDS_CACHEKEY);278if (data == null || ((zoneIDs = (Set<String>) data.get()) == null)) {279TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);280zoneIDs = rb.keySet();281cache.put(ZONE_IDS_CACHEKEY,282new ResourceReference(ZONE_IDS_CACHEKEY, (Object) zoneIDs, referenceQueue));283}284285return zoneIDs;286}287288// zoneStrings are cached separately in TimeZoneNameUtility.289String[][] getZoneStrings() {290TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);291Set<String> keyset = getZoneIDs();292// Use a LinkedHashSet to preseve the order293Set<String[]> value = new LinkedHashSet<>();294for (String key : keyset) {295value.add(rb.getStringArray(key));296}297298// Add aliases data for CLDR299if (type == LocaleProviderAdapter.Type.CLDR) {300// Note: TimeZoneNamesBundle creates a String[] on each getStringArray call.301Map<String, String> aliases = ZoneInfo.getAliasTable();302for (String alias : aliases.keySet()) {303if (!keyset.contains(alias)) {304String tzid = aliases.get(alias);305if (keyset.contains(tzid)) {306String[] val = rb.getStringArray(tzid);307val[0] = alias;308value.add(val);309}310}311}312}313return value.toArray(new String[0][]);314}315316String[] getCalendarNames(String key) {317String[] names = null;318String cacheKey = CALENDAR_NAMES + key;319320removeEmptyReferences();321ResourceReference data = cache.get(cacheKey);322323if (data == null || ((names = (String[]) data.get()) == null)) {324ResourceBundle rb = localeData.getDateFormatData(locale);325if (rb.containsKey(key)) {326names = rb.getStringArray(key);327cache.put(cacheKey,328new ResourceReference(cacheKey, (Object) names, referenceQueue));329}330}331332return names;333}334335String[] getJavaTimeNames(String key) {336String[] names = null;337String cacheKey = CALENDAR_NAMES + key;338339removeEmptyReferences();340ResourceReference data = cache.get(cacheKey);341342if (data == null || ((names = (String[]) data.get()) == null)) {343ResourceBundle rb = getJavaTimeFormatData();344if (rb.containsKey(key)) {345names = rb.getStringArray(key);346cache.put(cacheKey,347new ResourceReference(cacheKey, (Object) names, referenceQueue));348}349}350351return names;352}353354public String getDateTimePattern(int timeStyle, int dateStyle, Calendar cal) {355if (cal == null) {356cal = Calendar.getInstance(locale);357}358return getDateTimePattern(null, timeStyle, dateStyle, cal.getCalendarType());359}360361/**362* Returns a date-time format pattern363* @param timeStyle style of time; one of FULL, LONG, MEDIUM, SHORT in DateFormat,364* or -1 if not required365* @param dateStyle style of time; one of FULL, LONG, MEDIUM, SHORT in DateFormat,366* or -1 if not required367* @param calType the calendar type for the pattern368* @return the pattern string369*/370public String getJavaTimeDateTimePattern(int timeStyle, int dateStyle, String calType) {371calType = CalendarDataUtility.normalizeCalendarType(calType);372String pattern;373pattern = getDateTimePattern("java.time.", timeStyle, dateStyle, calType);374if (pattern == null) {375pattern = getDateTimePattern(null, timeStyle, dateStyle, calType);376}377return pattern;378}379380private String getDateTimePattern(String prefix, int timeStyle, int dateStyle, String calType) {381String pattern;382String timePattern = null;383String datePattern = null;384385if (timeStyle >= 0) {386if (prefix != null) {387timePattern = getDateTimePattern(prefix, "TimePatterns", timeStyle, calType);388}389if (timePattern == null) {390timePattern = getDateTimePattern(null, "TimePatterns", timeStyle, calType);391}392}393if (dateStyle >= 0) {394if (prefix != null) {395datePattern = getDateTimePattern(prefix, "DatePatterns", dateStyle, calType);396}397if (datePattern == null) {398datePattern = getDateTimePattern(null, "DatePatterns", dateStyle, calType);399}400}401if (timeStyle >= 0) {402if (dateStyle >= 0) {403String dateTimePattern = null;404if (prefix != null) {405dateTimePattern = getDateTimePattern(prefix, "DateTimePatterns", 0, calType);406}407if (dateTimePattern == null) {408dateTimePattern = getDateTimePattern(null, "DateTimePatterns", 0, calType);409}410switch (dateTimePattern) {411case "{1} {0}":412pattern = datePattern + " " + timePattern;413break;414case "{0} {1}":415pattern = timePattern + " " + datePattern;416break;417default:418pattern = MessageFormat.format(dateTimePattern, timePattern, datePattern);419break;420}421} else {422pattern = timePattern;423}424} else if (dateStyle >= 0) {425pattern = datePattern;426} else {427throw new IllegalArgumentException("No date or time style specified");428}429return pattern;430}431432public String[] getNumberPatterns() {433String[] numberPatterns = null;434435removeEmptyReferences();436ResourceReference data = cache.get(NUMBER_PATTERNS_CACHEKEY);437438if (data == null || ((numberPatterns = (String[]) data.get()) == null)) {439ResourceBundle resource = localeData.getNumberFormatData(locale);440numberPatterns = resource.getStringArray("NumberPatterns");441cache.put(NUMBER_PATTERNS_CACHEKEY,442new ResourceReference(NUMBER_PATTERNS_CACHEKEY, (Object) numberPatterns, referenceQueue));443}444445return numberPatterns;446}447448/**449* Returns the FormatData resource bundle of this LocaleResources.450* The FormatData should be used only for accessing extra451* resources required by JSR 310.452*/453public ResourceBundle getJavaTimeFormatData() {454ResourceBundle rb = localeData.getDateFormatData(locale);455if (rb instanceof ParallelListResourceBundle) {456localeData.setSupplementary((ParallelListResourceBundle) rb);457}458return rb;459}460461private String getDateTimePattern(String prefix, String key, int styleIndex, String calendarType) {462StringBuilder sb = new StringBuilder();463if (prefix != null) {464sb.append(prefix);465}466if (!"gregory".equals(calendarType)) {467sb.append(calendarType).append('.');468}469sb.append(key);470String resourceKey = sb.toString();471String cacheKey = sb.insert(0, DATE_TIME_PATTERN).toString();472473removeEmptyReferences();474ResourceReference data = cache.get(cacheKey);475Object value = NULLOBJECT;476477if (data == null || ((value = data.get()) == null)) {478ResourceBundle r = (prefix != null) ? getJavaTimeFormatData() : localeData.getDateFormatData(locale);479if (r.containsKey(resourceKey)) {480value = r.getStringArray(resourceKey);481} else {482assert !resourceKey.equals(key);483if (r.containsKey(key)) {484value = r.getStringArray(key);485}486}487cache.put(cacheKey,488new ResourceReference(cacheKey, value, referenceQueue));489}490if (value == NULLOBJECT) {491assert prefix != null;492return null;493}494return ((String[])value)[styleIndex];495}496497private static class ResourceReference extends SoftReference<Object> {498private final String cacheKey;499500ResourceReference(String cacheKey, Object o, ReferenceQueue<Object> q) {501super(o, q);502this.cacheKey = cacheKey;503}504505String getCacheKey() {506return cacheKey;507}508}509}510511512