diff options
Diffstat (limited to 'luni/src/main/native/libcore_icu_ICU.cpp')
-rw-r--r-- | luni/src/main/native/libcore_icu_ICU.cpp | 261 |
1 files changed, 188 insertions, 73 deletions
diff --git a/luni/src/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp index 5a07694..5224420 100644 --- a/luni/src/main/native/libcore_icu_ICU.cpp +++ b/luni/src/main/native/libcore_icu_ICU.cpp @@ -59,27 +59,37 @@ #include <time.h> #include <unistd.h> +// TODO: put this in a header file and use it everywhere! +// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. +// It goes in the private: declarations in a class. +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + class ScopedResourceBundle { -public: - ScopedResourceBundle(UResourceBundle* bundle) : mBundle(bundle) { - } + public: + ScopedResourceBundle(UResourceBundle* bundle) : bundle_(bundle) { + } - ~ScopedResourceBundle() { - if (mBundle != NULL) { - ures_close(mBundle); - } + ~ScopedResourceBundle() { + if (bundle_ != NULL) { + ures_close(bundle_); } + } - UResourceBundle* get() { - return mBundle; - } + UResourceBundle* get() { + return bundle_; + } -private: - UResourceBundle* mBundle; + bool hasKey(const char* key) { + UErrorCode status = U_ZERO_ERROR; + ures_getStringByKey(bundle_, key, NULL, &status); + return U_SUCCESS(status); + } - // Disallow copy and assignment. - ScopedResourceBundle(const ScopedResourceBundle&); - void operator=(const ScopedResourceBundle&); + private: + UResourceBundle* bundle_; + DISALLOW_COPY_AND_ASSIGN(ScopedResourceBundle); }; Locale getLocale(JNIEnv* env, jstring localeName) { @@ -302,14 +312,25 @@ static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, } static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, UResourceBundle* bundle, int index) { - UErrorCode status = U_ZERO_ERROR; - int charCount; - const UChar* chars = ures_getStringByIndex(bundle, index, &charCount, &status); - if (U_SUCCESS(status)) { - setStringField(env, obj, fieldName, env->NewString(chars, charCount)); - } else { - ALOGE("Error setting String field %s from ICU resource: %s", fieldName, u_errorName(status)); - } + UErrorCode status = U_ZERO_ERROR; + int charCount; + const UChar* chars = ures_getStringByIndex(bundle, index, &charCount, &status); + if (U_SUCCESS(status)) { + setStringField(env, obj, fieldName, env->NewString(chars, charCount)); + } else { + ALOGE("Error setting String field %s from ICU resource (index %d): %s", fieldName, index, u_errorName(status)); + } +} + +static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, UResourceBundle* bundle, const char* key) { + UErrorCode status = U_ZERO_ERROR; + int charCount; + const UChar* chars = ures_getStringByKey(bundle, key, &charCount, &status); + if (U_SUCCESS(status)) { + setStringField(env, obj, fieldName, env->NewString(chars, charCount)); + } else { + ALOGE("Error setting String field %s from ICU resource (key %s): %s", fieldName, key, u_errorName(status)); + } } static void setCharField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString& value) { @@ -361,80 +382,159 @@ static void setDecimalFormatSymbolsData(JNIEnv* env, jobject obj, jstring locale setCharField(env, obj, "zeroDigit", dfs.getSymbol(DecimalFormatSymbols::kZeroDigitSymbol)); } + +// Iterates up through the locale hierarchy. So "en_US" would return "en_US", "en", "". +class LocaleNameIterator { + public: + LocaleNameIterator(const char* locale_name, UErrorCode& status) : status_(status), has_next_(true) { + strcpy(locale_name_, locale_name); + locale_name_length_ = strlen(locale_name_); + } + + const char* Get() { + return locale_name_; + } + + bool HasNext() { + return has_next_; + } + + void Up() { + locale_name_length_ = uloc_getParent(locale_name_, locale_name_, sizeof(locale_name_), &status_); + if (locale_name_length_ == 0) { + has_next_ = false; + } + } + + private: + UErrorCode& status_; + bool has_next_; + char locale_name_[ULOC_FULLNAME_CAPACITY]; + int32_t locale_name_length_; + + DISALLOW_COPY_AND_ASSIGN(LocaleNameIterator); +}; + +static bool getDateTimePatterns(JNIEnv* env, jobject localeData, const char* locale_name) { + UErrorCode status = U_ZERO_ERROR; + ScopedResourceBundle root(ures_open(NULL, locale_name, &status)); + if (U_FAILURE(status)) { + return false; + } + ScopedResourceBundle calendar(ures_getByKey(root.get(), "calendar", NULL, &status)); + if (U_FAILURE(status)) { + return false; + } + ScopedResourceBundle gregorian(ures_getByKey(calendar.get(), "gregorian", NULL, &status)); + if (U_FAILURE(status)) { + return false; + } + ScopedResourceBundle dateTimePatterns(ures_getByKey(gregorian.get(), "DateTimePatterns", NULL, &status)); + if (U_FAILURE(status)) { + return false; + } + setStringField(env, localeData, "fullTimeFormat", dateTimePatterns.get(), 0); + setStringField(env, localeData, "longTimeFormat", dateTimePatterns.get(), 1); + setStringField(env, localeData, "mediumTimeFormat", dateTimePatterns.get(), 2); + setStringField(env, localeData, "shortTimeFormat", dateTimePatterns.get(), 3); + setStringField(env, localeData, "fullDateFormat", dateTimePatterns.get(), 4); + setStringField(env, localeData, "longDateFormat", dateTimePatterns.get(), 5); + setStringField(env, localeData, "mediumDateFormat", dateTimePatterns.get(), 6); + setStringField(env, localeData, "shortDateFormat", dateTimePatterns.get(), 7); + return true; +} + +static bool getYesterdayTodayAndTomorrow(JNIEnv* env, jobject localeData, const char* locale_name) { + UErrorCode status = U_ZERO_ERROR; + ScopedResourceBundle root(ures_open(NULL, locale_name, &status)); + if (U_FAILURE(status)) { + return false; + } + ScopedResourceBundle calendar(ures_getByKey(root.get(), "calendar", NULL, &status)); + if (U_FAILURE(status)) { + return false; + } + ScopedResourceBundle gregorian(ures_getByKey(calendar.get(), "gregorian", NULL, &status)); + if (U_FAILURE(status)) { + return false; + } + ScopedResourceBundle fields(ures_getByKey(gregorian.get(), "fields", NULL, &status)); + if (U_FAILURE(status)) { + return false; + } + ScopedResourceBundle day(ures_getByKey(fields.get(), "day", NULL, &status)); + if (U_FAILURE(status)) { + return false; + } + ScopedResourceBundle relative(ures_getByKey(day.get(), "relative", NULL, &status)); + if (U_FAILURE(status)) { + return false; + } + // bn_BD only has a "-2" entry. + if (relative.hasKey("-1") && relative.hasKey("0") && relative.hasKey("1")) { + setStringField(env, localeData, "yesterday", relative.get(), "-1"); + setStringField(env, localeData, "today", relative.get(), "0"); + setStringField(env, localeData, "tomorrow", relative.get(), "1"); + return true; + } + return false; +} + static jboolean ICU_initLocaleDataImpl(JNIEnv* env, jclass, jstring locale, jobject localeData) { ScopedUtfChars localeName(env, locale); if (localeName.c_str() == NULL) { return JNI_FALSE; } - - // Get DateTimePatterns - UErrorCode status; - char currentLocale[ULOC_FULLNAME_CAPACITY]; - int32_t localeNameLen = 0; if (localeName.size() >= ULOC_FULLNAME_CAPACITY) { - return JNI_FALSE; // Exceed ICU defined limit of the whole locale ID. - } - strcpy(currentLocale, localeName.c_str()); - do { - status = U_ZERO_ERROR; - ScopedResourceBundle root(ures_open(NULL, currentLocale, &status)); - if (U_FAILURE(status)) { - if (localeNameLen == 0) { - break; // No parent locale, report this error outside the loop. - } else { - status = U_ZERO_ERROR; - continue; // get parent locale. - } - } - ScopedResourceBundle calendar(ures_getByKey(root.get(), "calendar", NULL, &status)); - if (U_FAILURE(status)) { - status = U_ZERO_ERROR; - continue; // get parent locale. - } + return JNI_FALSE; // ICU has a fixed-length limit. + } - ScopedResourceBundle gregorian(ures_getByKey(calendar.get(), "gregorian", NULL, &status)); - if (U_FAILURE(status)) { - status = U_ZERO_ERROR; - continue; // get parent locale. - } - ScopedResourceBundle dateTimePatterns(ures_getByKey(gregorian.get(), "DateTimePatterns", NULL, &status)); - if (U_SUCCESS(status)) { - setStringField(env, localeData, "fullTimeFormat", dateTimePatterns.get(), 0); - setStringField(env, localeData, "longTimeFormat", dateTimePatterns.get(), 1); - setStringField(env, localeData, "mediumTimeFormat", dateTimePatterns.get(), 2); - setStringField(env, localeData, "shortTimeFormat", dateTimePatterns.get(), 3); - setStringField(env, localeData, "fullDateFormat", dateTimePatterns.get(), 4); - setStringField(env, localeData, "longDateFormat", dateTimePatterns.get(), 5); - setStringField(env, localeData, "mediumDateFormat", dateTimePatterns.get(), 6); - setStringField(env, localeData, "shortDateFormat", dateTimePatterns.get(), 7); - break; - } else { - status = U_ZERO_ERROR; // get parent locale. - } - } while((localeNameLen = uloc_getParent(currentLocale, currentLocale, sizeof(currentLocale), &status)) >= 0); - if (U_FAILURE(status)) { - ALOGE("Error getting ICU resource bundle: %s", u_errorName(status)); + // Get the DateTimePatterns. + UErrorCode status = U_ZERO_ERROR; + bool foundDateTimePatterns = false; + for (LocaleNameIterator it(localeName.c_str(), status); it.HasNext(); it.Up()) { + if (getDateTimePatterns(env, localeData, it.Get())) { + foundDateTimePatterns = true; + break; + } + } + if (!foundDateTimePatterns) { + ALOGE("Couldn't find ICU DateTimePatterns for %s", localeName.c_str()); return JNI_FALSE; } + // Get the "Yesterday", "Today", and "Tomorrow" strings. + bool foundYesterdayTodayAndTomorrow = false; + for (LocaleNameIterator it(localeName.c_str(), status); it.HasNext(); it.Up()) { + if (getYesterdayTodayAndTomorrow(env, localeData, it.Get())) { + foundYesterdayTodayAndTomorrow = true; + break; + } + } + if (!foundYesterdayTodayAndTomorrow) { + ALOGE("Couldn't find ICU yesterday/today/tomorrow for %s", localeName.c_str()); + return JNI_FALSE; + } + status = U_ZERO_ERROR; Locale localeObj = getLocale(env, locale); - UniquePtr<Calendar> cal(Calendar::createInstance(localeObj, status)); if (U_FAILURE(status)) { return JNI_FALSE; } + setIntegerField(env, localeData, "firstDayOfWeek", cal->getFirstDayOfWeek()); setIntegerField(env, localeData, "minimalDaysInFirstWeek", cal->getMinimalDaysInFirstWeek()); - // Get DateFormatSymbols + // Get DateFormatSymbols. status = U_ZERO_ERROR; DateFormatSymbols dateFormatSym(localeObj, status); if (U_FAILURE(status)) { return JNI_FALSE; } + + // Get AM/PM and BC/AD. int32_t count = 0; - // Get AM/PM marker const UnicodeString* amPmStrs = dateFormatSym.getAmPmStrings(count); setStringArrayField(env, localeData, "amPm", amPmStrs, count); const UnicodeString* erasStrs = dateFormatSym.getEras(count); @@ -446,12 +546,18 @@ static jboolean ICU_initLocaleDataImpl(JNIEnv* env, jclass, jstring locale, jobj const UnicodeString* shortMonthNames = dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED); setStringArrayField(env, localeData, "shortMonthNames", shortMonthNames, count); + const UnicodeString* tinyMonthNames = + dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::NARROW); + setStringArrayField(env, localeData, "tinyMonthNames", tinyMonthNames, count); const UnicodeString* longWeekdayNames = dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::WIDE); setStringArrayField(env, localeData, "longWeekdayNames", longWeekdayNames, count); const UnicodeString* shortWeekdayNames = dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED); setStringArrayField(env, localeData, "shortWeekdayNames", shortWeekdayNames, count); + const UnicodeString* tinyWeekdayNames = + dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::NARROW); + setStringArrayField(env, localeData, "tinyWeekdayNames", tinyWeekdayNames, count); const UnicodeString* longStandAloneMonthNames = dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE); @@ -459,12 +565,18 @@ static jboolean ICU_initLocaleDataImpl(JNIEnv* env, jclass, jstring locale, jobj const UnicodeString* shortStandAloneMonthNames = dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED); setStringArrayField(env, localeData, "shortStandAloneMonthNames", shortStandAloneMonthNames, count); + const UnicodeString* tinyStandAloneMonthNames = + dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::NARROW); + setStringArrayField(env, localeData, "tinyStandAloneMonthNames", tinyStandAloneMonthNames, count); const UnicodeString* longStandAloneWeekdayNames = dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE); setStringArrayField(env, localeData, "longStandAloneWeekdayNames", longStandAloneWeekdayNames, count); const UnicodeString* shortStandAloneWeekdayNames = dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED); setStringArrayField(env, localeData, "shortStandAloneWeekdayNames", shortStandAloneWeekdayNames, count); + const UnicodeString* tinyStandAloneWeekdayNames = + dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::NARROW); + setStringArrayField(env, localeData, "tinyStandAloneWeekdayNames", tinyStandAloneWeekdayNames, count); status = U_ZERO_ERROR; @@ -543,9 +655,12 @@ static jobject ICU_getAvailableCurrencyCodes(JNIEnv* env, jclass) { UErrorCode status = U_ZERO_ERROR; UEnumeration* e(ucurr_openISOCurrencies(UCURR_COMMON|UCURR_NON_DEPRECATED, &status)); EnumerationCounter counter(uenum_count(e, &status)); + if (maybeThrowIcuException(env, "uenum_count", status)) { + return NULL; + } EnumerationGetter getter(e, &status); jobject result = toStringArray16(env, &counter, &getter); - maybeThrowIcuException(env, status); + maybeThrowIcuException(env, "uenum_unext", status); uenum_close(e); return result; } |