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.text.MessageFormat;
44 import java.util.Calendar;
45 import java.util.Locale;
46 import java.util.ResourceBundle;
47 import java.util.concurrent.ConcurrentHashMap;
48 import java.util.concurrent.ConcurrentMap;
49 import sun.util.resources.TimeZoneNamesBundle;
50
51 /**
52 * Central accessor to locale-dependent resources.
53 *
54 * @author Masayoshi Okutsu
55 */
56 public class LocaleResources {
57
58 private final LocaleProviderAdapter adapter;
59 private final Locale locale;
60
61 // Resource cache
62 private ConcurrentMap<String, Object> cache = new ConcurrentHashMap<>();
63
64
65 LocaleResources(LocaleProviderAdapter adapter, Locale locale) {
66 this.adapter = adapter;
67 this.locale = locale;
68 }
69
70 public TimeZoneNamesBundle getTimeZoneNames() {
71 TimeZoneNamesBundle tznames = (TimeZoneNamesBundle) cache.get("TimeZoneNames");
72 if (tznames == null) {
73 tznames = adapter.getLocaleData().getTimeZoneNames(locale);
74 TimeZoneNamesBundle tznb = (TimeZoneNamesBundle) cache.putIfAbsent("TimeZoneNames", tznames);
75 if (tznb != null) {
76 tznames = tznb;
77 }
78 }
79 return tznames;
80 }
81
82 public String getDateTimePattern(int timeStyle, int dateStyle, Calendar cal) {
83 String pattern;
84
85 if (cal == null) {
86 cal = Calendar.getInstance(locale);
87 }
88 String calType = cal.getCalendarType();
89 String timePattern = null;
90 String datePattern = null;
91 if (timeStyle >= 0) {
92 timePattern = getDateTimePattern("TimePatterns", timeStyle, calType);
93 }
94 if (dateStyle >= 0) {
95 datePattern = getDateTimePattern("DatePatterns", dateStyle, calType);
96 }
97 if (timeStyle >= 0) {
98 if (dateStyle >= 0) {
99 String dateTimePattern = getDateTimePattern("DateTimePatterns", 0, calType);
100 switch (dateTimePattern) {
101 case "{1} {0}":
103 break;
104 case "{0} {1}":
105 pattern = timePattern + " " + datePattern;
106 break;
107 default:
108 pattern = MessageFormat.format(dateTimePattern, timePattern, datePattern);
109 break;
110 }
111 } else {
112 pattern = timePattern;
113 }
114 } else if (dateStyle >= 0) {
115 pattern = datePattern;
116 } else {
117 throw new IllegalArgumentException("No date or time style specified");
118 }
119 return pattern;
120 }
121
122 public String[] getNumberPatterns() {
123 /* try the cache first */
124 String[] numberPatterns = (String[]) cache.get("NumberPatterns");
125 if (numberPatterns == null) { /* cache miss */
126 ResourceBundle resource = adapter.getLocaleData().getNumberFormatData(locale);
127 numberPatterns = resource.getStringArray("NumberPatterns");
128 /* update cache */
129 cache.put("NumberPatterns", numberPatterns);
130 }
131 return numberPatterns;
132 }
133
134 private String getDateTimePattern(String key, int styleIndex, String calendarType) {
135 String resourceKey = "gregory".equals(calendarType) ? key : calendarType + "." + key;
136 /* try the cache first */
137 String[] patterns = (String[]) cache.get(resourceKey);
138 if (patterns == null) { /* cache miss */
139 ResourceBundle r = adapter.getLocaleData().getDateFormatData(locale);
140 if (r.containsKey(resourceKey)) {
141 patterns = r.getStringArray(resourceKey);
142 } else {
143 assert !resourceKey.equals(key);
144 patterns = r.getStringArray(key);
145 }
146 /* update cache */
147 cache.putIfAbsent(resourceKey, patterns);
148 }
149 return patterns[styleIndex];
150 }
151 }
|
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.util.Calendar;
47 import java.util.LinkedHashSet;
48 import java.util.Locale;
49 import java.util.Map;
50 import java.util.ResourceBundle;
51 import java.util.Set;
52 import java.util.concurrent.ConcurrentHashMap;
53 import java.util.concurrent.ConcurrentMap;
54 import sun.util.calendar.ZoneInfo;
55 import sun.util.resources.LocaleData;
56 import sun.util.resources.OpenListResourceBundle;
57 import sun.util.resources.TimeZoneNamesBundle;
58
59 /**
60 * Central accessor to locale-dependent resources for JRE/CLDR provider adapters.
61 *
62 * @author Masayoshi Okutsu
63 * @author Naoto Sato
64 */
65 public class LocaleResources {
66
67 private final Locale locale;
68 private final LocaleData localeData;
69 private final LocaleProviderAdapter.Type type;
70
71 // Resource cache
72 private ConcurrentMap<String, ResourceReference> cache = new ConcurrentHashMap<>();
73 private ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
74
75 // cache key prefixes
76 private static final String BREAK_ITERATOR_INFO = "BII.";
77 private static final String CALENDAR_DATA = "CALD.";
78 private static final String COLLATION_DATA_CACHEKEY = "COLD";
79 private static final String DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY = "DFSD";
80 private static final String CURRENCY_NAMES = "CN.";
81 private static final String LOCALE_NAMES = "LN.";
82 private static final String TIME_ZONE_NAMES = "TZN.";
83 private static final String ZONE_IDS_CACHEKEY = "ZID";
84 private static final String CALENDAR_NAMES = "CALN.";
85 private static final String NUMBER_PATTERNS_CACHEKEY = "NP";
86 private static final String DATE_TIME_PATTERN = "DTP.";
87
88 // null singleton cache value
89 private static final Object NULLOBJECT = new Object();
90
91 LocaleResources(ResourceBundleBasedAdapter adapter, Locale locale) {
92 this.locale = locale;
93 this.localeData = adapter.getLocaleData();
94 type = ((LocaleProviderAdapter)adapter).getAdapterType();
95 }
96
97 private void removeEmptyReferences() {
98 Object ref;
99 while ((ref = referenceQueue.poll()) != null) {
100 cache.remove(((ResourceReference)ref).getCacheKey());
101 }
102 }
103
104 Object getBreakIteratorInfo(String key) {
105 Object biInfo;
106 String cacheKey = BREAK_ITERATOR_INFO + key;
107
108 removeEmptyReferences();
109 ResourceReference data = cache.get(cacheKey);
110 if (data == null || ((biInfo = data.get()) == null)) {
111 biInfo = localeData.getBreakIteratorInfo(locale).getObject(key);
112 cache.put(cacheKey, new ResourceReference(cacheKey, biInfo, referenceQueue));
113 }
114
115 return biInfo;
116 }
117
118 int getCalendarData(String key) {
119 Integer caldata;
120 String cacheKey = CALENDAR_DATA + key;
121
122 removeEmptyReferences();
123
124 ResourceReference data = cache.get(cacheKey);
125 if (data == null || ((caldata = (Integer) data.get()) == null)) {
126 ResourceBundle rb = localeData.getCalendarData(locale);
127 if (rb.containsKey(key)) {
128 caldata = Integer.parseInt(rb.getString(key));
129 } else {
130 caldata = 0;
131 }
132
133 cache.put(cacheKey,
134 new ResourceReference(cacheKey, (Object) caldata, referenceQueue));
135 }
136
137 return caldata;
138 }
139
140 public String getCollationData() {
141 String key = "Rule";
142 String coldata = "";
143
144 removeEmptyReferences();
145 ResourceReference data = cache.get(COLLATION_DATA_CACHEKEY);
146 if (data == null || ((coldata = (String) data.get()) == null)) {
147 ResourceBundle rb = localeData.getCollationData(locale);
148 if (rb.containsKey(key)) {
149 coldata = rb.getString(key);
150 }
151 cache.put(COLLATION_DATA_CACHEKEY,
152 new ResourceReference(COLLATION_DATA_CACHEKEY, (Object) coldata, referenceQueue));
153 }
154
155 return coldata;
156 }
157
158 public Object[] getDecimalFormatSymbolsData() {
159 Object[] dfsdata;
160
161 removeEmptyReferences();
162 ResourceReference data = cache.get(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY);
163 if (data == null || ((dfsdata = (Object[]) data.get()) == null)) {
164 // Note that only dfsdata[0] is prepared here in this method. Other
165 // elements are provided by the caller, yet they are cached here.
166 ResourceBundle rb = localeData.getNumberFormatData(locale);
167 dfsdata = new Object[3];
168
169 // NumberElements look up. First, try the Unicode extension
170 String numElemKey;
171 String numberType = locale.getUnicodeLocaleType("nu");
172 if (numberType != null) {
173 numElemKey = numberType + ".NumberElements";
174 if (rb.containsKey(numElemKey)) {
175 dfsdata[0] = rb.getStringArray(numElemKey);
176 }
177 }
178
179 // Next, try DefaultNumberingSystem value
180 if (dfsdata[0] == null && rb.containsKey("DefaultNumberingSystem")) {
181 numElemKey = rb.getString("DefaultNumberingSystem") + ".NumberElements";
182 if (rb.containsKey(numElemKey)) {
183 dfsdata[0] = rb.getStringArray(numElemKey);
184 }
185 }
186
187 // Last resort. No need to check the availability.
188 // Just let it throw MissingResourceException when needed.
189 if (dfsdata[0] == null) {
190 dfsdata[0] = rb.getStringArray("NumberElements");
191 }
192
193 cache.put(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY,
194 new ResourceReference(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY, (Object) dfsdata, referenceQueue));
195 }
196
197 return dfsdata;
198 }
199
200 public String getCurrencyName(String key) {
201 Object currencyName = null;
202 String cacheKey = CURRENCY_NAMES + key;
203
204 removeEmptyReferences();
205 ResourceReference data = cache.get(cacheKey);
206
207 if (data != null && ((currencyName = data.get()) != null)) {
208 if (currencyName.equals(NULLOBJECT)) {
209 currencyName = null;
210 }
211
212 return (String) currencyName;
213 }
214
215 OpenListResourceBundle olrb = localeData.getCurrencyNames(locale);
216
217 if (olrb.containsKey(key)) {
218 currencyName = olrb.getObject(key);
219 cache.put(cacheKey,
220 new ResourceReference(cacheKey, currencyName, referenceQueue));
221 }
222
223 return (String) currencyName;
224 }
225
226 public String getLocaleName(String key) {
227 Object localeName = null;
228 String cacheKey = LOCALE_NAMES + key;
229
230 removeEmptyReferences();
231 ResourceReference data = cache.get(cacheKey);
232
233 if (data != null && ((localeName = data.get()) != null)) {
234 if (localeName.equals(NULLOBJECT)) {
235 localeName = null;
236 }
237
238 return (String) localeName;
239 }
240
241 OpenListResourceBundle olrb = localeData.getLocaleNames(locale);
242
243 if (olrb.containsKey(key)) {
244 localeName = olrb.getObject(key);
245 cache.put(cacheKey,
246 new ResourceReference(cacheKey, localeName, referenceQueue));
247 }
248
249 return (String) localeName;
250 }
251
252 String[] getTimeZoneNames(String key, int size) {
253 String[] names = null;
254 String cacheKey = TIME_ZONE_NAMES + key;
255
256 removeEmptyReferences();
257 ResourceReference data = cache.get(cacheKey);
258
259 if (data == null || ((names = (String[]) data.get()) == null)) {
260 TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale);
261 if (tznb.containsKey(key)) {
262 names = tznb.getStringArray(key, size);
263 cache.put(cacheKey,
264 new ResourceReference(cacheKey, (Object) names, referenceQueue));
265 }
266 }
267
268 return names;
269 }
270
271 @SuppressWarnings("unchecked")
272 Set<String> getZoneIDs() {
273 Set<String> zoneIDs = null;
274
275 removeEmptyReferences();
276 ResourceReference data = cache.get(ZONE_IDS_CACHEKEY);
277 if (data == null || ((zoneIDs = (Set<String>) data.get()) == null)) {
278 TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);
279 zoneIDs = rb.keySet();
280 cache.put(ZONE_IDS_CACHEKEY,
281 new ResourceReference(ZONE_IDS_CACHEKEY, (Object) zoneIDs, referenceQueue));
282 }
283
284 return zoneIDs;
285 }
286
287 // zoneStrings are cached separately in TimeZoneNameUtility.
288 String[][] getZoneStrings() {
289 TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);
290 Set<String> keyset = getZoneIDs();
291 // Use a LinkedHashSet to preseve the order
292 Set<String[]> value = new LinkedHashSet<>();
293 for (String key : keyset) {
294 value.add(rb.getStringArray(key));
295 }
296
297 // Add aliases data for CLDR
298 if (type == LocaleProviderAdapter.Type.CLDR) {
299 // Note: TimeZoneNamesBundle creates a String[] on each getStringArray call.
300 Map<String, String> aliases = ZoneInfo.getAliasTable();
301 for (String alias : aliases.keySet()) {
302 if (!keyset.contains(alias)) {
303 String tzid = aliases.get(alias);
304 if (keyset.contains(tzid)) {
305 String[] val = rb.getStringArray(tzid);
306 val[0] = alias;
307 value.add(val);
308 }
309 }
310 }
311 }
312 return value.toArray(new String[0][]);
313 }
314
315 String[] getCalendarNames(String key) {
316 String[] names = null;
317 String cacheKey = CALENDAR_NAMES + key;
318
319 removeEmptyReferences();
320 ResourceReference data = cache.get(cacheKey);
321
322 if (data == null || ((names = (String[]) data.get()) == null)) {
323 ResourceBundle rb = localeData.getDateFormatData(locale);
324 if (rb.containsKey(key)) {
325 names = rb.getStringArray(key);
326 cache.put(cacheKey,
327 new ResourceReference(cacheKey, (Object) names, referenceQueue));
328 }
329 }
330
331 return names;
332 }
333
334 public String getDateTimePattern(int timeStyle, int dateStyle, Calendar cal) {
335 String pattern;
336
337 if (cal == null) {
338 cal = Calendar.getInstance(locale);
339 }
340 String calType = cal.getCalendarType();
341 String timePattern = null;
342 String datePattern = null;
343 if (timeStyle >= 0) {
344 timePattern = getDateTimePattern("TimePatterns", timeStyle, calType);
345 }
346 if (dateStyle >= 0) {
347 datePattern = getDateTimePattern("DatePatterns", dateStyle, calType);
348 }
349 if (timeStyle >= 0) {
350 if (dateStyle >= 0) {
351 String dateTimePattern = getDateTimePattern("DateTimePatterns", 0, calType);
352 switch (dateTimePattern) {
353 case "{1} {0}":
355 break;
356 case "{0} {1}":
357 pattern = timePattern + " " + datePattern;
358 break;
359 default:
360 pattern = MessageFormat.format(dateTimePattern, timePattern, datePattern);
361 break;
362 }
363 } else {
364 pattern = timePattern;
365 }
366 } else if (dateStyle >= 0) {
367 pattern = datePattern;
368 } else {
369 throw new IllegalArgumentException("No date or time style specified");
370 }
371 return pattern;
372 }
373
374 public String[] getNumberPatterns() {
375 String[] numberPatterns = null;
376
377 removeEmptyReferences();
378 ResourceReference data = cache.get(NUMBER_PATTERNS_CACHEKEY);
379
380 if (data == null || ((numberPatterns = (String[]) data.get()) == null)) {
381 ResourceBundle resource = localeData.getNumberFormatData(locale);
382 numberPatterns = resource.getStringArray("NumberPatterns");
383 cache.put(NUMBER_PATTERNS_CACHEKEY,
384 new ResourceReference(NUMBER_PATTERNS_CACHEKEY, (Object) numberPatterns, referenceQueue));
385 }
386
387 return numberPatterns;
388 }
389
390 private String getDateTimePattern(String key, int styleIndex, String calendarType) {
391 String resourceKey = "gregory".equals(calendarType) ? key : calendarType + "." + key;
392 String cacheKey = DATE_TIME_PATTERN + resourceKey;
393 String[] patterns = null;
394
395 removeEmptyReferences();
396 ResourceReference data = cache.get(cacheKey);
397
398 if (data == null || ((patterns = (String[]) data.get()) == null)) {
399 ResourceBundle r = localeData.getDateFormatData(locale);
400 if (r.containsKey(resourceKey)) {
401 patterns = r.getStringArray(resourceKey);
402 } else {
403 assert !resourceKey.equals(key);
404 patterns = r.getStringArray(key);
405 }
406 cache.put(cacheKey,
407 new ResourceReference(cacheKey, (Object) patterns, referenceQueue));
408 }
409
410 return patterns[styleIndex];
411 }
412
413 private static class ResourceReference extends SoftReference<Object> {
414 private final String cacheKey;
415
416 ResourceReference(String cacheKey, Object o, ReferenceQueue<Object> q) {
417 super(o, q);
418 this.cacheKey = cacheKey;
419 }
420
421 String getCacheKey() {
422 return cacheKey;
423 }
424 }
425 }
|