diff options
-rw-r--r-- | benchmarks/src/benchmarks/regression/IcuBenchmark.java | 32 | ||||
-rw-r--r-- | luni/src/main/java/libcore/icu/ICU.java | 366 | ||||
-rw-r--r-- | luni/src/main/java/libcore/icu/LocaleData.java | 2 | ||||
-rw-r--r-- | luni/src/main/native/libcore_icu_ICU.cpp | 8 |
4 files changed, 225 insertions, 183 deletions
diff --git a/benchmarks/src/benchmarks/regression/IcuBenchmark.java b/benchmarks/src/benchmarks/regression/IcuBenchmark.java new file mode 100644 index 0000000..ee8270a --- /dev/null +++ b/benchmarks/src/benchmarks/regression/IcuBenchmark.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package benchmarks.regression; + +import com.google.caliper.SimpleBenchmark; + +import java.util.Locale; +import java.util.TimeZone; + +import libcore.icu.ICU; + +public class IcuBenchmark extends SimpleBenchmark { + public void time_getBestDateTimePattern(int reps) throws Exception { + for (int rep = 0; rep < reps; ++rep) { + ICU.getBestDateTimePattern("dEEEMMM", "US"); + } + } +} diff --git a/luni/src/main/java/libcore/icu/ICU.java b/luni/src/main/java/libcore/icu/ICU.java index 6a72d44..76d9c54 100644 --- a/luni/src/main/java/libcore/icu/ICU.java +++ b/luni/src/main/java/libcore/icu/ICU.java @@ -18,218 +18,228 @@ package libcore.icu; import java.util.LinkedHashSet; import java.util.Locale; +import libcore.util.BasicLruCache; /** * Makes ICU data accessible to Java. */ public final class ICU { - /** - * Cache for ISO language names. - */ - private static String[] isoLanguages; - - /** - * Cache for ISO country names. - */ - private static String[] isoCountries; - - /** - * Returns an array of ISO language names (two-letter codes), fetched either - * from ICU's database or from our memory cache. - * - * @return The array. - */ - public static String[] getISOLanguages() { - if (isoLanguages == null) { - isoLanguages = getISOLanguagesNative(); - } - return isoLanguages.clone(); - } + private static final BasicLruCache<String, String> CACHED_PATTERNS = + new BasicLruCache<String, String>(8); - /** - * Returns an array of ISO country names (two-letter codes), fetched either - * from ICU's database or from our memory cache. - * - * @return The array. - */ - public static String[] getISOCountries() { - if (isoCountries == null) { - isoCountries = getISOCountriesNative(); - } - return isoCountries.clone(); - } + private static Locale[] availableLocalesCache; - /** - * Returns the appropriate {@code Locale} given a {@code String} of the form returned - * by {@code toString}. This is very lenient, and doesn't care what's between the underscores: - * this method can parse strings that {@code Locale.toString} won't produce. - * Used to remove duplication. - */ - public static Locale localeFromString(String localeName) { - int first = localeName.indexOf('_'); - int second = localeName.indexOf('_', first + 1); - if (first == -1) { - // Language only ("ja"). - return new Locale(localeName); - } else if (second == -1) { - // Language and country ("ja_JP"). - return new Locale(localeName.substring(0, first), localeName.substring(first + 1)); - } else { - // Language and country and variant ("ja_JP_TRADITIONAL"). - return new Locale(localeName.substring(0, first), localeName.substring(first + 1, second), localeName.substring(second + 1)); - } - } + private static String[] isoCountries; - public static Locale[] localesFromStrings(String[] localeNames) { - // We need to remove duplicates caused by the conversion of "he" to "iw", et cetera. - // Java needs the obsolete code, ICU needs the modern code, but we let ICU know about - // both so that we never need to convert back when talking to it. - LinkedHashSet<Locale> set = new LinkedHashSet<Locale>(); - for (String localeName : localeNames) { - set.add(localeFromString(localeName)); - } - return set.toArray(new Locale[set.size()]); - } + private static String[] isoLanguages; - private static Locale[] availableLocalesCache; - public static Locale[] getAvailableLocales() { - if (availableLocalesCache == null) { - availableLocalesCache = localesFromStrings(getAvailableLocalesNative()); - } - return availableLocalesCache.clone(); + /** + * Returns an array of two-letter ISO 639-1 language codes, either from ICU or our cache. + */ + public static String[] getISOLanguages() { + if (isoLanguages == null) { + isoLanguages = getISOLanguagesNative(); } - - public static Locale[] getAvailableBreakIteratorLocales() { - return localesFromStrings(getAvailableBreakIteratorLocalesNative()); + return isoLanguages.clone(); + } + + /** + * Returns an array of two-letter ISO 3166 country codes, either from ICU or our cache. + */ + public static String[] getISOCountries() { + if (isoCountries == null) { + isoCountries = getISOCountriesNative(); } - - public static Locale[] getAvailableCalendarLocales() { - return localesFromStrings(getAvailableCalendarLocalesNative()); + return isoCountries.clone(); + } + + /** + * Returns the appropriate {@code Locale} given a {@code String} of the form returned + * by {@code toString}. This is very lenient, and doesn't care what's between the underscores: + * this method can parse strings that {@code Locale.toString} won't produce. + * Used to remove duplication. + */ + public static Locale localeFromString(String localeName) { + int first = localeName.indexOf('_'); + int second = localeName.indexOf('_', first + 1); + if (first == -1) { + // Language only ("ja"). + return new Locale(localeName); + } else if (second == -1) { + // Language and country ("ja_JP"). + String language = localeName.substring(0, first); + String country = localeName.substring(first + 1); + return new Locale(language, country); + } else { + // Language and country and variant ("ja_JP_TRADITIONAL"). + String language = localeName.substring(0, first); + String country = localeName.substring(first + 1, second); + String variant = localeName.substring(second + 1); + return new Locale(language, country, variant); } - - public static Locale[] getAvailableCollatorLocales() { - return localesFromStrings(getAvailableCollatorLocalesNative()); + } + + public static Locale[] localesFromStrings(String[] localeNames) { + // We need to remove duplicates caused by the conversion of "he" to "iw", et cetera. + // Java needs the obsolete code, ICU needs the modern code, but we let ICU know about + // both so that we never need to convert back when talking to it. + LinkedHashSet<Locale> set = new LinkedHashSet<Locale>(); + for (String localeName : localeNames) { + set.add(localeFromString(localeName)); } + return set.toArray(new Locale[set.size()]); + } - public static Locale[] getAvailableDateFormatLocales() { - return localesFromStrings(getAvailableDateFormatLocalesNative()); + public static Locale[] getAvailableLocales() { + if (availableLocalesCache == null) { + availableLocalesCache = localesFromStrings(getAvailableLocalesNative()); } - - public static Locale[] getAvailableDateFormatSymbolsLocales() { - return getAvailableDateFormatLocales(); - } - - public static Locale[] getAvailableDecimalFormatSymbolsLocales() { - return getAvailableNumberFormatLocales(); - } - - public static Locale[] getAvailableNumberFormatLocales() { - return localesFromStrings(getAvailableNumberFormatLocalesNative()); + return availableLocalesCache.clone(); + } + + public static Locale[] getAvailableBreakIteratorLocales() { + return localesFromStrings(getAvailableBreakIteratorLocalesNative()); + } + + public static Locale[] getAvailableCalendarLocales() { + return localesFromStrings(getAvailableCalendarLocalesNative()); + } + + public static Locale[] getAvailableCollatorLocales() { + return localesFromStrings(getAvailableCollatorLocalesNative()); + } + + public static Locale[] getAvailableDateFormatLocales() { + return localesFromStrings(getAvailableDateFormatLocalesNative()); + } + + public static Locale[] getAvailableDateFormatSymbolsLocales() { + return getAvailableDateFormatLocales(); + } + + public static Locale[] getAvailableDecimalFormatSymbolsLocales() { + return getAvailableNumberFormatLocales(); + } + + public static Locale[] getAvailableNumberFormatLocales() { + return localesFromStrings(getAvailableNumberFormatLocalesNative()); + } + + public static String getBestDateTimePattern(String skeleton, String localeName) { + String key = skeleton + "\t" + localeName; + synchronized (CACHED_PATTERNS) { + String pattern = CACHED_PATTERNS.get(key); + if (pattern == null) { + pattern = getBestDateTimePatternNative(skeleton, localeName); + CACHED_PATTERNS.put(key, pattern); + } + return pattern; } - - public static native String getBestDateTimePattern(String skeleton, String localeName); - - public static char[] getDateFormatOrder(String pattern) { - char[] result = new char[3]; - int resultIndex = 0; - boolean sawDay = false; - boolean sawMonth = false; - boolean sawYear = false; - - for (int i = 0; i < pattern.length(); ++i) { - char ch = pattern.charAt(i); - if (ch == 'd' || ch == 'L' || ch == 'M' || ch == 'y') { - if (ch == 'd' && !sawDay) { - result[resultIndex++] = 'd'; - sawDay = true; - } else if ((ch == 'L' || ch == 'M') && !sawMonth) { - result[resultIndex++] = 'M'; - sawMonth = true; - } else if ((ch == 'y') && !sawYear) { - result[resultIndex++] = 'y'; - sawYear = true; - } - } else if (ch == 'G') { - // Ignore the era specifier, if present. - } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { - throw new IllegalArgumentException("Bad pattern character '" + ch + "' in " + pattern); - } else if (ch == '\'') { - if (i < pattern.length() - 1 && pattern.charAt(i + 1) == '\'') { - ++i; - } else { - i = pattern.indexOf('\'', i + 1); - if (i == -1) { - throw new IllegalArgumentException("Bad quoting in " + pattern); - } - ++i; - } + } + + private static native String getBestDateTimePatternNative(String skeleton, String localeName); + + public static char[] getDateFormatOrder(String pattern) { + char[] result = new char[3]; + int resultIndex = 0; + boolean sawDay = false; + boolean sawMonth = false; + boolean sawYear = false; + + for (int i = 0; i < pattern.length(); ++i) { + char ch = pattern.charAt(i); + if (ch == 'd' || ch == 'L' || ch == 'M' || ch == 'y') { + if (ch == 'd' && !sawDay) { + result[resultIndex++] = 'd'; + sawDay = true; + } else if ((ch == 'L' || ch == 'M') && !sawMonth) { + result[resultIndex++] = 'M'; + sawMonth = true; + } else if ((ch == 'y') && !sawYear) { + result[resultIndex++] = 'y'; + sawYear = true; + } + } else if (ch == 'G') { + // Ignore the era specifier, if present. + } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { + throw new IllegalArgumentException("Bad pattern character '" + ch + "' in " + pattern); + } else if (ch == '\'') { + if (i < pattern.length() - 1 && pattern.charAt(i + 1) == '\'') { + ++i; } else { - // Ignore spaces and punctuation. + i = pattern.indexOf('\'', i + 1); + if (i == -1) { + throw new IllegalArgumentException("Bad quoting in " + pattern); + } + ++i; } + } else { + // Ignore spaces and punctuation. } - return result; } + return result; + } - /** - * Returns the version of the CLDR data in use, such as "22.1.1". - */ - public static native String getCldrVersion(); + /** + * Returns the version of the CLDR data in use, such as "22.1.1". + */ + public static native String getCldrVersion(); - /** - * Returns the icu4c version in use, such as "50.1.1". - */ - public static native String getIcuVersion(); + /** + * Returns the icu4c version in use, such as "50.1.1". + */ + public static native String getIcuVersion(); - /** - * Returns the Unicode version our ICU supports, such as "6.2". - */ - public static native String getUnicodeVersion(); + /** + * Returns the Unicode version our ICU supports, such as "6.2". + */ + public static native String getUnicodeVersion(); - // --- Case mapping. + // --- Case mapping. - public static native String toLowerCase(String s, String localeName); - public static native String toUpperCase(String s, String localeName); + public static native String toLowerCase(String s, String localeName); + public static native String toUpperCase(String s, String localeName); - // --- Errors. + // --- Errors. - // Just the subset of error codes needed by CharsetDecoderICU/CharsetEncoderICU. - public static final int U_ZERO_ERROR = 0; - public static final int U_INVALID_CHAR_FOUND = 10; - public static final int U_TRUNCATED_CHAR_FOUND = 11; - public static final int U_ILLEGAL_CHAR_FOUND = 12; - public static final int U_BUFFER_OVERFLOW_ERROR = 15; + // Just the subset of error codes needed by CharsetDecoderICU/CharsetEncoderICU. + public static final int U_ZERO_ERROR = 0; + public static final int U_INVALID_CHAR_FOUND = 10; + public static final int U_TRUNCATED_CHAR_FOUND = 11; + public static final int U_ILLEGAL_CHAR_FOUND = 12; + public static final int U_BUFFER_OVERFLOW_ERROR = 15; - public static boolean U_FAILURE(int error) { - return error > U_ZERO_ERROR; - } + public static boolean U_FAILURE(int error) { + return error > U_ZERO_ERROR; + } - // --- Native methods accessing ICU's database. + // --- Native methods accessing ICU's database. - private static native String[] getAvailableBreakIteratorLocalesNative(); - private static native String[] getAvailableCalendarLocalesNative(); - private static native String[] getAvailableCollatorLocalesNative(); - private static native String[] getAvailableDateFormatLocalesNative(); - private static native String[] getAvailableLocalesNative(); - private static native String[] getAvailableNumberFormatLocalesNative(); + private static native String[] getAvailableBreakIteratorLocalesNative(); + private static native String[] getAvailableCalendarLocalesNative(); + private static native String[] getAvailableCollatorLocalesNative(); + private static native String[] getAvailableDateFormatLocalesNative(); + private static native String[] getAvailableLocalesNative(); + private static native String[] getAvailableNumberFormatLocalesNative(); - public static native String[] getAvailableCurrencyCodes(); - public static native String getCurrencyCode(String countryCode); - public static native String getCurrencyDisplayName(String locale, String currencyCode); - public static native int getCurrencyFractionDigits(String currencyCode); - public static native String getCurrencySymbol(String locale, String currencyCode); + public static native String[] getAvailableCurrencyCodes(); + public static native String getCurrencyCode(String countryCode); + public static native String getCurrencyDisplayName(String locale, String currencyCode); + public static native int getCurrencyFractionDigits(String currencyCode); + public static native String getCurrencySymbol(String locale, String currencyCode); - public static native String getDisplayCountryNative(String countryCode, String locale); - public static native String getDisplayLanguageNative(String languageCode, String locale); - public static native String getDisplayVariantNative(String variantCode, String locale); + public static native String getDisplayCountryNative(String countryCode, String locale); + public static native String getDisplayLanguageNative(String languageCode, String locale); + public static native String getDisplayVariantNative(String variantCode, String locale); - public static native String getISO3CountryNative(String locale); - public static native String getISO3LanguageNative(String locale); + public static native String getISO3CountryNative(String locale); + public static native String getISO3LanguageNative(String locale); - public static native String addLikelySubtags(String locale); - public static native String getScript(String locale); + public static native String addLikelySubtags(String locale); + public static native String getScript(String locale); - private static native String[] getISOLanguagesNative(); - private static native String[] getISOCountriesNative(); + private static native String[] getISOLanguagesNative(); + private static native String[] getISOCountriesNative(); - static native boolean initLocaleDataImpl(String locale, LocaleData result); + static native boolean initLocaleDataNative(String locale, LocaleData result); } diff --git a/luni/src/main/java/libcore/icu/LocaleData.java b/luni/src/main/java/libcore/icu/LocaleData.java index 2a0573f..f00c30f 100644 --- a/luni/src/main/java/libcore/icu/LocaleData.java +++ b/luni/src/main/java/libcore/icu/LocaleData.java @@ -171,7 +171,7 @@ public final class LocaleData { private static LocaleData initLocaleData(Locale locale) { LocaleData localeData = new LocaleData(); - if (!ICU.initLocaleDataImpl(locale.toString(), localeData)) { + if (!ICU.initLocaleDataNative(locale.toString(), 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 34d5803..38e436c 100644 --- a/luni/src/main/native/libcore_icu_ICU.cpp +++ b/luni/src/main/native/libcore_icu_ICU.cpp @@ -467,7 +467,7 @@ static bool getYesterdayTodayAndTomorrow(JNIEnv* env, jobject localeData, const return false; } -static jboolean ICU_initLocaleDataImpl(JNIEnv* env, jclass, jstring javaLocaleName, jobject localeData) { +static jboolean ICU_initLocaleDataNative(JNIEnv* env, jclass, jstring javaLocaleName, jobject localeData) { ScopedUtfChars localeName(env, javaLocaleName); if (localeName.c_str() == NULL) { return JNI_FALSE; @@ -645,7 +645,7 @@ static jobject ICU_getAvailableCurrencyCodes(JNIEnv* env, jclass) { return fromStringEnumeration(env, status, "ucurr_openISOCurrencies", &e); } -static jstring ICU_getBestDateTimePattern(JNIEnv* env, jclass, jstring javaSkeleton, jstring javaLocaleName) { +static jstring ICU_getBestDateTimePatternNative(JNIEnv* env, jclass, jstring javaSkeleton, jstring javaLocaleName) { Locale locale = getLocale(env, javaLocaleName); UErrorCode status = U_ZERO_ERROR; UniquePtr<DateTimePatternGenerator> generator(DateTimePatternGenerator::createInstance(locale, status)); @@ -674,7 +674,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(ICU, getAvailableDateFormatLocalesNative, "()[Ljava/lang/String;"), NATIVE_METHOD(ICU, getAvailableLocalesNative, "()[Ljava/lang/String;"), NATIVE_METHOD(ICU, getAvailableNumberFormatLocalesNative, "()[Ljava/lang/String;"), - NATIVE_METHOD(ICU, getBestDateTimePattern, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"), + NATIVE_METHOD(ICU, getBestDateTimePatternNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"), NATIVE_METHOD(ICU, getCldrVersion, "()Ljava/lang/String;"), NATIVE_METHOD(ICU, getCurrencyCode, "(Ljava/lang/String;)Ljava/lang/String;"), NATIVE_METHOD(ICU, getCurrencyDisplayName, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"), @@ -690,7 +690,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(ICU, getIcuVersion, "()Ljava/lang/String;"), NATIVE_METHOD(ICU, getScript, "(Ljava/lang/String;)Ljava/lang/String;"), NATIVE_METHOD(ICU, getUnicodeVersion, "()Ljava/lang/String;"), - NATIVE_METHOD(ICU, initLocaleDataImpl, "(Ljava/lang/String;Llibcore/icu/LocaleData;)Z"), + NATIVE_METHOD(ICU, initLocaleDataNative, "(Ljava/lang/String;Llibcore/icu/LocaleData;)Z"), NATIVE_METHOD(ICU, toLowerCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"), NATIVE_METHOD(ICU, toUpperCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"), }; |