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