• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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