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 "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