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