History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/util/LocaleServiceProviderPool.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/util/LocaleServiceProviderPool.java+534−0
@@ -0,0 +1,534 @@
1/*
2 * Copyright (c) 2005, 2010, 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
26package sun.util;
27
28import java.security.AccessController;
29import java.security.PrivilegedActionException;
30import java.security.PrivilegedExceptionAction;
31import java.util.ArrayList;
32import java.util.HashSet;
33import java.util.IllformedLocaleException;
34import java.util.LinkedHashSet;
35import java.util.List;
36import java.util.Locale;
37import java.util.Locale.Builder;
38import java.util.Map;
39import java.util.ResourceBundle.Control;
40import java.util.ServiceLoader;
41import java.util.Set;
42import java.util.concurrent.ConcurrentHashMap;
43import java.util.spi.LocaleServiceProvider;
44
45import sun.util.logging.PlatformLogger;
46import sun.util.resources.LocaleData;
47import sun.util.resources.OpenListResourceBundle;
48
49/**
50 * An instance of this class holds a set of the third party implementations of a particular
51 * locale sensitive service, such as {@link java.util.spi.LocaleNameProvider}.
52 *
53 */
54public final class LocaleServiceProviderPool {
55
56 /**
57 * A Map that holds singleton instances of this class. Each instance holds a
58 * set of provider implementations of a particular locale sensitive service.
59 */
60 private static Map<Class, LocaleServiceProviderPool> poolOfPools =
61 new ConcurrentHashMap<Class, LocaleServiceProviderPool>();
62
63 /**
64 * A Set containing locale service providers that implement the
65 * specified provider SPI
66 */
67 private Set<LocaleServiceProvider> providers =
68 new LinkedHashSet<LocaleServiceProvider>();
69
70 /**
71 * A Map that retains Locale->provider mapping
72 */
73 private Map<Locale, LocaleServiceProvider> providersCache =
74 new ConcurrentHashMap<Locale, LocaleServiceProvider>();
75
76 /**
77 * Available locales for this locale sensitive service. This also contains
78 * JRE's available locales
79 */
80 private Set<Locale> availableLocales = null;
81
82 /**
83 * Available locales within this JRE. Currently this is declared as
84 * static. This could be non-static later, so that they could have
85 * different sets for each locale sensitive services.
86 */
87 private static volatile List<Locale> availableJRELocales = null;
88
89 /**
90 * Provider locales for this locale sensitive service.
91 */
92 private Set<Locale> providerLocales = null;
93
94 /**
95 * Special locale for ja_JP with Japanese calendar
96 */
97 private static Locale locale_ja_JP_JP = new Locale("ja", "JP", "JP");
98
99 /**
100 * Special locale for th_TH with Thai numbering system
101 */
102 private static Locale locale_th_TH_TH = new Locale("th", "TH", "TH");
103
104 /**
105 * A factory method that returns a singleton instance
106 */
107 public static LocaleServiceProviderPool getPool(Class<? extends LocaleServiceProvider> providerClass) {
108 LocaleServiceProviderPool pool = poolOfPools.get(providerClass);
109 if (pool == null) {
110 LocaleServiceProviderPool newPool =
111 new LocaleServiceProviderPool(providerClass);
112 pool = poolOfPools.put(providerClass, newPool);
113 if (pool == null) {
114 pool = newPool;
115 }
116 }
117
118 return pool;
119 }
120
121 /**
122 * The sole constructor.
123 *
124 * @param c class of the locale sensitive service
125 */
126 private LocaleServiceProviderPool (final Class<? extends LocaleServiceProvider> c) {
127 try {
128 AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
129 public Object run() {
130 for (LocaleServiceProvider provider : ServiceLoader.loadInstalled(c)) {
131 providers.add(provider);
132 }
133 return null;
134 }
135 });
136 } catch (PrivilegedActionException e) {
137 config(e.toString());
138 }
139 }
140
141 private static void config(String message) {
142 PlatformLogger logger = PlatformLogger.getLogger("sun.util.LocaleServiceProviderPool");
143 logger.config(message);
144 }
145
146 /**
147 * Lazy loaded set of available locales.
148 * Loading all locales is a very long operation.
149 */
150 private static class AllAvailableLocales {
151 /**
152 * Available locales for all locale sensitive services.
153 * This also contains JRE's available locales
154 */
155 static final Locale[] allAvailableLocales;
156
157 static {
158 Class[] providerClasses = {
159 java.text.spi.BreakIteratorProvider.class,
160 java.text.spi.CollatorProvider.class,
161 java.text.spi.DateFormatProvider.class,
162 java.text.spi.DateFormatSymbolsProvider.class,
163 java.text.spi.DecimalFormatSymbolsProvider.class,
164 java.text.spi.NumberFormatProvider.class,
165 java.util.spi.CurrencyNameProvider.class,
166 java.util.spi.LocaleNameProvider.class,
167 java.util.spi.TimeZoneNameProvider.class };
168
169 // Normalize locales for look up
170 Locale[] allLocales = LocaleData.getAvailableLocales();
171 Set<Locale> all = new HashSet<Locale>(allLocales.length);
172 for (Locale locale : allLocales) {
173 all.add(getLookupLocale(locale));
174 }
175
176 for (Class providerClass : providerClasses) {
177 LocaleServiceProviderPool pool =
178 LocaleServiceProviderPool.getPool(providerClass);
179 all.addAll(pool.getProviderLocales());
180 }
181
182 allAvailableLocales = all.toArray(new Locale[0]);
183 }
184 }
185
186 /**
187 * Returns an array of available locales for all the provider classes.
188 * This array is a merged array of all the locales that are provided by each
189 * provider, including the JRE.
190 *
191 * @return an array of the available locales for all provider classes
192 */
193 public static Locale[] getAllAvailableLocales() {
194 return AllAvailableLocales.allAvailableLocales.clone();
195 }
196
197 /**
198 * Returns an array of available locales. This array is a
199 * merged array of all the locales that are provided by each
200 * provider, including the JRE.
201 *
202 * @return an array of the available locales
203 */
204 public synchronized Locale[] getAvailableLocales() {
205 if (availableLocales == null) {
206 availableLocales = new HashSet<Locale>(getJRELocales());
207 if (hasProviders()) {
208 availableLocales.addAll(getProviderLocales());
209 }
210 }
211 Locale[] tmp = new Locale[availableLocales.size()];
212 availableLocales.toArray(tmp);
213 return tmp;
214 }
215
216 /**
217 * Returns an array of available locales (already normalized
218 * for service lookup) from providers.
219 * Note that this method does not return a defensive copy.
220 *
221 * @return list of the provider locales
222 */
223 private synchronized Set<Locale> getProviderLocales() {
224 if (providerLocales == null) {
225 providerLocales = new HashSet<Locale>();
226 if (hasProviders()) {
227 for (LocaleServiceProvider lsp : providers) {
228 Locale[] locales = lsp.getAvailableLocales();
229 for (Locale locale: locales) {
230 providerLocales.add(getLookupLocale(locale));
231 }
232 }
233 }
234 }
235 return providerLocales;
236 }
237
238 /**
239 * Returns whether any provider for this locale sensitive
240 * service is available or not.
241 *
242 * @return true if any provider is available
243 */
244 public boolean hasProviders() {
245 return !providers.isEmpty();
246 }
247
248 /**
249 * Returns an array of available locales (already normalized for
250 * service lookup) supported by the JRE.
251 * Note that this method does not return a defensive copy.
252 *
253 * @return list of the available JRE locales
254 */
255 private List<Locale> getJRELocales() {
256 if (availableJRELocales == null) {
257 synchronized (LocaleServiceProviderPool.class) {
258 if (availableJRELocales == null) {
259 Locale[] allLocales = LocaleData.getAvailableLocales();
260 availableJRELocales = new ArrayList<Locale>(allLocales.length);
261 for (Locale locale : allLocales) {
262 availableJRELocales.add(getLookupLocale(locale));
263 }
264 }
265 }
266 }
267 return availableJRELocales;
268 }
269
270 /**
271 * Returns whether the given locale is supported by the JRE.
272 *
273 * @param locale the locale to test.
274 * @return true, if the locale is supported by the JRE. false
275 * otherwise.
276 */
277 private boolean isJRESupported(Locale locale) {
278 List<Locale> locales = getJRELocales();
279 return locales.contains(getLookupLocale(locale));
280 }
281
282 /**
283 * Returns the provider's localized object for the specified
284 * locale.
285 *
286 * @param getter an object on which getObject() method
287 * is called to obtain the provider's instance.
288 * @param locale the given locale that is used as the starting one
289 * @param params provider specific parameters
290 * @return provider's instance, or null.
291 */
292 public <P, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
293 Locale locale,
294 Object... params) {
295 return getLocalizedObjectImpl(getter, locale, true, null, null, null, params);
296 }
297
298 /**
299 * Returns the provider's localized name for the specified
300 * locale.
301 *
302 * @param getter an object on which getObject() method
303 * is called to obtain the provider's instance.
304 * @param locale the given locale that is used as the starting one
305 * @param bundle JRE resource bundle that contains
306 * the localized names, or null for localized objects.
307 * @param key the key string if bundle is supplied, otherwise null.
308 * @param params provider specific parameters
309 * @return provider's instance, or null.
310 */
311 public <P, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
312 Locale locale,
313 OpenListResourceBundle bundle,
314 String key,
315 Object... params) {
316 return getLocalizedObjectImpl(getter, locale, false, null, bundle, key, params);
317 }
318
319 /**
320 * Returns the provider's localized name for the specified
321 * locale.
322 *
323 * @param getter an object on which getObject() method
324 * is called to obtain the provider's instance.
325 * @param locale the given locale that is used as the starting one
326 * @param bundleKey JRE specific bundle key. e.g., "USD" is for currency
327 symbol and "usd" is for currency display name in the JRE bundle.
328 * @param bundle JRE resource bundle that contains
329 * the localized names, or null for localized objects.
330 * @param key the key string if bundle is supplied, otherwise null.
331 * @param params provider specific parameters
332 * @return provider's instance, or null.
333 */
334 public <P, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
335 Locale locale,
336 String bundleKey,
337 OpenListResourceBundle bundle,
338 String key,
339 Object... params) {
340 return getLocalizedObjectImpl(getter, locale, false, bundleKey, bundle, key, params);
341 }
342
343 private <P, S> S getLocalizedObjectImpl(LocalizedObjectGetter<P, S> getter,
344 Locale locale,
345 boolean isObjectProvider,
346 String bundleKey,
347 OpenListResourceBundle bundle,
348 String key,
349 Object... params) {
350 if (hasProviders()) {
351 if (bundleKey == null) {
352 bundleKey = key;
353 }
354 Locale bundleLocale = (bundle != null ? bundle.getLocale() : null);
355 List<Locale> lookupLocales = getLookupLocales(locale);
356 P lsp;
357 S providersObj = null;
358
359 // check whether a provider has an implementation that's closer
360 // to the requested locale than the bundle we've found (for
361 // localized names), or Java runtime's supported locale
362 // (for localized objects)
363 Set<Locale> provLoc = getProviderLocales();
364 for (int i = 0; i < lookupLocales.size(); i++) {
365 Locale current = lookupLocales.get(i);
366 if (bundleLocale != null) {
367 if (current.equals(bundleLocale)) {
368 break;
369 }
370 } else {
371 if (isJRESupported(current)) {
372 break;
373 }
374 }
375 if (provLoc.contains(current)) {
376 lsp = (P)findProvider(current);
377 if (lsp != null) {
378 providersObj = getter.getObject(lsp, locale, key, params);
379 if (providersObj != null) {
380 return providersObj;
381 } else if (isObjectProvider) {
382 config(
383 "A locale sensitive service provider returned null for a localized objects, which should not happen. provider: " + lsp + " locale: " + locale);
384 }
385 }
386 }
387 }
388
389 // look up the JRE bundle and its parent chain. Only
390 // providers for localized names are checked hereafter.
391 while (bundle != null) {
392 bundleLocale = bundle.getLocale();
393
394 if (bundle.handleGetKeys().contains(bundleKey)) {
395 // JRE has it.
396 return null;
397 } else {
398 lsp = (P)findProvider(bundleLocale);
399 if (lsp != null) {
400 providersObj = getter.getObject(lsp, locale, key, params);
401 if (providersObj != null) {
402 return providersObj;
403 }
404 }
405 }
406
407 // try parent bundle
408 bundle = bundle.getParent();
409 }
410 }
411
412 // not found.
413 return null;
414 }
415
416 /**
417 * Returns a locale service provider instance that supports
418 * the specified locale.
419 *
420 * @param locale the given locale
421 * @return the provider, or null if there is
422 * no provider available.
423 */
424 private LocaleServiceProvider findProvider(Locale locale) {
425 if (!hasProviders()) {
426 return null;
427 }
428
429 if (providersCache.containsKey(locale)) {
430 LocaleServiceProvider provider = providersCache.get(locale);
431 if (provider != NullProvider.INSTANCE) {
432 return provider;
433 }
434 } else {
435 for (LocaleServiceProvider lsp : providers) {
436 Locale[] locales = lsp.getAvailableLocales();
437 for (Locale available: locales) {
438 // normalize
439 available = getLookupLocale(available);
440 if (locale.equals(available)) {
441 LocaleServiceProvider providerInCache =
442 providersCache.put(locale, lsp);
443 return (providerInCache != null ?
444 providerInCache :
445 lsp);
446 }
447 }
448 }
449 providersCache.put(locale, NullProvider.INSTANCE);
450 }
451 return null;
452 }
453
454 /**
455 * Returns a list of candidate locales for service look up.
456 * @param locale the input locale
457 * @return the list of candiate locales for the given locale
458 */
459 private static List<Locale> getLookupLocales(Locale locale) {
460 // Note: We currently use the default implementation of
461 // ResourceBundle.Control.getCandidateLocales. The result
462 // returned by getCandidateLocales are already normalized
463 // (no extensions) for service look up.
464 List<Locale> lookupLocales = new Control(){}.getCandidateLocales("", locale);
465 return lookupLocales;
466 }
467
468 /**
469 * Returns an instance of Locale used for service look up.
470 * The result Locale has no extensions except for ja_JP_JP
471 * and th_TH_TH
472 *
473 * @param locale the locale
474 * @return the locale used for service look up
475 */
476 private static Locale getLookupLocale(Locale locale) {
477 Locale lookupLocale = locale;
478 Set<Character> extensions = locale.getExtensionKeys();
479 if (!extensions.isEmpty()
480 && !locale.equals(locale_ja_JP_JP)
481 && !locale.equals(locale_th_TH_TH)) {
482 // remove extensions
483 Builder locbld = new Builder();
484 try {
485 locbld.setLocale(locale);
486 locbld.clearExtensions();
487 lookupLocale = locbld.build();
488 } catch (IllformedLocaleException e) {
489 // A Locale with non-empty extensions
490 // should have well-formed fields except
491 // for ja_JP_JP and th_TH_TH. Therefore,
492 // it should never enter in this catch clause.
493 config("A locale(" + locale + ") has non-empty extensions, but has illformed fields.");
494
495 // Fallback - script field will be lost.
496 lookupLocale = new Locale(locale.getLanguage(), locale.getCountry(), locale.getVariant());
497 }
498 }
499 return lookupLocale;
500 }
501
502 /**
503 * A dummy locale service provider that indicates there is no
504 * provider available
505 */
506 private static class NullProvider extends LocaleServiceProvider {
507 private static final NullProvider INSTANCE = new NullProvider();
508
509 public Locale[] getAvailableLocales() {
510 throw new RuntimeException("Should not get called.");
511 }
512 }
513
514 /**
515 * An interface to get a localized object for each locale sensitve
516 * service class.
517 */
518 public interface LocalizedObjectGetter<P, S> {
519 /**
520 * Returns an object from the provider
521 *
522 * @param lsp the provider
523 * @param locale the locale
524 * @param key key string to localize, or null if the provider is not
525 * a name provider
526 * @param params provider specific params
527 * @return localized object from the provider
528 */
529 public S getObject(P lsp,
530 Locale locale,
531 String key,
532 Object... params);
533 }
534}