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, "DecimalFormat::DecimalFormat", 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, "unum_setSymbol", 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, "unum_setTextAttribute", 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, "unum_getTextAttribute", 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     const char* function;
    199     if (localized) {
    200         function = "DecimalFormat::applyLocalizedPattern";
    201         fmt->applyLocalizedPattern(pattern.unicodeString(), status);
    202     } else {
    203         function = "DecimalFormat::applyPattern";
    204         fmt->applyPattern(pattern.unicodeString(), status);
    205     }
    206     maybeThrowIcuException(env, function, status);
    207 }
    208 
    209 static jstring NativeDecimalFormat_toPatternImpl(JNIEnv* env, jclass, jint addr, jboolean localized) {
    210     DecimalFormat* fmt = toDecimalFormat(addr);
    211     UnicodeString pattern;
    212     if (localized) {
    213         fmt->toLocalizedPattern(pattern);
    214     } else {
    215         fmt->toPattern(pattern);
    216     }
    217     return env->NewString(pattern.getBuffer(), pattern.length());
    218 }
    219 
    220 static jcharArray formatResult(JNIEnv* env, const UnicodeString &str, FieldPositionIterator* fpi, jobject fpIter) {
    221     static jmethodID gFPI_setData = env->GetMethodID(JniConstants::fieldPositionIteratorClass, "setData", "([I)V");
    222 
    223     if (fpi != NULL) {
    224         int len = fpi->getData(NULL, 0);
    225         jintArray data = NULL;
    226         if (len) {
    227             data = env->NewIntArray(len);
    228             ScopedIntArrayRW ints(env, data);
    229             if (ints.get() == NULL) {
    230                 return NULL;
    231             }
    232             fpi->getData(ints.get(), len);
    233         }
    234         env->CallVoidMethod(fpIter, gFPI_setData, data);
    235     }
    236 
    237     jcharArray result = env->NewCharArray(str.length());
    238     if (result != NULL) {
    239         env->SetCharArrayRegion(result, 0, str.length(), str.getBuffer());
    240     }
    241     return result;
    242 }
    243 
    244 template <typename T>
    245 static jcharArray format(JNIEnv* env, jint addr, jobject fpIter, T val) {
    246     UErrorCode status = U_ZERO_ERROR;
    247     UnicodeString str;
    248     DecimalFormat* fmt = toDecimalFormat(addr);
    249     FieldPositionIterator fpi;
    250     FieldPositionIterator* pfpi = fpIter ? &fpi : NULL;
    251     fmt->format(val, str, pfpi, status);
    252     return formatResult(env, str, pfpi, fpIter);
    253 }
    254 
    255 static jcharArray NativeDecimalFormat_formatLong(JNIEnv* env, jclass, jint addr, jlong value, jobject fpIter) {
    256     return format(env, addr, fpIter, value);
    257 }
    258 
    259 static jcharArray NativeDecimalFormat_formatDouble(JNIEnv* env, jclass, jint addr, jdouble value, jobject fpIter) {
    260     return format(env, addr, fpIter, value);
    261 }
    262 
    263 static jcharArray NativeDecimalFormat_formatDigitList(JNIEnv* env, jclass, jint addr, jstring value, jobject fpIter) {
    264     ScopedUtfChars chars(env, value);
    265     if (chars.c_str() == NULL) {
    266         return NULL;
    267     }
    268     StringPiece sp(chars.c_str());
    269     return format(env, addr, fpIter, sp);
    270 }
    271 
    272 static jobject newBigDecimal(JNIEnv* env, const char* value, jsize len) {
    273     static jmethodID gBigDecimal_init = env->GetMethodID(JniConstants::bigDecimalClass, "<init>", "(Ljava/lang/String;)V");
    274 
    275     // this is painful...
    276     // value is a UTF-8 string of invariant characters, but isn't guaranteed to be
    277     // null-terminated.  NewStringUTF requires a terminated UTF-8 string.  So we copy the
    278     // data to jchars using UnicodeString, and call NewString instead.
    279     UnicodeString tmp(value, len, UnicodeString::kInvariant);
    280     jobject str = env->NewString(tmp.getBuffer(), tmp.length());
    281     return env->NewObject(JniConstants::bigDecimalClass, gBigDecimal_init, str);
    282 }
    283 
    284 static jobject NativeDecimalFormat_parse(JNIEnv* env, jclass, jint addr, jstring text,
    285         jobject position, jboolean parseBigDecimal) {
    286 
    287     static jmethodID gPP_getIndex = env->GetMethodID(JniConstants::parsePositionClass, "getIndex", "()I");
    288     static jmethodID gPP_setIndex = env->GetMethodID(JniConstants::parsePositionClass, "setIndex", "(I)V");
    289     static jmethodID gPP_setErrorIndex = env->GetMethodID(JniConstants::parsePositionClass, "setErrorIndex", "(I)V");
    290 
    291     // make sure the ParsePosition is valid. Actually icu4c would parse a number
    292     // correctly even if the parsePosition is set to -1, but since the RI fails
    293     // for that case we have to fail too
    294     int parsePos = env->CallIntMethod(position, gPP_getIndex, NULL);
    295     if (parsePos < 0 || parsePos > env->GetStringLength(text)) {
    296         return NULL;
    297     }
    298 
    299     Formattable res;
    300     ParsePosition pp(parsePos);
    301     ScopedJavaUnicodeString src(env, text);
    302     DecimalFormat* fmt = toDecimalFormat(addr);
    303     fmt->parse(src.unicodeString(), res, pp);
    304 
    305     if (pp.getErrorIndex() == -1) {
    306         env->CallVoidMethod(position, gPP_setIndex, pp.getIndex());
    307     } else {
    308         env->CallVoidMethod(position, gPP_setErrorIndex, pp.getErrorIndex());
    309         return NULL;
    310     }
    311 
    312     if (parseBigDecimal) {
    313         UErrorCode status = U_ZERO_ERROR;
    314         StringPiece str = res.getDecimalNumber(status);
    315         if (U_SUCCESS(status)) {
    316             int len = str.length();
    317             const char* data = str.data();
    318             if (strncmp(data, "NaN", 3) == 0 ||
    319                 strncmp(data, "Inf", 3) == 0 ||
    320                 strncmp(data, "-Inf", 4) == 0) {
    321                 double resultDouble = res.getDouble(status);
    322                 return doubleValueOf(env, resultDouble);
    323             }
    324             return newBigDecimal(env, data, len);
    325         }
    326         return NULL;
    327     }
    328 
    329     switch (res.getType()) {
    330         case Formattable::kDouble: return doubleValueOf(env, res.getDouble());
    331         case Formattable::kLong:   return longValueOf(env, res.getLong());
    332         case Formattable::kInt64:  return longValueOf(env, res.getInt64());
    333         default:                   return NULL;
    334     }
    335 }
    336 
    337 static jint NativeDecimalFormat_cloneImpl(JNIEnv*, jclass, jint addr) {
    338     DecimalFormat* fmt = toDecimalFormat(addr);
    339     return static_cast<jint>(reinterpret_cast<uintptr_t>(fmt->clone()));
    340 }
    341 
    342 static JNINativeMethod gMethods[] = {
    343     NATIVE_METHOD(NativeDecimalFormat, applyPatternImpl, "(IZLjava/lang/String;)V"),
    344     NATIVE_METHOD(NativeDecimalFormat, cloneImpl, "(I)I"),
    345     NATIVE_METHOD(NativeDecimalFormat, close, "(I)V"),
    346     NATIVE_METHOD(NativeDecimalFormat, formatDouble, "(IDLlibcore/icu/NativeDecimalFormat$FieldPositionIterator;)[C"),
    347     NATIVE_METHOD(NativeDecimalFormat, formatLong, "(IJLlibcore/icu/NativeDecimalFormat$FieldPositionIterator;)[C"),
    348     NATIVE_METHOD(NativeDecimalFormat, formatDigitList, "(ILjava/lang/String;Llibcore/icu/NativeDecimalFormat$FieldPositionIterator;)[C"),
    349     NATIVE_METHOD(NativeDecimalFormat, getAttribute, "(II)I"),
    350     NATIVE_METHOD(NativeDecimalFormat, getTextAttribute, "(II)Ljava/lang/String;"),
    351     NATIVE_METHOD(NativeDecimalFormat, open, "(Ljava/lang/String;Ljava/lang/String;CCLjava/lang/String;CLjava/lang/String;Ljava/lang/String;CCLjava/lang/String;CCCC)I"),
    352     NATIVE_METHOD(NativeDecimalFormat, parse, "(ILjava/lang/String;Ljava/text/ParsePosition;Z)Ljava/lang/Number;"),
    353     NATIVE_METHOD(NativeDecimalFormat, setAttribute, "(III)V"),
    354     NATIVE_METHOD(NativeDecimalFormat, setDecimalFormatSymbols, "(ILjava/lang/String;CCLjava/lang/String;CLjava/lang/String;Ljava/lang/String;CCLjava/lang/String;CCCC)V"),
    355     NATIVE_METHOD(NativeDecimalFormat, setRoundingMode, "(IID)V"),
    356     NATIVE_METHOD(NativeDecimalFormat, setSymbol, "(IILjava/lang/String;)V"),
    357     NATIVE_METHOD(NativeDecimalFormat, setTextAttribute, "(IILjava/lang/String;)V"),
    358     NATIVE_METHOD(NativeDecimalFormat, toPatternImpl, "(IZ)Ljava/lang/String;"),
    359 };
    360 void register_libcore_icu_NativeDecimalFormat(JNIEnv* env) {
    361     jniRegisterNativeMethods(env, "libcore/icu/NativeDecimalFormat", gMethods, NELEM(gMethods));
    362 }
    363