1 /*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "ExpatParser"
18
19 #include "JNIHelp.h"
20 #include "JniConstants.h"
21 #include "JniException.h"
22 #include "LocalArray.h"
23 #include "ScopedLocalRef.h"
24 #include "ScopedPrimitiveArray.h"
25 #include "ScopedStringChars.h"
26 #include "ScopedUtfChars.h"
27 #include "UniquePtr.h"
28 #include "jni.h"
29 #include "cutils/log.h"
30 #include "unicode/unistr.h"
31
32 #include <string.h>
33 #include <expat.h>
34
35 #define BUCKET_COUNT 128
36
37 /**
38 * Wrapper around an interned string.
39 */
40 struct InternedString {
InternedStringInternedString41 InternedString() : interned(NULL), bytes(NULL) {
42 }
43
~InternedStringInternedString44 ~InternedString() {
45 delete[] bytes;
46 }
47
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. */
55 int hash;
56 };
57
58 /**
59 * Keeps track of strings between start and end events.
60 */
61 class StringStack {
62 public:
StringStack()63 StringStack() : array(new jstring[DEFAULT_CAPACITY]), capacity(DEFAULT_CAPACITY), size(0) {
64 }
65
~StringStack()66 ~StringStack() {
67 delete[] array;
68 }
69
push(JNIEnv * env,jstring s)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) {
75 jniThrowOutOfMemoryError(env, NULL);
76 return;
77 }
78 memcpy(newArray, array, capacity * sizeof(jstring));
79
80 array = newArray;
81 capacity = newCapacity;
82 }
83
84 array[size++] = s;
85 }
86
pop()87 jstring pop() {
88 return (size == 0) ? NULL : array[--size];
89 }
90
91 private:
92 enum { DEFAULT_CAPACITY = 10 };
93
94 jstring* array;
95 int capacity;
96 int size;
97 };
98
99 /**
100 * Data passed to parser handler method by the parser.
101 */
102 struct ParsingContext {
ParsingContextParsingContext103 ParsingContext(jobject object) : env(NULL), object(object), buffer(NULL), bufferSize(-1) {
104 for (int i = 0; i < BUCKET_COUNT; i++) {
105 internedStrings[i] = NULL;
106 }
107 }
108
109 // Warning: 'env' must be valid on entry.
~ParsingContextParsingContext110 ~ParsingContext() {
111 freeBuffer();
112
113 // Free interned string cache.
114 for (int i = 0; i < BUCKET_COUNT; i++) {
115 if (internedStrings[i]) {
116 InternedString** bucket = internedStrings[i];
117 InternedString* current;
118 while ((current = *(bucket++)) != NULL) {
119 // Free the interned string reference.
120 env->DeleteGlobalRef(current->interned);
121
122 // Free the bucket.
123 delete current;
124 }
125
126 // Free the buckets.
127 delete[] internedStrings[i];
128 }
129 }
130 }
131
ensureCapacityParsingContext132 jcharArray ensureCapacity(int length) {
133 if (bufferSize < length) {
134 // Free the existing char[].
135 freeBuffer();
136
137 // Allocate a new char[].
138 jcharArray javaBuffer = env->NewCharArray(length);
139 if (javaBuffer == NULL) return NULL;
140
141 // Create a global reference.
142 javaBuffer = (jcharArray) env->NewGlobalRef(javaBuffer);
143 if (javaBuffer == NULL) return NULL;
144
145 buffer = javaBuffer;
146 bufferSize = length;
147 }
148 return buffer;
149 }
150
151 private:
freeBufferParsingContext152 void freeBuffer() {
153 if (buffer != NULL) {
154 env->DeleteGlobalRef(buffer);
155 buffer = NULL;
156 bufferSize = -1;
157 }
158 }
159
160 public:
161 /**
162 * The JNI environment for the current thread. This should only be used
163 * to keep a reference to the env for use in Expat callbacks.
164 */
165 JNIEnv* env;
166
167 /** The Java parser object. */
168 jobject object;
169
170 /** Buffer for text events. */
171 jcharArray buffer;
172
173 private:
174 /** The size of our buffer in jchars. */
175 int bufferSize;
176
177 public:
178 /** Current attributes. */
179 const char** attributes;
180
181 /** Number of attributes. */
182 int attributeCount;
183
184 /** True if namespace support is enabled. */
185 bool processNamespaces;
186
187 /** Keep track of names. */
188 StringStack stringStack;
189
190 /** Cache of interned strings. */
191 InternedString** internedStrings[BUCKET_COUNT];
192 };
193
toParsingContext(void * data)194 static ParsingContext* toParsingContext(void* data) {
195 return reinterpret_cast<ParsingContext*>(data);
196 }
197
toParsingContext(XML_Parser parser)198 static ParsingContext* toParsingContext(XML_Parser parser) {
199 return reinterpret_cast<ParsingContext*>(XML_GetUserData(parser));
200 }
201
202 static jmethodID commentMethod;
203 static jmethodID endCdataMethod;
204 static jmethodID endDtdMethod;
205 static jmethodID endElementMethod;
206 static jmethodID endNamespaceMethod;
207 static jmethodID handleExternalEntityMethod;
208 static jmethodID internMethod;
209 static jmethodID notationDeclMethod;
210 static jmethodID processingInstructionMethod;
211 static jmethodID startCdataMethod;
212 static jmethodID startDtdMethod;
213 static jmethodID startElementMethod;
214 static jmethodID startNamespaceMethod;
215 static jmethodID textMethod;
216 static jmethodID unparsedEntityDeclMethod;
217 static jstring emptyString;
218
219 /**
220 * Calculates a hash code for a null-terminated string. This is *not* equivalent
221 * to Java's String.hashCode(). This hashes the bytes while String.hashCode()
222 * hashes UTF-16 chars.
223 *
224 * @param s null-terminated string to hash
225 * @returns hash code
226 */
hashString(const char * s)227 static int hashString(const char* s) {
228 int hash = 0;
229 if (s) {
230 while (*s) {
231 hash = hash * 31 + *s++;
232 }
233 }
234 return hash;
235 }
236
237 /**
238 * Creates a new interned string wrapper. Looks up the interned string
239 * representing the given UTF-8 bytes.
240 *
241 * @param bytes null-terminated string to intern
242 * @param hash of bytes
243 * @returns wrapper of interned Java string
244 */
newInternedString(JNIEnv * env,const char * bytes,int hash)245 static InternedString* newInternedString(JNIEnv* env, const char* bytes, int hash) {
246 // Allocate a new wrapper.
247 UniquePtr<InternedString> wrapper(new InternedString);
248 if (wrapper.get() == NULL) {
249 jniThrowOutOfMemoryError(env, NULL);
250 return NULL;
251 }
252
253 // Create a copy of the UTF-8 bytes.
254 // TODO: sometimes we already know the length. Reuse it if so.
255 char* copy = new char[strlen(bytes) + 1];
256 if (copy == NULL) {
257 jniThrowOutOfMemoryError(env, NULL);
258 return NULL;
259 }
260 strcpy(copy, bytes);
261 wrapper->bytes = copy;
262
263 // Save the hash.
264 wrapper->hash = hash;
265
266 // To intern a string, we must first create a new string and then call
267 // intern() on it. We then keep a global reference to the interned string.
268 ScopedLocalRef<jstring> newString(env, env->NewStringUTF(bytes));
269 if (env->ExceptionCheck()) {
270 return NULL;
271 }
272
273 // Call intern().
274 ScopedLocalRef<jstring> interned(env,
275 reinterpret_cast<jstring>(env->CallObjectMethod(newString.get(), internMethod)));
276 if (env->ExceptionCheck()) {
277 return NULL;
278 }
279
280 // Create a global reference to the interned string.
281 wrapper->interned = (jstring) env->NewGlobalRef(interned.get());
282 if (env->ExceptionCheck()) {
283 return NULL;
284 }
285
286 return wrapper.release();
287 }
288
289 /**
290 * Allocates a new bucket with one entry.
291 *
292 * @param entry to store in the bucket
293 * @returns a reference to the bucket
294 */
newInternedStringBucket(InternedString * entry)295 static InternedString** newInternedStringBucket(InternedString* entry) {
296 InternedString** bucket = new InternedString*[2];
297 if (bucket != NULL) {
298 bucket[0] = entry;
299 bucket[1] = NULL;
300 }
301 return bucket;
302 }
303
304 /**
305 * Expands an interned string bucket and adds the given entry. Frees the
306 * provided bucket and returns a new one.
307 *
308 * @param existingBucket the bucket to replace
309 * @param entry to add to the bucket
310 * @returns a reference to the newly-allocated bucket containing the given entry
311 */
expandInternedStringBucket(InternedString ** existingBucket,InternedString * entry)312 static InternedString** expandInternedStringBucket(
313 InternedString** existingBucket, InternedString* entry) {
314 // Determine the size of the existing bucket.
315 int size = 0;
316 while (existingBucket[size]) size++;
317
318 // Allocate the new bucket with enough space for one more entry and
319 // a null terminator.
320 InternedString** newBucket = new InternedString*[size + 2];
321 if (newBucket == NULL) return NULL;
322
323 memcpy(newBucket, existingBucket, size * sizeof(InternedString*));
324 newBucket[size] = entry;
325 newBucket[size + 1] = NULL;
326
327 return newBucket;
328 }
329
330 /**
331 * Returns an interned string for the given UTF-8 string.
332 *
333 * @param bucket to search for s
334 * @param s null-terminated string to find
335 * @param hash of s
336 * @returns interned Java string equivalent of s or null if not found
337 */
findInternedString(InternedString ** bucket,const char * s,int hash)338 static jstring findInternedString(InternedString** bucket, const char* s, int hash) {
339 InternedString* current;
340 while ((current = *(bucket++)) != NULL) {
341 if (current->hash != hash) continue;
342 if (!strcmp(s, current->bytes)) return current->interned;
343 }
344 return NULL;
345 }
346
347 /**
348 * Returns an interned string for the given UTF-8 string.
349 *
350 * @param s null-terminated string to intern
351 * @returns interned Java string equivelent of s or NULL if s is null
352 */
internString(JNIEnv * env,ParsingContext * parsingContext,const char * s)353 static jstring internString(JNIEnv* env, ParsingContext* parsingContext, const char* s) {
354 if (s == NULL) return NULL;
355
356 int hash = hashString(s);
357 int bucketIndex = hash & (BUCKET_COUNT - 1);
358
359 InternedString*** buckets = parsingContext->internedStrings;
360 InternedString** bucket = buckets[bucketIndex];
361 InternedString* internedString;
362
363 if (bucket) {
364 // We have a bucket already. Look for the given string.
365 jstring found = findInternedString(bucket, s, hash);
366 if (found) {
367 // We found it!
368 return found;
369 }
370
371 // We didn't find it. :(
372 // Create a new entry.
373 internedString = newInternedString(env, s, hash);
374 if (internedString == NULL) return NULL;
375
376 // Expand the bucket.
377 bucket = expandInternedStringBucket(bucket, internedString);
378 if (bucket == NULL) {
379 jniThrowOutOfMemoryError(env, NULL);
380 return NULL;
381 }
382
383 buckets[bucketIndex] = bucket;
384
385 return internedString->interned;
386 } else {
387 // We don't even have a bucket yet. Create an entry.
388 internedString = newInternedString(env, s, hash);
389 if (internedString == NULL) return NULL;
390
391 // Create a new bucket with one entry.
392 bucket = newInternedStringBucket(internedString);
393 if (bucket == NULL) {
394 jniThrowOutOfMemoryError(env, NULL);
395 return NULL;
396 }
397
398 buckets[bucketIndex] = bucket;
399
400 return internedString->interned;
401 }
402 }
403
jniThrowExpatException(JNIEnv * env,XML_Error error)404 static void jniThrowExpatException(JNIEnv* env, XML_Error error) {
405 const char* message = XML_ErrorString(error);
406 jniThrowException(env, "org/apache/harmony/xml/ExpatException", message);
407 }
408
409 /**
410 * Copies UTF-8 characters into the buffer. Returns the number of Java chars
411 * which were buffered.
412 *
413 * @returns number of UTF-16 characters which were copied
414 */
fillBuffer(ParsingContext * parsingContext,const char * utf8,int byteCount)415 static size_t fillBuffer(ParsingContext* parsingContext, const char* utf8, int byteCount) {
416 JNIEnv* env = parsingContext->env;
417
418 // Grow buffer if necessary (the length in bytes is always >= the length in chars).
419 jcharArray javaChars = parsingContext->ensureCapacity(byteCount);
420 if (javaChars == NULL) return -1;
421
422 // Decode UTF-8 characters into our char[].
423 ScopedCharArrayRW chars(env, javaChars);
424 if (chars.get() == NULL) {
425 return -1;
426 }
427 UErrorCode status = U_ZERO_ERROR;
428 UnicodeString utf16(UnicodeString::fromUTF8(StringPiece(utf8, byteCount)));
429 return utf16.extract(chars.get(), byteCount, status);
430 }
431
432 /**
433 * Buffers the given text and passes it to the given method.
434 *
435 * @param method to pass the characters and length to with signature
436 * (char[], int)
437 * @param data parsing context
438 * @param text to copy into the buffer
439 * @param length of text to copy (in bytes)
440 */
bufferAndInvoke(jmethodID method,void * data,const char * text,size_t length)441 static void bufferAndInvoke(jmethodID method, void* data, const char* text, size_t length) {
442 ParsingContext* parsingContext = toParsingContext(data);
443 JNIEnv* env = parsingContext->env;
444
445 // Bail out if a previously called handler threw an exception.
446 if (env->ExceptionCheck()) return;
447
448 // Buffer the element name.
449 size_t utf16length = fillBuffer(parsingContext, text, length);
450
451 // Invoke given method.
452 jobject javaParser = parsingContext->object;
453 jcharArray buffer = parsingContext->buffer;
454 env->CallVoidMethod(javaParser, method, buffer, utf16length);
455 }
456
toAttributes(jint attributePointer)457 static const char** toAttributes(jint attributePointer) {
458 return reinterpret_cast<const char**>(static_cast<uintptr_t>(attributePointer));
459 }
460
461 /**
462 * The component parts of an attribute or element name.
463 */
464 class ExpatElementName {
465 public:
ExpatElementName(JNIEnv * env,ParsingContext * parsingContext,jint attributePointer,jint index)466 ExpatElementName(JNIEnv* env, ParsingContext* parsingContext, jint attributePointer, jint index) {
467 const char** attributes = toAttributes(attributePointer);
468 const char* name = attributes[index * 2];
469 init(env, parsingContext, name);
470 }
471
ExpatElementName(JNIEnv * env,ParsingContext * parsingContext,const char * s)472 ExpatElementName(JNIEnv* env, ParsingContext* parsingContext, const char* s) {
473 init(env, parsingContext, s);
474 }
475
~ExpatElementName()476 ~ExpatElementName() {
477 free(mCopy);
478 }
479
480 /**
481 * Returns the namespace URI, like "http://www.w3.org/1999/xhtml".
482 * Possibly empty.
483 */
uri()484 jstring uri() {
485 return internString(mEnv, mParsingContext, mUri);
486 }
487
488 /**
489 * Returns the element or attribute local name, like "h1". Never empty. When
490 * namespace processing is disabled, this may contain a prefix, yielding a
491 * local name like "html:h1". In such cases, the qName will always be empty.
492 */
localName()493 jstring localName() {
494 return internString(mEnv, mParsingContext, mLocalName);
495 }
496
497 /**
498 * Returns the namespace prefix, like "html". Possibly empty.
499 */
qName()500 jstring qName() {
501 if (*mPrefix == 0) {
502 return localName();
503 }
504
505 // return prefix + ":" + localName
506 LocalArray<1024> qName(strlen(mPrefix) + 1 + strlen(mLocalName) + 1);
507 snprintf(&qName[0], qName.size(), "%s:%s", mPrefix, mLocalName);
508 return internString(mEnv, mParsingContext, &qName[0]);
509 }
510
511 /**
512 * Returns true if this expat name has the same URI and local name.
513 */
matches(const char * uri,const char * localName)514 bool matches(const char* uri, const char* localName) {
515 return strcmp(uri, mUri) == 0 && strcmp(localName, mLocalName) == 0;
516 }
517
518 /**
519 * Returns true if this expat name has the same qualified name.
520 */
matchesQName(const char * qName)521 bool matchesQName(const char* qName) {
522 const char* lastColon = strrchr(qName, ':');
523
524 // Compare local names only if either:
525 // - the input qualified name doesn't have a colon (like "h1")
526 // - this element doesn't have a prefix. Such is the case when it
527 // doesn't belong to a namespace, or when this parser's namespace
528 // processing is disabled. In the latter case, this element's local
529 // name may still contain a colon (like "html:h1").
530 if (lastColon == NULL || *mPrefix == 0) {
531 return strcmp(qName, mLocalName) == 0;
532 }
533
534 // Otherwise compare both prefix and local name
535 size_t prefixLength = lastColon - qName;
536 return strlen(mPrefix) == prefixLength
537 && strncmp(qName, mPrefix, prefixLength) == 0
538 && strcmp(lastColon + 1, mLocalName) == 0;
539 }
540
541 private:
542 JNIEnv* mEnv;
543 ParsingContext* mParsingContext;
544 char* mCopy;
545 const char* mUri;
546 const char* mLocalName;
547 const char* mPrefix;
548
549 /**
550 * Decodes an Expat-encoded name of one of these three forms:
551 * "uri|localName|prefix" (example: "http://www.w3.org/1999/xhtml|h1|html")
552 * "uri|localName" (example: "http://www.w3.org/1999/xhtml|h1")
553 * "localName" (example: "h1")
554 */
init(JNIEnv * env,ParsingContext * parsingContext,const char * s)555 void init(JNIEnv* env, ParsingContext* parsingContext, const char* s) {
556 mEnv = env;
557 mParsingContext = parsingContext;
558 mCopy = strdup(s);
559
560 // split the input into up to 3 parts: a|b|c
561 char* context = NULL;
562 char* a = strtok_r(mCopy, "|", &context);
563 char* b = strtok_r(NULL, "|", &context);
564 char* c = strtok_r(NULL, "|", &context);
565
566 if (c != NULL) { // input of the form "uri|localName|prefix"
567 mUri = a;
568 mLocalName = b;
569 mPrefix = c;
570 } else if (b != NULL) { // input of the form "uri|localName"
571 mUri = a;
572 mLocalName = b;
573 mPrefix = "";
574 } else { // input of the form "localName"
575 mLocalName = a;
576 mUri = "";
577 mPrefix = "";
578 }
579 }
580
581 // Disallow copy and assignment.
582 ExpatElementName(const ExpatElementName&);
583 void operator=(const ExpatElementName&);
584 };
585
586 /**
587 * Called by Expat at the start of an element. Delegates to the same method
588 * on the Java parser.
589 *
590 * @param data parsing context
591 * @param elementName "uri|localName" or "localName" for the current element
592 * @param attributes alternating attribute names and values. Like element
593 * names, attribute names follow the format "uri|localName" or "localName".
594 */
startElement(void * data,const char * elementName,const char ** attributes)595 static void startElement(void* data, const char* elementName, const char** attributes) {
596 ParsingContext* parsingContext = toParsingContext(data);
597 JNIEnv* env = parsingContext->env;
598
599 // Bail out if a previously called handler threw an exception.
600 if (env->ExceptionCheck()) return;
601
602 // Count the number of attributes.
603 int count = 0;
604 while (attributes[count * 2]) count++;
605
606 // Make the attributes available for the duration of this call.
607 parsingContext->attributes = attributes;
608 parsingContext->attributeCount = count;
609
610 jobject javaParser = parsingContext->object;
611
612 ExpatElementName e(env, parsingContext, elementName);
613 jstring uri = parsingContext->processNamespaces ? e.uri() : emptyString;
614 jstring localName = parsingContext->processNamespaces ? e.localName() : emptyString;
615 jstring qName = e.qName();
616
617 parsingContext->stringStack.push(env, qName);
618 parsingContext->stringStack.push(env, uri);
619 parsingContext->stringStack.push(env, localName);
620
621 env->CallVoidMethod(javaParser, startElementMethod, uri, localName, qName, attributes, count);
622
623 parsingContext->attributes = NULL;
624 parsingContext->attributeCount = -1;
625 }
626
627 /**
628 * Called by Expat at the end of an element. Delegates to the same method
629 * on the Java parser.
630 *
631 * @param data parsing context
632 * @param elementName "uri|localName" or "localName" for the current element;
633 * we assume that this matches the last data on our stack.
634 */
endElement(void * data,const char *)635 static void endElement(void* data, const char* /*elementName*/) {
636 ParsingContext* parsingContext = toParsingContext(data);
637 JNIEnv* env = parsingContext->env;
638
639 // Bail out if a previously called handler threw an exception.
640 if (env->ExceptionCheck()) return;
641
642 jobject javaParser = parsingContext->object;
643
644 jstring localName = parsingContext->stringStack.pop();
645 jstring uri = parsingContext->stringStack.pop();
646 jstring qName = parsingContext->stringStack.pop();
647
648 env->CallVoidMethod(javaParser, endElementMethod, uri, localName, qName);
649 }
650
651 /**
652 * Called by Expat when it encounters text. Delegates to the same method
653 * on the Java parser. This may be called mutiple times with incremental pieces
654 * of the same contiguous block of text.
655 *
656 * @param data parsing context
657 * @param characters buffer containing encountered text
658 * @param length number of characters in the buffer
659 */
text(void * data,const char * characters,int length)660 static void text(void* data, const char* characters, int length) {
661 bufferAndInvoke(textMethod, data, characters, length);
662 }
663
664 /**
665 * Called by Expat when it encounters a comment. Delegates to the same method
666 * on the Java parser.
667
668 * @param data parsing context
669 * @param comment 0-terminated
670 */
comment(void * data,const char * comment)671 static void comment(void* data, const char* comment) {
672 bufferAndInvoke(commentMethod, data, comment, strlen(comment));
673 }
674
675 /**
676 * Called by Expat at the beginning of a namespace mapping.
677 *
678 * @param data parsing context
679 * @param prefix null-terminated namespace prefix used in the XML
680 * @param uri of the namespace
681 */
startNamespace(void * data,const char * prefix,const char * uri)682 static void startNamespace(void* data, const char* prefix, const char* uri) {
683 ParsingContext* parsingContext = toParsingContext(data);
684 JNIEnv* env = parsingContext->env;
685
686 // Bail out if a previously called handler threw an exception.
687 if (env->ExceptionCheck()) return;
688
689 jstring internedPrefix = emptyString;
690 if (prefix != NULL) {
691 internedPrefix = internString(env, parsingContext, prefix);
692 if (env->ExceptionCheck()) return;
693 }
694
695 jstring internedUri = emptyString;
696 if (uri != NULL) {
697 internedUri = internString(env, parsingContext, uri);
698 if (env->ExceptionCheck()) return;
699 }
700
701 parsingContext->stringStack.push(env, internedPrefix);
702
703 jobject javaParser = parsingContext->object;
704 env->CallVoidMethod(javaParser, startNamespaceMethod, internedPrefix, internedUri);
705 }
706
707 /**
708 * Called by Expat at the end of a namespace mapping.
709 *
710 * @param data parsing context
711 * @param prefix null-terminated namespace prefix used in the XML;
712 * we assume this is the same as the last prefix on the stack.
713 */
endNamespace(void * data,const char *)714 static void endNamespace(void* data, const char* /*prefix*/) {
715 ParsingContext* parsingContext = toParsingContext(data);
716 JNIEnv* env = parsingContext->env;
717
718 // Bail out if a previously called handler threw an exception.
719 if (env->ExceptionCheck()) return;
720
721 jstring internedPrefix = parsingContext->stringStack.pop();
722
723 jobject javaParser = parsingContext->object;
724 env->CallVoidMethod(javaParser, endNamespaceMethod, internedPrefix);
725 }
726
727 /**
728 * Called by Expat at the beginning of a CDATA section.
729 *
730 * @param data parsing context
731 */
startCdata(void * data)732 static void startCdata(void* data) {
733 ParsingContext* parsingContext = toParsingContext(data);
734 JNIEnv* env = parsingContext->env;
735
736 // Bail out if a previously called handler threw an exception.
737 if (env->ExceptionCheck()) return;
738
739 jobject javaParser = parsingContext->object;
740 env->CallVoidMethod(javaParser, startCdataMethod);
741 }
742
743 /**
744 * Called by Expat at the end of a CDATA section.
745 *
746 * @param data parsing context
747 */
endCdata(void * data)748 static void endCdata(void* data) {
749 ParsingContext* parsingContext = toParsingContext(data);
750 JNIEnv* env = parsingContext->env;
751
752 // Bail out if a previously called handler threw an exception.
753 if (env->ExceptionCheck()) return;
754
755 jobject javaParser = parsingContext->object;
756 env->CallVoidMethod(javaParser, endCdataMethod);
757 }
758
759 /**
760 * Called by Expat at the beginning of a DOCTYPE section.
761 * Expat gives us 'hasInternalSubset', but the Java API doesn't expect it, so we don't need it.
762 */
startDtd(void * data,const char * name,const char * systemId,const char * publicId,int)763 static void startDtd(void* data, const char* name,
764 const char* systemId, const char* publicId, int /*hasInternalSubset*/) {
765 ParsingContext* parsingContext = toParsingContext(data);
766 JNIEnv* env = parsingContext->env;
767
768 // Bail out if a previously called handler threw an exception.
769 if (env->ExceptionCheck()) return;
770
771 jstring javaName = internString(env, parsingContext, name);
772 if (env->ExceptionCheck()) return;
773
774 jstring javaPublicId = internString(env, parsingContext, publicId);
775 if (env->ExceptionCheck()) return;
776
777 jstring javaSystemId = internString(env, parsingContext, systemId);
778 if (env->ExceptionCheck()) return;
779
780 jobject javaParser = parsingContext->object;
781 env->CallVoidMethod(javaParser, startDtdMethod, javaName, javaPublicId,
782 javaSystemId);
783 }
784
785 /**
786 * Called by Expat at the end of a DOCTYPE section.
787 *
788 * @param data parsing context
789 */
endDtd(void * data)790 static void endDtd(void* data) {
791 ParsingContext* parsingContext = toParsingContext(data);
792 JNIEnv* env = parsingContext->env;
793
794 // Bail out if a previously called handler threw an exception.
795 if (env->ExceptionCheck()) return;
796
797 jobject javaParser = parsingContext->object;
798 env->CallVoidMethod(javaParser, endDtdMethod);
799 }
800
801 /**
802 * Called by Expat when it encounters processing instructions.
803 *
804 * @param data parsing context
805 * @param target of the instruction
806 * @param instructionData
807 */
processingInstruction(void * data,const char * target,const char * instructionData)808 static void processingInstruction(void* data, const char* target, const char* instructionData) {
809 ParsingContext* parsingContext = toParsingContext(data);
810 JNIEnv* env = parsingContext->env;
811
812 // Bail out if a previously called handler threw an exception.
813 if (env->ExceptionCheck()) return;
814
815 jstring javaTarget = internString(env, parsingContext, target);
816 if (env->ExceptionCheck()) return;
817
818 ScopedLocalRef<jstring> javaInstructionData(env, env->NewStringUTF(instructionData));
819 if (env->ExceptionCheck()) return;
820
821 jobject javaParser = parsingContext->object;
822 env->CallVoidMethod(javaParser, processingInstructionMethod, javaTarget, javaInstructionData.get());
823 }
824
825 /**
826 * Creates a new entity parser.
827 *
828 * @param object the Java ExpatParser instance
829 * @param parentParser pointer
830 * @param javaEncoding the character encoding name
831 * @param javaContext that was provided to handleExternalEntity
832 * @returns the pointer to the C Expat entity parser
833 */
ExpatParser_createEntityParser(JNIEnv * env,jobject,jint parentParser,jstring javaContext)834 static jint ExpatParser_createEntityParser(JNIEnv* env, jobject, jint parentParser, jstring javaContext) {
835 ScopedUtfChars context(env, javaContext);
836 if (context.c_str() == NULL) {
837 return 0;
838 }
839
840 XML_Parser parent = (XML_Parser) parentParser;
841 XML_Parser entityParser = XML_ExternalEntityParserCreate(parent, context.c_str(), NULL);
842 if (entityParser == NULL) {
843 jniThrowOutOfMemoryError(env, NULL);
844 }
845
846 return (jint) entityParser;
847 }
848
849 /**
850 * Handles external entities. We ignore the "base" URI and keep track of it
851 * ourselves.
852 */
handleExternalEntity(XML_Parser parser,const char * context,const char *,const char * systemId,const char * publicId)853 static int handleExternalEntity(XML_Parser parser, const char* context,
854 const char*, const char* systemId, const char* publicId) {
855 ParsingContext* parsingContext = toParsingContext(parser);
856 jobject javaParser = parsingContext->object;
857 JNIEnv* env = parsingContext->env;
858 jobject object = parsingContext->object;
859
860 // Bail out if a previously called handler threw an exception.
861 if (env->ExceptionCheck()) {
862 return XML_STATUS_ERROR;
863 }
864
865 ScopedLocalRef<jstring> javaSystemId(env, env->NewStringUTF(systemId));
866 if (env->ExceptionCheck()) {
867 return XML_STATUS_ERROR;
868 }
869 ScopedLocalRef<jstring> javaPublicId(env, env->NewStringUTF(publicId));
870 if (env->ExceptionCheck()) {
871 return XML_STATUS_ERROR;
872 }
873 ScopedLocalRef<jstring> javaContext(env, env->NewStringUTF(context));
874 if (env->ExceptionCheck()) {
875 return XML_STATUS_ERROR;
876 }
877
878 // Pass the wrapped parser and both strings to java.
879 env->CallVoidMethod(javaParser, handleExternalEntityMethod, javaContext.get(),
880 javaPublicId.get(), javaSystemId.get());
881
882 /*
883 * Parsing the external entity leaves parsingContext->env and object set to
884 * NULL, so we need to restore both.
885 *
886 * TODO: consider restoring the original env and object instead of setting
887 * them to NULL in the append() functions.
888 */
889 parsingContext->env = env;
890 parsingContext->object = object;
891
892 return env->ExceptionCheck() ? XML_STATUS_ERROR : XML_STATUS_OK;
893 }
894
895 /**
896 * Expat gives us 'base', but the Java API doesn't expect it, so we don't need it.
897 */
unparsedEntityDecl(void * data,const char * name,const char *,const char * systemId,const char * publicId,const char * notationName)898 static void unparsedEntityDecl(void* data, const char* name, const char* /*base*/, const char* systemId, const char* publicId, const char* notationName) {
899 ParsingContext* parsingContext = toParsingContext(data);
900 jobject javaParser = parsingContext->object;
901 JNIEnv* env = parsingContext->env;
902
903 // Bail out if a previously called handler threw an exception.
904 if (env->ExceptionCheck()) return;
905
906 ScopedLocalRef<jstring> javaName(env, env->NewStringUTF(name));
907 if (env->ExceptionCheck()) return;
908 ScopedLocalRef<jstring> javaPublicId(env, env->NewStringUTF(publicId));
909 if (env->ExceptionCheck()) return;
910 ScopedLocalRef<jstring> javaSystemId(env, env->NewStringUTF(systemId));
911 if (env->ExceptionCheck()) return;
912 ScopedLocalRef<jstring> javaNotationName(env, env->NewStringUTF(notationName));
913 if (env->ExceptionCheck()) return;
914
915 env->CallVoidMethod(javaParser, unparsedEntityDeclMethod, javaName.get(), javaPublicId.get(), javaSystemId.get(), javaNotationName.get());
916 }
917
918 /**
919 * Expat gives us 'base', but the Java API doesn't expect it, so we don't need it.
920 */
notationDecl(void * data,const char * name,const char *,const char * systemId,const char * publicId)921 static void notationDecl(void* data, const char* name, const char* /*base*/, const char* systemId, const char* publicId) {
922 ParsingContext* parsingContext = toParsingContext(data);
923 jobject javaParser = parsingContext->object;
924 JNIEnv* env = parsingContext->env;
925
926 // Bail out if a previously called handler threw an exception.
927 if (env->ExceptionCheck()) return;
928
929 ScopedLocalRef<jstring> javaName(env, env->NewStringUTF(name));
930 if (env->ExceptionCheck()) return;
931 ScopedLocalRef<jstring> javaPublicId(env, env->NewStringUTF(publicId));
932 if (env->ExceptionCheck()) return;
933 ScopedLocalRef<jstring> javaSystemId(env, env->NewStringUTF(systemId));
934 if (env->ExceptionCheck()) return;
935
936 env->CallVoidMethod(javaParser, notationDeclMethod, javaName.get(), javaPublicId.get(), javaSystemId.get());
937 }
938
939 /**
940 * Creates a new Expat parser. Called from the Java ExpatParser constructor.
941 *
942 * @param object the Java ExpatParser instance
943 * @param javaEncoding the character encoding name
944 * @param processNamespaces true if the parser should handle namespaces
945 * @returns the pointer to the C Expat parser
946 */
ExpatParser_initialize(JNIEnv * env,jobject object,jstring javaEncoding,jboolean processNamespaces)947 static jint ExpatParser_initialize(JNIEnv* env, jobject object, jstring javaEncoding,
948 jboolean processNamespaces) {
949 // Allocate parsing context.
950 UniquePtr<ParsingContext> context(new ParsingContext(object));
951 if (context.get() == NULL) {
952 jniThrowOutOfMemoryError(env, NULL);
953 return 0;
954 }
955
956 context->processNamespaces = (bool) processNamespaces;
957
958 // Create a parser.
959 XML_Parser parser;
960 ScopedUtfChars encoding(env, javaEncoding);
961 if (encoding.c_str() == NULL) {
962 return 0;
963 }
964 if (processNamespaces) {
965 // Use '|' to separate URIs from local names.
966 parser = XML_ParserCreateNS(encoding.c_str(), '|');
967 } else {
968 parser = XML_ParserCreate(encoding.c_str());
969 }
970
971 if (parser != NULL) {
972 if (processNamespaces) {
973 XML_SetNamespaceDeclHandler(parser, startNamespace, endNamespace);
974 XML_SetReturnNSTriplet(parser, 1);
975 }
976
977 XML_SetCdataSectionHandler(parser, startCdata, endCdata);
978 XML_SetCharacterDataHandler(parser, text);
979 XML_SetCommentHandler(parser, comment);
980 XML_SetDoctypeDeclHandler(parser, startDtd, endDtd);
981 XML_SetElementHandler(parser, startElement, endElement);
982 XML_SetExternalEntityRefHandler(parser, handleExternalEntity);
983 XML_SetNotationDeclHandler(parser, notationDecl);
984 XML_SetProcessingInstructionHandler(parser, processingInstruction);
985 XML_SetUnparsedEntityDeclHandler(parser, unparsedEntityDecl);
986 XML_SetUserData(parser, context.release());
987 } else {
988 jniThrowOutOfMemoryError(env, NULL);
989 return 0;
990 }
991
992 return (jint) parser;
993 }
994
995 /**
996 * Decodes the bytes as characters and parse the characters as XML. This
997 * performs character decoding using the charset specified at XML_Parser
998 * creation. For Java chars, that charset must be UTF-16 so that a Java char[]
999 * can be reinterpreted as a UTF-16 encoded byte[]. appendBytes, appendChars
1000 * and appendString all call through this method.
1001 */
append(JNIEnv * env,jobject object,jint pointer,const char * bytes,size_t byteOffset,size_t byteCount,jboolean isFinal)1002 static void append(JNIEnv* env, jobject object, jint pointer,
1003 const char* bytes, size_t byteOffset, size_t byteCount, jboolean isFinal) {
1004 XML_Parser parser = (XML_Parser) pointer;
1005 ParsingContext* context = toParsingContext(parser);
1006 context->env = env;
1007 context->object = object;
1008 if (!XML_Parse(parser, bytes + byteOffset, byteCount, isFinal) && !env->ExceptionCheck()) {
1009 jniThrowExpatException(env, XML_GetErrorCode(parser));
1010 }
1011 context->object = NULL;
1012 context->env = NULL;
1013 }
1014
ExpatParser_appendBytes(JNIEnv * env,jobject object,jint pointer,jbyteArray xml,jint byteOffset,jint byteCount)1015 static void ExpatParser_appendBytes(JNIEnv* env, jobject object, jint pointer,
1016 jbyteArray xml, jint byteOffset, jint byteCount) {
1017 ScopedByteArrayRO byteArray(env, xml);
1018 if (byteArray.get() == NULL) {
1019 return;
1020 }
1021
1022 const char* bytes = reinterpret_cast<const char*>(byteArray.get());
1023 append(env, object, pointer, bytes, byteOffset, byteCount, XML_FALSE);
1024 }
1025
ExpatParser_appendChars(JNIEnv * env,jobject object,jint pointer,jcharArray xml,jint charOffset,jint charCount)1026 static void ExpatParser_appendChars(JNIEnv* env, jobject object, jint pointer,
1027 jcharArray xml, jint charOffset, jint charCount) {
1028 ScopedCharArrayRO charArray(env, xml);
1029 if (charArray.get() == NULL) {
1030 return;
1031 }
1032
1033 const char* bytes = reinterpret_cast<const char*>(charArray.get());
1034 size_t byteOffset = 2 * charOffset;
1035 size_t byteCount = 2 * charCount;
1036 append(env, object, pointer, bytes, byteOffset, byteCount, XML_FALSE);
1037 }
1038
ExpatParser_appendString(JNIEnv * env,jobject object,jint pointer,jstring javaXml,jboolean isFinal)1039 static void ExpatParser_appendString(JNIEnv* env, jobject object, jint pointer, jstring javaXml, jboolean isFinal) {
1040 ScopedStringChars xml(env, javaXml);
1041 if (xml.get() == NULL) {
1042 return;
1043 }
1044 const char* bytes = reinterpret_cast<const char*>(xml.get());
1045 size_t byteCount = 2 * xml.size();
1046 append(env, object, pointer, bytes, 0, byteCount, isFinal);
1047 }
1048
1049 /**
1050 * Releases parser only.
1051 *
1052 * @param object the Java ExpatParser instance
1053 * @param i pointer to the C expat parser
1054 */
ExpatParser_releaseParser(JNIEnv *,jobject,jint i)1055 static void ExpatParser_releaseParser(JNIEnv*, jobject, jint i) {
1056 XML_Parser parser = (XML_Parser) i;
1057 XML_ParserFree(parser);
1058 }
1059
1060 /**
1061 * Cleans up after the parser. Called at garbage collection time.
1062 *
1063 * @param object the Java ExpatParser instance
1064 * @param i pointer to the C expat parser
1065 */
ExpatParser_release(JNIEnv * env,jobject,jint i)1066 static void ExpatParser_release(JNIEnv* env, jobject, jint i) {
1067 XML_Parser parser = (XML_Parser) i;
1068
1069 ParsingContext* context = toParsingContext(parser);
1070 context->env = env;
1071 delete context;
1072
1073 XML_ParserFree(parser);
1074 }
1075
1076 /**
1077 * Gets the current line.
1078 *
1079 * @param object the Java ExpatParser instance
1080 * @param pointer to the C expat parser
1081 * @returns current line number
1082 */
ExpatParser_line(JNIEnv *,jobject,jint pointer)1083 static int ExpatParser_line(JNIEnv*, jobject, jint pointer) {
1084 XML_Parser parser = (XML_Parser) pointer;
1085 return XML_GetCurrentLineNumber(parser);
1086 }
1087
1088 /**
1089 * Gets the current column.
1090 *
1091 * @param object the Java ExpatParser instance
1092 * @param pointer to the C expat parser
1093 * @returns current column number
1094 */
ExpatParser_column(JNIEnv *,jobject,jint pointer)1095 static int ExpatParser_column(JNIEnv*, jobject, jint pointer) {
1096 XML_Parser parser = (XML_Parser) pointer;
1097 return XML_GetCurrentColumnNumber(parser);
1098 }
1099
1100 /**
1101 * Gets the URI of the attribute at the given index.
1102 *
1103 * @param object Java ExpatParser instance
1104 * @param pointer to the C expat parser
1105 * @param attributePointer to the attribute array
1106 * @param index of the attribute
1107 * @returns interned Java string containing attribute's URI
1108 */
ExpatAttributes_getURI(JNIEnv * env,jobject,jint pointer,jint attributePointer,jint index)1109 static jstring ExpatAttributes_getURI(JNIEnv* env, jobject, jint pointer,
1110 jint attributePointer, jint index) {
1111 XML_Parser parser = (XML_Parser) pointer;
1112 ParsingContext* context = toParsingContext(parser);
1113 return ExpatElementName(env, context, attributePointer, index).uri();
1114 }
1115
1116 /**
1117 * Gets the local name of the attribute at the given index.
1118 *
1119 * @param object Java ExpatParser instance
1120 * @param pointer to the C expat parser
1121 * @param attributePointer to the attribute array
1122 * @param index of the attribute
1123 * @returns interned Java string containing attribute's local name
1124 */
ExpatAttributes_getLocalName(JNIEnv * env,jobject,jint pointer,jint attributePointer,jint index)1125 static jstring ExpatAttributes_getLocalName(JNIEnv* env, jobject, jint pointer,
1126 jint attributePointer, jint index) {
1127 XML_Parser parser = (XML_Parser) pointer;
1128 ParsingContext* context = toParsingContext(parser);
1129 return ExpatElementName(env, context, attributePointer, index).localName();
1130 }
1131
1132 /**
1133 * Gets the qualified name of the attribute at the given index.
1134 *
1135 * @param object Java ExpatParser instance
1136 * @param pointer to the C expat parser
1137 * @param attributePointer to the attribute array
1138 * @param index of the attribute
1139 * @returns interned Java string containing attribute's local name
1140 */
ExpatAttributes_getQName(JNIEnv * env,jobject,jint pointer,jint attributePointer,jint index)1141 static jstring ExpatAttributes_getQName(JNIEnv* env, jobject, jint pointer,
1142 jint attributePointer, jint index) {
1143 XML_Parser parser = (XML_Parser) pointer;
1144 ParsingContext* context = toParsingContext(parser);
1145 return ExpatElementName(env, context, attributePointer, index).qName();
1146 }
1147
1148 /**
1149 * Gets the value of the attribute at the given index.
1150 *
1151 * @param object Java ExpatParser instance
1152 * @param attributePointer to the attribute array
1153 * @param index of the attribute
1154 * @returns Java string containing attribute's value
1155 */
ExpatAttributes_getValueByIndex(JNIEnv * env,jobject,jint attributePointer,jint index)1156 static jstring ExpatAttributes_getValueByIndex(JNIEnv* env, jobject,
1157 jint attributePointer, jint index) {
1158 const char** attributes = toAttributes(attributePointer);
1159 const char* value = attributes[(index * 2) + 1];
1160 return env->NewStringUTF(value);
1161 }
1162
1163 /**
1164 * Gets the index of the attribute with the given qualified name.
1165 *
1166 * @param attributePointer to the attribute array
1167 * @param qName to look for
1168 * @returns index of attribute with the given uri and local name or -1 if not
1169 * found
1170 */
ExpatAttributes_getIndexForQName(JNIEnv * env,jobject,jint attributePointer,jstring qName)1171 static jint ExpatAttributes_getIndexForQName(JNIEnv* env, jobject,
1172 jint attributePointer, jstring qName) {
1173 ScopedUtfChars qNameBytes(env, qName);
1174 if (qNameBytes.c_str() == NULL) {
1175 return -1;
1176 }
1177
1178 const char** attributes = toAttributes(attributePointer);
1179 int found = -1;
1180 for (int index = 0; attributes[index * 2]; ++index) {
1181 if (ExpatElementName(NULL, NULL, attributePointer, index).matchesQName(qNameBytes.c_str())) {
1182 found = index;
1183 break;
1184 }
1185 }
1186
1187 return found;
1188 }
1189
1190 /**
1191 * Gets the index of the attribute with the given URI and name.
1192 *
1193 * @param attributePointer to the attribute array
1194 * @param uri to look for
1195 * @param localName to look for
1196 * @returns index of attribute with the given uri and local name or -1 if not
1197 * found
1198 */
ExpatAttributes_getIndex(JNIEnv * env,jobject,jint attributePointer,jstring uri,jstring localName)1199 static jint ExpatAttributes_getIndex(JNIEnv* env, jobject, jint attributePointer,
1200 jstring uri, jstring localName) {
1201 ScopedUtfChars uriBytes(env, uri);
1202 if (uriBytes.c_str() == NULL) {
1203 return -1;
1204 }
1205
1206 ScopedUtfChars localNameBytes(env, localName);
1207 if (localNameBytes.c_str() == NULL) {
1208 return -1;
1209 }
1210
1211 const char** attributes = toAttributes(attributePointer);
1212 for (int index = 0; attributes[index * 2]; ++index) {
1213 if (ExpatElementName(NULL, NULL, attributePointer, index).matches(uriBytes.c_str(),
1214 localNameBytes.c_str())) {
1215 return index;
1216 }
1217 }
1218 return -1;
1219 }
1220
1221 /**
1222 * Gets the value of the attribute with the given qualified name.
1223 *
1224 * @param attributePointer to the attribute array
1225 * @param uri to look for
1226 * @param localName to look for
1227 * @returns value of attribute with the given uri and local name or NULL if not
1228 * found
1229 */
ExpatAttributes_getValueForQName(JNIEnv * env,jobject clazz,jint attributePointer,jstring qName)1230 static jstring ExpatAttributes_getValueForQName(JNIEnv* env, jobject clazz,
1231 jint attributePointer, jstring qName) {
1232 jint index = ExpatAttributes_getIndexForQName(env, clazz, attributePointer, qName);
1233 return index == -1 ? NULL
1234 : ExpatAttributes_getValueByIndex(env, clazz, attributePointer, index);
1235 }
1236
1237 /**
1238 * Gets the value of the attribute with the given URI and name.
1239 *
1240 * @param attributePointer to the attribute array
1241 * @param uri to look for
1242 * @param localName to look for
1243 * @returns value of attribute with the given uri and local name or NULL if not
1244 * found
1245 */
ExpatAttributes_getValue(JNIEnv * env,jobject clazz,jint attributePointer,jstring uri,jstring localName)1246 static jstring ExpatAttributes_getValue(JNIEnv* env, jobject clazz,
1247 jint attributePointer, jstring uri, jstring localName) {
1248 jint index = ExpatAttributes_getIndex(env, clazz, attributePointer, uri, localName);
1249 return index == -1 ? NULL
1250 : ExpatAttributes_getValueByIndex(env, clazz, attributePointer, index);
1251 }
1252
1253 /**
1254 * Clones an array of strings. Uses one contiguous block of memory so as to
1255 * maximize performance.
1256 *
1257 * @param address char** to clone
1258 * @param count number of attributes
1259 */
ExpatParser_cloneAttributes(JNIEnv * env,jobject,jint address,jint count)1260 static jint ExpatParser_cloneAttributes(JNIEnv* env, jobject, jint address, jint count) {
1261 const char** source = reinterpret_cast<const char**>(static_cast<uintptr_t>(address));
1262 count *= 2;
1263
1264 // Figure out how big the buffer needs to be.
1265 int arraySize = (count + 1) * sizeof(char*);
1266 int totalSize = arraySize;
1267 int stringLengths[count];
1268 for (int i = 0; i < count; i++) {
1269 int length = strlen(source[i]);
1270 stringLengths[i] = length;
1271 totalSize += length + 1;
1272 }
1273
1274 char* buffer = new char[totalSize];
1275 if (buffer == NULL) {
1276 jniThrowOutOfMemoryError(env, NULL);
1277 return 0;
1278 }
1279
1280 // Array is at the beginning of the buffer.
1281 char** clonedArray = reinterpret_cast<char**>(buffer);
1282 clonedArray[count] = NULL; // null terminate
1283
1284 // String data follows immediately after.
1285 char* destinationString = buffer + arraySize;
1286 for (int i = 0; i < count; i++) {
1287 const char* sourceString = source[i];
1288 int stringLength = stringLengths[i];
1289 memcpy(destinationString, sourceString, stringLength + 1);
1290 clonedArray[i] = destinationString;
1291 destinationString += stringLength + 1;
1292 }
1293
1294 return static_cast<jint>(reinterpret_cast<uintptr_t>(buffer));
1295 }
1296
1297 /**
1298 * Frees cloned attributes.
1299 */
ExpatAttributes_freeAttributes(JNIEnv *,jobject,jint pointer)1300 static void ExpatAttributes_freeAttributes(JNIEnv*, jobject, jint pointer) {
1301 delete[] reinterpret_cast<char*>(static_cast<uintptr_t>(pointer));
1302 }
1303
1304 /**
1305 * Called when we initialize our Java parser class.
1306 *
1307 * @param clazz Java ExpatParser class
1308 */
ExpatParser_staticInitialize(JNIEnv * env,jobject classObject,jstring empty)1309 static void ExpatParser_staticInitialize(JNIEnv* env, jobject classObject, jstring empty) {
1310 jclass clazz = reinterpret_cast<jclass>(classObject);
1311 startElementMethod = env->GetMethodID(clazz, "startElement",
1312 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II)V");
1313 if (startElementMethod == NULL) return;
1314
1315 endElementMethod = env->GetMethodID(clazz, "endElement",
1316 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
1317 if (endElementMethod == NULL) return;
1318
1319 textMethod = env->GetMethodID(clazz, "text", "([CI)V");
1320 if (textMethod == NULL) return;
1321
1322 commentMethod = env->GetMethodID(clazz, "comment", "([CI)V");
1323 if (commentMethod == NULL) return;
1324
1325 startCdataMethod = env->GetMethodID(clazz, "startCdata", "()V");
1326 if (startCdataMethod == NULL) return;
1327
1328 endCdataMethod = env->GetMethodID(clazz, "endCdata", "()V");
1329 if (endCdataMethod == NULL) return;
1330
1331 startDtdMethod = env->GetMethodID(clazz, "startDtd",
1332 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
1333 if (startDtdMethod == NULL) return;
1334
1335 endDtdMethod = env->GetMethodID(clazz, "endDtd", "()V");
1336 if (endDtdMethod == NULL) return;
1337
1338 startNamespaceMethod = env->GetMethodID(clazz, "startNamespace",
1339 "(Ljava/lang/String;Ljava/lang/String;)V");
1340 if (startNamespaceMethod == NULL) return;
1341
1342 endNamespaceMethod = env->GetMethodID(clazz, "endNamespace",
1343 "(Ljava/lang/String;)V");
1344 if (endNamespaceMethod == NULL) return;
1345
1346 processingInstructionMethod = env->GetMethodID(clazz,
1347 "processingInstruction", "(Ljava/lang/String;Ljava/lang/String;)V");
1348 if (processingInstructionMethod == NULL) return;
1349
1350 handleExternalEntityMethod = env->GetMethodID(clazz,
1351 "handleExternalEntity",
1352 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
1353 if (handleExternalEntityMethod == NULL) return;
1354
1355 notationDeclMethod = env->GetMethodID(clazz, "notationDecl",
1356 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
1357 if (notationDeclMethod == NULL) return;
1358
1359 unparsedEntityDeclMethod = env->GetMethodID(clazz, "unparsedEntityDecl",
1360 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
1361 if (unparsedEntityDeclMethod == NULL) return;
1362
1363 internMethod = env->GetMethodID(JniConstants::stringClass, "intern", "()Ljava/lang/String;");
1364 if (internMethod == NULL) return;
1365
1366 // Reference to "".
1367 emptyString = (jstring) env->NewGlobalRef(empty);
1368 }
1369
1370 static JNINativeMethod parserMethods[] = {
1371 NATIVE_METHOD(ExpatParser, appendString, "(ILjava/lang/String;Z)V"),
1372 NATIVE_METHOD(ExpatParser, appendBytes, "(I[BII)V"),
1373 NATIVE_METHOD(ExpatParser, appendChars, "(I[CII)V"),
1374 NATIVE_METHOD(ExpatParser, cloneAttributes, "(II)I"),
1375 NATIVE_METHOD(ExpatParser, column, "(I)I"),
1376 NATIVE_METHOD(ExpatParser, createEntityParser, "(ILjava/lang/String;)I"),
1377 NATIVE_METHOD(ExpatParser, initialize, "(Ljava/lang/String;Z)I"),
1378 NATIVE_METHOD(ExpatParser, line, "(I)I"),
1379 NATIVE_METHOD(ExpatParser, release, "(I)V"),
1380 NATIVE_METHOD(ExpatParser, releaseParser, "(I)V"),
1381 NATIVE_METHOD(ExpatParser, staticInitialize, "(Ljava/lang/String;)V"),
1382 };
1383
1384 static JNINativeMethod attributeMethods[] = {
1385 NATIVE_METHOD(ExpatAttributes, freeAttributes, "(I)V"),
1386 NATIVE_METHOD(ExpatAttributes, getIndexForQName, "(ILjava/lang/String;)I"),
1387 NATIVE_METHOD(ExpatAttributes, getIndex, "(ILjava/lang/String;Ljava/lang/String;)I"),
1388 NATIVE_METHOD(ExpatAttributes, getLocalName, "(III)Ljava/lang/String;"),
1389 NATIVE_METHOD(ExpatAttributes, getQName, "(III)Ljava/lang/String;"),
1390 NATIVE_METHOD(ExpatAttributes, getURI, "(III)Ljava/lang/String;"),
1391 NATIVE_METHOD(ExpatAttributes, getValueByIndex, "(II)Ljava/lang/String;"),
1392 NATIVE_METHOD(ExpatAttributes, getValueForQName, "(ILjava/lang/String;)Ljava/lang/String;"),
1393 NATIVE_METHOD(ExpatAttributes, getValue, "(ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
1394 };
register_org_apache_harmony_xml_ExpatParser(JNIEnv * env)1395 void register_org_apache_harmony_xml_ExpatParser(JNIEnv* env) {
1396 jniRegisterNativeMethods(env, "org/apache/harmony/xml/ExpatParser", parserMethods, NELEM(parserMethods));
1397 jniRegisterNativeMethods(env, "org/apache/harmony/xml/ExpatAttributes", attributeMethods, NELEM(attributeMethods));
1398 }
1399