OSDN Git Service

auto import from //depot/cupcake/@132589
[android-x86/packages-apps-IM.git] / libwbxml / src / wbxml_jni.cpp
1 /*
2  * Copyright (C) 2007 Esmertec AG.
3  * Copyright (C) 2007 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define LOG_TAG "wbxml"
19
20 #include <jni.h>
21 #include <cutils/jstring.h>
22
23 #include "wbxml_parser.h"
24 #include "imps_encoder.h"
25 #include "csp13_hash.h"
26
27 // FIXME: dalvik seems to have problem throwing non-system exceptions. Use IAE for now.
28 #if 1
29 #define PARSER_EXCEPTION_CLASS      "java/lang/IllegalArgumentException"
30 #define SERIALIZER_EXCEPTION_CLASS  "java/lang/IllegalArgumentException"
31 #else
32 #define PARSER_EXCEPTION_CLASS      "com/android/im/imps/ParserException"
33 #define SERIALIZER_EXCEPTION_CLASS  "com/android/im/imps/SerializerException"
34 #endif
35
36 class JniWbxmlContentHandler;
37 class JniWbxmlDataHandler;
38
39 struct WbxmlParsingContext
40 {
41     /** The JNI enviromenet. */
42     JNIEnv * env;
43     /** The Java parser object. */
44     jobject object;
45     /** The wbxml parser. */
46     WbxmlParser * parser;
47     /** The content handler.*/
48     JniWbxmlContentHandler * contentHandler;
49 };
50
51 struct WbxmlEncodingContext
52 {
53     /** The JNI enviroment. */
54     JNIEnv * env;
55     /** The Java encoder object. */
56     jobject object;
57     /** The wbxml encoder. */
58     WbxmlEncoder * encoder;
59     /** The handler to get encoding result. */
60     JniWbxmlDataHandler * handler;
61 };
62
63 static jmethodID parserCallbackStartElement;
64 static jmethodID parserCallbackEndElement;
65 static jmethodID parserCallbackCharacters;
66 static jmethodID encoderCallbackWbxmlData;
67 static jclass stringClass;
68
69 /**
70  * Copies UTF-8 characters into the buffer.
71  */
72 static jcharArray bytesToCharArray(JNIEnv * env, const char *data, int length, size_t * out_len)
73 {
74     // TODO: store the buffer in ParsingContext to reuse it instead create a
75     // new one everytime.
76     jcharArray buffer = env->NewCharArray(length);
77     if(buffer == NULL) {
78         return NULL;
79     }
80
81     // Get a native reference to the buffer.
82     jboolean copy;
83     jchar *nativeBuffer = env->GetCharArrayElements(buffer, &copy);
84     if (copy) {
85         jclass clazz = env->FindClass("java/lang/AssertionError");
86         env->ThrowNew(clazz, "Unexpected copy");
87         return NULL;
88     }
89
90     // Decode UTF-8 characters into the buffer.
91     strcpylen8to16((char16_t *) nativeBuffer, data, length, out_len);
92
93     // Release our native reference.
94     env->ReleaseCharArrayElements(buffer, nativeBuffer, JNI_ABORT);
95
96     return buffer;
97 }
98
99 class JniWbxmlContentHandler : public DefaultWbxmlContentHandler
100 {
101 public:
102     JniWbxmlContentHandler(WbxmlParsingContext * context):mContext(context)
103     {
104     }
105
106     void reset(void)
107     {
108         mCurTag.clear();
109     }
110
111     void startElement(const char * name, const vector<Attribute> & atts)
112     {
113         JNIEnv * env = mContext->env;
114
115         if(env->ExceptionCheck()) return;
116
117         mCurTag = name;
118
119         //TODO cache jstrings for later use?
120         jstring localName = env->NewStringUTF(name);
121         jobjectArray attrNames = NULL;
122         jobjectArray attrValues = NULL;
123         int count = atts.size();
124         if(count > 0) {
125             attrNames = env->NewObjectArray(count, stringClass, NULL);
126             attrValues = env->NewObjectArray(count, stringClass, NULL);
127         }
128
129         for(int i = 0; i < count; i++) {
130             jstring attrName = env->NewStringUTF(atts[i].name.c_str());
131             jstring attrValue = env->NewStringUTF(atts[i].value.c_str());
132             env->SetObjectArrayElement(attrNames, i, attrName);
133             env->SetObjectArrayElement(attrValues, i, attrValue);
134         }
135
136         jobject javaParser = mContext->object;
137         env->CallVoidMethod(javaParser, parserCallbackStartElement, localName, attrNames, attrValues);
138         env->DeleteLocalRef(localName);
139         for(int i = 0; i < count; i++) {
140             env->DeleteLocalRef(env->GetObjectArrayElement(attrNames, i));
141             env->DeleteLocalRef(env->GetObjectArrayElement(attrValues, i));
142         }
143     }
144
145     void endElement(const char * name)
146     {
147         JNIEnv * env = mContext->env;
148
149         if(env->ExceptionCheck()) return;
150
151         mCurTag.clear();
152
153         jstring localName = env->NewStringUTF(name);
154         jobject javaParser = mContext->object;
155
156         env->CallVoidMethod(javaParser, parserCallbackEndElement, localName);
157         env->DeleteLocalRef(localName);
158     }
159
160     void characters(const char * data, int len)
161     {
162         JNIEnv * env = mContext->env;
163
164         if(env->ExceptionCheck()) return;
165
166         size_t utf16length;
167         jcharArray buffer = bytesToCharArray(env, data, len, &utf16length);
168         jobject javaParser = mContext->object;
169         env->CallVoidMethod(javaParser, parserCallbackCharacters, buffer, utf16length);
170         env->DeleteLocalRef(buffer);
171     }
172
173     void opaque(const char * data, int len)
174     {
175         JNIEnv * env = mContext->env;
176
177         if(env->ExceptionCheck()) return;
178
179         int val = 0;
180         if (csp13IsIntegerTag(mCurTag.c_str())) {
181             while (len--){
182                 val <<= 8;
183                 val |= (unsigned char)*data++;
184             }
185
186             char buf[20];
187             sprintf(buf, "%d", val);
188             size_t utf16length;
189             jcharArray buffer = bytesToCharArray(env, buf, strlen(buf), &utf16length);
190             jobject javaParser = mContext->object;
191             env->CallVoidMethod(javaParser, parserCallbackCharacters, buffer, utf16length);
192             env->DeleteLocalRef(buffer);
193         }
194         // FIXME: handle DateTime and binary data too
195     }
196 private:
197     WbxmlParsingContext * mContext;
198     string mCurTag;
199 };
200
201 static void parserStaticInitialize(JNIEnv * env, jclass clazz)
202 {
203     parserCallbackStartElement = env->GetMethodID((jclass) clazz, "startElement",
204         "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V");
205
206     parserCallbackEndElement = env->GetMethodID((jclass) clazz, "endElement",
207         "(Ljava/lang/String;)V");
208
209     parserCallbackCharacters = env->GetMethodID((jclass) clazz, "characters",
210         "([CI)V");
211
212     stringClass = env->FindClass("java/lang/String");
213 }
214
215 static jint parserCreate(JNIEnv * env, jobject thisObj, jstring encoding)
216 {
217     // TODO: encoding
218     WbxmlParsingContext * context = NULL;
219     WbxmlParser * parser = NULL;
220     JniWbxmlContentHandler * handler = NULL;
221
222     context = new WbxmlParsingContext();
223     if (!context) {
224         goto error;
225     }
226
227     parser = new WbxmlParser(0);
228     if (!parser) {
229         goto error;
230     }
231
232     handler = new JniWbxmlContentHandler(context);
233     if (!handler) {
234         goto error;
235     }
236
237     parser->setContentHandler(handler);
238     context->parser = parser;
239     context->contentHandler = handler;
240     context->env = env;
241     context->object = thisObj;
242
243     return (jint)context;
244
245 error:
246     // C++ is OK with deleting NULL ptr.
247     delete context;
248     delete parser;
249     return 0;
250 }
251
252 static void parserDelete(JNIEnv * env, jobject thisObj, jint nativeParser)
253 {
254     WbxmlParsingContext * context = (WbxmlParsingContext *)nativeParser;
255     delete context->parser;
256     delete context->contentHandler;
257     delete context;
258 }
259
260 static void parserReset(JNIEnv * env, jobject thisObj, jint nativeParser)
261 {
262     WbxmlParsingContext * context = (WbxmlParsingContext *)nativeParser;
263     WbxmlParser * parser = context->parser;
264     JniWbxmlContentHandler * handler = context->contentHandler;
265
266     parser->reset();
267     handler->reset();
268     parser->setContentHandler(handler);
269 }
270
271 static void parserParse(JNIEnv * env, jobject thisObj, jint nativeParser,
272         jbyteArray buf, jint len, jboolean isEnd)
273 {
274     WbxmlParsingContext * context = (WbxmlParsingContext *)nativeParser;
275     WbxmlParser * parser = context->parser;
276     context->env = env;
277
278     jboolean copy;
279     jbyte * bytes = env->GetByteArrayElements(buf, &copy);
280     if(copy) {
281         jclass clazz = env->FindClass("java/lang/AssertionError");
282         env->ThrowNew(clazz, "Unexpected copy");
283         return;
284     }
285
286     // make sure the context is updated because we may get called from
287     // a different thread
288     context->env = env;
289     context->object = thisObj;
290
291     if (parser->parse((char*)bytes, len, isEnd) != WBXML_STATUS_OK) {
292         LOGW("WbxmlParser parse error %d\n", parser->getError());
293         jclass clazz = env->FindClass(PARSER_EXCEPTION_CLASS);
294         if (clazz == NULL) {
295             LOGE("Can't find class " PARSER_EXCEPTION_CLASS "\n");
296             return;
297         }
298         env->ThrowNew(clazz, NULL);
299     }
300
301     jthrowable exception = env->ExceptionOccurred();
302     if (exception) {
303         env->ExceptionClear();
304         env->ReleaseByteArrayElements(buf, bytes, JNI_ABORT);
305         env->Throw(exception);
306     } else {
307         env->ReleaseByteArrayElements(buf, bytes, JNI_ABORT);
308     }
309     context->env = NULL;
310 }
311
312 class JniWbxmlDataHandler : public WbxmlHandler
313 {
314 public:
315     JniWbxmlDataHandler(WbxmlEncodingContext * context) : mContext(context)
316     {
317     }
318
319     void reset()
320     {
321         // nothing to do
322     }
323
324     void wbxmlData(const char *data, uint32_t len)
325     {
326         JNIEnv * env = mContext->env;
327
328         if (env->ExceptionCheck()) {
329             return;
330         }
331
332         if (encoderCallbackWbxmlData == NULL) {
333             jclass clazz = env->GetObjectClass(mContext->object);
334             encoderCallbackWbxmlData = env->GetMethodID(clazz, "onWbxmlData", "([BI)V");
335         }
336         jbyteArray byteArray = env->NewByteArray(len);
337         if (byteArray == NULL) {
338             return;
339         }
340
341         env->SetByteArrayRegion(byteArray, 0, len, (const jbyte*)data);
342
343         env->CallVoidMethod(mContext->object, encoderCallbackWbxmlData, byteArray, len);
344     }
345
346 private:
347     WbxmlEncodingContext * mContext;
348 };
349
350 static jint encoderCreate(JNIEnv * env, jobject thisObj, int publicId)
351 {
352     WbxmlEncodingContext * context = NULL;
353     WbxmlEncoder * encoder = NULL;
354     JniWbxmlDataHandler * handler = NULL;
355
356     context = new WbxmlEncodingContext();
357     if (!context) {
358         goto error;
359     }
360
361     encoder = new ImpsWbxmlEncoder(publicId);
362     if (!encoder) {
363         goto error;
364     }
365
366     handler = new JniWbxmlDataHandler(context);
367     if (!handler) {
368         goto error;
369     }
370
371     encoder->setWbxmlHandler(handler);
372
373     context->encoder = encoder;
374     context->handler = handler;
375     context->env = env;
376     context->object = thisObj;
377
378     return (jint)context;
379
380 error:
381     // C++ is OK with deleting NULL ptr.
382     delete context;
383     delete encoder;
384     return 0;
385 }
386
387 static void encoderDelete(JNIEnv * env, jobject thisObj, jint nativeEncoder)
388 {
389     WbxmlEncodingContext * context = (WbxmlEncodingContext *)nativeEncoder;
390     delete context->encoder;
391     delete context->handler;
392     delete context;
393 }
394
395 static void encoderReset(JNIEnv * env, jobject thisObj, jint nativeEncoder)
396 {
397     WbxmlEncodingContext * context = (WbxmlEncodingContext *)nativeEncoder;
398     WbxmlEncoder * encoder = context->encoder;
399     JniWbxmlDataHandler * handler = context->handler;
400
401     encoder->reset();
402     handler->reset();
403     encoder->setWbxmlHandler(handler);
404 }
405
406 static void encoderStartElement(JNIEnv * env, jobject thisObj, jint nativeEncoder,
407         jstring name, jobjectArray atts)
408 {
409     WbxmlEncodingContext * context = (WbxmlEncodingContext *)nativeEncoder;
410     WbxmlEncoder * encoder = context->encoder;
411
412     const char ** c_atts = NULL;
413     int count = atts ? env->GetArrayLength(atts) : 0;
414     // TODO: handle out of memory.
415     c_atts = new const char *[count + 1];
416     if (atts != NULL) {
417         for(int i = 0; i < count; i++) {
418             jstring str = (jstring)env->GetObjectArrayElement(atts, i);
419             c_atts[i] = env->GetStringUTFChars(str, NULL);
420         }
421     }
422     c_atts[count] = NULL;
423     const char * c_name = env->GetStringUTFChars(name, NULL);
424
425     // make sure the context is updated because we may get called from
426     // a different thread
427     context->env = env;
428     context->object = thisObj;
429
430     EncoderError ret = encoder->startElement(c_name, c_atts);
431
432     // encoder->startElement() may result in a call to WbxmlHandler.wbxmlData()
433     // which in turns call back to the Java side. Therefore we might have
434     // a pending Java exception here. Although current WbxmlEncoder always
435     // call WbxmlHandler.wbxmlData() only after a successful complete parsing
436     // but this may change later.
437
438     if (env->ExceptionCheck() == JNI_FALSE && ret != NO_ERROR) {
439         // only throw a new exception when there is no pending one
440         LOGW("WbxmlEncoder startElement error:%d\n", ret);
441         jclass clazz = env->FindClass(SERIALIZER_EXCEPTION_CLASS);
442         if (clazz == NULL) {
443             LOGE("Can't find class " SERIALIZER_EXCEPTION_CLASS);
444             return;
445         }
446         env->ThrowNew(clazz, "Wbxml encode error");
447     }
448
449     jthrowable exception = env->ExceptionOccurred();
450     if(exception) {
451         env->ExceptionClear();
452     }
453
454     env->ReleaseStringUTFChars(name, c_name);
455     for (int i = 0; i < count; i++) {
456         jstring str = (jstring)env->GetObjectArrayElement(atts, i);
457         env->ReleaseStringUTFChars(str, c_atts[i]);
458     }
459     delete[] c_atts;
460     if (exception) {
461         env->Throw(exception);
462     }
463 }
464
465 static void encoderCharacters(JNIEnv * env, jobject thisObj, jint nativeEncoder, jstring chars)
466 {
467     WbxmlEncodingContext * context = (WbxmlEncodingContext *)nativeEncoder;
468     WbxmlEncoder * encoder = context->encoder;
469
470     const char * c_chars = env->GetStringUTFChars(chars, NULL);
471
472     // make sure the context is updated because we may get called from
473     // a different thread
474     context->env = env;
475     context->object = thisObj;
476
477     EncoderError ret = encoder->characters(c_chars, env->GetStringUTFLength(chars));
478
479     // encoder->characters() may result in a call to WbxmlHandler.wbxmlData()
480     // which in turns call back to the Java side. Therefore we might have
481     // a pending Java exception here. Although current WbxmlEncoder always
482     // call WbxmlHandler.wbxmlData() only after a successful complete parsing
483     // but this may change later.
484
485     if (env->ExceptionCheck() == JNI_FALSE && ret != NO_ERROR) {
486         // only throw a new exception when there is no pending one
487         LOGE("WbxmlEncoder characters error:%d\n", ret);
488         jclass clazz = env->FindClass(SERIALIZER_EXCEPTION_CLASS);
489         if (clazz == NULL) {
490             LOGE("Can't find class " SERIALIZER_EXCEPTION_CLASS);
491             return;
492         }
493         env->ThrowNew(clazz, "Wbxml encode error");
494     }
495     jthrowable exception = env->ExceptionOccurred();
496     if (exception) {
497         env->ExceptionClear();
498         env->ReleaseStringUTFChars(chars, c_chars);
499         env->Throw(exception);
500     } else {
501         env->ReleaseStringUTFChars(chars, c_chars);
502     }
503 }
504
505 static void encoderEndElement(JNIEnv * env, jobject thisObj, jint nativeEncoder)
506 {
507     WbxmlEncodingContext * context = (WbxmlEncodingContext *)nativeEncoder;
508     WbxmlEncoder * encoder = context->encoder;
509
510     // make sure the context is updated because we may get called from
511     // a different thread
512     context->env = env;
513     context->object = thisObj;
514
515     EncoderError ret = encoder->endElement();
516
517     // encoder->endElement() may result in a call to WbxmlHandler.wbxmlData()
518     // which in turns call back to the Java side. Therefore we might have
519     // a pending Java exception here.
520     if (env->ExceptionCheck() == JNI_FALSE && ret != NO_ERROR) {
521         // only throw a new exception when there is no pending one
522         LOGE("WbxmlEncoder endElement error:%d\n", ret);
523         jclass clazz = env->FindClass(SERIALIZER_EXCEPTION_CLASS);
524         if (clazz == NULL) {
525             LOGE("Can't find class " SERIALIZER_EXCEPTION_CLASS);
526             return;
527         }
528         env->ThrowNew(clazz, "Wbxml encode error");
529     }
530 }
531
532 /**
533  * Table of methods associated with WbxmlParser.
534  */
535 static JNINativeMethod parserMethods[] = {
536     /* name, signature, funcPtr */
537     { "nativeStaticInitialize", "()V",                      (void*) parserStaticInitialize },
538     { "nativeCreate",           "(Ljava/lang/String;)I",    (void*) parserCreate },
539     { "nativeRelease",          "(I)V",                     (void*) parserDelete },
540     { "nativeReset",            "(I)V",                     (void*) parserReset },
541     { "nativeParse",            "(I[BIZ)V",                 (void*) parserParse },
542 };
543
544 /**
545  * Table of methods associated with WbxmlSerializer.
546  */
547 static JNINativeMethod encoderMethods[] = {
548     /* name, signature, funcPtr */
549     { "nativeCreate",       "(I)I",                                     (void*) encoderCreate },
550     { "nativeRelease",      "(I)V",                                     (void*) encoderDelete },
551     { "nativeReset",        "(I)V",                                     (void*) encoderReset },
552     { "nativeStartElement", "(ILjava/lang/String;[Ljava/lang/String;)V", (void*) encoderStartElement },
553     { "nativeCharacters",   "(ILjava/lang/String;)V",                   (void*) encoderCharacters },
554     { "nativeEndElement",   "(I)V",                                     (void*) encoderEndElement },
555 };
556
557 /*
558  * Register several native methods for one class.
559  */
560 static int registerNativeMethods(JNIEnv* env, const char* className,
561     JNINativeMethod* methods, int numMethods)
562 {
563     jclass clazz;
564
565     clazz = env->FindClass(className);
566     if (clazz == NULL) {
567         LOGE("Native registration unable to find class '%s'\n", className);
568         return JNI_FALSE;
569     }
570     if (env->RegisterNatives(clazz, methods, numMethods) < 0) {
571         LOGE("RegisterNatives failed for '%s'\n", className);
572         return JNI_FALSE;
573     }
574
575     return JNI_TRUE;
576 }
577
578 /*
579  * Register native methods.
580  */
581 static int registerNatives(JNIEnv* env)
582 {
583     if (!registerNativeMethods(env, "com/android/im/imps/WbxmlParser",
584             parserMethods, sizeof(parserMethods) / sizeof(parserMethods[0])))
585         return JNI_FALSE;
586
587     if (!registerNativeMethods(env, "com/android/im/imps/WbxmlSerializer",
588             encoderMethods, sizeof(encoderMethods) / sizeof(encoderMethods[0])))
589         return JNI_FALSE;
590
591     return JNI_TRUE;
592 }
593
594 /*
595  * Set some test stuff up.
596  *
597  * Returns the JNI version on success, -1 on failure.
598  */
599 jint JNI_OnLoad(JavaVM* vm, void* reserved)
600 {
601     JNIEnv* env = NULL;
602     jint result = -1;
603
604     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
605         LOGE("ERROR: GetEnv failed\n");
606         goto bail;
607     }
608     assert(env != NULL);
609
610     // LOGI("In WbxmlParser JNI_OnLoad\n");
611
612     if (!registerNatives(env)) {
613         LOGE("ERROR: WbxmlParser native registration failed\n");
614         goto bail;
615     }
616
617     /* success -- return valid version number */
618     result = JNI_VERSION_1_4;
619
620 bail:
621     return result;
622 }
623