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