diff options
author | Narayan Kamath <narayan@google.com> | 2014-06-25 16:18:38 +0100 |
---|---|---|
committer | Narayan Kamath <narayan@google.com> | 2014-07-23 10:28:42 +0100 |
commit | 5c0472fd7c53464e526bb833707551d85dbafec1 (patch) | |
tree | c9812c0de3b2c46f4059e07f3179c48df79227ce | |
parent | 1debf79647da22cf39dd5b4d0db2e008026fbc60 (diff) | |
download | libcore-5c0472fd7c53464e526bb833707551d85dbafec1.zip libcore-5c0472fd7c53464e526bb833707551d85dbafec1.tar.gz libcore-5c0472fd7c53464e526bb833707551d85dbafec1.tar.bz2 |
Fix handling of invalid locales in Date/DecimalFormatSymbols.
For locales whose language code is "und" we use Locale.ROOT
instead. This also fixes two other corner cases :
- We were using the wrong locale to fetch timezone strings
when the input locale was null. we now use the same locale
throughout by making sure we don't perform any subsititutions
in LocaleData.get.
- Adds a clearer comment about the broken serialization
behaviour.
bug: 15849709
Change-Id: I95e7eb0ccb7458711038ce9b1c76b3279acda9d6
6 files changed, 38 insertions, 23 deletions
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java index 9fe3681..70e41a2 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java @@ -88,7 +88,7 @@ public class DateFormatSymbolsTest extends junit.framework.TestCase { Locale locale = new Locale("not exist language", "not exist country"); DateFormatSymbols symbols = DateFormatSymbols.getInstance(locale); - assertNotNull(symbols); + assertEquals(DateFormatSymbols.getInstance(Locale.ROOT), symbols); } /** diff --git a/luni/src/main/java/java/text/DateFormatSymbols.java b/luni/src/main/java/java/text/DateFormatSymbols.java index 0d33d75..54a1a3f 100644 --- a/luni/src/main/java/java/text/DateFormatSymbols.java +++ b/luni/src/main/java/java/text/DateFormatSymbols.java @@ -102,7 +102,7 @@ public class DateFormatSymbols implements Serializable, Cloneable { * the locale. */ public DateFormatSymbols(Locale locale) { - this.locale = locale; + this.locale = LocaleData.mapInvalidAndNullLocales(locale); this.localPatternChars = SimpleDateFormat.PATTERN_CHARS; this.localeData = LocaleData.get(locale); @@ -152,7 +152,12 @@ public class DateFormatSymbols implements Serializable, Cloneable { private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ois.defaultReadObject(); - this.localeData = LocaleData.get(locale); + + // NOTE: We don't serialize the locale we were created with, so we can't + // get back the localeData object we want. This is broken for callers that + // access this field directly (i.e, SimpleDateFormat). We should ideally + // have serialized the locale we were created with. See b/16502916. + this.localeData = LocaleData.get(Locale.getDefault()); } private void writeObject(ObjectOutputStream oos) throws IOException { diff --git a/luni/src/main/java/java/text/DecimalFormatSymbols.java b/luni/src/main/java/java/text/DecimalFormatSymbols.java index 1611594..6e25c1b 100644 --- a/luni/src/main/java/java/text/DecimalFormatSymbols.java +++ b/luni/src/main/java/java/text/DecimalFormatSymbols.java @@ -81,6 +81,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { * the locale. */ public DecimalFormatSymbols(Locale locale) { + locale = LocaleData.mapInvalidAndNullLocales(locale); LocaleData localeData = LocaleData.get(locale); this.zeroDigit = localeData.zeroDigit; this.digit = '#'; diff --git a/luni/src/main/java/libcore/icu/ICU.java b/luni/src/main/java/libcore/icu/ICU.java index bb57f49..06e2205 100644 --- a/luni/src/main/java/libcore/icu/ICU.java +++ b/luni/src/main/java/libcore/icu/ICU.java @@ -432,7 +432,7 @@ public final class ICU { private static native String[] getISOLanguagesNative(); private static native String[] getISOCountriesNative(); - static native boolean initLocaleDataNative(String locale, LocaleData result); + static native boolean initLocaleDataNative(String languageTag, LocaleData result); /** * Takes a BCP-47 language tag (Locale.toLanguageTag()). e.g. en-US, not en_US diff --git a/luni/src/main/java/libcore/icu/LocaleData.java b/luni/src/main/java/libcore/icu/LocaleData.java index 845ba32..ec05b53 100644 --- a/luni/src/main/java/libcore/icu/LocaleData.java +++ b/luni/src/main/java/libcore/icu/LocaleData.java @@ -112,27 +112,36 @@ public final class LocaleData { private LocaleData() { } + public static Locale mapInvalidAndNullLocales(Locale locale) { + if (locale == null) { + return Locale.getDefault(); + } + + if ("und".equals(locale.toLanguageTag())) { + return Locale.ROOT; + } + + return locale; + } + /** * Returns a shared LocaleData for the given locale. */ public static LocaleData get(Locale locale) { - if (locale == null) { - locale = Locale.getDefault(); - } - String localeName = locale.toString(); + final String languageTag = locale.toLanguageTag(); synchronized (localeDataCache) { - LocaleData localeData = localeDataCache.get(localeName); + LocaleData localeData = localeDataCache.get(languageTag); if (localeData != null) { return localeData; } } LocaleData newLocaleData = initLocaleData(locale); synchronized (localeDataCache) { - LocaleData localeData = localeDataCache.get(localeName); + LocaleData localeData = localeDataCache.get(languageTag); if (localeData != null) { return localeData; } - localeDataCache.put(localeName, newLocaleData); + localeDataCache.put(languageTag, newLocaleData); return newLocaleData; } } @@ -171,7 +180,7 @@ public final class LocaleData { private static LocaleData initLocaleData(Locale locale) { LocaleData localeData = new LocaleData(); - if (!ICU.initLocaleDataNative(locale.toString(), localeData)) { + if (!ICU.initLocaleDataNative(locale.toLanguageTag(), localeData)) { throw new AssertionError("couldn't initialize LocaleData for locale " + locale); } diff --git a/luni/src/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp index 733bf38..df26b39 100644 --- a/luni/src/main/native/libcore_icu_ICU.cpp +++ b/luni/src/main/native/libcore_icu_ICU.cpp @@ -513,16 +513,16 @@ static bool getYesterdayTodayAndTomorrow(JNIEnv* env, jobject localeData, const return true; } -static jboolean ICU_initLocaleDataNative(JNIEnv* env, jclass, jstring javaLocaleName, jobject localeData) { - ScopedUtfChars localeName(env, javaLocaleName); - if (localeName.c_str() == NULL) { +static jboolean ICU_initLocaleDataNative(JNIEnv* env, jclass, jstring javaLanguageTag, jobject localeData) { + ScopedUtfChars languageTag(env, javaLanguageTag); + if (languageTag.c_str() == NULL) { return JNI_FALSE; } - if (localeName.size() >= ULOC_FULLNAME_CAPACITY) { + if (languageTag.size() >= ULOC_FULLNAME_CAPACITY) { return JNI_FALSE; // ICU has a fixed-length limit. } - ScopedIcuLocale icuLocale(env, javaLocaleName); + ScopedIcuLocale icuLocale(env, javaLanguageTag); if (!icuLocale.valid()) { return JNI_FALSE; } @@ -530,27 +530,27 @@ static jboolean ICU_initLocaleDataNative(JNIEnv* env, jclass, jstring javaLocale // Get the DateTimePatterns. UErrorCode status = U_ZERO_ERROR; bool foundDateTimePatterns = false; - for (LocaleNameIterator it(localeName.c_str(), status); it.HasNext(); it.Up()) { + for (LocaleNameIterator it(icuLocale.locale().getBaseName(), status); it.HasNext(); it.Up()) { if (getDateTimePatterns(env, localeData, it.Get())) { foundDateTimePatterns = true; break; } } if (!foundDateTimePatterns) { - ALOGE("Couldn't find ICU DateTimePatterns for %s", localeName.c_str()); + ALOGE("Couldn't find ICU DateTimePatterns for %s", languageTag.c_str()); return JNI_FALSE; } // Get the "Yesterday", "Today", and "Tomorrow" strings. bool foundYesterdayTodayAndTomorrow = false; - for (LocaleNameIterator it(localeName.c_str(), status); it.HasNext(); it.Up()) { + for (LocaleNameIterator it(icuLocale.locale().getBaseName(), status); it.HasNext(); it.Up()) { if (getYesterdayTodayAndTomorrow(env, localeData, icuLocale.locale(), it.Get())) { foundYesterdayTodayAndTomorrow = true; break; } } if (!foundYesterdayTodayAndTomorrow) { - ALOGE("Couldn't find ICU yesterday/today/tomorrow for %s", localeName.c_str()); + ALOGE("Couldn't find ICU yesterday/today/tomorrow for %s", languageTag.c_str()); return JNI_FALSE; } @@ -621,14 +621,14 @@ static jboolean ICU_initLocaleDataNative(JNIEnv* env, jclass, jstring javaLocale setNumberPatterns(env, localeData, icuLocale.locale()); setDecimalFormatSymbolsData(env, localeData, icuLocale.locale()); - jstring countryCode = env->NewStringUTF(Locale::createFromName(localeName.c_str()).getCountry()); + jstring countryCode = env->NewStringUTF(icuLocale.locale().getCountry()); jstring internationalCurrencySymbol = ICU_getCurrencyCode(env, NULL, countryCode); env->DeleteLocalRef(countryCode); countryCode = NULL; jstring currencySymbol = NULL; if (internationalCurrencySymbol != NULL) { - currencySymbol = ICU_getCurrencySymbol(env, NULL, javaLocaleName, internationalCurrencySymbol); + currencySymbol = ICU_getCurrencySymbol(env, NULL, javaLanguageTag, internationalCurrencySymbol); } else { internationalCurrencySymbol = env->NewStringUTF("XXX"); } |