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