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