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 "JNIHelp.h"
20#include "JniConstants.h"
21#include "JniException.h"
22#include "ScopedFd.h"
23#include "ScopedJavaUnicodeString.h"
24#include "ScopedLocalRef.h"
25#include "ScopedStringChars.h"
26#include "ScopedUtfChars.h"
27#include "UniquePtr.h"
28#include "cutils/log.h"
29#include "toStringArray.h"
30#include "unicode/calendar.h"
31#include "unicode/datefmt.h"
32#include "unicode/dcfmtsym.h"
33#include "unicode/decimfmt.h"
34#include "unicode/dtfmtsym.h"
35#include "unicode/gregocal.h"
36#include "unicode/locid.h"
37#include "unicode/numfmt.h"
38#include "unicode/strenum.h"
39#include "unicode/ubrk.h"
40#include "unicode/ucal.h"
41#include "unicode/uclean.h"
42#include "unicode/ucol.h"
43#include "unicode/ucurr.h"
44#include "unicode/udat.h"
45#include "unicode/uloc.h"
46#include "unicode/ustring.h"
47#include "ureslocs.h"
48#include "valueOf.h"
49
50#include <errno.h>
51#include <fcntl.h>
52#include <stdlib.h>
53#include <string.h>
54#include <string>
55#include <sys/mman.h>
56#include <sys/stat.h>
57#include <sys/time.h>
58#include <sys/types.h>
59#include <time.h>
60#include <unistd.h>
61
62// TODO: put this in a header file and use it everywhere!
63// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions.
64// It goes in the private: declarations in a class.
65#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
66    TypeName(const TypeName&); \
67    void operator=(const TypeName&)
68
69class ScopedResourceBundle {
70 public:
71  ScopedResourceBundle(UResourceBundle* bundle) : bundle_(bundle) {
72  }
73
74  ~ScopedResourceBundle() {
75    if (bundle_ != NULL) {
76      ures_close(bundle_);
77    }
78  }
79
80  UResourceBundle* get() {
81    return bundle_;
82  }
83
84  bool hasKey(const char* key) {
85    UErrorCode status = U_ZERO_ERROR;
86    ures_getStringByKey(bundle_, key, NULL, &status);
87    return U_SUCCESS(status);
88  }
89
90 private:
91  UResourceBundle* bundle_;
92  DISALLOW_COPY_AND_ASSIGN(ScopedResourceBundle);
93};
94
95Locale getLocale(JNIEnv* env, jstring localeName) {
96    return Locale::createFromName(ScopedUtfChars(env, localeName).c_str());
97}
98
99static jstring ICU_addLikelySubtags(JNIEnv* env, jclass, jstring javaLocale) {
100    UErrorCode status = U_ZERO_ERROR;
101    ScopedUtfChars localeID(env, javaLocale);
102    char maximizedLocaleID[ULOC_FULLNAME_CAPACITY];
103    uloc_addLikelySubtags(localeID.c_str(), maximizedLocaleID, sizeof(maximizedLocaleID), &status);
104    if (U_FAILURE(status)) {
105        return javaLocale;
106    }
107    return env->NewStringUTF(maximizedLocaleID);
108}
109
110static jstring ICU_getScript(JNIEnv* env, jclass, jstring javaLocale) {
111    UErrorCode status = U_ZERO_ERROR;
112    ScopedUtfChars localeID(env, javaLocale);
113    char script[ULOC_SCRIPT_CAPACITY];
114    uloc_getScript(localeID.c_str(), script, sizeof(script), &status);
115    if (U_FAILURE(status)) {
116        return NULL;
117    }
118    return env->NewStringUTF(script);
119}
120
121static jint ICU_getCurrencyFractionDigits(JNIEnv* env, jclass, jstring javaCurrencyCode) {
122    ScopedJavaUnicodeString currencyCode(env, javaCurrencyCode);
123    UnicodeString icuCurrencyCode(currencyCode.unicodeString());
124    UErrorCode status = U_ZERO_ERROR;
125    return ucurr_getDefaultFractionDigits(icuCurrencyCode.getTerminatedBuffer(), &status);
126}
127
128static jstring ICU_getCurrencyCode(JNIEnv* env, jclass, jstring javaCountryCode) {
129    UErrorCode status = U_ZERO_ERROR;
130    ScopedResourceBundle supplData(ures_openDirect(U_ICUDATA_CURR, "supplementalData", &status));
131    if (U_FAILURE(status)) {
132        return NULL;
133    }
134
135    ScopedResourceBundle currencyMap(ures_getByKey(supplData.get(), "CurrencyMap", NULL, &status));
136    if (U_FAILURE(status)) {
137        return NULL;
138    }
139
140    ScopedUtfChars countryCode(env, javaCountryCode);
141    ScopedResourceBundle currency(ures_getByKey(currencyMap.get(), countryCode.c_str(), NULL, &status));
142    if (U_FAILURE(status)) {
143        return NULL;
144    }
145
146    ScopedResourceBundle currencyElem(ures_getByIndex(currency.get(), 0, NULL, &status));
147    if (U_FAILURE(status)) {
148        return env->NewStringUTF("XXX");
149    }
150
151    // Check if there's a 'to' date. If there is, the currency isn't used anymore.
152    ScopedResourceBundle currencyTo(ures_getByKey(currencyElem.get(), "to", NULL, &status));
153    if (!U_FAILURE(status)) {
154        return NULL;
155    }
156    // Ignore the failure to find a 'to' date.
157    status = U_ZERO_ERROR;
158
159    ScopedResourceBundle currencyId(ures_getByKey(currencyElem.get(), "id", NULL, &status));
160    if (U_FAILURE(status)) {
161        // No id defined for this country
162        return env->NewStringUTF("XXX");
163    }
164
165    int32_t charCount;
166    const jchar* chars = ures_getString(currencyId.get(), &charCount, &status);
167    return (charCount == 0) ? env->NewStringUTF("XXX") : env->NewString(chars, charCount);
168}
169
170static jstring ICU_getCurrencyDisplayName(JNIEnv* env, jclass, jstring javaLocaleName, jstring javaCurrencyCode) {
171    ScopedUtfChars localeName(env, javaLocaleName);
172    ScopedJavaUnicodeString currencyCode(env, javaCurrencyCode);
173    UnicodeString icuCurrencyCode(currencyCode.unicodeString());
174    UErrorCode status = U_ZERO_ERROR;
175    UBool isChoiceFormat;
176    int32_t charCount;
177    const UChar* chars = ucurr_getName(icuCurrencyCode.getTerminatedBuffer(), localeName.c_str(),
178            UCURR_LONG_NAME, &isChoiceFormat, &charCount, &status);
179    if (status == U_USING_DEFAULT_WARNING) {
180        // ICU's default is English. We want the ISO 4217 currency code instead.
181        chars = icuCurrencyCode.getBuffer();
182        charCount = icuCurrencyCode.length();
183    }
184    return (charCount == 0) ? NULL : env->NewString(chars, charCount);
185}
186
187static jstring ICU_getCurrencySymbol(JNIEnv* env, jclass, jstring locale, jstring currencyCode) {
188    // We can't use ucurr_getName because it doesn't distinguish between using data root from
189    // the root locale and parroting back the input because it's never heard of the currency code.
190    ScopedUtfChars localeName(env, locale);
191    UErrorCode status = U_ZERO_ERROR;
192    ScopedResourceBundle currLoc(ures_open(U_ICUDATA_CURR, localeName.c_str(), &status));
193    if (U_FAILURE(status)) {
194        return NULL;
195    }
196
197    ScopedResourceBundle currencies(ures_getByKey(currLoc.get(), "Currencies", NULL, &status));
198    if (U_FAILURE(status)) {
199        return NULL;
200    }
201
202    ScopedUtfChars currency(env, currencyCode);
203    ScopedResourceBundle currencyElems(ures_getByKey(currencies.get(), currency.c_str(), NULL, &status));
204    if (U_FAILURE(status)) {
205        return NULL;
206    }
207
208    int32_t charCount;
209    const jchar* chars = ures_getStringByIndex(currencyElems.get(), 0, &charCount, &status);
210    if (U_FAILURE(status)) {
211        return NULL;
212    }
213    return (charCount == 0) ? NULL : env->NewString(chars, charCount);
214}
215
216static jstring ICU_getDisplayCountryNative(JNIEnv* env, jclass, jstring targetLocale, jstring locale) {
217    Locale loc = getLocale(env, locale);
218    Locale targetLoc = getLocale(env, targetLocale);
219    UnicodeString str;
220    targetLoc.getDisplayCountry(loc, str);
221    return env->NewString(str.getBuffer(), str.length());
222}
223
224static jstring ICU_getDisplayLanguageNative(JNIEnv* env, jclass, jstring targetLocale, jstring locale) {
225    Locale loc = getLocale(env, locale);
226    Locale targetLoc = getLocale(env, targetLocale);
227    UnicodeString str;
228    targetLoc.getDisplayLanguage(loc, str);
229    return env->NewString(str.getBuffer(), str.length());
230}
231
232static jstring ICU_getDisplayVariantNative(JNIEnv* env, jclass, jstring targetLocale, jstring locale) {
233    Locale loc = getLocale(env, locale);
234    Locale targetLoc = getLocale(env, targetLocale);
235    UnicodeString str;
236    targetLoc.getDisplayVariant(loc, str);
237    return env->NewString(str.getBuffer(), str.length());
238}
239
240static jstring ICU_getISO3CountryNative(JNIEnv* env, jclass, jstring locale) {
241    Locale loc = getLocale(env, locale);
242    return env->NewStringUTF(loc.getISO3Country());
243}
244
245static jstring ICU_getISO3LanguageNative(JNIEnv* env, jclass, jstring locale) {
246    Locale loc = getLocale(env, locale);
247    return env->NewStringUTF(loc.getISO3Language());
248}
249
250static jobjectArray ICU_getISOCountriesNative(JNIEnv* env, jclass) {
251    return toStringArray(env, Locale::getISOCountries());
252}
253
254static jobjectArray ICU_getISOLanguagesNative(JNIEnv* env, jclass) {
255    return toStringArray(env, Locale::getISOLanguages());
256}
257
258static jobjectArray ICU_getAvailableLocalesNative(JNIEnv* env, jclass) {
259    return toStringArray(env, uloc_countAvailable, uloc_getAvailable);
260}
261
262static jobjectArray ICU_getAvailableBreakIteratorLocalesNative(JNIEnv* env, jclass) {
263    return toStringArray(env, ubrk_countAvailable, ubrk_getAvailable);
264}
265
266static jobjectArray ICU_getAvailableCalendarLocalesNative(JNIEnv* env, jclass) {
267    return toStringArray(env, ucal_countAvailable, ucal_getAvailable);
268}
269
270static jobjectArray ICU_getAvailableCollatorLocalesNative(JNIEnv* env, jclass) {
271    return toStringArray(env, ucol_countAvailable, ucol_getAvailable);
272}
273
274static jobjectArray ICU_getAvailableDateFormatLocalesNative(JNIEnv* env, jclass) {
275    return toStringArray(env, udat_countAvailable, udat_getAvailable);
276}
277
278static jobjectArray ICU_getAvailableNumberFormatLocalesNative(JNIEnv* env, jclass) {
279    return toStringArray(env, unum_countAvailable, unum_getAvailable);
280}
281
282static void setIntegerField(JNIEnv* env, jobject obj, const char* fieldName, int value) {
283    ScopedLocalRef<jobject> integerValue(env, integerValueOf(env, value));
284    jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, fieldName, "Ljava/lang/Integer;");
285    env->SetObjectField(obj, fid, integerValue.get());
286}
287
288static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, jstring value) {
289    jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, fieldName, "Ljava/lang/String;");
290    env->SetObjectField(obj, fid, value);
291    env->DeleteLocalRef(value);
292}
293
294static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, jobjectArray value) {
295    jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, fieldName, "[Ljava/lang/String;");
296    env->SetObjectField(obj, fid, value);
297}
298
299static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString* valueArray, int32_t size) {
300    ScopedLocalRef<jobjectArray> result(env, env->NewObjectArray(size, JniConstants::stringClass, NULL));
301    for (int32_t i = 0; i < size ; i++) {
302        ScopedLocalRef<jstring> s(env, env->NewString(valueArray[i].getBuffer(),valueArray[i].length()));
303        if (env->ExceptionCheck()) {
304            return;
305        }
306        env->SetObjectArrayElement(result.get(), i, s.get());
307        if (env->ExceptionCheck()) {
308            return;
309        }
310    }
311    setStringArrayField(env, obj, fieldName, result.get());
312}
313
314static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, UResourceBundle* bundle, int index) {
315  UErrorCode status = U_ZERO_ERROR;
316  int charCount;
317  const UChar* chars = ures_getStringByIndex(bundle, index, &charCount, &status);
318  if (U_SUCCESS(status)) {
319    setStringField(env, obj, fieldName, env->NewString(chars, charCount));
320  } else {
321    ALOGE("Error setting String field %s from ICU resource (index %d): %s", fieldName, index, u_errorName(status));
322  }
323}
324
325static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, UResourceBundle* bundle, const char* key) {
326  UErrorCode status = U_ZERO_ERROR;
327  int charCount;
328  const UChar* chars = ures_getStringByKey(bundle, key, &charCount, &status);
329  if (U_SUCCESS(status)) {
330    setStringField(env, obj, fieldName, env->NewString(chars, charCount));
331  } else {
332    ALOGE("Error setting String field %s from ICU resource (key %s): %s", fieldName, key, u_errorName(status));
333  }
334}
335
336static void setCharField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString& value) {
337    if (value.length() == 0) {
338        return;
339    }
340    jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, fieldName, "C");
341    env->SetCharField(obj, fid, value.charAt(0));
342}
343
344static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString& value) {
345    const UChar* chars = value.getBuffer();
346    setStringField(env, obj, fieldName, env->NewString(chars, value.length()));
347}
348
349static void setNumberPatterns(JNIEnv* env, jobject obj, jstring locale) {
350    UErrorCode status = U_ZERO_ERROR;
351    Locale localeObj = getLocale(env, locale);
352
353    UnicodeString pattern;
354    UniquePtr<DecimalFormat> fmt(static_cast<DecimalFormat*>(NumberFormat::createInstance(localeObj, UNUM_CURRENCY, status)));
355    pattern = fmt->toPattern(pattern.remove());
356    setStringField(env, obj, "currencyPattern", pattern);
357
358    fmt.reset(static_cast<DecimalFormat*>(NumberFormat::createInstance(localeObj, UNUM_DECIMAL, status)));
359    pattern = fmt->toPattern(pattern.remove());
360    setStringField(env, obj, "numberPattern", pattern);
361
362    fmt.reset(static_cast<DecimalFormat*>(NumberFormat::createInstance(localeObj, UNUM_PERCENT, status)));
363    pattern = fmt->toPattern(pattern.remove());
364    setStringField(env, obj, "percentPattern", pattern);
365}
366
367static void setDecimalFormatSymbolsData(JNIEnv* env, jobject obj, jstring locale) {
368    UErrorCode status = U_ZERO_ERROR;
369    Locale localeObj = getLocale(env, locale);
370    DecimalFormatSymbols dfs(localeObj, status);
371
372    setCharField(env, obj, "decimalSeparator", dfs.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol));
373    setCharField(env, obj, "groupingSeparator", dfs.getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol));
374    setCharField(env, obj, "patternSeparator", dfs.getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol));
375    setCharField(env, obj, "percent", dfs.getSymbol(DecimalFormatSymbols::kPercentSymbol));
376    setCharField(env, obj, "perMill", dfs.getSymbol(DecimalFormatSymbols::kPerMillSymbol));
377    setCharField(env, obj, "monetarySeparator", dfs.getSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol));
378    setCharField(env, obj, "minusSign", dfs.getSymbol(DecimalFormatSymbols:: kMinusSignSymbol));
379    setStringField(env, obj, "exponentSeparator", dfs.getSymbol(DecimalFormatSymbols::kExponentialSymbol));
380    setStringField(env, obj, "infinity", dfs.getSymbol(DecimalFormatSymbols::kInfinitySymbol));
381    setStringField(env, obj, "NaN", dfs.getSymbol(DecimalFormatSymbols::kNaNSymbol));
382    setCharField(env, obj, "zeroDigit", dfs.getSymbol(DecimalFormatSymbols::kZeroDigitSymbol));
383}
384
385
386// Iterates up through the locale hierarchy. So "en_US" would return "en_US", "en", "".
387class LocaleNameIterator {
388 public:
389  LocaleNameIterator(const char* locale_name, UErrorCode& status) : status_(status), has_next_(true) {
390    strcpy(locale_name_, locale_name);
391    locale_name_length_ = strlen(locale_name_);
392  }
393
394  const char* Get() {
395      return locale_name_;
396  }
397
398  bool HasNext() {
399    return has_next_;
400  }
401
402  void Up() {
403    locale_name_length_ = uloc_getParent(locale_name_, locale_name_, sizeof(locale_name_), &status_);
404    if (locale_name_length_ == 0) {
405      has_next_ = false;
406    }
407  }
408
409 private:
410  UErrorCode& status_;
411  bool has_next_;
412  char locale_name_[ULOC_FULLNAME_CAPACITY];
413  int32_t locale_name_length_;
414
415  DISALLOW_COPY_AND_ASSIGN(LocaleNameIterator);
416};
417
418static bool getDateTimePatterns(JNIEnv* env, jobject localeData, const char* locale_name) {
419  UErrorCode status = U_ZERO_ERROR;
420  ScopedResourceBundle root(ures_open(NULL, locale_name, &status));
421  if (U_FAILURE(status)) {
422    return false;
423  }
424  ScopedResourceBundle calendar(ures_getByKey(root.get(), "calendar", NULL, &status));
425  if (U_FAILURE(status)) {
426    return false;
427  }
428  ScopedResourceBundle gregorian(ures_getByKey(calendar.get(), "gregorian", NULL, &status));
429  if (U_FAILURE(status)) {
430    return false;
431  }
432  ScopedResourceBundle dateTimePatterns(ures_getByKey(gregorian.get(), "DateTimePatterns", NULL, &status));
433  if (U_FAILURE(status)) {
434    return false;
435  }
436  setStringField(env, localeData, "fullTimeFormat", dateTimePatterns.get(), 0);
437  setStringField(env, localeData, "longTimeFormat", dateTimePatterns.get(), 1);
438  setStringField(env, localeData, "mediumTimeFormat", dateTimePatterns.get(), 2);
439  setStringField(env, localeData, "shortTimeFormat", dateTimePatterns.get(), 3);
440  setStringField(env, localeData, "fullDateFormat", dateTimePatterns.get(), 4);
441  setStringField(env, localeData, "longDateFormat", dateTimePatterns.get(), 5);
442  setStringField(env, localeData, "mediumDateFormat", dateTimePatterns.get(), 6);
443  setStringField(env, localeData, "shortDateFormat", dateTimePatterns.get(), 7);
444  return true;
445}
446
447static bool getYesterdayTodayAndTomorrow(JNIEnv* env, jobject localeData, const char* locale_name) {
448  UErrorCode status = U_ZERO_ERROR;
449  ScopedResourceBundle root(ures_open(NULL, locale_name, &status));
450  if (U_FAILURE(status)) {
451    return false;
452  }
453  ScopedResourceBundle calendar(ures_getByKey(root.get(), "calendar", NULL, &status));
454  if (U_FAILURE(status)) {
455    return false;
456  }
457  ScopedResourceBundle gregorian(ures_getByKey(calendar.get(), "gregorian", NULL, &status));
458  if (U_FAILURE(status)) {
459    return false;
460  }
461  ScopedResourceBundle fields(ures_getByKey(gregorian.get(), "fields", NULL, &status));
462  if (U_FAILURE(status)) {
463    return false;
464  }
465  ScopedResourceBundle day(ures_getByKey(fields.get(), "day", NULL, &status));
466  if (U_FAILURE(status)) {
467    return false;
468  }
469  ScopedResourceBundle relative(ures_getByKey(day.get(), "relative", NULL, &status));
470  if (U_FAILURE(status)) {
471    return false;
472  }
473  // bn_BD only has a "-2" entry.
474  if (relative.hasKey("-1") && relative.hasKey("0") && relative.hasKey("1")) {
475    setStringField(env, localeData, "yesterday", relative.get(), "-1");
476    setStringField(env, localeData, "today", relative.get(), "0");
477    setStringField(env, localeData, "tomorrow", relative.get(), "1");
478    return true;
479  }
480  return false;
481}
482
483static jboolean ICU_initLocaleDataImpl(JNIEnv* env, jclass, jstring locale, jobject localeData) {
484    ScopedUtfChars localeName(env, locale);
485    if (localeName.c_str() == NULL) {
486        return JNI_FALSE;
487    }
488    if (localeName.size() >= ULOC_FULLNAME_CAPACITY) {
489        return JNI_FALSE; // ICU has a fixed-length limit.
490    }
491
492    // Get the DateTimePatterns.
493    UErrorCode status = U_ZERO_ERROR;
494    bool foundDateTimePatterns = false;
495    for (LocaleNameIterator it(localeName.c_str(), status); it.HasNext(); it.Up()) {
496      if (getDateTimePatterns(env, localeData, it.Get())) {
497          foundDateTimePatterns = true;
498          break;
499      }
500    }
501    if (!foundDateTimePatterns) {
502        ALOGE("Couldn't find ICU DateTimePatterns for %s", localeName.c_str());
503        return JNI_FALSE;
504    }
505
506    // Get the "Yesterday", "Today", and "Tomorrow" strings.
507    bool foundYesterdayTodayAndTomorrow = false;
508    for (LocaleNameIterator it(localeName.c_str(), status); it.HasNext(); it.Up()) {
509      if (getYesterdayTodayAndTomorrow(env, localeData, it.Get())) {
510        foundYesterdayTodayAndTomorrow = true;
511        break;
512      }
513    }
514    if (!foundYesterdayTodayAndTomorrow) {
515      ALOGE("Couldn't find ICU yesterday/today/tomorrow for %s", localeName.c_str());
516      return JNI_FALSE;
517    }
518
519    status = U_ZERO_ERROR;
520    Locale localeObj = getLocale(env, locale);
521    UniquePtr<Calendar> cal(Calendar::createInstance(localeObj, status));
522    if (U_FAILURE(status)) {
523        return JNI_FALSE;
524    }
525
526    setIntegerField(env, localeData, "firstDayOfWeek", cal->getFirstDayOfWeek());
527    setIntegerField(env, localeData, "minimalDaysInFirstWeek", cal->getMinimalDaysInFirstWeek());
528
529    // Get DateFormatSymbols.
530    status = U_ZERO_ERROR;
531    DateFormatSymbols dateFormatSym(localeObj, status);
532    if (U_FAILURE(status)) {
533        return JNI_FALSE;
534    }
535
536    // Get AM/PM and BC/AD.
537    int32_t count = 0;
538    const UnicodeString* amPmStrs = dateFormatSym.getAmPmStrings(count);
539    setStringArrayField(env, localeData, "amPm", amPmStrs, count);
540    const UnicodeString* erasStrs = dateFormatSym.getEras(count);
541    setStringArrayField(env, localeData, "eras", erasStrs, count);
542
543    const UnicodeString* longMonthNames =
544       dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::WIDE);
545    setStringArrayField(env, localeData, "longMonthNames", longMonthNames, count);
546    const UnicodeString* shortMonthNames =
547        dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED);
548    setStringArrayField(env, localeData, "shortMonthNames", shortMonthNames, count);
549    const UnicodeString* tinyMonthNames =
550        dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::NARROW);
551    setStringArrayField(env, localeData, "tinyMonthNames", tinyMonthNames, count);
552    const UnicodeString* longWeekdayNames =
553        dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::WIDE);
554    setStringArrayField(env, localeData, "longWeekdayNames", longWeekdayNames, count);
555    const UnicodeString* shortWeekdayNames =
556        dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED);
557    setStringArrayField(env, localeData, "shortWeekdayNames", shortWeekdayNames, count);
558    const UnicodeString* tinyWeekdayNames =
559        dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::NARROW);
560    setStringArrayField(env, localeData, "tinyWeekdayNames", tinyWeekdayNames, count);
561
562    const UnicodeString* longStandAloneMonthNames =
563        dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE);
564    setStringArrayField(env, localeData, "longStandAloneMonthNames", longStandAloneMonthNames, count);
565    const UnicodeString* shortStandAloneMonthNames =
566        dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED);
567    setStringArrayField(env, localeData, "shortStandAloneMonthNames", shortStandAloneMonthNames, count);
568    const UnicodeString* tinyStandAloneMonthNames =
569        dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::NARROW);
570    setStringArrayField(env, localeData, "tinyStandAloneMonthNames", tinyStandAloneMonthNames, count);
571    const UnicodeString* longStandAloneWeekdayNames =
572        dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE);
573    setStringArrayField(env, localeData, "longStandAloneWeekdayNames", longStandAloneWeekdayNames, count);
574    const UnicodeString* shortStandAloneWeekdayNames =
575        dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED);
576    setStringArrayField(env, localeData, "shortStandAloneWeekdayNames", shortStandAloneWeekdayNames, count);
577    const UnicodeString* tinyStandAloneWeekdayNames =
578        dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::NARROW);
579    setStringArrayField(env, localeData, "tinyStandAloneWeekdayNames", tinyStandAloneWeekdayNames, count);
580
581    status = U_ZERO_ERROR;
582
583    // For numberPatterns and symbols.
584    setNumberPatterns(env, localeData, locale);
585    setDecimalFormatSymbolsData(env, localeData, locale);
586
587    jstring countryCode = env->NewStringUTF(Locale::createFromName(localeName.c_str()).getCountry());
588    jstring internationalCurrencySymbol = ICU_getCurrencyCode(env, NULL, countryCode);
589    env->DeleteLocalRef(countryCode);
590    countryCode = NULL;
591
592    jstring currencySymbol = NULL;
593    if (internationalCurrencySymbol != NULL) {
594        currencySymbol = ICU_getCurrencySymbol(env, NULL, locale, internationalCurrencySymbol);
595    } else {
596        internationalCurrencySymbol = env->NewStringUTF("XXX");
597    }
598    if (currencySymbol == NULL) {
599        // This is the UTF-8 encoding of U+00A4 (CURRENCY SIGN).
600        currencySymbol = env->NewStringUTF("\xc2\xa4");
601    }
602    setStringField(env, localeData, "currencySymbol", currencySymbol);
603    setStringField(env, localeData, "internationalCurrencySymbol", internationalCurrencySymbol);
604
605    return JNI_TRUE;
606}
607
608static jstring ICU_toLowerCase(JNIEnv* env, jclass, jstring javaString, jstring localeName) {
609    ScopedJavaUnicodeString scopedString(env, javaString);
610    UnicodeString& s(scopedString.unicodeString());
611    UnicodeString original(s);
612    s.toLower(Locale::createFromName(ScopedUtfChars(env, localeName).c_str()));
613    return s == original ? javaString : env->NewString(s.getBuffer(), s.length());
614}
615
616static jstring ICU_toUpperCase(JNIEnv* env, jclass, jstring javaString, jstring localeName) {
617    ScopedJavaUnicodeString scopedString(env, javaString);
618    UnicodeString& s(scopedString.unicodeString());
619    UnicodeString original(s);
620    s.toUpper(Locale::createFromName(ScopedUtfChars(env, localeName).c_str()));
621    return s == original ? javaString : env->NewString(s.getBuffer(), s.length());
622}
623
624static jstring versionString(JNIEnv* env, const UVersionInfo& version) {
625    char versionString[U_MAX_VERSION_STRING_LENGTH];
626    u_versionToString(const_cast<UVersionInfo&>(version), &versionString[0]);
627    return env->NewStringUTF(versionString);
628}
629
630static jstring ICU_getIcuVersion(JNIEnv* env, jclass) {
631    UVersionInfo icuVersion;
632    u_getVersion(icuVersion);
633    return versionString(env, icuVersion);
634}
635
636static jstring ICU_getUnicodeVersion(JNIEnv* env, jclass) {
637    UVersionInfo unicodeVersion;
638    u_getUnicodeVersion(unicodeVersion);
639    return versionString(env, unicodeVersion);
640}
641
642
643struct EnumerationCounter {
644    const size_t count;
645    EnumerationCounter(size_t count) : count(count) {}
646    size_t operator()() { return count; }
647};
648struct EnumerationGetter {
649    UEnumeration* e;
650    UErrorCode* status;
651    EnumerationGetter(UEnumeration* e, UErrorCode* status) : e(e), status(status) {}
652    const UChar* operator()(int32_t* charCount) { return uenum_unext(e, charCount, status); }
653};
654static jobject ICU_getAvailableCurrencyCodes(JNIEnv* env, jclass) {
655    UErrorCode status = U_ZERO_ERROR;
656    UEnumeration* e(ucurr_openISOCurrencies(UCURR_COMMON|UCURR_NON_DEPRECATED, &status));
657    EnumerationCounter counter(uenum_count(e, &status));
658    if (maybeThrowIcuException(env, "uenum_count", status)) {
659        return NULL;
660    }
661    EnumerationGetter getter(e, &status);
662    jobject result = toStringArray16(env, &counter, &getter);
663    maybeThrowIcuException(env, "uenum_unext", status);
664    uenum_close(e);
665    return result;
666}
667
668static JNINativeMethod gMethods[] = {
669    NATIVE_METHOD(ICU, addLikelySubtags, "(Ljava/lang/String;)Ljava/lang/String;"),
670    NATIVE_METHOD(ICU, getAvailableBreakIteratorLocalesNative, "()[Ljava/lang/String;"),
671    NATIVE_METHOD(ICU, getAvailableCalendarLocalesNative, "()[Ljava/lang/String;"),
672    NATIVE_METHOD(ICU, getAvailableCollatorLocalesNative, "()[Ljava/lang/String;"),
673    NATIVE_METHOD(ICU, getAvailableCurrencyCodes, "()[Ljava/lang/String;"),
674    NATIVE_METHOD(ICU, getAvailableDateFormatLocalesNative, "()[Ljava/lang/String;"),
675    NATIVE_METHOD(ICU, getAvailableLocalesNative, "()[Ljava/lang/String;"),
676    NATIVE_METHOD(ICU, getAvailableNumberFormatLocalesNative, "()[Ljava/lang/String;"),
677    NATIVE_METHOD(ICU, getCurrencyCode, "(Ljava/lang/String;)Ljava/lang/String;"),
678    NATIVE_METHOD(ICU, getCurrencyDisplayName, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
679    NATIVE_METHOD(ICU, getCurrencyFractionDigits, "(Ljava/lang/String;)I"),
680    NATIVE_METHOD(ICU, getCurrencySymbol, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
681    NATIVE_METHOD(ICU, getDisplayCountryNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
682    NATIVE_METHOD(ICU, getDisplayLanguageNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
683    NATIVE_METHOD(ICU, getDisplayVariantNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
684    NATIVE_METHOD(ICU, getISO3CountryNative, "(Ljava/lang/String;)Ljava/lang/String;"),
685    NATIVE_METHOD(ICU, getISO3LanguageNative, "(Ljava/lang/String;)Ljava/lang/String;"),
686    NATIVE_METHOD(ICU, getISOCountriesNative, "()[Ljava/lang/String;"),
687    NATIVE_METHOD(ICU, getISOLanguagesNative, "()[Ljava/lang/String;"),
688    NATIVE_METHOD(ICU, getIcuVersion, "()Ljava/lang/String;"),
689    NATIVE_METHOD(ICU, getScript, "(Ljava/lang/String;)Ljava/lang/String;"),
690    NATIVE_METHOD(ICU, getUnicodeVersion, "()Ljava/lang/String;"),
691    NATIVE_METHOD(ICU, initLocaleDataImpl, "(Ljava/lang/String;Llibcore/icu/LocaleData;)Z"),
692    NATIVE_METHOD(ICU, toLowerCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
693    NATIVE_METHOD(ICU, toUpperCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
694};
695void register_libcore_icu_ICU(JNIEnv* env) {
696    std::string path;
697    path = u_getDataDirectory();
698    path += "/";
699    path += U_ICUDATA_NAME;
700    path += ".dat";
701
702    #define FAIL_WITH_STRERROR(s) \
703        ALOGE("Couldn't " s " '%s': %s", path.c_str(), strerror(errno)); \
704        abort();
705    #define MAYBE_FAIL_WITH_ICU_ERROR(s) \
706        if (status != U_ZERO_ERROR) {\
707            ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path.c_str()); \
708            abort(); \
709        }
710
711    // Open the file and get its length.
712    ScopedFd fd(open(path.c_str(), O_RDONLY));
713    if (fd.get() == -1) {
714        FAIL_WITH_STRERROR("open");
715    }
716    struct stat sb;
717    if (fstat(fd.get(), &sb) == -1) {
718        FAIL_WITH_STRERROR("stat");
719    }
720
721    // Map it.
722    void* data = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd.get(), 0);
723    if (data == MAP_FAILED) {
724        FAIL_WITH_STRERROR("mmap");
725    }
726
727    // Tell the kernel that accesses are likely to be random rather than sequential.
728    if (madvise(data, sb.st_size, MADV_RANDOM) == -1) {
729        FAIL_WITH_STRERROR("madvise(MADV_RANDOM)");
730    }
731
732    // Tell ICU to use our memory-mapped data.
733    UErrorCode status = U_ZERO_ERROR;
734    udata_setCommonData(data, &status);
735    MAYBE_FAIL_WITH_ICU_ERROR("udata_setCommonData");
736    // Tell ICU it can *only* use our memory-mapped data.
737    udata_setFileAccess(UDATA_NO_FILES, &status);
738    MAYBE_FAIL_WITH_ICU_ERROR("udata_setFileAccess");
739
740    // Failures to find the ICU data tend to be somewhat obscure because ICU loads its data on first
741    // use, which can be anywhere. Force initialization up front so we can report a nice clear error
742    // and bail.
743    u_init(&status);
744    MAYBE_FAIL_WITH_ICU_ERROR("u_init");
745    jniRegisterNativeMethods(env, "libcore/icu/ICU", gMethods, NELEM(gMethods));
746}
747