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