blob: 2ea880694dd803e5fff7ded66e0fd8827c309267 [file] [log] [blame]
The Android Open Source Projectdd828f42008-12-17 18:03:55 -08001/*
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07002 * 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#define LOG_TAG "ExpatParser"
18
Elliott Hughes845ce3c2009-11-13 17:07:00 -080019#include "JNIHelp.h"
Elliott Hughesa9f5c162010-06-16 16:32:18 -070020#include "JniConstants.h"
Elliott Hughesfb85a3a2011-01-16 16:06:11 -080021#include "JniException.h"
Elliott Hughes845ce3c2009-11-13 17:07:00 -080022#include "LocalArray.h"
Elliott Hughes8044bf62010-05-17 22:34:46 -070023#include "ScopedLocalRef.h"
Elliott Hughes99c59bf2010-05-17 16:22:04 -070024#include "ScopedPrimitiveArray.h"
Elliott Hughesfe711d62011-04-22 15:56:03 -070025#include "ScopedStringChars.h"
Elliott Hughes05960872010-05-26 17:45:07 -070026#include "ScopedUtfChars.h"
Elliott Hughes8044bf62010-05-17 22:34:46 -070027#include "UniquePtr.h"
The Android Open Source Projectfdb27042008-10-21 07:00:00 -070028#include "jni.h"
Elliott Hughes8545b832011-06-08 15:31:22 -070029#include "cutils/log.h"
Elliott Hughes7cd67602012-05-03 17:21:04 -070030#include "unicode/unistr.h"
The Android Open Source Projectfdb27042008-10-21 07:00:00 -070031
32#include <string.h>
Elliott Hughes44d73722012-09-09 13:08:17 -070033#include <libexpat/expat.h>
The Android Open Source Projectfdb27042008-10-21 07:00:00 -070034
35#define BUCKET_COUNT 128
36
37/**
38 * Wrapper around an interned string.
39 */
Elliott Hughesc9b92b42009-12-09 16:05:29 -080040struct InternedString {
Elliott Hughes4eafbcc2010-06-21 16:52:06 -070041 InternedString() : interned(NULL), bytes(NULL) {
42 }
43
44 ~InternedString() {
45 delete[] bytes;
46 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -070047
48 /** The interned string itself. */
49 jstring interned;
50
51 /** UTF-8 equivalent of the interned string. */
52 const char* bytes;
53
54 /** Hash code of the interned string. */
Elliott Hughes99c59bf2010-05-17 16:22:04 -070055 int hash;
Elliott Hughesc9b92b42009-12-09 16:05:29 -080056};
The Android Open Source Projectfdb27042008-10-21 07:00:00 -070057
58/**
59 * Keeps track of strings between start and end events.
60 */
Elliott Hughes4eafbcc2010-06-21 16:52:06 -070061class StringStack {
62public:
63 StringStack() : array(new jstring[DEFAULT_CAPACITY]), capacity(DEFAULT_CAPACITY), size(0) {
64 }
65
66 ~StringStack() {
67 delete[] array;
68 }
69
70 void push(JNIEnv* env, jstring s) {
71 if (size == capacity) {
72 int newCapacity = capacity * 2;
73 jstring* newArray = new jstring[newCapacity];
74 if (newArray == NULL) {
Elliott Hughesfb85a3a2011-01-16 16:06:11 -080075 jniThrowOutOfMemoryError(env, NULL);
Elliott Hughes4eafbcc2010-06-21 16:52:06 -070076 return;
77 }
78 memcpy(newArray, array, capacity * sizeof(jstring));
79
andrey yusipove8c62582013-02-21 12:52:27 +010080 delete[] array;
Elliott Hughes4eafbcc2010-06-21 16:52:06 -070081 array = newArray;
82 capacity = newCapacity;
83 }
84
85 array[size++] = s;
86 }
87
88 jstring pop() {
89 return (size == 0) ? NULL : array[--size];
90 }
91
92private:
93 enum { DEFAULT_CAPACITY = 10 };
The Android Open Source Projectfdb27042008-10-21 07:00:00 -070094
95 jstring* array;
96 int capacity;
97 int size;
Elliott Hughesc9b92b42009-12-09 16:05:29 -080098};
The Android Open Source Projectfdb27042008-10-21 07:00:00 -070099
100/**
101 * Data passed to parser handler method by the parser.
102 */
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800103struct ParsingContext {
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700104 ParsingContext(jobject object) : env(NULL), object(object), buffer(NULL), bufferSize(-1) {
105 for (int i = 0; i < BUCKET_COUNT; i++) {
106 internedStrings[i] = NULL;
107 }
108 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700109
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700110 // Warning: 'env' must be valid on entry.
111 ~ParsingContext() {
112 freeBuffer();
113
114 // Free interned string cache.
115 for (int i = 0; i < BUCKET_COUNT; i++) {
116 if (internedStrings[i]) {
117 InternedString** bucket = internedStrings[i];
118 InternedString* current;
119 while ((current = *(bucket++)) != NULL) {
120 // Free the interned string reference.
121 env->DeleteGlobalRef(current->interned);
122
123 // Free the bucket.
124 delete current;
125 }
126
127 // Free the buckets.
128 delete[] internedStrings[i];
129 }
130 }
131 }
132
133 jcharArray ensureCapacity(int length) {
134 if (bufferSize < length) {
135 // Free the existing char[].
136 freeBuffer();
137
138 // Allocate a new char[].
139 jcharArray javaBuffer = env->NewCharArray(length);
140 if (javaBuffer == NULL) return NULL;
141
142 // Create a global reference.
Joel Dice0b00d812013-02-27 16:15:26 -0700143 javaBuffer = reinterpret_cast<jcharArray>(env->NewGlobalRef(javaBuffer));
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700144 if (javaBuffer == NULL) return NULL;
145
146 buffer = javaBuffer;
147 bufferSize = length;
148 }
149 return buffer;
150 }
151
152private:
153 void freeBuffer() {
154 if (buffer != NULL) {
155 env->DeleteGlobalRef(buffer);
156 buffer = NULL;
157 bufferSize = -1;
158 }
159 }
160
161public:
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700162 /**
163 * The JNI environment for the current thread. This should only be used
164 * to keep a reference to the env for use in Expat callbacks.
165 */
166 JNIEnv* env;
167
168 /** The Java parser object. */
169 jobject object;
170
171 /** Buffer for text events. */
172 jcharArray buffer;
173
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700174private:
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700175 /** The size of our buffer in jchars. */
176 int bufferSize;
177
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700178public:
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700179 /** Current attributes. */
180 const char** attributes;
181
182 /** Number of attributes. */
183 int attributeCount;
184
185 /** True if namespace support is enabled. */
186 bool processNamespaces;
187
188 /** Keep track of names. */
189 StringStack stringStack;
190
191 /** Cache of interned strings. */
192 InternedString** internedStrings[BUCKET_COUNT];
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800193};
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700194
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700195static ParsingContext* toParsingContext(void* data) {
196 return reinterpret_cast<ParsingContext*>(data);
197}
198
199static ParsingContext* toParsingContext(XML_Parser parser) {
200 return reinterpret_cast<ParsingContext*>(XML_GetUserData(parser));
201}
202
Joel Dice0b00d812013-02-27 16:15:26 -0700203static XML_Parser toXMLParser(jlong address) {
204 return reinterpret_cast<XML_Parser>(address);
205}
206
207static jlong fromXMLParser(XML_Parser parser) {
208 return reinterpret_cast<uintptr_t>(parser);
209}
210
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700211static jmethodID commentMethod;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700212static jmethodID endCdataMethod;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700213static jmethodID endDtdMethod;
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800214static jmethodID endElementMethod;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700215static jmethodID endNamespaceMethod;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700216static jmethodID handleExternalEntityMethod;
217static jmethodID internMethod;
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800218static jmethodID notationDeclMethod;
219static jmethodID processingInstructionMethod;
220static jmethodID startCdataMethod;
221static jmethodID startDtdMethod;
222static jmethodID startElementMethod;
223static jmethodID startNamespaceMethod;
224static jmethodID textMethod;
225static jmethodID unparsedEntityDeclMethod;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700226static jstring emptyString;
227
228/**
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700229 * Calculates a hash code for a null-terminated string. This is *not* equivalent
230 * to Java's String.hashCode(). This hashes the bytes while String.hashCode()
231 * hashes UTF-16 chars.
232 *
233 * @param s null-terminated string to hash
234 * @returns hash code
235 */
236static int hashString(const char* s) {
237 int hash = 0;
238 if (s) {
239 while (*s) {
240 hash = hash * 31 + *s++;
241 }
242 }
243 return hash;
244}
245
246/**
247 * Creates a new interned string wrapper. Looks up the interned string
248 * representing the given UTF-8 bytes.
249 *
250 * @param bytes null-terminated string to intern
251 * @param hash of bytes
252 * @returns wrapper of interned Java string
253 */
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700254static InternedString* newInternedString(JNIEnv* env, const char* bytes, int hash) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700255 // Allocate a new wrapper.
Elliott Hughes8044bf62010-05-17 22:34:46 -0700256 UniquePtr<InternedString> wrapper(new InternedString);
257 if (wrapper.get() == NULL) {
Elliott Hughesfb85a3a2011-01-16 16:06:11 -0800258 jniThrowOutOfMemoryError(env, NULL);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700259 return NULL;
260 }
261
262 // Create a copy of the UTF-8 bytes.
263 // TODO: sometimes we already know the length. Reuse it if so.
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700264 char* copy = new char[strlen(bytes) + 1];
265 if (copy == NULL) {
Elliott Hughesfb85a3a2011-01-16 16:06:11 -0800266 jniThrowOutOfMemoryError(env, NULL);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700267 return NULL;
268 }
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700269 strcpy(copy, bytes);
270 wrapper->bytes = copy;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700271
272 // Save the hash.
273 wrapper->hash = hash;
274
275 // To intern a string, we must first create a new string and then call
276 // intern() on it. We then keep a global reference to the interned string.
Elliott Hughes8044bf62010-05-17 22:34:46 -0700277 ScopedLocalRef<jstring> newString(env, env->NewStringUTF(bytes));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700278 if (env->ExceptionCheck()) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700279 return NULL;
280 }
281
282 // Call intern().
Elliott Hughes8044bf62010-05-17 22:34:46 -0700283 ScopedLocalRef<jstring> interned(env,
284 reinterpret_cast<jstring>(env->CallObjectMethod(newString.get(), internMethod)));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700285 if (env->ExceptionCheck()) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700286 return NULL;
287 }
288
289 // Create a global reference to the interned string.
Joel Dice0b00d812013-02-27 16:15:26 -0700290 wrapper->interned = reinterpret_cast<jstring>(env->NewGlobalRef(interned.get()));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700291 if (env->ExceptionCheck()) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700292 return NULL;
293 }
294
Elliott Hughes8044bf62010-05-17 22:34:46 -0700295 return wrapper.release();
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700296}
297
298/**
299 * Allocates a new bucket with one entry.
300 *
301 * @param entry to store in the bucket
302 * @returns a reference to the bucket
303 */
304static InternedString** newInternedStringBucket(InternedString* entry) {
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700305 InternedString** bucket = new InternedString*[2];
306 if (bucket != NULL) {
307 bucket[0] = entry;
308 bucket[1] = NULL;
309 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700310 return bucket;
311}
312
313/**
314 * Expands an interned string bucket and adds the given entry. Frees the
315 * provided bucket and returns a new one.
316 *
317 * @param existingBucket the bucket to replace
318 * @param entry to add to the bucket
319 * @returns a reference to the newly-allocated bucket containing the given entry
320 */
321static InternedString** expandInternedStringBucket(
322 InternedString** existingBucket, InternedString* entry) {
323 // Determine the size of the existing bucket.
324 int size = 0;
325 while (existingBucket[size]) size++;
326
327 // Allocate the new bucket with enough space for one more entry and
328 // a null terminator.
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700329 InternedString** newBucket = new InternedString*[size + 2];
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700330 if (newBucket == NULL) return NULL;
331
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700332 memcpy(newBucket, existingBucket, size * sizeof(InternedString*));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700333 newBucket[size] = entry;
334 newBucket[size + 1] = NULL;
Mikael Gullstrand08bfdd72013-05-14 13:15:34 +0200335 delete[] existingBucket;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700336
337 return newBucket;
338}
339
340/**
341 * Returns an interned string for the given UTF-8 string.
342 *
343 * @param bucket to search for s
344 * @param s null-terminated string to find
345 * @param hash of s
346 * @returns interned Java string equivalent of s or null if not found
347 */
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700348static jstring findInternedString(InternedString** bucket, const char* s, int hash) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700349 InternedString* current;
350 while ((current = *(bucket++)) != NULL) {
351 if (current->hash != hash) continue;
352 if (!strcmp(s, current->bytes)) return current->interned;
353 }
354 return NULL;
355}
356
357/**
358 * Returns an interned string for the given UTF-8 string.
359 *
360 * @param s null-terminated string to intern
361 * @returns interned Java string equivelent of s or NULL if s is null
362 */
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700363static jstring internString(JNIEnv* env, ParsingContext* parsingContext, const char* s) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700364 if (s == NULL) return NULL;
365
366 int hash = hashString(s);
367 int bucketIndex = hash & (BUCKET_COUNT - 1);
368
369 InternedString*** buckets = parsingContext->internedStrings;
370 InternedString** bucket = buckets[bucketIndex];
371 InternedString* internedString;
372
373 if (bucket) {
374 // We have a bucket already. Look for the given string.
375 jstring found = findInternedString(bucket, s, hash);
376 if (found) {
377 // We found it!
378 return found;
379 }
380
381 // We didn't find it. :(
382 // Create a new entry.
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700383 internedString = newInternedString(env, s, hash);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700384 if (internedString == NULL) return NULL;
385
386 // Expand the bucket.
387 bucket = expandInternedStringBucket(bucket, internedString);
388 if (bucket == NULL) {
Mikael Gullstrand08bfdd72013-05-14 13:15:34 +0200389 delete internedString;
Elliott Hughesfb85a3a2011-01-16 16:06:11 -0800390 jniThrowOutOfMemoryError(env, NULL);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700391 return NULL;
392 }
393
394 buckets[bucketIndex] = bucket;
395
396 return internedString->interned;
397 } else {
398 // We don't even have a bucket yet. Create an entry.
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700399 internedString = newInternedString(env, s, hash);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700400 if (internedString == NULL) return NULL;
401
402 // Create a new bucket with one entry.
403 bucket = newInternedStringBucket(internedString);
404 if (bucket == NULL) {
Mikael Gullstrand08bfdd72013-05-14 13:15:34 +0200405 delete internedString;
Elliott Hughesfb85a3a2011-01-16 16:06:11 -0800406 jniThrowOutOfMemoryError(env, NULL);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700407 return NULL;
408 }
409
410 buckets[bucketIndex] = bucket;
411
412 return internedString->interned;
413 }
414}
415
Elliott Hughes738f9502009-10-01 11:20:29 -0700416static void jniThrowExpatException(JNIEnv* env, XML_Error error) {
417 const char* message = XML_ErrorString(error);
418 jniThrowException(env, "org/apache/harmony/xml/ExpatException", message);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700419}
420
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700421/**
422 * Copies UTF-8 characters into the buffer. Returns the number of Java chars
423 * which were buffered.
424 *
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700425 * @returns number of UTF-16 characters which were copied
426 */
Elliott Hughes7cd67602012-05-03 17:21:04 -0700427static size_t fillBuffer(ParsingContext* parsingContext, const char* utf8, int byteCount) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700428 JNIEnv* env = parsingContext->env;
429
Elliott Hughes7cd67602012-05-03 17:21:04 -0700430 // Grow buffer if necessary (the length in bytes is always >= the length in chars).
431 jcharArray javaChars = parsingContext->ensureCapacity(byteCount);
Elliott Hughes6c1e5f42013-01-25 15:10:58 -0800432 if (javaChars == NULL) {
433 return -1;
434 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700435
Elliott Hughes7cd67602012-05-03 17:21:04 -0700436 // Decode UTF-8 characters into our char[].
437 ScopedCharArrayRW chars(env, javaChars);
438 if (chars.get() == NULL) {
Elliott Hughes64101122010-07-12 09:53:54 -0700439 return -1;
440 }
Elliott Hughes7cd67602012-05-03 17:21:04 -0700441 UErrorCode status = U_ZERO_ERROR;
442 UnicodeString utf16(UnicodeString::fromUTF8(StringPiece(utf8, byteCount)));
443 return utf16.extract(chars.get(), byteCount, status);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700444}
445
446/**
447 * Buffers the given text and passes it to the given method.
448 *
449 * @param method to pass the characters and length to with signature
450 * (char[], int)
451 * @param data parsing context
452 * @param text to copy into the buffer
453 * @param length of text to copy (in bytes)
454 */
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700455static void bufferAndInvoke(jmethodID method, void* data, const char* text, size_t length) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700456 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700457 JNIEnv* env = parsingContext->env;
458
459 // Bail out if a previously called handler threw an exception.
460 if (env->ExceptionCheck()) return;
461
462 // Buffer the element name.
463 size_t utf16length = fillBuffer(parsingContext, text, length);
464
465 // Invoke given method.
466 jobject javaParser = parsingContext->object;
467 jcharArray buffer = parsingContext->buffer;
468 env->CallVoidMethod(javaParser, method, buffer, utf16length);
469}
470
Joel Dice0b00d812013-02-27 16:15:26 -0700471static const char** toAttributes(jlong attributePointer) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700472 return reinterpret_cast<const char**>(static_cast<uintptr_t>(attributePointer));
473}
474
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700475/**
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800476 * The component parts of an attribute or element name.
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700477 */
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800478class ExpatElementName {
479public:
Joel Dice0b00d812013-02-27 16:15:26 -0700480 ExpatElementName(JNIEnv* env, ParsingContext* parsingContext, jlong attributePointer, jint index) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700481 const char** attributes = toAttributes(attributePointer);
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800482 const char* name = attributes[index * 2];
483 init(env, parsingContext, name);
484 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700485
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800486 ExpatElementName(JNIEnv* env, ParsingContext* parsingContext, const char* s) {
487 init(env, parsingContext, s);
488 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700489
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800490 ~ExpatElementName() {
491 free(mCopy);
492 }
493
494 /**
495 * Returns the namespace URI, like "http://www.w3.org/1999/xhtml".
496 * Possibly empty.
497 */
498 jstring uri() {
499 return internString(mEnv, mParsingContext, mUri);
500 }
501
502 /**
Jesse Wilsona1440512010-01-29 17:06:44 -0800503 * Returns the element or attribute local name, like "h1". Never empty. When
504 * namespace processing is disabled, this may contain a prefix, yielding a
505 * local name like "html:h1". In such cases, the qName will always be empty.
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800506 */
507 jstring localName() {
508 return internString(mEnv, mParsingContext, mLocalName);
509 }
510
511 /**
512 * Returns the namespace prefix, like "html". Possibly empty.
513 */
514 jstring qName() {
515 if (*mPrefix == 0) {
516 return localName();
517 }
518
519 // return prefix + ":" + localName
Craig Cornelius0ebd3422012-10-09 14:13:52 -0700520 ::LocalArray<1024> qName(strlen(mPrefix) + 1 + strlen(mLocalName) + 1);
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800521 snprintf(&qName[0], qName.size(), "%s:%s", mPrefix, mLocalName);
522 return internString(mEnv, mParsingContext, &qName[0]);
523 }
524
525 /**
526 * Returns true if this expat name has the same URI and local name.
527 */
528 bool matches(const char* uri, const char* localName) {
529 return strcmp(uri, mUri) == 0 && strcmp(localName, mLocalName) == 0;
530 }
531
532 /**
533 * Returns true if this expat name has the same qualified name.
534 */
535 bool matchesQName(const char* qName) {
Brian Carlstrombf87c562010-05-27 23:09:59 -0700536 const char* lastColon = strrchr(qName, ':');
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800537
Jesse Wilsona1440512010-01-29 17:06:44 -0800538 // Compare local names only if either:
539 // - the input qualified name doesn't have a colon (like "h1")
540 // - this element doesn't have a prefix. Such is the case when it
541 // doesn't belong to a namespace, or when this parser's namespace
542 // processing is disabled. In the latter case, this element's local
543 // name may still contain a colon (like "html:h1").
544 if (lastColon == NULL || *mPrefix == 0) {
545 return strcmp(qName, mLocalName) == 0;
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800546 }
547
Jesse Wilsona1440512010-01-29 17:06:44 -0800548 // Otherwise compare both prefix and local name
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800549 size_t prefixLength = lastColon - qName;
550 return strlen(mPrefix) == prefixLength
551 && strncmp(qName, mPrefix, prefixLength) == 0
552 && strcmp(lastColon + 1, mLocalName) == 0;
553 }
554
555private:
556 JNIEnv* mEnv;
557 ParsingContext* mParsingContext;
558 char* mCopy;
559 const char* mUri;
560 const char* mLocalName;
561 const char* mPrefix;
562
563 /**
564 * Decodes an Expat-encoded name of one of these three forms:
565 * "uri|localName|prefix" (example: "http://www.w3.org/1999/xhtml|h1|html")
566 * "uri|localName" (example: "http://www.w3.org/1999/xhtml|h1")
567 * "localName" (example: "h1")
568 */
569 void init(JNIEnv* env, ParsingContext* parsingContext, const char* s) {
570 mEnv = env;
571 mParsingContext = parsingContext;
572 mCopy = strdup(s);
573
574 // split the input into up to 3 parts: a|b|c
575 char* context = NULL;
576 char* a = strtok_r(mCopy, "|", &context);
577 char* b = strtok_r(NULL, "|", &context);
578 char* c = strtok_r(NULL, "|", &context);
579
580 if (c != NULL) { // input of the form "uri|localName|prefix"
581 mUri = a;
582 mLocalName = b;
583 mPrefix = c;
584 } else if (b != NULL) { // input of the form "uri|localName"
585 mUri = a;
586 mLocalName = b;
587 mPrefix = "";
588 } else { // input of the form "localName"
589 mLocalName = a;
590 mUri = "";
591 mPrefix = "";
592 }
593 }
Elliott Hughes7ca6fd02010-03-29 20:51:48 -0700594
595 // Disallow copy and assignment.
596 ExpatElementName(const ExpatElementName&);
597 void operator=(const ExpatElementName&);
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800598};
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700599
600/**
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700601 * Called by Expat at the start of an element. Delegates to the same method
602 * on the Java parser.
603 *
604 * @param data parsing context
605 * @param elementName "uri|localName" or "localName" for the current element
606 * @param attributes alternating attribute names and values. Like element
607 * names, attribute names follow the format "uri|localName" or "localName".
608 */
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700609static void startElement(void* data, const char* elementName, const char** attributes) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700610 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700611 JNIEnv* env = parsingContext->env;
612
613 // Bail out if a previously called handler threw an exception.
614 if (env->ExceptionCheck()) return;
615
616 // Count the number of attributes.
617 int count = 0;
Elliott Hughesb5bde2f2010-11-02 10:34:40 -0700618 while (attributes[count * 2]) count++;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700619
620 // Make the attributes available for the duration of this call.
621 parsingContext->attributes = attributes;
622 parsingContext->attributeCount = count;
623
624 jobject javaParser = parsingContext->object;
625
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800626 ExpatElementName e(env, parsingContext, elementName);
Jesse Wilson4a5c3fd2010-03-19 12:51:43 -0700627 jstring uri = parsingContext->processNamespaces ? e.uri() : emptyString;
628 jstring localName = parsingContext->processNamespaces ? e.localName() : emptyString;
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800629 jstring qName = e.qName();
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700630
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700631 parsingContext->stringStack.push(env, qName);
632 parsingContext->stringStack.push(env, uri);
633 parsingContext->stringStack.push(env, localName);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700634
Elliott Hughes8107b202014-02-05 22:16:48 +0000635 jlong attributesAddress = reinterpret_cast<jlong>(attributes);
636 env->CallVoidMethod(javaParser, startElementMethod, uri, localName, qName, attributesAddress, count);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700637
638 parsingContext->attributes = NULL;
639 parsingContext->attributeCount = -1;
640}
641
642/**
643 * Called by Expat at the end of an element. Delegates to the same method
644 * on the Java parser.
645 *
646 * @param data parsing context
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700647 * @param elementName "uri|localName" or "localName" for the current element;
648 * we assume that this matches the last data on our stack.
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700649 */
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700650static void endElement(void* data, const char* /*elementName*/) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700651 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700652 JNIEnv* env = parsingContext->env;
653
654 // Bail out if a previously called handler threw an exception.
655 if (env->ExceptionCheck()) return;
656
657 jobject javaParser = parsingContext->object;
658
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700659 jstring localName = parsingContext->stringStack.pop();
660 jstring uri = parsingContext->stringStack.pop();
661 jstring qName = parsingContext->stringStack.pop();
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700662
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800663 env->CallVoidMethod(javaParser, endElementMethod, uri, localName, qName);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700664}
665
666/**
667 * Called by Expat when it encounters text. Delegates to the same method
668 * on the Java parser. This may be called mutiple times with incremental pieces
669 * of the same contiguous block of text.
670 *
671 * @param data parsing context
672 * @param characters buffer containing encountered text
673 * @param length number of characters in the buffer
674 */
675static void text(void* data, const char* characters, int length) {
676 bufferAndInvoke(textMethod, data, characters, length);
677}
678
679/**
680 * Called by Expat when it encounters a comment. Delegates to the same method
681 * on the Java parser.
682
683 * @param data parsing context
684 * @param comment 0-terminated
685 */
686static void comment(void* data, const char* comment) {
687 bufferAndInvoke(commentMethod, data, comment, strlen(comment));
688}
689
690/**
691 * Called by Expat at the beginning of a namespace mapping.
692 *
693 * @param data parsing context
694 * @param prefix null-terminated namespace prefix used in the XML
695 * @param uri of the namespace
696 */
697static void startNamespace(void* data, const char* prefix, const char* uri) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700698 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700699 JNIEnv* env = parsingContext->env;
700
701 // Bail out if a previously called handler threw an exception.
702 if (env->ExceptionCheck()) return;
703
704 jstring internedPrefix = emptyString;
705 if (prefix != NULL) {
706 internedPrefix = internString(env, parsingContext, prefix);
707 if (env->ExceptionCheck()) return;
708 }
709
710 jstring internedUri = emptyString;
711 if (uri != NULL) {
712 internedUri = internString(env, parsingContext, uri);
713 if (env->ExceptionCheck()) return;
714 }
715
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700716 parsingContext->stringStack.push(env, internedPrefix);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700717
718 jobject javaParser = parsingContext->object;
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700719 env->CallVoidMethod(javaParser, startNamespaceMethod, internedPrefix, internedUri);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700720}
721
722/**
723 * Called by Expat at the end of a namespace mapping.
724 *
725 * @param data parsing context
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700726 * @param prefix null-terminated namespace prefix used in the XML;
727 * we assume this is the same as the last prefix on the stack.
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700728 */
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700729static void endNamespace(void* data, const char* /*prefix*/) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700730 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700731 JNIEnv* env = parsingContext->env;
732
733 // Bail out if a previously called handler threw an exception.
734 if (env->ExceptionCheck()) return;
735
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700736 jstring internedPrefix = parsingContext->stringStack.pop();
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700737
738 jobject javaParser = parsingContext->object;
739 env->CallVoidMethod(javaParser, endNamespaceMethod, internedPrefix);
740}
741
742/**
743 * Called by Expat at the beginning of a CDATA section.
744 *
745 * @param data parsing context
746 */
747static void startCdata(void* data) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700748 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700749 JNIEnv* env = parsingContext->env;
750
751 // Bail out if a previously called handler threw an exception.
752 if (env->ExceptionCheck()) return;
753
754 jobject javaParser = parsingContext->object;
755 env->CallVoidMethod(javaParser, startCdataMethod);
756}
757
758/**
759 * Called by Expat at the end of a CDATA section.
760 *
761 * @param data parsing context
762 */
763static void endCdata(void* data) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700764 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700765 JNIEnv* env = parsingContext->env;
766
767 // Bail out if a previously called handler threw an exception.
768 if (env->ExceptionCheck()) return;
769
770 jobject javaParser = parsingContext->object;
771 env->CallVoidMethod(javaParser, endCdataMethod);
772}
773
774/**
775 * Called by Expat at the beginning of a DOCTYPE section.
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700776 * Expat gives us 'hasInternalSubset', but the Java API doesn't expect it, so we don't need it.
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700777 */
778static void startDtd(void* data, const char* name,
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700779 const char* systemId, const char* publicId, int /*hasInternalSubset*/) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700780 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700781 JNIEnv* env = parsingContext->env;
782
783 // Bail out if a previously called handler threw an exception.
784 if (env->ExceptionCheck()) return;
785
786 jstring javaName = internString(env, parsingContext, name);
787 if (env->ExceptionCheck()) return;
788
789 jstring javaPublicId = internString(env, parsingContext, publicId);
790 if (env->ExceptionCheck()) return;
791
792 jstring javaSystemId = internString(env, parsingContext, systemId);
793 if (env->ExceptionCheck()) return;
794
795 jobject javaParser = parsingContext->object;
796 env->CallVoidMethod(javaParser, startDtdMethod, javaName, javaPublicId,
797 javaSystemId);
798}
799
800/**
801 * Called by Expat at the end of a DOCTYPE section.
802 *
803 * @param data parsing context
804 */
805static void endDtd(void* data) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700806 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700807 JNIEnv* env = parsingContext->env;
808
809 // Bail out if a previously called handler threw an exception.
810 if (env->ExceptionCheck()) return;
811
812 jobject javaParser = parsingContext->object;
813 env->CallVoidMethod(javaParser, endDtdMethod);
814}
815
816/**
817 * Called by Expat when it encounters processing instructions.
818 *
819 * @param data parsing context
820 * @param target of the instruction
821 * @param instructionData
822 */
Elliott Hughes8044bf62010-05-17 22:34:46 -0700823static void processingInstruction(void* data, const char* target, const char* instructionData) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700824 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700825 JNIEnv* env = parsingContext->env;
826
827 // Bail out if a previously called handler threw an exception.
828 if (env->ExceptionCheck()) return;
829
830 jstring javaTarget = internString(env, parsingContext, target);
831 if (env->ExceptionCheck()) return;
832
Elliott Hughes8044bf62010-05-17 22:34:46 -0700833 ScopedLocalRef<jstring> javaInstructionData(env, env->NewStringUTF(instructionData));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700834 if (env->ExceptionCheck()) return;
835
836 jobject javaParser = parsingContext->object;
Elliott Hughes8044bf62010-05-17 22:34:46 -0700837 env->CallVoidMethod(javaParser, processingInstructionMethod, javaTarget, javaInstructionData.get());
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700838}
839
840/**
841 * Creates a new entity parser.
842 *
843 * @param object the Java ExpatParser instance
844 * @param parentParser pointer
845 * @param javaEncoding the character encoding name
846 * @param javaContext that was provided to handleExternalEntity
847 * @returns the pointer to the C Expat entity parser
848 */
Joel Dice0b00d812013-02-27 16:15:26 -0700849static jlong ExpatParser_createEntityParser(JNIEnv* env, jobject, jlong parentParser, jstring javaContext) {
Elliott Hughes05960872010-05-26 17:45:07 -0700850 ScopedUtfChars context(env, javaContext);
851 if (context.c_str() == NULL) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700852 return 0;
853 }
854
Joel Dice0b00d812013-02-27 16:15:26 -0700855 XML_Parser parent = toXMLParser(parentParser);
Elliott Hughes05960872010-05-26 17:45:07 -0700856 XML_Parser entityParser = XML_ExternalEntityParserCreate(parent, context.c_str(), NULL);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700857 if (entityParser == NULL) {
Elliott Hughesfb85a3a2011-01-16 16:06:11 -0800858 jniThrowOutOfMemoryError(env, NULL);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700859 }
860
Joel Dice0b00d812013-02-27 16:15:26 -0700861 return fromXMLParser(entityParser);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700862}
863
864/**
865 * Handles external entities. We ignore the "base" URI and keep track of it
866 * ourselves.
867 */
868static int handleExternalEntity(XML_Parser parser, const char* context,
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700869 const char*, const char* systemId, const char* publicId) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700870 ParsingContext* parsingContext = toParsingContext(parser);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700871 jobject javaParser = parsingContext->object;
872 JNIEnv* env = parsingContext->env;
873 jobject object = parsingContext->object;
874
875 // Bail out if a previously called handler threw an exception.
876 if (env->ExceptionCheck()) {
877 return XML_STATUS_ERROR;
878 }
879
Elliott Hughes8044bf62010-05-17 22:34:46 -0700880 ScopedLocalRef<jstring> javaSystemId(env, env->NewStringUTF(systemId));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700881 if (env->ExceptionCheck()) {
882 return XML_STATUS_ERROR;
883 }
Elliott Hughes8044bf62010-05-17 22:34:46 -0700884 ScopedLocalRef<jstring> javaPublicId(env, env->NewStringUTF(publicId));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700885 if (env->ExceptionCheck()) {
886 return XML_STATUS_ERROR;
887 }
Elliott Hughes8044bf62010-05-17 22:34:46 -0700888 ScopedLocalRef<jstring> javaContext(env, env->NewStringUTF(context));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700889 if (env->ExceptionCheck()) {
890 return XML_STATUS_ERROR;
891 }
892
893 // Pass the wrapped parser and both strings to java.
Elliott Hughes8044bf62010-05-17 22:34:46 -0700894 env->CallVoidMethod(javaParser, handleExternalEntityMethod, javaContext.get(),
895 javaPublicId.get(), javaSystemId.get());
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700896
897 /*
898 * Parsing the external entity leaves parsingContext->env and object set to
899 * NULL, so we need to restore both.
900 *
901 * TODO: consider restoring the original env and object instead of setting
902 * them to NULL in the append() functions.
903 */
904 parsingContext->env = env;
905 parsingContext->object = object;
906
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700907 return env->ExceptionCheck() ? XML_STATUS_ERROR : XML_STATUS_OK;
908}
909
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700910/**
911 * Expat gives us 'base', but the Java API doesn't expect it, so we don't need it.
912 */
913static void unparsedEntityDecl(void* data, const char* name, const char* /*base*/, const char* systemId, const char* publicId, const char* notationName) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700914 ParsingContext* parsingContext = toParsingContext(data);
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800915 jobject javaParser = parsingContext->object;
916 JNIEnv* env = parsingContext->env;
Elliott Hughes99c59bf2010-05-17 16:22:04 -0700917
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800918 // Bail out if a previously called handler threw an exception.
919 if (env->ExceptionCheck()) return;
Elliott Hughes99c59bf2010-05-17 16:22:04 -0700920
Elliott Hughes8044bf62010-05-17 22:34:46 -0700921 ScopedLocalRef<jstring> javaName(env, env->NewStringUTF(name));
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800922 if (env->ExceptionCheck()) return;
Elliott Hughes8044bf62010-05-17 22:34:46 -0700923 ScopedLocalRef<jstring> javaPublicId(env, env->NewStringUTF(publicId));
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800924 if (env->ExceptionCheck()) return;
Elliott Hughes8044bf62010-05-17 22:34:46 -0700925 ScopedLocalRef<jstring> javaSystemId(env, env->NewStringUTF(systemId));
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800926 if (env->ExceptionCheck()) return;
Elliott Hughes8044bf62010-05-17 22:34:46 -0700927 ScopedLocalRef<jstring> javaNotationName(env, env->NewStringUTF(notationName));
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800928 if (env->ExceptionCheck()) return;
Elliott Hughes99c59bf2010-05-17 16:22:04 -0700929
Elliott Hughes8044bf62010-05-17 22:34:46 -0700930 env->CallVoidMethod(javaParser, unparsedEntityDeclMethod, javaName.get(), javaPublicId.get(), javaSystemId.get(), javaNotationName.get());
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800931}
932
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700933/**
934 * Expat gives us 'base', but the Java API doesn't expect it, so we don't need it.
935 */
936static void notationDecl(void* data, const char* name, const char* /*base*/, const char* systemId, const char* publicId) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700937 ParsingContext* parsingContext = toParsingContext(data);
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800938 jobject javaParser = parsingContext->object;
939 JNIEnv* env = parsingContext->env;
Elliott Hughes99c59bf2010-05-17 16:22:04 -0700940
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800941 // Bail out if a previously called handler threw an exception.
942 if (env->ExceptionCheck()) return;
Elliott Hughes99c59bf2010-05-17 16:22:04 -0700943
Elliott Hughes8044bf62010-05-17 22:34:46 -0700944 ScopedLocalRef<jstring> javaName(env, env->NewStringUTF(name));
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800945 if (env->ExceptionCheck()) return;
Elliott Hughes8044bf62010-05-17 22:34:46 -0700946 ScopedLocalRef<jstring> javaPublicId(env, env->NewStringUTF(publicId));
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800947 if (env->ExceptionCheck()) return;
Elliott Hughes8044bf62010-05-17 22:34:46 -0700948 ScopedLocalRef<jstring> javaSystemId(env, env->NewStringUTF(systemId));
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800949 if (env->ExceptionCheck()) return;
Elliott Hughes99c59bf2010-05-17 16:22:04 -0700950
Elliott Hughes8044bf62010-05-17 22:34:46 -0700951 env->CallVoidMethod(javaParser, notationDeclMethod, javaName.get(), javaPublicId.get(), javaSystemId.get());
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800952}
953
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700954/**
Jesse Wilsona1440512010-01-29 17:06:44 -0800955 * Creates a new Expat parser. Called from the Java ExpatParser constructor.
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700956 *
957 * @param object the Java ExpatParser instance
958 * @param javaEncoding the character encoding name
959 * @param processNamespaces true if the parser should handle namespaces
960 * @returns the pointer to the C Expat parser
961 */
Joel Dice0b00d812013-02-27 16:15:26 -0700962static jlong ExpatParser_initialize(JNIEnv* env, jobject object, jstring javaEncoding,
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700963 jboolean processNamespaces) {
964 // Allocate parsing context.
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700965 UniquePtr<ParsingContext> context(new ParsingContext(object));
966 if (context.get() == NULL) {
Elliott Hughesfb85a3a2011-01-16 16:06:11 -0800967 jniThrowOutOfMemoryError(env, NULL);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700968 return 0;
969 }
970
Joel Dice0b00d812013-02-27 16:15:26 -0700971 context->processNamespaces = processNamespaces;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700972
973 // Create a parser.
974 XML_Parser parser;
Elliott Hughes05960872010-05-26 17:45:07 -0700975 ScopedUtfChars encoding(env, javaEncoding);
976 if (encoding.c_str() == NULL) {
977 return 0;
978 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700979 if (processNamespaces) {
980 // Use '|' to separate URIs from local names.
Elliott Hughes05960872010-05-26 17:45:07 -0700981 parser = XML_ParserCreateNS(encoding.c_str(), '|');
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700982 } else {
Elliott Hughes05960872010-05-26 17:45:07 -0700983 parser = XML_ParserCreate(encoding.c_str());
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700984 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700985
986 if (parser != NULL) {
987 if (processNamespaces) {
988 XML_SetNamespaceDeclHandler(parser, startNamespace, endNamespace);
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800989 XML_SetReturnNSTriplet(parser, 1);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700990 }
991
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700992 XML_SetCdataSectionHandler(parser, startCdata, endCdata);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700993 XML_SetCharacterDataHandler(parser, text);
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800994 XML_SetCommentHandler(parser, comment);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700995 XML_SetDoctypeDeclHandler(parser, startDtd, endDtd);
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800996 XML_SetElementHandler(parser, startElement, endElement);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700997 XML_SetExternalEntityRefHandler(parser, handleExternalEntity);
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800998 XML_SetNotationDeclHandler(parser, notationDecl);
999 XML_SetProcessingInstructionHandler(parser, processingInstruction);
1000 XML_SetUnparsedEntityDeclHandler(parser, unparsedEntityDecl);
Elliott Hughes4eafbcc2010-06-21 16:52:06 -07001001 XML_SetUserData(parser, context.release());
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001002 } else {
Elliott Hughesfb85a3a2011-01-16 16:06:11 -08001003 jniThrowOutOfMemoryError(env, NULL);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001004 return 0;
1005 }
1006
Joel Dice0b00d812013-02-27 16:15:26 -07001007 return fromXMLParser(parser);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001008}
1009
1010/**
Jesse Wilsondabcbd62010-11-10 17:34:18 -08001011 * Decodes the bytes as characters and parse the characters as XML. This
1012 * performs character decoding using the charset specified at XML_Parser
1013 * creation. For Java chars, that charset must be UTF-16 so that a Java char[]
1014 * can be reinterpreted as a UTF-16 encoded byte[]. appendBytes, appendChars
1015 * and appendString all call through this method.
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001016 */
Joel Dice0b00d812013-02-27 16:15:26 -07001017static void append(JNIEnv* env, jobject object, jlong pointer,
Elliott Hughes99c59bf2010-05-17 16:22:04 -07001018 const char* bytes, size_t byteOffset, size_t byteCount, jboolean isFinal) {
Joel Dice0b00d812013-02-27 16:15:26 -07001019 XML_Parser parser = toXMLParser(pointer);
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001020 ParsingContext* context = toParsingContext(parser);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001021 context->env = env;
1022 context->object = object;
Elliott Hughes05960872010-05-26 17:45:07 -07001023 if (!XML_Parse(parser, bytes + byteOffset, byteCount, isFinal) && !env->ExceptionCheck()) {
Elliott Hughes738f9502009-10-01 11:20:29 -07001024 jniThrowExpatException(env, XML_GetErrorCode(parser));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001025 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001026 context->object = NULL;
1027 context->env = NULL;
1028}
1029
Joel Dice0b00d812013-02-27 16:15:26 -07001030static void ExpatParser_appendBytes(JNIEnv* env, jobject object, jlong pointer,
Elliott Hughes99c59bf2010-05-17 16:22:04 -07001031 jbyteArray xml, jint byteOffset, jint byteCount) {
Elliott Hughesebca53a2010-05-20 20:54:45 -07001032 ScopedByteArrayRO byteArray(env, xml);
Elliott Hughes64101122010-07-12 09:53:54 -07001033 if (byteArray.get() == NULL) {
1034 return;
1035 }
1036
Elliott Hughes99c59bf2010-05-17 16:22:04 -07001037 const char* bytes = reinterpret_cast<const char*>(byteArray.get());
1038 append(env, object, pointer, bytes, byteOffset, byteCount, XML_FALSE);
1039}
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001040
Joel Dice0b00d812013-02-27 16:15:26 -07001041static void ExpatParser_appendChars(JNIEnv* env, jobject object, jlong pointer,
Elliott Hughes99c59bf2010-05-17 16:22:04 -07001042 jcharArray xml, jint charOffset, jint charCount) {
Elliott Hughesebca53a2010-05-20 20:54:45 -07001043 ScopedCharArrayRO charArray(env, xml);
Elliott Hughes64101122010-07-12 09:53:54 -07001044 if (charArray.get() == NULL) {
1045 return;
1046 }
1047
Elliott Hughes99c59bf2010-05-17 16:22:04 -07001048 const char* bytes = reinterpret_cast<const char*>(charArray.get());
1049 size_t byteOffset = 2 * charOffset;
1050 size_t byteCount = 2 * charCount;
1051 append(env, object, pointer, bytes, byteOffset, byteCount, XML_FALSE);
1052}
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001053
Joel Dice0b00d812013-02-27 16:15:26 -07001054static void ExpatParser_appendString(JNIEnv* env, jobject object, jlong pointer, jstring javaXml, jboolean isFinal) {
Elliott Hughesfe711d62011-04-22 15:56:03 -07001055 ScopedStringChars xml(env, javaXml);
1056 if (xml.get() == NULL) {
1057 return;
1058 }
1059 const char* bytes = reinterpret_cast<const char*>(xml.get());
1060 size_t byteCount = 2 * xml.size();
Elliott Hughes99c59bf2010-05-17 16:22:04 -07001061 append(env, object, pointer, bytes, 0, byteCount, isFinal);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001062}
1063
1064/**
1065 * Releases parser only.
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001066 */
Joel Dice0b00d812013-02-27 16:15:26 -07001067static void ExpatParser_releaseParser(JNIEnv*, jobject, jlong address) {
1068 XML_ParserFree(toXMLParser(address));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001069}
1070
1071/**
1072 * Cleans up after the parser. Called at garbage collection time.
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001073 */
Joel Dice0b00d812013-02-27 16:15:26 -07001074static void ExpatParser_release(JNIEnv* env, jobject, jlong address) {
1075 XML_Parser parser = toXMLParser(address);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001076
Joel Dice0b00d812013-02-27 16:15:26 -07001077 ParsingContext* context = toParsingContext(parser);
1078 context->env = env;
1079 delete context;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001080
Joel Dice0b00d812013-02-27 16:15:26 -07001081 XML_ParserFree(parser);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001082}
1083
Joel Dice0b00d812013-02-27 16:15:26 -07001084static int ExpatParser_line(JNIEnv*, jobject, jlong address) {
1085 return XML_GetCurrentLineNumber(toXMLParser(address));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001086}
1087
Joel Dice0b00d812013-02-27 16:15:26 -07001088static int ExpatParser_column(JNIEnv*, jobject, jlong address) {
1089 return XML_GetCurrentColumnNumber(toXMLParser(address));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001090}
1091
1092/**
1093 * Gets the URI of the attribute at the given index.
1094 *
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001095 * @param attributePointer to the attribute array
1096 * @param index of the attribute
1097 * @returns interned Java string containing attribute's URI
1098 */
Joel Dice0b00d812013-02-27 16:15:26 -07001099static jstring ExpatAttributes_getURI(JNIEnv* env, jobject, jlong address,
1100 jlong attributePointer, jint index) {
1101 ParsingContext* context = toParsingContext(toXMLParser(address));
1102 return ExpatElementName(env, context, attributePointer, index).uri();
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001103}
1104
1105/**
1106 * Gets the local name of the attribute at the given index.
1107 *
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001108 * @param attributePointer to the attribute array
1109 * @param index of the attribute
1110 * @returns interned Java string containing attribute's local name
1111 */
Joel Dice0b00d812013-02-27 16:15:26 -07001112static jstring ExpatAttributes_getLocalName(JNIEnv* env, jobject, jlong address,
1113 jlong attributePointer, jint index) {
1114 ParsingContext* context = toParsingContext(toXMLParser(address));
1115 return ExpatElementName(env, context, attributePointer, index).localName();
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001116}
1117
1118/**
1119 * Gets the qualified name of the attribute at the given index.
1120 *
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001121 * @param attributePointer to the attribute array
1122 * @param index of the attribute
1123 * @returns interned Java string containing attribute's local name
1124 */
Joel Dice0b00d812013-02-27 16:15:26 -07001125static jstring ExpatAttributes_getQName(JNIEnv* env, jobject, jlong address,
1126 jlong attributePointer, jint index) {
1127 ParsingContext* context = toParsingContext(toXMLParser(address));
1128 return ExpatElementName(env, context, attributePointer, index).qName();
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001129}
1130
1131/**
1132 * Gets the value of the attribute at the given index.
1133 *
1134 * @param object Java ExpatParser instance
1135 * @param attributePointer to the attribute array
1136 * @param index of the attribute
1137 * @returns Java string containing attribute's value
1138 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001139static jstring ExpatAttributes_getValueByIndex(JNIEnv* env, jobject,
Joel Dice0b00d812013-02-27 16:15:26 -07001140 jlong attributePointer, jint index) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001141 const char** attributes = toAttributes(attributePointer);
Elliott Hughesb5bde2f2010-11-02 10:34:40 -07001142 const char* value = attributes[(index * 2) + 1];
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001143 return env->NewStringUTF(value);
1144}
1145
1146/**
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001147 * Gets the index of the attribute with the given qualified name.
1148 *
1149 * @param attributePointer to the attribute array
1150 * @param qName to look for
1151 * @returns index of attribute with the given uri and local name or -1 if not
1152 * found
1153 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001154static jint ExpatAttributes_getIndexForQName(JNIEnv* env, jobject,
Joel Dice0b00d812013-02-27 16:15:26 -07001155 jlong attributePointer, jstring qName) {
Elliott Hughes05960872010-05-26 17:45:07 -07001156 ScopedUtfChars qNameBytes(env, qName);
1157 if (qNameBytes.c_str() == NULL) {
Jesse Wilsondf36e8c2010-01-23 00:49:53 -08001158 return -1;
1159 }
1160
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001161 const char** attributes = toAttributes(attributePointer);
Jesse Wilsondf36e8c2010-01-23 00:49:53 -08001162 int found = -1;
1163 for (int index = 0; attributes[index * 2]; ++index) {
Elliott Hughes05960872010-05-26 17:45:07 -07001164 if (ExpatElementName(NULL, NULL, attributePointer, index).matchesQName(qNameBytes.c_str())) {
Jesse Wilsondf36e8c2010-01-23 00:49:53 -08001165 found = index;
1166 break;
1167 }
1168 }
1169
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001170 return found;
1171}
1172
1173/**
1174 * Gets the index of the attribute with the given URI and name.
1175 *
1176 * @param attributePointer to the attribute array
1177 * @param uri to look for
1178 * @param localName to look for
1179 * @returns index of attribute with the given uri and local name or -1 if not
1180 * found
1181 */
Joel Dice0b00d812013-02-27 16:15:26 -07001182static jint ExpatAttributes_getIndex(JNIEnv* env, jobject, jlong attributePointer,
Elliott Hughes05960872010-05-26 17:45:07 -07001183 jstring uri, jstring localName) {
1184 ScopedUtfChars uriBytes(env, uri);
1185 if (uriBytes.c_str() == NULL) {
1186 return -1;
1187 }
1188
1189 ScopedUtfChars localNameBytes(env, localName);
1190 if (localNameBytes.c_str() == NULL) {
1191 return -1;
1192 }
1193
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001194 const char** attributes = toAttributes(attributePointer);
Jesse Wilsondf36e8c2010-01-23 00:49:53 -08001195 for (int index = 0; attributes[index * 2]; ++index) {
Elliott Hughes05960872010-05-26 17:45:07 -07001196 if (ExpatElementName(NULL, NULL, attributePointer, index).matches(uriBytes.c_str(),
1197 localNameBytes.c_str())) {
1198 return index;
Jesse Wilsondf36e8c2010-01-23 00:49:53 -08001199 }
1200 }
Elliott Hughes05960872010-05-26 17:45:07 -07001201 return -1;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001202}
1203
1204/**
1205 * Gets the value of the attribute with the given qualified name.
1206 *
1207 * @param attributePointer to the attribute array
1208 * @param uri to look for
1209 * @param localName to look for
1210 * @returns value of attribute with the given uri and local name or NULL if not
1211 * found
1212 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001213static jstring ExpatAttributes_getValueForQName(JNIEnv* env, jobject clazz,
Joel Dice0b00d812013-02-27 16:15:26 -07001214 jlong attributePointer, jstring qName) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001215 jint index = ExpatAttributes_getIndexForQName(env, clazz, attributePointer, qName);
1216 return index == -1 ? NULL
1217 : ExpatAttributes_getValueByIndex(env, clazz, attributePointer, index);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001218}
1219
1220/**
1221 * Gets the value of the attribute with the given URI and name.
1222 *
1223 * @param attributePointer to the attribute array
1224 * @param uri to look for
1225 * @param localName to look for
1226 * @returns value of attribute with the given uri and local name or NULL if not
1227 * found
1228 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001229static jstring ExpatAttributes_getValue(JNIEnv* env, jobject clazz,
Joel Dice0b00d812013-02-27 16:15:26 -07001230 jlong attributePointer, jstring uri, jstring localName) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001231 jint index = ExpatAttributes_getIndex(env, clazz, attributePointer, uri, localName);
1232 return index == -1 ? NULL
1233 : ExpatAttributes_getValueByIndex(env, clazz, attributePointer, index);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001234}
1235
1236/**
1237 * Clones an array of strings. Uses one contiguous block of memory so as to
1238 * maximize performance.
Elliott Hughes91499042010-06-21 18:06:12 -07001239 *
1240 * @param address char** to clone
1241 * @param count number of attributes
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001242 */
Joel Dice0b00d812013-02-27 16:15:26 -07001243static jlong ExpatParser_cloneAttributes(JNIEnv* env, jobject, jlong address, jint count) {
Elliott Hughes91499042010-06-21 18:06:12 -07001244 const char** source = reinterpret_cast<const char**>(static_cast<uintptr_t>(address));
Elliott Hughesb5bde2f2010-11-02 10:34:40 -07001245 count *= 2;
Elliott Hughes91499042010-06-21 18:06:12 -07001246
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001247 // Figure out how big the buffer needs to be.
1248 int arraySize = (count + 1) * sizeof(char*);
1249 int totalSize = arraySize;
1250 int stringLengths[count];
1251 for (int i = 0; i < count; i++) {
1252 int length = strlen(source[i]);
1253 stringLengths[i] = length;
1254 totalSize += length + 1;
1255 }
1256
Elliott Hughes4eafbcc2010-06-21 16:52:06 -07001257 char* buffer = new char[totalSize];
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001258 if (buffer == NULL) {
Elliott Hughesfb85a3a2011-01-16 16:06:11 -08001259 jniThrowOutOfMemoryError(env, NULL);
Olivier Bailly26e0cc02010-11-16 19:27:42 -08001260 return 0;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001261 }
1262
1263 // Array is at the beginning of the buffer.
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001264 char** clonedArray = reinterpret_cast<char**>(buffer);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001265 clonedArray[count] = NULL; // null terminate
1266
Elliott Hughes91499042010-06-21 18:06:12 -07001267 // String data follows immediately after.
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001268 char* destinationString = buffer + arraySize;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001269 for (int i = 0; i < count; i++) {
1270 const char* sourceString = source[i];
1271 int stringLength = stringLengths[i];
1272 memcpy(destinationString, sourceString, stringLength + 1);
1273 clonedArray[i] = destinationString;
1274 destinationString += stringLength + 1;
1275 }
1276
Joel Dice0b00d812013-02-27 16:15:26 -07001277 return reinterpret_cast<uintptr_t>(buffer);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001278}
1279
1280/**
1281 * Frees cloned attributes.
1282 */
Joel Dice0b00d812013-02-27 16:15:26 -07001283static void ExpatAttributes_freeAttributes(JNIEnv*, jobject, jlong pointer) {
Elliott Hughes4eafbcc2010-06-21 16:52:06 -07001284 delete[] reinterpret_cast<char*>(static_cast<uintptr_t>(pointer));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001285}
1286
1287/**
1288 * Called when we initialize our Java parser class.
1289 *
1290 * @param clazz Java ExpatParser class
1291 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001292static void ExpatParser_staticInitialize(JNIEnv* env, jobject classObject, jstring empty) {
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001293 jclass clazz = reinterpret_cast<jclass>(classObject);
1294 startElementMethod = env->GetMethodID(clazz, "startElement",
Elliott Hughes8107b202014-02-05 22:16:48 +00001295 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JI)V");
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001296 if (startElementMethod == NULL) return;
Elliott Hughes99c59bf2010-05-17 16:22:04 -07001297
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001298 endElementMethod = env->GetMethodID(clazz, "endElement",
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001299 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
1300 if (endElementMethod == NULL) return;
1301
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001302 textMethod = env->GetMethodID(clazz, "text", "([CI)V");
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001303 if (textMethod == NULL) return;
1304
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001305 commentMethod = env->GetMethodID(clazz, "comment", "([CI)V");
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001306 if (commentMethod == NULL) return;
1307
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001308 startCdataMethod = env->GetMethodID(clazz, "startCdata", "()V");
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001309 if (startCdataMethod == NULL) return;
1310
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001311 endCdataMethod = env->GetMethodID(clazz, "endCdata", "()V");
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001312 if (endCdataMethod == NULL) return;
1313
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001314 startDtdMethod = env->GetMethodID(clazz, "startDtd",
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001315 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
1316 if (startDtdMethod == NULL) return;
1317
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001318 endDtdMethod = env->GetMethodID(clazz, "endDtd", "()V");
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001319 if (endDtdMethod == NULL) return;
1320
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001321 startNamespaceMethod = env->GetMethodID(clazz, "startNamespace",
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001322 "(Ljava/lang/String;Ljava/lang/String;)V");
1323 if (startNamespaceMethod == NULL) return;
1324
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001325 endNamespaceMethod = env->GetMethodID(clazz, "endNamespace",
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001326 "(Ljava/lang/String;)V");
1327 if (endNamespaceMethod == NULL) return;
1328
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001329 processingInstructionMethod = env->GetMethodID(clazz,
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001330 "processingInstruction", "(Ljava/lang/String;Ljava/lang/String;)V");
1331 if (processingInstructionMethod == NULL) return;
1332
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001333 handleExternalEntityMethod = env->GetMethodID(clazz,
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001334 "handleExternalEntity",
1335 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
1336 if (handleExternalEntityMethod == NULL) return;
1337
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001338 notationDeclMethod = env->GetMethodID(clazz, "notationDecl",
1339 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
1340 if (notationDeclMethod == NULL) return;
1341
1342 unparsedEntityDeclMethod = env->GetMethodID(clazz, "unparsedEntityDecl",
1343 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
1344 if (unparsedEntityDeclMethod == NULL) return;
1345
Elliott Hughesa9f5c162010-06-16 16:32:18 -07001346 internMethod = env->GetMethodID(JniConstants::stringClass, "intern", "()Ljava/lang/String;");
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001347 if (internMethod == NULL) return;
1348
1349 // Reference to "".
Joel Dice0b00d812013-02-27 16:15:26 -07001350 emptyString = reinterpret_cast<jstring>(env->NewGlobalRef(empty));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001351}
1352
1353static JNINativeMethod parserMethods[] = {
Joel Dice0b00d812013-02-27 16:15:26 -07001354 NATIVE_METHOD(ExpatParser, appendString, "(JLjava/lang/String;Z)V"),
1355 NATIVE_METHOD(ExpatParser, appendBytes, "(J[BII)V"),
1356 NATIVE_METHOD(ExpatParser, appendChars, "(J[CII)V"),
1357 NATIVE_METHOD(ExpatParser, cloneAttributes, "(JI)J"),
1358 NATIVE_METHOD(ExpatParser, column, "(J)I"),
1359 NATIVE_METHOD(ExpatParser, createEntityParser, "(JLjava/lang/String;)J"),
1360 NATIVE_METHOD(ExpatParser, initialize, "(Ljava/lang/String;Z)J"),
1361 NATIVE_METHOD(ExpatParser, line, "(J)I"),
1362 NATIVE_METHOD(ExpatParser, release, "(J)V"),
1363 NATIVE_METHOD(ExpatParser, releaseParser, "(J)V"),
Elliott Hughese22935d2010-08-12 17:27:27 -07001364 NATIVE_METHOD(ExpatParser, staticInitialize, "(Ljava/lang/String;)V"),
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001365};
1366
1367static JNINativeMethod attributeMethods[] = {
Joel Dice0b00d812013-02-27 16:15:26 -07001368 NATIVE_METHOD(ExpatAttributes, freeAttributes, "(J)V"),
1369 NATIVE_METHOD(ExpatAttributes, getIndexForQName, "(JLjava/lang/String;)I"),
1370 NATIVE_METHOD(ExpatAttributes, getIndex, "(JLjava/lang/String;Ljava/lang/String;)I"),
1371 NATIVE_METHOD(ExpatAttributes, getLocalName, "(JJI)Ljava/lang/String;"),
1372 NATIVE_METHOD(ExpatAttributes, getQName, "(JJI)Ljava/lang/String;"),
1373 NATIVE_METHOD(ExpatAttributes, getURI, "(JJI)Ljava/lang/String;"),
1374 NATIVE_METHOD(ExpatAttributes, getValueByIndex, "(JI)Ljava/lang/String;"),
1375 NATIVE_METHOD(ExpatAttributes, getValueForQName, "(JLjava/lang/String;)Ljava/lang/String;"),
1376 NATIVE_METHOD(ExpatAttributes, getValue, "(JLjava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001377};
Elliott Hughes7cd67602012-05-03 17:21:04 -07001378void register_org_apache_harmony_xml_ExpatParser(JNIEnv* env) {
1379 jniRegisterNativeMethods(env, "org/apache/harmony/xml/ExpatParser", parserMethods, NELEM(parserMethods));
1380 jniRegisterNativeMethods(env, "org/apache/harmony/xml/ExpatAttributes", attributeMethods, NELEM(attributeMethods));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001381}