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