blob: 606f2cce424c4051bb49bd7126630af0881f16ad [file] [log] [blame]
The Android Open Source Projectadc854b2009-03-03 19:28:47 -08001/*
Elliott Hughesb56d4f12010-01-21 10:52:50 -08002 * Copyright (C) 2006 The Android Open Source Project
The Android Open Source Projectadc854b2009-03-03 19:28:47 -08003 *
Elliott Hughesb56d4f12010-01-21 10:52:50 -08004 * 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 Projectadc854b2009-03-03 19:28:47 -08007 *
Elliott Hughesb56d4f12010-01-21 10:52:50 -08008 * http://www.apache.org/licenses/LICENSE-2.0
The Android Open Source Projectadc854b2009-03-03 19:28:47 -08009 *
Elliott Hughesb56d4f12010-01-21 10:52:50 -080010 * 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 Projectadc854b2009-03-03 19:28:47 -080015 */
16
Elliott Hughesf2d50622010-01-25 23:13:46 -080017#define LOG_TAG "NativeDecimalFormat"
Elliott Hughes757a7942010-04-16 14:14:28 -070018
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080019#include "JNIHelp.h"
Elliott Hughesf2d50622010-01-25 23:13:46 -080020#include "cutils/log.h"
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080021#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 Hughesd5344fe2010-01-28 12:18:39 -080028#include "ScopedJavaUnicodeString.h"
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080029#include <stdlib.h>
30#include <string.h>
Elliott Hughes1698d142010-01-22 18:22:53 -080031
Elliott Hughesd5344fe2010-01-28 12:18:39 -080032static DecimalFormat* toDecimalFormat(jint addr) {
Elliott Hughesf2d50622010-01-25 23:13:46 -080033 return reinterpret_cast<DecimalFormat*>(static_cast<uintptr_t>(addr));
34}
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080035
Elliott Hughes5d593ea2010-01-28 17:47:00 -080036static 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
66static 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 Carlstrom44e0e562010-05-06 23:44:16 -070080static jint openDecimalFormatImpl(JNIEnv* env, jclass, jstring pattern0,
Elliott Hughes5d593ea2010-01-28 17:47:00 -080081 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 Hughesd5344fe2010-01-28 12:18:39 -080086 if (pattern0 == NULL) {
Elliott Hughesda4f31d2010-01-28 13:43:39 -080087 jniThrowNullPointerException(env, NULL);
Elliott Hughesf2d50622010-01-25 23:13:46 -080088 return 0;
89 }
Elliott Hughesf2d50622010-01-25 23:13:46 -080090 UErrorCode status = U_ZERO_ERROR;
Elliott Hughesd5344fe2010-01-28 12:18:39 -080091 UParseError parseError;
92 ScopedJavaUnicodeString pattern(env, pattern0);
Elliott Hughes5d593ea2010-01-28 17:47:00 -080093 DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env,
94 currencySymbol, decimalSeparator, digit, groupingSeparator,
95 infinity, internationalCurrencySymbol, minusSign,
96 monetaryDecimalSeparator, nan, patternSeparator, percent, perMill,
97 zeroDigit);
Elliott Hughesd5344fe2010-01-28 12:18:39 -080098 DecimalFormat* fmt = new DecimalFormat(pattern.unicodeString(), symbols, parseError, status);
99 if (fmt == NULL) {
100 delete symbols;
101 }
Elliott Hughesf2d50622010-01-25 23:13:46 -0800102 icu4jni_error(env, status);
103 return static_cast<jint>(reinterpret_cast<uintptr_t>(fmt));
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800104}
105
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700106static void closeDecimalFormatImpl(JNIEnv*, jclass, jint addr) {
Elliott Hughesd5344fe2010-01-28 12:18:39 -0800107 delete toDecimalFormat(addr);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800108}
109
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700110static void setRoundingMode(JNIEnv*, jclass, jint addr, jint mode, jdouble increment) {
Elliott Hughes42db7d12010-03-18 17:00:07 -0700111 DecimalFormat* fmt = toDecimalFormat(addr);
112 fmt->setRoundingMode(static_cast<DecimalFormat::ERoundingMode>(mode));
113 fmt->setRoundingIncrement(increment);
114}
115
Elliott Hughes5d593ea2010-01-28 17:47:00 -0800116static void setSymbol(JNIEnv* env, jclass, jint addr, jint symbol, jstring s) {
Elliott Hughes64e410f2010-01-04 15:23:25 -0800117 const UChar* chars = env->GetStringChars(s, NULL);
118 const int32_t charCount = env->GetStringLength(s);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800119 UErrorCode status = U_ZERO_ERROR;
Elliott Hughes5d593ea2010-01-28 17:47:00 -0800120 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 Projectadc854b2009-03-03 19:28:47 -0800124}
125
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700126static void setAttribute(JNIEnv*, jclass, jint addr, jint symbol,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800127 jint value) {
128
129 UNumberFormat *fmt = (UNumberFormat *)(int)addr;
130
131 unum_setAttribute(fmt, (UNumberFormatAttribute) symbol, value);
132}
133
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700134static jint getAttribute(JNIEnv*, jclass, jint addr, jint symbol) {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800135
136 UNumberFormat *fmt = (UNumberFormat *)(int)addr;
137
138 int res = unum_getAttribute(fmt, (UNumberFormatAttribute) symbol);
139
140 return res;
141}
142
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700143static void setTextAttribute(JNIEnv* env, jclass, jint addr, jint symbol,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800144 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 Hughesc742cad2009-09-11 10:58:44 -0700160 icu4jni_error(env, status);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800161}
162
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700163static jstring getTextAttribute(JNIEnv* env, jclass, jint addr,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800164 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 Hughesc742cad2009-09-11 10:58:44 -0700190 if (icu4jni_error(env, status) != FALSE) {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800191 return NULL;
192 }
193
194 jstring res = env->NewString(result, reslenneeded);
195
196 free(result);
197
198 return res;
199}
200
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700201static void applyPatternImpl(JNIEnv* env, jclass, jint addr, jboolean localized, jstring pattern0) {
Elliott Hughesd5344fe2010-01-28 12:18:39 -0800202 if (pattern0 == NULL) {
Elliott Hughesda4f31d2010-01-28 13:43:39 -0800203 jniThrowNullPointerException(env, NULL);
Elliott Hughesf2d50622010-01-25 23:13:46 -0800204 return;
205 }
Elliott Hughesd5344fe2010-01-28 12:18:39 -0800206 ScopedJavaUnicodeString pattern(env, pattern0);
207 DecimalFormat* fmt = toDecimalFormat(addr);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800208 UErrorCode status = U_ZERO_ERROR;
Elliott Hughesd5344fe2010-01-28 12:18:39 -0800209 if (localized) {
210 fmt->applyLocalizedPattern(pattern.unicodeString(), status);
211 } else {
212 fmt->applyPattern(pattern.unicodeString(), status);
213 }
Elliott Hughesc742cad2009-09-11 10:58:44 -0700214 icu4jni_error(env, status);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800215}
216
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700217static jstring toPatternImpl(JNIEnv* env, jclass, jint addr, jboolean localized) {
Elliott Hughesf2d50622010-01-25 23:13:46 -0800218 DecimalFormat* fmt = toDecimalFormat(addr);
219 UnicodeString pattern;
220 if (localized) {
221 fmt->toLocalizedPattern(pattern);
222 } else {
223 fmt->toPattern(pattern);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800224 }
Elliott Hughesf2d50622010-01-25 23:13:46 -0800225 return env->NewString(pattern.getBuffer(), pattern.length());
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800226}
227
Elliott Hughesb56d4f12010-01-21 10:52:50 -0800228template <typename T>
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700229static jstring format(JNIEnv* env, jint addr, jobject field, jstring fieldType, jobject attributes, T val) {
Elliott Hughesb56d4f12010-01-21 10:52:50 -0800230 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 Projectadc854b2009-03-03 19:28:47 -0800240 }
241
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800242 FieldPosition fp;
243 fp.setField(FieldPosition::DONT_CARE);
244
Elliott Hughesb56d4f12010-01-21 10:52:50 -0800245 UnicodeString str;
Elliott Hughesf2d50622010-01-25 23:13:46 -0800246 DecimalFormat* fmt = toDecimalFormat(addr);
Elliott Hughesb56d4f12010-01-21 10:52:50 -0800247 fmt->format(val, str, fp, attrBufferPtr);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800248
Elliott Hughesb56d4f12010-01-21 10:52:50 -0800249 if (attrBufferPtr && strlen(attrBuffer.buffer) > 0) {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800250 // check if we want to get all attributes
Elliott Hughesb56d4f12010-01-21 10:52:50 -0800251 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 Projectadc854b2009-03-03 19:28:47 -0800255 env->CallObjectMethod(attributes, appendMethodID, attrString);
256 }
257
258 // check if we want one special attribute returned in the given FieldPos
Elliott Hughesb56d4f12010-01-21 10:52:50 -0800259 if (fieldType != NULL && field != NULL) {
260 const char* fieldName = env->GetStringUTFChars(fieldType, NULL);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800261
Elliott Hughesb56d4f12010-01-21 10:52:50 -0800262 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 Projectadc854b2009-03-03 19:28:47 -0800268 }
269
Elliott Hughesb56d4f12010-01-21 10:52:50 -0800270 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 Projectadc854b2009-03-03 19:28:47 -0800275
Elliott Hughesb56d4f12010-01-21 10:52:50 -0800276 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 Projectadc854b2009-03-03 19:28:47 -0800279 env->CallVoidMethod(field, setBeginIndexMethodID, (jint) begin);
280 env->CallVoidMethod(field, setEndIndexMethodID, (jint) end);
281 }
Elliott Hughesb56d4f12010-01-21 10:52:50 -0800282 env->ReleaseStringUTFChars(fieldType, fieldName);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800283 }
284 }
285
Elliott Hughesf2d50622010-01-25 23:13:46 -0800286 jstring result = env->NewString(str.getBuffer(), str.length());
Elliott Hughesb56d4f12010-01-21 10:52:50 -0800287 delete[] attrBuffer.buffer;
288 return result;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800289}
290
Elliott Hughesb56d4f12010-01-21 10:52:50 -0800291static jstring formatLong(JNIEnv* env, jclass, jint addr, jlong value,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800292 jobject field, jstring fieldType, jobject attributes) {
Elliott Hughesb56d4f12010-01-21 10:52:50 -0800293 int64_t longValue = value;
294 return format(env, addr, field, fieldType, attributes, longValue);
295}
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800296
Elliott Hughesb56d4f12010-01-21 10:52:50 -0800297static 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 Projectadc854b2009-03-03 19:28:47 -0800301}
302
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700303static jstring formatDigitList(JNIEnv* env, jclass, jint addr, jstring value,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800304 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 Hughesc742cad2009-09-11 10:58:44 -0700311 icu4jni_error(env, U_ILLEGAL_ARGUMENT_ERROR);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800312 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 Hughesf2d50622010-01-25 23:13:46 -0800333 DecimalFormat* fmt = toDecimalFormat(addr);
Jesse Wilson1bf75222009-10-12 14:07:16 -0700334
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800335 // 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 Wilson1bf75222009-10-12 14:07:16 -0700354 digitList.fRoundingMode = fmt->getRoundingMode();
355 digitList.round(fmt->getMaximumFractionDigits() + digitList.fDecimalAt);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800356
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 Projectadc854b2009-03-03 19:28:47 -0800371 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 Hughesc742cad2009-09-11 10:58:44 -0700384 if (icu4jni_error(env, status) != FALSE) {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800385 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 Carlstrom44e0e562010-05-06 23:44:16 -0700471static jobject parse(JNIEnv* env, jclass, jint addr, jstring text,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800472 jobject position) {
Elliott Hughes80e88aa2009-09-09 18:15:23 -0700473 // 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 Projectadc854b2009-03-03 19:28:47 -0800479
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 Projectadc854b2009-03-03 19:28:47 -0800491
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800492 // 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 Hughes80e88aa2009-09-09 18:15:23 -0700495 int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL);
496 const int strlength = env->GetStringLength(text);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800497 if(parsePos < 0 || parsePos > strlength) {
498 return NULL;
499 }
Elliott Hughes80e88aa2009-09-09 18:15:23 -0700500
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800501 ParsePosition pp;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800502 pp.setIndex(parsePos);
503
504 DigitList digits;
Elliott Hughes80e88aa2009-09-09 18:15:23 -0700505
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 Projectadc854b2009-03-03 19:28:47 -0800511 ((const DecimalFormat*)fmt)->parse(src, resultAssigned, res, pp, FALSE, digits);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800512 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 Hughes80e88aa2009-09-09 18:15:23 -0700522 Formattable::Type numType = res.getType();
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800523
524 double resultDouble;
525 long resultLong;
526 int64_t resultInt64;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800527 jstring resultStr;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800528 jobject resultObject1, resultObject2;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800529
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 Carlstrom44e0e562010-05-06 23:44:16 -0700578static jint cloneDecimalFormatImpl(JNIEnv*, jclass, jint addr) {
Elliott Hughesf2d50622010-01-25 23:13:46 -0800579 DecimalFormat* fmt = toDecimalFormat(addr);
580 return static_cast<jint>(reinterpret_cast<uintptr_t>(fmt->clone()));
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800581}
582
583static JNINativeMethod gMethods[] = {
Elliott Hughesd5344fe2010-01-28 12:18:39 -0800584 {"applyPatternImpl", "(IZLjava/lang/String;)V", (void*) applyPatternImpl},
585 {"cloneDecimalFormatImpl", "(I)I", (void*) cloneDecimalFormatImpl},
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800586 {"closeDecimalFormatImpl", "(I)V", (void*) closeDecimalFormatImpl},
Elliott Hughesd5344fe2010-01-28 12:18:39 -0800587 {"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 Hughesd5344fe2010-01-28 12:18:39 -0800591 {"getTextAttribute", "(II)Ljava/lang/String;", (void*) getTextAttribute},
Elliott Hughes5d593ea2010-01-28 17:47:00 -0800592 {"openDecimalFormatImpl", "(Ljava/lang/String;Ljava/lang/String;CCCLjava/lang/String;Ljava/lang/String;CCLjava/lang/String;CCCC)I", (void*) openDecimalFormatImpl},
Elliott Hughesd5344fe2010-01-28 12:18:39 -0800593 {"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 Hughes5d593ea2010-01-28 17:47:00 -0800596 {"setSymbol", "(IILjava/lang/String;)V", (void*) setSymbol},
Elliott Hughes42db7d12010-03-18 17:00:07 -0700597 {"setRoundingMode", "(IID)V", (void*) setRoundingMode},
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800598 {"setTextAttribute", "(IILjava/lang/String;)V", (void*) setTextAttribute},
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800599 {"toPatternImpl", "(IZ)Ljava/lang/String;", (void*) toPatternImpl},
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800600};
601int register_com_ibm_icu4jni_text_NativeDecimalFormat(JNIEnv* env) {
Elliott Hughesc08f9fb2010-04-16 17:44:12 -0700602 return jniRegisterNativeMethods(env, "com/ibm/icu4jni/text/NativeDecimalFormat", gMethods,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800603 NELEM(gMethods));
604}