/* * 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/rbnf.h" #include "unicode/fmtable.h" #include "unicode/ustring.h" #include "unicode/locid.h" #include "ErrorCode.h" #include <stdlib.h> #include <string.h> static UBool icuError(JNIEnv *env, UErrorCode errorcode) { const char *emsg = u_errorName(errorcode); jclass exception; if (errorcode > U_ZERO_ERROR && errorcode < U_ERROR_LIMIT) { 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 jint openRBNFImpl1(JNIEnv* env, jclass clazz, jint type, jstring locale) { // LOGI("ENTER openRBNFImpl1"); // the errorcode returned by unum_open UErrorCode status = U_ZERO_ERROR; // prepare the locale string for the call to unum_open const char *localeChars = env->GetStringUTFChars(locale, NULL); URBNFRuleSetTag style; if(type == 0) { style = URBNF_SPELLOUT; } else if(type == 1) { style = URBNF_ORDINAL; } else if(type == 2) { style = URBNF_DURATION; } else if(type == 3) { style = URBNF_COUNT; } else { icuError(env, U_ILLEGAL_ARGUMENT_ERROR); } Locale loc = Locale::createFromName(localeChars); // open a default type number format RuleBasedNumberFormat *fmt = new RuleBasedNumberFormat(style, loc, status); // release the allocated strings env->ReleaseStringUTFChars(locale, localeChars); // check for an error if ( icuError(env, status) != FALSE) { return 0; } // return the handle to the number format return (long) fmt; } static jint openRBNFImpl2(JNIEnv* env, jclass clazz, jstring rule, jstring locale) { // LOGI("ENTER openRBNFImpl2"); // the errorcode returned by unum_open UErrorCode status = U_ZERO_ERROR; // prepare the pattern string for the call to unum_open const UChar *ruleChars = env->GetStringChars(rule, NULL); int ruleLen = env->GetStringLength(rule); // prepare the locale string for the call to unum_open const char *localeChars = env->GetStringUTFChars(locale, NULL); // open a rule based number format UNumberFormat *fmt = unum_open(UNUM_PATTERN_RULEBASED, ruleChars, ruleLen, localeChars, NULL, &status); // release the allocated strings env->ReleaseStringChars(rule, ruleChars); env->ReleaseStringUTFChars(locale, localeChars); // check for an error if ( icuError(env, status) != FALSE) { return 0; } // return the handle to the number format return (long) fmt; } static void closeRBNFImpl(JNIEnv *env, jclass clazz, jint addr) { // LOGI("ENTER closeRBNFImpl"); // get the pointer to the number format RuleBasedNumberFormat *fmt = (RuleBasedNumberFormat *)(int)addr; // close this number format delete fmt; } static jstring formatLongRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jlong value, jobject field, jstring fieldType, jobject attributes) { // LOGI("ENTER formatLongRBNFImpl"); const char * fieldPositionClassName = "java/text/FieldPosition"; const char * stringBufferClassName = "java/lang/StringBuffer"; jclass fieldPositionClass = env->FindClass(fieldPositionClassName); jclass stringBufferClass = env->FindClass(stringBufferClassName); jmethodID setBeginIndexMethodID = env->GetMethodID(fieldPositionClass, "setBeginIndex", "(I)V"); jmethodID setEndIndexMethodID = env->GetMethodID(fieldPositionClass, "setEndIndex", "(I)V"); jmethodID appendMethodID = env->GetMethodID(stringBufferClass, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;"); const char * fieldName = NULL; if(fieldType != NULL) { fieldName = env->GetStringUTFChars(fieldType, NULL); } uint32_t reslenneeded; int64_t val = value; UChar *result = NULL; FieldPosition fp; fp.setField(FieldPosition::DONT_CARE); UErrorCode status = U_ZERO_ERROR; RuleBasedNumberFormat *fmt = (RuleBasedNumberFormat *)(int)addr; UnicodeString res; fmt->format(val, res, fp); reslenneeded = res.extract(NULL, 0, status); if(status==U_BUFFER_OVERFLOW_ERROR) { status=U_ZERO_ERROR; result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1)); res.extract(result, reslenneeded + 1, status); } if (icuError(env, status) != FALSE) { free(result); return NULL; } if(fieldType != NULL) { env->ReleaseStringUTFChars(fieldType, fieldName); } jstring resulting = env->NewString(result, reslenneeded); free(result); return resulting; } static jstring formatDoubleRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jdouble value, jobject field, jstring fieldType, jobject attributes) { // LOGI("ENTER formatDoubleRBNFImpl"); const char * fieldPositionClassName = "java/text/FieldPosition"; const char * stringBufferClassName = "java/lang/StringBuffer"; jclass fieldPositionClass = env->FindClass(fieldPositionClassName); jclass stringBufferClass = env->FindClass(stringBufferClassName); jmethodID setBeginIndexMethodID = env->GetMethodID(fieldPositionClass, "setBeginIndex", "(I)V"); jmethodID setEndIndexMethodID = env->GetMethodID(fieldPositionClass, "setEndIndex", "(I)V"); jmethodID appendMethodID = env->GetMethodID(stringBufferClass, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;"); const char * fieldName = NULL; if(fieldType != NULL) { fieldName = env->GetStringUTFChars(fieldType, NULL); } uint32_t reslenneeded; double val = value; UChar *result = NULL; FieldPosition fp; fp.setField(FieldPosition::DONT_CARE); UErrorCode status = U_ZERO_ERROR; RuleBasedNumberFormat *fmt = (RuleBasedNumberFormat *)(int)addr; UnicodeString res; fmt->format(val, res, fp); reslenneeded = res.extract(NULL, 0, status); if(status==U_BUFFER_OVERFLOW_ERROR) { status=U_ZERO_ERROR; result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1)); res.extract(result, reslenneeded + 1, status); } if (icuError(env, status) != FALSE) { free(result); return NULL; } if(fieldType != NULL) { env->ReleaseStringUTFChars(fieldType, fieldName); } jstring resulting = env->NewString(result, reslenneeded); free(result); return resulting; } static jobject parseRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jstring text, jobject position, jboolean lenient) { // LOGI("ENTER parseRBNFImpl"); const char * parsePositionClassName = "java/text/ParsePosition"; const char * longClassName = "java/lang/Long"; const char * doubleClassName = "java/lang/Double"; UErrorCode status = U_ZERO_ERROR; UNumberFormat *fmt = (UNumberFormat *)(int)addr; jchar *str = (UChar *)env->GetStringChars(text, NULL); int strlength = env->GetStringLength(text); jclass parsePositionClass = env->FindClass(parsePositionClassName); jclass longClass = env->FindClass(longClassName); jclass doubleClass = env->FindClass(doubleClassName); jmethodID getIndexMethodID = env->GetMethodID(parsePositionClass, "getIndex", "()I"); jmethodID setIndexMethodID = env->GetMethodID(parsePositionClass, "setIndex", "(I)V"); jmethodID setErrorIndexMethodID = env->GetMethodID(parsePositionClass, "setErrorIndex", "(I)V"); jmethodID longInitMethodID = env->GetMethodID(longClass, "<init>", "(J)V"); jmethodID dblInitMethodID = env->GetMethodID(doubleClass, "<init>", "(D)V"); int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL); // make sure the ParsePosition is valid. Actually icu4c would parse a number // correctly even if the parsePosition is set to -1, but since the RI fails // for that case we have to fail too if(parsePos < 0 || parsePos > strlength) { return NULL; } Formattable res; const UnicodeString src((UChar*)str, strlength, strlength); ParsePosition pp; pp.setIndex(parsePos); if(lenient) { unum_setAttribute(fmt, UNUM_LENIENT_PARSE, JNI_TRUE); } ((const NumberFormat*)fmt)->parse(src, res, pp); if(lenient) { unum_setAttribute(fmt, UNUM_LENIENT_PARSE, JNI_FALSE); } env->ReleaseStringChars(text, str); if(pp.getErrorIndex() == -1) { parsePos = pp.getIndex(); } else { env->CallVoidMethod(position, setErrorIndexMethodID, (jint) pp.getErrorIndex()); return NULL; } Formattable::Type numType; numType = res.getType(); UErrorCode fmtStatus; double resultDouble; long resultLong; int64_t resultInt64; switch(numType) { case Formattable::kDouble: resultDouble = res.getDouble(); env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); return env->NewObject(doubleClass, dblInitMethodID, (jdouble) resultDouble); case Formattable::kLong: resultLong = res.getLong(); env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); return env->NewObject(longClass, longInitMethodID, (jlong) resultLong); case Formattable::kInt64: resultInt64 = res.getInt64(); env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); return env->NewObject(longClass, longInitMethodID, (jlong) resultInt64); default: break; } return NULL; } static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ {"openRBNFImpl", "(ILjava/lang/String;)I", (void*) openRBNFImpl1}, {"openRBNFImpl", "(Ljava/lang/String;Ljava/lang/String;)I", (void*) openRBNFImpl2}, {"closeRBNFImpl", "(I)V", (void*) closeRBNFImpl}, {"formatRBNFImpl", "(IJLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;", (void*) formatLongRBNFImpl}, {"formatRBNFImpl", "(IDLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;", (void*) formatDoubleRBNFImpl}, {"parseRBNFImpl", "(ILjava/lang/String;Ljava/text/ParsePosition;Z)Ljava/lang/Number;", (void*) parseRBNFImpl}, }; int register_com_ibm_icu4jni_text_NativeRBNF(JNIEnv* env) { return jniRegisterNativeMethods(env, "com/ibm/icu4jni/text/RuleBasedNumberFormat", gMethods, NELEM(gMethods)); }