blob: c2862e1813cb4e253c91daf530c643814d4d4790 [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 Hughes845ce3c2009-11-13 17:07:00 -080021#include "LocalArray.h"
Elliott Hughes4e3714f2010-06-22 14:32:47 -070022#include "ScopedJavaUnicodeString.h"
Elliott Hughes8044bf62010-05-17 22:34:46 -070023#include "ScopedLocalRef.h"
Elliott Hughes99c59bf2010-05-17 16:22:04 -070024#include "ScopedPrimitiveArray.h"
Elliott Hughes05960872010-05-26 17:45:07 -070025#include "ScopedUtfChars.h"
Elliott Hughes8044bf62010-05-17 22:34:46 -070026#include "UniquePtr.h"
The Android Open Source Projectfdb27042008-10-21 07:00:00 -070027#include "jni.h"
28#include "utils/Log.h"
The Android Open Source Projectfdb27042008-10-21 07:00:00 -070029
30#include <string.h>
31#include <utils/misc.h>
32#include <expat.h>
33#include <cutils/jstring.h>
34
35#define BUCKET_COUNT 128
36
Elliott Hughes4eafbcc2010-06-21 16:52:06 -070037static void throw_OutOfMemoryError(JNIEnv* env) {
38 jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory.");
39}
40
The Android Open Source Projectfdb27042008-10-21 07:00:00 -070041/**
42 * Wrapper around an interned string.
43 */
Elliott Hughesc9b92b42009-12-09 16:05:29 -080044struct InternedString {
Elliott Hughes4eafbcc2010-06-21 16:52:06 -070045 InternedString() : interned(NULL), bytes(NULL) {
46 }
47
48 ~InternedString() {
49 delete[] bytes;
50 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -070051
52 /** The interned string itself. */
53 jstring interned;
54
55 /** UTF-8 equivalent of the interned string. */
56 const char* bytes;
57
58 /** Hash code of the interned string. */
Elliott Hughes99c59bf2010-05-17 16:22:04 -070059 int hash;
Elliott Hughesc9b92b42009-12-09 16:05:29 -080060};
The Android Open Source Projectfdb27042008-10-21 07:00:00 -070061
62/**
63 * Keeps track of strings between start and end events.
64 */
Elliott Hughes4eafbcc2010-06-21 16:52:06 -070065class StringStack {
66public:
67 StringStack() : array(new jstring[DEFAULT_CAPACITY]), capacity(DEFAULT_CAPACITY), size(0) {
68 }
69
70 ~StringStack() {
71 delete[] array;
72 }
73
74 void push(JNIEnv* env, jstring s) {
75 if (size == capacity) {
76 int newCapacity = capacity * 2;
77 jstring* newArray = new jstring[newCapacity];
78 if (newArray == NULL) {
79 throw_OutOfMemoryError(env);
80 return;
81 }
82 memcpy(newArray, array, capacity * sizeof(jstring));
83
84 array = newArray;
85 capacity = newCapacity;
86 }
87
88 array[size++] = s;
89 }
90
91 jstring pop() {
92 return (size == 0) ? NULL : array[--size];
93 }
94
95private:
96 enum { DEFAULT_CAPACITY = 10 };
The Android Open Source Projectfdb27042008-10-21 07:00:00 -070097
98 jstring* array;
99 int capacity;
100 int size;
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800101};
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700102
103/**
104 * Data passed to parser handler method by the parser.
105 */
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800106struct ParsingContext {
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700107 ParsingContext(jobject object) : env(NULL), object(object), buffer(NULL), bufferSize(-1) {
108 for (int i = 0; i < BUCKET_COUNT; i++) {
109 internedStrings[i] = NULL;
110 }
111 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700112
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700113 // Warning: 'env' must be valid on entry.
114 ~ParsingContext() {
115 freeBuffer();
116
117 // Free interned string cache.
118 for (int i = 0; i < BUCKET_COUNT; i++) {
119 if (internedStrings[i]) {
120 InternedString** bucket = internedStrings[i];
121 InternedString* current;
122 while ((current = *(bucket++)) != NULL) {
123 // Free the interned string reference.
124 env->DeleteGlobalRef(current->interned);
125
126 // Free the bucket.
127 delete current;
128 }
129
130 // Free the buckets.
131 delete[] internedStrings[i];
132 }
133 }
134 }
135
136 jcharArray ensureCapacity(int length) {
137 if (bufferSize < length) {
138 // Free the existing char[].
139 freeBuffer();
140
141 // Allocate a new char[].
142 jcharArray javaBuffer = env->NewCharArray(length);
143 if (javaBuffer == NULL) return NULL;
144
145 // Create a global reference.
146 javaBuffer = (jcharArray) env->NewGlobalRef(javaBuffer);
147 if (javaBuffer == NULL) return NULL;
148
149 buffer = javaBuffer;
150 bufferSize = length;
151 }
152 return buffer;
153 }
154
155private:
156 void freeBuffer() {
157 if (buffer != NULL) {
158 env->DeleteGlobalRef(buffer);
159 buffer = NULL;
160 bufferSize = -1;
161 }
162 }
163
164public:
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700165 /**
166 * The JNI environment for the current thread. This should only be used
167 * to keep a reference to the env for use in Expat callbacks.
168 */
169 JNIEnv* env;
170
171 /** The Java parser object. */
172 jobject object;
173
174 /** Buffer for text events. */
175 jcharArray buffer;
176
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700177private:
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700178 /** The size of our buffer in jchars. */
179 int bufferSize;
180
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700181public:
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700182 /** Current attributes. */
183 const char** attributes;
184
185 /** Number of attributes. */
186 int attributeCount;
187
188 /** True if namespace support is enabled. */
189 bool processNamespaces;
190
191 /** Keep track of names. */
192 StringStack stringStack;
193
194 /** Cache of interned strings. */
195 InternedString** internedStrings[BUCKET_COUNT];
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800196};
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700197
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700198static ParsingContext* toParsingContext(void* data) {
199 return reinterpret_cast<ParsingContext*>(data);
200}
201
202static ParsingContext* toParsingContext(XML_Parser parser) {
203 return reinterpret_cast<ParsingContext*>(XML_GetUserData(parser));
204}
205
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700206static jmethodID commentMethod;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700207static jmethodID endCdataMethod;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700208static jmethodID endDtdMethod;
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800209static jmethodID endElementMethod;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700210static jmethodID endNamespaceMethod;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700211static jmethodID handleExternalEntityMethod;
212static jmethodID internMethod;
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800213static jmethodID notationDeclMethod;
214static jmethodID processingInstructionMethod;
215static jmethodID startCdataMethod;
216static jmethodID startDtdMethod;
217static jmethodID startElementMethod;
218static jmethodID startNamespaceMethod;
219static jmethodID textMethod;
220static jmethodID unparsedEntityDeclMethod;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700221static jstring emptyString;
222
223/**
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700224 * Calculates a hash code for a null-terminated string. This is *not* equivalent
225 * to Java's String.hashCode(). This hashes the bytes while String.hashCode()
226 * hashes UTF-16 chars.
227 *
228 * @param s null-terminated string to hash
229 * @returns hash code
230 */
231static int hashString(const char* s) {
232 int hash = 0;
233 if (s) {
234 while (*s) {
235 hash = hash * 31 + *s++;
236 }
237 }
238 return hash;
239}
240
241/**
242 * Creates a new interned string wrapper. Looks up the interned string
243 * representing the given UTF-8 bytes.
244 *
245 * @param bytes null-terminated string to intern
246 * @param hash of bytes
247 * @returns wrapper of interned Java string
248 */
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700249static InternedString* newInternedString(JNIEnv* env, const char* bytes, int hash) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700250 // Allocate a new wrapper.
Elliott Hughes8044bf62010-05-17 22:34:46 -0700251 UniquePtr<InternedString> wrapper(new InternedString);
252 if (wrapper.get() == NULL) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700253 throw_OutOfMemoryError(env);
254 return NULL;
255 }
256
257 // Create a copy of the UTF-8 bytes.
258 // TODO: sometimes we already know the length. Reuse it if so.
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700259 char* copy = new char[strlen(bytes) + 1];
260 if (copy == NULL) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700261 throw_OutOfMemoryError(env);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700262 return NULL;
263 }
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700264 strcpy(copy, bytes);
265 wrapper->bytes = copy;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700266
267 // Save the hash.
268 wrapper->hash = hash;
269
270 // To intern a string, we must first create a new string and then call
271 // intern() on it. We then keep a global reference to the interned string.
Elliott Hughes8044bf62010-05-17 22:34:46 -0700272 ScopedLocalRef<jstring> newString(env, env->NewStringUTF(bytes));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700273 if (env->ExceptionCheck()) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700274 return NULL;
275 }
276
277 // Call intern().
Elliott Hughes8044bf62010-05-17 22:34:46 -0700278 ScopedLocalRef<jstring> interned(env,
279 reinterpret_cast<jstring>(env->CallObjectMethod(newString.get(), internMethod)));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700280 if (env->ExceptionCheck()) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700281 return NULL;
282 }
283
284 // Create a global reference to the interned string.
Elliott Hughes8044bf62010-05-17 22:34:46 -0700285 wrapper->interned = (jstring) env->NewGlobalRef(interned.get());
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700286 if (env->ExceptionCheck()) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700287 return NULL;
288 }
289
Elliott Hughes8044bf62010-05-17 22:34:46 -0700290 return wrapper.release();
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700291}
292
293/**
294 * Allocates a new bucket with one entry.
295 *
296 * @param entry to store in the bucket
297 * @returns a reference to the bucket
298 */
299static InternedString** newInternedStringBucket(InternedString* entry) {
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700300 InternedString** bucket = new InternedString*[2];
301 if (bucket != NULL) {
302 bucket[0] = entry;
303 bucket[1] = NULL;
304 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700305 return bucket;
306}
307
308/**
309 * Expands an interned string bucket and adds the given entry. Frees the
310 * provided bucket and returns a new one.
311 *
312 * @param existingBucket the bucket to replace
313 * @param entry to add to the bucket
314 * @returns a reference to the newly-allocated bucket containing the given entry
315 */
316static InternedString** expandInternedStringBucket(
317 InternedString** existingBucket, InternedString* entry) {
318 // Determine the size of the existing bucket.
319 int size = 0;
320 while (existingBucket[size]) size++;
321
322 // Allocate the new bucket with enough space for one more entry and
323 // a null terminator.
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700324 InternedString** newBucket = new InternedString*[size + 2];
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700325 if (newBucket == NULL) return NULL;
326
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700327 memcpy(newBucket, existingBucket, size * sizeof(InternedString*));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700328 newBucket[size] = entry;
329 newBucket[size + 1] = NULL;
330
331 return newBucket;
332}
333
334/**
335 * Returns an interned string for the given UTF-8 string.
336 *
337 * @param bucket to search for s
338 * @param s null-terminated string to find
339 * @param hash of s
340 * @returns interned Java string equivalent of s or null if not found
341 */
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700342static jstring findInternedString(InternedString** bucket, const char* s, int hash) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700343 InternedString* current;
344 while ((current = *(bucket++)) != NULL) {
345 if (current->hash != hash) continue;
346 if (!strcmp(s, current->bytes)) return current->interned;
347 }
348 return NULL;
349}
350
351/**
352 * Returns an interned string for the given UTF-8 string.
353 *
354 * @param s null-terminated string to intern
355 * @returns interned Java string equivelent of s or NULL if s is null
356 */
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700357static jstring internString(JNIEnv* env, ParsingContext* parsingContext, const char* s) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700358 if (s == NULL) return NULL;
359
360 int hash = hashString(s);
361 int bucketIndex = hash & (BUCKET_COUNT - 1);
362
363 InternedString*** buckets = parsingContext->internedStrings;
364 InternedString** bucket = buckets[bucketIndex];
365 InternedString* internedString;
366
367 if (bucket) {
368 // We have a bucket already. Look for the given string.
369 jstring found = findInternedString(bucket, s, hash);
370 if (found) {
371 // We found it!
372 return found;
373 }
374
375 // We didn't find it. :(
376 // Create a new entry.
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700377 internedString = newInternedString(env, s, hash);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700378 if (internedString == NULL) return NULL;
379
380 // Expand the bucket.
381 bucket = expandInternedStringBucket(bucket, internedString);
382 if (bucket == NULL) {
383 throw_OutOfMemoryError(env);
384 return NULL;
385 }
386
387 buckets[bucketIndex] = bucket;
388
389 return internedString->interned;
390 } else {
391 // We don't even have a bucket yet. Create an entry.
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700392 internedString = newInternedString(env, s, hash);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700393 if (internedString == NULL) return NULL;
394
395 // Create a new bucket with one entry.
396 bucket = newInternedStringBucket(internedString);
397 if (bucket == NULL) {
398 throw_OutOfMemoryError(env);
399 return NULL;
400 }
401
402 buckets[bucketIndex] = bucket;
403
404 return internedString->interned;
405 }
406}
407
Elliott Hughes738f9502009-10-01 11:20:29 -0700408static void jniThrowExpatException(JNIEnv* env, XML_Error error) {
409 const char* message = XML_ErrorString(error);
410 jniThrowException(env, "org/apache/harmony/xml/ExpatException", message);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700411}
412
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700413/**
414 * Copies UTF-8 characters into the buffer. Returns the number of Java chars
415 * which were buffered.
416 *
417 * @param characters to copy into the buffer
418 * @param length of characters to copy (in bytes)
419 * @returns number of UTF-16 characters which were copied
420 */
Elliott Hughesebca53a2010-05-20 20:54:45 -0700421static size_t fillBuffer(ParsingContext* parsingContext, const char* characters, int length) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700422 JNIEnv* env = parsingContext->env;
423
424 // Grow buffer if necessary.
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700425 jcharArray buffer = parsingContext->ensureCapacity(length);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700426 if (buffer == NULL) return -1;
427
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700428 // Decode UTF-8 characters into our buffer.
Elliott Hughesebca53a2010-05-20 20:54:45 -0700429 ScopedCharArrayRW nativeBuffer(env, buffer);
Elliott Hughes64101122010-07-12 09:53:54 -0700430 if (nativeBuffer.get() == NULL) {
431 return -1;
432 }
433
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700434 size_t utf16length;
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700435 strcpylen8to16(nativeBuffer.get(), characters, length, &utf16length);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700436 return utf16length;
437}
438
439/**
440 * Buffers the given text and passes it to the given method.
441 *
442 * @param method to pass the characters and length to with signature
443 * (char[], int)
444 * @param data parsing context
445 * @param text to copy into the buffer
446 * @param length of text to copy (in bytes)
447 */
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700448static void bufferAndInvoke(jmethodID method, void* data, const char* text, size_t length) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700449 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700450 JNIEnv* env = parsingContext->env;
451
452 // Bail out if a previously called handler threw an exception.
453 if (env->ExceptionCheck()) return;
454
455 // Buffer the element name.
456 size_t utf16length = fillBuffer(parsingContext, text, length);
457
458 // Invoke given method.
459 jobject javaParser = parsingContext->object;
460 jcharArray buffer = parsingContext->buffer;
461 env->CallVoidMethod(javaParser, method, buffer, utf16length);
462}
463
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700464static const char** toAttributes(jint attributePointer) {
465 return reinterpret_cast<const char**>(static_cast<uintptr_t>(attributePointer));
466}
467
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700468/**
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800469 * The component parts of an attribute or element name.
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700470 */
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800471class ExpatElementName {
472public:
473 ExpatElementName(JNIEnv* env, ParsingContext* parsingContext, jint attributePointer, jint index) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700474 const char** attributes = toAttributes(attributePointer);
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800475 const char* name = attributes[index * 2];
476 init(env, parsingContext, name);
477 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700478
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800479 ExpatElementName(JNIEnv* env, ParsingContext* parsingContext, const char* s) {
480 init(env, parsingContext, s);
481 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700482
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800483 ~ExpatElementName() {
484 free(mCopy);
485 }
486
487 /**
488 * Returns the namespace URI, like "http://www.w3.org/1999/xhtml".
489 * Possibly empty.
490 */
491 jstring uri() {
492 return internString(mEnv, mParsingContext, mUri);
493 }
494
495 /**
Jesse Wilsona1440512010-01-29 17:06:44 -0800496 * Returns the element or attribute local name, like "h1". Never empty. When
497 * namespace processing is disabled, this may contain a prefix, yielding a
498 * local name like "html:h1". In such cases, the qName will always be empty.
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800499 */
500 jstring localName() {
501 return internString(mEnv, mParsingContext, mLocalName);
502 }
503
504 /**
505 * Returns the namespace prefix, like "html". Possibly empty.
506 */
507 jstring qName() {
508 if (*mPrefix == 0) {
509 return localName();
510 }
511
512 // return prefix + ":" + localName
513 LocalArray<1024> qName(strlen(mPrefix) + 1 + strlen(mLocalName) + 1);
514 snprintf(&qName[0], qName.size(), "%s:%s", mPrefix, mLocalName);
515 return internString(mEnv, mParsingContext, &qName[0]);
516 }
517
518 /**
519 * Returns true if this expat name has the same URI and local name.
520 */
521 bool matches(const char* uri, const char* localName) {
522 return strcmp(uri, mUri) == 0 && strcmp(localName, mLocalName) == 0;
523 }
524
525 /**
526 * Returns true if this expat name has the same qualified name.
527 */
528 bool matchesQName(const char* qName) {
Brian Carlstrombf87c562010-05-27 23:09:59 -0700529 const char* lastColon = strrchr(qName, ':');
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800530
Jesse Wilsona1440512010-01-29 17:06:44 -0800531 // Compare local names only if either:
532 // - the input qualified name doesn't have a colon (like "h1")
533 // - this element doesn't have a prefix. Such is the case when it
534 // doesn't belong to a namespace, or when this parser's namespace
535 // processing is disabled. In the latter case, this element's local
536 // name may still contain a colon (like "html:h1").
537 if (lastColon == NULL || *mPrefix == 0) {
538 return strcmp(qName, mLocalName) == 0;
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800539 }
540
Jesse Wilsona1440512010-01-29 17:06:44 -0800541 // Otherwise compare both prefix and local name
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800542 size_t prefixLength = lastColon - qName;
543 return strlen(mPrefix) == prefixLength
544 && strncmp(qName, mPrefix, prefixLength) == 0
545 && strcmp(lastColon + 1, mLocalName) == 0;
546 }
547
548private:
549 JNIEnv* mEnv;
550 ParsingContext* mParsingContext;
551 char* mCopy;
552 const char* mUri;
553 const char* mLocalName;
554 const char* mPrefix;
555
556 /**
557 * Decodes an Expat-encoded name of one of these three forms:
558 * "uri|localName|prefix" (example: "http://www.w3.org/1999/xhtml|h1|html")
559 * "uri|localName" (example: "http://www.w3.org/1999/xhtml|h1")
560 * "localName" (example: "h1")
561 */
562 void init(JNIEnv* env, ParsingContext* parsingContext, const char* s) {
563 mEnv = env;
564 mParsingContext = parsingContext;
565 mCopy = strdup(s);
566
567 // split the input into up to 3 parts: a|b|c
568 char* context = NULL;
569 char* a = strtok_r(mCopy, "|", &context);
570 char* b = strtok_r(NULL, "|", &context);
571 char* c = strtok_r(NULL, "|", &context);
572
573 if (c != NULL) { // input of the form "uri|localName|prefix"
574 mUri = a;
575 mLocalName = b;
576 mPrefix = c;
577 } else if (b != NULL) { // input of the form "uri|localName"
578 mUri = a;
579 mLocalName = b;
580 mPrefix = "";
581 } else { // input of the form "localName"
582 mLocalName = a;
583 mUri = "";
584 mPrefix = "";
585 }
586 }
Elliott Hughes7ca6fd02010-03-29 20:51:48 -0700587
588 // Disallow copy and assignment.
589 ExpatElementName(const ExpatElementName&);
590 void operator=(const ExpatElementName&);
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800591};
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700592
593/**
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700594 * Called by Expat at the start of an element. Delegates to the same method
595 * on the Java parser.
596 *
597 * @param data parsing context
598 * @param elementName "uri|localName" or "localName" for the current element
599 * @param attributes alternating attribute names and values. Like element
600 * names, attribute names follow the format "uri|localName" or "localName".
601 */
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700602static void startElement(void* data, const char* elementName, const char** attributes) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700603 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700604 JNIEnv* env = parsingContext->env;
605
606 // Bail out if a previously called handler threw an exception.
607 if (env->ExceptionCheck()) return;
608
609 // Count the number of attributes.
610 int count = 0;
611 while (attributes[count << 1]) count++;
612
613 // Make the attributes available for the duration of this call.
614 parsingContext->attributes = attributes;
615 parsingContext->attributeCount = count;
616
617 jobject javaParser = parsingContext->object;
618
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800619 ExpatElementName e(env, parsingContext, elementName);
Jesse Wilson4a5c3fd2010-03-19 12:51:43 -0700620 jstring uri = parsingContext->processNamespaces ? e.uri() : emptyString;
621 jstring localName = parsingContext->processNamespaces ? e.localName() : emptyString;
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800622 jstring qName = e.qName();
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700623
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700624 parsingContext->stringStack.push(env, qName);
625 parsingContext->stringStack.push(env, uri);
626 parsingContext->stringStack.push(env, localName);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700627
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700628 env->CallVoidMethod(javaParser, startElementMethod, uri, localName, qName, attributes, count);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700629
630 parsingContext->attributes = NULL;
631 parsingContext->attributeCount = -1;
632}
633
634/**
635 * Called by Expat at the end of an element. Delegates to the same method
636 * on the Java parser.
637 *
638 * @param data parsing context
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700639 * @param elementName "uri|localName" or "localName" for the current element;
640 * we assume that this matches the last data on our stack.
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700641 */
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700642static void endElement(void* data, const char* /*elementName*/) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700643 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700644 JNIEnv* env = parsingContext->env;
645
646 // Bail out if a previously called handler threw an exception.
647 if (env->ExceptionCheck()) return;
648
649 jobject javaParser = parsingContext->object;
650
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700651 jstring localName = parsingContext->stringStack.pop();
652 jstring uri = parsingContext->stringStack.pop();
653 jstring qName = parsingContext->stringStack.pop();
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700654
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800655 env->CallVoidMethod(javaParser, endElementMethod, uri, localName, qName);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700656}
657
658/**
659 * Called by Expat when it encounters text. Delegates to the same method
660 * on the Java parser. This may be called mutiple times with incremental pieces
661 * of the same contiguous block of text.
662 *
663 * @param data parsing context
664 * @param characters buffer containing encountered text
665 * @param length number of characters in the buffer
666 */
667static void text(void* data, const char* characters, int length) {
668 bufferAndInvoke(textMethod, data, characters, length);
669}
670
671/**
672 * Called by Expat when it encounters a comment. Delegates to the same method
673 * on the Java parser.
674
675 * @param data parsing context
676 * @param comment 0-terminated
677 */
678static void comment(void* data, const char* comment) {
679 bufferAndInvoke(commentMethod, data, comment, strlen(comment));
680}
681
682/**
683 * Called by Expat at the beginning of a namespace mapping.
684 *
685 * @param data parsing context
686 * @param prefix null-terminated namespace prefix used in the XML
687 * @param uri of the namespace
688 */
689static void startNamespace(void* data, const char* prefix, const char* uri) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700690 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700691 JNIEnv* env = parsingContext->env;
692
693 // Bail out if a previously called handler threw an exception.
694 if (env->ExceptionCheck()) return;
695
696 jstring internedPrefix = emptyString;
697 if (prefix != NULL) {
698 internedPrefix = internString(env, parsingContext, prefix);
699 if (env->ExceptionCheck()) return;
700 }
701
702 jstring internedUri = emptyString;
703 if (uri != NULL) {
704 internedUri = internString(env, parsingContext, uri);
705 if (env->ExceptionCheck()) return;
706 }
707
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700708 parsingContext->stringStack.push(env, internedPrefix);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700709
710 jobject javaParser = parsingContext->object;
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700711 env->CallVoidMethod(javaParser, startNamespaceMethod, internedPrefix, internedUri);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700712}
713
714/**
715 * Called by Expat at the end of a namespace mapping.
716 *
717 * @param data parsing context
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700718 * @param prefix null-terminated namespace prefix used in the XML;
719 * we assume this is the same as the last prefix on the stack.
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700720 */
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700721static void endNamespace(void* data, const char* /*prefix*/) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700722 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700723 JNIEnv* env = parsingContext->env;
724
725 // Bail out if a previously called handler threw an exception.
726 if (env->ExceptionCheck()) return;
727
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700728 jstring internedPrefix = parsingContext->stringStack.pop();
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700729
730 jobject javaParser = parsingContext->object;
731 env->CallVoidMethod(javaParser, endNamespaceMethod, internedPrefix);
732}
733
734/**
735 * Called by Expat at the beginning of a CDATA section.
736 *
737 * @param data parsing context
738 */
739static void startCdata(void* data) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700740 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700741 JNIEnv* env = parsingContext->env;
742
743 // Bail out if a previously called handler threw an exception.
744 if (env->ExceptionCheck()) return;
745
746 jobject javaParser = parsingContext->object;
747 env->CallVoidMethod(javaParser, startCdataMethod);
748}
749
750/**
751 * Called by Expat at the end of a CDATA section.
752 *
753 * @param data parsing context
754 */
755static void endCdata(void* data) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700756 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700757 JNIEnv* env = parsingContext->env;
758
759 // Bail out if a previously called handler threw an exception.
760 if (env->ExceptionCheck()) return;
761
762 jobject javaParser = parsingContext->object;
763 env->CallVoidMethod(javaParser, endCdataMethod);
764}
765
766/**
767 * Called by Expat at the beginning of a DOCTYPE section.
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700768 * 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 -0700769 */
770static void startDtd(void* data, const char* name,
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700771 const char* systemId, const char* publicId, int /*hasInternalSubset*/) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700772 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700773 JNIEnv* env = parsingContext->env;
774
775 // Bail out if a previously called handler threw an exception.
776 if (env->ExceptionCheck()) return;
777
778 jstring javaName = internString(env, parsingContext, name);
779 if (env->ExceptionCheck()) return;
780
781 jstring javaPublicId = internString(env, parsingContext, publicId);
782 if (env->ExceptionCheck()) return;
783
784 jstring javaSystemId = internString(env, parsingContext, systemId);
785 if (env->ExceptionCheck()) return;
786
787 jobject javaParser = parsingContext->object;
788 env->CallVoidMethod(javaParser, startDtdMethod, javaName, javaPublicId,
789 javaSystemId);
790}
791
792/**
793 * Called by Expat at the end of a DOCTYPE section.
794 *
795 * @param data parsing context
796 */
797static void endDtd(void* data) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700798 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700799 JNIEnv* env = parsingContext->env;
800
801 // Bail out if a previously called handler threw an exception.
802 if (env->ExceptionCheck()) return;
803
804 jobject javaParser = parsingContext->object;
805 env->CallVoidMethod(javaParser, endDtdMethod);
806}
807
808/**
809 * Called by Expat when it encounters processing instructions.
810 *
811 * @param data parsing context
812 * @param target of the instruction
813 * @param instructionData
814 */
Elliott Hughes8044bf62010-05-17 22:34:46 -0700815static void processingInstruction(void* data, const char* target, const char* instructionData) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700816 ParsingContext* parsingContext = toParsingContext(data);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700817 JNIEnv* env = parsingContext->env;
818
819 // Bail out if a previously called handler threw an exception.
820 if (env->ExceptionCheck()) return;
821
822 jstring javaTarget = internString(env, parsingContext, target);
823 if (env->ExceptionCheck()) return;
824
Elliott Hughes8044bf62010-05-17 22:34:46 -0700825 ScopedLocalRef<jstring> javaInstructionData(env, env->NewStringUTF(instructionData));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700826 if (env->ExceptionCheck()) return;
827
828 jobject javaParser = parsingContext->object;
Elliott Hughes8044bf62010-05-17 22:34:46 -0700829 env->CallVoidMethod(javaParser, processingInstructionMethod, javaTarget, javaInstructionData.get());
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700830}
831
832/**
833 * Creates a new entity parser.
834 *
835 * @param object the Java ExpatParser instance
836 * @param parentParser pointer
837 * @param javaEncoding the character encoding name
838 * @param javaContext that was provided to handleExternalEntity
839 * @returns the pointer to the C Expat entity parser
840 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700841static jint ExpatParser_createEntityParser(JNIEnv* env, jobject, jint parentParser, jstring javaContext) {
Elliott Hughes05960872010-05-26 17:45:07 -0700842 ScopedUtfChars context(env, javaContext);
843 if (context.c_str() == NULL) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700844 return 0;
845 }
846
847 XML_Parser parent = (XML_Parser) parentParser;
Elliott Hughes05960872010-05-26 17:45:07 -0700848 XML_Parser entityParser = XML_ExternalEntityParserCreate(parent, context.c_str(), NULL);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700849 if (entityParser == NULL) {
850 throw_OutOfMemoryError(env);
851 }
852
853 return (jint) entityParser;
854}
855
856/**
857 * Handles external entities. We ignore the "base" URI and keep track of it
858 * ourselves.
859 */
860static int handleExternalEntity(XML_Parser parser, const char* context,
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700861 const char*, const char* systemId, const char* publicId) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700862 ParsingContext* parsingContext = toParsingContext(parser);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700863 jobject javaParser = parsingContext->object;
864 JNIEnv* env = parsingContext->env;
865 jobject object = parsingContext->object;
866
867 // Bail out if a previously called handler threw an exception.
868 if (env->ExceptionCheck()) {
869 return XML_STATUS_ERROR;
870 }
871
Elliott Hughes8044bf62010-05-17 22:34:46 -0700872 ScopedLocalRef<jstring> javaSystemId(env, env->NewStringUTF(systemId));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700873 if (env->ExceptionCheck()) {
874 return XML_STATUS_ERROR;
875 }
Elliott Hughes8044bf62010-05-17 22:34:46 -0700876 ScopedLocalRef<jstring> javaPublicId(env, env->NewStringUTF(publicId));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700877 if (env->ExceptionCheck()) {
878 return XML_STATUS_ERROR;
879 }
Elliott Hughes8044bf62010-05-17 22:34:46 -0700880 ScopedLocalRef<jstring> javaContext(env, env->NewStringUTF(context));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700881 if (env->ExceptionCheck()) {
882 return XML_STATUS_ERROR;
883 }
884
885 // Pass the wrapped parser and both strings to java.
Elliott Hughes8044bf62010-05-17 22:34:46 -0700886 env->CallVoidMethod(javaParser, handleExternalEntityMethod, javaContext.get(),
887 javaPublicId.get(), javaSystemId.get());
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700888
889 /*
890 * Parsing the external entity leaves parsingContext->env and object set to
891 * NULL, so we need to restore both.
892 *
893 * TODO: consider restoring the original env and object instead of setting
894 * them to NULL in the append() functions.
895 */
896 parsingContext->env = env;
897 parsingContext->object = object;
898
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700899 return env->ExceptionCheck() ? XML_STATUS_ERROR : XML_STATUS_OK;
900}
901
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700902/**
903 * Expat gives us 'base', but the Java API doesn't expect it, so we don't need it.
904 */
905static 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 -0700906 ParsingContext* parsingContext = toParsingContext(data);
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800907 jobject javaParser = parsingContext->object;
908 JNIEnv* env = parsingContext->env;
Elliott Hughes99c59bf2010-05-17 16:22:04 -0700909
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800910 // Bail out if a previously called handler threw an exception.
911 if (env->ExceptionCheck()) return;
Elliott Hughes99c59bf2010-05-17 16:22:04 -0700912
Elliott Hughes8044bf62010-05-17 22:34:46 -0700913 ScopedLocalRef<jstring> javaName(env, env->NewStringUTF(name));
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800914 if (env->ExceptionCheck()) return;
Elliott Hughes8044bf62010-05-17 22:34:46 -0700915 ScopedLocalRef<jstring> javaPublicId(env, env->NewStringUTF(publicId));
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800916 if (env->ExceptionCheck()) return;
Elliott Hughes8044bf62010-05-17 22:34:46 -0700917 ScopedLocalRef<jstring> javaSystemId(env, env->NewStringUTF(systemId));
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800918 if (env->ExceptionCheck()) return;
Elliott Hughes8044bf62010-05-17 22:34:46 -0700919 ScopedLocalRef<jstring> javaNotationName(env, env->NewStringUTF(notationName));
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800920 if (env->ExceptionCheck()) return;
Elliott Hughes99c59bf2010-05-17 16:22:04 -0700921
Elliott Hughes8044bf62010-05-17 22:34:46 -0700922 env->CallVoidMethod(javaParser, unparsedEntityDeclMethod, javaName.get(), javaPublicId.get(), javaSystemId.get(), javaNotationName.get());
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800923}
924
Elliott Hughes4e3714f2010-06-22 14:32:47 -0700925/**
926 * Expat gives us 'base', but the Java API doesn't expect it, so we don't need it.
927 */
928static void notationDecl(void* data, const char* name, const char* /*base*/, const char* systemId, const char* publicId) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700929 ParsingContext* parsingContext = toParsingContext(data);
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800930 jobject javaParser = parsingContext->object;
931 JNIEnv* env = parsingContext->env;
Elliott Hughes99c59bf2010-05-17 16:22:04 -0700932
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800933 // Bail out if a previously called handler threw an exception.
934 if (env->ExceptionCheck()) return;
Elliott Hughes99c59bf2010-05-17 16:22:04 -0700935
Elliott Hughes8044bf62010-05-17 22:34:46 -0700936 ScopedLocalRef<jstring> javaName(env, env->NewStringUTF(name));
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800937 if (env->ExceptionCheck()) return;
Elliott Hughes8044bf62010-05-17 22:34:46 -0700938 ScopedLocalRef<jstring> javaPublicId(env, env->NewStringUTF(publicId));
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800939 if (env->ExceptionCheck()) return;
Elliott Hughes8044bf62010-05-17 22:34:46 -0700940 ScopedLocalRef<jstring> javaSystemId(env, env->NewStringUTF(systemId));
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800941 if (env->ExceptionCheck()) return;
Elliott Hughes99c59bf2010-05-17 16:22:04 -0700942
Elliott Hughes8044bf62010-05-17 22:34:46 -0700943 env->CallVoidMethod(javaParser, notationDeclMethod, javaName.get(), javaPublicId.get(), javaSystemId.get());
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800944}
945
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700946/**
Jesse Wilsona1440512010-01-29 17:06:44 -0800947 * Creates a new Expat parser. Called from the Java ExpatParser constructor.
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700948 *
949 * @param object the Java ExpatParser instance
950 * @param javaEncoding the character encoding name
951 * @param processNamespaces true if the parser should handle namespaces
952 * @returns the pointer to the C Expat parser
953 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -0700954static jint ExpatParser_initialize(JNIEnv* env, jobject object, jstring javaEncoding,
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700955 jboolean processNamespaces) {
956 // Allocate parsing context.
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700957 UniquePtr<ParsingContext> context(new ParsingContext(object));
958 if (context.get() == NULL) {
959 throw_OutOfMemoryError(env);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700960 return 0;
961 }
962
963 context->processNamespaces = (bool) processNamespaces;
964
965 // Create a parser.
966 XML_Parser parser;
Elliott Hughes05960872010-05-26 17:45:07 -0700967 ScopedUtfChars encoding(env, javaEncoding);
968 if (encoding.c_str() == NULL) {
969 return 0;
970 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700971 if (processNamespaces) {
972 // Use '|' to separate URIs from local names.
Elliott Hughes05960872010-05-26 17:45:07 -0700973 parser = XML_ParserCreateNS(encoding.c_str(), '|');
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700974 } else {
Elliott Hughes05960872010-05-26 17:45:07 -0700975 parser = XML_ParserCreate(encoding.c_str());
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700976 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700977
978 if (parser != NULL) {
979 if (processNamespaces) {
980 XML_SetNamespaceDeclHandler(parser, startNamespace, endNamespace);
Jesse Wilsondf36e8c2010-01-23 00:49:53 -0800981 XML_SetReturnNSTriplet(parser, 1);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700982 }
983
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700984 XML_SetCdataSectionHandler(parser, startCdata, endCdata);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700985 XML_SetCharacterDataHandler(parser, text);
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800986 XML_SetCommentHandler(parser, comment);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700987 XML_SetDoctypeDeclHandler(parser, startDtd, endDtd);
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800988 XML_SetElementHandler(parser, startElement, endElement);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700989 XML_SetExternalEntityRefHandler(parser, handleExternalEntity);
Elliott Hughesc9b92b42009-12-09 16:05:29 -0800990 XML_SetNotationDeclHandler(parser, notationDecl);
991 XML_SetProcessingInstructionHandler(parser, processingInstruction);
992 XML_SetUnparsedEntityDeclHandler(parser, unparsedEntityDecl);
Elliott Hughes4eafbcc2010-06-21 16:52:06 -0700993 XML_SetUserData(parser, context.release());
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700994 } else {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -0700995 throw_OutOfMemoryError(env);
996 return 0;
997 }
998
999 return (jint) parser;
1000}
1001
1002/**
Elliott Hughes99c59bf2010-05-17 16:22:04 -07001003 * Expat decides for itself what character encoding it's looking at. The interface is in terms of
1004 * bytes, which may point to UTF-8, UTF-16, ISO-8859-1, or US-ASCII. appendBytes, appendCharacters,
1005 * and appendString thus all call through to this method, strange though that appears.
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001006 */
Elliott Hughes99c59bf2010-05-17 16:22:04 -07001007static void append(JNIEnv* env, jobject object, jint pointer,
1008 const char* bytes, size_t byteOffset, size_t byteCount, jboolean isFinal) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001009 XML_Parser parser = (XML_Parser) pointer;
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001010 ParsingContext* context = toParsingContext(parser);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001011 context->env = env;
1012 context->object = object;
Elliott Hughes05960872010-05-26 17:45:07 -07001013 if (!XML_Parse(parser, bytes + byteOffset, byteCount, isFinal) && !env->ExceptionCheck()) {
Elliott Hughes738f9502009-10-01 11:20:29 -07001014 jniThrowExpatException(env, XML_GetErrorCode(parser));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001015 }
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001016 context->object = NULL;
1017 context->env = NULL;
1018}
1019
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001020static void ExpatParser_appendBytes(JNIEnv* env, jobject object, jint pointer,
Elliott Hughes99c59bf2010-05-17 16:22:04 -07001021 jbyteArray xml, jint byteOffset, jint byteCount) {
Elliott Hughesebca53a2010-05-20 20:54:45 -07001022 ScopedByteArrayRO byteArray(env, xml);
Elliott Hughes64101122010-07-12 09:53:54 -07001023 if (byteArray.get() == NULL) {
1024 return;
1025 }
1026
Elliott Hughes99c59bf2010-05-17 16:22:04 -07001027 const char* bytes = reinterpret_cast<const char*>(byteArray.get());
1028 append(env, object, pointer, bytes, byteOffset, byteCount, XML_FALSE);
1029}
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001030
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001031static void ExpatParser_appendCharacters(JNIEnv* env, jobject object, jint pointer,
Elliott Hughes99c59bf2010-05-17 16:22:04 -07001032 jcharArray xml, jint charOffset, jint charCount) {
Elliott Hughesebca53a2010-05-20 20:54:45 -07001033 ScopedCharArrayRO charArray(env, xml);
Elliott Hughes64101122010-07-12 09:53:54 -07001034 if (charArray.get() == NULL) {
1035 return;
1036 }
1037
Elliott Hughes99c59bf2010-05-17 16:22:04 -07001038 const char* bytes = reinterpret_cast<const char*>(charArray.get());
1039 size_t byteOffset = 2 * charOffset;
1040 size_t byteCount = 2 * charCount;
1041 append(env, object, pointer, bytes, byteOffset, byteCount, XML_FALSE);
1042}
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001043
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001044static void ExpatParser_appendString(JNIEnv* env, jobject object, jint pointer,
Elliott Hughes4e3714f2010-06-22 14:32:47 -07001045 jstring javaXml, jboolean isFinal) {
1046 ScopedJavaUnicodeString xml(env, javaXml);
1047 const char* bytes = reinterpret_cast<const char*>(xml.unicodeString().getBuffer());
1048 size_t byteCount = 2 * xml.unicodeString().length();
Elliott Hughes99c59bf2010-05-17 16:22:04 -07001049 append(env, object, pointer, bytes, 0, byteCount, isFinal);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001050}
1051
1052/**
1053 * Releases parser only.
1054 *
1055 * @param object the Java ExpatParser instance
1056 * @param i pointer to the C expat parser
1057 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001058static void ExpatParser_releaseParser(JNIEnv*, jobject, jint i) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001059 XML_Parser parser = (XML_Parser) i;
1060 XML_ParserFree(parser);
1061}
1062
1063/**
1064 * Cleans up after the parser. Called at garbage collection time.
1065 *
1066 * @param object the Java ExpatParser instance
1067 * @param i pointer to the C expat parser
1068 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001069static void ExpatParser_release(JNIEnv* env, jobject, jint i) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001070 XML_Parser parser = (XML_Parser) i;
1071
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001072 ParsingContext* context = toParsingContext(parser);
Elliott Hughes4eafbcc2010-06-21 16:52:06 -07001073 context->env = env;
1074 delete context;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001075
1076 XML_ParserFree(parser);
1077}
1078
1079/**
1080 * Gets the current line.
1081 *
1082 * @param object the Java ExpatParser instance
1083 * @param pointer to the C expat parser
1084 * @returns current line number
1085 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001086static int ExpatParser_line(JNIEnv*, jobject, jint pointer) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001087 XML_Parser parser = (XML_Parser) pointer;
1088 return XML_GetCurrentLineNumber(parser);
1089}
1090
1091/**
1092 * Gets the current column.
1093 *
1094 * @param object the Java ExpatParser instance
1095 * @param pointer to the C expat parser
1096 * @returns current column number
1097 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001098static int ExpatParser_column(JNIEnv*, jobject, jint pointer) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001099 XML_Parser parser = (XML_Parser) pointer;
1100 return XML_GetCurrentColumnNumber(parser);
1101}
1102
1103/**
1104 * Gets the URI of the attribute at the given index.
1105 *
1106 * @param object Java ExpatParser instance
1107 * @param pointer to the C expat parser
1108 * @param attributePointer to the attribute array
1109 * @param index of the attribute
1110 * @returns interned Java string containing attribute's URI
1111 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001112static jstring ExpatAttributes_getURI(JNIEnv* env, jobject, jint pointer,
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001113 jint attributePointer, jint index) {
1114 XML_Parser parser = (XML_Parser) pointer;
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001115 ParsingContext* context = toParsingContext(parser);
Jesse Wilsondf36e8c2010-01-23 00:49:53 -08001116 return ExpatElementName(env, context, attributePointer, index).uri();
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001117}
1118
1119/**
1120 * Gets the local name of the attribute at the given index.
1121 *
1122 * @param object Java ExpatParser instance
1123 * @param pointer to the C expat parser
1124 * @param attributePointer to the attribute array
1125 * @param index of the attribute
1126 * @returns interned Java string containing attribute's local name
1127 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001128static jstring ExpatAttributes_getLocalName(JNIEnv* env, jobject, jint pointer,
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001129 jint attributePointer, jint index) {
1130 XML_Parser parser = (XML_Parser) pointer;
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001131 ParsingContext* context = toParsingContext(parser);
Jesse Wilsondf36e8c2010-01-23 00:49:53 -08001132 return ExpatElementName(env, context, attributePointer, index).localName();
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001133}
1134
1135/**
1136 * Gets the qualified name of the attribute at the given index.
1137 *
1138 * @param object Java ExpatParser instance
1139 * @param pointer to the C expat parser
1140 * @param attributePointer to the attribute array
1141 * @param index of the attribute
1142 * @returns interned Java string containing attribute's local name
1143 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001144static jstring ExpatAttributes_getQName(JNIEnv* env, jobject, jint pointer,
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001145 jint attributePointer, jint index) {
1146 XML_Parser parser = (XML_Parser) pointer;
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001147 ParsingContext* context = toParsingContext(parser);
Jesse Wilsondf36e8c2010-01-23 00:49:53 -08001148 return ExpatElementName(env, context, attributePointer, index).qName();
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001149}
1150
1151/**
1152 * Gets the value of the attribute at the given index.
1153 *
1154 * @param object Java ExpatParser instance
1155 * @param attributePointer to the attribute array
1156 * @param index of the attribute
1157 * @returns Java string containing attribute's value
1158 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001159static jstring ExpatAttributes_getValueByIndex(JNIEnv* env, jobject,
1160 jint attributePointer, jint index) {
1161 const char** attributes = toAttributes(attributePointer);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001162 const char* value = attributes[(index << 1) + 1];
1163 return env->NewStringUTF(value);
1164}
1165
1166/**
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001167 * Gets the index of the attribute with the given qualified name.
1168 *
1169 * @param attributePointer to the attribute array
1170 * @param qName to look for
1171 * @returns index of attribute with the given uri and local name or -1 if not
1172 * found
1173 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001174static jint ExpatAttributes_getIndexForQName(JNIEnv* env, jobject,
1175 jint attributePointer, jstring qName) {
Elliott Hughes05960872010-05-26 17:45:07 -07001176 ScopedUtfChars qNameBytes(env, qName);
1177 if (qNameBytes.c_str() == NULL) {
Jesse Wilsondf36e8c2010-01-23 00:49:53 -08001178 return -1;
1179 }
1180
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001181 const char** attributes = toAttributes(attributePointer);
Jesse Wilsondf36e8c2010-01-23 00:49:53 -08001182 int found = -1;
1183 for (int index = 0; attributes[index * 2]; ++index) {
Elliott Hughes05960872010-05-26 17:45:07 -07001184 if (ExpatElementName(NULL, NULL, attributePointer, index).matchesQName(qNameBytes.c_str())) {
Jesse Wilsondf36e8c2010-01-23 00:49:53 -08001185 found = index;
1186 break;
1187 }
1188 }
1189
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001190 return found;
1191}
1192
1193/**
1194 * Gets the index of the attribute with the given URI and name.
1195 *
1196 * @param attributePointer to the attribute array
1197 * @param uri to look for
1198 * @param localName to look for
1199 * @returns index of attribute with the given uri and local name or -1 if not
1200 * found
1201 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001202static jint ExpatAttributes_getIndex(JNIEnv* env, jobject, jint attributePointer,
Elliott Hughes05960872010-05-26 17:45:07 -07001203 jstring uri, jstring localName) {
1204 ScopedUtfChars uriBytes(env, uri);
1205 if (uriBytes.c_str() == NULL) {
1206 return -1;
1207 }
1208
1209 ScopedUtfChars localNameBytes(env, localName);
1210 if (localNameBytes.c_str() == NULL) {
1211 return -1;
1212 }
1213
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001214 const char** attributes = toAttributes(attributePointer);
Jesse Wilsondf36e8c2010-01-23 00:49:53 -08001215 for (int index = 0; attributes[index * 2]; ++index) {
Elliott Hughes05960872010-05-26 17:45:07 -07001216 if (ExpatElementName(NULL, NULL, attributePointer, index).matches(uriBytes.c_str(),
1217 localNameBytes.c_str())) {
1218 return index;
Jesse Wilsondf36e8c2010-01-23 00:49:53 -08001219 }
1220 }
Elliott Hughes05960872010-05-26 17:45:07 -07001221 return -1;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001222}
1223
1224/**
1225 * Gets the value of the attribute with the given qualified name.
1226 *
1227 * @param attributePointer to the attribute array
1228 * @param uri to look for
1229 * @param localName to look for
1230 * @returns value of attribute with the given uri and local name or NULL if not
1231 * found
1232 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001233static jstring ExpatAttributes_getValueForQName(JNIEnv* env, jobject clazz,
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001234 jint attributePointer, jstring qName) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001235 jint index = ExpatAttributes_getIndexForQName(env, clazz, attributePointer, qName);
1236 return index == -1 ? NULL
1237 : ExpatAttributes_getValueByIndex(env, clazz, attributePointer, index);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001238}
1239
1240/**
1241 * Gets the value of the attribute with the given URI and name.
1242 *
1243 * @param attributePointer to the attribute array
1244 * @param uri to look for
1245 * @param localName to look for
1246 * @returns value of attribute with the given uri and local name or NULL if not
1247 * found
1248 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001249static jstring ExpatAttributes_getValue(JNIEnv* env, jobject clazz,
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001250 jint attributePointer, jstring uri, jstring localName) {
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001251 jint index = ExpatAttributes_getIndex(env, clazz, attributePointer, uri, localName);
1252 return index == -1 ? NULL
1253 : ExpatAttributes_getValueByIndex(env, clazz, attributePointer, index);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001254}
1255
1256/**
1257 * Clones an array of strings. Uses one contiguous block of memory so as to
1258 * maximize performance.
Elliott Hughes91499042010-06-21 18:06:12 -07001259 *
1260 * @param address char** to clone
1261 * @param count number of attributes
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001262 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001263static jint ExpatParser_cloneAttributes(JNIEnv* env, jobject, jint address, jint count) {
Elliott Hughes91499042010-06-21 18:06:12 -07001264 const char** source = reinterpret_cast<const char**>(static_cast<uintptr_t>(address));
1265 count <<= 1;
1266
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001267 // Figure out how big the buffer needs to be.
1268 int arraySize = (count + 1) * sizeof(char*);
1269 int totalSize = arraySize;
1270 int stringLengths[count];
1271 for (int i = 0; i < count; i++) {
1272 int length = strlen(source[i]);
1273 stringLengths[i] = length;
1274 totalSize += length + 1;
1275 }
1276
Elliott Hughes4eafbcc2010-06-21 16:52:06 -07001277 char* buffer = new char[totalSize];
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001278 if (buffer == NULL) {
Elliott Hughes4eafbcc2010-06-21 16:52:06 -07001279 throw_OutOfMemoryError(env);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001280 return NULL;
1281 }
1282
1283 // Array is at the beginning of the buffer.
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001284 char** clonedArray = reinterpret_cast<char**>(buffer);
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001285 clonedArray[count] = NULL; // null terminate
1286
Elliott Hughes91499042010-06-21 18:06:12 -07001287 // String data follows immediately after.
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001288 char* destinationString = buffer + arraySize;
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001289 for (int i = 0; i < count; i++) {
1290 const char* sourceString = source[i];
1291 int stringLength = stringLengths[i];
1292 memcpy(destinationString, sourceString, stringLength + 1);
1293 clonedArray[i] = destinationString;
1294 destinationString += stringLength + 1;
1295 }
1296
Elliott Hughes91499042010-06-21 18:06:12 -07001297 return static_cast<jint>(reinterpret_cast<uintptr_t>(buffer));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001298}
1299
1300/**
1301 * Frees cloned attributes.
1302 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001303static void ExpatAttributes_freeAttributes(JNIEnv*, jobject, jint pointer) {
Elliott Hughes4eafbcc2010-06-21 16:52:06 -07001304 delete[] reinterpret_cast<char*>(static_cast<uintptr_t>(pointer));
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001305}
1306
1307/**
1308 * Called when we initialize our Java parser class.
1309 *
1310 * @param clazz Java ExpatParser class
1311 */
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001312static void ExpatParser_staticInitialize(JNIEnv* env, jobject classObject, jstring empty) {
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001313 jclass clazz = reinterpret_cast<jclass>(classObject);
1314 startElementMethod = env->GetMethodID(clazz, "startElement",
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001315 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II)V");
1316 if (startElementMethod == NULL) return;
Elliott Hughes99c59bf2010-05-17 16:22:04 -07001317
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001318 endElementMethod = env->GetMethodID(clazz, "endElement",
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001319 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
1320 if (endElementMethod == NULL) return;
1321
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001322 textMethod = env->GetMethodID(clazz, "text", "([CI)V");
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001323 if (textMethod == NULL) return;
1324
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001325 commentMethod = env->GetMethodID(clazz, "comment", "([CI)V");
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001326 if (commentMethod == NULL) return;
1327
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001328 startCdataMethod = env->GetMethodID(clazz, "startCdata", "()V");
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001329 if (startCdataMethod == NULL) return;
1330
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001331 endCdataMethod = env->GetMethodID(clazz, "endCdata", "()V");
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001332 if (endCdataMethod == NULL) return;
1333
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001334 startDtdMethod = env->GetMethodID(clazz, "startDtd",
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001335 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
1336 if (startDtdMethod == NULL) return;
1337
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001338 endDtdMethod = env->GetMethodID(clazz, "endDtd", "()V");
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001339 if (endDtdMethod == NULL) return;
1340
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001341 startNamespaceMethod = env->GetMethodID(clazz, "startNamespace",
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001342 "(Ljava/lang/String;Ljava/lang/String;)V");
1343 if (startNamespaceMethod == NULL) return;
1344
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001345 endNamespaceMethod = env->GetMethodID(clazz, "endNamespace",
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001346 "(Ljava/lang/String;)V");
1347 if (endNamespaceMethod == NULL) return;
1348
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001349 processingInstructionMethod = env->GetMethodID(clazz,
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001350 "processingInstruction", "(Ljava/lang/String;Ljava/lang/String;)V");
1351 if (processingInstructionMethod == NULL) return;
1352
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001353 handleExternalEntityMethod = env->GetMethodID(clazz,
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001354 "handleExternalEntity",
1355 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
1356 if (handleExternalEntityMethod == NULL) return;
1357
Elliott Hughesc9b92b42009-12-09 16:05:29 -08001358 notationDeclMethod = env->GetMethodID(clazz, "notationDecl",
1359 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
1360 if (notationDeclMethod == NULL) return;
1361
1362 unparsedEntityDeclMethod = env->GetMethodID(clazz, "unparsedEntityDecl",
1363 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
1364 if (unparsedEntityDeclMethod == NULL) return;
1365
Elliott Hughesa9f5c162010-06-16 16:32:18 -07001366 internMethod = env->GetMethodID(JniConstants::stringClass, "intern", "()Ljava/lang/String;");
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001367 if (internMethod == NULL) return;
1368
1369 // Reference to "".
1370 emptyString = (jstring) env->NewGlobalRef(empty);
1371}
1372
1373static JNINativeMethod parserMethods[] = {
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001374 { "append", "(ILjava/lang/String;Z)V", (void*) ExpatParser_appendString },
1375 { "append", "(I[BII)V", (void*) ExpatParser_appendBytes },
1376 { "append", "(I[CII)V", (void*) ExpatParser_appendCharacters },
1377 { "cloneAttributes", "(II)I", (void*) ExpatParser_cloneAttributes },
1378 { "column", "(I)I", (void*) ExpatParser_column },
1379 { "createEntityParser", "(ILjava/lang/String;)I", (void*) ExpatParser_createEntityParser},
1380 { "initialize", "(Ljava/lang/String;Z)I", (void*) ExpatParser_initialize},
1381 { "line", "(I)I", (void*) ExpatParser_line },
1382 { "release", "(I)V", (void*) ExpatParser_release },
1383 { "releaseParser", "(I)V", (void*) ExpatParser_releaseParser },
1384 { "staticInitialize", "(Ljava/lang/String;)V", (void*) ExpatParser_staticInitialize},
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001385};
1386
1387static JNINativeMethod attributeMethods[] = {
Elliott Hughes2edc04d2010-07-22 14:07:37 -07001388 { "freeAttributes", "(I)V", (void*) ExpatAttributes_freeAttributes },
1389 { "getIndex", "(ILjava/lang/String;)I", (void*) ExpatAttributes_getIndexForQName },
1390 { "getIndex", "(ILjava/lang/String;Ljava/lang/String;)I", (void*) ExpatAttributes_getIndex },
1391 { "getLocalName", "(III)Ljava/lang/String;", (void*) ExpatAttributes_getLocalName },
1392 { "getQName", "(III)Ljava/lang/String;", (void*) ExpatAttributes_getQName },
1393 { "getURI", "(III)Ljava/lang/String;", (void*) ExpatAttributes_getURI },
1394 { "getValue", "(II)Ljava/lang/String;", (void*) ExpatAttributes_getValueByIndex },
1395 { "getValue", "(ILjava/lang/String;)Ljava/lang/String;", (void*) ExpatAttributes_getValueForQName },
1396 { "getValue", "(ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;", (void*) ExpatAttributes_getValue },
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001397};
Elliott Hughesc08f9fb2010-04-16 17:44:12 -07001398int register_org_apache_harmony_xml_ExpatParser(JNIEnv* env) {
The Android Open Source Projectfdb27042008-10-21 07:00:00 -07001399 int result = jniRegisterNativeMethods(env, "org/apache/harmony/xml/ExpatParser",
1400 parserMethods, NELEM(parserMethods));
1401 if (result != 0) {
1402 return result;
1403 }
1404
1405 return jniRegisterNativeMethods(env, "org/apache/harmony/xml/ExpatAttributes",
1406 attributeMethods, NELEM(attributeMethods));
1407}