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