1 /*
2 * Copyright (C) 2008 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 #include "JNIHelp.h"
18 #include "AndroidSystemNatives.h"
19 #include "unicode/numfmt.h"
20 #include "unicode/rbnf.h"
21 #include "unicode/fmtable.h"
22 #include "unicode/ustring.h"
23 #include "unicode/locid.h"
24 #include "ErrorCode.h"
25 #include <stdlib.h>
26 #include <string.h>
27
icuError(JNIEnv * env,UErrorCode errorcode)28 static UBool icuError(JNIEnv *env, UErrorCode errorcode)
29 {
30 const char *emsg = u_errorName(errorcode);
31 jclass exception;
32
33 if (errorcode > U_ZERO_ERROR && errorcode < U_ERROR_LIMIT) {
34 switch (errorcode) {
35 case U_ILLEGAL_ARGUMENT_ERROR :
36 exception = env->FindClass("java/lang/IllegalArgumentException");
37 break;
38 case U_INDEX_OUTOFBOUNDS_ERROR :
39 case U_BUFFER_OVERFLOW_ERROR :
40 exception = env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
41 break;
42 case U_UNSUPPORTED_ERROR :
43 exception = env->FindClass("java/lang/UnsupportedOperationException");
44 break;
45 default :
46 exception = env->FindClass("java/lang/RuntimeException");
47 }
48
49 return (env->ThrowNew(exception, emsg) != 0);
50 }
51 return 0;
52 }
53
openRBNFImpl1(JNIEnv * env,jclass clazz,jint type,jstring locale)54 static jint openRBNFImpl1(JNIEnv* env, jclass clazz,
55 jint type, jstring locale) {
56
57 // LOGI("ENTER openRBNFImpl1");
58
59 // the errorcode returned by unum_open
60 UErrorCode status = U_ZERO_ERROR;
61
62 // prepare the locale string for the call to unum_open
63 const char *localeChars = env->GetStringUTFChars(locale, NULL);
64
65 URBNFRuleSetTag style;
66 if(type == 0) {
67 style = URBNF_SPELLOUT;
68 } else if(type == 1) {
69 style = URBNF_ORDINAL;
70 } else if(type == 2) {
71 style = URBNF_DURATION;
72 } else if(type == 3) {
73 style = URBNF_COUNT;
74 } else {
75 icuError(env, U_ILLEGAL_ARGUMENT_ERROR);
76 }
77
78 Locale loc = Locale::createFromName(localeChars);
79
80 // open a default type number format
81 RuleBasedNumberFormat *fmt = new RuleBasedNumberFormat(style, loc, status);
82
83 // release the allocated strings
84 env->ReleaseStringUTFChars(locale, localeChars);
85
86 // check for an error
87 if ( icuError(env, status) != FALSE) {
88 return 0;
89 }
90
91 // return the handle to the number format
92 return (long) fmt;
93
94 }
95
openRBNFImpl2(JNIEnv * env,jclass clazz,jstring rule,jstring locale)96 static jint openRBNFImpl2(JNIEnv* env, jclass clazz,
97 jstring rule, jstring locale) {
98
99 // LOGI("ENTER openRBNFImpl2");
100
101 // the errorcode returned by unum_open
102 UErrorCode status = U_ZERO_ERROR;
103
104 // prepare the pattern string for the call to unum_open
105 const UChar *ruleChars = env->GetStringChars(rule, NULL);
106 int ruleLen = env->GetStringLength(rule);
107
108 // prepare the locale string for the call to unum_open
109 const char *localeChars = env->GetStringUTFChars(locale, NULL);
110
111 // open a rule based number format
112 UNumberFormat *fmt = unum_open(UNUM_PATTERN_RULEBASED, ruleChars, ruleLen,
113 localeChars, NULL, &status);
114
115 // release the allocated strings
116 env->ReleaseStringChars(rule, ruleChars);
117 env->ReleaseStringUTFChars(locale, localeChars);
118
119 // check for an error
120 if ( icuError(env, status) != FALSE) {
121 return 0;
122 }
123
124 // return the handle to the number format
125 return (long) fmt;
126
127 }
128
closeRBNFImpl(JNIEnv * env,jclass clazz,jint addr)129 static void closeRBNFImpl(JNIEnv *env, jclass clazz, jint addr) {
130
131 // LOGI("ENTER closeRBNFImpl");
132
133 // get the pointer to the number format
134 RuleBasedNumberFormat *fmt = (RuleBasedNumberFormat *)(int)addr;
135
136 // close this number format
137 delete fmt;
138 }
139
formatLongRBNFImpl(JNIEnv * env,jclass clazz,jint addr,jlong value,jobject field,jstring fieldType,jobject attributes)140 static jstring formatLongRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jlong value,
141 jobject field, jstring fieldType, jobject attributes) {
142
143 // LOGI("ENTER formatLongRBNFImpl");
144
145 const char * fieldPositionClassName = "java/text/FieldPosition";
146 const char * stringBufferClassName = "java/lang/StringBuffer";
147 jclass fieldPositionClass = env->FindClass(fieldPositionClassName);
148 jclass stringBufferClass = env->FindClass(stringBufferClassName);
149 jmethodID setBeginIndexMethodID = env->GetMethodID(fieldPositionClass,
150 "setBeginIndex", "(I)V");
151 jmethodID setEndIndexMethodID = env->GetMethodID(fieldPositionClass,
152 "setEndIndex", "(I)V");
153 jmethodID appendMethodID = env->GetMethodID(stringBufferClass,
154 "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
155
156 const char * fieldName = NULL;
157
158 if(fieldType != NULL) {
159 fieldName = env->GetStringUTFChars(fieldType, NULL);
160 }
161
162 uint32_t reslenneeded;
163 int64_t val = value;
164 UChar *result = NULL;
165
166 FieldPosition fp;
167 fp.setField(FieldPosition::DONT_CARE);
168
169 UErrorCode status = U_ZERO_ERROR;
170
171 RuleBasedNumberFormat *fmt = (RuleBasedNumberFormat *)(int)addr;
172
173 UnicodeString res;
174
175 fmt->format(val, res, fp);
176
177 reslenneeded = res.extract(NULL, 0, status);
178
179 if(status==U_BUFFER_OVERFLOW_ERROR) {
180 status=U_ZERO_ERROR;
181
182 result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1));
183
184 res.extract(result, reslenneeded + 1, status);
185 }
186 if (icuError(env, status) != FALSE) {
187 free(result);
188 return NULL;
189 }
190
191 if(fieldType != NULL) {
192 env->ReleaseStringUTFChars(fieldType, fieldName);
193 }
194
195 jstring resulting = env->NewString(result, reslenneeded);
196
197 free(result);
198
199 return resulting;
200 }
201
formatDoubleRBNFImpl(JNIEnv * env,jclass clazz,jint addr,jdouble value,jobject field,jstring fieldType,jobject attributes)202 static jstring formatDoubleRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jdouble value,
203 jobject field, jstring fieldType, jobject attributes) {
204
205 // LOGI("ENTER formatDoubleRBNFImpl");
206
207 const char * fieldPositionClassName = "java/text/FieldPosition";
208 const char * stringBufferClassName = "java/lang/StringBuffer";
209 jclass fieldPositionClass = env->FindClass(fieldPositionClassName);
210 jclass stringBufferClass = env->FindClass(stringBufferClassName);
211 jmethodID setBeginIndexMethodID = env->GetMethodID(fieldPositionClass,
212 "setBeginIndex", "(I)V");
213 jmethodID setEndIndexMethodID = env->GetMethodID(fieldPositionClass,
214 "setEndIndex", "(I)V");
215 jmethodID appendMethodID = env->GetMethodID(stringBufferClass,
216 "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
217
218 const char * fieldName = NULL;
219
220 if(fieldType != NULL) {
221 fieldName = env->GetStringUTFChars(fieldType, NULL);
222 }
223
224 uint32_t reslenneeded;
225 double val = value;
226 UChar *result = NULL;
227
228 FieldPosition fp;
229 fp.setField(FieldPosition::DONT_CARE);
230
231 UErrorCode status = U_ZERO_ERROR;
232
233 RuleBasedNumberFormat *fmt = (RuleBasedNumberFormat *)(int)addr;
234
235 UnicodeString res;
236
237 fmt->format(val, res, fp);
238
239 reslenneeded = res.extract(NULL, 0, status);
240
241 if(status==U_BUFFER_OVERFLOW_ERROR) {
242 status=U_ZERO_ERROR;
243
244 result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1));
245
246 res.extract(result, reslenneeded + 1, status);
247 }
248 if (icuError(env, status) != FALSE) {
249 free(result);
250 return NULL;
251 }
252
253 if(fieldType != NULL) {
254 env->ReleaseStringUTFChars(fieldType, fieldName);
255 }
256
257 jstring resulting = env->NewString(result, reslenneeded);
258
259 free(result);
260
261 return resulting;
262 }
263
parseRBNFImpl(JNIEnv * env,jclass clazz,jint addr,jstring text,jobject position,jboolean lenient)264 static jobject parseRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jstring text,
265 jobject position, jboolean lenient) {
266
267 // LOGI("ENTER parseRBNFImpl");
268
269 const char * parsePositionClassName = "java/text/ParsePosition";
270 const char * longClassName = "java/lang/Long";
271 const char * doubleClassName = "java/lang/Double";
272
273
274 UErrorCode status = U_ZERO_ERROR;
275
276 UNumberFormat *fmt = (UNumberFormat *)(int)addr;
277
278 jchar *str = (UChar *)env->GetStringChars(text, NULL);
279 int strlength = env->GetStringLength(text);
280
281 jclass parsePositionClass = env->FindClass(parsePositionClassName);
282 jclass longClass = env->FindClass(longClassName);
283 jclass doubleClass = env->FindClass(doubleClassName);
284
285 jmethodID getIndexMethodID = env->GetMethodID(parsePositionClass,
286 "getIndex", "()I");
287 jmethodID setIndexMethodID = env->GetMethodID(parsePositionClass,
288 "setIndex", "(I)V");
289 jmethodID setErrorIndexMethodID = env->GetMethodID(parsePositionClass,
290 "setErrorIndex", "(I)V");
291
292 jmethodID longInitMethodID = env->GetMethodID(longClass, "<init>", "(J)V");
293 jmethodID dblInitMethodID = env->GetMethodID(doubleClass, "<init>", "(D)V");
294
295 int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL);
296
297 // make sure the ParsePosition is valid. Actually icu4c would parse a number
298 // correctly even if the parsePosition is set to -1, but since the RI fails
299 // for that case we have to fail too
300 if(parsePos < 0 || parsePos > strlength) {
301 return NULL;
302 }
303
304 Formattable res;
305
306 const UnicodeString src((UChar*)str, strlength, strlength);
307 ParsePosition pp;
308
309 pp.setIndex(parsePos);
310
311 if(lenient) {
312 unum_setAttribute(fmt, UNUM_LENIENT_PARSE, JNI_TRUE);
313 }
314
315 ((const NumberFormat*)fmt)->parse(src, res, pp);
316
317 if(lenient) {
318 unum_setAttribute(fmt, UNUM_LENIENT_PARSE, JNI_FALSE);
319 }
320
321 env->ReleaseStringChars(text, str);
322
323 if(pp.getErrorIndex() == -1) {
324 parsePos = pp.getIndex();
325 } else {
326 env->CallVoidMethod(position, setErrorIndexMethodID,
327 (jint) pp.getErrorIndex());
328 return NULL;
329 }
330
331 Formattable::Type numType;
332 numType = res.getType();
333 UErrorCode fmtStatus;
334
335 double resultDouble;
336 long resultLong;
337 int64_t resultInt64;
338
339 switch(numType) {
340 case Formattable::kDouble:
341 resultDouble = res.getDouble();
342 env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
343 return env->NewObject(doubleClass, dblInitMethodID,
344 (jdouble) resultDouble);
345 case Formattable::kLong:
346 resultLong = res.getLong();
347 env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
348 return env->NewObject(longClass, longInitMethodID,
349 (jlong) resultLong);
350 case Formattable::kInt64:
351 resultInt64 = res.getInt64();
352 env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
353 return env->NewObject(longClass, longInitMethodID,
354 (jlong) resultInt64);
355 default:
356 break;
357 }
358
359 return NULL;
360 }
361
362 static JNINativeMethod gMethods[] = {
363 /* name, signature, funcPtr */
364 {"openRBNFImpl", "(ILjava/lang/String;)I", (void*) openRBNFImpl1},
365 {"openRBNFImpl", "(Ljava/lang/String;Ljava/lang/String;)I",
366 (void*) openRBNFImpl2},
367 {"closeRBNFImpl", "(I)V", (void*) closeRBNFImpl},
368 {"formatRBNFImpl",
369 "(IJLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;",
370 (void*) formatLongRBNFImpl},
371 {"formatRBNFImpl",
372 "(IDLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;",
373 (void*) formatDoubleRBNFImpl},
374 {"parseRBNFImpl",
375 "(ILjava/lang/String;Ljava/text/ParsePosition;Z)Ljava/lang/Number;",
376 (void*) parseRBNFImpl},
377 };
register_com_ibm_icu4jni_text_NativeRBNF(JNIEnv * env)378 int register_com_ibm_icu4jni_text_NativeRBNF(JNIEnv* env) {
379 return jniRegisterNativeMethods(env,
380 "com/ibm/icu4jni/text/RuleBasedNumberFormat", gMethods,
381 NELEM(gMethods));
382 }
383