libcore_icu_ICU.cpp revision 02b43bf44cb8bf8653baf8d951943a907310c3a2
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 "IcuUtilities.h"
20#include "JNIHelp.h"
21#include "JniConstants.h"
22#include "JniException.h"
23#include "ScopedFd.h"
24#include "ScopedIcuLocale.h"
25#include "ScopedJavaUnicodeString.h"
26#include "ScopedLocalRef.h"
27#include "ScopedUtfChars.h"
28#include "UniquePtr.h"
29#include "cutils/log.h"
30#include "toStringArray.h"
31#include "unicode/brkiter.h"
32#include "unicode/calendar.h"
33#include "unicode/datefmt.h"
34#include "unicode/dcfmtsym.h"
35#include "unicode/decimfmt.h"
36#include "unicode/dtfmtsym.h"
37#include "unicode/dtptngen.h"
38#include "unicode/gregocal.h"
39#include "unicode/locid.h"
40#include "unicode/numfmt.h"
41#include "unicode/strenum.h"
42#include "unicode/ubrk.h"
43#include "unicode/ucal.h"
44#include "unicode/uclean.h"
45#include "unicode/ucol.h"
46#include "unicode/ucurr.h"
47#include "unicode/udat.h"
48#include "unicode/uloc.h"
49#include "unicode/ulocdata.h"
50#include "unicode/ustring.h"
51#include "ureslocs.h"
52#include "valueOf.h"
53
54#include <errno.h>
55#include <fcntl.h>
56#include <stdlib.h>
57#include <string.h>
58#include <string>
59#include <sys/mman.h>
60#include <sys/stat.h>
61#include <sys/time.h>
62#include <sys/types.h>
63#include <time.h>
64#include <unistd.h>
65#include <vector>
66
67// TODO: put this in a header file and use it everywhere!
68// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions.
69// It goes in the private: declarations in a class.
70#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
71    TypeName(const TypeName&); \
72    void operator=(const TypeName&)
73
74class ScopedResourceBundle {
75 public:
76  ScopedResourceBundle(UResourceBundle* bundle) : bundle_(bundle) {
77  }
78
79  ~ScopedResourceBundle() {
80    if (bundle_ != NULL) {
81      ures_close(bundle_);
82    }
83  }
84
85  UResourceBundle* get() {
86    return bundle_;
87  }
88
89  bool hasKey(const char* key) {
90    UErrorCode status = U_ZERO_ERROR;
91    ures_getStringByKey(bundle_, key, NULL, &status);
92    return U_SUCCESS(status);
93  }
94
95 private:
96  UResourceBundle* bundle_;
97  DISALLOW_COPY_AND_ASSIGN(ScopedResourceBundle);
98};
99
100static jstring ICU_addLikelySubtags(JNIEnv* env, jclass, jstring javaLocaleName) {
101    UErrorCode status = U_ZERO_ERROR;
102    ScopedUtfChars localeID(env, javaLocaleName);
103    char maximizedLocaleID[ULOC_FULLNAME_CAPACITY];
104    uloc_addLikelySubtags(localeID.c_str(), maximizedLocaleID, sizeof(maximizedLocaleID), &status);
105    if (U_FAILURE(status)) {
106        return javaLocaleName;
107    }
108    return env->NewStringUTF(maximizedLocaleID);
109}
110
111static jstring ICU_getScript(JNIEnv* env, jclass, jstring javaLocaleName) {
112  ScopedIcuLocale icuLocale(env, javaLocaleName);
113  if (!icuLocale.valid()) {
114    return NULL;
115  }
116  return env->NewStringUTF(icuLocale.locale().getScript());
117}
118
119static jint ICU_getCurrencyFractionDigits(JNIEnv* env, jclass, jstring javaCurrencyCode) {
120  ScopedJavaUnicodeString currencyCode(env, javaCurrencyCode);
121  if (!currencyCode.valid()) {
122    return 0;
123  }
124  UnicodeString icuCurrencyCode(currencyCode.unicodeString());
125  UErrorCode status = U_ZERO_ERROR;
126  return ucurr_getDefaultFractionDigits(icuCurrencyCode.getTerminatedBuffer(), &status);
127}
128
129static jint ICU_getCurrencyNumericCode(JNIEnv* env, jclass, jstring javaCurrencyCode) {
130  ScopedJavaUnicodeString currencyCode(env, javaCurrencyCode);
131  if (!currencyCode.valid()) {
132    return 0;
133  }
134  UnicodeString icuCurrencyCode(currencyCode.unicodeString());
135  return ucurr_getNumericCode(icuCurrencyCode.getTerminatedBuffer());
136}
137
138// TODO: rewrite this with int32_t ucurr_forLocale(const char* locale, UChar* buff, int32_t buffCapacity, UErrorCode* ec)...
139static jstring ICU_getCurrencyCode(JNIEnv* env, jclass, jstring javaCountryCode) {
140    UErrorCode status = U_ZERO_ERROR;
141    ScopedResourceBundle supplData(ures_openDirect(U_ICUDATA_CURR, "supplementalData", &status));
142    if (U_FAILURE(status)) {
143        return NULL;
144    }
145
146    ScopedResourceBundle currencyMap(ures_getByKey(supplData.get(), "CurrencyMap", NULL, &status));
147    if (U_FAILURE(status)) {
148        return NULL;
149    }
150
151    ScopedUtfChars countryCode(env, javaCountryCode);
152    ScopedResourceBundle currency(ures_getByKey(currencyMap.get(), countryCode.c_str(), NULL, &status));
153    if (U_FAILURE(status)) {
154        return NULL;
155    }
156
157    ScopedResourceBundle currencyElem(ures_getByIndex(currency.get(), 0, NULL, &status));
158    if (U_FAILURE(status)) {
159        return env->NewStringUTF("XXX");
160    }
161
162    // Check if there's a 'to' date. If there is, the currency isn't used anymore.
163    ScopedResourceBundle currencyTo(ures_getByKey(currencyElem.get(), "to", NULL, &status));
164    if (!U_FAILURE(status)) {
165        return NULL;
166    }
167    // Ignore the failure to find a 'to' date.
168    status = U_ZERO_ERROR;
169
170    ScopedResourceBundle currencyId(ures_getByKey(currencyElem.get(), "id", NULL, &status));
171    if (U_FAILURE(status)) {
172        // No id defined for this country
173        return env->NewStringUTF("XXX");
174    }
175
176    int32_t charCount;
177    const jchar* chars = ures_getString(currencyId.get(), &charCount, &status);
178    return (charCount == 0) ? env->NewStringUTF("XXX") : env->NewString(chars, charCount);
179}
180
181static jstring getCurrencyName(JNIEnv* env, jstring javaLanguageTag, jstring javaCurrencyCode, UCurrNameStyle nameStyle) {
182  ScopedUtfChars languageTag(env, javaLanguageTag);
183  if (languageTag.c_str() == NULL) {
184    return NULL;
185  }
186  ScopedJavaUnicodeString currencyCode(env, javaCurrencyCode);
187  if (!currencyCode.valid()) {
188    return NULL;
189  }
190  UnicodeString icuCurrencyCode(currencyCode.unicodeString());
191  UErrorCode status = U_ZERO_ERROR;
192  UBool isChoiceFormat = false;
193  int32_t charCount;
194  const UChar* chars = ucurr_getName(icuCurrencyCode.getTerminatedBuffer(), languageTag.c_str(),
195                                     nameStyle, &isChoiceFormat, &charCount, &status);
196  if (status == U_USING_DEFAULT_WARNING) {
197    if (nameStyle == UCURR_SYMBOL_NAME) {
198      // ICU doesn't distinguish between falling back to the root locale and meeting a genuinely
199      // unknown currency. The Currency class does.
200      if (!ucurr_isAvailable(icuCurrencyCode.getTerminatedBuffer(), U_DATE_MIN, U_DATE_MAX, &status)) {
201        return NULL;
202      }
203    }
204    if (nameStyle == UCURR_LONG_NAME) {
205      // ICU's default is English. We want the ISO 4217 currency code instead.
206      chars = icuCurrencyCode.getBuffer();
207      charCount = icuCurrencyCode.length();
208    }
209  }
210  return (charCount == 0) ? NULL : env->NewString(chars, charCount);
211}
212
213static jstring ICU_getCurrencyDisplayName(JNIEnv* env, jclass, jstring javaLanguageTag, jstring javaCurrencyCode) {
214  return getCurrencyName(env, javaLanguageTag, javaCurrencyCode, UCURR_LONG_NAME);
215}
216
217static jstring ICU_getCurrencySymbol(JNIEnv* env, jclass, jstring javaLanguageTag, jstring javaCurrencyCode) {
218  return getCurrencyName(env, javaLanguageTag, javaCurrencyCode, UCURR_SYMBOL_NAME);
219}
220
221static jstring ICU_getDisplayCountryNative(JNIEnv* env, jclass, jstring javaTargetLanguageTag, jstring javaLanguageTag) {
222  ScopedIcuLocale icuLocale(env, javaLanguageTag);
223  if (!icuLocale.valid()) {
224    return NULL;
225  }
226  ScopedIcuLocale icuTargetLocale(env, javaTargetLanguageTag);
227  if (!icuTargetLocale.valid()) {
228    return NULL;
229  }
230
231  UnicodeString str;
232  icuTargetLocale.locale().getDisplayCountry(icuLocale.locale(), str);
233  return env->NewString(str.getBuffer(), str.length());
234}
235
236static jstring ICU_getDisplayLanguageNative(JNIEnv* env, jclass, jstring javaTargetLanguageTag, jstring javaLanguageTag) {
237  ScopedIcuLocale icuLocale(env, javaLanguageTag);
238  if (!icuLocale.valid()) {
239    return NULL;
240  }
241  ScopedIcuLocale icuTargetLocale(env, javaTargetLanguageTag);
242  if (!icuTargetLocale.valid()) {
243    return NULL;
244  }
245
246  UnicodeString str;
247  icuTargetLocale.locale().getDisplayLanguage(icuLocale.locale(), str);
248  return env->NewString(str.getBuffer(), str.length());
249}
250
251static jstring ICU_getDisplayScriptNative(JNIEnv* env, jclass, jstring javaTargetLanguageTag, jstring javaLanguageTag) {
252  ScopedIcuLocale icuLocale(env, javaLanguageTag);
253  if (!icuLocale.valid()) {
254    return NULL;
255  }
256  ScopedIcuLocale icuTargetLocale(env, javaTargetLanguageTag);
257  if (!icuTargetLocale.valid()) {
258    return NULL;
259  }
260
261  UnicodeString str;
262  icuTargetLocale.locale().getDisplayScript(icuLocale.locale(), str);
263  return env->NewString(str.getBuffer(), str.length());
264}
265
266static jstring ICU_getDisplayVariantNative(JNIEnv* env, jclass, jstring javaTargetLanguageTag, jstring javaLanguageTag) {
267  ScopedIcuLocale icuLocale(env, javaLanguageTag);
268  if (!icuLocale.valid()) {
269    return NULL;
270  }
271  ScopedIcuLocale icuTargetLocale(env, javaTargetLanguageTag);
272  if (!icuTargetLocale.valid()) {
273    return NULL;
274  }
275
276  UnicodeString str;
277  icuTargetLocale.locale().getDisplayVariant(icuLocale.locale(), str);
278  return env->NewString(str.getBuffer(), str.length());
279}
280
281static jstring ICU_getISO3Country(JNIEnv* env, jclass, jstring javaLanguageTag) {
282  ScopedIcuLocale icuLocale(env, javaLanguageTag);
283  if (!icuLocale.valid()) {
284    return NULL;
285  }
286  return env->NewStringUTF(icuLocale.locale().getISO3Country());
287}
288
289static jstring ICU_getISO3Language(JNIEnv* env, jclass, jstring javaLanguageTag) {
290  ScopedIcuLocale icuLocale(env, javaLanguageTag);
291  if (!icuLocale.valid()) {
292    return NULL;
293  }
294  return env->NewStringUTF(icuLocale.locale().getISO3Language());
295}
296
297static jobjectArray ICU_getISOCountriesNative(JNIEnv* env, jclass) {
298    return toStringArray(env, Locale::getISOCountries());
299}
300
301static jobjectArray ICU_getISOLanguagesNative(JNIEnv* env, jclass) {
302    return toStringArray(env, Locale::getISOLanguages());
303}
304
305static jobjectArray ICU_getAvailableLocalesNative(JNIEnv* env, jclass) {
306    return toStringArray(env, uloc_countAvailable, uloc_getAvailable);
307}
308
309static jobjectArray ICU_getAvailableBreakIteratorLocalesNative(JNIEnv* env, jclass) {
310    return toStringArray(env, ubrk_countAvailable, ubrk_getAvailable);
311}
312
313static jobjectArray ICU_getAvailableCalendarLocalesNative(JNIEnv* env, jclass) {
314    return toStringArray(env, ucal_countAvailable, ucal_getAvailable);
315}
316
317static jobjectArray ICU_getAvailableCollatorLocalesNative(JNIEnv* env, jclass) {
318    return toStringArray(env, ucol_countAvailable, ucol_getAvailable);
319}
320
321static jobjectArray ICU_getAvailableDateFormatLocalesNative(JNIEnv* env, jclass) {
322    return toStringArray(env, udat_countAvailable, udat_getAvailable);
323}
324
325static jobjectArray ICU_getAvailableNumberFormatLocalesNative(JNIEnv* env, jclass) {
326    return toStringArray(env, unum_countAvailable, unum_getAvailable);
327}
328
329static bool setIntegerField(JNIEnv* env, jobject obj, const char* fieldName, int value) {
330    ScopedLocalRef<jobject> integerValue(env, integerValueOf(env, value));
331    if (integerValue.get() == NULL) return false;
332    jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, fieldName, "Ljava/lang/Integer;");
333    env->SetObjectField(obj, fid, integerValue.get());
334    return true;
335}
336
337static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, jstring value) {
338    jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, fieldName, "Ljava/lang/String;");
339    env->SetObjectField(obj, fid, value);
340    env->DeleteLocalRef(value);
341}
342
343static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, jobjectArray value) {
344    jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, fieldName, "[Ljava/lang/String;");
345    env->SetObjectField(obj, fid, value);
346}
347
348static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString* valueArray, int32_t size) {
349    ScopedLocalRef<jobjectArray> result(env, env->NewObjectArray(size, JniConstants::stringClass, NULL));
350    for (int32_t i = 0; i < size ; i++) {
351        ScopedLocalRef<jstring> s(env, env->NewString(valueArray[i].getBuffer(),valueArray[i].length()));
352        if (env->ExceptionCheck()) {
353            return;
354        }
355        env->SetObjectArrayElement(result.get(), i, s.get());
356        if (env->ExceptionCheck()) {
357            return;
358        }
359    }
360    setStringArrayField(env, obj, fieldName, result.get());
361}
362
363static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, UResourceBundle* bundle, int index) {
364  UErrorCode status = U_ZERO_ERROR;
365  int charCount;
366  const UChar* chars = ures_getStringByIndex(bundle, index, &charCount, &status);
367  if (U_SUCCESS(status)) {
368    setStringField(env, obj, fieldName, env->NewString(chars, charCount));
369  } else {
370    ALOGE("Error setting String field %s from ICU resource (index %d): %s", fieldName, index, u_errorName(status));
371  }
372}
373
374static void setCharField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString& value) {
375    if (value.length() == 0) {
376        return;
377    }
378    jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, fieldName, "C");
379    env->SetCharField(obj, fid, value.charAt(0));
380}
381
382static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString& value) {
383    const UChar* chars = value.getBuffer();
384    setStringField(env, obj, fieldName, env->NewString(chars, value.length()));
385}
386
387static void setNumberPatterns(JNIEnv* env, jobject obj, Locale& locale) {
388    UErrorCode status = U_ZERO_ERROR;
389
390    UnicodeString pattern;
391    UniquePtr<DecimalFormat> fmt(static_cast<DecimalFormat*>(NumberFormat::createInstance(locale, UNUM_CURRENCY, status)));
392    pattern = fmt->toPattern(pattern.remove());
393    setStringField(env, obj, "currencyPattern", pattern);
394
395    fmt.reset(static_cast<DecimalFormat*>(NumberFormat::createInstance(locale, UNUM_DECIMAL, status)));
396    pattern = fmt->toPattern(pattern.remove());
397    setStringField(env, obj, "numberPattern", pattern);
398
399    fmt.reset(static_cast<DecimalFormat*>(NumberFormat::createInstance(locale, UNUM_PERCENT, status)));
400    pattern = fmt->toPattern(pattern.remove());
401    setStringField(env, obj, "percentPattern", pattern);
402}
403
404static void setDecimalFormatSymbolsData(JNIEnv* env, jobject obj, Locale& locale) {
405    UErrorCode status = U_ZERO_ERROR;
406    DecimalFormatSymbols dfs(locale, status);
407
408    setCharField(env, obj, "decimalSeparator", dfs.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol));
409    setCharField(env, obj, "groupingSeparator", dfs.getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol));
410    setCharField(env, obj, "patternSeparator", dfs.getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol));
411    setStringField(env, obj, "percent", dfs.getSymbol(DecimalFormatSymbols::kPercentSymbol));
412    setCharField(env, obj, "perMill", dfs.getSymbol(DecimalFormatSymbols::kPerMillSymbol));
413    setCharField(env, obj, "monetarySeparator", dfs.getSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol));
414    setStringField(env, obj, "minusSign", dfs.getSymbol(DecimalFormatSymbols:: kMinusSignSymbol));
415    setStringField(env, obj, "exponentSeparator", dfs.getSymbol(DecimalFormatSymbols::kExponentialSymbol));
416    setStringField(env, obj, "infinity", dfs.getSymbol(DecimalFormatSymbols::kInfinitySymbol));
417    setStringField(env, obj, "NaN", dfs.getSymbol(DecimalFormatSymbols::kNaNSymbol));
418    setCharField(env, obj, "zeroDigit", dfs.getSymbol(DecimalFormatSymbols::kZeroDigitSymbol));
419}
420
421
422// Iterates up through the locale hierarchy. So "en_US" would return "en_US", "en", "".
423class LocaleNameIterator {
424 public:
425  LocaleNameIterator(const char* locale_name, UErrorCode& status) : status_(status), has_next_(true) {
426    strcpy(locale_name_, locale_name);
427    locale_name_length_ = strlen(locale_name_);
428  }
429
430  const char* Get() {
431      return locale_name_;
432  }
433
434  bool HasNext() {
435    return has_next_;
436  }
437
438  void Up() {
439    if (locale_name_length_ == 0) {
440      has_next_ = false;
441    } else {
442      locale_name_length_ = uloc_getParent(locale_name_, locale_name_, sizeof(locale_name_), &status_);
443    }
444  }
445
446 private:
447  UErrorCode& status_;
448  bool has_next_;
449  char locale_name_[ULOC_FULLNAME_CAPACITY];
450  int32_t locale_name_length_;
451
452  DISALLOW_COPY_AND_ASSIGN(LocaleNameIterator);
453};
454
455static bool getAmPmMarkersNarrow(JNIEnv* env, jobject localeData, const char* locale_name) {
456  UErrorCode status = U_ZERO_ERROR;
457  ScopedResourceBundle root(ures_open(NULL, locale_name, &status));
458  if (U_FAILURE(status)) {
459    return false;
460  }
461  ScopedResourceBundle calendar(ures_getByKey(root.get(), "calendar", NULL, &status));
462  if (U_FAILURE(status)) {
463    return false;
464  }
465  ScopedResourceBundle gregorian(ures_getByKey(calendar.get(), "gregorian", NULL, &status));
466  if (U_FAILURE(status)) {
467    return false;
468  }
469  ScopedResourceBundle amPmMarkersNarrow(ures_getByKey(gregorian.get(), "AmPmMarkersNarrow", NULL, &status));
470  if (U_FAILURE(status)) {
471    return false;
472  }
473  setStringField(env, localeData, "narrowAm", amPmMarkersNarrow.get(), 0);
474  setStringField(env, localeData, "narrowPm", amPmMarkersNarrow.get(), 1);
475  return true;
476}
477
478static bool getDateTimePatterns(JNIEnv* env, jobject localeData, const char* locale_name) {
479  UErrorCode status = U_ZERO_ERROR;
480  ScopedResourceBundle root(ures_open(NULL, locale_name, &status));
481  if (U_FAILURE(status)) {
482    return false;
483  }
484  ScopedResourceBundle calendar(ures_getByKey(root.get(), "calendar", NULL, &status));
485  if (U_FAILURE(status)) {
486    return false;
487  }
488  ScopedResourceBundle gregorian(ures_getByKey(calendar.get(), "gregorian", NULL, &status));
489  if (U_FAILURE(status)) {
490    return false;
491  }
492  ScopedResourceBundle dateTimePatterns(ures_getByKey(gregorian.get(), "DateTimePatterns", NULL, &status));
493  if (U_FAILURE(status)) {
494    return false;
495  }
496  setStringField(env, localeData, "fullTimeFormat", dateTimePatterns.get(), 0);
497  setStringField(env, localeData, "longTimeFormat", dateTimePatterns.get(), 1);
498  setStringField(env, localeData, "mediumTimeFormat", dateTimePatterns.get(), 2);
499  setStringField(env, localeData, "shortTimeFormat", dateTimePatterns.get(), 3);
500  setStringField(env, localeData, "fullDateFormat", dateTimePatterns.get(), 4);
501  setStringField(env, localeData, "longDateFormat", dateTimePatterns.get(), 5);
502  setStringField(env, localeData, "mediumDateFormat", dateTimePatterns.get(), 6);
503  setStringField(env, localeData, "shortDateFormat", dateTimePatterns.get(), 7);
504  return true;
505}
506
507static bool getYesterdayTodayAndTomorrow(JNIEnv* env, jobject localeData, const Locale& locale, const char* locale_name) {
508  UErrorCode status = U_ZERO_ERROR;
509  ScopedResourceBundle root(ures_open(NULL, locale_name, &status));
510  ScopedResourceBundle fields(ures_getByKey(root.get(), "fields", NULL, &status));
511  ScopedResourceBundle day(ures_getByKey(fields.get(), "day", NULL, &status));
512  ScopedResourceBundle relative(ures_getByKey(day.get(), "relative", NULL, &status));
513  if (U_FAILURE(status)) {
514    return false;
515  }
516
517  UnicodeString yesterday(ures_getUnicodeStringByKey(relative.get(), "-1", &status));
518  UnicodeString today(ures_getUnicodeStringByKey(relative.get(), "0", &status));
519  UnicodeString tomorrow(ures_getUnicodeStringByKey(relative.get(), "1", &status));
520  if (U_FAILURE(status)) {
521    ALOGE("Error getting yesterday/today/tomorrow for %s: %s", locale_name, u_errorName(status));
522    return false;
523  }
524
525  // We title-case the strings so they have consistent capitalization (http://b/14493853).
526  UniquePtr<BreakIterator> brk(BreakIterator::createSentenceInstance(locale, status));
527  if (U_FAILURE(status)) {
528    ALOGE("Error getting yesterday/today/tomorrow break iterator for %s: %s", locale_name, u_errorName(status));
529    return false;
530  }
531  yesterday.toTitle(brk.get(), locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
532  today.toTitle(brk.get(), locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
533  tomorrow.toTitle(brk.get(), locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
534
535  setStringField(env, localeData, "yesterday", yesterday);
536  setStringField(env, localeData, "today", today);
537  setStringField(env, localeData, "tomorrow", tomorrow);
538  return true;
539}
540
541static jboolean ICU_initLocaleDataNative(JNIEnv* env, jclass, jstring javaLanguageTag, jobject localeData) {
542    ScopedUtfChars languageTag(env, javaLanguageTag);
543    if (languageTag.c_str() == NULL) {
544        return JNI_FALSE;
545    }
546    if (languageTag.size() >= ULOC_FULLNAME_CAPACITY) {
547        return JNI_FALSE; // ICU has a fixed-length limit.
548    }
549
550    ScopedIcuLocale icuLocale(env, javaLanguageTag);
551    if (!icuLocale.valid()) {
552      return JNI_FALSE;
553    }
554
555    // Get the DateTimePatterns.
556    UErrorCode status = U_ZERO_ERROR;
557    bool foundDateTimePatterns = false;
558    for (LocaleNameIterator it(icuLocale.locale().getBaseName(), status); it.HasNext(); it.Up()) {
559      if (getDateTimePatterns(env, localeData, it.Get())) {
560          foundDateTimePatterns = true;
561          break;
562      }
563    }
564    if (!foundDateTimePatterns) {
565        ALOGE("Couldn't find ICU DateTimePatterns for %s", languageTag.c_str());
566        return JNI_FALSE;
567    }
568
569    // Get the "Yesterday", "Today", and "Tomorrow" strings.
570    bool foundYesterdayTodayAndTomorrow = false;
571    for (LocaleNameIterator it(icuLocale.locale().getBaseName(), status); it.HasNext(); it.Up()) {
572      if (getYesterdayTodayAndTomorrow(env, localeData, icuLocale.locale(), it.Get())) {
573        foundYesterdayTodayAndTomorrow = true;
574        break;
575      }
576    }
577    if (!foundYesterdayTodayAndTomorrow) {
578      ALOGE("Couldn't find ICU yesterday/today/tomorrow for %s", languageTag.c_str());
579      return JNI_FALSE;
580    }
581
582    // Get the narrow "AM" and "PM" strings.
583    bool foundAmPmMarkersNarrow = false;
584    for (LocaleNameIterator it(icuLocale.locale().getBaseName(), status); it.HasNext(); it.Up()) {
585      if (getAmPmMarkersNarrow(env, localeData, it.Get())) {
586        foundAmPmMarkersNarrow = true;
587        break;
588      }
589    }
590    if (!foundAmPmMarkersNarrow) {
591      ALOGE("Couldn't find ICU AmPmMarkersNarrow for %s", languageTag.c_str());
592      return JNI_FALSE;
593    }
594
595    status = U_ZERO_ERROR;
596    UniquePtr<Calendar> cal(Calendar::createInstance(icuLocale.locale(), status));
597    if (U_FAILURE(status)) {
598        return JNI_FALSE;
599    }
600    if (!setIntegerField(env, localeData, "firstDayOfWeek", cal->getFirstDayOfWeek())) {
601      return JNI_FALSE;
602    }
603    if (!setIntegerField(env, localeData, "minimalDaysInFirstWeek", cal->getMinimalDaysInFirstWeek())) {
604      return JNI_FALSE;
605    }
606
607    // Get DateFormatSymbols.
608    status = U_ZERO_ERROR;
609    DateFormatSymbols dateFormatSym(icuLocale.locale(), status);
610    if (U_FAILURE(status)) {
611        return JNI_FALSE;
612    }
613
614    // Get AM/PM and BC/AD.
615    int32_t count = 0;
616    const UnicodeString* amPmStrs = dateFormatSym.getAmPmStrings(count);
617    setStringArrayField(env, localeData, "amPm", amPmStrs, count);
618    const UnicodeString* erasStrs = dateFormatSym.getEras(count);
619    setStringArrayField(env, localeData, "eras", erasStrs, count);
620
621    const UnicodeString* longMonthNames =
622       dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::WIDE);
623    setStringArrayField(env, localeData, "longMonthNames", longMonthNames, count);
624    const UnicodeString* shortMonthNames =
625        dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED);
626    setStringArrayField(env, localeData, "shortMonthNames", shortMonthNames, count);
627    const UnicodeString* tinyMonthNames =
628        dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::NARROW);
629    setStringArrayField(env, localeData, "tinyMonthNames", tinyMonthNames, count);
630    const UnicodeString* longWeekdayNames =
631        dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::WIDE);
632    setStringArrayField(env, localeData, "longWeekdayNames", longWeekdayNames, count);
633    const UnicodeString* shortWeekdayNames =
634        dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED);
635    setStringArrayField(env, localeData, "shortWeekdayNames", shortWeekdayNames, count);
636    const UnicodeString* tinyWeekdayNames =
637        dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::NARROW);
638    setStringArrayField(env, localeData, "tinyWeekdayNames", tinyWeekdayNames, count);
639
640    const UnicodeString* longStandAloneMonthNames =
641        dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE);
642    setStringArrayField(env, localeData, "longStandAloneMonthNames", longStandAloneMonthNames, count);
643    const UnicodeString* shortStandAloneMonthNames =
644        dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED);
645    setStringArrayField(env, localeData, "shortStandAloneMonthNames", shortStandAloneMonthNames, count);
646    const UnicodeString* tinyStandAloneMonthNames =
647        dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::NARROW);
648    setStringArrayField(env, localeData, "tinyStandAloneMonthNames", tinyStandAloneMonthNames, count);
649    const UnicodeString* longStandAloneWeekdayNames =
650        dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE);
651    setStringArrayField(env, localeData, "longStandAloneWeekdayNames", longStandAloneWeekdayNames, count);
652    const UnicodeString* shortStandAloneWeekdayNames =
653        dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED);
654    setStringArrayField(env, localeData, "shortStandAloneWeekdayNames", shortStandAloneWeekdayNames, count);
655    const UnicodeString* tinyStandAloneWeekdayNames =
656        dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::NARROW);
657    setStringArrayField(env, localeData, "tinyStandAloneWeekdayNames", tinyStandAloneWeekdayNames, count);
658
659    status = U_ZERO_ERROR;
660
661    // For numberPatterns and symbols.
662    setNumberPatterns(env, localeData, icuLocale.locale());
663    setDecimalFormatSymbolsData(env, localeData, icuLocale.locale());
664
665    jstring countryCode = env->NewStringUTF(icuLocale.locale().getCountry());
666    jstring internationalCurrencySymbol = ICU_getCurrencyCode(env, NULL, countryCode);
667    env->DeleteLocalRef(countryCode);
668    countryCode = NULL;
669
670    jstring currencySymbol = NULL;
671    if (internationalCurrencySymbol != NULL) {
672        currencySymbol = ICU_getCurrencySymbol(env, NULL, javaLanguageTag, internationalCurrencySymbol);
673    } else {
674        internationalCurrencySymbol = env->NewStringUTF("XXX");
675    }
676    if (currencySymbol == NULL) {
677        // This is the UTF-8 encoding of U+00A4 (CURRENCY SIGN).
678        currencySymbol = env->NewStringUTF("\xc2\xa4");
679    }
680    setStringField(env, localeData, "currencySymbol", currencySymbol);
681    setStringField(env, localeData, "internationalCurrencySymbol", internationalCurrencySymbol);
682
683    return JNI_TRUE;
684}
685
686static jstring ICU_toLowerCase(JNIEnv* env, jclass, jstring javaString, jstring javaLanguageTag) {
687  ScopedJavaUnicodeString scopedString(env, javaString);
688  if (!scopedString.valid()) {
689    return NULL;
690  }
691  ScopedIcuLocale icuLocale(env, javaLanguageTag);
692  if (!icuLocale.valid()) {
693    return NULL;
694  }
695  UnicodeString& s(scopedString.unicodeString());
696  UnicodeString original(s);
697  s.toLower(icuLocale.locale());
698  return s == original ? javaString : env->NewString(s.getBuffer(), s.length());
699}
700
701static jstring ICU_toUpperCase(JNIEnv* env, jclass, jstring javaString, jstring javaLanguageTag) {
702  ScopedJavaUnicodeString scopedString(env, javaString);
703  if (!scopedString.valid()) {
704    return NULL;
705  }
706  ScopedIcuLocale icuLocale(env, javaLanguageTag);
707  if (!icuLocale.valid()) {
708    return NULL;
709  }
710  UnicodeString& s(scopedString.unicodeString());
711  UnicodeString original(s);
712  s.toUpper(icuLocale.locale());
713  return s == original ? javaString : env->NewString(s.getBuffer(), s.length());
714}
715
716static jstring versionString(JNIEnv* env, const UVersionInfo& version) {
717    char versionString[U_MAX_VERSION_STRING_LENGTH];
718    u_versionToString(const_cast<UVersionInfo&>(version), &versionString[0]);
719    return env->NewStringUTF(versionString);
720}
721
722static jstring ICU_getCldrVersion(JNIEnv* env, jclass) {
723  UErrorCode status = U_ZERO_ERROR;
724  UVersionInfo cldrVersion;
725  ulocdata_getCLDRVersion(cldrVersion, &status);
726  return versionString(env, cldrVersion);
727}
728
729static jstring ICU_getIcuVersion(JNIEnv* env, jclass) {
730    UVersionInfo icuVersion;
731    u_getVersion(icuVersion);
732    return versionString(env, icuVersion);
733}
734
735static jstring ICU_getUnicodeVersion(JNIEnv* env, jclass) {
736    UVersionInfo unicodeVersion;
737    u_getUnicodeVersion(unicodeVersion);
738    return versionString(env, unicodeVersion);
739}
740
741static jobject ICU_getAvailableCurrencyCodes(JNIEnv* env, jclass) {
742  UErrorCode status = U_ZERO_ERROR;
743  UStringEnumeration e(ucurr_openISOCurrencies(UCURR_COMMON|UCURR_NON_DEPRECATED, &status));
744  return fromStringEnumeration(env, status, "ucurr_openISOCurrencies", &e);
745}
746
747static jstring ICU_getBestDateTimePatternNative(JNIEnv* env, jclass, jstring javaSkeleton, jstring javaLanguageTag) {
748  ScopedIcuLocale icuLocale(env, javaLanguageTag);
749  if (!icuLocale.valid()) {
750    return NULL;
751  }
752
753  UErrorCode status = U_ZERO_ERROR;
754  UniquePtr<DateTimePatternGenerator> generator(DateTimePatternGenerator::createInstance(icuLocale.locale(), status));
755  if (maybeThrowIcuException(env, "DateTimePatternGenerator::createInstance", status)) {
756    return NULL;
757  }
758
759  ScopedJavaUnicodeString skeletonHolder(env, javaSkeleton);
760  if (!skeletonHolder.valid()) {
761    return NULL;
762  }
763  UnicodeString result(generator->getBestPattern(skeletonHolder.unicodeString(), status));
764  if (maybeThrowIcuException(env, "DateTimePatternGenerator::getBestPattern", status)) {
765    return NULL;
766  }
767
768  return env->NewString(result.getBuffer(), result.length());
769}
770
771static void ICU_setDefaultLocale(JNIEnv* env, jclass, jstring javaLanguageTag) {
772  ScopedIcuLocale icuLocale(env, javaLanguageTag);
773  if (!icuLocale.valid()) {
774    return;
775  }
776
777  UErrorCode status = U_ZERO_ERROR;
778  Locale::setDefault(icuLocale.locale(), status);
779  maybeThrowIcuException(env, "Locale::setDefault", status);
780}
781
782static jstring ICU_getDefaultLocale(JNIEnv* env, jclass) {
783  return env->NewStringUTF(Locale::getDefault().getName());
784}
785
786static JNINativeMethod gMethods[] = {
787    NATIVE_METHOD(ICU, addLikelySubtags, "(Ljava/lang/String;)Ljava/lang/String;"),
788    NATIVE_METHOD(ICU, getAvailableBreakIteratorLocalesNative, "()[Ljava/lang/String;"),
789    NATIVE_METHOD(ICU, getAvailableCalendarLocalesNative, "()[Ljava/lang/String;"),
790    NATIVE_METHOD(ICU, getAvailableCollatorLocalesNative, "()[Ljava/lang/String;"),
791    NATIVE_METHOD(ICU, getAvailableCurrencyCodes, "()[Ljava/lang/String;"),
792    NATIVE_METHOD(ICU, getAvailableDateFormatLocalesNative, "()[Ljava/lang/String;"),
793    NATIVE_METHOD(ICU, getAvailableLocalesNative, "()[Ljava/lang/String;"),
794    NATIVE_METHOD(ICU, getAvailableNumberFormatLocalesNative, "()[Ljava/lang/String;"),
795    NATIVE_METHOD(ICU, getBestDateTimePatternNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
796    NATIVE_METHOD(ICU, getCldrVersion, "()Ljava/lang/String;"),
797    NATIVE_METHOD(ICU, getCurrencyCode, "(Ljava/lang/String;)Ljava/lang/String;"),
798    NATIVE_METHOD(ICU, getCurrencyDisplayName, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
799    NATIVE_METHOD(ICU, getCurrencyFractionDigits, "(Ljava/lang/String;)I"),
800    NATIVE_METHOD(ICU, getCurrencyNumericCode, "(Ljava/lang/String;)I"),
801    NATIVE_METHOD(ICU, getCurrencySymbol, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
802    NATIVE_METHOD(ICU, getDefaultLocale, "()Ljava/lang/String;"),
803    NATIVE_METHOD(ICU, getDisplayCountryNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
804    NATIVE_METHOD(ICU, getDisplayLanguageNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
805    NATIVE_METHOD(ICU, getDisplayScriptNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
806    NATIVE_METHOD(ICU, getDisplayVariantNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
807    NATIVE_METHOD(ICU, getISO3Country, "(Ljava/lang/String;)Ljava/lang/String;"),
808    NATIVE_METHOD(ICU, getISO3Language, "(Ljava/lang/String;)Ljava/lang/String;"),
809    NATIVE_METHOD(ICU, getISOCountriesNative, "()[Ljava/lang/String;"),
810    NATIVE_METHOD(ICU, getISOLanguagesNative, "()[Ljava/lang/String;"),
811    NATIVE_METHOD(ICU, getIcuVersion, "()Ljava/lang/String;"),
812    NATIVE_METHOD(ICU, getScript, "(Ljava/lang/String;)Ljava/lang/String;"),
813    NATIVE_METHOD(ICU, getUnicodeVersion, "()Ljava/lang/String;"),
814    NATIVE_METHOD(ICU, initLocaleDataNative, "(Ljava/lang/String;Llibcore/icu/LocaleData;)Z"),
815    NATIVE_METHOD(ICU, setDefaultLocale, "(Ljava/lang/String;)V"),
816    NATIVE_METHOD(ICU, toLowerCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
817    NATIVE_METHOD(ICU, toUpperCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
818};
819
820#define FAIL_WITH_STRERROR(s) \
821    ALOGE("Couldn't " s " '%s': %s", path.c_str(), strerror(errno)); \
822    return FALSE;
823
824#define MAYBE_FAIL_WITH_ICU_ERROR(s) \
825    if (status != U_ZERO_ERROR) {\
826        ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path.c_str()); \
827        return FALSE; \
828    }
829
830static bool mapIcuData(const std::string& path) {
831    // Open the file and get its length.
832    ScopedFd fd(open(path.c_str(), O_RDONLY));
833    if (fd.get() == -1) {
834        FAIL_WITH_STRERROR("open");
835    }
836    struct stat sb;
837    if (fstat(fd.get(), &sb) == -1) {
838        FAIL_WITH_STRERROR("stat");
839    }
840
841    // Map it.
842    void* data = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd.get(), 0);
843    if (data == MAP_FAILED) {
844        FAIL_WITH_STRERROR("mmap");
845    }
846
847    // Tell the kernel that accesses are likely to be random rather than sequential.
848    if (madvise(data, sb.st_size, MADV_RANDOM) == -1) {
849        FAIL_WITH_STRERROR("madvise(MADV_RANDOM)");
850    }
851
852    UErrorCode status = U_ZERO_ERROR;
853
854    // Tell ICU to use our memory-mapped data.
855    udata_setCommonData(data, &status);
856    MAYBE_FAIL_WITH_ICU_ERROR("udata_setCommonData");
857
858    return TRUE;
859}
860
861void register_libcore_icu_ICU(JNIEnv* env) {
862    // Check the timezone override file exists. If it does, map it first so we use it in preference
863    // to the one that shipped with the device.
864    const char* dataPathPrefix = getenv("ANDROID_DATA");
865    if (dataPathPrefix == NULL) {
866        ALOGE("ANDROID_DATA environment variable not set"); \
867        abort();
868    }
869
870    UErrorCode status = U_ZERO_ERROR;
871    // Tell ICU it can *only* use our memory-mapped data.
872    udata_setFileAccess(UDATA_NO_FILES, &status);
873    if (status != U_ZERO_ERROR) {
874        ALOGE("Couldn't initialize ICU (s_setFileAccess): %s", u_errorName(status));
875        abort();
876    }
877
878    // Map in optional TZ data files.
879    std::string dataPath;
880    dataPath = dataPathPrefix;
881    dataPath += "/misc/zoneinfo/current/icu/icu_tzdata.dat";
882
883    struct stat sb;
884    if (stat(dataPath.c_str(), &sb) == 0) {
885        ALOGD("Timezone override file found: %s", dataPath.c_str());
886        if (!mapIcuData(dataPath)) {
887            ALOGW("TZ override file %s exists but could not be loaded. Skipping.", dataPath.c_str());
888        }
889    } else {
890        ALOGD("No timezone override file found: %s", dataPath.c_str());
891    }
892
893    // Use the ICU data files that shipped with the device for everything else.
894    std::string systemPath;
895    systemPath = u_getDataDirectory();
896    systemPath += "/";
897    systemPath += U_ICUDATA_NAME;
898    systemPath += ".dat";
899
900    if (!mapIcuData(systemPath)) {
901        abort();
902    }
903
904    // Failures to find the ICU data tend to be somewhat obscure because ICU loads its data on first
905    // use, which can be anywhere. Force initialization up front so we can report a nice clear error
906    // and bail.
907    u_init(&status);
908    if (status != U_ZERO_ERROR) {\
909        ALOGE("Couldn't initialize ICU (u_init): %s", u_errorName(status));
910        abort();
911    }
912
913    jniRegisterNativeMethods(env, "libcore/icu/ICU", gMethods, NELEM(gMethods));
914}
915