1 /*
2 * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41 package sun.util.locale.provider;
42
43 import java.lang.ref.ReferenceQueue;
44 import java.lang.ref.SoftReference;
45 import java.text.MessageFormat;
46 import java.util.Calendar;
47 import java.util.LinkedHashSet;
48 import java.util.Locale;
49 import java.util.Map;
50 import java.util.Objects;
51 import java.util.ResourceBundle;
52 import java.util.Set;
53 import java.util.concurrent.ConcurrentHashMap;
54 import java.util.concurrent.ConcurrentMap;
55 import sun.util.calendar.ZoneInfo;
56 import sun.util.resources.LocaleData;
57 import sun.util.resources.OpenListResourceBundle;
58 import sun.util.resources.ParallelListResourceBundle;
59 import sun.util.resources.TimeZoneNamesBundle;
60
61 /**
62 * Central accessor to locale-dependent resources for JRE/CLDR provider adapters.
63 *
64 * @author Masayoshi Okutsu
65 * @author Naoto Sato
66 */
67 public class LocaleResources {
68
69 private final Locale locale;
70 private final LocaleData localeData;
71 private final LocaleProviderAdapter.Type type;
72
73 // Resource cache
74 private final ConcurrentMap<String, ResourceReference> cache = new ConcurrentHashMap<>();
75 private final ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
76
77 // cache key prefixes
78 private static final String BREAK_ITERATOR_INFO = "BII.";
79 private static final String CALENDAR_DATA = "CALD.";
80 private static final String COLLATION_DATA_CACHEKEY = "COLD";
81 private static final String DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY = "DFSD";
82 private static final String CURRENCY_NAMES = "CN.";
83 private static final String LOCALE_NAMES = "LN.";
84 private static final String TIME_ZONE_NAMES = "TZN.";
85 private static final String ZONE_IDS_CACHEKEY = "ZID";
86 private static final String CALENDAR_NAMES = "CALN.";
87 private static final String NUMBER_PATTERNS_CACHEKEY = "NP";
88 private static final String DATE_TIME_PATTERN = "DTP.";
89
90 // null singleton cache value
91 private static final Object NULLOBJECT = new Object();
92
93 LocaleResources(ResourceBundleBasedAdapter adapter, Locale locale) {
94 this.locale = locale;
95 this.localeData = adapter.getLocaleData();
96 type = ((LocaleProviderAdapter)adapter).getAdapterType();
97 }
98
99 private void removeEmptyReferences() {
100 Object ref;
101 while ((ref = referenceQueue.poll()) != null) {
102 cache.remove(((ResourceReference)ref).getCacheKey());
103 }
104 }
105
106 Object getBreakIteratorInfo(String key) {
107 Object biInfo;
108 String cacheKey = BREAK_ITERATOR_INFO + key;
109
237
238 if (data != null && ((localeName = data.get()) != null)) {
239 if (localeName.equals(NULLOBJECT)) {
240 localeName = null;
241 }
242
243 return (String) localeName;
244 }
245
246 OpenListResourceBundle olrb = localeData.getLocaleNames(locale);
247
248 if (olrb.containsKey(key)) {
249 localeName = olrb.getObject(key);
250 cache.put(cacheKey,
251 new ResourceReference(cacheKey, localeName, referenceQueue));
252 }
253
254 return (String) localeName;
255 }
256
257 String[] getTimeZoneNames(String key) {
258 String[] names = null;
259 String cacheKey = TIME_ZONE_NAMES + '.' + key;
260
261 removeEmptyReferences();
262 ResourceReference data = cache.get(cacheKey);
263
264 if (Objects.isNull(data) || Objects.isNull((names = (String[]) data.get()))) {
265 TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale);
266 if (tznb.containsKey(key)) {
267 names = tznb.getStringArray(key);
268 cache.put(cacheKey,
269 new ResourceReference(cacheKey, (Object) names, referenceQueue));
270 }
271 }
272
273 return names;
274 }
275
276 @SuppressWarnings("unchecked")
277 Set<String> getZoneIDs() {
278 Set<String> zoneIDs = null;
279
280 removeEmptyReferences();
281 ResourceReference data = cache.get(ZONE_IDS_CACHEKEY);
282 if (data == null || ((zoneIDs = (Set<String>) data.get()) == null)) {
283 TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);
284 zoneIDs = rb.keySet();
285 cache.put(ZONE_IDS_CACHEKEY,
286 new ResourceReference(ZONE_IDS_CACHEKEY, (Object) zoneIDs, referenceQueue));
287 }
288
289 return zoneIDs;
290 }
291
292 // zoneStrings are cached separately in TimeZoneNameUtility.
293 String[][] getZoneStrings() {
294 TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);
295 Set<String> keyset = getZoneIDs();
296 // Use a LinkedHashSet to preseve the order
297 Set<String[]> value = new LinkedHashSet<>();
298 for (String key : keyset) {
299 value.add(rb.getStringArray(key));
300 }
301
302 // Add aliases data for CLDR
303 if (type == LocaleProviderAdapter.Type.CLDR) {
304 // Note: TimeZoneNamesBundle creates a String[] on each getStringArray call.
305 Map<String, String> aliases = ZoneInfo.getAliasTable();
306 for (String alias : aliases.keySet()) {
307 if (!keyset.contains(alias)) {
308 String tzid = aliases.get(alias);
309 if (keyset.contains(tzid)) {
310 String[] val = rb.getStringArray(tzid);
311 val[0] = alias;
312 value.add(val);
313 }
314 }
315 }
316 }
317 return value.toArray(new String[0][]);
318 }
319
320 String[] getCalendarNames(String key) {
497 return null;
498 }
499
500 // for DateTimePatterns. CLDR has multiple styles, while JRE has one.
501 String[] styles = (String[])value;
502 return (styles.length > 1 ? styles[styleIndex] : styles[0]);
503 }
504
505 private static class ResourceReference extends SoftReference<Object> {
506 private final String cacheKey;
507
508 ResourceReference(String cacheKey, Object o, ReferenceQueue<Object> q) {
509 super(o, q);
510 this.cacheKey = cacheKey;
511 }
512
513 String getCacheKey() {
514 return cacheKey;
515 }
516 }
517 }
|
1 /*
2 * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41 package sun.util.locale.provider;
42
43 import java.lang.ref.ReferenceQueue;
44 import java.lang.ref.SoftReference;
45 import java.text.MessageFormat;
46 import java.util.Calendar;
47 import java.util.LinkedHashSet;
48 import java.util.Locale;
49 import java.util.Map;
50 import java.util.Objects;
51 import java.util.ResourceBundle;
52 import java.util.Set;
53 import java.util.concurrent.ConcurrentHashMap;
54 import java.util.concurrent.ConcurrentMap;
55 import sun.security.action.GetPropertyAction;
56 import sun.util.calendar.ZoneInfo;
57 import sun.util.resources.LocaleData;
58 import sun.util.resources.OpenListResourceBundle;
59 import sun.util.resources.ParallelListResourceBundle;
60 import sun.util.resources.TimeZoneNamesBundle;
61
62 /**
63 * Central accessor to locale-dependent resources for JRE/CLDR provider adapters.
64 *
65 * @author Masayoshi Okutsu
66 * @author Naoto Sato
67 */
68 public class LocaleResources {
69
70 private final Locale locale;
71 private final LocaleData localeData;
72 private final LocaleProviderAdapter.Type type;
73
74 // Resource cache
75 private final ConcurrentMap<String, ResourceReference> cache = new ConcurrentHashMap<>();
76 private final ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
77
78 // cache key prefixes
79 private static final String BREAK_ITERATOR_INFO = "BII.";
80 private static final String CALENDAR_DATA = "CALD.";
81 private static final String COLLATION_DATA_CACHEKEY = "COLD";
82 private static final String DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY = "DFSD";
83 private static final String CURRENCY_NAMES = "CN.";
84 private static final String LOCALE_NAMES = "LN.";
85 private static final String TIME_ZONE_NAMES = "TZN.";
86 private static final String ZONE_IDS_CACHEKEY = "ZID";
87 private static final String CALENDAR_NAMES = "CALN.";
88 private static final String NUMBER_PATTERNS_CACHEKEY = "NP";
89 private static final String DATE_TIME_PATTERN = "DTP.";
90
91 // TimeZoneNamesBundle exemplar city prefix
92 private static final String TZNB_EXCITY_PREFIX = "timezone.excity.";
93
94 // null singleton cache value
95 private static final Object NULLOBJECT = new Object();
96
97 LocaleResources(ResourceBundleBasedAdapter adapter, Locale locale) {
98 this.locale = locale;
99 this.localeData = adapter.getLocaleData();
100 type = ((LocaleProviderAdapter)adapter).getAdapterType();
101 }
102
103 private void removeEmptyReferences() {
104 Object ref;
105 while ((ref = referenceQueue.poll()) != null) {
106 cache.remove(((ResourceReference)ref).getCacheKey());
107 }
108 }
109
110 Object getBreakIteratorInfo(String key) {
111 Object biInfo;
112 String cacheKey = BREAK_ITERATOR_INFO + key;
113
241
242 if (data != null && ((localeName = data.get()) != null)) {
243 if (localeName.equals(NULLOBJECT)) {
244 localeName = null;
245 }
246
247 return (String) localeName;
248 }
249
250 OpenListResourceBundle olrb = localeData.getLocaleNames(locale);
251
252 if (olrb.containsKey(key)) {
253 localeName = olrb.getObject(key);
254 cache.put(cacheKey,
255 new ResourceReference(cacheKey, localeName, referenceQueue));
256 }
257
258 return (String) localeName;
259 }
260
261 public Object getTimeZoneNames(String key) {
262 Object val = null;
263 String cacheKey = TIME_ZONE_NAMES + key;
264
265 removeEmptyReferences();
266 ResourceReference data = cache.get(cacheKey);
267
268 if (Objects.isNull(data) || Objects.isNull(val = data.get())) {
269 TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale);
270 if (tznb.containsKey(key)) {
271 if (key.startsWith(TZNB_EXCITY_PREFIX)) {
272 val = tznb.getString(key);
273 assert val instanceof String;
274 trace("tznb: %s key: %s, val: %s\n", tznb, key, val);
275 } else {
276 String[] names = tznb.getStringArray(key);
277 trace("tznb: %s key: %s, names: %s, %s, %s, %s, %s, %s, %s\n", tznb, key,
278 names[0], names[1], names[2], names[3], names[4], names[5], names[6]);
279 val = names;
280 }
281 cache.put(cacheKey,
282 new ResourceReference(cacheKey, val, referenceQueue));
283 }
284 }
285
286 return val;
287 }
288
289 @SuppressWarnings("unchecked")
290 Set<String> getZoneIDs() {
291 Set<String> zoneIDs = null;
292
293 removeEmptyReferences();
294 ResourceReference data = cache.get(ZONE_IDS_CACHEKEY);
295 if (data == null || ((zoneIDs = (Set<String>) data.get()) == null)) {
296 TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);
297 zoneIDs = rb.keySet();
298 cache.put(ZONE_IDS_CACHEKEY,
299 new ResourceReference(ZONE_IDS_CACHEKEY, (Object) zoneIDs, referenceQueue));
300 }
301
302 return zoneIDs;
303 }
304
305 // zoneStrings are cached separately in TimeZoneNameUtility.
306 String[][] getZoneStrings() {
307 TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);
308 Set<String> keyset = getZoneIDs();
309 // Use a LinkedHashSet to preseve the order
310 Set<String[]> value = new LinkedHashSet<>();
311 for (String key : keyset) {
312 if (!key.startsWith(TZNB_EXCITY_PREFIX)) {
313 value.add(rb.getStringArray(key));
314 }
315 }
316
317 // Add aliases data for CLDR
318 if (type == LocaleProviderAdapter.Type.CLDR) {
319 // Note: TimeZoneNamesBundle creates a String[] on each getStringArray call.
320 Map<String, String> aliases = ZoneInfo.getAliasTable();
321 for (String alias : aliases.keySet()) {
322 if (!keyset.contains(alias)) {
323 String tzid = aliases.get(alias);
324 if (keyset.contains(tzid)) {
325 String[] val = rb.getStringArray(tzid);
326 val[0] = alias;
327 value.add(val);
328 }
329 }
330 }
331 }
332 return value.toArray(new String[0][]);
333 }
334
335 String[] getCalendarNames(String key) {
512 return null;
513 }
514
515 // for DateTimePatterns. CLDR has multiple styles, while JRE has one.
516 String[] styles = (String[])value;
517 return (styles.length > 1 ? styles[styleIndex] : styles[0]);
518 }
519
520 private static class ResourceReference extends SoftReference<Object> {
521 private final String cacheKey;
522
523 ResourceReference(String cacheKey, Object o, ReferenceQueue<Object> q) {
524 super(o, q);
525 this.cacheKey = cacheKey;
526 }
527
528 String getCacheKey() {
529 return cacheKey;
530 }
531 }
532
533 private static final boolean TRACE_ON = Boolean.valueOf(
534 GetPropertyAction.privilegedGetProperty("locale.resources.debug", "false"));
535
536 public static void trace(String format, Object... params) {
537 if (TRACE_ON) {
538 System.out.format(format, params);
539 }
540 }
541 }
|