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 #include "JNIHelp.h"
     19 #include "AndroidSystemNatives.h"
     20 #include "cutils/log.h"
     21 #include "unicode/unum.h"
     22 #include "unicode/numfmt.h"
     23 #include "unicode/decimfmt.h"
     24 #include "unicode/fmtable.h"
     25 #include "unicode/ustring.h"
     26 #include "digitlst.h"
     27 #include "ErrorCode.h"
     28 #include "ScopedJavaUnicodeString.h"
     29 #include <stdlib.h>
     30 #include <string.h>
     31 
     32 static DecimalFormat* toDecimalFormat(jint addr) {
     33     return reinterpret_cast<DecimalFormat*>(static_cast<uintptr_t>(addr));
     34 }
     35 
     36 static DecimalFormatSymbols* makeDecimalFormatSymbols(JNIEnv* env,
     37         jstring currencySymbol0, jchar decimalSeparator, jchar digit,
     38         jchar groupingSeparator0, jstring infinity0,
     39         jstring internationalCurrencySymbol0, jchar minusSign,
     40         jchar monetaryDecimalSeparator, jstring nan0, jchar patternSeparator,
     41         jchar percent, jchar perMill, jchar zeroDigit) {
     42     ScopedJavaUnicodeString currencySymbol(env, currencySymbol0);
     43     ScopedJavaUnicodeString infinity(env, infinity0);
     44     ScopedJavaUnicodeString internationalCurrencySymbol(env, internationalCurrencySymbol0);
     45     ScopedJavaUnicodeString nan(env, nan0);
     46     UnicodeString groupingSeparator(groupingSeparator0);
     47 
     48     DecimalFormatSymbols* result = new DecimalFormatSymbols;
     49     result->setSymbol(DecimalFormatSymbols::kCurrencySymbol, currencySymbol.unicodeString());
     50     result->setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, UnicodeString(decimalSeparator));
     51     result->setSymbol(DecimalFormatSymbols::kDigitSymbol, UnicodeString(digit));
     52     result->setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, groupingSeparator);
     53     result->setSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol, groupingSeparator);
     54     result->setSymbol(DecimalFormatSymbols::kInfinitySymbol, infinity.unicodeString());
     55     result->setSymbol(DecimalFormatSymbols::kIntlCurrencySymbol, internationalCurrencySymbol.unicodeString());
     56     result->setSymbol(DecimalFormatSymbols::kMinusSignSymbol, UnicodeString(minusSign));
     57     result->setSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol, UnicodeString(monetaryDecimalSeparator));
     58     result->setSymbol(DecimalFormatSymbols::kNaNSymbol, nan.unicodeString());
     59     result->setSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol, UnicodeString(patternSeparator));
     60     result->setSymbol(DecimalFormatSymbols::kPercentSymbol, UnicodeString(percent));
     61     result->setSymbol(DecimalFormatSymbols::kPerMillSymbol, UnicodeString(perMill));
     62     result->setSymbol(DecimalFormatSymbols::kZeroDigitSymbol, UnicodeString(zeroDigit));
     63     return result;
     64 }
     65 
     66 static void setDecimalFormatSymbols(JNIEnv* env, jclass, jint addr,
     67         jstring currencySymbol, jchar decimalSeparator, jchar digit,
     68         jchar groupingSeparator, jstring infinity,
     69         jstring internationalCurrencySymbol, jchar minusSign,
     70         jchar monetaryDecimalSeparator, jstring nan, jchar patternSeparator,
     71         jchar percent, jchar perMill, jchar zeroDigit) {
     72     DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env,
     73             currencySymbol, decimalSeparator, digit, groupingSeparator,
     74             infinity, internationalCurrencySymbol, minusSign,
     75             monetaryDecimalSeparator, nan, patternSeparator, percent, perMill,
     76             zeroDigit);
     77     toDecimalFormat(addr)->adoptDecimalFormatSymbols(symbols);
     78 }
     79 
     80 static jint openDecimalFormatImpl(JNIEnv* env, jclass clazz, jstring pattern0,
     81         jstring currencySymbol, jchar decimalSeparator, jchar digit,
     82         jchar groupingSeparator, jstring infinity,
     83         jstring internationalCurrencySymbol, jchar minusSign,
     84         jchar monetaryDecimalSeparator, jstring nan, jchar patternSeparator,
     85         jchar percent, jchar perMill, jchar zeroDigit) {
     86     if (pattern0 == NULL) {
     87         jniThrowNullPointerException(env, NULL);
     88         return 0;
     89     }
     90     UErrorCode status = U_ZERO_ERROR;
     91     UParseError parseError;
     92     ScopedJavaUnicodeString pattern(env, pattern0);
     93     DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env,
     94             currencySymbol, decimalSeparator, digit, groupingSeparator,
     95             infinity, internationalCurrencySymbol, minusSign,
     96             monetaryDecimalSeparator, nan, patternSeparator, percent, perMill,
     97             zeroDigit);
     98     DecimalFormat* fmt = new DecimalFormat(pattern.unicodeString(), symbols, parseError, status);
     99     if (fmt == NULL) {
    100         delete symbols;
    101     }
    102     icu4jni_error(env, status);
    103     return static_cast<jint>(reinterpret_cast<uintptr_t>(fmt));
    104 }
    105 
    106 static void closeDecimalFormatImpl(JNIEnv *env, jclass clazz, jint addr) {
    107     delete toDecimalFormat(addr);
    108 }
    109 
    110 static void setSymbol(JNIEnv* env, jclass, jint addr, jint symbol, jstring s) {
    111     const UChar* chars = env->GetStringChars(s, NULL);
    112     const int32_t charCount = env->GetStringLength(s);
    113     UErrorCode status = U_ZERO_ERROR;
    114     UNumberFormat* fmt = reinterpret_cast<UNumberFormat*>(static_cast<uintptr_t>(addr));
    115     unum_setSymbol(fmt, static_cast<UNumberFormatSymbol>(symbol), chars, charCount, &status);
    116     icu4jni_error(env, status);
    117     env->ReleaseStringChars(s, chars);
    118 }
    119 
    120 static void setAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol,
    121         jint value) {
    122 
    123     UNumberFormat *fmt = (UNumberFormat *)(int)addr;
    124 
    125     unum_setAttribute(fmt, (UNumberFormatAttribute) symbol, value);
    126 }
    127 
    128 static jint getAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol) {
    129 
    130     UNumberFormat *fmt = (UNumberFormat *)(int)addr;
    131 
    132     int res = unum_getAttribute(fmt, (UNumberFormatAttribute) symbol);
    133 
    134     return res;
    135 }
    136 
    137 static void setTextAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol,
    138         jstring text) {
    139 
    140     // the errorcode returned by unum_setTextAttribute
    141     UErrorCode status = U_ZERO_ERROR;
    142 
    143     // get the pointer to the number format
    144     UNumberFormat *fmt = (UNumberFormat *)(int)addr;
    145 
    146     const UChar *textChars = env->GetStringChars(text, NULL);
    147     int textLen = env->GetStringLength(text);
    148 
    149     unum_setTextAttribute(fmt, (UNumberFormatTextAttribute) symbol, textChars,
    150             textLen, &status);
    151 
    152     env->ReleaseStringChars(text, textChars);
    153 
    154     icu4jni_error(env, status);
    155 }
    156 
    157 static jstring getTextAttribute(JNIEnv *env, jclass clazz, jint addr,
    158         jint symbol) {
    159 
    160     uint32_t resultlength, reslenneeded;
    161 
    162     // the errorcode returned by unum_getTextAttribute
    163     UErrorCode status = U_ZERO_ERROR;
    164 
    165     // get the pointer to the number format
    166     UNumberFormat *fmt = (UNumberFormat *)(int)addr;
    167 
    168     UChar* result = NULL;
    169     resultlength=0;
    170 
    171     // find out how long the result will be
    172     reslenneeded=unum_getTextAttribute(fmt, (UNumberFormatTextAttribute) symbol,
    173             result, resultlength, &status);
    174 
    175     result = NULL;
    176     if(status==U_BUFFER_OVERFLOW_ERROR) {
    177         status=U_ZERO_ERROR;
    178         resultlength=reslenneeded+1;
    179         result=(UChar*)malloc(sizeof(UChar) * resultlength);
    180         reslenneeded=unum_getTextAttribute(fmt,
    181                 (UNumberFormatTextAttribute) symbol, result, resultlength,
    182                 &status);
    183     }
    184     if (icu4jni_error(env, status) != FALSE) {
    185         return NULL;
    186     }
    187 
    188     jstring res = env->NewString(result, reslenneeded);
    189 
    190     free(result);
    191 
    192     return res;
    193 }
    194 
    195 static void applyPatternImpl(JNIEnv *env, jclass clazz, jint addr, jboolean localized, jstring pattern0) {
    196     if (pattern0 == NULL) {
    197         jniThrowNullPointerException(env, NULL);
    198         return;
    199     }
    200     ScopedJavaUnicodeString pattern(env, pattern0);
    201     DecimalFormat* fmt = toDecimalFormat(addr);
    202     UErrorCode status = U_ZERO_ERROR;
    203     if (localized) {
    204         fmt->applyLocalizedPattern(pattern.unicodeString(), status);
    205     } else {
    206         fmt->applyPattern(pattern.unicodeString(), status);
    207     }
    208     icu4jni_error(env, status);
    209 }
    210 
    211 static jstring toPatternImpl(JNIEnv *env, jclass, jint addr, jboolean localized) {
    212     DecimalFormat* fmt = toDecimalFormat(addr);
    213     UnicodeString pattern;
    214     if (localized) {
    215         fmt->toLocalizedPattern(pattern);
    216     } else {
    217         fmt->toPattern(pattern);
    218     }
    219     return env->NewString(pattern.getBuffer(), pattern.length());
    220 }
    221 
    222 template <typename T>
    223 static jstring format(JNIEnv *env, jint addr, jobject field, jstring fieldType, jobject attributes, T val) {
    224     UErrorCode status = U_ZERO_ERROR;
    225 
    226     DecimalFormat::AttributeBuffer attrBuffer;
    227     attrBuffer.buffer = NULL;
    228     DecimalFormat::AttributeBuffer* attrBufferPtr = NULL;
    229     if (attributes != NULL || (fieldType != NULL && field != NULL)) {
    230         attrBufferPtr = &attrBuffer;
    231         // ICU requires that this is dynamically allocated and non-zero size.
    232         // ICU grows it in chunks of 128 bytes, so that's a reasonable initial size.
    233         attrBuffer.bufferSize = 128;
    234         attrBuffer.buffer = new char[attrBuffer.bufferSize];
    235         attrBuffer.buffer[0] = '\0';
    236     }
    237 
    238     FieldPosition fp;
    239     fp.setField(FieldPosition::DONT_CARE);
    240 
    241     UnicodeString str;
    242     DecimalFormat* fmt = toDecimalFormat(addr);
    243     fmt->format(val, str, fp, attrBufferPtr);
    244 
    245     if (attrBufferPtr && strlen(attrBuffer.buffer) > 0) {
    246         // check if we want to get all attributes
    247         if (attributes != NULL) {
    248             jstring attrString = env->NewStringUTF(attrBuffer.buffer + 1);  // cut off the leading ';'
    249             jclass stringBufferClass = env->FindClass("java/lang/StringBuffer");
    250             jmethodID appendMethodID = env->GetMethodID(stringBufferClass, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
    251             env->CallObjectMethod(attributes, appendMethodID, attrString);
    252         }
    253 
    254         // check if we want one special attribute returned in the given FieldPos
    255         if (fieldType != NULL && field != NULL) {
    256             const char* fieldName = env->GetStringUTFChars(fieldType, NULL);
    257 
    258             const char* delimiter = ";";
    259             char* context = NULL;
    260             char* resattr = strtok_r(attrBuffer.buffer, delimiter, &context);
    261 
    262             while (resattr != NULL && strcmp(resattr, fieldName) != 0) {
    263                 resattr = strtok_r(NULL, delimiter, &context);
    264             }
    265 
    266             if (resattr != NULL && strcmp(resattr, fieldName) == 0) {
    267                 resattr = strtok_r(NULL, delimiter, &context);
    268                 int begin = (int) strtol(resattr, NULL, 10);
    269                 resattr = strtok_r(NULL, delimiter, &context);
    270                 int end = (int) strtol(resattr, NULL, 10);
    271 
    272                 jclass fieldPositionClass = env->FindClass("java/text/FieldPosition");
    273                 jmethodID setBeginIndexMethodID = env->GetMethodID(fieldPositionClass, "setBeginIndex", "(I)V");
    274                 jmethodID setEndIndexMethodID = env->GetMethodID(fieldPositionClass, "setEndIndex", "(I)V");
    275                 env->CallVoidMethod(field, setBeginIndexMethodID, (jint) begin);
    276                 env->CallVoidMethod(field, setEndIndexMethodID, (jint) end);
    277             }
    278             env->ReleaseStringUTFChars(fieldType, fieldName);
    279         }
    280     }
    281 
    282     jstring result = env->NewString(str.getBuffer(), str.length());
    283     delete[] attrBuffer.buffer;
    284     return result;
    285 }
    286 
    287 static jstring formatLong(JNIEnv* env, jclass, jint addr, jlong value,
    288         jobject field, jstring fieldType, jobject attributes) {
    289     int64_t longValue = value;
    290     return format(env, addr, field, fieldType, attributes, longValue);
    291 }
    292 
    293 static jstring formatDouble(JNIEnv* env, jclass, jint addr, jdouble value,
    294         jobject field, jstring fieldType, jobject attributes) {
    295     double doubleValue = value;
    296     return format(env, addr, field, fieldType, attributes, doubleValue);
    297 }
    298 
    299 static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring value,
    300         jobject field, jstring fieldType, jobject attributes, jint scale) {
    301 
    302     // const char * valueUTF = env->GetStringUTFChars(value, NULL);
    303     // LOGI("ENTER formatDigitList: %s, scale: %d", valueUTF, scale);
    304     // env->ReleaseStringUTFChars(value, valueUTF);
    305 
    306     if (scale < 0) {
    307         icu4jni_error(env, U_ILLEGAL_ARGUMENT_ERROR);
    308         return NULL;
    309     }
    310 
    311     const char * fieldName = NULL;
    312     if(fieldType != NULL) {
    313         fieldName = env->GetStringUTFChars(fieldType, NULL);
    314     }
    315 
    316     uint32_t reslenneeded;
    317 
    318     // prepare digit list
    319 
    320     const char *valueChars = env->GetStringUTFChars(value, NULL);
    321 
    322     bool isInteger = (scale == 0);
    323     bool isPositive = (*valueChars != '-');
    324 
    325     // skip the '-' if the number is negative
    326     const char *digits = (isPositive ? valueChars : valueChars + 1);
    327     int length = strlen(digits);
    328 
    329     DecimalFormat* fmt = toDecimalFormat(addr);
    330 
    331     // The length of our digit list buffer must be the actual string length + 3,
    332     // because ICU will append some additional characters at the head and at the
    333     // tail of the string, in order to keep strtod() happy:
    334     //
    335     // - The sign "+" or "-" is appended at the head
    336     // - The exponent "e" and the "\0" terminator is appended at the tail
    337     //
    338     // In retrospect, the changes to ICU's DigitList that were necessary for
    339     // big numbers look a bit hacky. It would make sense to rework all this
    340     // once ICU 4.x has been integrated into Android. Ideally, big number
    341     // support would make it into ICU itself, so we don't need our private
    342     // fix anymore.
    343     DigitList digitList(length + 3);
    344     digitList.fCount = length;
    345     strcpy(digitList.fDigits, digits);
    346     env->ReleaseStringUTFChars(value, valueChars);
    347 
    348     digitList.fDecimalAt = digitList.fCount - scale;
    349     digitList.fIsPositive = isPositive;
    350     digitList.fRoundingMode = fmt->getRoundingMode();
    351     digitList.round(fmt->getMaximumFractionDigits() + digitList.fDecimalAt);
    352 
    353     UChar *result = NULL;
    354 
    355     FieldPosition fp;
    356     fp.setField(FieldPosition::DONT_CARE);
    357     fp.setBeginIndex(0);
    358     fp.setEndIndex(0);
    359 
    360     UErrorCode status = U_ZERO_ERROR;
    361 
    362     DecimalFormat::AttributeBuffer *attrBuffer = NULL;
    363     attrBuffer = (DecimalFormat::AttributeBuffer *) calloc(sizeof(DecimalFormat::AttributeBuffer), 1);
    364     attrBuffer->bufferSize = 128;
    365     attrBuffer->buffer = (char *) calloc(129 * sizeof(char), 1);
    366 
    367     UnicodeString res;
    368 
    369     fmt->subformat(res, fp, attrBuffer, digitList, isInteger);
    370 
    371     reslenneeded = res.extract(NULL, 0, status);
    372 
    373     if(status==U_BUFFER_OVERFLOW_ERROR) {
    374         status=U_ZERO_ERROR;
    375 
    376         result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1));
    377 
    378         res.extract(result, reslenneeded + 1, status);
    379 
    380         if (icu4jni_error(env, status) != FALSE) {
    381             if(fieldType != NULL) {
    382                 env->ReleaseStringUTFChars(fieldType, fieldName);
    383             }
    384             free(result);
    385             free(attrBuffer->buffer);
    386             free(attrBuffer);
    387             return NULL;
    388         }
    389 
    390     } else {
    391         if(fieldType != NULL) {
    392             env->ReleaseStringUTFChars(fieldType, fieldName);
    393         }
    394         free(attrBuffer->buffer);
    395         free(attrBuffer);
    396         return NULL;
    397     }
    398 
    399     int attrLength = (strlen(attrBuffer->buffer) + 1 );
    400 
    401     if(attrLength > 1) {
    402 
    403         // check if we want to get all attributes
    404         if(attributes != NULL) {
    405             // prepare the classes and method ids
    406             const char * stringBufferClassName = "java/lang/StringBuffer";
    407             jclass stringBufferClass = env->FindClass(stringBufferClassName);
    408             jmethodID appendMethodID = env->GetMethodID(stringBufferClass,
    409                     "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
    410 
    411             jstring attrString = env->NewStringUTF(attrBuffer->buffer + 1);  // cut off the leading ';'
    412             env->CallObjectMethod(attributes, appendMethodID, attrString);
    413         }
    414 
    415         // check if we want one special attribute returned in the given FieldPos
    416         if(fieldName != NULL && field != NULL) {
    417             const char *delimiter = ";";
    418             int begin;
    419             int end;
    420             char * resattr;
    421             resattr = strtok(attrBuffer->buffer, delimiter);
    422 
    423             while(resattr != NULL && strcmp(resattr, fieldName) != 0) {
    424                 resattr = strtok(NULL, delimiter);
    425             }
    426 
    427             if(resattr != NULL && strcmp(resattr, fieldName) == 0) {
    428 
    429                 // prepare the classes and method ids
    430                 const char * fieldPositionClassName =
    431                         "java/text/FieldPosition";
    432                 jclass fieldPositionClass = env->FindClass(
    433                         fieldPositionClassName);
    434                 jmethodID setBeginIndexMethodID = env->GetMethodID(
    435                         fieldPositionClass, "setBeginIndex", "(I)V");
    436                 jmethodID setEndIndexMethodID = env->GetMethodID(
    437                        fieldPositionClass, "setEndIndex", "(I)V");
    438 
    439 
    440                 resattr = strtok(NULL, delimiter);
    441                 begin = (int) strtol(resattr, NULL, 10);
    442                 resattr = strtok(NULL, delimiter);
    443                 end = (int) strtol(resattr, NULL, 10);
    444 
    445                 env->CallVoidMethod(field, setBeginIndexMethodID, (jint) begin);
    446                 env->CallVoidMethod(field, setEndIndexMethodID, (jint) end);
    447             }
    448         }
    449     }
    450 
    451     if(fieldType != NULL) {
    452         env->ReleaseStringUTFChars(fieldType, fieldName);
    453     }
    454 
    455     jstring resulting = env->NewString(result, reslenneeded);
    456 
    457     free(attrBuffer->buffer);
    458     free(attrBuffer);
    459     free(result);
    460     // const char * resultUTF = env->GetStringUTFChars(resulting, NULL);
    461     // LOGI("RETURN formatDigitList: %s", resultUTF);
    462     // env->ReleaseStringUTFChars(resulting, resultUTF);
    463 
    464     return resulting;
    465 }
    466 
    467 static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text,
    468         jobject position) {
    469     // TODO: cache these?
    470     jclass parsePositionClass = env->FindClass("java/text/ParsePosition");
    471     jclass longClass =  env->FindClass("java/lang/Long");
    472     jclass doubleClass =  env->FindClass("java/lang/Double");
    473     jclass bigDecimalClass = env->FindClass("java/math/BigDecimal");
    474     jclass bigIntegerClass = env->FindClass("java/math/BigInteger");
    475 
    476     jmethodID getIndexMethodID = env->GetMethodID(parsePositionClass,
    477             "getIndex", "()I");
    478     jmethodID setIndexMethodID = env->GetMethodID(parsePositionClass,
    479             "setIndex", "(I)V");
    480     jmethodID setErrorIndexMethodID = env->GetMethodID(parsePositionClass,
    481             "setErrorIndex", "(I)V");
    482 
    483     jmethodID longInitMethodID = env->GetMethodID(longClass, "<init>", "(J)V");
    484     jmethodID dblInitMethodID = env->GetMethodID(doubleClass, "<init>", "(D)V");
    485     jmethodID bigDecimalInitMethodID = env->GetMethodID(bigDecimalClass, "<init>", "(Ljava/math/BigInteger;I)V");
    486     jmethodID bigIntegerInitMethodID = env->GetMethodID(bigIntegerClass, "<init>", "(Ljava/lang/String;)V");
    487     jmethodID doubleValueMethodID = env->GetMethodID(bigDecimalClass, "doubleValue", "()D");
    488 
    489     // make sure the ParsePosition is valid. Actually icu4c would parse a number
    490     // correctly even if the parsePosition is set to -1, but since the RI fails
    491     // for that case we have to fail too
    492     int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL);
    493     const int strlength = env->GetStringLength(text);
    494     if(parsePos < 0 || parsePos > strlength) {
    495         return NULL;
    496     }
    497 
    498     ParsePosition pp;
    499     pp.setIndex(parsePos);
    500 
    501     DigitList digits;
    502 
    503     UNumberFormat *fmt = (UNumberFormat *)(int)addr;
    504     Formattable res;
    505     bool resultAssigned;
    506     jchar *str = (UChar *)env->GetStringChars(text, NULL);
    507     const UnicodeString src((UChar*)str, strlength, strlength);
    508     ((const DecimalFormat*)fmt)->parse(src, resultAssigned, res, pp, FALSE, digits);
    509     env->ReleaseStringChars(text, str);
    510 
    511     if(pp.getErrorIndex() == -1) {
    512         parsePos = pp.getIndex();
    513     } else {
    514         env->CallVoidMethod(position, setErrorIndexMethodID,
    515                 (jint) pp.getErrorIndex());
    516         return NULL;
    517     }
    518 
    519     Formattable::Type numType = res.getType();
    520     UErrorCode fmtStatus;
    521 
    522     double resultDouble;
    523     long resultLong;
    524     int64_t resultInt64;
    525     jstring resultStr;
    526     jobject resultObject1, resultObject2;
    527 
    528     if (resultAssigned)
    529     {
    530         switch(numType) {
    531         case Formattable::kDouble:
    532             resultDouble = res.getDouble();
    533             env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
    534             return env->NewObject(doubleClass, dblInitMethodID,
    535                     (jdouble) resultDouble);
    536         case Formattable::kLong:
    537             resultLong = res.getLong();
    538             env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
    539             return env->NewObject(longClass, longInitMethodID,
    540                     (jlong) resultLong);
    541         case Formattable::kInt64:
    542             resultInt64 = res.getInt64();
    543             env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
    544             return env->NewObject(longClass, longInitMethodID,
    545                     (jlong) resultInt64);
    546         default:
    547             return NULL;
    548         }
    549     }
    550     else
    551     {
    552         int scale = digits.fCount - digits.fDecimalAt;
    553         // ATTENTION: Abuse of Implementation Knowlegde!
    554         digits.fDigits[digits.fCount] = 0;
    555         if (digits.fIsPositive) {
    556             resultStr = env->NewStringUTF(digits.fDigits);
    557         } else {
    558             if (digits.fCount == 0) {
    559                 env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
    560                 return env->NewObject(doubleClass, dblInitMethodID, (jdouble)-0);
    561             } else {
    562                 // ATTENTION: Abuse of Implementation Knowlegde!
    563                 *(digits.fDigits - 1) = '-';
    564                 resultStr = env->NewStringUTF(digits.fDigits - 1);
    565             }
    566         }
    567 
    568         env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
    569 
    570         resultObject1 = env->NewObject(bigIntegerClass, bigIntegerInitMethodID, resultStr);
    571         resultObject2 = env->NewObject(bigDecimalClass, bigDecimalInitMethodID, resultObject1, scale);
    572         return resultObject2;
    573     }
    574 }
    575 
    576 static jint cloneDecimalFormatImpl(JNIEnv *env, jclass, jint addr) {
    577     DecimalFormat* fmt = toDecimalFormat(addr);
    578     return static_cast<jint>(reinterpret_cast<uintptr_t>(fmt->clone()));
    579 }
    580 
    581 static JNINativeMethod gMethods[] = {
    582     /* name, signature, funcPtr */
    583     {"applyPatternImpl", "(IZLjava/lang/String;)V", (void*) applyPatternImpl},
    584     {"cloneDecimalFormatImpl", "(I)I", (void*) cloneDecimalFormatImpl},
    585     {"closeDecimalFormatImpl", "(I)V", (void*) closeDecimalFormatImpl},
    586     {"format", "(IDLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;", (void*) formatDouble},
    587     {"format", "(IJLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;", (void*) formatLong},
    588     {"format", "(ILjava/lang/String;Ljava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;I)Ljava/lang/String;", (void*) formatDigitList},
    589     {"getAttribute", "(II)I", (void*) getAttribute},
    590     {"getTextAttribute", "(II)Ljava/lang/String;", (void*) getTextAttribute},
    591     {"openDecimalFormatImpl", "(Ljava/lang/String;Ljava/lang/String;CCCLjava/lang/String;Ljava/lang/String;CCLjava/lang/String;CCCC)I", (void*) openDecimalFormatImpl},
    592     {"parse", "(ILjava/lang/String;Ljava/text/ParsePosition;)Ljava/lang/Number;", (void*) parse},
    593     {"setAttribute", "(III)V", (void*) setAttribute},
    594     {"setDecimalFormatSymbols", "(ILjava/lang/String;CCCLjava/lang/String;Ljava/lang/String;CCLjava/lang/String;CCCC)V", (void*) setDecimalFormatSymbols},
    595     {"setSymbol", "(IILjava/lang/String;)V", (void*) setSymbol},
    596     {"setTextAttribute", "(IILjava/lang/String;)V", (void*) setTextAttribute},
    597     {"toPatternImpl", "(IZ)Ljava/lang/String;", (void*) toPatternImpl},
    598 };
    599 int register_com_ibm_icu4jni_text_NativeDecimalFormat(JNIEnv* env) {
    600     return jniRegisterNativeMethods(env,
    601             "com/ibm/icu4jni/text/NativeDecimalFormat", gMethods,
    602             NELEM(gMethods));
    603 }
    604