| 1 2 9 10 package org.xmlpull.mxp1; 11 12 import java.io.EOFException ; 13 import java.io.IOException ; 14 import java.io.Reader ; 15 import java.io.InputStreamReader ; 16 import java.io.UnsupportedEncodingException ; 17 18 20 import org.xmlpull.v1.XmlPullParser; 21 import org.xmlpull.v1.XmlPullParserException; 22 23 26 28 30 35 36 public class MXParser 37 implements XmlPullParser 38 { 39 protected final static String XML_URI = "http://www.w3.org/XML/1998/namespace"; 41 protected final static String XMLNS_URI = "http://www.w3.org/2000/xmlns/"; 42 protected final static String FEATURE_XML_ROUNDTRIP= 43 "http://xmlpull.org/v1/doc/features.html#xml-roundtrip"; 45 protected final static String FEATURE_NAMES_INTERNED = 46 "http://xmlpull.org/v1/doc/features.html#names-interned"; 47 protected final static String PROPERTY_XMLDECL_VERSION = 48 "http://xmlpull.org/v1/doc/properties.html#xmldecl-version"; 49 protected final static String PROPERTY_XMLDECL_STANDALONE = 50 "http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone"; 51 protected final static String PROPERTY_XMLDECL_CONTENT = 52 "http://xmlpull.org/v1/doc/properties.html#xmldecl-content"; 53 54 55 62 protected boolean allStringsInterned; 63 64 protected void resetStringCache() { 65 } 67 68 protected String newString(char[] cbuf, int off, int len) { 69 return new String (cbuf, off, len); 70 } 71 72 protected String newStringIntern(char[] cbuf, int off, int len) { 73 return (new String (cbuf, off, len)).intern(); 74 } 75 76 77 private static final boolean TRACE_SIZING = false; 78 79 protected boolean processNamespaces; 81 protected boolean roundtripSupported; 82 83 protected int lineNumber; 85 protected int columnNumber; 86 protected boolean seenRoot; 87 protected boolean reachedEnd; 88 protected int eventType; 89 protected boolean emptyElementTag; 90 protected int depth; 92 protected char[] elRawName[]; 93 protected int elRawNameEnd[]; 94 protected String elName[]; 96 protected String elPrefix[]; 97 protected String elUri[]; 98 protected int elNamespaceCount[]; 100 101 102 103 107 protected void ensureElementsCapacity() { 108 int elStackSize = elName != null ? elName.length : 0; 109 if( (depth + 1) >= elStackSize) { 110 int newSize = (depth >= 7 ? 2 * depth : 8) + 2; if(TRACE_SIZING) { 113 System.err.println("TRACE_SIZING elStackSize "+elStackSize+" ==> "+newSize); 114 } 115 boolean needsCopying = elStackSize > 0; 116 String [] arr = null; 117 arr = new String [newSize]; 118 if(needsCopying) System.arraycopy(elName, 0, arr, 0, elStackSize); 119 elName = arr; 120 arr = new String [newSize]; 121 if(needsCopying) System.arraycopy(elPrefix, 0, arr, 0, elStackSize); 122 elPrefix = arr; 123 arr = new String [newSize]; 124 if(needsCopying) System.arraycopy(elUri, 0, arr, 0, elStackSize); 125 elUri = arr; 126 127 int[] iarr = new int[newSize]; 128 if(needsCopying) { 129 System.arraycopy(elNamespaceCount, 0, iarr, 0, elStackSize); 130 } else { 131 iarr[0] = 0; 133 } 134 elNamespaceCount = iarr; 135 136 iarr = new int[newSize]; 138 if(needsCopying) { 139 System.arraycopy(elRawNameEnd, 0, iarr, 0, elStackSize); 140 } 141 elRawNameEnd = iarr; 142 143 char[][] carr = new char[newSize][]; 144 if(needsCopying) { 145 System.arraycopy(elRawName, 0, carr, 0, elStackSize); 146 } 147 elRawName = carr; 148 } 163 } 164 165 166 protected static final int LOOKUP_MAX = 0x400; 168 protected static final char LOOKUP_MAX_CHAR = (char)LOOKUP_MAX; 169 protected static boolean lookupNameStartChar[] = new boolean[ LOOKUP_MAX ]; 172 protected static boolean lookupNameChar[] = new boolean[ LOOKUP_MAX ]; 173 174 private static final void setName(char ch) 175 { lookupNameChar[ ch ] = true; } 177 private static final void setNameStart(char ch) 178 { lookupNameStartChar[ ch ] = true; setName(ch); } 180 181 static { 182 setNameStart(':'); 183 for (char ch = 'A'; ch <= 'Z'; ++ch) setNameStart(ch); 184 setNameStart('_'); 185 for (char ch = 'a'; ch <= 'z'; ++ch) setNameStart(ch); 186 for (char ch = '\u00c0'; ch <= '\u02FF'; ++ch) setNameStart(ch); 187 for (char ch = '\u0370'; ch <= '\u037d'; ++ch) setNameStart(ch); 188 for (char ch = '\u037f'; ch < '\u0400'; ++ch) setNameStart(ch); 189 190 setName('-'); 191 setName('.'); 192 for (char ch = '0'; ch <= '9'; ++ch) setName(ch); 193 setName('\u00b7'); 194 for (char ch = '\u0300'; ch <= '\u036f'; ++ch) setName(ch); 195 } 196 197 protected boolean isNameStartChar(char ch) { 199 return (ch < LOOKUP_MAX_CHAR && lookupNameStartChar[ ch ]) 200 || (ch >= LOOKUP_MAX_CHAR && ch <= '\u2027') 201 || (ch >= '\u202A' && ch <= '\u218F') 202 || (ch >= '\u2800' && ch <= '\uFFEF') 203 ; 204 205 223 224 } 226 227 protected boolean isNameChar(char ch) { 229 231 233 return (ch < LOOKUP_MAX_CHAR && lookupNameChar[ ch ]) 234 || (ch >= LOOKUP_MAX_CHAR && ch <= '\u2027') 235 || (ch >= '\u202A' && ch <= '\u218F') 236 || (ch >= '\u2800' && ch <= '\uFFEF') 237 ; 238 243 } 253 254 protected boolean isS(char ch) { 255 return (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'); 256 } 258 259 262 263 protected int attributeCount; 265 protected String attributeName[]; 266 protected int attributeNameHash[]; 267 protected String attributePrefix[]; 270 protected String attributeUri[]; 271 protected String attributeValue[]; 272 275 276 279 protected void ensureAttributesCapacity(int size) { 280 int attrPosSize = attributeName != null ? attributeName.length : 0; 281 if(size >= attrPosSize) { 282 int newSize = size > 7 ? 2 * size : 8; if(TRACE_SIZING) { 284 System.err.println("TRACE_SIZING attrPosSize "+attrPosSize+" ==> "+newSize); 285 } 286 boolean needsCopying = attrPosSize > 0; 287 String [] arr = null; 288 289 arr = new String [newSize]; 290 if(needsCopying) System.arraycopy(attributeName, 0, arr, 0, attrPosSize); 291 attributeName = arr; 292 293 arr = new String [newSize]; 294 if(needsCopying) System.arraycopy(attributePrefix, 0, arr, 0, attrPosSize); 295 attributePrefix = arr; 296 297 arr = new String [newSize]; 298 if(needsCopying) System.arraycopy(attributeUri, 0, arr, 0, attrPosSize); 299 attributeUri = arr; 300 301 arr = new String [newSize]; 302 if(needsCopying) System.arraycopy(attributeValue, 0, arr, 0, attrPosSize); 303 attributeValue = arr; 304 305 if( ! allStringsInterned ) { 306 int[] iarr = new int[newSize]; 307 if(needsCopying) System.arraycopy(attributeNameHash, 0, iarr, 0, attrPosSize); 308 attributeNameHash = iarr; 309 } 310 311 arr = null; 312 } 314 } 315 316 protected int namespaceEnd; 318 protected String namespacePrefix[]; 319 protected int namespacePrefixHash[]; 320 protected String namespaceUri[]; 321 322 protected void ensureNamespacesCapacity(int size) { 323 int namespaceSize = namespacePrefix != null ? namespacePrefix.length : 0; 324 if(size >= namespaceSize) { 325 int newSize = size > 7 ? 2 * size : 8; if(TRACE_SIZING) { 327 System.err.println("TRACE_SIZING namespaceSize "+namespaceSize+" ==> "+newSize); 328 } 329 String [] newNamespacePrefix = new String [newSize]; 330 String [] newNamespaceUri = new String [newSize]; 331 if(namespacePrefix != null) { 332 System.arraycopy( 333 namespacePrefix, 0, newNamespacePrefix, 0, namespaceEnd); 334 System.arraycopy( 335 namespaceUri, 0, newNamespaceUri, 0, namespaceEnd); 336 } 337 namespacePrefix = newNamespacePrefix; 338 namespaceUri = newNamespaceUri; 339 340 341 if( ! allStringsInterned ) { 342 int[] newNamespacePrefixHash = new int[newSize]; 343 if(namespacePrefixHash != null) { 344 System.arraycopy( 345 namespacePrefixHash, 0, newNamespacePrefixHash, 0, namespaceEnd); 346 } 347 namespacePrefixHash = newNamespacePrefixHash; 348 } 349 } 352 } 353 354 359 protected static final int fastHash( char ch[], int off, int len ) { 360 if(len == 0) return 0; 361 int hash = ch[off]; hash = (hash << 7) + ch[ off + len - 1 ]; if(len > 16) hash = (hash << 7) + ch[ off + (len / 4)]; if(len > 8) hash = (hash << 7) + ch[ off + (len / 2)]; return hash; 375 } 376 377 protected int entityEnd; 379 protected String entityName[]; 380 protected char[] entityNameBuf[]; 381 protected int entityNameHash[]; 382 protected char[] entityReplacementBuf[]; 383 protected String entityReplacement[]; 384 385 386 protected void ensureEntityCapacity() { 387 int entitySize = entityReplacementBuf != null ? entityReplacementBuf.length : 0; 388 if(entityEnd >= entitySize) { 389 int newSize = entityEnd > 7 ? 2 * entityEnd : 8; if(TRACE_SIZING) { 391 System.err.println("TRACE_SIZING entitySize "+entitySize+" ==> "+newSize); 392 } 393 String [] newEntityName = new String [newSize]; 394 char[] newEntityNameBuf[] = new char[newSize][]; 395 String [] newEntityReplacement = new String [newSize]; 396 char[] newEntityReplacementBuf[] = new char[newSize][]; 397 if(entityName != null) { 398 System.arraycopy(entityName, 0, newEntityName, 0, entityEnd); 399 System.arraycopy(entityReplacementBuf, 0, newEntityReplacement, 0, entityEnd); 400 System.arraycopy(entityReplacement, 0, newEntityReplacement, 0, entityEnd); 401 System.arraycopy(entityReplacementBuf, 0, newEntityReplacementBuf, 0, entityEnd); 402 } 403 entityName = newEntityName; 404 entityNameBuf = newEntityNameBuf; 405 entityReplacement = newEntityReplacement; 406 entityReplacementBuf = newEntityReplacementBuf; 407 408 if( ! allStringsInterned ) { 409 int[] newEntityNameHash = new int[newSize]; 410 if(entityNameHash != null) { 411 System.arraycopy(entityNameHash, 0, newEntityNameHash, 0, entityEnd); 412 } 413 entityNameHash = newEntityNameHash; 414 } 415 } 416 } 417 418 protected static final int READ_CHUNK_SIZE = 8*1024; protected Reader reader; 421 protected String inputEncoding; 422 423 424 protected int bufLoadFactor = 95; 427 protected char buf[] = new char[ 428 Runtime.getRuntime().freeMemory() > 1000000L ? READ_CHUNK_SIZE : 256 ]; 429 protected int bufSoftLimit = ( bufLoadFactor * buf.length ) /100; protected boolean preventBufferCompaction; 431 432 protected int bufAbsoluteStart; protected int bufStart; 434 protected int bufEnd; 435 protected int pos; 436 protected int posStart; 437 protected int posEnd; 438 439 protected char pc[] = new char[ 440 Runtime.getRuntime().freeMemory() > 1000000L ? READ_CHUNK_SIZE : 64 ]; 441 protected int pcStart; 442 protected int pcEnd; 443 444 445 protected boolean usePC; 449 450 451 protected boolean seenStartTag; 452 protected boolean seenEndTag; 453 protected boolean pastEndTag; 454 protected boolean seenAmpersand; 455 protected boolean seenMarkup; 456 protected boolean seenDocdecl; 457 458 protected boolean tokenize; 460 protected String text; 461 protected String entityRefName; 462 463 protected String xmlDeclVersion; 464 protected Boolean xmlDeclStandalone; 465 protected String xmlDeclContent; 466 467 protected void reset() { 468 lineNumber = 1; 470 columnNumber = 0; 471 seenRoot = false; 472 reachedEnd = false; 473 eventType = START_DOCUMENT; 474 emptyElementTag = false; 475 476 depth = 0; 477 478 attributeCount = 0; 479 480 namespaceEnd = 0; 481 482 entityEnd = 0; 483 484 reader = null; 485 inputEncoding = null; 486 487 preventBufferCompaction = false; 488 bufAbsoluteStart = 0; 489 bufEnd = bufStart = 0; 490 pos = posStart = posEnd = 0; 491 492 pcEnd = pcStart = 0; 493 494 usePC = false; 495 496 seenStartTag = false; 497 seenEndTag = false; 498 pastEndTag = false; 499 seenAmpersand = false; 500 seenMarkup = false; 501 seenDocdecl = false; 502 503 xmlDeclVersion = null; 504 xmlDeclStandalone = null; 505 xmlDeclContent = null; 506 507 resetStringCache(); 508 } 509 510 public MXParser() { 511 } 512 513 514 523 public void setFeature(String name, 524 boolean state) throws XmlPullParserException 525 { 526 if(name == null) throw new IllegalArgumentException ("feature name should not be nulll"); 527 if(FEATURE_PROCESS_NAMESPACES.equals(name)) { 528 if(eventType != START_DOCUMENT) throw new XmlPullParserException( 529 "namespace processing feature can only be changed before parsing", this, null); 530 processNamespaces = state; 531 } else if(FEATURE_NAMES_INTERNED.equals(name)) { 536 if(state != false) { 537 throw new XmlPullParserException( 538 "interning names in this implementation is not supported"); 539 } 540 } else if(FEATURE_PROCESS_DOCDECL.equals(name)) { 541 if(state != false) { 542 throw new XmlPullParserException( 543 "processing DOCDECL is not supported"); 544 } 545 } else if(FEATURE_XML_ROUNDTRIP.equals(name)) { 548 roundtripSupported = state; 553 } else { 554 throw new XmlPullParserException("unknown feature "+name); 555 } 556 } 557 558 559 public boolean getFeature(String name) 560 { 561 if(name == null) throw new IllegalArgumentException ("feature name should not be nulll"); 562 if(FEATURE_PROCESS_NAMESPACES.equals(name)) { 563 return processNamespaces; 564 } else if(FEATURE_NAMES_INTERNED.equals(name)) { 567 return false; 568 } else if(FEATURE_PROCESS_DOCDECL.equals(name)) { 569 return false; 570 } else if(FEATURE_XML_ROUNDTRIP.equals(name)) { 573 return roundtripSupported; 575 } 576 return false; 577 } 578 579 public void setProperty(String name, 580 Object value) 581 throws XmlPullParserException 582 { 583 throw new XmlPullParserException("unsupported property: '"+name+"'"); 584 } 585 586 587 public Object getProperty(String name) 588 { 589 if(name == null) throw new IllegalArgumentException ("property name should not be nulll"); 590 if(PROPERTY_XMLDECL_VERSION.equals(name)) { 591 return xmlDeclVersion; 592 } else if(PROPERTY_XMLDECL_STANDALONE.equals(name)) { 593 return xmlDeclStandalone; 594 } else if(PROPERTY_XMLDECL_CONTENT.equals(name)) { 595 return xmlDeclContent; 596 } 597 return null; 598 } 599 600 601 public void setInput(Reader in) throws XmlPullParserException 602 { 603 reset(); 604 reader = in; 605 } 606 607 608 public void setInput(java.io.InputStream inputStream, String inputEncoding) 609 throws XmlPullParserException 610 { 611 if(inputStream == null) { 612 throw new IllegalArgumentException ("input stream can not be null"); 613 } 614 Reader reader; 615 if(inputEncoding != null) { 616 try { 617 if(inputEncoding != null) { 618 reader = new InputStreamReader (inputStream, inputEncoding); 619 } else { 620 reader = new InputStreamReader (inputStream); 621 } 622 } catch (UnsupportedEncodingException une) { 623 throw new XmlPullParserException( 624 "could not create reader for encoding "+inputEncoding+" : "+une, this, une); 625 } 626 } else { 627 reader = new InputStreamReader (inputStream); 628 } 629 setInput(reader); 630 this.inputEncoding = inputEncoding; 632 } 633 634 public String getInputEncoding() { 635 return inputEncoding; 636 } 637 638 public void defineEntityReplacementText(String entityName, 639 String replacementText) 640 throws XmlPullParserException 641 { 642 644 ensureEntityCapacity(); 646 647 this.entityName[entityEnd] = newString(entityName.toCharArray(), 0, entityName.length()); 649 entityNameBuf[entityEnd] = entityName.toCharArray(); 650 651 entityReplacement[entityEnd] = replacementText; 652 entityReplacementBuf[entityEnd] = replacementText.toCharArray(); 653 if(!allStringsInterned) { 654 entityNameHash[ entityEnd ] = 655 fastHash(entityNameBuf[entityEnd], 0, entityNameBuf[entityEnd].length); 656 } 657 ++entityEnd; 658 } 661 662 public int getNamespaceCount(int depth) 663 throws XmlPullParserException 664 { 665 if(processNamespaces == false || depth == 0) { 666 return 0; 667 } 668 if(depth < 0 || depth > this.depth) throw new IllegalArgumentException ( 671 "napespace count mayt be for depth 0.."+this.depth+" not "+depth); 672 return elNamespaceCount[ depth ]; 673 } 674 675 public String getNamespacePrefix(int pos) 676 throws XmlPullParserException 677 { 678 679 if(pos < namespaceEnd) { 682 return namespacePrefix[ pos ]; 683 } else { 684 throw new XmlPullParserException( 685 "position "+pos+" exceeded number of available namespaces "+namespaceEnd); 686 } 687 } 688 689 public String getNamespaceUri(int pos) throws XmlPullParserException 690 { 691 if(pos < namespaceEnd) { 694 return namespaceUri[ pos ]; 695 } else { 696 throw new XmlPullParserException( 697 "position "+pos+" exceedded number of available namespaces "+namespaceEnd); 698 } 699 } 700 701 public String getNamespace( String prefix ) 702 { 704 if(prefix != null) { 706 for( int i = namespaceEnd -1; i >= 0; i--) { 707 if( prefix.equals( namespacePrefix[ i ] ) ) { 708 return namespaceUri[ i ]; 709 } 710 } 711 if("xml".equals( prefix )) { 712 return XML_URI; 713 } else if("xmlns".equals( prefix )) { 714 return XMLNS_URI; 715 } 716 } else { 717 for( int i = namespaceEnd -1; i >= 0; i--) { 718 if( namespacePrefix[ i ] == null ) { 719 return namespaceUri[ i ]; 720 } 721 } 722 723 } 724 return null; 725 } 726 727 728 public int getDepth() 729 { 730 return depth; 731 } 732 733 734 private static int findFragment(int bufMinPos, char[] b, int start, int end) { 735 if(start < bufMinPos) { 737 start = bufMinPos; 738 if(start > end) start = end; 739 return start; 740 } 741 if(end - start > 65) { 742 start = end - 10; } 744 int i = start + 1; 745 while(--i > bufMinPos) { 746 if((end - i) > 65) break; 747 char c = b[i]; 748 if(c == '<' && (start - i) > 10) break; 749 } 750 return i; 751 } 752 753 754 758 public String getPositionDescription () 759 { 760 String fragment = null; 761 if(posStart <= pos) { 762 int start = findFragment(0, buf, posStart, pos); 763 if(start < pos) { 765 fragment = new String (buf, start, pos - start); 766 } 767 if(bufAbsoluteStart > 0 || start > 0) fragment = "..." + fragment; 768 } 769 return " "+TYPES[ eventType ] + 773 (fragment != null ? " seen "+printable(fragment)+"..." : "")+ 774 " @"+getLineNumber()+":"+getColumnNumber(); 775 } 776 777 public int getLineNumber() 778 { 779 return lineNumber; 780 } 781 782 public int getColumnNumber() 783 { 784 return columnNumber; 785 } 786 787 788 public boolean isWhitespace() throws XmlPullParserException 789 { 790 if(eventType == TEXT || eventType == CDSECT) { 791 if(usePC) { 792 |