Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java
41161 views
1
/*
2
* Copyright (c) 2012, 2020, 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
23
* questions.
24
*/
25
26
/*
27
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28
* (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
29
*
30
* The original version of this source code and documentation
31
* is copyrighted and owned by Taligent, Inc., a wholly-owned
32
* subsidiary of IBM. These materials are provided under terms
33
* of a License Agreement between Taligent and Sun. This technology
34
* is protected by multiple US and International patents.
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.text.NumberFormat;
47
import java.util.Arrays;
48
import java.util.Calendar;
49
import java.util.HashSet;
50
import java.util.LinkedHashSet;
51
import java.util.Locale;
52
import java.util.Objects;
53
import java.util.ResourceBundle;
54
import java.util.Set;
55
import java.util.TimeZone;
56
import java.util.concurrent.ConcurrentHashMap;
57
import java.util.concurrent.ConcurrentMap;
58
import sun.security.action.GetPropertyAction;
59
import sun.util.resources.LocaleData;
60
import sun.util.resources.OpenListResourceBundle;
61
import sun.util.resources.ParallelListResourceBundle;
62
import sun.util.resources.TimeZoneNamesBundle;
63
64
/**
65
* Central accessor to locale-dependent resources for JRE/CLDR provider adapters.
66
*
67
* @author Masayoshi Okutsu
68
* @author Naoto Sato
69
*/
70
public class LocaleResources {
71
72
private final Locale locale;
73
private final LocaleData localeData;
74
private final LocaleProviderAdapter.Type type;
75
76
// Resource cache
77
private final ConcurrentMap<String, ResourceReference> cache = new ConcurrentHashMap<>();
78
private final ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
79
80
// cache key prefixes
81
private static final String BREAK_ITERATOR_INFO = "BII.";
82
private static final String CALENDAR_DATA = "CALD.";
83
private static final String COLLATION_DATA_CACHEKEY = "COLD";
84
private static final String DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY = "DFSD";
85
private static final String CURRENCY_NAMES = "CN.";
86
private static final String LOCALE_NAMES = "LN.";
87
private static final String TIME_ZONE_NAMES = "TZN.";
88
private static final String ZONE_IDS_CACHEKEY = "ZID";
89
private static final String CALENDAR_NAMES = "CALN.";
90
private static final String NUMBER_PATTERNS_CACHEKEY = "NP";
91
private static final String COMPACT_NUMBER_PATTERNS_CACHEKEY = "CNP";
92
private static final String DATE_TIME_PATTERN = "DTP.";
93
private static final String RULES_CACHEKEY = "RULE";
94
95
// TimeZoneNamesBundle exemplar city prefix
96
private static final String TZNB_EXCITY_PREFIX = "timezone.excity.";
97
98
// null singleton cache value
99
private static final Object NULLOBJECT = new Object();
100
101
LocaleResources(ResourceBundleBasedAdapter adapter, Locale locale) {
102
this.locale = locale;
103
this.localeData = adapter.getLocaleData();
104
type = ((LocaleProviderAdapter)adapter).getAdapterType();
105
}
106
107
private void removeEmptyReferences() {
108
Object ref;
109
while ((ref = referenceQueue.poll()) != null) {
110
cache.remove(((ResourceReference)ref).getCacheKey());
111
}
112
}
113
114
Object getBreakIteratorInfo(String key) {
115
Object biInfo;
116
String cacheKey = BREAK_ITERATOR_INFO + key;
117
118
removeEmptyReferences();
119
ResourceReference data = cache.get(cacheKey);
120
if (data == null || ((biInfo = data.get()) == null)) {
121
biInfo = localeData.getBreakIteratorInfo(locale).getObject(key);
122
cache.put(cacheKey, new ResourceReference(cacheKey, biInfo, referenceQueue));
123
}
124
125
return biInfo;
126
}
127
128
byte[] getBreakIteratorResources(String key) {
129
return (byte[]) localeData.getBreakIteratorResources(locale).getObject(key);
130
}
131
132
public String getCalendarData(String key) {
133
String caldata = "";
134
String cacheKey = CALENDAR_DATA + key;
135
136
removeEmptyReferences();
137
138
ResourceReference data = cache.get(cacheKey);
139
if (data == null || ((caldata = (String) data.get()) == null)) {
140
ResourceBundle rb = localeData.getCalendarData(locale);
141
if (rb.containsKey(key)) {
142
caldata = rb.getString(key);
143
}
144
145
cache.put(cacheKey,
146
new ResourceReference(cacheKey, caldata, referenceQueue));
147
}
148
149
return caldata;
150
}
151
152
public String getCollationData() {
153
String key = "Rule";
154
String coldata = "";
155
156
removeEmptyReferences();
157
ResourceReference data = cache.get(COLLATION_DATA_CACHEKEY);
158
if (data == null || ((coldata = (String) data.get()) == null)) {
159
ResourceBundle rb = localeData.getCollationData(locale);
160
if (rb.containsKey(key)) {
161
coldata = rb.getString(key);
162
}
163
cache.put(COLLATION_DATA_CACHEKEY,
164
new ResourceReference(COLLATION_DATA_CACHEKEY, coldata, referenceQueue));
165
}
166
167
return coldata;
168
}
169
170
public Object[] getDecimalFormatSymbolsData() {
171
Object[] dfsdata;
172
173
removeEmptyReferences();
174
ResourceReference data = cache.get(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY);
175
if (data == null || ((dfsdata = (Object[]) data.get()) == null)) {
176
// Note that only dfsdata[0] is prepared here in this method. Other
177
// elements are provided by the caller, yet they are cached here.
178
ResourceBundle rb = localeData.getNumberFormatData(locale);
179
dfsdata = new Object[3];
180
dfsdata[0] = getNumberStrings(rb, "NumberElements");
181
182
cache.put(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY,
183
new ResourceReference(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY, dfsdata, referenceQueue));
184
}
185
186
return dfsdata;
187
}
188
189
private String[] getNumberStrings(ResourceBundle rb, String type) {
190
String[] ret = null;
191
String key;
192
String numSys;
193
194
// Number strings look up. First, try the Unicode extension
195
numSys = locale.getUnicodeLocaleType("nu");
196
if (numSys != null) {
197
key = numSys + "." + type;
198
if (rb.containsKey(key)) {
199
ret = rb.getStringArray(key);
200
}
201
}
202
203
// Next, try DefaultNumberingSystem value
204
if (ret == null && rb.containsKey("DefaultNumberingSystem")) {
205
key = rb.getString("DefaultNumberingSystem") + "." + type;
206
if (rb.containsKey(key)) {
207
ret = rb.getStringArray(key);
208
}
209
}
210
211
// Last resort. No need to check the availability.
212
// Just let it throw MissingResourceException when needed.
213
if (ret == null) {
214
ret = rb.getStringArray(type);
215
}
216
217
return ret;
218
}
219
220
public String getCurrencyName(String key) {
221
Object currencyName = null;
222
String cacheKey = CURRENCY_NAMES + key;
223
224
removeEmptyReferences();
225
ResourceReference data = cache.get(cacheKey);
226
227
if (data != null && ((currencyName = data.get()) != null)) {
228
if (currencyName.equals(NULLOBJECT)) {
229
currencyName = null;
230
}
231
232
return (String) currencyName;
233
}
234
235
OpenListResourceBundle olrb = localeData.getCurrencyNames(locale);
236
237
if (olrb.containsKey(key)) {
238
currencyName = olrb.getObject(key);
239
cache.put(cacheKey,
240
new ResourceReference(cacheKey, currencyName, referenceQueue));
241
}
242
243
return (String) currencyName;
244
}
245
246
public String getLocaleName(String key) {
247
Object localeName = null;
248
String cacheKey = LOCALE_NAMES + key;
249
250
removeEmptyReferences();
251
ResourceReference data = cache.get(cacheKey);
252
253
if (data != null && ((localeName = data.get()) != null)) {
254
if (localeName.equals(NULLOBJECT)) {
255
localeName = null;
256
}
257
258
return (String) localeName;
259
}
260
261
OpenListResourceBundle olrb = localeData.getLocaleNames(locale);
262
263
if (olrb.containsKey(key)) {
264
localeName = olrb.getObject(key);
265
cache.put(cacheKey,
266
new ResourceReference(cacheKey, localeName, referenceQueue));
267
}
268
269
return (String) localeName;
270
}
271
272
public Object getTimeZoneNames(String key) {
273
Object val = null;
274
String cacheKey = TIME_ZONE_NAMES + key;
275
276
removeEmptyReferences();
277
ResourceReference data = cache.get(cacheKey);
278
279
if (Objects.isNull(data) || Objects.isNull(val = data.get())) {
280
TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale);
281
if (key.startsWith(TZNB_EXCITY_PREFIX)) {
282
if (tznb.containsKey(key)) {
283
val = tznb.getString(key);
284
assert val instanceof String;
285
trace("tznb: %s key: %s, val: %s\n", tznb, key, val);
286
}
287
} else {
288
String[] names = null;
289
if (tznb.containsKey(key)) {
290
names = tznb.getStringArray(key);
291
} else {
292
var tz = TimeZoneNameUtility.canonicalTZID(key).orElse(key);
293
if (tznb.containsKey(tz)) {
294
names = tznb.getStringArray(tz);
295
}
296
}
297
298
if (names != null) {
299
names[0] = key;
300
trace("tznb: %s key: %s, names: %s, %s, %s, %s, %s, %s, %s\n", tznb, key,
301
names[0], names[1], names[2], names[3], names[4], names[5], names[6]);
302
val = names;
303
}
304
}
305
if (val != null) {
306
cache.put(cacheKey,
307
new ResourceReference(cacheKey, val, referenceQueue));
308
}
309
}
310
311
return val;
312
}
313
314
@SuppressWarnings("unchecked")
315
Set<String> getZoneIDs() {
316
Set<String> zoneIDs;
317
318
removeEmptyReferences();
319
ResourceReference data = cache.get(ZONE_IDS_CACHEKEY);
320
if (data == null || ((zoneIDs = (Set<String>) data.get()) == null)) {
321
TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);
322
zoneIDs = rb.keySet();
323
cache.put(ZONE_IDS_CACHEKEY,
324
new ResourceReference(ZONE_IDS_CACHEKEY, zoneIDs, referenceQueue));
325
}
326
327
return zoneIDs;
328
}
329
330
// zoneStrings are cached separately in TimeZoneNameUtility.
331
String[][] getZoneStrings() {
332
TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);
333
Set<String> keyset = getZoneIDs();
334
// Use a LinkedHashSet to preseve the order
335
Set<String[]> value = new LinkedHashSet<>();
336
Set<String> tzIds = new HashSet<>(Arrays.asList(TimeZone.getAvailableIDs()));
337
for (String key : keyset) {
338
if (!key.startsWith(TZNB_EXCITY_PREFIX)) {
339
value.add(rb.getStringArray(key));
340
tzIds.remove(key);
341
}
342
}
343
344
if (type == LocaleProviderAdapter.Type.CLDR) {
345
// Note: TimeZoneNamesBundle creates a String[] on each getStringArray call.
346
347
// Add timezones which are not present in this keyset,
348
// so that their fallback names will be generated at runtime.
349
tzIds.stream().filter(i -> (!i.startsWith("Etc/GMT")
350
&& !i.startsWith("GMT")
351
&& !i.startsWith("SystemV")))
352
.forEach(tzid -> {
353
String[] val = new String[7];
354
if (keyset.contains(tzid)) {
355
val = rb.getStringArray(tzid);
356
} else {
357
var canonID = TimeZoneNameUtility.canonicalTZID(tzid)
358
.orElse(tzid);
359
if (keyset.contains(canonID)) {
360
val = rb.getStringArray(canonID);
361
}
362
}
363
val[0] = tzid;
364
value.add(val);
365
});
366
}
367
return value.toArray(new String[0][]);
368
}
369
370
String[] getCalendarNames(String key) {
371
String[] names = null;
372
String cacheKey = CALENDAR_NAMES + key;
373
374
removeEmptyReferences();
375
ResourceReference data = cache.get(cacheKey);
376
377
if (data == null || ((names = (String[]) data.get()) == null)) {
378
ResourceBundle rb = localeData.getDateFormatData(locale);
379
if (rb.containsKey(key)) {
380
names = rb.getStringArray(key);
381
cache.put(cacheKey,
382
new ResourceReference(cacheKey, names, referenceQueue));
383
}
384
}
385
386
return names;
387
}
388
389
String[] getJavaTimeNames(String key) {
390
String[] names = null;
391
String cacheKey = CALENDAR_NAMES + key;
392
393
removeEmptyReferences();
394
ResourceReference data = cache.get(cacheKey);
395
396
if (data == null || ((names = (String[]) data.get()) == null)) {
397
ResourceBundle rb = getJavaTimeFormatData();
398
if (rb.containsKey(key)) {
399
names = rb.getStringArray(key);
400
cache.put(cacheKey,
401
new ResourceReference(cacheKey, names, referenceQueue));
402
}
403
}
404
405
return names;
406
}
407
408
public String getDateTimePattern(int timeStyle, int dateStyle, Calendar cal) {
409
if (cal == null) {
410
cal = Calendar.getInstance(locale);
411
}
412
return getDateTimePattern(null, timeStyle, dateStyle, cal.getCalendarType());
413
}
414
415
/**
416
* Returns a date-time format pattern
417
* @param timeStyle style of time; one of FULL, LONG, MEDIUM, SHORT in DateFormat,
418
* or -1 if not required
419
* @param dateStyle style of time; one of FULL, LONG, MEDIUM, SHORT in DateFormat,
420
* or -1 if not required
421
* @param calType the calendar type for the pattern
422
* @return the pattern string
423
*/
424
public String getJavaTimeDateTimePattern(int timeStyle, int dateStyle, String calType) {
425
calType = CalendarDataUtility.normalizeCalendarType(calType);
426
String pattern;
427
pattern = getDateTimePattern("java.time.", timeStyle, dateStyle, calType);
428
if (pattern == null) {
429
pattern = getDateTimePattern(null, timeStyle, dateStyle, calType);
430
}
431
return pattern;
432
}
433
434
private String getDateTimePattern(String prefix, int timeStyle, int dateStyle, String calType) {
435
String pattern;
436
String timePattern = null;
437
String datePattern = null;
438
439
if (timeStyle >= 0) {
440
if (prefix != null) {
441
timePattern = getDateTimePattern(prefix, "TimePatterns", timeStyle, calType);
442
}
443
if (timePattern == null) {
444
timePattern = getDateTimePattern(null, "TimePatterns", timeStyle, calType);
445
}
446
}
447
if (dateStyle >= 0) {
448
if (prefix != null) {
449
datePattern = getDateTimePattern(prefix, "DatePatterns", dateStyle, calType);
450
}
451
if (datePattern == null) {
452
datePattern = getDateTimePattern(null, "DatePatterns", dateStyle, calType);
453
}
454
}
455
if (timeStyle >= 0) {
456
if (dateStyle >= 0) {
457
String dateTimePattern = null;
458
int dateTimeStyle = Math.max(dateStyle, timeStyle);
459
if (prefix != null) {
460
dateTimePattern = getDateTimePattern(prefix, "DateTimePatterns", dateTimeStyle, calType);
461
}
462
if (dateTimePattern == null) {
463
dateTimePattern = getDateTimePattern(null, "DateTimePatterns", dateTimeStyle, calType);
464
}
465
pattern = switch (Objects.requireNonNull(dateTimePattern)) {
466
case "{1} {0}" -> datePattern + " " + timePattern;
467
case "{0} {1}" -> timePattern + " " + datePattern;
468
default -> MessageFormat.format(dateTimePattern.replaceAll("'", "''"), timePattern, datePattern);
469
};
470
} else {
471
pattern = timePattern;
472
}
473
} else if (dateStyle >= 0) {
474
pattern = datePattern;
475
} else {
476
throw new IllegalArgumentException("No date or time style specified");
477
}
478
return pattern;
479
}
480
481
public String[] getNumberPatterns() {
482
String[] numberPatterns;
483
484
removeEmptyReferences();
485
ResourceReference data = cache.get(NUMBER_PATTERNS_CACHEKEY);
486
487
if (data == null || ((numberPatterns = (String[]) data.get()) == null)) {
488
ResourceBundle resource = localeData.getNumberFormatData(locale);
489
numberPatterns = getNumberStrings(resource, "NumberPatterns");
490
cache.put(NUMBER_PATTERNS_CACHEKEY,
491
new ResourceReference(NUMBER_PATTERNS_CACHEKEY, numberPatterns, referenceQueue));
492
}
493
494
return numberPatterns;
495
}
496
497
/**
498
* Returns the compact number format patterns.
499
* @param formatStyle the style for formatting a number
500
* @return an array of compact number patterns
501
*/
502
public String[] getCNPatterns(NumberFormat.Style formatStyle) {
503
504
Objects.requireNonNull(formatStyle);
505
String[] compactNumberPatterns;
506
removeEmptyReferences();
507
String width = (formatStyle == NumberFormat.Style.LONG) ? "long" : "short";
508
String cacheKey = width + "." + COMPACT_NUMBER_PATTERNS_CACHEKEY;
509
ResourceReference data = cache.get(cacheKey);
510
if (data == null || ((compactNumberPatterns
511
= (String[]) data.get()) == null)) {
512
ResourceBundle resource = localeData.getNumberFormatData(locale);
513
compactNumberPatterns = (String[]) resource
514
.getObject(width + ".CompactNumberPatterns");
515
cache.put(cacheKey, new ResourceReference(cacheKey, compactNumberPatterns, referenceQueue));
516
}
517
return compactNumberPatterns;
518
}
519
520
521
/**
522
* Returns the FormatData resource bundle of this LocaleResources.
523
* The FormatData should be used only for accessing extra
524
* resources required by JSR 310.
525
*/
526
public ResourceBundle getJavaTimeFormatData() {
527
ResourceBundle rb = localeData.getDateFormatData(locale);
528
if (rb instanceof ParallelListResourceBundle) {
529
localeData.setSupplementary((ParallelListResourceBundle) rb);
530
}
531
return rb;
532
}
533
534
private String getDateTimePattern(String prefix, String key, int styleIndex, String calendarType) {
535
StringBuilder sb = new StringBuilder();
536
if (prefix != null) {
537
sb.append(prefix);
538
}
539
if (!"gregory".equals(calendarType)) {
540
sb.append(calendarType).append('.');
541
}
542
sb.append(key);
543
String resourceKey = sb.toString();
544
String cacheKey = sb.insert(0, DATE_TIME_PATTERN).toString();
545
546
removeEmptyReferences();
547
ResourceReference data = cache.get(cacheKey);
548
Object value = NULLOBJECT;
549
550
if (data == null || ((value = data.get()) == null)) {
551
ResourceBundle r = (prefix != null) ? getJavaTimeFormatData() : localeData.getDateFormatData(locale);
552
if (r.containsKey(resourceKey)) {
553
value = r.getStringArray(resourceKey);
554
} else {
555
assert !resourceKey.equals(key);
556
if (r.containsKey(key)) {
557
value = r.getStringArray(key);
558
}
559
}
560
cache.put(cacheKey,
561
new ResourceReference(cacheKey, value, referenceQueue));
562
}
563
if (value == NULLOBJECT) {
564
assert prefix != null;
565
return null;
566
}
567
568
// for DateTimePatterns. CLDR has multiple styles, while JRE has one.
569
String[] styles = (String[])value;
570
return (styles.length > 1 ? styles[styleIndex] : styles[0]);
571
}
572
573
public String[] getRules() {
574
String[] rules;
575
576
removeEmptyReferences();
577
ResourceReference data = cache.get(RULES_CACHEKEY);
578
579
if (data == null || ((rules = (String[]) data.get()) == null)) {
580
ResourceBundle rb = localeData.getDateFormatData(locale);
581
rules = new String[2];
582
rules[0] = rules[1] = "";
583
if (rb.containsKey("PluralRules")) {
584
rules[0] = rb.getString("PluralRules");
585
}
586
if (rb.containsKey("DayPeriodRules")) {
587
rules[1] = rb.getString("DayPeriodRules");
588
}
589
cache.put(RULES_CACHEKEY, new ResourceReference(RULES_CACHEKEY, rules, referenceQueue));
590
}
591
592
return rules;
593
}
594
595
private static class ResourceReference extends SoftReference<Object> {
596
private final String cacheKey;
597
598
ResourceReference(String cacheKey, Object o, ReferenceQueue<Object> q) {
599
super(o, q);
600
this.cacheKey = cacheKey;
601
}
602
603
String getCacheKey() {
604
return cacheKey;
605
}
606
}
607
608
private static final boolean TRACE_ON = Boolean.valueOf(
609
GetPropertyAction.privilegedGetProperty("locale.resources.debug", "false"));
610
611
public static void trace(String format, Object... params) {
612
if (TRACE_ON) {
613
System.out.format(format, params);
614
}
615
}
616
}
617
618