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+
26+package sun.util;
27+
28+import java.security.AccessController;
29+import java.security.PrivilegedActionException;
30+import java.security.PrivilegedExceptionAction;
31+import java.util.ArrayList;
32+import java.util.HashSet;
33+import java.util.IllformedLocaleException;
34+import java.util.LinkedHashSet;
35+import java.util.List;
36+import java.util.Locale;
37+import java.util.Locale.Builder;
38+import java.util.Map;
39+import java.util.ResourceBundle.Control;
40+import java.util.ServiceLoader;
41+import java.util.Set;
42+import java.util.concurrent.ConcurrentHashMap;
43+import java.util.spi.LocaleServiceProvider;
44+
45+import sun.util.logging.PlatformLogger;
46+import sun.util.resources.LocaleData;
47+import 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+ */
54+public 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+}