• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
ScopedResourceBundle(UResourceBundle * bundle)52     ScopedResourceBundle(UResourceBundle* bundle) : mBundle(bundle) {
53     }
54 
~ScopedResourceBundle()55     ~ScopedResourceBundle() {
56         if (mBundle != NULL) {
57             ures_close(mBundle);
58         }
59     }
60 
get()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 
getLocale(JNIEnv * env,jstring localeName)73 Locale getLocale(JNIEnv* env, jstring localeName) {
74     return Locale::createFromName(ScopedUtfChars(env, localeName).c_str());
75 }
76 
ICU_getCurrencyFractionDigitsNative(JNIEnv * env,jclass,jstring javaCurrencyCode)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 
ICU_getCurrencyCodeNative(JNIEnv * env,jclass,jstring javaKey)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 
ICU_getCurrencySymbolNative(JNIEnv * env,jclass,jstring locale,jstring currencyCode)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 
ICU_getDisplayCountryNative(JNIEnv * env,jclass,jstring targetLocale,jstring locale)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 
ICU_getDisplayLanguageNative(JNIEnv * env,jclass,jstring targetLocale,jstring locale)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 
ICU_getDisplayVariantNative(JNIEnv * env,jclass,jstring targetLocale,jstring locale)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 
ICU_getISO3CountryNative(JNIEnv * env,jclass,jstring locale)192 static jstring ICU_getISO3CountryNative(JNIEnv* env, jclass, jstring locale) {
193     Locale loc = getLocale(env, locale);
194     return env->NewStringUTF(loc.getISO3Country());
195 }
196 
ICU_getISO3LanguageNative(JNIEnv * env,jclass,jstring locale)197 static jstring ICU_getISO3LanguageNative(JNIEnv* env, jclass, jstring locale) {
198     Locale loc = getLocale(env, locale);
199     return env->NewStringUTF(loc.getISO3Language());
200 }
201 
toStringArray(JNIEnv * env,const char * const * strings)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 
ICU_getISOCountriesNative(JNIEnv * env,jclass)215 static jobjectArray ICU_getISOCountriesNative(JNIEnv* env, jclass) {
216     return toStringArray(env, Locale::getISOCountries());
217 }
218 
ICU_getISOLanguagesNative(JNIEnv * env,jclass)219 static jobjectArray ICU_getISOLanguagesNative(JNIEnv* env, jclass) {
220     return toStringArray(env, Locale::getISOLanguages());
221 }
222 
223 template <typename Counter, typename Getter>
getAvailableLocales(JNIEnv * env,Counter * counter,Getter * 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 
ICU_getAvailableLocalesNative(JNIEnv * env,jclass)234 static jobjectArray ICU_getAvailableLocalesNative(JNIEnv* env, jclass) {
235     return getAvailableLocales(env, uloc_countAvailable, uloc_getAvailable);
236 }
237 
ICU_getAvailableBreakIteratorLocalesNative(JNIEnv * env,jclass)238 static jobjectArray ICU_getAvailableBreakIteratorLocalesNative(JNIEnv* env, jclass) {
239     return getAvailableLocales(env, ubrk_countAvailable, ubrk_getAvailable);
240 }
241 
ICU_getAvailableCalendarLocalesNative(JNIEnv * env,jclass)242 static jobjectArray ICU_getAvailableCalendarLocalesNative(JNIEnv* env, jclass) {
243     return getAvailableLocales(env, ucal_countAvailable, ucal_getAvailable);
244 }
245 
ICU_getAvailableCollatorLocalesNative(JNIEnv * env,jclass)246 static jobjectArray ICU_getAvailableCollatorLocalesNative(JNIEnv* env, jclass) {
247     return getAvailableLocales(env, ucol_countAvailable, ucol_getAvailable);
248 }
249 
ICU_getAvailableDateFormatLocalesNative(JNIEnv * env,jclass)250 static jobjectArray ICU_getAvailableDateFormatLocalesNative(JNIEnv* env, jclass) {
251     return getAvailableLocales(env, udat_countAvailable, udat_getAvailable);
252 }
253 
ICU_getAvailableNumberFormatLocalesNative(JNIEnv * env,jclass)254 static jobjectArray ICU_getAvailableNumberFormatLocalesNative(JNIEnv* env, jclass) {
255     return getAvailableLocales(env, unum_countAvailable, unum_getAvailable);
256 }
257 
getDayIntVector(JNIEnv *,UResourceBundle * gregorian,int * values)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 
getAmPmMarkers(JNIEnv * env,UResourceBundle * gregorian)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 
getEras(JNIEnv * env,UResourceBundle * gregorian)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 };
getNames(JNIEnv * env,UResourceBundle * namesBundle,bool months,NameType type,NameWidth width)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 
getIntCurrencyCode(JNIEnv * env,jstring locale)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 
setIntegerField(JNIEnv * env,jobject obj,const char * fieldName,int value)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 
setStringField(JNIEnv * env,jobject obj,const char * fieldName,jstring value)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 
setStringArrayField(JNIEnv * env,jobject obj,const char * fieldName,jobjectArray value)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 
setStringField(JNIEnv * env,jobject obj,const char * fieldName,UResourceBundle * bundle,int index)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 
setCharField(JNIEnv * env,jobject obj,const char * fieldName,UResourceBundle * bundle,int index)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 
ICU_initLocaleDataImpl(JNIEnv * env,jclass,jstring locale,jobject localeData)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 
ICU_toLowerCase(JNIEnv * env,jclass,jstring javaString,jstring localeName)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 
ICU_toUpperCase(JNIEnv * env,jclass,jstring javaString,jstring localeName)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 };
register_com_ibm_icu4jni_util_ICU(JNIEnv * env)570 int register_com_ibm_icu4jni_util_ICU(JNIEnv* env) {
571     return jniRegisterNativeMethods(env, "com/ibm/icu4jni/util/ICU", gMethods, NELEM(gMethods));
572 }
573