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 "LocalArray.h" 22 #include "ScopedJavaUnicodeString.h" 23 #include "ScopedLocalRef.h" 24 #include "ScopedPrimitiveArray.h" 25 #include "ScopedUtfChars.h" 26 #include "UniquePtr.h" 27 #include "jni.h" 28 #include "utils/Log.h" 29 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 37 static void throw_OutOfMemoryError(JNIEnv* env) { 38 jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory."); 39 } 40 41 /** 42 * Wrapper around an interned string. 43 */ 44 struct InternedString { 45 InternedString() : interned(NULL), bytes(NULL) { 46 } 47 48 ~InternedString() { 49 delete[] bytes; 50 } 51 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. */ 59 int hash; 60 }; 61 62 /** 63 * Keeps track of strings between start and end events. 64 */ 65 class StringStack { 66 public: 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 95 private: 96 enum { DEFAULT_CAPACITY = 10 }; 97 98 jstring* array; 99 int capacity; 100 int size; 101 }; 102 103 /** 104 * Data passed to parser handler method by the parser. 105 */ 106 struct ParsingContext { 107 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 } 112 113 // 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 155 private: 156 void freeBuffer() { 157 if (buffer != NULL) { 158 env->DeleteGlobalRef(buffer); 159 buffer = NULL; 160 bufferSize = -1; 161 } 162 } 163 164 public: 165 /** 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 177 private: 178 /** The size of our buffer in jchars. */ 179 int bufferSize; 180 181 public: 182 /** 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]; 196 }; 197 198 static ParsingContext* toParsingContext(void* data) { 199 return reinterpret_cast<ParsingContext*>(data); 200 } 201 202 static ParsingContext* toParsingContext(XML_Parser parser) { 203 return reinterpret_cast<ParsingContext*>(XML_GetUserData(parser)); 204 } 205 206 static jmethodID commentMethod; 207 static jmethodID endCdataMethod; 208 static jmethodID endDtdMethod; 209 static jmethodID endElementMethod; 210 static jmethodID endNamespaceMethod; 211 static jmethodID handleExternalEntityMethod; 212 static jmethodID internMethod; 213 static jmethodID notationDeclMethod; 214 static jmethodID processingInstructionMethod; 215 static jmethodID startCdataMethod; 216 static jmethodID startDtdMethod; 217 static jmethodID startElementMethod; 218 static jmethodID startNamespaceMethod; 219 static jmethodID textMethod; 220 static jmethodID unparsedEntityDeclMethod; 221 static jstring emptyString; 222 223 /** 224 * 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 */ 231 static 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 */ 249 static InternedString* newInternedString(JNIEnv* env, const char* bytes, int hash) { 250 // Allocate a new wrapper. 251 UniquePtr<InternedString> wrapper(new InternedString); 252 if (wrapper.get() == NULL) { 253 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. 259 char* copy = new char[strlen(bytes) + 1]; 260 if (copy == NULL) { 261 throw_OutOfMemoryError(env); 262 return NULL; 263 } 264 strcpy(copy, bytes); 265 wrapper->bytes = copy; 266 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. 272 ScopedLocalRef<jstring> newString(env, env->NewStringUTF(bytes)); 273 if (env->ExceptionCheck()) { 274 return NULL; 275 } 276 277 // Call intern(). 278 ScopedLocalRef<jstring> interned(env, 279 reinterpret_cast<jstring>(env->CallObjectMethod(newString.get(), internMethod))); 280 if (env->ExceptionCheck()) { 281 return NULL; 282 } 283 284 // Create a global reference to the interned string. 285 wrapper->interned = (jstring) env->NewGlobalRef(interned.get()); 286 if (env->ExceptionCheck()) { 287 return NULL; 288 } 289 290 return wrapper.release(); 291 } 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 */ 299 static InternedString** newInternedStringBucket(InternedString* entry) { 300 InternedString** bucket = new InternedString*[2]; 301 if (bucket != NULL) { 302 bucket[0] = entry; 303 bucket[1] = NULL; 304 } 305 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 */ 316 static 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. 324 InternedString** newBucket = new InternedString*[size + 2]; 325 if (newBucket == NULL) return NULL; 326 327 memcpy(newBucket, existingBucket, size * sizeof(InternedString*)); 328 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 */ 342 static jstring findInternedString(InternedString** bucket, const char* s, int hash) { 343 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 */ 357 static jstring internString(JNIEnv* env, ParsingContext* parsingContext, const char* s) { 358 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. 377 internedString = newInternedString(env, s, hash); 378 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. 392 internedString = newInternedString(env, s, hash); 393 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 408 static void jniThrowExpatException(JNIEnv* env, XML_Error error) { 409 const char* message = XML_ErrorString(error); 410 jniThrowException(env, "org/apache/harmony/xml/ExpatException", message); 411 } 412 413 /** 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 */ 421 static size_t fillBuffer(ParsingContext* parsingContext, const char* characters, int length) { 422 JNIEnv* env = parsingContext->env; 423 424 // Grow buffer if necessary. 425 jcharArray buffer = parsingContext->ensureCapacity(length); 426 if (buffer == NULL) return -1; 427 428 // Decode UTF-8 characters into our buffer. 429 ScopedCharArrayRW nativeBuffer(env, buffer); 430 if (nativeBuffer.get() == NULL) { 431 return -1; 432 } 433 434 size_t utf16length; 435 strcpylen8to16(nativeBuffer.get(), characters, length, &utf16length); 436 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 */ 448 static void bufferAndInvoke(jmethodID method, void* data, const char* text, size_t length) { 449 ParsingContext* parsingContext = toParsingContext(data); 450 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 464 static const char** toAttributes(jint attributePointer) { 465 return reinterpret_cast<const char**>(static_cast<uintptr_t>(attributePointer)); 466 } 467 468 /** 469 * The component parts of an attribute or element name. 470 */ 471 class ExpatElementName { 472 public: 473 ExpatElementName(JNIEnv* env, ParsingContext* parsingContext, jint attributePointer, jint index) { 474 const char** attributes = toAttributes(attributePointer); 475 const char* name = attributes[index * 2]; 476 init(env, parsingContext, name); 477 } 478 479 ExpatElementName(JNIEnv* env, ParsingContext* parsingContext, const char* s) { 480 init(env, parsingContext, s); 481 } 482 483 ~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 /** 496 * 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. 499 */ 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) { 529 const char* lastColon = strrchr(qName, ':'); 530 531 // 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; 539 } 540 541 // Otherwise compare both prefix and local name 542 size_t prefixLength = lastColon - qName; 543 return strlen(mPrefix) == prefixLength 544 && strncmp(qName, mPrefix, prefixLength) == 0 545 && strcmp(lastColon + 1, mLocalName) == 0; 546 } 547 548 private: 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 } 587 588 // Disallow copy and assignment. 589 ExpatElementName(const ExpatElementName&); 590 void operator=(const ExpatElementName&); 591 }; 592 593 /** 594 * 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 */ 602 static void startElement(void* data, const char* elementName, const char** attributes) { 603 ParsingContext* parsingContext = toParsingContext(data); 604 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 619 ExpatElementName e(env, parsingContext, elementName); 620 jstring uri = parsingContext->processNamespaces ? e.uri() : emptyString; 621 jstring localName = parsingContext->processNamespaces ? e.localName() : emptyString; 622 jstring qName = e.qName(); 623 624 parsingContext->stringStack.push(env, qName); 625 parsingContext->stringStack.push(env, uri); 626 parsingContext->stringStack.push(env, localName); 627 628 env->CallVoidMethod(javaParser, startElementMethod, uri, localName, qName, attributes, count); 629 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 639 * @param elementName "uri|localName" or "localName" for the current element; 640 * we assume that this matches the last data on our stack. 641 */ 642 static void endElement(void* data, const char* /*elementName*/) { 643 ParsingContext* parsingContext = toParsingContext(data); 644 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 651 jstring localName = parsingContext->stringStack.pop(); 652 jstring uri = parsingContext->stringStack.pop(); 653 jstring qName = parsingContext->stringStack.pop(); 654 655 env->CallVoidMethod(javaParser, endElementMethod, uri, localName, qName); 656 } 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 */ 667 static 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 */ 678 static 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 */ 689 static void startNamespace(void* data, const char* prefix, const char* uri) { 690 ParsingContext* parsingContext = toParsingContext(data); 691 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 708 parsingContext->stringStack.push(env, internedPrefix); 709 710 jobject javaParser = parsingContext->object; 711 env->CallVoidMethod(javaParser, startNamespaceMethod, internedPrefix, internedUri); 712 } 713 714 /** 715 * Called by Expat at the end of a namespace mapping. 716 * 717 * @param data parsing context 718 * @param prefix null-terminated namespace prefix used in the XML; 719 * we assume this is the same as the last prefix on the stack. 720 */ 721 static void endNamespace(void* data, const char* /*prefix*/) { 722 ParsingContext* parsingContext = toParsingContext(data); 723 JNIEnv* env = parsingContext->env; 724 725 // Bail out if a previously called handler threw an exception. 726 if (env->ExceptionCheck()) return; 727 728 jstring internedPrefix = parsingContext->stringStack.pop(); 729 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 */ 739 static void startCdata(void* data) { 740 ParsingContext* parsingContext = toParsingContext(data); 741 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 */ 755 static void endCdata(void* data) { 756 ParsingContext* parsingContext = toParsingContext(data); 757 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. 768 * Expat gives us 'hasInternalSubset', but the Java API doesn't expect it, so we don't need it. 769 */ 770 static void startDtd(void* data, const char* name, 771 const char* systemId, const char* publicId, int /*hasInternalSubset*/) { 772 ParsingContext* parsingContext = toParsingContext(data); 773 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 */ 797 static void endDtd(void* data) { 798 ParsingContext* parsingContext = toParsingContext(data); 799 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 */ 815 static void processingInstruction(void* data, const char* target, const char* instructionData) { 816 ParsingContext* parsingContext = toParsingContext(data); 817 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 825 ScopedLocalRef<jstring> javaInstructionData(env, env->NewStringUTF(instructionData)); 826 if (env->ExceptionCheck()) return; 827 828 jobject javaParser = parsingContext->object; 829 env->CallVoidMethod(javaParser, processingInstructionMethod, javaTarget, javaInstructionData.get()); 830 } 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 */ 841 static jint ExpatParser_createEntityParser(JNIEnv* env, jobject, jint parentParser, jstring javaContext) { 842 ScopedUtfChars context(env, javaContext); 843 if (context.c_str() == NULL) { 844 return 0; 845 } 846 847 XML_Parser parent = (XML_Parser) parentParser; 848 XML_Parser entityParser = XML_ExternalEntityParserCreate(parent, context.c_str(), NULL); 849 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 */ 860 static int handleExternalEntity(XML_Parser parser, const char* context, 861 const char*, const char* systemId, const char* publicId) { 862 ParsingContext* parsingContext = toParsingContext(parser); 863 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 872 ScopedLocalRef<jstring> javaSystemId(env, env->NewStringUTF(systemId)); 873 if (env->ExceptionCheck()) { 874 return XML_STATUS_ERROR; 875 } 876 ScopedLocalRef<jstring> javaPublicId(env, env->NewStringUTF(publicId)); 877 if (env->ExceptionCheck()) { 878 return XML_STATUS_ERROR; 879 } 880 ScopedLocalRef<jstring> javaContext(env, env->NewStringUTF(context)); 881 if (env->ExceptionCheck()) { 882 return XML_STATUS_ERROR; 883 } 884 885 // Pass the wrapped parser and both strings to java. 886 env->CallVoidMethod(javaParser, handleExternalEntityMethod, javaContext.get(), 887 javaPublicId.get(), javaSystemId.get()); 888 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 899 return env->ExceptionCheck() ? XML_STATUS_ERROR : XML_STATUS_OK; 900 } 901 902 /** 903 * Expat gives us 'base', but the Java API doesn't expect it, so we don't need it. 904 */ 905 static void unparsedEntityDecl(void* data, const char* name, const char* /*base*/, const char* systemId, const char* publicId, const char* notationName) { 906 ParsingContext* parsingContext = toParsingContext(data); 907 jobject javaParser = parsingContext->object; 908 JNIEnv* env = parsingContext->env; 909 910 // Bail out if a previously called handler threw an exception. 911 if (env->ExceptionCheck()) return; 912 913 ScopedLocalRef<jstring> javaName(env, env->NewStringUTF(name)); 914 if (env->ExceptionCheck()) return; 915 ScopedLocalRef<jstring> javaPublicId(env, env->NewStringUTF(publicId)); 916 if (env->ExceptionCheck()) return; 917 ScopedLocalRef<jstring> javaSystemId(env, env->NewStringUTF(systemId)); 918 if (env->ExceptionCheck()) return; 919 ScopedLocalRef<jstring> javaNotationName(env, env->NewStringUTF(notationName)); 920 if (env->ExceptionCheck()) return; 921 922 env->CallVoidMethod(javaParser, unparsedEntityDeclMethod, javaName.get(), javaPublicId.get(), javaSystemId.get(), javaNotationName.get()); 923 } 924 925 /** 926 * Expat gives us 'base', but the Java API doesn't expect it, so we don't need it. 927 */ 928 static void notationDecl(void* data, const char* name, const char* /*base*/, const char* systemId, const char* publicId) { 929 ParsingContext* parsingContext = toParsingContext(data); 930 jobject javaParser = parsingContext->object; 931 JNIEnv* env = parsingContext->env; 932 933 // Bail out if a previously called handler threw an exception. 934 if (env->ExceptionCheck()) return; 935 936 ScopedLocalRef<jstring> javaName(env, env->NewStringUTF(name)); 937 if (env->ExceptionCheck()) return; 938 ScopedLocalRef<jstring> javaPublicId(env, env->NewStringUTF(publicId)); 939 if (env->ExceptionCheck()) return; 940 ScopedLocalRef<jstring> javaSystemId(env, env->NewStringUTF(systemId)); 941 if (env->ExceptionCheck()) return; 942 943 env->CallVoidMethod(javaParser, notationDeclMethod, javaName.get(), javaPublicId.get(), javaSystemId.get()); 944 } 945 946 /** 947 * Creates a new Expat parser. Called from the Java ExpatParser constructor. 948 * 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 */ 954 static jint ExpatParser_initialize(JNIEnv* env, jobject object, jstring javaEncoding, 955 jboolean processNamespaces) { 956 // Allocate parsing context. 957 UniquePtr<ParsingContext> context(new ParsingContext(object)); 958 if (context.get() == NULL) { 959 throw_OutOfMemoryError(env); 960 return 0; 961 } 962 963 context->processNamespaces = (bool) processNamespaces; 964 965 // Create a parser. 966 XML_Parser parser; 967 ScopedUtfChars encoding(env, javaEncoding); 968 if (encoding.c_str() == NULL) { 969 return 0; 970 } 971 if (processNamespaces) { 972 // Use '|' to separate URIs from local names. 973 parser = XML_ParserCreateNS(encoding.c_str(), '|'); 974 } else { 975 parser = XML_ParserCreate(encoding.c_str()); 976 } 977 978 if (parser != NULL) { 979 if (processNamespaces) { 980 XML_SetNamespaceDeclHandler(parser, startNamespace, endNamespace); 981 XML_SetReturnNSTriplet(parser, 1); 982 } 983 984 XML_SetCdataSectionHandler(parser, startCdata, endCdata); 985 XML_SetCharacterDataHandler(parser, text); 986 XML_SetCommentHandler(parser, comment); 987 XML_SetDoctypeDeclHandler(parser, startDtd, endDtd); 988 XML_SetElementHandler(parser, startElement, endElement); 989 XML_SetExternalEntityRefHandler(parser, handleExternalEntity); 990 XML_SetNotationDeclHandler(parser, notationDecl); 991 XML_SetProcessingInstructionHandler(parser, processingInstruction); 992 XML_SetUnparsedEntityDeclHandler(parser, unparsedEntityDecl); 993 XML_SetUserData(parser, context.release()); 994 } else { 995 throw_OutOfMemoryError(env); 996 return 0; 997 } 998 999 return (jint) parser; 1000 } 1001 1002 /** 1003 * 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. 1006 */ 1007 static void append(JNIEnv* env, jobject object, jint pointer, 1008 const char* bytes, size_t byteOffset, size_t byteCount, jboolean isFinal) { 1009 XML_Parser parser = (XML_Parser) pointer; 1010 ParsingContext* context = toParsingContext(parser); 1011 context->env = env; 1012 context->object = object; 1013 if (!XML_Parse(parser, bytes + byteOffset, byteCount, isFinal) && !env->ExceptionCheck()) { 1014 jniThrowExpatException(env, XML_GetErrorCode(parser)); 1015 } 1016 context->object = NULL; 1017 context->env = NULL; 1018 } 1019 1020 static void ExpatParser_appendBytes(JNIEnv* env, jobject object, jint pointer, 1021 jbyteArray xml, jint byteOffset, jint byteCount) { 1022 ScopedByteArrayRO byteArray(env, xml); 1023 if (byteArray.get() == NULL) { 1024 return; 1025 } 1026 1027 const char* bytes = reinterpret_cast<const char*>(byteArray.get()); 1028 append(env, object, pointer, bytes, byteOffset, byteCount, XML_FALSE); 1029 } 1030 1031 static void ExpatParser_appendChars(JNIEnv* env, jobject object, jint pointer, 1032 jcharArray xml, jint charOffset, jint charCount) { 1033 ScopedCharArrayRO charArray(env, xml); 1034 if (charArray.get() == NULL) { 1035 return; 1036 } 1037 1038 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 } 1043 1044 static void ExpatParser_appendString(JNIEnv* env, jobject object, jint pointer, 1045 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(); 1049 append(env, object, pointer, bytes, 0, byteCount, isFinal); 1050 } 1051 1052 /** 1053 * Releases parser only. 1054 * 1055 * @param object the Java ExpatParser instance 1056 * @param i pointer to the C expat parser 1057 */ 1058 static void ExpatParser_releaseParser(JNIEnv*, jobject, jint i) { 1059 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 */ 1069 static void ExpatParser_release(JNIEnv* env, jobject, jint i) { 1070 XML_Parser parser = (XML_Parser) i; 1071 1072 ParsingContext* context = toParsingContext(parser); 1073 context->env = env; 1074 delete context; 1075 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 */ 1086 static int ExpatParser_line(JNIEnv*, jobject, jint pointer) { 1087 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 */ 1098 static int ExpatParser_column(JNIEnv*, jobject, jint pointer) { 1099 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 */ 1112 static jstring ExpatAttributes_getURI(JNIEnv* env, jobject, jint pointer, 1113 jint attributePointer, jint index) { 1114 XML_Parser parser = (XML_Parser) pointer; 1115 ParsingContext* context = toParsingContext(parser); 1116 return ExpatElementName(env, context, attributePointer, index).uri(); 1117 } 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 */ 1128 static jstring ExpatAttributes_getLocalName(JNIEnv* env, jobject, jint pointer, 1129 jint attributePointer, jint index) { 1130 XML_Parser parser = (XML_Parser) pointer; 1131 ParsingContext* context = toParsingContext(parser); 1132 return ExpatElementName(env, context, attributePointer, index).localName(); 1133 } 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 */ 1144 static jstring ExpatAttributes_getQName(JNIEnv* env, jobject, jint pointer, 1145 jint attributePointer, jint index) { 1146 XML_Parser parser = (XML_Parser) pointer; 1147 ParsingContext* context = toParsingContext(parser); 1148 return ExpatElementName(env, context, attributePointer, index).qName(); 1149 } 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 */ 1159 static jstring ExpatAttributes_getValueByIndex(JNIEnv* env, jobject, 1160 jint attributePointer, jint index) { 1161 const char** attributes = toAttributes(attributePointer); 1162 const char* value = attributes[(index << 1) + 1]; 1163 return env->NewStringUTF(value); 1164 } 1165 1166 /** 1167 * 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 */ 1174 static jint ExpatAttributes_getIndexForQName(JNIEnv* env, jobject, 1175 jint attributePointer, jstring qName) { 1176 ScopedUtfChars qNameBytes(env, qName); 1177 if (qNameBytes.c_str() == NULL) { 1178 return -1; 1179 } 1180 1181 const char** attributes = toAttributes(attributePointer); 1182 int found = -1; 1183 for (int index = 0; attributes[index * 2]; ++index) { 1184 if (ExpatElementName(NULL, NULL, attributePointer, index).matchesQName(qNameBytes.c_str())) { 1185 found = index; 1186 break; 1187 } 1188 } 1189 1190 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 */ 1202 static jint ExpatAttributes_getIndex(JNIEnv* env, jobject, jint attributePointer, 1203 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 1214 const char** attributes = toAttributes(attributePointer); 1215 for (int index = 0; attributes[index * 2]; ++index) { 1216 if (ExpatElementName(NULL, NULL, attributePointer, index).matches(uriBytes.c_str(), 1217 localNameBytes.c_str())) { 1218 return index; 1219 } 1220 } 1221 return -1; 1222 } 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 */ 1233 static jstring ExpatAttributes_getValueForQName(JNIEnv* env, jobject clazz, 1234 jint attributePointer, jstring qName) { 1235 jint index = ExpatAttributes_getIndexForQName(env, clazz, attributePointer, qName); 1236 return index == -1 ? NULL 1237 : ExpatAttributes_getValueByIndex(env, clazz, attributePointer, index); 1238 } 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 */ 1249 static jstring ExpatAttributes_getValue(JNIEnv* env, jobject clazz, 1250 jint attributePointer, jstring uri, jstring localName) { 1251 jint index = ExpatAttributes_getIndex(env, clazz, attributePointer, uri, localName); 1252 return index == -1 ? NULL 1253 : ExpatAttributes_getValueByIndex(env, clazz, attributePointer, index); 1254 } 1255 1256 /** 1257 * Clones an array of strings. Uses one contiguous block of memory so as to 1258 * maximize performance. 1259 * 1260 * @param address char** to clone 1261 * @param count number of attributes 1262 */ 1263 static jint ExpatParser_cloneAttributes(JNIEnv* env, jobject, jint address, jint count) { 1264 const char** source = reinterpret_cast<const char**>(static_cast<uintptr_t>(address)); 1265 count <<= 1; 1266 1267 // 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 1277 char* buffer = new char[totalSize]; 1278 if (buffer == NULL) { 1279 throw_OutOfMemoryError(env); 1280 return NULL; 1281 } 1282 1283 // Array is at the beginning of the buffer. 1284 char** clonedArray = reinterpret_cast<char**>(buffer); 1285 clonedArray[count] = NULL; // null terminate 1286 1287 // String data follows immediately after. 1288 char* destinationString = buffer + arraySize; 1289 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 1297 return static_cast<jint>(reinterpret_cast<uintptr_t>(buffer)); 1298 } 1299 1300 /** 1301 * Frees cloned attributes. 1302 */ 1303 static void ExpatAttributes_freeAttributes(JNIEnv*, jobject, jint pointer) { 1304 delete[] reinterpret_cast<char*>(static_cast<uintptr_t>(pointer)); 1305 } 1306 1307 /** 1308 * Called when we initialize our Java parser class. 1309 * 1310 * @param clazz Java ExpatParser class 1311 */ 1312 static void ExpatParser_staticInitialize(JNIEnv* env, jobject classObject, jstring empty) { 1313 jclass clazz = reinterpret_cast<jclass>(classObject); 1314 startElementMethod = env->GetMethodID(clazz, "startElement", 1315 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II)V"); 1316 if (startElementMethod == NULL) return; 1317 1318 endElementMethod = env->GetMethodID(clazz, "endElement", 1319 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); 1320 if (endElementMethod == NULL) return; 1321 1322 textMethod = env->GetMethodID(clazz, "text", "([CI)V"); 1323 if (textMethod == NULL) return; 1324 1325 commentMethod = env->GetMethodID(clazz, "comment", "([CI)V"); 1326 if (commentMethod == NULL) return; 1327 1328 startCdataMethod = env->GetMethodID(clazz, "startCdata", "()V"); 1329 if (startCdataMethod == NULL) return; 1330 1331 endCdataMethod = env->GetMethodID(clazz, "endCdata", "()V"); 1332 if (endCdataMethod == NULL) return; 1333 1334 startDtdMethod = env->GetMethodID(clazz, "startDtd", 1335 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); 1336 if (startDtdMethod == NULL) return; 1337 1338 endDtdMethod = env->GetMethodID(clazz, "endDtd", "()V"); 1339 if (endDtdMethod == NULL) return; 1340 1341 startNamespaceMethod = env->GetMethodID(clazz, "startNamespace", 1342 "(Ljava/lang/String;Ljava/lang/String;)V"); 1343 if (startNamespaceMethod == NULL) return; 1344 1345 endNamespaceMethod = env->GetMethodID(clazz, "endNamespace", 1346 "(Ljava/lang/String;)V"); 1347 if (endNamespaceMethod == NULL) return; 1348 1349 processingInstructionMethod = env->GetMethodID(clazz, 1350 "processingInstruction", "(Ljava/lang/String;Ljava/lang/String;)V"); 1351 if (processingInstructionMethod == NULL) return; 1352 1353 handleExternalEntityMethod = env->GetMethodID(clazz, 1354 "handleExternalEntity", 1355 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); 1356 if (handleExternalEntityMethod == NULL) return; 1357 1358 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 1366 internMethod = env->GetMethodID(JniConstants::stringClass, "intern", "()Ljava/lang/String;"); 1367 if (internMethod == NULL) return; 1368 1369 // Reference to "". 1370 emptyString = (jstring) env->NewGlobalRef(empty); 1371 } 1372 1373 static JNINativeMethod parserMethods[] = { 1374 NATIVE_METHOD(ExpatParser, appendString, "(ILjava/lang/String;Z)V"), 1375 NATIVE_METHOD(ExpatParser, appendBytes, "(I[BII)V"), 1376 NATIVE_METHOD(ExpatParser, appendChars, "(I[CII)V"), 1377 NATIVE_METHOD(ExpatParser, cloneAttributes, "(II)I"), 1378 NATIVE_METHOD(ExpatParser, column, "(I)I"), 1379 NATIVE_METHOD(ExpatParser, createEntityParser, "(ILjava/lang/String;)I"), 1380 NATIVE_METHOD(ExpatParser, initialize, "(Ljava/lang/String;Z)I"), 1381 NATIVE_METHOD(ExpatParser, line, "(I)I"), 1382 NATIVE_METHOD(ExpatParser, release, "(I)V"), 1383 NATIVE_METHOD(ExpatParser, releaseParser, "(I)V"), 1384 NATIVE_METHOD(ExpatParser, staticInitialize, "(Ljava/lang/String;)V"), 1385 }; 1386 1387 static JNINativeMethod attributeMethods[] = { 1388 NATIVE_METHOD(ExpatAttributes, freeAttributes, "(I)V"), 1389 NATIVE_METHOD(ExpatAttributes, getIndexForQName, "(ILjava/lang/String;)I"), 1390 NATIVE_METHOD(ExpatAttributes, getIndex, "(ILjava/lang/String;Ljava/lang/String;)I"), 1391 NATIVE_METHOD(ExpatAttributes, getLocalName, "(III)Ljava/lang/String;"), 1392 NATIVE_METHOD(ExpatAttributes, getQName, "(III)Ljava/lang/String;"), 1393 NATIVE_METHOD(ExpatAttributes, getURI, "(III)Ljava/lang/String;"), 1394 NATIVE_METHOD(ExpatAttributes, getValueByIndex, "(II)Ljava/lang/String;"), 1395 NATIVE_METHOD(ExpatAttributes, getValueForQName, "(ILjava/lang/String;)Ljava/lang/String;"), 1396 NATIVE_METHOD(ExpatAttributes, getValue, "(ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;"), 1397 }; 1398 int register_org_apache_harmony_xml_ExpatParser(JNIEnv* env) { 1399 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 } 1408