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