1 /*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Internal native functions. All of the functions defined here make
5 * direct use of VM functions or data structures, so they can't be written
6 * with JNI and shouldn't really be in a shared library.
7 *
8 * All functions here either complete quickly or are used to enter a wait
9 * state, so we don't set the thread status to THREAD_NATIVE when executing
10 * these methods. This means that the GC will wait for these functions
11 * to finish. DO NOT perform long operations or blocking I/O in here.
12 *
13 * In some cases we're following the division of labor defined by GNU
14 * ClassPath, e.g. java.lang.Thread has "Thread" and "VMThread", with
15 * the VM-specific behavior isolated in VMThread.
16 */
17
18 #define LOG_TAG "DecimalFormatInterface"
19 #include "JNIHelp.h"
20 #include "AndroidSystemNatives.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 <stdlib.h>
29 #include <string.h>
30 #include "cutils/log.h"
31
32
icuError(JNIEnv * env,UErrorCode errorcode)33 static UBool icuError(JNIEnv *env, UErrorCode errorcode)
34 {
35 const char *emsg = u_errorName(errorcode);
36 jclass exception;
37
38 if (U_FAILURE(errorcode)) {// errorcode > U_ZERO_ERROR && errorcode < U_ERROR_LIMIT) {
39 switch (errorcode) {
40 case U_ILLEGAL_ARGUMENT_ERROR :
41 exception = env->FindClass("java/lang/IllegalArgumentException");
42 break;
43 case U_INDEX_OUTOFBOUNDS_ERROR :
44 case U_BUFFER_OVERFLOW_ERROR :
45 exception = env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
46 break;
47 case U_UNSUPPORTED_ERROR :
48 exception = env->FindClass("java/lang/UnsupportedOperationException");
49 break;
50 default :
51 exception = env->FindClass("java/lang/RuntimeException");
52 }
53
54 return (env->ThrowNew(exception, emsg) != 0);
55 }
56 return 0;
57 }
58
openDecimalFormatImpl(JNIEnv * env,jclass clazz,jstring locale,jstring pattern)59 static jint openDecimalFormatImpl(JNIEnv *env, jclass clazz, jstring locale,
60 jstring pattern) {
61
62 // the errorcode returned by unum_open
63 UErrorCode status = U_ZERO_ERROR;
64
65 // prepare the pattern string for the call to unum_open
66 const UChar *pattChars = env->GetStringChars(pattern, NULL);
67 int pattLen = env->GetStringLength(pattern);
68
69 // prepare the locale string for the call to unum_open
70 const char *localeChars = env->GetStringUTFChars(locale, NULL);
71
72 // open a default type number format
73 UNumberFormat *fmt = unum_open(UNUM_PATTERN_DECIMAL, pattChars, pattLen,
74 localeChars, NULL, &status);
75
76 // release the allocated strings
77 env->ReleaseStringChars(pattern, pattChars);
78 env->ReleaseStringUTFChars(locale, localeChars);
79
80 // check for an error
81 if ( icuError(env, status) != FALSE) {
82 return 0;
83 }
84
85 // return the handle to the number format
86 return (long) fmt;
87 }
88
closeDecimalFormatImpl(JNIEnv * env,jclass clazz,jint addr)89 static void closeDecimalFormatImpl(JNIEnv *env, jclass clazz, jint addr) {
90
91 // get the pointer to the number format
92 UNumberFormat *fmt = (UNumberFormat *)(int)addr;
93
94 // close this number format
95 unum_close(fmt);
96 }
97
setSymbol(JNIEnv * env,jclass clazz,jint addr,jint symbol,jstring text)98 static void setSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol,
99 jstring text) {
100
101 // the errorcode returned by unum_setSymbol
102 UErrorCode status = U_ZERO_ERROR;
103
104 // get the pointer to the number format
105 UNumberFormat *fmt = (UNumberFormat *)(int)addr;
106
107 // prepare the symbol string for the call to unum_setSymbol
108 const UChar *textChars = env->GetStringChars(text, NULL);
109 int textLen = env->GetStringLength(text);
110
111 // set the symbol
112 unum_setSymbol(fmt, (UNumberFormatSymbol) symbol, textChars, textLen,
113 &status);
114
115 // release previously allocated space
116 env->ReleaseStringChars(text, textChars);
117
118 // check if an error occured
119 icuError(env, status);
120 }
121
getSymbol(JNIEnv * env,jclass clazz,jint addr,jint symbol)122 static jstring getSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol) {
123
124 uint32_t resultlength, reslenneeded;
125
126 // the errorcode returned by unum_getSymbol
127 UErrorCode status = U_ZERO_ERROR;
128
129 // get the pointer to the number format
130 UNumberFormat *fmt = (UNumberFormat *)(int)addr;
131
132 UChar* result = NULL;
133 resultlength=0;
134
135 // find out how long the result will be
136 reslenneeded=unum_getSymbol(fmt, (UNumberFormatSymbol) symbol, result,
137 resultlength, &status);
138
139 result = NULL;
140 if(status==U_BUFFER_OVERFLOW_ERROR) {
141 status=U_ZERO_ERROR;
142 resultlength=reslenneeded+1;
143 result=(UChar*)malloc(sizeof(UChar) * resultlength);
144 reslenneeded=unum_getSymbol(fmt, (UNumberFormatSymbol) symbol, result,
145 resultlength, &status);
146 }
147 if (icuError(env, status) != FALSE) {
148 return NULL;
149 }
150
151 jstring res = env->NewString(result, reslenneeded);
152
153 free(result);
154
155 return res;
156 }
157
setAttribute(JNIEnv * env,jclass clazz,jint addr,jint symbol,jint value)158 static void setAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol,
159 jint value) {
160
161 UNumberFormat *fmt = (UNumberFormat *)(int)addr;
162
163 unum_setAttribute(fmt, (UNumberFormatAttribute) symbol, value);
164 }
165
getAttribute(JNIEnv * env,jclass clazz,jint addr,jint symbol)166 static jint getAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol) {
167
168 UNumberFormat *fmt = (UNumberFormat *)(int)addr;
169
170 int res = unum_getAttribute(fmt, (UNumberFormatAttribute) symbol);
171
172 return res;
173 }
174
setTextAttribute(JNIEnv * env,jclass clazz,jint addr,jint symbol,jstring text)175 static void setTextAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol,
176 jstring text) {
177
178 // the errorcode returned by unum_setTextAttribute
179 UErrorCode status = U_ZERO_ERROR;
180
181 // get the pointer to the number format
182 UNumberFormat *fmt = (UNumberFormat *)(int)addr;
183
184 const UChar *textChars = env->GetStringChars(text, NULL);
185 int textLen = env->GetStringLength(text);
186
187 unum_setTextAttribute(fmt, (UNumberFormatTextAttribute) symbol, textChars,
188 textLen, &status);
189
190 env->ReleaseStringChars(text, textChars);
191
192 icuError(env, status);
193 }
194
getTextAttribute(JNIEnv * env,jclass clazz,jint addr,jint symbol)195 static jstring getTextAttribute(JNIEnv *env, jclass clazz, jint addr,
196 jint symbol) {
197
198 uint32_t resultlength, reslenneeded;
199
200 // the errorcode returned by unum_getTextAttribute
201 UErrorCode status = U_ZERO_ERROR;
202
203 // get the pointer to the number format
204 UNumberFormat *fmt = (UNumberFormat *)(int)addr;
205
206 UChar* result = NULL;
207 resultlength=0;
208
209 // find out how long the result will be
210 reslenneeded=unum_getTextAttribute(fmt, (UNumberFormatTextAttribute) symbol,
211 result, resultlength, &status);
212
213 result = NULL;
214 if(status==U_BUFFER_OVERFLOW_ERROR) {
215 status=U_ZERO_ERROR;
216 resultlength=reslenneeded+1;
217 result=(UChar*)malloc(sizeof(UChar) * resultlength);
218 reslenneeded=unum_getTextAttribute(fmt,
219 (UNumberFormatTextAttribute) symbol, result, resultlength,
220 &status);
221 }
222 if (icuError(env, status) != FALSE) {
223 return NULL;
224 }
225
226 jstring res = env->NewString(result, reslenneeded);
227
228 free(result);
229
230 return res;
231 }
232
applyPatternImpl(JNIEnv * env,jclass clazz,jint addr,jboolean localized,jstring pattern)233 static void applyPatternImpl(JNIEnv *env, jclass clazz, jint addr,
234 jboolean localized, jstring pattern) {
235
236 // the errorcode returned by unum_applyPattern
237 UErrorCode status = U_ZERO_ERROR;
238
239 // get the pointer to the number format
240 UNumberFormat *fmt = (UNumberFormat *)(int)addr;
241
242 const UChar *pattChars = env->GetStringChars(pattern, NULL);
243 int pattLen = env->GetStringLength(pattern);
244
245 unum_applyPattern(fmt, localized, pattChars, pattLen, NULL, &status);
246
247 env->ReleaseStringChars(pattern, pattChars);
248
249 icuError(env, status);
250 }
251
toPatternImpl(JNIEnv * env,jclass clazz,jint addr,jboolean localized)252 static jstring toPatternImpl(JNIEnv *env, jclass clazz, jint addr,
253 jboolean localized) {
254
255 uint32_t resultlength, reslenneeded;
256
257 // the errorcode returned by unum_toPattern
258 UErrorCode status = U_ZERO_ERROR;
259
260 // get the pointer to the number format
261 UNumberFormat *fmt = (UNumberFormat *)(int)addr;
262
263 UChar* result = NULL;
264 resultlength=0;
265
266 // find out how long the result will be
267 reslenneeded=unum_toPattern(fmt, localized, result, resultlength, &status);
268
269 result = NULL;
270 if(status==U_BUFFER_OVERFLOW_ERROR) {
271 status=U_ZERO_ERROR;
272 resultlength=reslenneeded+1;
273 result=(UChar*)malloc(sizeof(UChar) * resultlength);
274 reslenneeded=unum_toPattern(fmt, localized, result, resultlength,
275 &status);
276 }
277 if (icuError(env, status) != FALSE) {
278 return NULL;
279 }
280
281 jstring res = env->NewString(result, reslenneeded);
282
283 free(result);
284
285 return res;
286 }
287
formatLong(JNIEnv * env,jclass clazz,jint addr,jlong value,jobject field,jstring fieldType,jobject attributes)288 static jstring formatLong(JNIEnv *env, jclass clazz, jint addr, jlong value,
289 jobject field, jstring fieldType, jobject attributes) {
290
291 const char * fieldPositionClassName = "java/text/FieldPosition";
292 const char * stringBufferClassName = "java/lang/StringBuffer";
293 jclass fieldPositionClass = env->FindClass(fieldPositionClassName);
294 jclass stringBufferClass = env->FindClass(stringBufferClassName);
295 jmethodID setBeginIndexMethodID = env->GetMethodID(fieldPositionClass,
296 "setBeginIndex", "(I)V");
297 jmethodID setEndIndexMethodID = env->GetMethodID(fieldPositionClass,
298 "setEndIndex", "(I)V");
299 jmethodID appendMethodID = env->GetMethodID(stringBufferClass,
300 "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
301
302 const char * fieldName = NULL;
303
304 if(fieldType != NULL) {
305 fieldName = env->GetStringUTFChars(fieldType, NULL);
306 }
307
308 uint32_t reslenneeded;
309 int64_t val = value;
310 UChar *result = NULL;
311
312 FieldPosition fp;
313 fp.setField(FieldPosition::DONT_CARE);
314
315 UErrorCode status = U_ZERO_ERROR;
316
317 DecimalFormat::AttrBuffer attrBuffer = NULL;
318 attrBuffer = (DecimalFormat::AttrBuffer) malloc(sizeof(*attrBuffer));
319 attrBuffer->bufferSize = 128;
320 attrBuffer->buffer = (char *) malloc(129 * sizeof(char));
321 attrBuffer->buffer[0] = '\0';
322
323 DecimalFormat *fmt = (DecimalFormat *)(int)addr;
324
325 UnicodeString *res = new UnicodeString();
326
327 fmt->format(val, *res, fp, attrBuffer);
328
329 reslenneeded = res->extract(NULL, 0, status);
330
331 if(status==U_BUFFER_OVERFLOW_ERROR) {
332 status=U_ZERO_ERROR;
333
334 result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1));
335
336 res->extract(result, reslenneeded + 1, status);
337 }
338 if (icuError(env, status) != FALSE) {
339 free(attrBuffer->buffer);
340 free(attrBuffer);
341 free(result);
342 delete(res);
343 return NULL;
344 }
345
346 int attrLength = 0;
347
348 attrLength = (strlen(attrBuffer->buffer) + 1 );
349
350 if(strlen(attrBuffer->buffer) > 0) {
351
352 // check if we want to get all attributes
353 if(attributes != NULL) {
354 jstring attrString = env->NewStringUTF(attrBuffer->buffer + 1); // cut off the leading ';'
355 env->CallObjectMethod(attributes, appendMethodID, attrString);
356 }
357
358 // check if we want one special attribute returned in the given FieldPos
359 if(fieldName != NULL && field != NULL) {
360 const char *delimiter = ";";
361 int begin;
362 int end;
363 char * resattr;
364 resattr = strtok(attrBuffer->buffer, delimiter);
365
366 while(resattr != NULL && strcmp(resattr, fieldName) != 0) {
367 resattr = strtok(NULL, delimiter);
368 }
369
370 if(resattr != NULL && strcmp(resattr, fieldName) == 0) {
371 resattr = strtok(NULL, delimiter);
372 begin = (int) strtol(resattr, NULL, 10);
373 resattr = strtok(NULL, delimiter);
374 end = (int) strtol(resattr, NULL, 10);
375
376 env->CallVoidMethod(field, setBeginIndexMethodID, (jint) begin);
377 env->CallVoidMethod(field, setEndIndexMethodID, (jint) end);
378 }
379 }
380 }
381
382 if(fieldType != NULL) {
383 env->ReleaseStringUTFChars(fieldType, fieldName);
384 }
385
386 jstring resulting = env->NewString(result, reslenneeded);
387
388 free(attrBuffer->buffer);
389 free(attrBuffer);
390 free(result);
391 delete(res);
392
393 return resulting;
394 }
395
formatDouble(JNIEnv * env,jclass clazz,jint addr,jdouble value,jobject field,jstring fieldType,jobject attributes)396 static jstring formatDouble(JNIEnv *env, jclass clazz, jint addr, jdouble value,
397 jobject field, jstring fieldType, jobject attributes) {
398
399 const char * fieldPositionClassName = "java/text/FieldPosition";
400 const char * stringBufferClassName = "java/lang/StringBuffer";
401 jclass fieldPositionClass = env->FindClass(fieldPositionClassName);
402 jclass stringBufferClass = env->FindClass(stringBufferClassName);
403 jmethodID setBeginIndexMethodID = env->GetMethodID(fieldPositionClass,
404 "setBeginIndex", "(I)V");
405 jmethodID setEndIndexMethodID = env->GetMethodID(fieldPositionClass,
406 "setEndIndex", "(I)V");
407 jmethodID appendMethodID = env->GetMethodID(stringBufferClass,
408 "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
409
410 const char * fieldName = NULL;
411
412 if(fieldType != NULL) {
413 fieldName = env->GetStringUTFChars(fieldType, NULL);
414 }
415
416 uint32_t reslenneeded;
417 double val = value;
418 UChar *result = NULL;
419
420 FieldPosition fp;
421 fp.setField(FieldPosition::DONT_CARE);
422
423 UErrorCode status = U_ZERO_ERROR;
424
425 DecimalFormat::AttrBuffer attrBuffer = NULL;
426 attrBuffer = (DecimalFormat::AttrBuffer) malloc(sizeof(*attrBuffer));
427 attrBuffer->bufferSize = 128;
428 attrBuffer->buffer = (char *) malloc(129 * sizeof(char));
429 attrBuffer->buffer[0] = '\0';
430
431 DecimalFormat *fmt = (DecimalFormat *)(int)addr;
432
433 UnicodeString *res = new UnicodeString();
434
435 fmt->format(val, *res, fp, attrBuffer);
436
437 reslenneeded = res->extract(NULL, 0, status);
438
439 if(status==U_BUFFER_OVERFLOW_ERROR) {
440 status=U_ZERO_ERROR;
441
442 result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1));
443
444 res->extract(result, reslenneeded + 1, status);
445
446 }
447 if (icuError(env, status) != FALSE) {
448 free(attrBuffer->buffer);
449 free(attrBuffer);
450 free(result);
451 delete(res);
452 return NULL;
453 }
454
455 int attrLength = 0;
456
457 attrLength = (strlen(attrBuffer->buffer) + 1 );
458
459 if(strlen(attrBuffer->buffer) > 0) {
460
461 // check if we want to get all attributes
462 if(attributes != NULL) {
463 jstring attrString = env->NewStringUTF(attrBuffer->buffer + 1); // cut off the leading ';'
464 env->CallObjectMethod(attributes, appendMethodID, attrString);
465 }
466
467 // check if we want one special attribute returned in the given FieldPos
468 if(fieldName != NULL && field != NULL) {
469 const char *delimiter = ";";
470 int begin;
471 int end;
472 char * resattr;
473 resattr = strtok(attrBuffer->buffer, delimiter);
474
475 while(resattr != NULL && strcmp(resattr, fieldName) != 0) {
476 resattr = strtok(NULL, delimiter);
477 }
478
479 if(resattr != NULL && strcmp(resattr, fieldName) == 0) {
480 resattr = strtok(NULL, delimiter);
481 begin = (int) strtol(resattr, NULL, 10);
482 resattr = strtok(NULL, delimiter);
483 end = (int) strtol(resattr, NULL, 10);
484
485 env->CallVoidMethod(field, setBeginIndexMethodID, (jint) begin);
486 env->CallVoidMethod(field, setEndIndexMethodID, (jint) end);
487 }
488 }
489 }
490
491 if(fieldType != NULL) {
492 env->ReleaseStringUTFChars(fieldType, fieldName);
493 }
494
495 jstring resulting = env->NewString(result, reslenneeded);
496
497 free(attrBuffer->buffer);
498 free(attrBuffer);
499 free(result);
500 delete(res);
501
502 return resulting;
503 }
504
formatDigitList(JNIEnv * env,jclass clazz,jint addr,jstring value,jobject field,jstring fieldType,jobject attributes,jint scale)505 static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring value,
506 jobject field, jstring fieldType, jobject attributes, jint scale) {
507
508 // const char * valueUTF = env->GetStringUTFChars(value, NULL);
509 // LOGI("ENTER formatDigitList: %s, scale: %d", valueUTF, scale);
510 // env->ReleaseStringUTFChars(value, valueUTF);
511
512 if (scale < 0) {
513 icuError(env, U_ILLEGAL_ARGUMENT_ERROR);
514 return NULL;
515 }
516
517 const char * fieldName = NULL;
518 if(fieldType != NULL) {
519 fieldName = env->GetStringUTFChars(fieldType, NULL);
520 }
521
522 uint32_t reslenneeded;
523
524 // prepare digit list
525
526 const char *valueChars = env->GetStringUTFChars(value, NULL);
527
528 bool isInteger = (scale == 0);
529 bool isPositive = (*valueChars != '-');
530
531 // skip the '-' if the number is negative
532 const char *digits = (isPositive ? valueChars : valueChars + 1);
533 int length = strlen(digits);
534
535 // The length of our digit list buffer must be the actual string length + 3,
536 // because ICU will append some additional characters at the head and at the
537 // tail of the string, in order to keep strtod() happy:
538 //
539 // - The sign "+" or "-" is appended at the head
540 // - The exponent "e" and the "\0" terminator is appended at the tail
541 //
542 // In retrospect, the changes to ICU's DigitList that were necessary for
543 // big numbers look a bit hacky. It would make sense to rework all this
544 // once ICU 4.x has been integrated into Android. Ideally, big number
545 // support would make it into ICU itself, so we don't need our private
546 // fix anymore.
547 DigitList digitList(length + 3);
548 digitList.fCount = length;
549 strcpy(digitList.fDigits, digits);
550 env->ReleaseStringUTFChars(value, valueChars);
551
552 digitList.fDecimalAt = digitList.fCount - scale;
553 digitList.fIsPositive = isPositive;
554 digitList.fRoundingMode = DecimalFormat::kRoundHalfUp;
555
556 UChar *result = NULL;
557
558 FieldPosition fp;
559 fp.setField(FieldPosition::DONT_CARE);
560 fp.setBeginIndex(0);
561 fp.setEndIndex(0);
562
563 UErrorCode status = U_ZERO_ERROR;
564
565 DecimalFormat::AttributeBuffer *attrBuffer = NULL;
566 attrBuffer = (DecimalFormat::AttributeBuffer *) calloc(sizeof(DecimalFormat::AttributeBuffer), 1);
567 attrBuffer->bufferSize = 128;
568 attrBuffer->buffer = (char *) calloc(129 * sizeof(char), 1);
569
570 DecimalFormat *fmt = (DecimalFormat *)(int)addr;
571
572 UnicodeString res;
573
574 fmt->subformat(res, fp, attrBuffer, digitList, isInteger);
575
576 reslenneeded = res.extract(NULL, 0, status);
577
578 if(status==U_BUFFER_OVERFLOW_ERROR) {
579 status=U_ZERO_ERROR;
580
581 result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1));
582
583 res.extract(result, reslenneeded + 1, status);
584
585 if (icuError(env, status) != FALSE) {
586 if(fieldType != NULL) {
587 env->ReleaseStringUTFChars(fieldType, fieldName);
588 }
589 free(result);
590 free(attrBuffer->buffer);
591 free(attrBuffer);
592 return NULL;
593 }
594
595 } else {
596 if(fieldType != NULL) {
597 env->ReleaseStringUTFChars(fieldType, fieldName);
598 }
599 free(attrBuffer->buffer);
600 free(attrBuffer);
601 return NULL;
602 }
603
604 int attrLength = (strlen(attrBuffer->buffer) + 1 );
605
606 if(attrLength > 1) {
607
608 // check if we want to get all attributes
609 if(attributes != NULL) {
610 // prepare the classes and method ids
611 const char * stringBufferClassName = "java/lang/StringBuffer";
612 jclass stringBufferClass = env->FindClass(stringBufferClassName);
613 jmethodID appendMethodID = env->GetMethodID(stringBufferClass,
614 "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
615
616 jstring attrString = env->NewStringUTF(attrBuffer->buffer + 1); // cut off the leading ';'
617 env->CallObjectMethod(attributes, appendMethodID, attrString);
618 }
619
620 // check if we want one special attribute returned in the given FieldPos
621 if(fieldName != NULL && field != NULL) {
622 const char *delimiter = ";";
623 int begin;
624 int end;
625 char * resattr;
626 resattr = strtok(attrBuffer->buffer, delimiter);
627
628 while(resattr != NULL && strcmp(resattr, fieldName) != 0) {
629 resattr = strtok(NULL, delimiter);
630 }
631
632 if(resattr != NULL && strcmp(resattr, fieldName) == 0) {
633
634 // prepare the classes and method ids
635 const char * fieldPositionClassName =
636 "java/text/FieldPosition";
637 jclass fieldPositionClass = env->FindClass(
638 fieldPositionClassName);
639 jmethodID setBeginIndexMethodID = env->GetMethodID(
640 fieldPositionClass, "setBeginIndex", "(I)V");
641 jmethodID setEndIndexMethodID = env->GetMethodID(
642 fieldPositionClass, "setEndIndex", "(I)V");
643
644
645 resattr = strtok(NULL, delimiter);
646 begin = (int) strtol(resattr, NULL, 10);
647 resattr = strtok(NULL, delimiter);
648 end = (int) strtol(resattr, NULL, 10);
649
650 env->CallVoidMethod(field, setBeginIndexMethodID, (jint) begin);
651 env->CallVoidMethod(field, setEndIndexMethodID, (jint) end);
652 }
653 }
654 }
655
656 if(fieldType != NULL) {
657 env->ReleaseStringUTFChars(fieldType, fieldName);
658 }
659
660 jstring resulting = env->NewString(result, reslenneeded);
661
662 free(attrBuffer->buffer);
663 free(attrBuffer);
664 free(result);
665 // const char * resultUTF = env->GetStringUTFChars(resulting, NULL);
666 // LOGI("RETURN formatDigitList: %s", resultUTF);
667 // env->ReleaseStringUTFChars(resulting, resultUTF);
668
669 return resulting;
670 }
671
parse(JNIEnv * env,jclass clazz,jint addr,jstring text,jobject position)672 static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text,
673 jobject position) {
674
675 const char * textUTF = env->GetStringUTFChars(text, NULL);
676 env->ReleaseStringUTFChars(text, textUTF);
677
678 const char * parsePositionClassName = "java/text/ParsePosition";
679 const char * longClassName = "java/lang/Long";
680 const char * doubleClassName = "java/lang/Double";
681 const char * bigDecimalClassName = "java/math/BigDecimal";
682 const char * bigIntegerClassName = "java/math/BigInteger";
683
684 UErrorCode status = U_ZERO_ERROR;
685
686 UNumberFormat *fmt = (UNumberFormat *)(int)addr;
687
688 jchar *str = (UChar *)env->GetStringChars(text, NULL);
689 int strlength = env->GetStringLength(text);
690
691 jclass parsePositionClass = env->FindClass(parsePositionClassName);
692 jclass longClass = env->FindClass(longClassName);
693 jclass doubleClass = env->FindClass(doubleClassName);
694 jclass bigDecimalClass = env->FindClass(bigDecimalClassName);
695 jclass bigIntegerClass = env->FindClass(bigIntegerClassName);
696
697 jmethodID getIndexMethodID = env->GetMethodID(parsePositionClass,
698 "getIndex", "()I");
699 jmethodID setIndexMethodID = env->GetMethodID(parsePositionClass,
700 "setIndex", "(I)V");
701 jmethodID setErrorIndexMethodID = env->GetMethodID(parsePositionClass,
702 "setErrorIndex", "(I)V");
703
704 jmethodID longInitMethodID = env->GetMethodID(longClass, "<init>", "(J)V");
705 jmethodID dblInitMethodID = env->GetMethodID(doubleClass, "<init>", "(D)V");
706 jmethodID bigDecimalInitMethodID = env->GetMethodID(bigDecimalClass, "<init>", "(Ljava/math/BigInteger;I)V");
707 jmethodID bigIntegerInitMethodID = env->GetMethodID(bigIntegerClass, "<init>", "(Ljava/lang/String;)V");
708 jmethodID doubleValueMethodID = env->GetMethodID(bigDecimalClass, "doubleValue", "()D");
709
710 bool resultAssigned;
711 int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL);
712
713 // make sure the ParsePosition is valid. Actually icu4c would parse a number
714 // correctly even if the parsePosition is set to -1, but since the RI fails
715 // for that case we have to fail too
716 if(parsePos < 0 || parsePos > strlength) {
717 return NULL;
718 }
719
720 Formattable res;
721
722 const UnicodeString src((UChar*)str, strlength, strlength);
723 ParsePosition pp;
724
725 pp.setIndex(parsePos);
726
727 DigitList digits;
728
729 ((const DecimalFormat*)fmt)->parse(src, resultAssigned, res, pp, FALSE, digits);
730
731 env->ReleaseStringChars(text, str);
732
733 if(pp.getErrorIndex() == -1) {
734 parsePos = pp.getIndex();
735 } else {
736 env->CallVoidMethod(position, setErrorIndexMethodID,
737 (jint) pp.getErrorIndex());
738 return NULL;
739 }
740
741 Formattable::Type numType;
742 numType = res.getType();
743 UErrorCode fmtStatus;
744
745 double resultDouble;
746 long resultLong;
747 int64_t resultInt64;
748 UnicodeString resultString;
749 jstring resultStr;
750 int resLength;
751 const char * resultUTF;
752 jobject resultObject1, resultObject2;
753 jdouble doubleTest;
754 jchar * result;
755
756 if (resultAssigned)
757 {
758 switch(numType) {
759 case Formattable::kDouble:
760 resultDouble = res.getDouble();
761 env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
762 return env->NewObject(doubleClass, dblInitMethodID,
763 (jdouble) resultDouble);
764 case Formattable::kLong:
765 resultLong = res.getLong();
766 env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
767 return env->NewObject(longClass, longInitMethodID,
768 (jlong) resultLong);
769 case Formattable::kInt64:
770 resultInt64 = res.getInt64();
771 env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
772 return env->NewObject(longClass, longInitMethodID,
773 (jlong) resultInt64);
774 default:
775 return NULL;
776 }
777 }
778 else
779 {
780 int scale = digits.fCount - digits.fDecimalAt;
781 // ATTENTION: Abuse of Implementation Knowlegde!
782 digits.fDigits[digits.fCount] = 0;
783 if (digits.fIsPositive) {
784 resultStr = env->NewStringUTF(digits.fDigits);
785 } else {
786 if (digits.fCount == 0) {
787 env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
788 return env->NewObject(doubleClass, dblInitMethodID, (jdouble)-0);
789 } else {
790 // ATTENTION: Abuse of Implementation Knowlegde!
791 *(digits.fDigits - 1) = '-';
792 resultStr = env->NewStringUTF(digits.fDigits - 1);
793 }
794 }
795
796 env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
797
798 resultObject1 = env->NewObject(bigIntegerClass, bigIntegerInitMethodID, resultStr);
799 resultObject2 = env->NewObject(bigDecimalClass, bigDecimalInitMethodID, resultObject1, scale);
800 return resultObject2;
801 }
802 }
803
cloneImpl(JNIEnv * env,jclass clazz,jint addr)804 static jint cloneImpl(JNIEnv *env, jclass clazz, jint addr) {
805
806 UErrorCode status = U_ZERO_ERROR;
807
808 UNumberFormat *fmt = (UNumberFormat *)(int)addr;
809
810 UNumberFormat *result = unum_clone(fmt, &status);
811
812 if(icuError(env, status) != FALSE) {
813 return 0;
814 }
815
816 return (long) result;
817
818 }
819
820 static JNINativeMethod gMethods[] = {
821 /* name, signature, funcPtr */
822 {"openDecimalFormatImpl", "(Ljava/lang/String;Ljava/lang/String;)I",
823 (void*) openDecimalFormatImpl},
824 {"closeDecimalFormatImpl", "(I)V", (void*) closeDecimalFormatImpl},
825 {"setSymbol", "(IILjava/lang/String;)V", (void*) setSymbol},
826 {"getSymbol", "(II)Ljava/lang/String;", (void*) getSymbol},
827 {"setAttribute", "(III)V", (void*) setAttribute},
828 {"getAttribute", "(II)I", (void*) getAttribute},
829 {"setTextAttribute", "(IILjava/lang/String;)V", (void*) setTextAttribute},
830 {"getTextAttribute", "(II)Ljava/lang/String;", (void*) getTextAttribute},
831 {"applyPatternImpl", "(IZLjava/lang/String;)V", (void*) applyPatternImpl},
832 {"toPatternImpl", "(IZ)Ljava/lang/String;", (void*) toPatternImpl},
833 {"format",
834 "(IJLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;",
835 (void*) formatLong},
836 {"format",
837 "(IDLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;",
838 (void*) formatDouble},
839 {"format",
840 "(ILjava/lang/String;Ljava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;I)Ljava/lang/String;",
841 (void*) formatDigitList},
842 {"parse",
843 "(ILjava/lang/String;Ljava/text/ParsePosition;)Ljava/lang/Number;",
844 (void*) parse},
845 {"cloneImpl", "(I)I", (void*) cloneImpl}
846 };
register_com_ibm_icu4jni_text_NativeDecimalFormat(JNIEnv * env)847 int register_com_ibm_icu4jni_text_NativeDecimalFormat(JNIEnv* env) {
848 return jniRegisterNativeMethods(env,
849 "com/ibm/icu4jni/text/NativeDecimalFormat", gMethods,
850 NELEM(gMethods));
851 }
852