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