1 /* 2 * Copyright (C) 2006 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 "NativeDecimalFormat" 18 19 #include "JNIHelp.h" 20 #include "JniConstants.h" 21 #include "JniException.h" 22 #include "ScopedJavaUnicodeString.h" 23 #include "ScopedPrimitiveArray.h" 24 #include "ScopedStringChars.h" 25 #include "ScopedUtfChars.h" 26 #include "UniquePtr.h" 27 #include "cutils/log.h" 28 #include "digitlst.h" 29 #include "unicode/decimfmt.h" 30 #include "unicode/fmtable.h" 31 #include "unicode/numfmt.h" 32 #include "unicode/unum.h" 33 #include "unicode/ustring.h" 34 #include "valueOf.h" 35 #include <stdlib.h> 36 #include <string.h> 37 38 static DecimalFormat* toDecimalFormat(jint addr) { 39 return reinterpret_cast<DecimalFormat*>(static_cast<uintptr_t>(addr)); 40 } 41 42 static UNumberFormat* toUNumberFormat(jint addr) { 43 return reinterpret_cast<UNumberFormat*>(static_cast<uintptr_t>(addr)); 44 } 45 46 static DecimalFormatSymbols* makeDecimalFormatSymbols(JNIEnv* env, 47 jstring currencySymbol0, jchar decimalSeparator, jchar digit, jstring exponentSeparator0, 48 jchar groupingSeparator0, jstring infinity0, 49 jstring internationalCurrencySymbol0, jchar minusSign, 50 jchar monetaryDecimalSeparator, jstring nan0, jchar patternSeparator, 51 jchar percent, jchar perMill, jchar zeroDigit) { 52 ScopedJavaUnicodeString currencySymbol(env, currencySymbol0); 53 ScopedJavaUnicodeString exponentSeparator(env, exponentSeparator0); 54 ScopedJavaUnicodeString infinity(env, infinity0); 55 ScopedJavaUnicodeString internationalCurrencySymbol(env, internationalCurrencySymbol0); 56 ScopedJavaUnicodeString nan(env, nan0); 57 UnicodeString groupingSeparator(groupingSeparator0); 58 59 DecimalFormatSymbols* result = new DecimalFormatSymbols; 60 result->setSymbol(DecimalFormatSymbols::kCurrencySymbol, currencySymbol.unicodeString()); 61 result->setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, UnicodeString(decimalSeparator)); 62 result->setSymbol(DecimalFormatSymbols::kDigitSymbol, UnicodeString(digit)); 63 result->setSymbol(DecimalFormatSymbols::kExponentialSymbol, exponentSeparator.unicodeString()); 64 result->setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, groupingSeparator); 65 result->setSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol, groupingSeparator); 66 result->setSymbol(DecimalFormatSymbols::kInfinitySymbol, infinity.unicodeString()); 67 result->setSymbol(DecimalFormatSymbols::kIntlCurrencySymbol, internationalCurrencySymbol.unicodeString()); 68 result->setSymbol(DecimalFormatSymbols::kMinusSignSymbol, UnicodeString(minusSign)); 69 result->setSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol, UnicodeString(monetaryDecimalSeparator)); 70 result->setSymbol(DecimalFormatSymbols::kNaNSymbol, nan.unicodeString()); 71 result->setSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol, UnicodeString(patternSeparator)); 72 result->setSymbol(DecimalFormatSymbols::kPercentSymbol, UnicodeString(percent)); 73 result->setSymbol(DecimalFormatSymbols::kPerMillSymbol, UnicodeString(perMill)); 74 // java.text.DecimalFormatSymbols just uses a zero digit, 75 // but ICU >= 4.6 has a field for each decimal digit. 76 result->setSymbol(DecimalFormatSymbols::kZeroDigitSymbol, UnicodeString(zeroDigit + 0)); 77 result->setSymbol(DecimalFormatSymbols::kOneDigitSymbol, UnicodeString(zeroDigit + 1)); 78 result->setSymbol(DecimalFormatSymbols::kTwoDigitSymbol, UnicodeString(zeroDigit + 2)); 79 result->setSymbol(DecimalFormatSymbols::kThreeDigitSymbol, UnicodeString(zeroDigit + 3)); 80 result->setSymbol(DecimalFormatSymbols::kFourDigitSymbol, UnicodeString(zeroDigit + 4)); 81 result->setSymbol(DecimalFormatSymbols::kFiveDigitSymbol, UnicodeString(zeroDigit + 5)); 82 result->setSymbol(DecimalFormatSymbols::kSixDigitSymbol, UnicodeString(zeroDigit + 6)); 83 result->setSymbol(DecimalFormatSymbols::kSevenDigitSymbol, UnicodeString(zeroDigit + 7)); 84 result->setSymbol(DecimalFormatSymbols::kEightDigitSymbol, UnicodeString(zeroDigit + 8)); 85 result->setSymbol(DecimalFormatSymbols::kNineDigitSymbol, UnicodeString(zeroDigit + 9)); 86 return result; 87 } 88 89 static void NativeDecimalFormat_setDecimalFormatSymbols(JNIEnv* env, jclass, jint addr, 90 jstring currencySymbol, jchar decimalSeparator, jchar digit, jstring exponentSeparator, 91 jchar groupingSeparator, jstring infinity, 92 jstring internationalCurrencySymbol, jchar minusSign, 93 jchar monetaryDecimalSeparator, jstring nan, jchar patternSeparator, 94 jchar percent, jchar perMill, jchar zeroDigit) { 95 DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env, 96 currencySymbol, decimalSeparator, digit, exponentSeparator, groupingSeparator, 97 infinity, internationalCurrencySymbol, minusSign, 98 monetaryDecimalSeparator, nan, patternSeparator, percent, perMill, 99 zeroDigit); 100 toDecimalFormat(addr)->adoptDecimalFormatSymbols(symbols); 101 } 102 103 static jint NativeDecimalFormat_open(JNIEnv* env, jclass, jstring pattern0, 104 jstring currencySymbol, jchar decimalSeparator, jchar digit, jstring exponentSeparator, 105 jchar groupingSeparator, jstring infinity, 106 jstring internationalCurrencySymbol, jchar minusSign, 107 jchar monetaryDecimalSeparator, jstring nan, jchar patternSeparator, 108 jchar percent, jchar perMill, jchar zeroDigit) { 109 if (pattern0 == NULL) { 110 jniThrowNullPointerException(env, NULL); 111 return 0; 112 } 113 UErrorCode status = U_ZERO_ERROR; 114 UParseError parseError; 115 ScopedJavaUnicodeString pattern(env, pattern0); 116 DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env, 117 currencySymbol, decimalSeparator, digit, exponentSeparator, groupingSeparator, 118 infinity, internationalCurrencySymbol, minusSign, 119 monetaryDecimalSeparator, nan, patternSeparator, percent, perMill, 120 zeroDigit); 121 DecimalFormat* fmt = new DecimalFormat(pattern.unicodeString(), symbols, parseError, status); 122 if (fmt == NULL) { 123 delete symbols; 124 } 125 maybeThrowIcuException(env, "DecimalFormat::DecimalFormat", status); 126 return static_cast<jint>(reinterpret_cast<uintptr_t>(fmt)); 127 } 128 129 static void NativeDecimalFormat_close(JNIEnv*, jclass, jint addr) { 130 delete toDecimalFormat(addr); 131 } 132 133 static void NativeDecimalFormat_setRoundingMode(JNIEnv*, jclass, jint addr, jint mode, jdouble increment) { 134 DecimalFormat* fmt = toDecimalFormat(addr); 135 fmt->setRoundingMode(static_cast<DecimalFormat::ERoundingMode>(mode)); 136 fmt->setRoundingIncrement(increment); 137 } 138 139 static void NativeDecimalFormat_setSymbol(JNIEnv* env, jclass, jint addr, jint javaSymbol, jstring javaValue) { 140 ScopedStringChars value(env, javaValue); 141 if (value.get() == NULL) { 142 return; 143 } 144 UErrorCode status = U_ZERO_ERROR; 145 UNumberFormatSymbol symbol = static_cast<UNumberFormatSymbol>(javaSymbol); 146 unum_setSymbol(toUNumberFormat(addr), symbol, value.get(), value.size(), &status); 147 maybeThrowIcuException(env, "unum_setSymbol", status); 148 } 149 150 static void NativeDecimalFormat_setAttribute(JNIEnv*, jclass, jint addr, jint javaAttr, jint value) { 151 UNumberFormatAttribute attr = static_cast<UNumberFormatAttribute>(javaAttr); 152 unum_setAttribute(toUNumberFormat(addr), attr, value); 153 } 154 155 static jint NativeDecimalFormat_getAttribute(JNIEnv*, jclass, jint addr, jint javaAttr) { 156 UNumberFormatAttribute attr = static_cast<UNumberFormatAttribute>(javaAttr); 157 return unum_getAttribute(toUNumberFormat(addr), attr); 158 } 159 160 static void NativeDecimalFormat_setTextAttribute(JNIEnv* env, jclass, jint addr, jint javaAttr, jstring javaValue) { 161 ScopedStringChars value(env, javaValue); 162 if (value.get() == NULL) { 163 return; 164 } 165 UErrorCode status = U_ZERO_ERROR; 166 UNumberFormatTextAttribute attr = static_cast<UNumberFormatTextAttribute>(javaAttr); 167 unum_setTextAttribute(toUNumberFormat(addr), attr, value.get(), value.size(), &status); 168 maybeThrowIcuException(env, "unum_setTextAttribute", status); 169 } 170 171 static jstring NativeDecimalFormat_getTextAttribute(JNIEnv* env, jclass, jint addr, jint javaAttr) { 172 UErrorCode status = U_ZERO_ERROR; 173 UNumberFormat* fmt = toUNumberFormat(addr); 174 UNumberFormatTextAttribute attr = static_cast<UNumberFormatTextAttribute>(javaAttr); 175 176 // Find out how long the result will be... 177 UniquePtr<UChar[]> chars; 178 uint32_t charCount = 0; 179 uint32_t desiredCount = unum_getTextAttribute(fmt, attr, chars.get(), charCount, &status); 180 if (status == U_BUFFER_OVERFLOW_ERROR) { 181 // ...then get it. 182 status = U_ZERO_ERROR; 183 charCount = desiredCount + 1; 184 chars.reset(new UChar[charCount]); 185 charCount = unum_getTextAttribute(fmt, attr, chars.get(), charCount, &status); 186 } 187 return maybeThrowIcuException(env, "unum_getTextAttribute", status) ? NULL : env->NewString(chars.get(), charCount); 188 } 189 190 static void NativeDecimalFormat_applyPatternImpl(JNIEnv* env, jclass, jint addr, jboolean localized, jstring pattern0) { 191 if (pattern0 == NULL) { 192 jniThrowNullPointerException(env, NULL); 193 return; 194 } 195 ScopedJavaUnicodeString pattern(env, pattern0); 196 DecimalFormat* fmt = toDecimalFormat(addr); 197 UErrorCode status = U_ZERO_ERROR; 198 const char* function; 199 if (localized) { 200 function = "DecimalFormat::applyLocalizedPattern"; 201 fmt->applyLocalizedPattern(pattern.unicodeString(), status); 202 } else { 203 function = "DecimalFormat::applyPattern"; 204 fmt->applyPattern(pattern.unicodeString(), status); 205 } 206 maybeThrowIcuException(env, function, status); 207 } 208 209 static jstring NativeDecimalFormat_toPatternImpl(JNIEnv* env, jclass, jint addr, jboolean localized) { 210 DecimalFormat* fmt = toDecimalFormat(addr); 211 UnicodeString pattern; 212 if (localized) { 213 fmt->toLocalizedPattern(pattern); 214 } else { 215 fmt->toPattern(pattern); 216 } 217 return env->NewString(pattern.getBuffer(), pattern.length()); 218 } 219 220 static jcharArray formatResult(JNIEnv* env, const UnicodeString &str, FieldPositionIterator* fpi, jobject fpIter) { 221 static jmethodID gFPI_setData = env->GetMethodID(JniConstants::fieldPositionIteratorClass, "setData", "([I)V"); 222 223 if (fpi != NULL) { 224 int len = fpi->getData(NULL, 0); 225 jintArray data = NULL; 226 if (len) { 227 data = env->NewIntArray(len); 228 ScopedIntArrayRW ints(env, data); 229 if (ints.get() == NULL) { 230 return NULL; 231 } 232 fpi->getData(ints.get(), len); 233 } 234 env->CallVoidMethod(fpIter, gFPI_setData, data); 235 } 236 237 jcharArray result = env->NewCharArray(str.length()); 238 if (result != NULL) { 239 env->SetCharArrayRegion(result, 0, str.length(), str.getBuffer()); 240 } 241 return result; 242 } 243 244 template <typename T> 245 static jcharArray format(JNIEnv* env, jint addr, jobject fpIter, T val) { 246 UErrorCode status = U_ZERO_ERROR; 247 UnicodeString str; 248 DecimalFormat* fmt = toDecimalFormat(addr); 249 FieldPositionIterator fpi; 250 FieldPositionIterator* pfpi = fpIter ? &fpi : NULL; 251 fmt->format(val, str, pfpi, status); 252 return formatResult(env, str, pfpi, fpIter); 253 } 254 255 static jcharArray NativeDecimalFormat_formatLong(JNIEnv* env, jclass, jint addr, jlong value, jobject fpIter) { 256 return format(env, addr, fpIter, value); 257 } 258 259 static jcharArray NativeDecimalFormat_formatDouble(JNIEnv* env, jclass, jint addr, jdouble value, jobject fpIter) { 260 return format(env, addr, fpIter, value); 261 } 262 263 static jcharArray NativeDecimalFormat_formatDigitList(JNIEnv* env, jclass, jint addr, jstring value, jobject fpIter) { 264 ScopedUtfChars chars(env, value); 265 if (chars.c_str() == NULL) { 266 return NULL; 267 } 268 StringPiece sp(chars.c_str()); 269 return format(env, addr, fpIter, sp); 270 } 271 272 static jobject newBigDecimal(JNIEnv* env, const char* value, jsize len) { 273 static jmethodID gBigDecimal_init = env->GetMethodID(JniConstants::bigDecimalClass, "<init>", "(Ljava/lang/String;)V"); 274 275 // this is painful... 276 // value is a UTF-8 string of invariant characters, but isn't guaranteed to be 277 // null-terminated. NewStringUTF requires a terminated UTF-8 string. So we copy the 278 // data to jchars using UnicodeString, and call NewString instead. 279 UnicodeString tmp(value, len, UnicodeString::kInvariant); 280 jobject str = env->NewString(tmp.getBuffer(), tmp.length()); 281 return env->NewObject(JniConstants::bigDecimalClass, gBigDecimal_init, str); 282 } 283 284 static jobject NativeDecimalFormat_parse(JNIEnv* env, jclass, jint addr, jstring text, 285 jobject position, jboolean parseBigDecimal) { 286 287 static jmethodID gPP_getIndex = env->GetMethodID(JniConstants::parsePositionClass, "getIndex", "()I"); 288 static jmethodID gPP_setIndex = env->GetMethodID(JniConstants::parsePositionClass, "setIndex", "(I)V"); 289 static jmethodID gPP_setErrorIndex = env->GetMethodID(JniConstants::parsePositionClass, "setErrorIndex", "(I)V"); 290 291 // make sure the ParsePosition is valid. Actually icu4c would parse a number 292 // correctly even if the parsePosition is set to -1, but since the RI fails 293 // for that case we have to fail too 294 int parsePos = env->CallIntMethod(position, gPP_getIndex, NULL); 295 if (parsePos < 0 || parsePos > env->GetStringLength(text)) { 296 return NULL; 297 } 298 299 Formattable res; 300 ParsePosition pp(parsePos); 301 ScopedJavaUnicodeString src(env, text); 302 DecimalFormat* fmt = toDecimalFormat(addr); 303 fmt->parse(src.unicodeString(), res, pp); 304 305 if (pp.getErrorIndex() == -1) { 306 env->CallVoidMethod(position, gPP_setIndex, pp.getIndex()); 307 } else { 308 env->CallVoidMethod(position, gPP_setErrorIndex, pp.getErrorIndex()); 309 return NULL; 310 } 311 312 if (parseBigDecimal) { 313 UErrorCode status = U_ZERO_ERROR; 314 StringPiece str = res.getDecimalNumber(status); 315 if (U_SUCCESS(status)) { 316 int len = str.length(); 317 const char* data = str.data(); 318 if (strncmp(data, "NaN", 3) == 0 || 319 strncmp(data, "Inf", 3) == 0 || 320 strncmp(data, "-Inf", 4) == 0) { 321 double resultDouble = res.getDouble(status); 322 return doubleValueOf(env, resultDouble); 323 } 324 return newBigDecimal(env, data, len); 325 } 326 return NULL; 327 } 328 329 switch (res.getType()) { 330 case Formattable::kDouble: return doubleValueOf(env, res.getDouble()); 331 case Formattable::kLong: return longValueOf(env, res.getLong()); 332 case Formattable::kInt64: return longValueOf(env, res.getInt64()); 333 default: return NULL; 334 } 335 } 336 337 static jint NativeDecimalFormat_cloneImpl(JNIEnv*, jclass, jint addr) { 338 DecimalFormat* fmt = toDecimalFormat(addr); 339 return static_cast<jint>(reinterpret_cast<uintptr_t>(fmt->clone())); 340 } 341 342 static JNINativeMethod gMethods[] = { 343 NATIVE_METHOD(NativeDecimalFormat, applyPatternImpl, "(IZLjava/lang/String;)V"), 344 NATIVE_METHOD(NativeDecimalFormat, cloneImpl, "(I)I"), 345 NATIVE_METHOD(NativeDecimalFormat, close, "(I)V"), 346 NATIVE_METHOD(NativeDecimalFormat, formatDouble, "(IDLlibcore/icu/NativeDecimalFormat$FieldPositionIterator;)[C"), 347 NATIVE_METHOD(NativeDecimalFormat, formatLong, "(IJLlibcore/icu/NativeDecimalFormat$FieldPositionIterator;)[C"), 348 NATIVE_METHOD(NativeDecimalFormat, formatDigitList, "(ILjava/lang/String;Llibcore/icu/NativeDecimalFormat$FieldPositionIterator;)[C"), 349 NATIVE_METHOD(NativeDecimalFormat, getAttribute, "(II)I"), 350 NATIVE_METHOD(NativeDecimalFormat, getTextAttribute, "(II)Ljava/lang/String;"), 351 NATIVE_METHOD(NativeDecimalFormat, open, "(Ljava/lang/String;Ljava/lang/String;CCLjava/lang/String;CLjava/lang/String;Ljava/lang/String;CCLjava/lang/String;CCCC)I"), 352 NATIVE_METHOD(NativeDecimalFormat, parse, "(ILjava/lang/String;Ljava/text/ParsePosition;Z)Ljava/lang/Number;"), 353 NATIVE_METHOD(NativeDecimalFormat, setAttribute, "(III)V"), 354 NATIVE_METHOD(NativeDecimalFormat, setDecimalFormatSymbols, "(ILjava/lang/String;CCLjava/lang/String;CLjava/lang/String;Ljava/lang/String;CCLjava/lang/String;CCCC)V"), 355 NATIVE_METHOD(NativeDecimalFormat, setRoundingMode, "(IID)V"), 356 NATIVE_METHOD(NativeDecimalFormat, setSymbol, "(IILjava/lang/String;)V"), 357 NATIVE_METHOD(NativeDecimalFormat, setTextAttribute, "(IILjava/lang/String;)V"), 358 NATIVE_METHOD(NativeDecimalFormat, toPatternImpl, "(IZ)Ljava/lang/String;"), 359 }; 360 void register_libcore_icu_NativeDecimalFormat(JNIEnv* env) { 361 jniRegisterNativeMethods(env, "libcore/icu/NativeDecimalFormat", gMethods, NELEM(gMethods)); 362 } 363