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