24 */ |
24 */ |
25 |
25 |
26 package sun.util.locale.provider; |
26 package sun.util.locale.provider; |
27 |
27 |
28 import java.lang.ref.SoftReference; |
28 import java.lang.ref.SoftReference; |
29 import java.util.Enumeration; |
|
30 import java.util.LinkedList; |
29 import java.util.LinkedList; |
31 import java.util.List; |
30 import java.util.List; |
32 import java.util.Locale; |
31 import java.util.Locale; |
33 import java.util.Map; |
32 import java.util.Map; |
34 import java.util.Set; |
|
35 import java.util.concurrent.ConcurrentHashMap; |
33 import java.util.concurrent.ConcurrentHashMap; |
36 import java.util.spi.TimeZoneNameProvider; |
34 import java.util.spi.TimeZoneNameProvider; |
37 import sun.util.calendar.ZoneInfo; |
35 import sun.util.calendar.ZoneInfo; |
38 import sun.util.resources.OpenListResourceBundle; |
36 import sun.util.resources.OpenListResourceBundle; |
|
37 import sun.util.resources.TimeZoneNamesBundle; |
39 |
38 |
40 /** |
39 /** |
41 * Utility class that deals with the localized time zone names |
40 * Utility class that deals with the localized time zone names |
42 * |
41 * |
43 * @author Naoto Sato |
42 * @author Naoto Sato |
|
43 * @author Masayoshi Okutsu |
44 */ |
44 */ |
45 public final class TimeZoneNameUtility { |
45 public final class TimeZoneNameUtility { |
46 |
46 |
47 /** |
47 /** |
48 * cache to hold time zone resource bundles. Keyed by Locale |
48 * cache to hold time zone resource bundles. Keyed by Locale |
49 */ |
49 */ |
50 private static ConcurrentHashMap<Locale, SoftReference<OpenListResourceBundle>> cachedBundles = |
50 private static ConcurrentHashMap<Locale, SoftReference<TimeZoneNamesBundle>> cachedBundles = |
51 new ConcurrentHashMap<>(); |
51 new ConcurrentHashMap<>(); |
52 |
52 |
53 /** |
53 /** |
54 * cache to hold time zone localized strings. Keyed by Locale |
54 * cache to hold time zone localized strings. Keyed by Locale |
55 */ |
55 */ |
71 |
71 |
72 return zones; |
72 return zones; |
73 } |
73 } |
74 |
74 |
75 private static String[][] loadZoneStrings(Locale locale) { |
75 private static String[][] loadZoneStrings(Locale locale) { |
|
76 // If the provider is a TimeZoneNameProviderImpl, call its getZoneStrings |
|
77 // in order to avoid per-ID retrieval. |
|
78 LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(TimeZoneNameProvider.class, locale); |
|
79 TimeZoneNameProvider provider = adapter.getTimeZoneNameProvider(); |
|
80 if (provider instanceof TimeZoneNameProviderImpl) { |
|
81 return ((TimeZoneNameProviderImpl)provider).getZoneStrings(locale); |
|
82 } |
|
83 |
|
84 // Performs per-ID retrieval. |
76 List<String[]> zones = new LinkedList<>(); |
85 List<String[]> zones = new LinkedList<>(); |
77 OpenListResourceBundle rb = getBundle(locale); |
86 OpenListResourceBundle rb = getBundle(locale); |
78 Enumeration<String> keys = rb.getKeys(); |
87 for (String key : rb.keySet()) { |
79 String[] names; |
88 String[] names = retrieveDisplayNamesImpl(key, locale); |
80 |
|
81 while(keys.hasMoreElements()) { |
|
82 String key = keys.nextElement(); |
|
83 |
|
84 names = retrieveDisplayNames(rb, key, locale); |
|
85 if (names != null) { |
89 if (names != null) { |
86 zones.add(names); |
90 zones.add(names); |
87 } |
91 } |
88 } |
92 } |
89 |
93 |
93 |
97 |
94 /** |
98 /** |
95 * Retrieve display names for a time zone ID. |
99 * Retrieve display names for a time zone ID. |
96 */ |
100 */ |
97 public static String[] retrieveDisplayNames(String id, Locale locale) { |
101 public static String[] retrieveDisplayNames(String id, Locale locale) { |
98 OpenListResourceBundle rb = getBundle(locale); |
|
99 return retrieveDisplayNames(rb, id, locale); |
|
100 } |
|
101 |
|
102 private static String[] retrieveDisplayNames(OpenListResourceBundle rb, |
|
103 String id, Locale locale) { |
|
104 if (id == null || locale == null) { |
102 if (id == null || locale == null) { |
105 throw new NullPointerException(); |
103 throw new NullPointerException(); |
106 } |
104 } |
107 |
105 return retrieveDisplayNamesImpl(id, locale); |
|
106 } |
|
107 |
|
108 /** |
|
109 * Retrieves a generic time zone display name for a time zone ID. |
|
110 * |
|
111 * @param id time zone ID |
|
112 * @param style TimeZone.LONG or TimeZone.SHORT |
|
113 * @param locale desired Locale |
|
114 * @return the requested generic time zone display name, or null if not found. |
|
115 */ |
|
116 public static String retrieveGenericDisplayName(String id, int style, Locale locale) { |
108 LocaleServiceProviderPool pool = |
117 LocaleServiceProviderPool pool = |
109 LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class); |
118 LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class); |
110 return pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale, id); |
119 return pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale, "generic", style, id); |
111 } |
120 } |
112 |
121 |
113 private static OpenListResourceBundle getBundle(Locale locale) { |
122 /** |
114 OpenListResourceBundle rb; |
123 * Retrieves a standard or daylight-saving time name for the given time zone ID. |
115 SoftReference<OpenListResourceBundle> data = cachedBundles.get(locale); |
124 * |
|
125 * @param id time zone ID |
|
126 * @param daylight true for a daylight saving time name, or false for a standard time name |
|
127 * @param style TimeZone.LONG or TimeZone.SHORT |
|
128 * @param locale desired Locale |
|
129 * @return the requested time zone name, or null if not found. |
|
130 */ |
|
131 public static String retrieveDisplayName(String id, boolean daylight, int style, Locale locale) { |
|
132 LocaleServiceProviderPool pool = |
|
133 LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class); |
|
134 return pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale, daylight ? "dst" : "std", style, id); |
|
135 } |
|
136 |
|
137 private static String[] retrieveDisplayNamesImpl(String id, Locale locale) { |
|
138 LocaleServiceProviderPool pool = |
|
139 LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class); |
|
140 return pool.getLocalizedObject(TimeZoneNameArrayGetter.INSTANCE, locale, id); |
|
141 } |
|
142 |
|
143 private static TimeZoneNamesBundle getBundle(Locale locale) { |
|
144 TimeZoneNamesBundle rb; |
|
145 SoftReference<TimeZoneNamesBundle> data = cachedBundles.get(locale); |
116 |
146 |
117 if (data == null || ((rb = data.get()) == null)) { |
147 if (data == null || ((rb = data.get()) == null)) { |
118 rb = LocaleProviderAdapter.forJRE().getLocaleData().getTimeZoneNames(locale); |
148 rb = LocaleProviderAdapter.forJRE().getLocaleData().getTimeZoneNames(locale); |
119 data = new SoftReference<>(rb); |
149 data = new SoftReference<>(rb); |
120 cachedBundles.put(locale, data); |
150 cachedBundles.put(locale, data); |
125 |
155 |
126 /** |
156 /** |
127 * Obtains a localized time zone strings from a TimeZoneNameProvider |
157 * Obtains a localized time zone strings from a TimeZoneNameProvider |
128 * implementation. |
158 * implementation. |
129 */ |
159 */ |
130 private static class TimeZoneNameGetter |
160 private static class TimeZoneNameArrayGetter |
131 implements LocaleServiceProviderPool.LocalizedObjectGetter<TimeZoneNameProvider, |
161 implements LocaleServiceProviderPool.LocalizedObjectGetter<TimeZoneNameProvider, |
132 String[]>{ |
162 String[]>{ |
133 private static final TimeZoneNameGetter INSTANCE = |
163 private static final TimeZoneNameArrayGetter INSTANCE = |
134 new TimeZoneNameGetter(); |
164 new TimeZoneNameArrayGetter(); |
135 |
165 |
136 @Override |
166 @Override |
137 public String[] getObject(TimeZoneNameProvider timeZoneNameProvider, |
167 public String[] getObject(TimeZoneNameProvider timeZoneNameProvider, |
138 Locale locale, |
168 Locale locale, |
139 String requestID, |
169 String requestID, |
140 Object... params) { |
170 Object... params) { |
141 assert params.length == 0; |
171 assert params.length == 0; |
142 String queryID = requestID; |
|
143 |
172 |
144 // First, try to get names with the request ID |
173 // First, try to get names with the request ID |
145 String[] names = buildZoneStrings(timeZoneNameProvider, locale, requestID); |
174 String[] names = buildZoneStrings(timeZoneNameProvider, locale, requestID); |
146 |
175 |
147 if (names == null) { |
176 if (names == null) { |
148 Map<String, String> aliases = ZoneInfo.getAliasTable(); |
177 Map<String, String> aliases = ZoneInfo.getAliasTable(); |
149 |
178 |
150 if (aliases != null) { |
179 if (aliases != null) { |
151 // Check whether this id is an alias, if so, |
180 // Check whether this id is an alias, if so, |
152 // look for the standard id. |
181 // look for the standard id. |
153 if (aliases.containsKey(queryID)) { |
182 String canonicalID = aliases.get(requestID); |
154 String prevID = queryID; |
183 if (canonicalID != null) { |
155 while ((queryID = aliases.get(queryID)) != null) { |
184 names = buildZoneStrings(timeZoneNameProvider, locale, canonicalID); |
156 prevID = queryID; |
185 } |
157 } |
|
158 queryID = prevID; |
|
159 } |
|
160 |
|
161 names = buildZoneStrings(timeZoneNameProvider, locale, queryID); |
|
162 |
|
163 if (names == null) { |
186 if (names == null) { |
164 // There may be a case that a standard id has become an |
187 // There may be a case that a standard id has become an |
165 // alias. so, check the aliases backward. |
188 // alias. so, check the aliases backward. |
166 names = examineAliases(timeZoneNameProvider, locale, |
189 names = examineAliases(timeZoneNameProvider, locale, |
167 queryID, aliases, aliases.entrySet()); |
190 canonicalID == null ? requestID : canonicalID, aliases); |
168 } |
191 } |
169 } |
192 } |
170 } |
193 } |
171 |
194 |
172 if (names != null) { |
195 if (names != null) { |
176 return names; |
199 return names; |
177 } |
200 } |
178 |
201 |
179 private static String[] examineAliases(TimeZoneNameProvider tznp, Locale locale, |
202 private static String[] examineAliases(TimeZoneNameProvider tznp, Locale locale, |
180 String id, |
203 String id, |
181 Map<String, String> aliases, |
204 Map<String, String> aliases) { |
182 Set<Map.Entry<String, String>> aliasesSet) { |
|
183 if (aliases.containsValue(id)) { |
205 if (aliases.containsValue(id)) { |
184 for (Map.Entry<String, String> entry : aliasesSet) { |
206 for (Map.Entry<String, String> entry : aliases.entrySet()) { |
185 if (entry.getValue().equals(id)) { |
207 if (entry.getValue().equals(id)) { |
186 String alias = entry.getKey(); |
208 String alias = entry.getKey(); |
187 String[] names = buildZoneStrings(tznp, locale, alias); |
209 String[] names = buildZoneStrings(tznp, locale, alias); |
188 if (names != null) { |
210 if (names != null) { |
189 return names; |
211 return names; |
190 } else { |
|
191 names = examineAliases(tznp, locale, alias, aliases, aliasesSet); |
|
192 if (names != null) { |
|
193 return names; |
|
194 } |
|
195 } |
212 } |
|
213 names = examineAliases(tznp, locale, alias, aliases); |
|
214 if (names != null) { |
|
215 return names; |
|
216 } |
196 } |
217 } |
197 } |
218 } |
198 } |
219 } |
199 |
220 |
200 return null; |
221 return null; |
201 } |
222 } |
202 |
223 |
203 private static String[] buildZoneStrings(TimeZoneNameProvider tznp, |
224 private static String[] buildZoneStrings(TimeZoneNameProvider tznp, |
204 Locale locale, String id) { |
225 Locale locale, String id) { |
205 String[] names = new String[5]; |
226 String[] names = new String[5]; |
206 |
227 |
207 for (int i = 1; i <= 4; i ++) { |
228 for (int i = 1; i <= 4; i ++) { |
208 names[i] = tznp.getDisplayName(id, i>=3, i%2, locale); |
229 names[i] = tznp.getDisplayName(id, i>=3, i%2, locale); |
209 if (i >= 3 && names[i] == null) { |
230 if (i >= 3 && names[i] == null) { |
218 |
239 |
219 return names; |
240 return names; |
220 } |
241 } |
221 } |
242 } |
222 |
243 |
|
244 private static class TimeZoneNameGetter |
|
245 implements LocaleServiceProviderPool.LocalizedObjectGetter<TimeZoneNameProvider, |
|
246 String> { |
|
247 private static final TimeZoneNameGetter INSTANCE = |
|
248 new TimeZoneNameGetter(); |
|
249 |
|
250 @Override |
|
251 public String getObject(TimeZoneNameProvider timeZoneNameProvider, |
|
252 Locale locale, |
|
253 String requestID, |
|
254 Object... params) { |
|
255 assert params.length == 2; |
|
256 int style = (int) params[0]; |
|
257 String tzid = (String) params[1]; |
|
258 String value = getName(timeZoneNameProvider, locale, requestID, style, tzid); |
|
259 if (value == null) { |
|
260 Map<String, String> aliases = ZoneInfo.getAliasTable(); |
|
261 if (aliases != null) { |
|
262 String canonicalID = aliases.get(tzid); |
|
263 if (canonicalID != null) { |
|
264 value = getName(timeZoneNameProvider, locale, requestID, style, canonicalID); |
|
265 } |
|
266 if (value == null) { |
|
267 value = examineAliases(timeZoneNameProvider, locale, requestID, |
|
268 canonicalID != null ? canonicalID : tzid, style, aliases); |
|
269 } |
|
270 } |
|
271 } |
|
272 |
|
273 return value; |
|
274 } |
|
275 |
|
276 private static String examineAliases(TimeZoneNameProvider tznp, Locale locale, |
|
277 String requestID, String tzid, int style, |
|
278 Map<String, String> aliases) { |
|
279 if (aliases.containsValue(tzid)) { |
|
280 for (Map.Entry<String, String> entry : aliases.entrySet()) { |
|
281 if (entry.getValue().equals(tzid)) { |
|
282 String alias = entry.getKey(); |
|
283 String name = getName(tznp, locale, requestID, style, alias); |
|
284 if (name != null) { |
|
285 return name; |
|
286 } |
|
287 name = examineAliases(tznp, locale, requestID, alias, style, aliases); |
|
288 if (name != null) { |
|
289 return name; |
|
290 } |
|
291 } |
|
292 } |
|
293 } |
|
294 return null; |
|
295 } |
|
296 |
|
297 private static String getName(TimeZoneNameProvider timeZoneNameProvider, |
|
298 Locale locale, String requestID, int style, String tzid) { |
|
299 String value = null; |
|
300 switch (requestID) { |
|
301 case "std": |
|
302 value = timeZoneNameProvider.getDisplayName(tzid, false, style, locale); |
|
303 break; |
|
304 case "dst": |
|
305 value = timeZoneNameProvider.getDisplayName(tzid, true, style, locale); |
|
306 break; |
|
307 case "generic": |
|
308 value = timeZoneNameProvider.getGenericDisplayName(tzid, style, locale); |
|
309 break; |
|
310 } |
|
311 return value; |
|
312 } |
|
313 } |
|
314 |
223 // No instantiation |
315 // No instantiation |
224 private TimeZoneNameUtility() { |
316 private TimeZoneNameUtility() { |
225 } |
317 } |
226 } |
318 } |