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 "cutils/jstring.h" // for strcpylen8to16 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 { 41 InternedString() : interned(NULL), bytes(NULL) { 42 } 43 44 ~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: 63 StringStack() : array(new jstring[DEFAULT_CAPACITY]), capacity(DEFAULT_CAPACITY), size(0) { 64 } 65 66 ~StringStack() { 67 delete[] array; 68 } 69 70 void push(JNIEnv* env, jstring s) { 71 if (size == capacity) { 72 int newCapacity = capacity * 2; 73 jstring* newArray = new jstring[newCapacity]; 74 if (newArray == NULL) { 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 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 { 103 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. 110 ~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 132 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: 152 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 194 static ParsingContext* toParsingContext(void* data) { 195 return reinterpret_cast<ParsingContext*>(data); 196 } 197 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 * @param characters to copy into the buffer 414 * @param length of characters to copy (in bytes) 415 * @returns number of UTF-16 characters which were copied 416 */ 417 static size_t fillBuffer(ParsingContext* parsingContext, const char* characters, int length) { 418 JNIEnv* env = parsingContext->env; 419 420 // Grow buffer if necessary. 421 jcharArray buffer = parsingContext->ensureCapacity(length); 422 if (buffer == NULL) return -1; 423 424 // Decode UTF-8 characters into our buffer. 425 ScopedCharArrayRW nativeBuffer(env, buffer); 426 if (nativeBuffer.get() == NULL) { 427 return -1; 428 } 429 430 size_t utf16length; 431 strcpylen8to16(nativeBuffer.get(), characters, length, &utf16length); 432 return utf16length; 433 } 434 435 /** 436 * Buffers the given text and passes it to the given method. 437 * 438 * @param method to pass the characters and length to with signature 439 * (char[], int) 440 * @param data parsing context 441 * @param text to copy into the buffer 442 * @param length of text to copy (in bytes) 443 */ 444 static void bufferAndInvoke(jmethodID method, void* data, const char* text, size_t length) { 445 ParsingContext* parsingContext = toParsingContext(data); 446 JNIEnv* env = parsingContext->env; 447 448 // Bail out if a previously called handler threw an exception. 449 if (env->ExceptionCheck()) return; 450 451 // Buffer the element name. 452 size_t utf16length = fillBuffer(parsingContext, text, length); 453 454 // Invoke given method. 455 jobject javaParser = parsingContext->object; 456 jcharArray buffer = parsingContext->buffer; 457 env->CallVoidMethod(javaParser, method, buffer, utf16length); 458 } 459 460 static const char** toAttributes(jint attributePointer) { 461 return reinterpret_cast<const char**>(static_cast<uintptr_t>(attributePointer)); 462 } 463 464 /** 465 * The component parts of an attribute or element name. 466 */ 467 class ExpatElementName { 468 public: 469 ExpatElementName(JNIEnv* env, ParsingContext* parsingContext, jint attributePointer, jint index) { 470 const char** attributes = toAttributes(attributePointer); 471 const char* name = attributes[index * 2]; 472 init(env, parsingContext, name); 473 } 474 475 ExpatElementName(JNIEnv* env, ParsingContext* parsingContext, const char* s) { 476 init(env, parsingContext, s); 477 } 478 479 ~ExpatElementName() { 480 free(mCopy); 481 } 482 483 /** 484 * Returns the namespace URI, like "http://www.w3.org/1999/xhtml". 485 * Possibly empty. 486 */ 487 jstring uri() { 488 return internString(mEnv, mParsingContext, mUri); 489 } 490 491 /** 492 * Returns the element or attribute local name, like "h1". Never empty. When 493 * namespace processing is disabled, this may contain a prefix, yielding a 494 * local name like "html:h1". In such cases, the qName will always be empty. 495 */ 496 jstring localName() { 497 return internString(mEnv, mParsingContext, mLocalName); 498 } 499 500 /** 501 * Returns the namespace prefix, like "html". Possibly empty. 502 */ 503 jstring qName() { 504 if (*mPrefix == 0) { 505 return localName(); 506 } 507 508 // return prefix + ":" + localName 509 LocalArray<1024> qName(strlen(mPrefix) + 1 + strlen(mLocalName) + 1); 510 snprintf(&qName[0], qName.size(), "%s:%s", mPrefix, mLocalName); 511 return internString(mEnv, mParsingContext, &qName[0]); 512 } 513 514 /** 515 * Returns true if this expat name has the same URI and local name. 516 */ 517 bool matches(const char* uri, const char* localName) { 518 return strcmp(uri, mUri) == 0 && strcmp(localName, mLocalName) == 0; 519 } 520 521 /** 522 * Returns true if this expat name has the same qualified name. 523 */ 524 bool matchesQName(const char* qName) { 525 const char* lastColon = strrchr(qName, ':'); 526 527 // Compare local names only if either: 528 // - the input qualified name doesn't have a colon (like "h1") 529 // - this element doesn't have a prefix. Such is the case when it 530 // doesn't belong to a namespace, or when this parser's namespace 531 // processing is disabled. In the latter case, this element's local 532 // name may still contain a colon (like "html:h1"). 533 if (lastColon == NULL || *mPrefix == 0) { 534 return strcmp(qName, mLocalName) == 0; 535 } 536 537 // Otherwise compare both prefix and local name 538 size_t prefixLength = lastColon - qName; 539 return strlen(mPrefix) == prefixLength 540 && strncmp(qName, mPrefix, prefixLength) == 0 541 && strcmp(lastColon + 1, mLocalName) == 0; 542 } 543 544 private: 545 JNIEnv* mEnv; 546 ParsingContext* mParsingContext; 547 char* mCopy; 548 const char* mUri; 549 const char* mLocalName; 550 const char* mPrefix; 551 552 /** 553 * Decodes an Expat-encoded name of one of these three forms: 554 * "uri|localName|prefix" (example: "http://www.w3.org/1999/xhtml|h1|html") 555 * "uri|localName" (example: "http://www.w3.org/1999/xhtml|h1") 556 * "localName" (example: "h1") 557 */ 558 void init(JNIEnv* env, ParsingContext* parsingContext, const char* s) { 559 mEnv = env; 560 mParsingContext = parsingContext; 561 mCopy = strdup(s); 562 563 // split the input into up to 3 parts: a|b|c 564 char* context = NULL; 565 char* a = strtok_r(mCopy, "|", &context); 566 char* b = strtok_r(NULL, "|", &context); 567 char* c = strtok_r(NULL, "|", &context); 568 569 if (c != NULL) { // input of the form "uri|localName|prefix" 570 mUri = a; 571 mLocalName = b; 572 mPrefix = c; 573 } else if (b != NULL) { // input of the form "uri|localName" 574 mUri = a; 575 mLocalName = b; 576 mPrefix = ""; 577 } else { // input of the form "localName" 578 mLocalName = a; 579 mUri = ""; 580 mPrefix = ""; 581 } 582 } 583 584 // Disallow copy and assignment. 585 ExpatElementName(const ExpatElementName&); 586 void operator=(const ExpatElementName&); 587 }; 588 589 /** 590 * Called by Expat at the start of an element. Delegates to the same method 591 * on the Java parser. 592 * 593 * @param data parsing context 594 * @param elementName "uri|localName" or "localName" for the current element 595 * @param attributes alternating attribute names and values. Like element 596 * names, attribute names follow the format "uri|localName" or "localName". 597 */ 598 static void startElement(void* data, const char* elementName, const char** attributes) { 599 ParsingContext* parsingContext = toParsingContext(data); 600 JNIEnv* env = parsingContext->env; 601 602 // Bail out if a previously called handler threw an exception. 603 if (env->ExceptionCheck()) return; 604 605 // Count the number of attributes. 606 int count = 0; 607 while (attributes[count * 2]) count++; 608 609 // Make the attributes available for the duration of this call. 610 parsingContext->attributes = attributes; 611 parsingContext->attributeCount = count; 612 613 jobject javaParser = parsingContext->object; 614 615 ExpatElementName e(env, parsingContext, elementName); 616 jstring uri = parsingContext->processNamespaces ? e.uri() : emptyString; 617 jstring localName = parsingContext->processNamespaces ? e.localName() : emptyString; 618 jstring qName = e.qName(); 619 620 parsingContext->stringStack.push(env, qName); 621 parsingContext->stringStack.push(env, uri); 622 parsingContext->stringStack.push(env, localName); 623 624 env->CallVoidMethod(javaParser, startElementMethod, uri, localName, qName, attributes, count); 625 626 parsingContext->attributes = NULL; 627 parsingContext->attributeCount = -1; 628 } 629 630 /** 631 * Called by Expat at the end of an element. Delegates to the same method 632 * on the Java parser. 633 * 634 * @param data parsing context 635 * @param elementName "uri|localName" or "localName" for the current element; 636 * we assume that this matches the last data on our stack. 637 */ 638 static void endElement(void* data, const char* /*elementName*/) { 639 ParsingContext* parsingContext = toParsingContext(data); 640 JNIEnv* env = parsingContext->env; 641 642 // Bail out if a previously called handler threw an exception. 643 if (env->ExceptionCheck()) return; 644 645 jobject javaParser = parsingContext->object; 646 647 jstring localName = parsingContext->stringStack.pop(); 648 jstring uri = parsingContext->stringStack.pop(); 649 jstring qName = parsingContext->stringStack.pop(); 650 651 env->CallVoidMethod(javaParser, endElementMethod, uri, localName, qName); 652 } 653 654 /** 655 * Called by Expat when it encounters text. Delegates to the same method 656 * on the Java parser. This may be called mutiple times with incremental pieces 657 * of the same contiguous block of text. 658 * 659 * @param data parsing context 660 * @param characters buffer containing encountered text 661 * @param length number of characters in the buffer 662 */ 663 static void text(void* data, const char* characters, int length) { 664 bufferAndInvoke(textMethod, data, characters, length); 665 } 666 667 /** 668 * Called by Expat when it encounters a comment. Delegates to the same method 669 * on the Java parser. 670 671 * @param data parsing context 672 * @param comment 0-terminated 673 */ 674 static void comment(void* data, const char* comment) { 675 bufferAndInvoke(commentMethod, data, comment, strlen(comment)); 676 } 677 678 /** 679 * Called by Expat at the beginning of a namespace mapping. 680 * 681 * @param data parsing context 682 * @param prefix null-terminated namespace prefix used in the XML 683 * @param uri of the namespace 684 */ 685 static void startNamespace(void* data, const char* prefix, const char* uri) { 686 ParsingContext* parsingContext = toParsingContext(data); 687 JNIEnv* env = parsingContext->env; 688 689 // Bail out if a previously called handler threw an exception. 690 if (env->ExceptionCheck()) return; 691 692 jstring internedPrefix = emptyString; 693 if (prefix != NULL) { 694 internedPrefix = internString(env, parsingContext, prefix); 695 if (env->ExceptionCheck()) return; 696 } 697 698 jstring internedUri = emptyString; 699 if (uri != NULL) { 700 internedUri = internString(env, parsingContext, uri); 701 if (env->ExceptionCheck()) return; 702 } 703 704 parsingContext->stringStack.push(env, internedPrefix); 705 706 jobject javaParser = parsingContext->object; 707 env->CallVoidMethod(javaParser, startNamespaceMethod, internedPrefix, internedUri); 708 } 709 710 /** 711 * Called by Expat at the end of a namespace mapping. 712 * 713 * @param data parsing context 714 * @param prefix null-terminated namespace prefix used in the XML; 715 * we assume this is the same as the last prefix on the stack. 716 */ 717 static void endNamespace(void* data, const char* /*prefix*/) { 718 ParsingContext* parsingContext = toParsingContext(data); 719 JNIEnv* env = parsingContext->env; 720 721 // Bail out if a previously called handler threw an exception. 722 if (env->ExceptionCheck()) return; 723 724 jstring internedPrefix = parsingContext->stringStack.pop(); 725 726 jobject javaParser = parsingContext->object; 727 env->CallVoidMethod(javaParser, endNamespaceMethod, internedPrefix); 728 } 729 730 /** 731 * Called by Expat at the beginning of a CDATA section. 732 * 733 * @param data parsing context 734 */ 735 static void startCdata(void* data) { 736 ParsingContext* parsingContext = toParsingContext(data); 737 JNIEnv* env = parsingContext->env; 738 739 // Bail out if a previously called handler threw an exception. 740 if (env->ExceptionCheck()) return; 741 742 jobject javaParser = parsingContext->object; 743 env->CallVoidMethod(javaParser, startCdataMethod); 744 } 745 746 /** 747 * Called by Expat at the end of a CDATA section. 748 * 749 * @param data parsing context 750 */ 751 static void endCdata(void* data) { 752 ParsingContext* parsingContext = toParsingContext(data); 753 JNIEnv* env = parsingContext->env; 754 755 // Bail out if a previously called handler threw an exception. 756 if (env->ExceptionCheck()) return; 757 758 jobject javaParser = parsingContext->object; 759 env->CallVoidMethod(javaParser, endCdataMethod); 760 } 761 762 /** 763 * Called by Expat at the beginning of a DOCTYPE section. 764 * Expat gives us 'hasInternalSubset', but the Java API doesn't expect it, so we don't need it. 765 */ 766 static void startDtd(void* data, const char* name, 767 const char* systemId, const char* publicId, int /*hasInternalSubset*/) { 768 ParsingContext* parsingContext = toParsingContext(data); 769 JNIEnv* env = parsingContext->env; 770 771 // Bail out if a previously called handler threw an exception. 772 if (env->ExceptionCheck()) return; 773 774 jstring javaName = internString(env, parsingContext, name); 775 if (env->ExceptionCheck()) return; 776 777 jstring javaPublicId = internString(env, parsingContext, publicId); 778 if (env->ExceptionCheck()) return; 779 780 jstring javaSystemId = internString(env, parsingContext, systemId); 781 if (env->ExceptionCheck()) return; 782 783 jobject javaParser = parsingContext->object; 784 env->CallVoidMethod(javaParser, startDtdMethod, javaName, javaPublicId, 785 javaSystemId); 786 } 787 788 /** 789 * Called by Expat at the end of a DOCTYPE section. 790 * 791 * @param data parsing context 792 */ 793 static void endDtd(void* data) { 794 ParsingContext* parsingContext = toParsingContext(data); 795 JNIEnv* env = parsingContext->env; 796 797 // Bail out if a previously called handler threw an exception. 798 if (env->ExceptionCheck()) return; 799 800 jobject javaParser = parsingContext->object; 801 env->CallVoidMethod(javaParser, endDtdMethod); 802 } 803 804 /** 805 * Called by Expat when it encounters processing instructions. 806 * 807 * @param data parsing context 808 * @param target of the instruction 809 * @param instructionData 810 */ 811 static void processingInstruction(void* data, const char* target, const char* instructionData) { 812 ParsingContext* parsingContext = toParsingContext(data); 813 JNIEnv* env = parsingContext->env; 814 815 // Bail out if a previously called handler threw an exception. 816 if (env->ExceptionCheck()) return; 817 818 jstring javaTarget = internString(env, parsingContext, target); 819 if (env->ExceptionCheck()) return; 820 821 ScopedLocalRef<jstring> javaInstructionData(env, env->NewStringUTF(instructionData)); 822 if (env->ExceptionCheck()) return; 823 824 jobject javaParser = parsingContext->object; 825 env->CallVoidMethod(javaParser, processingInstructionMethod, javaTarget, javaInstructionData.get()); 826 } 827 828 /** 829 * Creates a new entity parser. 830 * 831 * @param object the Java ExpatParser instance 832 * @param parentParser pointer 833 * @param javaEncoding the character encoding name 834 * @param javaContext that was provided to handleExternalEntity 835 * @returns the pointer to the C Expat entity parser 836 */ 837 static jint ExpatParser_createEntityParser(JNIEnv* env, jobject, jint parentParser, jstring javaContext) { 838 ScopedUtfChars context(env, javaContext); 839 if (context.c_str() == NULL) { 840 return 0; 841 } 842 843 XML_Parser parent = (XML_Parser) parentParser; 844 XML_Parser entityParser = XML_ExternalEntityParserCreate(parent, context.c_str(), NULL); 845 if (entityParser == NULL) { 846 jniThrowOutOfMemoryError(env, NULL); 847 } 848 849 return (jint) entityParser; 850 } 851 852 /** 853 * Handles external entities. We ignore the "base" URI and keep track of it 854 * ourselves. 855 */ 856 static int handleExternalEntity(XML_Parser parser, const char* context, 857 const char*, const char* systemId, const char* publicId) { 858 ParsingContext* parsingContext = toParsingContext(parser); 859 jobject javaParser = parsingContext->object; 860 JNIEnv* env = parsingContext->env; 861 jobject object = parsingContext->object; 862 863 // Bail out if a previously called handler threw an exception. 864 if (env->ExceptionCheck()) { 865 return XML_STATUS_ERROR; 866 } 867 868 ScopedLocalRef<jstring> javaSystemId(env, env->NewStringUTF(systemId)); 869 if (env->ExceptionCheck()) { 870 return XML_STATUS_ERROR; 871 } 872 ScopedLocalRef<jstring> javaPublicId(env, env->NewStringUTF(publicId)); 873 if (env->ExceptionCheck()) { 874 return XML_STATUS_ERROR; 875 } 876 ScopedLocalRef<jstring> javaContext(env, env->NewStringUTF(context)); 877 if (env->ExceptionCheck()) { 878 return XML_STATUS_ERROR; 879 } 880 881 // Pass the wrapped parser and both strings to java. 882 env->CallVoidMethod(javaParser, handleExternalEntityMethod, javaContext.get(), 883 javaPublicId.get(), javaSystemId.get()); 884 885 /* 886 * Parsing the external entity leaves parsingContext->env and object set to 887 * NULL, so we need to restore both. 888 * 889 * TODO: consider restoring the original env and object instead of setting 890 * them to NULL in the append() functions. 891 */ 892 parsingContext->env = env; 893 parsingContext->object = object; 894 895 return env->ExceptionCheck() ? XML_STATUS_ERROR : XML_STATUS_OK; 896 } 897 898 /** 899 * Expat gives us 'base', but the Java API doesn't expect it, so we don't need it. 900 */ 901 static void unparsedEntityDecl(void* data, const char* name, const char* /*base*/, const char* systemId, const char* publicId, const char* notationName) { 902 ParsingContext* parsingContext = toParsingContext(data); 903 jobject javaParser = parsingContext->object; 904 JNIEnv* env = parsingContext->env; 905 906 // Bail out if a previously called handler threw an exception. 907 if (env->ExceptionCheck()) return; 908 909 ScopedLocalRef<jstring> javaName(env, env->NewStringUTF(name)); 910 if (env->ExceptionCheck()) return; 911 ScopedLocalRef<jstring> javaPublicId(env, env->NewStringUTF(publicId)); 912 if (env->ExceptionCheck()) return; 913 ScopedLocalRef<jstring> javaSystemId(env, env->NewStringUTF(systemId)); 914 if (env->ExceptionCheck()) return; 915 ScopedLocalRef<jstring> javaNotationName(env, env->NewStringUTF(notationName)); 916 if (env->ExceptionCheck()) return; 917 918 env->CallVoidMethod(javaParser, unparsedEntityDeclMethod, javaName.get(), javaPublicId.get(), javaSystemId.get(), javaNotationName.get()); 919 } 920 921 /** 922 * Expat gives us 'base', but the Java API doesn't expect it, so we don't need it. 923 */ 924 static void notationDecl(void* data, const char* name, const char* /*base*/, const char* systemId, const char* publicId) { 925 ParsingContext* parsingContext = toParsingContext(data); 926 jobject javaParser = parsingContext->object; 927 JNIEnv* env = parsingContext->env; 928 929 // Bail out if a previously called handler threw an exception. 930 if (env->ExceptionCheck()) return; 931 932 ScopedLocalRef<jstring> javaName(env, env->NewStringUTF(name)); 933 if (env->ExceptionCheck()) return; 934 ScopedLocalRef<jstring> javaPublicId(env, env->NewStringUTF(publicId)); 935 if (env->ExceptionCheck()) return; 936 ScopedLocalRef<jstring> javaSystemId(env, env->NewStringUTF(systemId)); 937 if (env->ExceptionCheck()) return; 938 939 env->CallVoidMethod(javaParser, notationDeclMethod, javaName.get(), javaPublicId.get(), javaSystemId.get()); 940 } 941 942 /** 943 * Creates a new Expat parser. Called from the Java ExpatParser constructor. 944 * 945 * @param object the Java ExpatParser instance 946 * @param javaEncoding the character encoding name 947 * @param processNamespaces true if the parser should handle namespaces 948 * @returns the pointer to the C Expat parser 949 */ 950 static jint ExpatParser_initialize(JNIEnv* env, jobject object, jstring javaEncoding, 951 jboolean processNamespaces) { 952 // Allocate parsing context. 953 UniquePtr<ParsingContext> context(new ParsingContext(object)); 954 if (context.get() == NULL) { 955 jniThrowOutOfMemoryError(env, NULL); 956 return 0; 957 } 958 959 context->processNamespaces = (bool) processNamespaces; 960 961 // Create a parser. 962 XML_Parser parser; 963 ScopedUtfChars encoding(env, javaEncoding); 964 if (encoding.c_str() == NULL) { 965 return 0; 966 } 967 if (processNamespaces) { 968 // Use '|' to separate URIs from local names. 969 parser = XML_ParserCreateNS(encoding.c_str(), '|'); 970 } else { 971 parser = XML_ParserCreate(encoding.c_str()); 972 } 973 974 if (parser != NULL) { 975 if (processNamespaces) { 976 XML_SetNamespaceDeclHandler(parser, startNamespace, endNamespace); 977 XML_SetReturnNSTriplet(parser, 1); 978 } 979 980 XML_SetCdataSectionHandler(parser, startCdata, endCdata); 981 XML_SetCharacterDataHandler(parser, text); 982 XML_SetCommentHandler(parser, comment); 983 XML_SetDoctypeDeclHandler(parser, startDtd, endDtd); 984 XML_SetElementHandler(parser, startElement, endElement); 985 XML_SetExternalEntityRefHandler(parser, handleExternalEntity); 986 XML_SetNotationDeclHandler(parser, notationDecl); 987 XML_SetProcessingInstructionHandler(parser, processingInstruction); 988 XML_SetUnparsedEntityDeclHandler(parser, unparsedEntityDecl); 989 XML_SetUserData(parser, context.release()); 990 } else { 991 jniThrowOutOfMemoryError(env, NULL); 992 return 0; 993 } 994 995 return (jint) parser; 996 } 997 998 /** 999 * Decodes the bytes as characters and parse the characters as XML. This 1000 * performs character decoding using the charset specified at XML_Parser 1001 * creation. For Java chars, that charset must be UTF-16 so that a Java char[] 1002 * can be reinterpreted as a UTF-16 encoded byte[]. appendBytes, appendChars 1003 * and appendString all call through this method. 1004 */ 1005 static void append(JNIEnv* env, jobject object, jint pointer, 1006 const char* bytes, size_t byteOffset, size_t byteCount, jboolean isFinal) { 1007 XML_Parser parser = (XML_Parser) pointer; 1008 ParsingContext* context = toParsingContext(parser); 1009 context->env = env; 1010 context->object = object; 1011 if (!XML_Parse(parser, bytes + byteOffset, byteCount, isFinal) && !env->ExceptionCheck()) { 1012 jniThrowExpatException(env, XML_GetErrorCode(parser)); 1013 } 1014 context->object = NULL; 1015 context->env = NULL; 1016 } 1017 1018 static void ExpatParser_appendBytes(JNIEnv* env, jobject object, jint pointer, 1019 jbyteArray xml, jint byteOffset, jint byteCount) { 1020 ScopedByteArrayRO byteArray(env, xml); 1021 if (byteArray.get() == NULL) { 1022 return; 1023 } 1024 1025 const char* bytes = reinterpret_cast<const char*>(byteArray.get()); 1026 append(env, object, pointer, bytes, byteOffset, byteCount, XML_FALSE); 1027 } 1028 1029 static void ExpatParser_appendChars(JNIEnv* env, jobject object, jint pointer, 1030 jcharArray xml, jint charOffset, jint charCount) { 1031 ScopedCharArrayRO charArray(env, xml); 1032 if (charArray.get() == NULL) { 1033 return; 1034 } 1035 1036 const char* bytes = reinterpret_cast<const char*>(charArray.get()); 1037 size_t byteOffset = 2 * charOffset; 1038 size_t byteCount = 2 * charCount; 1039 append(env, object, pointer, bytes, byteOffset, byteCount, XML_FALSE); 1040 } 1041 1042 static void ExpatParser_appendString(JNIEnv* env, jobject object, jint pointer, jstring javaXml, jboolean isFinal) { 1043 ScopedStringChars xml(env, javaXml); 1044 if (xml.get() == NULL) { 1045 return; 1046 } 1047 const char* bytes = reinterpret_cast<const char*>(xml.get()); 1048 size_t byteCount = 2 * xml.size(); 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 * 2) + 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 *= 2; 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 jniThrowOutOfMemoryError(env, NULL); 1280 return 0; 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