diff --git a/src/main/java/org/apache/commons/lang3/LocaleUtils.java b/src/main/java/org/apache/commons/lang3/LocaleUtils.java index c25eda2f0..464eada6e 100644 --- a/src/main/java/org/apache/commons/lang3/LocaleUtils.java +++ b/src/main/java/org/apache/commons/lang3/LocaleUtils.java @@ -19,12 +19,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; /** *

Operations to assist when working with a {@link Locale}.

@@ -39,21 +39,13 @@ */ public class LocaleUtils { - /** Unmodifiable list of available locales. */ - //@GuardedBy("this") - private static List cAvailableLocaleList; // lazily created by availableLocaleList() + /** Concurrent map of language locales by country. */ + private static final ConcurrentMap> cLanguagesByCountry = + new ConcurrentHashMap>(); - /** Unmodifiable set of available locales. */ - //@GuardedBy("this") - private static Set cAvailableLocaleSet; // lazily created by availableLocaleSet() - - /** Unmodifiable map of language locales by country. */ - private static final Map> cLanguagesByCountry = - Collections.synchronizedMap(new HashMap>()); - - /** Unmodifiable map of country locales by language. */ - private static final Map> cCountriesByLanguage = - Collections.synchronizedMap(new HashMap>()); + /** Concurrent map of country locales by language. */ + private static final ConcurrentMap> cCountriesByLanguage = + new ConcurrentHashMap>(); /** *

LocaleUtils instances should NOT be constructed in standard programming. @@ -143,7 +135,7 @@ public static Locale toLocale(String str) { * * * @param locale the locale to start from - * @return the unmodifiable list of Locale objects, 0 being locale, never null + * @return the unmodifiable list of Locale objects, 0 being locale, not null */ public static List localeLookupList(Locale locale) { return localeLookupList(locale, locale); @@ -165,7 +157,7 @@ public static List localeLookupList(Locale locale) { * * @param locale the locale to start from, null returns empty list * @param defaultLocale the default locale to use if no other is found - * @return the unmodifiable list of Locale objects, 0 being locale, never null + * @return the unmodifiable list of Locale objects, 0 being locale, not null */ public static List localeLookupList(Locale locale, Locale defaultLocale) { List list = new ArrayList(4); @@ -195,22 +187,7 @@ public static List localeLookupList(Locale locale, Locale defaultLocale) * @return the unmodifiable list of available locales */ public static List availableLocaleList() { - if(cAvailableLocaleList == null) { - initAvailableLocaleList(); - } - return cAvailableLocaleList; - } - - /** - * Initializes the availableLocaleList. It is separate from availableLocaleList() - * to avoid the synchronized block affecting normal use, yet synchronized and - * lazy loading to avoid a static block affecting other methods in this class. - */ - private static synchronized void initAvailableLocaleList() { - if(cAvailableLocaleList == null) { - List list = Arrays.asList(Locale.getAvailableLocales()); - cAvailableLocaleList = Collections.unmodifiableList(list); - } + return SyncAvoid.AVAILABLE_LOCALE_LIST; } //----------------------------------------------------------------------- @@ -224,21 +201,7 @@ private static synchronized void initAvailableLocaleList() { * @return the unmodifiable set of available locales */ public static Set availableLocaleSet() { - if(cAvailableLocaleSet == null) { - initAvailableLocaleSet(); - } - return cAvailableLocaleSet; - } - - /** - * Initializes the availableLocaleSet. It is separate from availableLocaleSet() - * to avoid the synchronized block affecting normal use, yet synchronized and - * lazy loading to avoid a static block affecting other methods in this class. - */ - private static synchronized void initAvailableLocaleSet() { - if(cAvailableLocaleSet == null) { - cAvailableLocaleSet = Collections.unmodifiableSet( new HashSet(availableLocaleList()) ); - } + return SyncAvoid.AVAILABLE_LOCALE_SET; } //----------------------------------------------------------------------- @@ -260,26 +223,26 @@ public static boolean isAvailableLocale(Locale locale) { * languages available for that country. Variant locales are removed.

* * @param countryCode the 2 letter country code, null returns empty - * @return an unmodifiable List of Locale objects, never null + * @return an unmodifiable List of Locale objects, not null */ public static List languagesByCountry(String countryCode) { - List langs = cLanguagesByCountry.get(countryCode); //syncd + if (countryCode == null) { + return Collections.emptyList(); + } + List langs = cLanguagesByCountry.get(countryCode); if (langs == null) { - if (countryCode != null) { - langs = new ArrayList(); - List locales = availableLocaleList(); - for (int i = 0; i < locales.size(); i++) { - Locale locale = locales.get(i); - if (countryCode.equals(locale.getCountry()) && - locale.getVariant().length() == 0) { - langs.add(locale); - } + langs = new ArrayList(); + List locales = availableLocaleList(); + for (int i = 0; i < locales.size(); i++) { + Locale locale = locales.get(i); + if (countryCode.equals(locale.getCountry()) && + locale.getVariant().length() == 0) { + langs.add(locale); } - langs = Collections.unmodifiableList(langs); - } else { - langs = Collections.emptyList(); } - cLanguagesByCountry.put(countryCode, langs); //syncd + langs = Collections.unmodifiableList(langs); + cLanguagesByCountry.putIfAbsent(countryCode, langs); + langs = cLanguagesByCountry.get(countryCode); } return langs; } @@ -292,29 +255,44 @@ public static List languagesByCountry(String countryCode) { * countries available for that language. Variant locales are removed.

* * @param languageCode the 2 letter language code, null returns empty - * @return an unmodifiable List of Locale objects, never null + * @return an unmodifiable List of Locale objects, not null */ public static List countriesByLanguage(String languageCode) { - List countries = cCountriesByLanguage.get(languageCode); //syncd + if (languageCode == null) { + return Collections.emptyList(); + } + List countries = cCountriesByLanguage.get(languageCode); if (countries == null) { - if (languageCode != null) { - countries = new ArrayList(); - List locales = availableLocaleList(); - for (int i = 0; i < locales.size(); i++) { - Locale locale = locales.get(i); - if (languageCode.equals(locale.getLanguage()) && - locale.getCountry().length() != 0 && - locale.getVariant().length() == 0) { - countries.add(locale); - } + countries = new ArrayList(); + List locales = availableLocaleList(); + for (int i = 0; i < locales.size(); i++) { + Locale locale = locales.get(i); + if (languageCode.equals(locale.getLanguage()) && + locale.getCountry().length() != 0 && + locale.getVariant().length() == 0) { + countries.add(locale); } - countries = Collections.unmodifiableList(countries); - } else { - countries = Collections.emptyList(); } - cCountriesByLanguage.put(languageCode, countries); //syncd + countries = Collections.unmodifiableList(countries); + cCountriesByLanguage.putIfAbsent(languageCode, countries); + countries = cCountriesByLanguage.get(languageCode); } return countries; } + //----------------------------------------------------------------------- + // class to avoid synchronization + static class SyncAvoid { + /** Unmodifiable list of available locales. */ + private static List AVAILABLE_LOCALE_LIST; + /** Unmodifiable set of available locales. */ + private static Set AVAILABLE_LOCALE_SET; + + static { + List list = new ArrayList(Arrays.asList(Locale.getAvailableLocales())); // extra safe + AVAILABLE_LOCALE_LIST = Collections.unmodifiableList(list); + AVAILABLE_LOCALE_SET = Collections.unmodifiableSet(new HashSet(availableLocaleList())); + } + } + }