Home | History | Annotate | Download | only in native
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "ICU"
     18 
     19 #include "ErrorCode.h"
     20 #include "JNIHelp.h"
     21 #include "JniConstants.h"
     22 #include "ScopedJavaUnicodeString.h"
     23 #include "ScopedLocalRef.h"
     24 #include "ScopedUtfChars.h"
     25 #include "UniquePtr.h"
     26 #include "cutils/log.h"
     27 #include "unicode/calendar.h"
     28 #include "unicode/datefmt.h"
     29 #include "unicode/dcfmtsym.h"
     30 #include "unicode/decimfmt.h"
     31 #include "unicode/dtfmtsym.h"
     32 #include "unicode/gregocal.h"
     33 #include "unicode/locid.h"
     34 #include "unicode/numfmt.h"
     35 #include "unicode/strenum.h"
     36 #include "unicode/ubrk.h"
     37 #include "unicode/ucal.h"
     38 #include "unicode/uclean.h"
     39 #include "unicode/ucol.h"
     40 #include "unicode/ucurr.h"
     41 #include "unicode/udat.h"
     42 #include "unicode/ustring.h"
     43 #include "ureslocs.h"
     44 #include "valueOf.h"
     45 #include <stdlib.h>
     46 #include <string.h>
     47 #include <time.h>
     48 #include <sys/time.h>
     49 
     50 class ScopedResourceBundle {
     51 public:
     52     ScopedResourceBundle(UResourceBundle* bundle) : mBundle(bundle) {
     53     }
     54 
     55     ~ScopedResourceBundle() {
     56         if (mBundle != NULL) {
     57             ures_close(mBundle);
     58         }
     59     }
     60 
     61     UResourceBundle* get() {
     62         return mBundle;
     63     }
     64 
     65 private:
     66     UResourceBundle* mBundle;
     67 
     68     // Disallow copy and assignment.
     69     ScopedResourceBundle(const ScopedResourceBundle&);
     70     void operator=(const ScopedResourceBundle&);
     71 };
     72 
     73 Locale getLocale(JNIEnv* env, jstring localeName) {
     74     return Locale::createFromName(ScopedUtfChars(env, localeName).c_str());
     75 }
     76 
     77 static jint ICU_getCurrencyFractionDigitsNative(JNIEnv* env, jclass, jstring javaCurrencyCode) {
     78     UErrorCode status = U_ZERO_ERROR;
     79     UniquePtr<NumberFormat> fmt(NumberFormat::createCurrencyInstance(status));
     80     if (U_FAILURE(status)) {
     81         return -1;
     82     }
     83     ScopedJavaUnicodeString currencyCode(env, javaCurrencyCode);
     84     fmt->setCurrency(currencyCode.unicodeString().getBuffer(), status);
     85     if (U_FAILURE(status)) {
     86         return -1;
     87     }
     88     // for CurrencyFormats the minimum and maximum fraction digits are the same.
     89     return fmt->getMinimumFractionDigits();
     90 }
     91 
     92 static jstring ICU_getCurrencyCodeNative(JNIEnv* env, jclass, jstring javaKey) {
     93     UErrorCode status = U_ZERO_ERROR;
     94     ScopedResourceBundle supplData(ures_openDirect(U_ICUDATA_CURR, "supplementalData", &status));
     95     if (U_FAILURE(status)) {
     96         return NULL;
     97     }
     98 
     99     ScopedResourceBundle currencyMap(ures_getByKey(supplData.get(), "CurrencyMap", NULL, &status));
    100     if (U_FAILURE(status)) {
    101         return NULL;
    102     }
    103 
    104     ScopedUtfChars key(env, javaKey);
    105     ScopedResourceBundle currency(ures_getByKey(currencyMap.get(), key.c_str(), NULL, &status));
    106     if (U_FAILURE(status)) {
    107         return NULL;
    108     }
    109 
    110     ScopedResourceBundle currencyElem(ures_getByIndex(currency.get(), 0, NULL, &status));
    111     if (U_FAILURE(status)) {
    112         return env->NewStringUTF("None");
    113     }
    114 
    115     // check if there is a 'to' date. If there is, the currency isn't used anymore.
    116     ScopedResourceBundle currencyTo(ures_getByKey(currencyElem.get(), "to", NULL, &status));
    117     if (!U_FAILURE(status)) {
    118         // return and let the caller throw an exception
    119         return NULL;
    120     }
    121     // We need to reset 'status'. It works like errno in that ICU doesn't set it
    122     // to U_ZERO_ERROR on success: it only touches it on error, and the test
    123     // above means it now holds a failure code.
    124     status = U_ZERO_ERROR;
    125 
    126     ScopedResourceBundle currencyId(ures_getByKey(currencyElem.get(), "id", NULL, &status));
    127     if (U_FAILURE(status)) {
    128         // No id defined for this country
    129         return env->NewStringUTF("None");
    130     }
    131 
    132     int length;
    133     const jchar* id = ures_getString(currencyId.get(), &length, &status);
    134     if (U_FAILURE(status) || length == 0) {
    135         return env->NewStringUTF("None");
    136     }
    137     return env->NewString(id, length);
    138 }
    139 
    140 static jstring ICU_getCurrencySymbolNative(JNIEnv* env, jclass, jstring locale, jstring currencyCode) {
    141     ScopedUtfChars localeName(env, locale);
    142     UErrorCode status = U_ZERO_ERROR;
    143     ScopedResourceBundle currLoc(ures_open(U_ICUDATA_CURR, localeName.c_str(), &status));
    144     if (U_FAILURE(status)) {
    145         return NULL;
    146     }
    147 
    148     ScopedResourceBundle currencies(ures_getByKey(currLoc.get(), "Currencies", NULL, &status));
    149     if (U_FAILURE(status)) {
    150         return NULL;
    151     }
    152 
    153     ScopedUtfChars currency(env, currencyCode);
    154     ScopedResourceBundle currencyElems(ures_getByKey(currencies.get(), currency.c_str(), NULL, &status));
    155     if (U_FAILURE(status)) {
    156         return NULL;
    157     }
    158 
    159     int currSymbL;
    160     const jchar* currSymbU = ures_getStringByIndex(currencyElems.get(), 0, &currSymbL, &status);
    161     if (U_FAILURE(status)) {
    162         return NULL;
    163     }
    164 
    165     return (currSymbL == 0) ? NULL : env->NewString(currSymbU, currSymbL);
    166 }
    167 
    168 static jstring ICU_getDisplayCountryNative(JNIEnv* env, jclass, jstring targetLocale, jstring locale) {
    169     Locale loc = getLocale(env, locale);
    170     Locale targetLoc = getLocale(env, targetLocale);
    171     UnicodeString str;
    172     targetLoc.getDisplayCountry(loc, str);
    173     return env->NewString(str.getBuffer(), str.length());
    174 }
    175 
    176 static jstring ICU_getDisplayLanguageNative(JNIEnv* env, jclass, jstring targetLocale, jstring locale) {
    177     Locale loc = getLocale(env, locale);
    178     Locale targetLoc = getLocale(env, targetLocale);
    179     UnicodeString str;
    180     targetLoc.getDisplayLanguage(loc, str);
    181     return env->NewString(str.getBuffer(), str.length());
    182 }
    183 
    184 static jstring ICU_getDisplayVariantNative(JNIEnv* env, jclass, jstring targetLocale, jstring locale) {
    185     Locale loc = getLocale(env, locale);
    186     Locale targetLoc = getLocale(env, targetLocale);
    187     UnicodeString str;
    188     targetLoc.getDisplayVariant(loc, str);
    189     return env->NewString(str.getBuffer(), str.length());
    190 }
    191 
    192 static jstring ICU_getISO3CountryNative(JNIEnv* env, jclass, jstring locale) {
    193     Locale loc = getLocale(env, locale);
    194     return env->NewStringUTF(loc.getISO3Country());
    195 }
    196 
    197 static jstring ICU_getISO3LanguageNative(JNIEnv* env, jclass, jstring locale) {
    198     Locale loc = getLocale(env, locale);
    199     return env->NewStringUTF(loc.getISO3Language());
    200 }
    201 
    202 static jobjectArray toStringArray(JNIEnv* env, const char* const* strings) {
    203     size_t count = 0;
    204     while (strings[count] != NULL) {
    205         ++count;
    206     }
    207     jobjectArray result = env->NewObjectArray(count, JniConstants::stringClass, NULL);
    208     for (size_t i = 0; i < count; ++i) {
    209         ScopedLocalRef<jstring> s(env, env->NewStringUTF(strings[i]));
    210         env->SetObjectArrayElement(result, i, s.get());
    211     }
    212     return result;
    213 }
    214 
    215 static jobjectArray ICU_getISOCountriesNative(JNIEnv* env, jclass) {
    216     return toStringArray(env, Locale::getISOCountries());
    217 }
    218 
    219 static jobjectArray ICU_getISOLanguagesNative(JNIEnv* env, jclass) {
    220     return toStringArray(env, Locale::getISOLanguages());
    221 }
    222 
    223 template <typename Counter, typename Getter>
    224 static jobjectArray getAvailableLocales(JNIEnv* env, Counter* counter, Getter* getter) {
    225     size_t count = (*counter)();
    226     jobjectArray result = env->NewObjectArray(count, JniConstants::stringClass, NULL);
    227     for (size_t i = 0; i < count; ++i) {
    228         ScopedLocalRef<jstring> s(env, env->NewStringUTF((*getter)(i)));
    229         env->SetObjectArrayElement(result, i, s.get());
    230     }
    231     return result;
    232 }
    233 
    234 static jobjectArray ICU_getAvailableLocalesNative(JNIEnv* env, jclass) {
    235     return getAvailableLocales(env, uloc_countAvailable, uloc_getAvailable);
    236 }
    237 
    238 static jobjectArray ICU_getAvailableBreakIteratorLocalesNative(JNIEnv* env, jclass) {
    239     return getAvailableLocales(env, ubrk_countAvailable, ubrk_getAvailable);
    240 }
    241 
    242 static jobjectArray ICU_getAvailableCalendarLocalesNative(JNIEnv* env, jclass) {
    243     return getAvailableLocales(env, ucal_countAvailable, ucal_getAvailable);
    244 }
    245 
    246 static jobjectArray ICU_getAvailableCollatorLocalesNative(JNIEnv* env, jclass) {
    247     return getAvailableLocales(env, ucol_countAvailable, ucol_getAvailable);
    248 }
    249 
    250 static jobjectArray ICU_getAvailableDateFormatLocalesNative(JNIEnv* env, jclass) {
    251     return getAvailableLocales(env, udat_countAvailable, udat_getAvailable);
    252 }
    253 
    254 static jobjectArray ICU_getAvailableNumberFormatLocalesNative(JNIEnv* env, jclass) {
    255     return getAvailableLocales(env, unum_countAvailable, unum_getAvailable);
    256 }
    257 
    258 static bool getDayIntVector(JNIEnv*, UResourceBundle* gregorian, int* values) {
    259     // get the First day of week and the minimal days in first week numbers
    260     UErrorCode status = U_ZERO_ERROR;
    261     ScopedResourceBundle gregorianElems(ures_getByKey(gregorian, "DateTimeElements", NULL, &status));
    262     if (U_FAILURE(status)) {
    263         return false;
    264     }
    265 
    266     int intVectSize;
    267     const int* result = ures_getIntVector(gregorianElems.get(), &intVectSize, &status);
    268     if (U_FAILURE(status) || intVectSize != 2) {
    269         return false;
    270     }
    271 
    272     values[0] = result[0];
    273     values[1] = result[1];
    274     return true;
    275 }
    276 
    277 static jobjectArray getAmPmMarkers(JNIEnv* env, UResourceBundle* gregorian) {
    278     UErrorCode status = U_ZERO_ERROR;
    279     ScopedResourceBundle gregorianElems(ures_getByKey(gregorian, "AmPmMarkers", NULL, &status));
    280     if (U_FAILURE(status)) {
    281         return NULL;
    282     }
    283 
    284     int lengthAm, lengthPm;
    285     const jchar* am = ures_getStringByIndex(gregorianElems.get(), 0, &lengthAm, &status);
    286     const jchar* pm = ures_getStringByIndex(gregorianElems.get(), 1, &lengthPm, &status);
    287 
    288     if (U_FAILURE(status)) {
    289         return NULL;
    290     }
    291 
    292     jobjectArray amPmMarkers = env->NewObjectArray(2, JniConstants::stringClass, NULL);
    293     ScopedLocalRef<jstring> amU(env, env->NewString(am, lengthAm));
    294     env->SetObjectArrayElement(amPmMarkers, 0, amU.get());
    295     ScopedLocalRef<jstring> pmU(env, env->NewString(pm, lengthPm));
    296     env->SetObjectArrayElement(amPmMarkers, 1, pmU.get());
    297 
    298     return amPmMarkers;
    299 }
    300 
    301 static jobjectArray getEras(JNIEnv* env, UResourceBundle* gregorian) {
    302     UErrorCode status = U_ZERO_ERROR;
    303     ScopedResourceBundle gregorianElems(ures_getByKey(gregorian, "eras", NULL, &status));
    304     if (U_FAILURE(status)) {
    305         return NULL;
    306     }
    307 
    308     ScopedResourceBundle eraElems(ures_getByKey(gregorianElems.get(), "abbreviated", NULL, &status));
    309     if (U_FAILURE(status)) {
    310         return NULL;
    311     }
    312 
    313     int eraCount = ures_getSize(eraElems.get());
    314     jobjectArray eras = env->NewObjectArray(eraCount, JniConstants::stringClass, NULL);
    315     for (int i = 0; i < eraCount; ++i) {
    316         int eraLength;
    317         const jchar* era = ures_getStringByIndex(eraElems.get(), i, &eraLength, &status);
    318         if (U_FAILURE(status)) {
    319             return NULL;
    320         }
    321         ScopedLocalRef<jstring> eraU(env, env->NewString(era, eraLength));
    322         env->SetObjectArrayElement(eras, i, eraU.get());
    323     }
    324     return eras;
    325 }
    326 
    327 enum NameType { REGULAR, STAND_ALONE };
    328 enum NameWidth { LONG, SHORT };
    329 static jobjectArray getNames(JNIEnv* env, UResourceBundle* namesBundle, bool months, NameType type, NameWidth width) {
    330     const char* typeKey = (type == REGULAR) ? "format" : "stand-alone";
    331     const char* widthKey = (width == LONG) ? "wide" : "abbreviated";
    332     UErrorCode status = U_ZERO_ERROR;
    333     ScopedResourceBundle formatBundle(ures_getByKey(namesBundle, typeKey, NULL, &status));
    334     ScopedResourceBundle valuesBundle(ures_getByKey(formatBundle.get(), widthKey, NULL, &status));
    335     if (U_FAILURE(status)) {
    336         return NULL;
    337     }
    338 
    339     // The months array has a trailing empty string. The days array has a leading empty string.
    340     int count = ures_getSize(valuesBundle.get());
    341     jobjectArray result = env->NewObjectArray(count + 1, JniConstants::stringClass, NULL);
    342     env->SetObjectArrayElement(result, months ? count : 0, env->NewStringUTF(""));
    343     int arrayOffset = months ? 0 : 1;
    344     for (int i = 0; i < count; ++i) {
    345         int nameLength;
    346         const jchar* name = ures_getStringByIndex(valuesBundle.get(), i, &nameLength, &status);
    347         if (U_FAILURE(status)) {
    348             return NULL;
    349         }
    350         ScopedLocalRef<jstring> nameString(env, env->NewString(name, nameLength));
    351         env->SetObjectArrayElement(result, arrayOffset++, nameString.get());
    352     }
    353     return result;
    354 }
    355 
    356 static jstring getIntCurrencyCode(JNIEnv* env, jstring locale) {
    357     ScopedUtfChars localeChars(env, locale);
    358 
    359     // Extract the 2-character country name.
    360     if (strlen(localeChars.c_str()) < 5) {
    361         return NULL;
    362     }
    363     if (localeChars[3] < 'A' || localeChars[3] > 'Z' || localeChars[4] < 'A' || localeChars[4] > 'Z') {
    364         return NULL;
    365     }
    366 
    367     char country[3] = { localeChars[3], localeChars[4], 0 };
    368     return ICU_getCurrencyCodeNative(env, NULL, env->NewStringUTF(country));
    369 }
    370 
    371 static void setIntegerField(JNIEnv* env, jobject obj, const char* fieldName, int value) {
    372     ScopedLocalRef<jobject> integerValue(env, integerValueOf(env, value));
    373     jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, fieldName, "Ljava/lang/Integer;");
    374     env->SetObjectField(obj, fid, integerValue.get());
    375 }
    376 
    377 static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, jstring value) {
    378     jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, fieldName, "Ljava/lang/String;");
    379     env->SetObjectField(obj, fid, value);
    380 }
    381 
    382 static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, jobjectArray value) {
    383     jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, fieldName, "[Ljava/lang/String;");
    384     env->SetObjectField(obj, fid, value);
    385 }
    386 
    387 static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, UResourceBundle* bundle, int index) {
    388     UErrorCode status = U_ZERO_ERROR;
    389     int charCount;
    390     const UChar* chars = ures_getStringByIndex(bundle, index, &charCount, &status);
    391     if (U_SUCCESS(status)) {
    392         setStringField(env, obj, fieldName, env->NewString(chars, charCount));
    393     } else {
    394         LOGE("Error setting String field %s from ICU resource: %s", fieldName, u_errorName(status));
    395     }
    396 }
    397 
    398 static void setCharField(JNIEnv* env, jobject obj, const char* fieldName, UResourceBundle* bundle, int index) {
    399     UErrorCode status = U_ZERO_ERROR;
    400     int charCount;
    401     const UChar* chars = ures_getStringByIndex(bundle, index, &charCount, &status);
    402     if (U_SUCCESS(status)) {
    403         jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, fieldName, "C");
    404         env->SetCharField(obj, fid, chars[0]);
    405     } else {
    406         LOGE("Error setting char field %s from ICU resource: %s", fieldName, u_errorName(status));
    407     }
    408 }
    409 
    410 static jboolean ICU_initLocaleDataImpl(JNIEnv* env, jclass, jstring locale, jobject localeData) {
    411     ScopedUtfChars localeName(env, locale);
    412     UErrorCode status = U_ZERO_ERROR;
    413     ScopedResourceBundle root(ures_open(NULL, localeName.c_str(), &status));
    414     if (U_FAILURE(status)) {
    415         LOGE("Error getting ICU resource bundle: %s", u_errorName(status));
    416         status = U_ZERO_ERROR;
    417         return JNI_FALSE;
    418     }
    419 
    420     ScopedResourceBundle calendar(ures_getByKey(root.get(), "calendar", NULL, &status));
    421     if (U_FAILURE(status)) {
    422         LOGE("Error getting ICU calendar resource bundle: %s", u_errorName(status));
    423         return JNI_FALSE;
    424     }
    425 
    426     ScopedResourceBundle gregorian(ures_getByKey(calendar.get(), "gregorian", NULL, &status));
    427     if (U_FAILURE(status)) {
    428         LOGE("Error getting ICU gregorian resource bundle: %s", u_errorName(status));
    429         return JNI_FALSE;
    430     }
    431 
    432     int firstDayVals[] = { 0, 0 };
    433     if (getDayIntVector(env, gregorian.get(), firstDayVals)) {
    434         setIntegerField(env, localeData, "firstDayOfWeek", firstDayVals[0]);
    435         setIntegerField(env, localeData, "minimalDaysInFirstWeek", firstDayVals[1]);
    436     }
    437 
    438     setStringArrayField(env, localeData, "amPm", getAmPmMarkers(env, gregorian.get()));
    439     setStringArrayField(env, localeData, "eras", getEras(env, gregorian.get()));
    440 
    441     ScopedResourceBundle dayNames(ures_getByKey(gregorian.get(), "dayNames", NULL, &status));
    442     ScopedResourceBundle monthNames(ures_getByKey(gregorian.get(), "monthNames", NULL, &status));
    443 
    444     // Get the regular month and weekday names.
    445     jobjectArray longMonthNames = getNames(env, monthNames.get(), true, REGULAR, LONG);
    446     jobjectArray shortMonthNames = getNames(env, monthNames.get(), true, REGULAR, SHORT);
    447     jobjectArray longWeekdayNames = getNames(env, dayNames.get(), false, REGULAR, LONG);
    448     jobjectArray shortWeekdayNames = getNames(env, dayNames.get(), false, REGULAR, SHORT);
    449     setStringArrayField(env, localeData, "longMonthNames", longMonthNames);
    450     setStringArrayField(env, localeData, "shortMonthNames", shortMonthNames);
    451     setStringArrayField(env, localeData, "longWeekdayNames", longWeekdayNames);
    452     setStringArrayField(env, localeData, "shortWeekdayNames", shortWeekdayNames);
    453 
    454     // Get the stand-alone month and weekday names. If they're not available (as they aren't for
    455     // English), we reuse the regular names. If we returned null to Java, the usual fallback
    456     // mechanisms would come into play and we'd end up with the bogus stand-alone names from the
    457     // root locale ("1" for January, and so on).
    458     jobjectArray longStandAloneMonthNames = getNames(env, monthNames.get(), true, STAND_ALONE, LONG);
    459     if (longStandAloneMonthNames == NULL) {
    460         longStandAloneMonthNames = longMonthNames;
    461     }
    462     jobjectArray shortStandAloneMonthNames = getNames(env, monthNames.get(), true, STAND_ALONE, SHORT);
    463     if (shortStandAloneMonthNames == NULL) {
    464         shortStandAloneMonthNames = shortMonthNames;
    465     }
    466     jobjectArray longStandAloneWeekdayNames = getNames(env, dayNames.get(), false, STAND_ALONE, LONG);
    467     if (longStandAloneWeekdayNames == NULL) {
    468         longStandAloneWeekdayNames = longWeekdayNames;
    469     }
    470     jobjectArray shortStandAloneWeekdayNames = getNames(env, dayNames.get(), false, STAND_ALONE, SHORT);
    471     if (shortStandAloneWeekdayNames == NULL) {
    472         shortStandAloneWeekdayNames = shortWeekdayNames;
    473     }
    474     setStringArrayField(env, localeData, "longStandAloneMonthNames", longStandAloneMonthNames);
    475     setStringArrayField(env, localeData, "shortStandAloneMonthNames", shortStandAloneMonthNames);
    476     setStringArrayField(env, localeData, "longStandAloneWeekdayNames", longStandAloneWeekdayNames);
    477     setStringArrayField(env, localeData, "shortStandAloneWeekdayNames", shortStandAloneWeekdayNames);
    478 
    479     ScopedResourceBundle dateTimePatterns(ures_getByKey(gregorian.get(), "DateTimePatterns", NULL, &status));
    480     if (U_SUCCESS(status)) {
    481         setStringField(env, localeData, "fullTimeFormat", dateTimePatterns.get(), 0);
    482         setStringField(env, localeData, "longTimeFormat", dateTimePatterns.get(), 1);
    483         setStringField(env, localeData, "mediumTimeFormat", dateTimePatterns.get(), 2);
    484         setStringField(env, localeData, "shortTimeFormat", dateTimePatterns.get(), 3);
    485         setStringField(env, localeData, "fullDateFormat", dateTimePatterns.get(), 4);
    486         setStringField(env, localeData, "longDateFormat", dateTimePatterns.get(), 5);
    487         setStringField(env, localeData, "mediumDateFormat", dateTimePatterns.get(), 6);
    488         setStringField(env, localeData, "shortDateFormat", dateTimePatterns.get(), 7);
    489     }
    490     status = U_ZERO_ERROR;
    491 
    492     ScopedResourceBundle numberElements(ures_getByKey(root.get(), "NumberElements", NULL, &status));
    493     if (U_SUCCESS(status) && ures_getSize(numberElements.get()) >= 11) {
    494         setCharField(env, localeData, "zeroDigit", numberElements.get(), 4);
    495         setCharField(env, localeData, "digit", numberElements.get(), 5);
    496         setCharField(env, localeData, "decimalSeparator", numberElements.get(), 0);
    497         setCharField(env, localeData, "groupingSeparator", numberElements.get(), 1);
    498         setCharField(env, localeData, "patternSeparator", numberElements.get(), 2);
    499         setCharField(env, localeData, "percent", numberElements.get(), 3);
    500         setCharField(env, localeData, "perMill", numberElements.get(), 8);
    501         setCharField(env, localeData, "monetarySeparator", numberElements.get(), 0);
    502         setCharField(env, localeData, "minusSign", numberElements.get(), 6);
    503         setStringField(env, localeData, "exponentSeparator", numberElements.get(), 7);
    504         setStringField(env, localeData, "infinity", numberElements.get(), 9);
    505         setStringField(env, localeData, "NaN", numberElements.get(), 10);
    506     }
    507     status = U_ZERO_ERROR;
    508 
    509     jstring internationalCurrencySymbol = getIntCurrencyCode(env, locale);
    510     jstring currencySymbol = NULL;
    511     if (internationalCurrencySymbol != NULL) {
    512         currencySymbol = ICU_getCurrencySymbolNative(env, NULL, locale, internationalCurrencySymbol);
    513     } else {
    514         internationalCurrencySymbol = env->NewStringUTF("XXX");
    515     }
    516     if (currencySymbol == NULL) {
    517         // This is the UTF-8 encoding of U+00A4 (CURRENCY SIGN).
    518         currencySymbol = env->NewStringUTF("\xc2\xa4");
    519     }
    520     setStringField(env, localeData, "currencySymbol", currencySymbol);
    521     setStringField(env, localeData, "internationalCurrencySymbol", internationalCurrencySymbol);
    522 
    523     ScopedResourceBundle numberPatterns(ures_getByKey(root.get(), "NumberPatterns", NULL, &status));
    524     if (U_SUCCESS(status) && ures_getSize(numberPatterns.get()) >= 3) {
    525         setStringField(env, localeData, "numberPattern", numberPatterns.get(), 0);
    526         setStringField(env, localeData, "currencyPattern", numberPatterns.get(), 1);
    527         setStringField(env, localeData, "percentPattern", numberPatterns.get(), 2);
    528     }
    529 
    530     return JNI_TRUE;
    531 }
    532 
    533 static jstring ICU_toLowerCase(JNIEnv* env, jclass, jstring javaString, jstring localeName) {
    534     ScopedJavaUnicodeString scopedString(env, javaString);
    535     UnicodeString& s(scopedString.unicodeString());
    536     UnicodeString original(s);
    537     s.toLower(Locale::createFromName(ScopedUtfChars(env, localeName).c_str()));
    538     return s == original ? javaString : env->NewString(s.getBuffer(), s.length());
    539 }
    540 
    541 static jstring ICU_toUpperCase(JNIEnv* env, jclass, jstring javaString, jstring localeName) {
    542     ScopedJavaUnicodeString scopedString(env, javaString);
    543     UnicodeString& s(scopedString.unicodeString());
    544     UnicodeString original(s);
    545     s.toUpper(Locale::createFromName(ScopedUtfChars(env, localeName).c_str()));
    546     return s == original ? javaString : env->NewString(s.getBuffer(), s.length());
    547 }
    548 
    549 static JNINativeMethod gMethods[] = {
    550     NATIVE_METHOD(ICU, getAvailableBreakIteratorLocalesNative, "()[Ljava/lang/String;"),
    551     NATIVE_METHOD(ICU, getAvailableCalendarLocalesNative, "()[Ljava/lang/String;"),
    552     NATIVE_METHOD(ICU, getAvailableCollatorLocalesNative, "()[Ljava/lang/String;"),
    553     NATIVE_METHOD(ICU, getAvailableDateFormatLocalesNative, "()[Ljava/lang/String;"),
    554     NATIVE_METHOD(ICU, getAvailableLocalesNative, "()[Ljava/lang/String;"),
    555     NATIVE_METHOD(ICU, getAvailableNumberFormatLocalesNative, "()[Ljava/lang/String;"),
    556     NATIVE_METHOD(ICU, getCurrencyCodeNative, "(Ljava/lang/String;)Ljava/lang/String;"),
    557     NATIVE_METHOD(ICU, getCurrencyFractionDigitsNative, "(Ljava/lang/String;)I"),
    558     NATIVE_METHOD(ICU, getCurrencySymbolNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
    559     NATIVE_METHOD(ICU, getDisplayCountryNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
    560     NATIVE_METHOD(ICU, getDisplayLanguageNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
    561     NATIVE_METHOD(ICU, getDisplayVariantNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
    562     NATIVE_METHOD(ICU, getISO3CountryNative, "(Ljava/lang/String;)Ljava/lang/String;"),
    563     NATIVE_METHOD(ICU, getISO3LanguageNative, "(Ljava/lang/String;)Ljava/lang/String;"),
    564     NATIVE_METHOD(ICU, getISOCountriesNative, "()[Ljava/lang/String;"),
    565     NATIVE_METHOD(ICU, getISOLanguagesNative, "()[Ljava/lang/String;"),
    566     NATIVE_METHOD(ICU, initLocaleDataImpl, "(Ljava/lang/String;Lcom/ibm/icu4jni/util/LocaleData;)Z"),
    567     NATIVE_METHOD(ICU, toLowerCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
    568     NATIVE_METHOD(ICU, toUpperCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
    569 };
    570 int register_com_ibm_icu4jni_util_ICU(JNIEnv* env) {
    571     return jniRegisterNativeMethods(env, "com/ibm/icu4jni/util/ICU", gMethods, NELEM(gMethods));
    572 }
    573