/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "JNIHelp.h" #include "AndroidSystemNatives.h" #include "unicode/numfmt.h" #include "unicode/locid.h" #include "unicode/ucal.h" #include "unicode/gregocal.h" #include "unicode/ucurr.h" #include "unicode/calendar.h" #include "unicode/datefmt.h" #include "unicode/dtfmtsym.h" #include "unicode/decimfmt.h" #include "unicode/dcfmtsym.h" #include "unicode/uclean.h" #include "unicode/smpdtfmt.h" #include "unicode/strenum.h" #include "unicode/ustring.h" #include "unicode/timezone.h" #include "ErrorCode.h" #include <cutils/log.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <sys/time.h> jclass string_class; static UBool icuError(JNIEnv *env, UErrorCode errorcode) { const char *emsg = u_errorName(errorcode); jclass exception; if (U_FAILURE(errorcode)) { switch (errorcode) { case U_ILLEGAL_ARGUMENT_ERROR : exception = env->FindClass("java/lang/IllegalArgumentException"); break; case U_INDEX_OUTOFBOUNDS_ERROR : case U_BUFFER_OVERFLOW_ERROR : exception = env->FindClass("java/lang/ArrayIndexOutOfBoundsException"); break; case U_UNSUPPORTED_ERROR : exception = env->FindClass("java/lang/UnsupportedOperationException"); break; default : exception = env->FindClass("java/lang/RuntimeException"); } return (env->ThrowNew(exception, emsg) != 0); } return 0; } static Locale getLocale(JNIEnv *env, jstring locale) { const char *name = env->GetStringUTFChars(locale, NULL); Locale result = Locale::createFromName(name); env->ReleaseStringUTFChars(locale, name); return result; } static jstring getJStringFromUnicodeString(JNIEnv *env, UnicodeString string) { UErrorCode status = U_ZERO_ERROR; int stringLength = string.length(); jchar *res = (jchar *) malloc(sizeof(jchar) * (stringLength + 1)); string.extract(res, stringLength+1, status); if(U_FAILURE(status)) { free(res); LOGI("Error getting string for getJStringFromUnicodeString"); status = U_ZERO_ERROR; return NULL; } jstring result = env->NewString(res, stringLength); free(res); return result; } static void addObject(JNIEnv *env, jobjectArray result, const char *keyStr, jobject elem, int index) { jclass objArray_class = env->FindClass("java/lang/Object"); jobjectArray element = env->NewObjectArray(2, objArray_class, NULL); jstring key = env->NewStringUTF(keyStr); env->SetObjectArrayElement(element, 0, key); env->SetObjectArrayElement(element, 1, elem); env->SetObjectArrayElement(result, index, element); env->DeleteLocalRef(key); env->DeleteLocalRef(element); } static jint getFractionDigitsNative(JNIEnv* env, jclass clazz, jstring currencyCode) { // LOGI("ENTER getFractionDigitsNative"); UErrorCode status = U_ZERO_ERROR; NumberFormat *fmt = NumberFormat::createCurrencyInstance(status); if(U_FAILURE(status)) { return -1; } const jchar *cCode = env->GetStringChars(currencyCode, NULL); fmt->setCurrency(cCode, status); env->ReleaseStringChars(currencyCode, cCode); if(U_FAILURE(status)) { return -1; } // for CurrencyFormats the minimum and maximum fraction digits are the same. int result = fmt->getMinimumFractionDigits(); delete(fmt); return result; } static jstring getCurrencyCodeNative(JNIEnv* env, jclass clazz, jstring key) { // LOGI("ENTER getCurrencyCodeNative"); UErrorCode status = U_ZERO_ERROR; UResourceBundle *supplData = ures_openDirect(NULL, "supplementalData", &status); if(U_FAILURE(status)) { return NULL; } UResourceBundle *currencyMap = ures_getByKey(supplData, "CurrencyMap", NULL, &status); if(U_FAILURE(status)) { ures_close(supplData); return NULL; } const char *keyChars = env->GetStringUTFChars(key, NULL); UResourceBundle *currency = ures_getByKey(currencyMap, keyChars, NULL, &status); env->ReleaseStringUTFChars(key, keyChars); if(U_FAILURE(status)) { ures_close(currencyMap); ures_close(supplData); return NULL; } UResourceBundle *currencyElem = ures_getByIndex(currency, 0, NULL, &status); if(U_FAILURE(status)) { ures_close(currency); ures_close(currencyMap); ures_close(supplData); return env->NewStringUTF("None"); } // check if there is a to date. If there is, the currency isn't used anymore. UResourceBundle *currencyTo = ures_getByKey(currencyElem, "to", NULL, &status); if(!U_FAILURE(status)) { // return and let the ResourceBundle throw an exception ures_close(currencyElem); ures_close(currency); ures_close(currencyMap); ures_close(supplData); return NULL; } status = U_ZERO_ERROR; ures_close(currencyTo); UResourceBundle *currencyId = ures_getByKey(currencyElem, "id", NULL, &status); if(U_FAILURE(status)) { // No id defined for this country ures_close(currencyElem); ures_close(currency); ures_close(currencyMap); ures_close(supplData); return env->NewStringUTF("None"); } int length; const jchar *id = ures_getString(currencyId, &length, &status); if(U_FAILURE(status)) { ures_close(currencyId); ures_close(currencyElem); ures_close(currency); ures_close(currencyMap); ures_close(supplData); return env->NewStringUTF("None"); } ures_close(currencyId); ures_close(currencyElem); ures_close(currency); ures_close(currencyMap); ures_close(supplData); if(length == 0) { return env->NewStringUTF("None"); } return env->NewString(id, length); } static jstring getCurrencySymbolNative(JNIEnv* env, jclass clazz, jstring locale, jstring currencyCode) { // LOGI("ENTER getCurrencySymbolNative"); UErrorCode status = U_ZERO_ERROR; const char *locName = env->GetStringUTFChars(locale, NULL); UResourceBundle *root = ures_open(NULL, locName, &status); env->ReleaseStringUTFChars(locale, locName); if(U_FAILURE(status)) { return NULL; } UResourceBundle *rootElems = ures_getByKey(root, "Currencies", NULL, &status); if(U_FAILURE(status)) { ures_close(root); return NULL; } const char *currName = env->GetStringUTFChars(currencyCode, NULL); UResourceBundle *currencyElems = ures_getByKey(rootElems, currName, NULL, &status); env->ReleaseStringUTFChars(currencyCode, currName); if(U_FAILURE(status)) { ures_close(rootElems); ures_close(root); return NULL; } int currSymbL; const jchar *currSymbU = ures_getStringByIndex(currencyElems, 0, &currSymbL, &status); if(U_FAILURE(status)) { ures_close(currencyElems); ures_close(rootElems); ures_close(root); return NULL; } ures_close(currencyElems); ures_close(rootElems); ures_close(root); if(currSymbL == 0) { return NULL; } return env->NewString(currSymbU, currSymbL); } static jstring getDisplayCountryNative(JNIEnv* env, jclass clazz, jstring targetLocale, jstring locale) { // LOGI("ENTER getDisplayCountryNative"); UErrorCode status = U_ZERO_ERROR; Locale loc = getLocale(env, locale); Locale targetLoc = getLocale(env, targetLocale); UnicodeString string; targetLoc.getDisplayCountry(loc, string); jstring result = getJStringFromUnicodeString(env, string); return result; } static jstring getDisplayLanguageNative(JNIEnv* env, jclass clazz, jstring targetLocale, jstring locale) { // LOGI("ENTER getDisplayLanguageNative"); Locale loc = getLocale(env, locale); Locale targetLoc = getLocale(env, targetLocale); UnicodeString string; targetLoc.getDisplayLanguage(loc, string); jstring result = getJStringFromUnicodeString(env, string); return result; } static jstring getDisplayVariantNative(JNIEnv* env, jclass clazz, jstring targetLocale, jstring locale) { // LOGI("ENTER getDisplayVariantNative"); Locale loc = getLocale(env, locale); Locale targetLoc = getLocale(env, targetLocale); UnicodeString string; targetLoc.getDisplayVariant(loc, string); jstring result = getJStringFromUnicodeString(env, string); return result; } static jstring getISO3CountryNative(JNIEnv* env, jclass clazz, jstring locale) { // LOGI("ENTER getISO3CountryNative"); Locale loc = getLocale(env, locale); const char *string = loc.getISO3Country(); jstring result = env->NewStringUTF(string); return result; } static jstring getISO3LanguageNative(JNIEnv* env, jclass clazz, jstring locale) { // LOGI("ENTER getISO3LanguageNative"); Locale loc = getLocale(env, locale); const char *string = loc.getISO3Language(); jstring result = env->NewStringUTF(string); return result; } static jobjectArray getISOCountriesNative(JNIEnv* env, jclass clazz) { // LOGI("ENTER getISOCountriesNative"); const char* const* strings = Locale::getISOCountries(); int count = 0; while(strings[count] != NULL) { count++; } jobjectArray result = env->NewObjectArray(count, string_class, NULL); jstring res; for(int i = 0; i < count; i++) { res = env->NewStringUTF(strings[i]); env->SetObjectArrayElement(result, i, res); env->DeleteLocalRef(res); } return result; } static jobjectArray getISOLanguagesNative(JNIEnv* env, jclass clazz) { // LOGI("ENTER getISOLanguagesNative"); const char* const* strings = Locale::getISOLanguages(); const char *string = strings[0]; int count = 0; while(strings[count] != NULL) { count++; } jobjectArray result = env->NewObjectArray(count, string_class, NULL); jstring res; for(int i = 0; i < count; i++) { res = env->NewStringUTF(strings[i]); env->SetObjectArrayElement(result, i, res); env->DeleteLocalRef(res); } return result; } static jobjectArray getAvailableLocalesNative(JNIEnv* env, jclass clazz) { // LOGI("ENTER getAvailableLocalesNative"); int count = uloc_countAvailable(); jobjectArray result = env->NewObjectArray(count, string_class, NULL); jstring res; const char * string; for(int i = 0; i < count; i++) { string = uloc_getAvailable(i); res = env->NewStringUTF(string); env->SetObjectArrayElement(result, i, res); env->DeleteLocalRef(res); } return result; } static void getTimeZonesNative(JNIEnv* env, jclass clazz, jobjectArray outerArray, jstring locale) { // LOGI("ENTER getTimeZonesNative"); UErrorCode status = U_ZERO_ERROR; jobjectArray zoneIdArray; jobjectArray longStdTimeArray; jobjectArray shortStdTimeArray; jobjectArray longDlTimeArray; jobjectArray shortDlTimeArray; jstring content; jstring strObj; const jchar *res; UnicodeString resU; jint length; const UnicodeString *zoneID; DateFormat *df; UnicodeString longPattern("zzzz",""); UnicodeString shortPattern("z",""); Locale loc = getLocale(env, locale); SimpleDateFormat longFormat(longPattern, loc, status); SimpleDateFormat shortFormat(shortPattern, loc, status); zoneIdArray = (jobjectArray) env->GetObjectArrayElement(outerArray, 0); longStdTimeArray = (jobjectArray) env->GetObjectArrayElement(outerArray, 1); shortStdTimeArray = (jobjectArray) env->GetObjectArrayElement(outerArray, 2); longDlTimeArray = (jobjectArray) env->GetObjectArrayElement(outerArray, 3); shortDlTimeArray = (jobjectArray) env->GetObjectArrayElement(outerArray, 4); int count = env->GetArrayLength(zoneIdArray); TimeZone* zones[count]; // get all timezone objects for(int i = 0; i < count; i++) { strObj = (jstring) env->GetObjectArrayElement(zoneIdArray, i); length = env->GetStringLength(strObj); res = env->GetStringChars(strObj, NULL); const UnicodeString zoneID((UChar *)res, length); env->ReleaseStringChars(strObj, res); zones[i] = TimeZone::createTimeZone(zoneID); env->DeleteLocalRef(strObj); } // 15th January 2008 UDate date1 = 1203105600000.0; // 15th July 2008 UDate date2 = 1218826800000.0; for (int i = 0; i < count; i++) { TimeZone *tz = zones[i]; longFormat.setTimeZone(*tz); shortFormat.setTimeZone(*tz); int32_t daylightOffset; int32_t rawOffset; UDate standardDate; UDate daylightSavingDate; tz->getOffset(date1, false, rawOffset, daylightOffset, status); if (daylightOffset != 0) { // The Timezone is reporting that we are in daylight time // for the winter date. The dates are for the wrong hemisphere, // swap them. standardDate = date2; daylightSavingDate = date1; } else { standardDate = date1; daylightSavingDate = date2; } UnicodeString shortDayLight; UnicodeString longDayLight; UnicodeString shortStandard; UnicodeString longStandard; shortFormat.format(daylightSavingDate, shortDayLight); content = getJStringFromUnicodeString(env, shortDayLight); env->SetObjectArrayElement(shortDlTimeArray, i, content); env->DeleteLocalRef(content); shortFormat.format(standardDate, shortStandard); content = getJStringFromUnicodeString(env, shortStandard); env->SetObjectArrayElement(shortStdTimeArray, i, content); env->DeleteLocalRef(content); longFormat.format (daylightSavingDate, longDayLight); content = getJStringFromUnicodeString(env, longDayLight); env->SetObjectArrayElement(longDlTimeArray, i, content); env->DeleteLocalRef(content); longFormat.format (standardDate, longStandard); content = getJStringFromUnicodeString(env, longStandard); env->SetObjectArrayElement(longStdTimeArray, i, content); env->DeleteLocalRef(content); delete(tz); } } static jstring getDisplayTimeZoneNative(JNIEnv* env, jclass clazz, jstring zoneID, jboolean isDST, jint style, jstring localeID) { // Build TimeZone object const jchar* idChars = env->GetStringChars(zoneID, NULL); jint idLength = env->GetStringLength(zoneID); UnicodeString idString((UChar*)idChars, idLength); TimeZone* zone = TimeZone::createTimeZone(idString); env->ReleaseStringChars(zoneID, idChars); // Build Locale object (can we rely on zero termination of JNI result?) const char* localeChars = env->GetStringUTFChars(localeID, NULL); jint localeLength = env->GetStringLength(localeID); Locale locale = Locale::createFromName(localeChars); // Try to get the display name of the TimeZone according to the Locale UnicodeString buffer; zone->getDisplayName((UBool)isDST, (style == 0 ? TimeZone::SHORT : TimeZone::LONG), locale, buffer); const UChar* tempChars = buffer.getBuffer(); int tempLength = buffer.length(); jstring result = env->NewString((jchar*)tempChars, tempLength); env->ReleaseStringUTFChars(localeID, localeChars); // Clean up everything delete(zone); return result; } static void getDayInitVector(JNIEnv *env, UResourceBundle *gregorian, int *values) { UErrorCode status = U_ZERO_ERROR; // get the First day of week and the minimal days in first week numbers UResourceBundle *gregorianElems = ures_getByKey(gregorian, "DateTimeElements", NULL, &status); if(U_FAILURE(status)) { return; } int intVectSize; const int *result; result = ures_getIntVector(gregorianElems, &intVectSize, &status); if(U_FAILURE(status)) { ures_close(gregorianElems); return; } if(intVectSize == 2) { values[0] = result[0]; values[1] = result[1]; } ures_close(gregorianElems); } static jobjectArray getAmPmMarkers(JNIEnv *env, UResourceBundle *gregorian) { jobjectArray amPmMarkers; jstring pmU, amU; UErrorCode status = U_ZERO_ERROR; UResourceBundle *gregorianElems; gregorianElems = ures_getByKey(gregorian, "AmPmMarkers", NULL, &status); if(U_FAILURE(status)) { return NULL; } int lengthAm, lengthPm; ures_resetIterator(gregorianElems); const jchar* am = ures_getStringByIndex(gregorianElems, 0, &lengthAm, &status); const jchar* pm = ures_getStringByIndex(gregorianElems, 1, &lengthPm, &status); if(U_FAILURE(status)) { ures_close(gregorianElems); return NULL; } amPmMarkers = env->NewObjectArray(2, string_class, NULL); amU = env->NewString(am, lengthAm); env->SetObjectArrayElement(amPmMarkers, 0, amU); env->DeleteLocalRef(amU); pmU = env->NewString(pm, lengthPm); env->SetObjectArrayElement(amPmMarkers, 1, pmU); env->DeleteLocalRef(pmU); ures_close(gregorianElems); return amPmMarkers; } static jobjectArray getEras(JNIEnv* env, UResourceBundle *gregorian) { jobjectArray eras; jstring eraU; const jchar* era; UErrorCode status = U_ZERO_ERROR; UResourceBundle *gregorianElems; UResourceBundle *eraElems; gregorianElems = ures_getByKey(gregorian, "eras", NULL, &status); if(U_FAILURE(status)) { return NULL; } eraElems = ures_getByKey(gregorianElems, "abbreviated", NULL, &status); if(U_FAILURE(status)) { ures_close(gregorianElems); return NULL; } int eraLength; int eraCount = ures_getSize(eraElems); eras = env->NewObjectArray(eraCount, string_class, NULL); ures_resetIterator(eraElems); for(int i = 0; i < eraCount; i++) { era = ures_getStringByIndex(eraElems, i, &eraLength, &status); if(U_FAILURE(status)) { ures_close(gregorianElems); ures_close(eraElems); return NULL; } eraU = env->NewString(era, eraLength); env->SetObjectArrayElement(eras, i, eraU); env->DeleteLocalRef(eraU); } ures_close(eraElems); ures_close(gregorianElems); return eras; } static jobjectArray getMonthNames(JNIEnv *env, UResourceBundle *gregorian) { UErrorCode status = U_ZERO_ERROR; const jchar* month; jstring monthU; UResourceBundle *gregorianElems = ures_getByKey(gregorian, "monthNames", NULL, &status); if(U_FAILURE(status)) { return NULL; } UResourceBundle *monthNameElems = ures_getByKey(gregorianElems, "format", NULL, &status); if(U_FAILURE(status)) { ures_close(gregorianElems); return NULL; } UResourceBundle *monthNameElemsFormat = ures_getByKey(monthNameElems, "wide", NULL, &status); if(U_FAILURE(status)) { ures_close(monthNameElems); ures_close(gregorianElems); return NULL; } int monthNameLength; ures_resetIterator(monthNameElemsFormat); int monthCount = ures_getSize(monthNameElemsFormat); jobjectArray months = env->NewObjectArray(monthCount + 1, string_class, NULL); for(int i = 0; i < monthCount; i++) { month = ures_getStringByIndex(monthNameElemsFormat, i, &monthNameLength, &status); if(U_FAILURE(status)) { ures_close(monthNameElemsFormat); ures_close(monthNameElems); ures_close(gregorianElems); return NULL; } monthU = env->NewString(month, monthNameLength); env->SetObjectArrayElement(months, i, monthU); env->DeleteLocalRef(monthU); } monthU = env->NewStringUTF(""); env->SetObjectArrayElement(months, monthCount, monthU); env->DeleteLocalRef(monthU); ures_close(monthNameElemsFormat); ures_close(monthNameElems); ures_close(gregorianElems); return months; } static jobjectArray getShortMonthNames(JNIEnv *env, UResourceBundle *gregorian) { UErrorCode status = U_ZERO_ERROR; const jchar* shortMonth; jstring shortMonthU; UResourceBundle *gregorianElems = ures_getByKey(gregorian, "monthNames", NULL, &status); if(U_FAILURE(status)) { return NULL; } UResourceBundle *monthNameElems = ures_getByKey(gregorianElems, "format", NULL, &status); if(U_FAILURE(status)) { ures_close(gregorianElems); return NULL; } UResourceBundle *monthNameElemsFormat = ures_getByKey(monthNameElems, "abbreviated", NULL, &status); if(U_FAILURE(status)) { ures_close(monthNameElems); ures_close(gregorianElems); return NULL; } int shortMonthNameLength; ures_resetIterator(monthNameElemsFormat); int shortMonthCount = ures_getSize(monthNameElemsFormat); // the array length is +1 because the harmony locales had an empty string at the end of their month name array jobjectArray shortMonths = env->NewObjectArray(shortMonthCount + 1, string_class, NULL); for(int i = 0; i < shortMonthCount; i++) { shortMonth = ures_getStringByIndex(monthNameElemsFormat, i, &shortMonthNameLength, &status); if(U_FAILURE(status)) { ures_close(monthNameElemsFormat); ures_close(monthNameElems); ures_close(gregorianElems); return NULL; } shortMonthU = env->NewString(shortMonth, shortMonthNameLength); env->SetObjectArrayElement(shortMonths, i, shortMonthU); env->DeleteLocalRef(shortMonthU); } shortMonthU = env->NewStringUTF(""); env->SetObjectArrayElement(shortMonths, shortMonthCount, shortMonthU); env->DeleteLocalRef(shortMonthU); ures_close(monthNameElemsFormat); ures_close(monthNameElems); ures_close(gregorianElems); return shortMonths; } static jobjectArray getWeekdayNames(JNIEnv *env, UResourceBundle *gregorian) { UErrorCode status = U_ZERO_ERROR; const jchar* day; jstring dayU; UResourceBundle *gregorianElems = ures_getByKey(gregorian, "dayNames", NULL, &status); if(U_FAILURE(status)) { return NULL; } UResourceBundle *dayNameElems = ures_getByKey(gregorianElems, "format", NULL, &status); if(U_FAILURE(status)) { ures_close(gregorianElems); return NULL; } UResourceBundle *dayNameElemsFormat = ures_getByKey(dayNameElems, "wide", NULL, &status); if(U_FAILURE(status)) { ures_close(dayNameElems); ures_close(gregorianElems); return NULL; } int dayNameLength; ures_resetIterator(dayNameElemsFormat); int dayCount = ures_getSize(dayNameElemsFormat); jobjectArray weekdays = env->NewObjectArray(dayCount + 1, string_class, NULL); // first entry in the weekdays array is an empty string env->SetObjectArrayElement(weekdays, 0, env->NewStringUTF("")); for(int i = 0; i < dayCount; i++) { day = ures_getStringByIndex(dayNameElemsFormat, i, &dayNameLength, &status); if(U_FAILURE(status)) { ures_close(dayNameElemsFormat); ures_close(dayNameElems); ures_close(gregorianElems); return NULL; } dayU = env->NewString(day, dayNameLength); env->SetObjectArrayElement(weekdays, i + 1, dayU); env->DeleteLocalRef(dayU); } ures_close(dayNameElemsFormat); ures_close(dayNameElems); ures_close(gregorianElems); return weekdays; } static jobjectArray getShortWeekdayNames(JNIEnv *env, UResourceBundle *gregorian) { UErrorCode status = U_ZERO_ERROR; const jchar* shortDay; jstring shortDayU; UResourceBundle *gregorianElems = ures_getByKey(gregorian, "dayNames", NULL, &status); if(U_FAILURE(status)) { return NULL; } UResourceBundle *dayNameElems = ures_getByKey(gregorianElems, "format", NULL, &status); if(U_FAILURE(status)) { ures_close(gregorianElems); return NULL; } UResourceBundle *dayNameElemsFormat = ures_getByKey(dayNameElems, "abbreviated", NULL, &status); if(U_FAILURE(status)) { ures_close(dayNameElems); ures_close(gregorianElems); return NULL; } int shortDayNameLength; ures_resetIterator(dayNameElemsFormat); int shortDayCount = ures_getSize(dayNameElemsFormat); jobjectArray shortWeekdays = env->NewObjectArray(shortDayCount + 1, string_class, NULL); env->SetObjectArrayElement(shortWeekdays, 0, env->NewStringUTF("")); for(int i = 0; i < shortDayCount; i++) { shortDay = ures_getStringByIndex(dayNameElemsFormat, i, &shortDayNameLength, &status); if(U_FAILURE(status)) { ures_close(dayNameElemsFormat); ures_close(dayNameElems); ures_close(gregorianElems); return NULL; } shortDayU = env->NewString(shortDay, shortDayNameLength); env->SetObjectArrayElement(shortWeekdays, i + 1, shortDayU); env->DeleteLocalRef(shortDayU); } ures_close(dayNameElemsFormat); ures_close(dayNameElems); ures_close(gregorianElems); return shortWeekdays; } static jstring getDecimalPatternChars(JNIEnv *env, UResourceBundle *rootElems) { UErrorCode status = U_ZERO_ERROR; int zeroL, digitL, decSepL, groupL, listL, percentL, permillL, expL, currSepL, minusL; int patternLength; jchar *patternChars; const jchar* zero = ures_getStringByIndex(rootElems, 4, &zeroL, &status); const jchar* digit = ures_getStringByIndex(rootElems, 5, &digitL, &status); const jchar* decSep = ures_getStringByIndex(rootElems, 0, &decSepL, &status); const jchar* group = ures_getStringByIndex(rootElems, 1, &groupL, &status); const jchar* list = ures_getStringByIndex(rootElems, 2, &listL, &status); const jchar* percent = ures_getStringByIndex(rootElems, 3, &percentL, &status); const jchar* permill = ures_getStringByIndex(rootElems, 8, &permillL, &status); const jchar* exp = ures_getStringByIndex(rootElems, 7, &expL, &status); const jchar* currSep = ures_getStringByIndex(rootElems, 0, &currSepL, &status); const jchar* minus = ures_getStringByIndex(rootElems, 6, &minusL, &status); if(U_FAILURE(status)) { return NULL; } patternChars = (jchar *) malloc(11 * sizeof(jchar)); patternChars[0] = 0; u_strncat(patternChars, zero, 1); u_strncat(patternChars, digit, 1); u_strncat(patternChars, decSep, 1); u_strncat(patternChars, group, 1); u_strncat(patternChars, list, 1); u_strncat(patternChars, percent, 1); u_strncat(patternChars, permill, 1); u_strncat(patternChars, exp, 1); u_strncat(patternChars, currSep, 1); u_strncat(patternChars, minus, 1); jstring decimalPatternChars = env->NewString(patternChars, 10); free(patternChars); return decimalPatternChars; } static jstring getIntCurrencyCode(JNIEnv *env, jclass clazz, jstring locale) { const char *locStr = env->GetStringUTFChars(locale, NULL); char country[3] = {0,0,0}; // getting the 2 character country name if(strlen(locStr) < 5) { env->ReleaseStringUTFChars(locale, locStr); return NULL; } if(locStr[3] < 'A' || locStr[3] > 'Z' || locStr[4] < 'A' || locStr[4] > 'Z') { env->ReleaseStringUTFChars(locale, locStr); return NULL; } country[0] = locStr[3]; country[1] = locStr[4]; env->ReleaseStringUTFChars(locale, locStr); return getCurrencyCodeNative(env, clazz, env->NewStringUTF(country)); } static jstring getCurrencySymbol(JNIEnv *env, jclass clazz, jstring locale, jstring intCurrencySymbol) { jstring result = getCurrencySymbolNative(env, clazz, locale, intCurrencySymbol); if(result == intCurrencySymbol) { return NULL; } return result; } static jobjectArray getContentImpl(JNIEnv* env, jclass clazz, jstring locale, jboolean needsTZ) { UErrorCode status = U_ZERO_ERROR; const char *loc = env->GetStringUTFChars(locale, NULL); UResourceBundle *root = ures_openU(NULL, loc, &status); env->ReleaseStringUTFChars(locale, loc); if(U_FAILURE(status)) { LOGI("Error getting resources"); status = U_ZERO_ERROR; return NULL; } jclass obj_class = env->FindClass("[Ljava/lang/Object;"); jclass integer_class = env->FindClass("java/lang/Integer"); jmethodID integerInit = env->GetMethodID(integer_class, "<init>", "(I)V"); jobjectArray result; jobject firstDayOfWeek = NULL; jobject minimalDaysInFirstWeek = NULL; jobjectArray amPmMarkers = NULL; jobjectArray eras = NULL; jstring localPatternChars = NULL; jobjectArray weekdays = NULL; jobjectArray shortWeekdays = NULL; jobjectArray months = NULL; jobjectArray shortMonths = NULL; jstring time_SHORT = NULL; jstring time_MEDIUM = NULL; jstring time_LONG = NULL; jstring time_FULL = NULL; jstring date_SHORT = NULL; jstring date_MEDIUM = NULL; jstring date_LONG = NULL; jstring date_FULL = NULL; jstring decimalPatternChars = NULL; jstring naN = NULL; jstring infinity = NULL; jstring currencySymbol = NULL; jstring intCurrencySymbol = NULL; jstring numberPattern = NULL; jstring integerPattern = NULL; jstring currencyPattern = NULL; jstring percentPattern = NULL; jobjectArray zones = NULL; int counter = 0; int firstDayVals[2] = {-1, -1}; const jchar* nan = (const jchar *)NULL; const jchar* inf = (const jchar *)NULL; int nanL, infL; UResourceBundle *gregorian; UResourceBundle *gregorianElems; UResourceBundle *rootElems; // get the resources needed rootElems = ures_getByKey(root, "calendar", NULL, &status); if(U_FAILURE(status)) { return NULL; } gregorian = ures_getByKey(rootElems, "gregorian", NULL, &status); if(U_FAILURE(status)) { ures_close(rootElems); return NULL; } // adding the first day of week and minimal days in first week values getDayInitVector(env, gregorian, firstDayVals); if((firstDayVals[0] != -1) && (firstDayVals[1] != -1)) { firstDayOfWeek = env->NewObject(integer_class, integerInit, firstDayVals[0]); minimalDaysInFirstWeek = env->NewObject(integer_class, integerInit, firstDayVals[1]); // adding First_Day and Minimal_Days integer to the result counter += 2; } // adding ampm string array to the result"); amPmMarkers = getAmPmMarkers(env, gregorian); if(amPmMarkers != NULL) { counter++; } // adding eras string array to the result eras = getEras(env, gregorian); if(eras != NULL) { counter++; } // local pattern chars are initially always the same localPatternChars = env->NewStringUTF("GyMdkHmsSEDFwWahKzZ"); // adding local pattern chars string to the result counter++; // adding month names string array to the result months = getMonthNames(env, gregorian); if(months != NULL) { counter++; } // adding short month names string array to the result shortMonths = getShortMonthNames(env, gregorian); if(shortMonths != NULL) { counter++; } // adding day names string array to the result weekdays = getWeekdayNames(env, gregorian); if(weekdays != NULL) { counter++; } // adding short day names string array to the result shortWeekdays = getShortWeekdayNames(env, gregorian); if(shortWeekdays != NULL) { counter++; } const UChar *pattern; jchar check[2] = {0, 0}; u_uastrcpy(check, "v"); jchar replacement[2] = {0, 0}; u_uastrcpy(replacement, "z"); jchar *pos; jchar *patternCopy; int patternLength; // adding date and time format patterns to the result gregorianElems = ures_getByKey(gregorian, "DateTimePatterns", NULL, &status); if(U_FAILURE(status)) { status = U_ZERO_ERROR; goto endOfCalendar; } pattern = ures_getStringByIndex(gregorianElems, 0, &patternLength, &status); // there are some patterns in icu that use the pattern character 'v' // java doesn't accept this, so it gets replaced by 'z' which has // about the same result as 'v', the timezone name. // 'v' -> "PT", 'z' -> "PST", v is the generic timezone and z the standard tz // "vvvv" -> "Pacific Time", "zzzz" -> "Pacific Standard Time" patternCopy = (jchar *) malloc((patternLength + 1) * sizeof(jchar)); u_strcpy(patternCopy, pattern); if(U_FAILURE(status)) { free(patternCopy); status = U_ZERO_ERROR; goto endOfCalendar; } while((pos = u_strchr(patternCopy, check[0])) != NULL) { u_memset(pos, replacement[0], 1); } time_FULL = env->NewString(patternCopy, patternLength); free(patternCopy); counter++; pattern = ures_getStringByIndex(gregorianElems, 1, &patternLength, &status); if(U_FAILURE(status)) { status = U_ZERO_ERROR; goto endOfCalendar; } time_LONG = env->NewString(pattern, patternLength); counter++; pattern = ures_getStringByIndex(gregorianElems, 2, &patternLength, &status); if(U_FAILURE(status)) { status = U_ZERO_ERROR; goto endOfCalendar; } time_MEDIUM = env->NewString(pattern, patternLength); counter++; pattern = ures_getStringByIndex(gregorianElems, 3, &patternLength, &status); if(U_FAILURE(status)) { status = U_ZERO_ERROR; goto endOfCalendar; } time_SHORT = env->NewString(pattern, patternLength); counter++; pattern = ures_getStringByIndex(gregorianElems, 4, &patternLength, &status); if(U_FAILURE(status)) { status = U_ZERO_ERROR; goto endOfCalendar; } date_FULL = env->NewString(pattern, patternLength); counter++; pattern = ures_getStringByIndex(gregorianElems, 5, &patternLength, &status); if(U_FAILURE(status)) { status = U_ZERO_ERROR; goto endOfCalendar; } date_LONG = env->NewString(pattern, patternLength); counter++; pattern = ures_getStringByIndex(gregorianElems, 6, &patternLength, &status); if(U_FAILURE(status)) { status = U_ZERO_ERROR; goto endOfCalendar; } date_MEDIUM = env->NewString(pattern, patternLength); counter++; pattern = ures_getStringByIndex(gregorianElems, 7, &patternLength, &status); if(U_FAILURE(status)) { status = U_ZERO_ERROR; goto endOfCalendar; } date_SHORT = env->NewString(pattern, patternLength); counter++; endOfCalendar: if(gregorianElems != NULL) { ures_close(gregorianElems); } ures_close(gregorian); ures_close(rootElems); rootElems = ures_getByKey(root, "NumberElements", NULL, &status); if(U_FAILURE(status)) { status = U_ZERO_ERROR; } if(ures_getSize(rootElems) >= 11) { // adding decimal pattern chars to the result decimalPatternChars = getDecimalPatternChars(env, rootElems); if(decimalPatternChars != NULL) { counter++; } // adding NaN pattern char to the result nan = ures_getStringByIndex(rootElems, 10, &nanL, &status); if(U_SUCCESS(status)) { naN = env->NewString(nan, nanL); counter++; } status = U_ZERO_ERROR; // adding infinity pattern char to the result inf = ures_getStringByIndex(rootElems, 9, &infL, &status); if(U_SUCCESS(status)) { infinity = env->NewString(inf, infL); counter++; } status = U_ZERO_ERROR; } ures_close(rootElems); // adding intl currency code to result intCurrencySymbol = getIntCurrencyCode(env, clazz, locale); if(intCurrencySymbol != NULL) { // adding currency symbol to result currencySymbol = getCurrencySymbol(env, clazz, locale, intCurrencySymbol); } else { intCurrencySymbol = env->NewStringUTF("XXX"); } if(currencySymbol == NULL) { // creating a new string explicitly with the UTF-8 encoding of "\u00a4" currencySymbol = env->NewStringUTF("\xc2\xa4"); } counter += 2; // adding number format patterns to the result int numOfEntries; int decSepOffset; NumberFormat *nf; jchar *tmpPattern; rootElems = ures_getByKey(root, "NumberPatterns", NULL, &status); if(U_FAILURE(status)) { status = U_ZERO_ERROR; goto zones; } numOfEntries = ures_getSize(rootElems); if(numOfEntries < 3) { ures_close(rootElems); goto zones; } // number pattern pattern = ures_getStringByIndex(rootElems, 0, &patternLength, &status); if(U_FAILURE(status)) { status = U_ZERO_ERROR; ures_close(rootElems); goto zones; } numberPattern = env->NewString(pattern, patternLength); counter++; // integer pattern derived from number pattern // We need to convert a C string literal to a UChar string for u_strcspn. static const char c_decSep[] = "."; UChar decSep[sizeof(c_decSep)]; u_charsToUChars(c_decSep, decSep, sizeof(c_decSep)); decSepOffset = u_strcspn(pattern, decSep); tmpPattern = (jchar *) malloc((decSepOffset + 1) * sizeof(jchar)); u_strncpy(tmpPattern, pattern, decSepOffset); integerPattern = env->NewString(tmpPattern, decSepOffset); free(tmpPattern); counter++; // currency pattern pattern = ures_getStringByIndex(rootElems, 1, &patternLength, &status); if(U_FAILURE(status)) { status = U_ZERO_ERROR; ures_close(rootElems); goto zones; } currencyPattern = env->NewString(pattern, patternLength); counter++; // percent pattern pattern = ures_getStringByIndex(rootElems, 2, &patternLength, &status); if(U_FAILURE(status)) { status = U_ZERO_ERROR; ures_close(rootElems); goto zones; } percentPattern = env->NewString(pattern, patternLength); counter++; ures_close(rootElems); zones: ures_close(root); if(needsTZ == JNI_TRUE) { counter++; //add empty timezone } // collect all content and put it into an array result = env->NewObjectArray(counter, obj_class, NULL); int index = 0; if(needsTZ == JNI_TRUE) { addObject(env, result, "timezones", NULL, index++); } if(firstDayOfWeek != NULL && index < counter) { addObject(env, result, "First_Day", firstDayOfWeek, index++); } if(minimalDaysInFirstWeek != NULL && index < counter) { addObject(env, result, "Minimal_Days", minimalDaysInFirstWeek, index++); } if(amPmMarkers != NULL && index < counter) { addObject(env, result, "ampm", amPmMarkers, index++); } if(eras != NULL && index < counter) { addObject(env, result, "eras", eras, index++); } if(localPatternChars != NULL && index < counter) { addObject(env, result, "LocalPatternChars", localPatternChars, index++); } if(weekdays != NULL && index < counter) { addObject(env, result, "weekdays", weekdays, index++); } if(shortWeekdays != NULL && index < counter) { addObject(env, result, "shortWeekdays", shortWeekdays, index++); } if(months != NULL && index < counter) { addObject(env, result, "months", months, index++); } if(shortMonths != NULL && index < counter) { addObject(env, result, "shortMonths", shortMonths, index++); } if(time_SHORT != NULL && index < counter) { addObject(env, result, "Time_SHORT", time_SHORT, index++); } if(time_MEDIUM != NULL && index < counter) { addObject(env, result, "Time_MEDIUM", time_MEDIUM, index++); } if(time_LONG != NULL && index < counter) { addObject(env, result, "Time_LONG", time_LONG, index++); } if(time_FULL != NULL && index < counter) { addObject(env, result, "Time_FULL", time_FULL, index++); } if(date_SHORT != NULL && index < counter) { addObject(env, result, "Date_SHORT", date_SHORT, index++); } if(date_MEDIUM != NULL && index < counter) { addObject(env, result, "Date_MEDIUM", date_MEDIUM, index++); } if(date_LONG != NULL && index < counter) { addObject(env, result, "Date_LONG", date_LONG, index++); } if(date_FULL != NULL && index < counter) { addObject(env, result, "Date_FULL", date_FULL, index++); } if(decimalPatternChars != NULL && index < counter) { addObject(env, result, "DecimalPatternChars", decimalPatternChars, index++); } if(naN != NULL && index < counter) { addObject(env, result, "NaN", naN, index++); } if(infinity != NULL && index < counter) { addObject(env, result, "Infinity", infinity, index++); } if(currencySymbol != NULL && index < counter) { addObject(env, result, "CurrencySymbol", currencySymbol, index++); } if(intCurrencySymbol != NULL && index < counter) { addObject(env, result, "IntCurrencySymbol", intCurrencySymbol, index++); } if(numberPattern != NULL && index < counter) { addObject(env, result, "Number", numberPattern, index++); } if(integerPattern != NULL && index < counter) { addObject(env, result, "Integer", integerPattern, index++); } if(currencyPattern != NULL && index < counter) { addObject(env, result, "Currency", currencyPattern, index++); } if(percentPattern != NULL && index < counter) { addObject(env, result, "Percent", percentPattern, index++); } return result; } static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ {"getFractionDigitsNative", "(Ljava/lang/String;)I", (void*) getFractionDigitsNative}, {"getCurrencyCodeNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*) getCurrencyCodeNative}, {"getCurrencySymbolNative", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", (void*) getCurrencySymbolNative}, {"getDisplayCountryNative", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", (void*) getDisplayCountryNative}, {"getDisplayLanguageNative", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", (void*) getDisplayLanguageNative}, {"getDisplayVariantNative", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", (void*) getDisplayVariantNative}, {"getISO3CountryNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*) getISO3CountryNative}, {"getISO3LanguageNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*) getISO3LanguageNative}, {"getISOCountriesNative", "()[Ljava/lang/String;", (void*) getISOCountriesNative}, {"getISOLanguagesNative", "()[Ljava/lang/String;", (void*) getISOLanguagesNative}, {"getAvailableLocalesNative", "()[Ljava/lang/String;", (void*) getAvailableLocalesNative}, {"getTimeZonesNative", "([[Ljava/lang/String;Ljava/lang/String;)V", (void*) getTimeZonesNative}, {"getDisplayTimeZoneNative", "(Ljava/lang/String;ZILjava/lang/String;)Ljava/lang/String;", (void*) getDisplayTimeZoneNative}, {"getContentImpl", "(Ljava/lang/String;Z)[[Ljava/lang/Object;", (void*) getContentImpl}, }; int register_com_ibm_icu4jni_util_Resources(JNIEnv* env) { // initializing String jclass stringclass = env->FindClass("java/lang/String"); if(stringclass == NULL) { LOGE("Can't find java/lang/String"); jniThrowException(env, "java/lang/ClassNotFoundException", "java.lang.String"); return -1; } string_class = (jclass) env->NewGlobalRef(stringclass); return jniRegisterNativeMethods(env, "com/ibm/icu4jni/util/Resources", gMethods, NELEM(gMethods)); }