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 <vector>
     23 
     24 #include "cutils/log.h"
     25 #include "digitlst.h"
     26 #include "IcuUtilities.h"
     27 #include "JniConstants.h"
     28 #include "JniException.h"
     29 #include "JNIHelp.h"
     30 #include "ScopedJavaUnicodeString.h"
     31 #include "ScopedPrimitiveArray.h"
     32 #include "ScopedStringChars.h"
     33 #include "ScopedUtfChars.h"
     34 #include "unicode/decimfmt.h"
     35 #include "unicode/fmtable.h"
     36 #include "unicode/numfmt.h"
     37 #include "unicode/unum.h"
     38 #include "unicode/ustring.h"
     39 #include "UniquePtr.h"
     40 #include "valueOf.h"
     41 
     42 static DecimalFormat* toDecimalFormat(jlong addr) {
     43     return reinterpret_cast<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 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     UnicodeString groupingSeparator(groupingSeparator0);
     64 
     65     DecimalFormatSymbols* result = new DecimalFormatSymbols;
     66     result->setSymbol(DecimalFormatSymbols::kCurrencySymbol, currencySymbol.unicodeString());
     67     result->setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, UnicodeString(decimalSeparator));
     68     result->setSymbol(DecimalFormatSymbols::kDigitSymbol, UnicodeString(digit));
     69     result->setSymbol(DecimalFormatSymbols::kExponentialSymbol, exponentSeparator.unicodeString());
     70     result->setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, groupingSeparator);
     71     result->setSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol, groupingSeparator);
     72     result->setSymbol(DecimalFormatSymbols::kInfinitySymbol, infinity.unicodeString());
     73     result->setSymbol(DecimalFormatSymbols::kIntlCurrencySymbol, internationalCurrencySymbol.unicodeString());
     74     result->setSymbol(DecimalFormatSymbols::kMinusSignSymbol, minusSign.unicodeString());
     75     result->setSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol, UnicodeString(monetaryDecimalSeparator));
     76     result->setSymbol(DecimalFormatSymbols::kNaNSymbol, nan.unicodeString());
     77     result->setSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol, UnicodeString(patternSeparator));
     78     result->setSymbol(DecimalFormatSymbols::kPercentSymbol, percent.unicodeString());
     79     result->setSymbol(DecimalFormatSymbols::kPerMillSymbol, UnicodeString(perMill));
     80     // java.text.DecimalFormatSymbols just uses a zero digit,
     81     // but ICU >= 4.6 has a field for each decimal digit.
     82     result->setSymbol(DecimalFormatSymbols::kZeroDigitSymbol, UnicodeString(zeroDigit + 0));
     83     result->setSymbol(DecimalFormatSymbols::kOneDigitSymbol, UnicodeString(zeroDigit + 1));
     84     result->setSymbol(DecimalFormatSymbols::kTwoDigitSymbol, UnicodeString(zeroDigit + 2));
     85     result->setSymbol(DecimalFormatSymbols::kThreeDigitSymbol, UnicodeString(zeroDigit + 3));
     86     result->setSymbol(DecimalFormatSymbols::kFourDigitSymbol, UnicodeString(zeroDigit + 4));
     87     result->setSymbol(DecimalFormatSymbols::kFiveDigitSymbol, UnicodeString(zeroDigit + 5));
     88     result->setSymbol(DecimalFormatSymbols::kSixDigitSymbol, UnicodeString(zeroDigit + 6));
     89     result->setSymbol(DecimalFormatSymbols::kSevenDigitSymbol, UnicodeString(zeroDigit + 7));
     90     result->setSymbol(DecimalFormatSymbols::kEightDigitSymbol, UnicodeString(zeroDigit + 8));
     91     result->setSymbol(DecimalFormatSymbols::kNineDigitSymbol, UnicodeString(zeroDigit + 9));
     92     return result;
     93 }
     94 
     95 static void NativeDecimalFormat_setDecimalFormatSymbols(JNIEnv* env, jclass, jlong addr,
     96         jstring currencySymbol, jchar decimalSeparator, jchar digit, jstring exponentSeparator,
     97         jchar groupingSeparator, jstring infinity,
     98         jstring internationalCurrencySymbol, jstring minusSign,
     99         jchar monetaryDecimalSeparator, jstring nan, jchar patternSeparator,
    100         jstring percent, jchar perMill, jchar zeroDigit) {
    101     DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env,
    102             currencySymbol, decimalSeparator, digit, exponentSeparator, groupingSeparator,
    103             infinity, internationalCurrencySymbol, minusSign,
    104             monetaryDecimalSeparator, nan, patternSeparator, percent, perMill,
    105             zeroDigit);
    106     toDecimalFormat(addr)->adoptDecimalFormatSymbols(symbols);
    107 }
    108 
    109 static jlong NativeDecimalFormat_open(JNIEnv* env, jclass, jstring pattern0,
    110         jstring currencySymbol, jchar decimalSeparator, jchar digit, jstring exponentSeparator,
    111         jchar groupingSeparator, jstring infinity,
    112         jstring internationalCurrencySymbol, jstring minusSign,
    113         jchar monetaryDecimalSeparator, jstring nan, jchar patternSeparator,
    114         jstring percent, jchar perMill, jchar zeroDigit) {
    115     UErrorCode status = U_ZERO_ERROR;
    116     UParseError parseError;
    117     ScopedJavaUnicodeString pattern(env, pattern0);
    118     if (!pattern.valid()) {
    119       return 0;
    120     }
    121     DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env,
    122             currencySymbol, decimalSeparator, digit, exponentSeparator, groupingSeparator,
    123             infinity, internationalCurrencySymbol, minusSign,
    124             monetaryDecimalSeparator, nan, patternSeparator, percent, perMill,
    125             zeroDigit);
    126     DecimalFormat* fmt = new DecimalFormat(pattern.unicodeString(), symbols, parseError, status);
    127     if (fmt == NULL) {
    128         delete symbols;
    129     }
    130     maybeThrowIcuException(env, "DecimalFormat::DecimalFormat", status);
    131     return reinterpret_cast<uintptr_t>(fmt);
    132 }
    133 
    134 static void NativeDecimalFormat_close(JNIEnv*, jclass, jlong addr) {
    135     delete toDecimalFormat(addr);
    136 }
    137 
    138 static void NativeDecimalFormat_setRoundingMode(JNIEnv*, jclass, jlong addr, jint mode, jdouble increment) {
    139     DecimalFormat* fmt = toDecimalFormat(addr);
    140     fmt->setRoundingMode(static_cast<DecimalFormat::ERoundingMode>(mode));
    141     fmt->setRoundingIncrement(increment);
    142 }
    143 
    144 static void NativeDecimalFormat_setSymbol(JNIEnv* env, jclass, jlong addr, jint javaSymbol, jstring javaValue) {
    145     ScopedStringChars value(env, javaValue);
    146     if (value.get() == NULL) {
    147         return;
    148     }
    149     UErrorCode status = U_ZERO_ERROR;
    150     UNumberFormatSymbol symbol = static_cast<UNumberFormatSymbol>(javaSymbol);
    151     unum_setSymbol(toUNumberFormat(addr), symbol, value.get(), value.size(), &status);
    152     maybeThrowIcuException(env, "unum_setSymbol", status);
    153 }
    154 
    155 static void NativeDecimalFormat_setAttribute(JNIEnv*, jclass, jlong addr, jint javaAttr, jint value) {
    156     UNumberFormatAttribute attr = static_cast<UNumberFormatAttribute>(javaAttr);
    157     unum_setAttribute(toUNumberFormat(addr), attr, value);
    158 }
    159 
    160 static jint NativeDecimalFormat_getAttribute(JNIEnv*, jclass, jlong addr, jint javaAttr) {
    161     UNumberFormatAttribute attr = static_cast<UNumberFormatAttribute>(javaAttr);
    162     return unum_getAttribute(toUNumberFormat(addr), attr);
    163 }
    164 
    165 static void NativeDecimalFormat_setTextAttribute(JNIEnv* env, jclass, jlong addr, jint javaAttr, jstring javaValue) {
    166     ScopedStringChars value(env, javaValue);
    167     if (value.get() == NULL) {
    168         return;
    169     }
    170     UErrorCode status = U_ZERO_ERROR;
    171     UNumberFormatTextAttribute attr = static_cast<UNumberFormatTextAttribute>(javaAttr);
    172     unum_setTextAttribute(toUNumberFormat(addr), attr, value.get(), value.size(), &status);
    173     maybeThrowIcuException(env, "unum_setTextAttribute", status);
    174 }
    175 
    176 static jstring NativeDecimalFormat_getTextAttribute(JNIEnv* env, jclass, jlong addr, jint javaAttr) {
    177     UErrorCode status = U_ZERO_ERROR;
    178     UNumberFormat* fmt = toUNumberFormat(addr);
    179     UNumberFormatTextAttribute attr = static_cast<UNumberFormatTextAttribute>(javaAttr);
    180 
    181     // Find out how long the result will be...
    182     UniquePtr<UChar[]> chars;
    183     uint32_t charCount = 0;
    184     uint32_t desiredCount = unum_getTextAttribute(fmt, attr, chars.get(), charCount, &status);
    185     if (status == U_BUFFER_OVERFLOW_ERROR) {
    186         // ...then get it.
    187         status = U_ZERO_ERROR;
    188         charCount = desiredCount + 1;
    189         chars.reset(new UChar[charCount]);
    190         charCount = unum_getTextAttribute(fmt, attr, chars.get(), charCount, &status);
    191     }
    192     return maybeThrowIcuException(env, "unum_getTextAttribute", status) ? NULL : env->NewString(chars.get(), charCount);
    193 }
    194 
    195 static void NativeDecimalFormat_applyPatternImpl(JNIEnv* env, jclass, jlong addr, jboolean localized, jstring pattern0) {
    196     ScopedJavaUnicodeString pattern(env, pattern0);
    197     if (!pattern.valid()) {
    198       return;
    199     }
    200     DecimalFormat* fmt = toDecimalFormat(addr);
    201     UErrorCode status = U_ZERO_ERROR;
    202     const char* function;
    203     if (localized) {
    204         function = "DecimalFormat::applyLocalizedPattern";
    205         fmt->applyLocalizedPattern(pattern.unicodeString(), status);
    206     } else {
    207         function = "DecimalFormat::applyPattern";
    208         fmt->applyPattern(pattern.unicodeString(), status);
    209     }
    210     maybeThrowIcuException(env, function, status);
    211 }
    212 
    213 static jstring NativeDecimalFormat_toPatternImpl(JNIEnv* env, jclass, jlong addr, jboolean localized) {
    214     DecimalFormat* fmt = toDecimalFormat(addr);
    215     UnicodeString pattern;
    216     if (localized) {
    217         fmt->toLocalizedPattern(pattern);
    218     } else {
    219         fmt->toPattern(pattern);
    220     }
    221     return env->NewString(pattern.getBuffer(), pattern.length());
    222 }
    223 
    224 static jcharArray formatResult(JNIEnv* env, const UnicodeString& s, FieldPositionIterator* fpi, jobject javaFieldPositionIterator) {
    225     static jmethodID gFPI_setData = env->GetMethodID(JniConstants::fieldPositionIteratorClass, "setData", "([I)V");
    226 
    227     if (fpi != NULL) {
    228         std::vector<int32_t> data;
    229         FieldPosition fp;
    230         while (fpi->next(fp)) {
    231             data.push_back(fp.getField());
    232             data.push_back(fp.getBeginIndex());
    233             data.push_back(fp.getEndIndex());
    234         }
    235 
    236         jintArray javaData = NULL;
    237         if (!data.empty()) {
    238             javaData = env->NewIntArray(data.size());
    239             if (javaData == NULL) {
    240                 return NULL;
    241             }
    242             ScopedIntArrayRW ints(env, javaData);
    243             if (ints.get() == NULL) {
    244                 return NULL;
    245             }
    246             memcpy(ints.get(), &data[0], data.size() * sizeof(int32_t));
    247         }
    248         env->CallVoidMethod(javaFieldPositionIterator, gFPI_setData, javaData);
    249     }
    250 
    251     jcharArray result = env->NewCharArray(s.length());
    252     if (result != NULL) {
    253         env->SetCharArrayRegion(result, 0, s.length(), s.getBuffer());
    254     }
    255     return result;
    256 }
    257 
    258 template <typename T>
    259 static jcharArray format(JNIEnv* env, jlong addr, jobject javaFieldPositionIterator, T value) {
    260     UErrorCode status = U_ZERO_ERROR;
    261     UnicodeString s;
    262     DecimalFormat* fmt = toDecimalFormat(addr);
    263     FieldPositionIterator nativeFieldPositionIterator;
    264     FieldPositionIterator* fpi = javaFieldPositionIterator ? &nativeFieldPositionIterator : NULL;
    265     fmt->format(value, s, fpi, status);
    266     if (maybeThrowIcuException(env, "DecimalFormat::format", status)) {
    267         return NULL;
    268     }
    269     return formatResult(env, s, fpi, javaFieldPositionIterator);
    270 }
    271 
    272 static jcharArray NativeDecimalFormat_formatLong(JNIEnv* env, jclass, jlong addr, jlong value, jobject javaFieldPositionIterator) {
    273     return format<int64_t>(env, addr, javaFieldPositionIterator, value);
    274 }
    275 
    276 static jcharArray NativeDecimalFormat_formatDouble(JNIEnv* env, jclass, jlong addr, jdouble value, jobject javaFieldPositionIterator) {
    277     return format<double>(env, addr, javaFieldPositionIterator, value);
    278 }
    279 
    280 static jcharArray NativeDecimalFormat_formatDigitList(JNIEnv* env, jclass, jlong addr, jstring value, jobject javaFieldPositionIterator) {
    281     ScopedUtfChars chars(env, value);
    282     if (chars.c_str() == NULL) {
    283         return NULL;
    284     }
    285     StringPiece sp(chars.c_str());
    286     return format(env, addr, javaFieldPositionIterator, sp);
    287 }
    288 
    289 static jobject newBigDecimal(JNIEnv* env, const char* value, jsize len) {
    290     static jmethodID gBigDecimal_init = env->GetMethodID(JniConstants::bigDecimalClass, "<init>", "(Ljava/lang/String;)V");
    291 
    292     // this is painful...
    293     // value is a UTF-8 string of invariant characters, but isn't guaranteed to be
    294     // null-terminated.  NewStringUTF requires a terminated UTF-8 string.  So we copy the
    295     // data to jchars using UnicodeString, and call NewString instead.
    296     UnicodeString tmp(value, len, UnicodeString::kInvariant);
    297     jobject str = env->NewString(tmp.getBuffer(), tmp.length());
    298     return env->NewObject(JniConstants::bigDecimalClass, gBigDecimal_init, str);
    299 }
    300 
    301 static jobject NativeDecimalFormat_parse(JNIEnv* env, jclass, jlong addr, jstring text,
    302         jobject position, jboolean parseBigDecimal) {
    303 
    304     static jmethodID gPP_getIndex = env->GetMethodID(JniConstants::parsePositionClass, "getIndex", "()I");
    305     static jmethodID gPP_setIndex = env->GetMethodID(JniConstants::parsePositionClass, "setIndex", "(I)V");
    306     static jmethodID gPP_setErrorIndex = env->GetMethodID(JniConstants::parsePositionClass, "setErrorIndex", "(I)V");
    307 
    308     ScopedJavaUnicodeString src(env, text);
    309     if (!src.valid()) {
    310       return NULL;
    311     }
    312 
    313     // make sure the ParsePosition is valid. Actually icu4c would parse a number
    314     // correctly even if the parsePosition is set to -1, but since the RI fails
    315     // for that case we have to fail too
    316     int parsePos = env->CallIntMethod(position, gPP_getIndex, NULL);
    317     if (parsePos < 0 || parsePos > env->GetStringLength(text)) {
    318         return NULL;
    319     }
    320 
    321     Formattable res;
    322     ParsePosition pp(parsePos);
    323     DecimalFormat* fmt = toDecimalFormat(addr);
    324     fmt->parse(src.unicodeString(), res, pp);
    325 
    326     if (pp.getErrorIndex() == -1) {
    327         env->CallVoidMethod(position, gPP_setIndex, pp.getIndex());
    328     } else {
    329         env->CallVoidMethod(position, gPP_setErrorIndex, pp.getErrorIndex());
    330         return NULL;
    331     }
    332 
    333     if (parseBigDecimal) {
    334         UErrorCode status = U_ZERO_ERROR;
    335         StringPiece str = res.getDecimalNumber(status);
    336         if (U_SUCCESS(status)) {
    337             int len = str.length();
    338             const char* data = str.data();
    339             if (strncmp(data, "NaN", 3) == 0 ||
    340                 strncmp(data, "Inf", 3) == 0 ||
    341                 strncmp(data, "-Inf", 4) == 0) {
    342                 double resultDouble = res.getDouble(status);
    343                 return doubleValueOf(env, resultDouble);
    344             }
    345             return newBigDecimal(env, data, len);
    346         }
    347         return NULL;
    348     }
    349 
    350     switch (res.getType()) {
    351         case Formattable::kDouble: return doubleValueOf(env, res.getDouble());
    352         case Formattable::kLong:   return longValueOf(env, res.getLong());
    353         case Formattable::kInt64:  return longValueOf(env, res.getInt64());
    354         default:                   return NULL;
    355     }
    356 }
    357 
    358 static jlong NativeDecimalFormat_cloneImpl(JNIEnv*, jclass, jlong addr) {
    359     DecimalFormat* fmt = toDecimalFormat(addr);
    360     return reinterpret_cast<uintptr_t>(fmt->clone());
    361 }
    362 
    363 static JNINativeMethod gMethods[] = {
    364     NATIVE_METHOD(NativeDecimalFormat, applyPatternImpl, "(JZLjava/lang/String;)V"),
    365     NATIVE_METHOD(NativeDecimalFormat, cloneImpl, "(J)J"),
    366     NATIVE_METHOD(NativeDecimalFormat, close, "(J)V"),
    367     NATIVE_METHOD(NativeDecimalFormat, formatDouble, "(JDLlibcore/icu/NativeDecimalFormat$FieldPositionIterator;)[C"),
    368     NATIVE_METHOD(NativeDecimalFormat, formatLong, "(JJLlibcore/icu/NativeDecimalFormat$FieldPositionIterator;)[C"),
    369     NATIVE_METHOD(NativeDecimalFormat, formatDigitList, "(JLjava/lang/String;Llibcore/icu/NativeDecimalFormat$FieldPositionIterator;)[C"),
    370     NATIVE_METHOD(NativeDecimalFormat, getAttribute, "(JI)I"),
    371     NATIVE_METHOD(NativeDecimalFormat, getTextAttribute, "(JI)Ljava/lang/String;"),
    372     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"),
    373     NATIVE_METHOD(NativeDecimalFormat, parse, "(JLjava/lang/String;Ljava/text/ParsePosition;Z)Ljava/lang/Number;"),
    374     NATIVE_METHOD(NativeDecimalFormat, setAttribute, "(JII)V"),
    375     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"),
    376     NATIVE_METHOD(NativeDecimalFormat, setRoundingMode, "(JID)V"),
    377     NATIVE_METHOD(NativeDecimalFormat, setSymbol, "(JILjava/lang/String;)V"),
    378     NATIVE_METHOD(NativeDecimalFormat, setTextAttribute, "(JILjava/lang/String;)V"),
    379     NATIVE_METHOD(NativeDecimalFormat, toPatternImpl, "(JZ)Ljava/lang/String;"),
    380 };
    381 void register_libcore_icu_NativeDecimalFormat(JNIEnv* env) {
    382     jniRegisterNativeMethods(env, "libcore/icu/NativeDecimalFormat", gMethods, NELEM(gMethods));
    383 }
    384