Home | History | Annotate | Download | only in native
      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