1 /*
2 * Copyright (c) 2005, 2019, 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.locale.provider;
27
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.HashSet;
32 import java.util.IllformedLocaleException;
33 import java.util.List;
34 import java.util.Locale;
35 import java.util.Locale.Builder;
36 import java.util.ResourceBundle.Control;
37 import java.util.Set;
38 import java.util.concurrent.ConcurrentHashMap;
39 import java.util.concurrent.ConcurrentMap;
40 import java.util.spi.LocaleServiceProvider;
41 import sun.util.logging.PlatformLogger;
42
43 /**
44 * An instance of this class holds a set of the third party implementations of a particular
45 * locale sensitive service, such as {@link java.util.spi.LocaleNameProvider}.
46 *
47 * @author Naoto Sato
48 * @author Masayoshi Okutsu
49 */
50 public final class LocaleServiceProviderPool {
51
52 /**
53 * A Map that holds singleton instances of this class. Each instance holds a
54 * set of provider implementations of a particular locale sensitive service.
55 */
56 private static final ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProviderPool> poolOfPools =
57 new ConcurrentHashMap<>();
58
59 /**
60 * A Map that retains Locale->provider mapping
61 */
104 LocaleServiceProviderPool newPool =
105 new LocaleServiceProviderPool(providerClass);
106 pool = poolOfPools.putIfAbsent(providerClass, newPool);
107 if (pool == null) {
108 pool = newPool;
109 }
110 }
111
112 return pool;
113 }
114
115 /**
116 * The sole constructor.
117 *
118 * @param c class of the locale sensitive service
119 */
120 private LocaleServiceProviderPool (final Class<? extends LocaleServiceProvider> c) {
121 providerClass = c;
122 }
123
124 static void config(Class<? extends Object> caller, String message) {
125 PlatformLogger logger = PlatformLogger.getLogger(caller.getCanonicalName());
126 logger.config(message);
127 }
128
129 /**
130 * Lazy loaded set of available locales.
131 * Loading all locales is a very long operation.
132 */
133 private static class AllAvailableLocales {
134 /**
135 * Available locales for all locale sensitive services.
136 * This also contains JRE's available locales
137 */
138 static final Locale[] allAvailableLocales;
139
140 static {
141 Set<Locale> all = new HashSet<>();
142 for (Class<? extends LocaleServiceProvider> c : spiClasses) {
143 LocaleServiceProviderPool pool =
144 LocaleServiceProviderPool.getPool(c);
145 all.addAll(pool.getAvailableLocaleSet());
146 }
147
148 allAvailableLocales = all.toArray(new Locale[0]);
265 @SuppressWarnings("unchecked")
266 private <P extends LocaleServiceProvider, S> S getLocalizedObjectImpl(LocalizedObjectGetter<P, S> getter,
267 Locale locale,
268 boolean isObjectProvider,
269 String key,
270 Object... params) {
271 if (locale == null) {
272 throw new NullPointerException();
273 }
274
275 List<Locale> lookupLocales = getLookupLocales(locale);
276
277 for (Locale current : lookupLocales) {
278 S providersObj;
279
280 for (LocaleServiceProvider lsp: findProviders(current, isObjectProvider)) {
281 providersObj = getter.getObject((P)lsp, locale, key, params);
282 if (providersObj != null) {
283 return providersObj;
284 } else if (isObjectProvider) {
285 config(LocaleServiceProviderPool.class,
286 "A locale sensitive service object provider returned null, " +
287 "which should not happen. Provider: " + lsp + " Locale: " + locale);
288 }
289 }
290 }
291
292 // not found.
293 return null;
294 }
295
296 /**
297 * Returns the list of locale service provider instances that support
298 * the specified locale.
299 *
300 * @param locale the given locale
301 * @return the list of locale data adapter types
302 */
303 private List<LocaleServiceProvider> findProviders(Locale locale, boolean isObjectProvider) {
304 List<LocaleServiceProvider> providersList = providersCache.get(locale);
305 if (providersList == null) {
324 providersList = NULL_LIST;
325 }
326 List<LocaleServiceProvider> val = providersCache.putIfAbsent(locale, providersList);
327 if (val != null) {
328 providersList = val;
329 }
330 }
331 return providersList;
332 }
333
334 /**
335 * Returns a list of candidate locales for service look up.
336 * @param locale the input locale
337 * @return the list of candidate locales for the given locale
338 */
339 static List<Locale> getLookupLocales(Locale locale) {
340 // Note: We currently use the default implementation of
341 // ResourceBundle.Control.getCandidateLocales. The result
342 // returned by getCandidateLocales are already normalized
343 // (no extensions) for service look up.
344 List<Locale> lookupLocales = Control.getNoFallbackControl(Control.FORMAT_DEFAULT)
345 .getCandidateLocales("", locale);
346 return lookupLocales;
347 }
348
349 /**
350 * Returns an instance of Locale used for service look up.
351 * The result Locale has no extensions except for ja_JP_JP
352 * and th_TH_TH
353 *
354 * @param locale the locale
355 * @return the locale used for service look up
356 */
357 static Locale getLookupLocale(Locale locale) {
358 Locale lookupLocale = locale;
359 if (locale.hasExtensions()
360 && !locale.equals(JRELocaleConstants.JA_JP_JP)
361 && !locale.equals(JRELocaleConstants.TH_TH_TH)) {
362 // remove extensions
363 Builder locbld = new Builder();
364 try {
365 locbld.setLocale(locale);
366 locbld.clearExtensions();
367 lookupLocale = locbld.build();
368 } catch (IllformedLocaleException e) {
369 // A Locale with non-empty extensions
370 // should have well-formed fields except
371 // for ja_JP_JP and th_TH_TH. Therefore,
372 // it should never enter in this catch clause.
373 config(LocaleServiceProviderPool.class,
374 "A locale(" + locale + ") has non-empty extensions, but has illformed fields.");
375
376 // Fallback - script field will be lost.
377 lookupLocale = new Locale(locale.getLanguage(), locale.getCountry(), locale.getVariant());
378 }
379 }
380 return lookupLocale;
381 }
382
383 /**
384 * A dummy locale service provider list that indicates there is no
385 * provider available
386 */
387 private static final List<LocaleServiceProvider> NULL_LIST =
388 Collections.emptyList();
389
390 /**
391 * An interface to get a localized object for each locale sensitive
392 * service class.
393 */
394 public interface LocalizedObjectGetter<P extends LocaleServiceProvider, S> {
395 /**
396 * Returns an object from the provider
397 *
398 * @param lsp the provider
399 * @param locale the locale
400 * @param key key string to localize, or null if the provider is not
401 * a name provider
402 * @param params provider specific params
403 * @return localized object from the provider
404 */
405 public S getObject(P lsp,
406 Locale locale,
407 String key,
408 Object... params);
409 }
410 }
|
1 /*
2 * Copyright (c) 2005, 2020, 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.locale.provider;
27
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.HashSet;
32 import java.util.IllformedLocaleException;
33 import java.util.List;
34 import java.util.Locale;
35 import java.util.Locale.Builder;
36 import java.util.ResourceBundle.Control;
37 import java.util.Set;
38 import java.util.concurrent.ConcurrentHashMap;
39 import java.util.concurrent.ConcurrentMap;
40 import java.util.spi.LocaleServiceProvider;
41
42 /**
43 * An instance of this class holds a set of the third party implementations of a particular
44 * locale sensitive service, such as {@link java.util.spi.LocaleNameProvider}.
45 *
46 * @author Naoto Sato
47 * @author Masayoshi Okutsu
48 */
49 public final class LocaleServiceProviderPool {
50
51 /**
52 * A Map that holds singleton instances of this class. Each instance holds a
53 * set of provider implementations of a particular locale sensitive service.
54 */
55 private static final ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProviderPool> poolOfPools =
56 new ConcurrentHashMap<>();
57
58 /**
59 * A Map that retains Locale->provider mapping
60 */
103 LocaleServiceProviderPool newPool =
104 new LocaleServiceProviderPool(providerClass);
105 pool = poolOfPools.putIfAbsent(providerClass, newPool);
106 if (pool == null) {
107 pool = newPool;
108 }
109 }
110
111 return pool;
112 }
113
114 /**
115 * The sole constructor.
116 *
117 * @param c class of the locale sensitive service
118 */
119 private LocaleServiceProviderPool (final Class<? extends LocaleServiceProvider> c) {
120 providerClass = c;
121 }
122
123 /**
124 * Lazy loaded set of available locales.
125 * Loading all locales is a very long operation.
126 */
127 private static class AllAvailableLocales {
128 /**
129 * Available locales for all locale sensitive services.
130 * This also contains JRE's available locales
131 */
132 static final Locale[] allAvailableLocales;
133
134 static {
135 Set<Locale> all = new HashSet<>();
136 for (Class<? extends LocaleServiceProvider> c : spiClasses) {
137 LocaleServiceProviderPool pool =
138 LocaleServiceProviderPool.getPool(c);
139 all.addAll(pool.getAvailableLocaleSet());
140 }
141
142 allAvailableLocales = all.toArray(new Locale[0]);
259 @SuppressWarnings("unchecked")
260 private <P extends LocaleServiceProvider, S> S getLocalizedObjectImpl(LocalizedObjectGetter<P, S> getter,
261 Locale locale,
262 boolean isObjectProvider,
263 String key,
264 Object... params) {
265 if (locale == null) {
266 throw new NullPointerException();
267 }
268
269 List<Locale> lookupLocales = getLookupLocales(locale);
270
271 for (Locale current : lookupLocales) {
272 S providersObj;
273
274 for (LocaleServiceProvider lsp: findProviders(current, isObjectProvider)) {
275 providersObj = getter.getObject((P)lsp, locale, key, params);
276 if (providersObj != null) {
277 return providersObj;
278 } else if (isObjectProvider) {
279 System.getLogger(LocaleServiceProviderPool.class.getCanonicalName())
280 .log(System.Logger.Level.INFO,
281 "A locale sensitive service object provider returned null, " +
282 "which should not happen. Provider: " + lsp + " Locale: " + locale);
283 }
284 }
285 }
286
287 // not found.
288 return null;
289 }
290
291 /**
292 * Returns the list of locale service provider instances that support
293 * the specified locale.
294 *
295 * @param locale the given locale
296 * @return the list of locale data adapter types
297 */
298 private List<LocaleServiceProvider> findProviders(Locale locale, boolean isObjectProvider) {
299 List<LocaleServiceProvider> providersList = providersCache.get(locale);
300 if (providersList == null) {
319 providersList = NULL_LIST;
320 }
321 List<LocaleServiceProvider> val = providersCache.putIfAbsent(locale, providersList);
322 if (val != null) {
323 providersList = val;
324 }
325 }
326 return providersList;
327 }
328
329 /**
330 * Returns a list of candidate locales for service look up.
331 * @param locale the input locale
332 * @return the list of candidate locales for the given locale
333 */
334 static List<Locale> getLookupLocales(Locale locale) {
335 // Note: We currently use the default implementation of
336 // ResourceBundle.Control.getCandidateLocales. The result
337 // returned by getCandidateLocales are already normalized
338 // (no extensions) for service look up.
339 return Control.getNoFallbackControl(Control.FORMAT_DEFAULT)
340 .getCandidateLocales("", locale);
341 }
342
343 /**
344 * Returns an instance of Locale used for service look up.
345 * The result Locale has no extensions except for ja_JP_JP
346 * and th_TH_TH
347 *
348 * @param locale the locale
349 * @return the locale used for service look up
350 */
351 static Locale getLookupLocale(Locale locale) {
352 Locale lookupLocale = locale;
353 if (locale.hasExtensions()
354 && !locale.equals(JRELocaleConstants.JA_JP_JP)
355 && !locale.equals(JRELocaleConstants.TH_TH_TH)) {
356 // remove extensions
357 Builder locbld = new Builder();
358 try {
359 locbld.setLocale(locale);
360 locbld.clearExtensions();
361 lookupLocale = locbld.build();
362 } catch (IllformedLocaleException e) {
363 // A Locale with non-empty extensions
364 // should have well-formed fields except
365 // for ja_JP_JP and th_TH_TH. Therefore,
366 // it should never enter in this catch clause.
367 System.getLogger(LocaleServiceProviderPool.class.getCanonicalName())
368 .log(System.Logger.Level.INFO,
369 "A locale(" + locale + ") has non-empty extensions, but has illformed fields.");
370
371 // Fallback - script field will be lost.
372 lookupLocale = new Locale(locale.getLanguage(), locale.getCountry(), locale.getVariant());
373 }
374 }
375 return lookupLocale;
376 }
377
378 /**
379 * A dummy locale service provider list that indicates there is no
380 * provider available
381 */
382 private static final List<LocaleServiceProvider> NULL_LIST =
383 Collections.emptyList();
384
385 /**
386 * An interface to get a localized object for each locale sensitive
387 * service class.
388 */
389 public interface LocalizedObjectGetter<P extends LocaleServiceProvider, S> {
390 /**
391 * Returns an object from the provider
392 *
393 * @param lsp the provider
394 * @param locale the locale
395 * @param key key string to localize, or null if the provider is not
396 * a name provider
397 * @param params provider specific params
398 * @return localized object from the provider
399 */
400 S getObject(P lsp,
401 Locale locale,
402 String key,
403 Object... params);
404 }
405 }
|