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