Print this page
rev 6057 : imported patch 8001205.8001562
Split |
Close |
Expand all |
Collapse all |
--- old/src/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java
+++ new/src/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java
1 1 /*
2 2 * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
3 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 4 *
5 5 * This code is free software; you can redistribute it and/or modify it
6 6 * under the terms of the GNU General Public License version 2 only, as
7 7 * published by the Free Software Foundation. Oracle designates this
8 8 * particular file as subject to the "Classpath" exception as provided
9 9 * by Oracle in the LICENSE file that accompanied this code.
10 10 *
11 11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 14 * version 2 for more details (a copy is included in the LICENSE file that
15 15 * accompanied this code).
16 16 *
17 17 * You should have received a copy of the GNU General Public License version
18 18 * 2 along with this work; if not, write to the Free Software Foundation,
↓ open down ↓ |
18 lines elided |
↑ open up ↑ |
19 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 20 *
21 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 22 * or visit www.oracle.com if you need additional information or have any
23 23 * questions.
24 24 */
25 25
26 26 package sun.util.locale.provider;
27 27
28 28 import java.util.ArrayList;
29 +import java.util.Arrays;
29 30 import java.util.Collections;
30 31 import java.util.HashSet;
31 32 import java.util.IllformedLocaleException;
32 33 import java.util.List;
33 34 import java.util.Locale;
34 35 import java.util.Locale.Builder;
35 36 import java.util.ResourceBundle.Control;
36 37 import java.util.Set;
37 38 import java.util.concurrent.ConcurrentHashMap;
38 39 import java.util.concurrent.ConcurrentMap;
39 40 import java.util.spi.LocaleServiceProvider;
40 41 import sun.util.logging.PlatformLogger;
41 42
42 43 /**
43 44 * An instance of this class holds a set of the third party implementations of a particular
44 45 * locale sensitive service, such as {@link java.util.spi.LocaleNameProvider}.
45 46 *
46 47 * @author Naoto Sato
47 48 * @author Masayoshi Okutsu
48 49 */
49 50 public final class LocaleServiceProviderPool {
50 51
51 52 /**
52 53 * A Map that holds singleton instances of this class. Each instance holds a
53 54 * set of provider implementations of a particular locale sensitive service.
54 55 */
55 56 private static ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProviderPool> poolOfPools =
56 57 new ConcurrentHashMap<>();
57 58
58 59 /**
59 60 * A Map containing locale service providers that implement the
60 61 * specified provider SPI, keyed by a LocaleProviderAdapter.Type
61 62 */
62 63 private ConcurrentMap<LocaleProviderAdapter.Type, LocaleServiceProvider> providers =
63 64 new ConcurrentHashMap<>();
64 65
65 66 /**
66 67 * A Map that retains Locale->provider mapping
67 68 */
68 69 private ConcurrentMap<Locale, List<LocaleProviderAdapter.Type>> providersCache =
69 70 new ConcurrentHashMap<>();
70 71
71 72 /**
72 73 * Available locales for this locale sensitive service. This also contains
73 74 * JRE's available locales
74 75 */
75 76 private Set<Locale> availableLocales = null;
76 77
77 78 /**
78 79 * Provider class
79 80 */
80 81 private Class<? extends LocaleServiceProvider> providerClass;
81 82
82 83 /**
83 84 * Array of all Locale Sensitive SPI classes.
84 85 *
85 86 * We know "spiClasses" contains classes that extends LocaleServiceProvider,
86 87 * but generic array creation is not allowed, thus the "unchecked" warning
87 88 * is suppressed here.
88 89 */
89 90 @SuppressWarnings("unchecked")
90 91 static final Class<LocaleServiceProvider>[] spiClasses =
91 92 (Class<LocaleServiceProvider>[]) new Class<?>[] {
92 93 java.text.spi.BreakIteratorProvider.class,
93 94 java.text.spi.CollatorProvider.class,
94 95 java.text.spi.DateFormatProvider.class,
95 96 java.text.spi.DateFormatSymbolsProvider.class,
96 97 java.text.spi.DecimalFormatSymbolsProvider.class,
97 98 java.text.spi.NumberFormatProvider.class,
98 99 java.util.spi.CurrencyNameProvider.class,
99 100 java.util.spi.LocaleNameProvider.class,
100 101 java.util.spi.TimeZoneNameProvider.class,
101 102 java.util.spi.CalendarDataProvider.class
102 103 };
103 104
104 105 /**
105 106 * A factory method that returns a singleton instance
106 107 */
107 108 public static LocaleServiceProviderPool getPool(Class<? extends LocaleServiceProvider> providerClass) {
108 109 LocaleServiceProviderPool pool = poolOfPools.get(providerClass);
109 110 if (pool == null) {
110 111 LocaleServiceProviderPool newPool =
111 112 new LocaleServiceProviderPool(providerClass);
112 113 pool = poolOfPools.putIfAbsent(providerClass, newPool);
113 114 if (pool == null) {
114 115 pool = newPool;
115 116 }
116 117 }
117 118
118 119 return pool;
119 120 }
120 121
121 122 /**
122 123 * The sole constructor.
123 124 *
124 125 * @param c class of the locale sensitive service
125 126 */
126 127 private LocaleServiceProviderPool (final Class<? extends LocaleServiceProvider> c) {
127 128 providerClass = c;
128 129
129 130 // Add the JRE Locale Data Adapter implementation.
130 131 providers.putIfAbsent(LocaleProviderAdapter.Type.JRE,
131 132 LocaleProviderAdapter.forJRE().getLocaleServiceProvider(c));
132 133
133 134 // Add the SPI Locale Data Adapter implementation.
134 135 LocaleProviderAdapter lda = LocaleProviderAdapter.forType(LocaleProviderAdapter.Type.SPI);
135 136 LocaleServiceProvider provider = lda.getLocaleServiceProvider(c);
136 137 if (provider != null) {
137 138 providers.putIfAbsent(LocaleProviderAdapter.Type.SPI, provider);
138 139 }
139 140
140 141 // Add the CLDR Locale Data Adapter implementation, if needed.
141 142 lda = LocaleProviderAdapter.forType(LocaleProviderAdapter.Type.CLDR);
142 143 if (lda != null) {
143 144 provider = lda.getLocaleServiceProvider(c);
144 145 if (provider != null) {
145 146 providers.putIfAbsent(LocaleProviderAdapter.Type.CLDR, provider);
146 147 }
147 148 }
148 149
149 150 // Add the Host Locale Data Adapter implementation, if needed.
150 151 lda = LocaleProviderAdapter.forType(LocaleProviderAdapter.Type.HOST);
151 152 if (lda != null) {
152 153 provider = lda.getLocaleServiceProvider(c);
153 154 if (provider != null) {
154 155 providers.putIfAbsent(LocaleProviderAdapter.Type.HOST, provider);
155 156 }
156 157 }
157 158 }
158 159
159 160 static void config(Class<? extends Object> caller, String message) {
160 161 PlatformLogger logger = PlatformLogger.getLogger(caller.getCanonicalName());
161 162 logger.config(message);
162 163 }
163 164
164 165 /**
165 166 * Lazy loaded set of available locales.
166 167 * Loading all locales is a very long operation.
167 168 */
168 169 private static class AllAvailableLocales {
169 170 /**
↓ open down ↓ |
131 lines elided |
↑ open up ↑ |
170 171 * Available locales for all locale sensitive services.
171 172 * This also contains JRE's available locales
172 173 */
173 174 static final Locale[] allAvailableLocales;
174 175
175 176 static {
176 177 Set<Locale> all = new HashSet<>();
177 178 for (Class<? extends LocaleServiceProvider> c : spiClasses) {
178 179 LocaleServiceProviderPool pool =
179 180 LocaleServiceProviderPool.getPool(c);
180 - all.addAll(pool.getAvailableLocaleList());
181 + all.addAll(pool.getAvailableLocaleSet());
181 182 }
182 183
183 184 allAvailableLocales = all.toArray(new Locale[0]);
184 185 }
185 186
186 187 // No instantiation
187 188 private AllAvailableLocales() {
188 189 }
189 190 }
190 191
191 192 /**
192 193 * Returns an array of available locales for all the provider classes.
193 194 * This array is a merged array of all the locales that are provided by each
194 195 * provider, including the JRE.
↓ open down ↓ |
4 lines elided |
↑ open up ↑ |
195 196 *
196 197 * @return an array of the available locales for all provider classes
197 198 */
198 199 public static Locale[] getAllAvailableLocales() {
199 200 return AllAvailableLocales.allAvailableLocales.clone();
200 201 }
201 202
202 203 /**
203 204 * Returns an array of available locales. This array is a
204 205 * merged array of all the locales that are provided by each
205 - * provider, including the JRE.
206 + * provider, including the JRE's FormatData locales.
206 207 *
207 208 * @return an array of the available locales
208 209 */
209 210 public Locale[] getAvailableLocales() {
210 - Set<Locale> locList = getAvailableLocaleList();
211 + Set<Locale> locList = new HashSet<>();
212 + locList.addAll(getAvailableLocaleSet());
213 + // Make sure it all contains JRE's FormatData locales for compatibility.
214 + locList.addAll(Arrays.asList(LocaleProviderAdapter.forJRE().getAvailableLocales()));
211 215 Locale[] tmp = new Locale[locList.size()];
212 216 locList.toArray(tmp);
213 217 return tmp;
214 218 }
215 219
216 - private synchronized Set<Locale> getAvailableLocaleList() {
220 + /**
221 + * Returns the union of locale sets that are available from
222 + * each service provider. This method does NOT return the
223 + * defensive copy.
224 + *
225 + * @return a set of available locales
226 + */
227 + private synchronized Set<Locale> getAvailableLocaleSet() {
217 228 if (availableLocales == null) {
218 229 availableLocales = new HashSet<>();
219 230 for (LocaleServiceProvider lsp : providers.values()) {
220 231 Locale[] locales = lsp.getAvailableLocales();
221 232 for (Locale locale: locales) {
222 233 availableLocales.add(getLookupLocale(locale));
223 234 }
224 235 }
225 -
226 - // Remove Locale.ROOT for the compatibility.
227 - availableLocales.remove(Locale.ROOT);
228 236 }
229 237
230 238 return availableLocales;
231 239 }
232 240
233 241 /**
234 242 * Returns whether any provider for this locale sensitive
235 243 * service is available or not, excluding JRE's one.
236 244 *
237 245 * @return true if any provider (other than JRE) is available
238 246 */
239 247 boolean hasProviders() {
240 248 return providers.size() != 1 ||
241 249 providers.get(LocaleProviderAdapter.Type.JRE) == null;
242 250 }
243 251
244 252 /**
245 253 * Returns the provider's localized object for the specified
246 254 * locale.
247 255 *
248 256 * @param getter an object on which getObject() method
249 257 * is called to obtain the provider's instance.
250 258 * @param locale the given locale that is used as the starting one
251 259 * @param params provider specific parameters
252 260 * @return provider's instance, or null.
253 261 */
254 262 public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
255 263 Locale locale,
256 264 Object... params) {
257 265 return getLocalizedObjectImpl(getter, locale, true, null, params);
258 266 }
259 267
260 268 /**
261 269 * Returns the provider's localized name for the specified
262 270 * locale.
263 271 *
264 272 * @param getter an object on which getObject() method
265 273 * is called to obtain the provider's instance.
266 274 * @param locale the given locale that is used as the starting one
267 275 * @param key the key string for name providers
268 276 * @param params provider specific parameters
269 277 * @return provider's instance, or null.
270 278 */
271 279 public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
272 280 Locale locale,
273 281 String key,
274 282 Object... params) {
275 283 return getLocalizedObjectImpl(getter, locale, false, key, params);
276 284 }
277 285
278 286 @SuppressWarnings("unchecked")
279 287 private <P extends LocaleServiceProvider, S> S getLocalizedObjectImpl(LocalizedObjectGetter<P, S> getter,
280 288 Locale locale,
281 289 boolean isObjectProvider,
282 290 String key,
283 291 Object... params) {
284 292 if (locale == null) {
285 293 throw new NullPointerException();
286 294 }
287 295
↓ open down ↓ |
50 lines elided |
↑ open up ↑ |
288 296 // Check whether JRE is the sole locale data provider or not,
289 297 // and directly call it if it is.
290 298 if (!hasProviders()) {
291 299 return getter.getObject(
292 300 (P)providers.get(LocaleProviderAdapter.Type.JRE),
293 301 locale, key, params);
294 302 }
295 303
296 304 List<Locale> lookupLocales = getLookupLocales(locale);
297 305
298 - Set<Locale> available = getAvailableLocaleList();
306 + Set<Locale> available = getAvailableLocaleSet();
299 307 for (Locale current : lookupLocales) {
300 308 if (available.contains(current)) {
301 309 S providersObj;
302 310
303 311 for (LocaleProviderAdapter.Type type: findProviders(current)) {
304 312 LocaleServiceProvider lsp = providers.get(type);
305 313 providersObj = getter.getObject((P)lsp, locale, key, params);
306 314 if (providersObj != null) {
307 315 return providersObj;
308 316 } else if (isObjectProvider) {
309 317 config(LocaleServiceProviderPool.class,
310 318 "A locale sensitive service provider returned null for a localized objects, which should not happen. provider: "
311 319 + lsp + " locale: " + locale);
312 320 }
313 321 }
314 322 }
315 323 }
316 324
317 325 // not found.
318 326 return null;
319 327 }
320 328
321 329 /**
322 330 * Returns the list of locale service provider instances that support
323 331 * the specified locale.
324 332 *
325 333 * @param locale the given locale
326 334 * @return the list of locale data adapter types
327 335 */
328 336 private List<LocaleProviderAdapter.Type> findProviders(Locale locale) {
329 337 List<LocaleProviderAdapter.Type> providersList = providersCache.get(locale);
330 338 if (providersList == null) {
331 339 for (LocaleProviderAdapter.Type type : LocaleProviderAdapter.getAdapterPreference()) {
332 340 LocaleServiceProvider lsp = providers.get(type);
333 341 if (lsp != null) {
334 342 if (lsp.isSupportedLocale(locale)) {
335 343 if (providersList == null) {
336 344 providersList = new ArrayList<>(2);
337 345 }
338 346 providersList.add(type);
339 347
340 348 }
341 349 }
342 350 }
343 351 if (providersList == null) {
344 352 providersList = NULL_LIST;
345 353 }
346 354 List<LocaleProviderAdapter.Type> val = providersCache.putIfAbsent(locale, providersList);
347 355 if (val != null) {
348 356 providersList = val;
349 357 }
350 358 }
351 359 return providersList;
352 360 }
353 361
354 362 /**
355 363 * Returns a list of candidate locales for service look up.
356 364 * @param locale the input locale
357 365 * @return the list of candidate locales for the given locale
358 366 */
359 367 static List<Locale> getLookupLocales(Locale locale) {
360 368 // Note: We currently use the default implementation of
361 369 // ResourceBundle.Control.getCandidateLocales. The result
362 370 // returned by getCandidateLocales are already normalized
363 371 // (no extensions) for service look up.
364 372 List<Locale> lookupLocales = Control.getNoFallbackControl(Control.FORMAT_DEFAULT)
365 373 .getCandidateLocales("", locale);
366 374 return lookupLocales;
367 375 }
368 376
369 377 /**
370 378 * Returns an instance of Locale used for service look up.
371 379 * The result Locale has no extensions except for ja_JP_JP
372 380 * and th_TH_TH
373 381 *
374 382 * @param locale the locale
375 383 * @return the locale used for service look up
376 384 */
377 385 static Locale getLookupLocale(Locale locale) {
378 386 Locale lookupLocale = locale;
379 387 if (locale.hasExtensions()
380 388 && !locale.equals(JRELocaleConstants.JA_JP_JP)
381 389 && !locale.equals(JRELocaleConstants.TH_TH_TH)) {
382 390 // remove extensions
383 391 Builder locbld = new Builder();
384 392 try {
385 393 locbld.setLocale(locale);
386 394 locbld.clearExtensions();
387 395 lookupLocale = locbld.build();
388 396 } catch (IllformedLocaleException e) {
389 397 // A Locale with non-empty extensions
390 398 // should have well-formed fields except
391 399 // for ja_JP_JP and th_TH_TH. Therefore,
392 400 // it should never enter in this catch clause.
393 401 config(LocaleServiceProviderPool.class,
394 402 "A locale(" + locale + ") has non-empty extensions, but has illformed fields.");
395 403
396 404 // Fallback - script field will be lost.
397 405 lookupLocale = new Locale(locale.getLanguage(), locale.getCountry(), locale.getVariant());
398 406 }
399 407 }
400 408 return lookupLocale;
401 409 }
402 410
403 411 /**
404 412 * A dummy locale service provider list that indicates there is no
405 413 * provider available
406 414 */
407 415 private static List<LocaleProviderAdapter.Type> NULL_LIST =
408 416 Collections.emptyList();
409 417
410 418 /**
411 419 * An interface to get a localized object for each locale sensitive
412 420 * service class.
413 421 */
414 422 public interface LocalizedObjectGetter<P extends LocaleServiceProvider, S> {
415 423 /**
416 424 * Returns an object from the provider
417 425 *
418 426 * @param lsp the provider
419 427 * @param locale the locale
420 428 * @param key key string to localize, or null if the provider is not
421 429 * a name provider
422 430 * @param params provider specific params
423 431 * @return localized object from the provider
424 432 */
425 433 public S getObject(P lsp,
426 434 Locale locale,
427 435 String key,
428 436 Object... params);
429 437 }
430 438 }
↓ open down ↓ |
122 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX