1
2
3
4
5
6
7
8 package io.github.xstream.mxparser;
9
10 import java.io.EOFException;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.io.InputStreamReader;
14 import java.io.Reader;
15 import java.io.UnsupportedEncodingException;
16 import org.xmlpull.v1.XmlPullParser;
17 import org.xmlpull.v1.XmlPullParserException;
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 public class MXParser
33 implements XmlPullParser
34 {
35
36 private final static String XML_URI = "http://www.w3.org/XML/1998/namespace";
37 private final static String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
38 private final static String FEATURE_XML_ROUNDTRIP=
39
40 "http://xmlpull.org/v1/doc/features.html#xml-roundtrip";
41 private final static String FEATURE_NAMES_INTERNED =
42 "http://xmlpull.org/v1/doc/features.html#names-interned";
43 private final static String PROPERTY_XMLDECL_VERSION =
44 "http://xmlpull.org/v1/doc/properties.html#xmldecl-version";
45 private final static String PROPERTY_XMLDECL_STANDALONE =
46 "http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone";
47 private final static String PROPERTY_XMLDECL_CONTENT =
48 "http://xmlpull.org/v1/doc/properties.html#xmldecl-content";
49 private final static String PROPERTY_LOCATION =
50 "http://xmlpull.org/v1/doc/properties.html#location";
51
52
53
54
55
56
57
58
59 private boolean allStringsInterned;
60
61 private void resetStringCache() {
62
63 }
64
65 private String newString(char[] cbuf, int off, int len) {
66 return new String(cbuf, off, len);
67 }
68
69 private String newStringIntern(char[] cbuf, int off, int len) {
70 return (new String(cbuf, off, len)).intern();
71 }
72
73 private static final boolean TRACE_SIZING = false;
74
75
76 private boolean processNamespaces;
77 private boolean roundtripSupported;
78
79
80 private String location;
81 private int lineNumber;
82 private int columnNumber;
83 private boolean seenRoot;
84 private boolean reachedEnd;
85 private int eventType;
86 private boolean emptyElementTag;
87
88 private int depth;
89 private char[] elRawName[];
90 private int elRawNameEnd[];
91 private int elRawNameLine[];
92
93 private String elName[];
94 private String elPrefix[];
95 private String elUri[];
96
97 private int elNamespaceCount[];
98
99
100
101
102
103
104
105 private void ensureElementsCapacity() {
106 final int elStackSize = elName != null ? elName.length : 0;
107 if( (depth + 1) >= elStackSize) {
108
109 final int newSize = (depth >= 7 ? 2 * depth : 8) + 2;
110 if(TRACE_SIZING) {
111 System.err.println("TRACE_SIZING elStackSize "+elStackSize+" ==> "+newSize);
112 }
113 final boolean needsCopying = elStackSize > 0;
114 String[] arr = null;
115
116 arr = new String[newSize];
117 if(needsCopying) System.arraycopy(elName, 0, arr, 0, elStackSize);
118 elName = arr;
119 arr = new String[newSize];
120 if(needsCopying) System.arraycopy(elPrefix, 0, arr, 0, elStackSize);
121 elPrefix = arr;
122 arr = new String[newSize];
123 if(needsCopying) System.arraycopy(elUri, 0, arr, 0, elStackSize);
124 elUri = arr;
125
126 int[] iarr = new int[newSize];
127 if(needsCopying) {
128 System.arraycopy(elNamespaceCount, 0, iarr, 0, elStackSize);
129 } else {
130
131 iarr[0] = 0;
132 }
133 elNamespaceCount = iarr;
134
135
136 iarr = new int[newSize];
137 if(needsCopying) {
138 System.arraycopy(elRawNameEnd, 0, iarr, 0, elStackSize);
139 }
140 elRawNameEnd = iarr;
141
142 iarr = new int[newSize];
143 if(needsCopying) {
144 System.arraycopy(elRawNameLine, 0, iarr, 0, elStackSize);
145 }
146 elRawNameLine = iarr;
147
148 final char[][] carr = new char[newSize][];
149 if(needsCopying) {
150 System.arraycopy(elRawName, 0, carr, 0, elStackSize);
151 }
152 elRawName = carr;
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 }
168 }
169
170
171
172
173 private int attributeCount;
174 private String attributeName[];
175 private int attributeNameHash[];
176
177
178 private String attributePrefix[];
179 private String attributeUri[];
180 private String attributeValue[];
181
182
183
184
185
186
187
188 private void ensureAttributesCapacity(int size) {
189 final int attrPosSize = attributeName != null ? attributeName.length : 0;
190 if(size >= attrPosSize) {
191 final int newSize = size > 7 ? 2 * size : 8;
192 if(TRACE_SIZING) {
193 System.err.println("TRACE_SIZING attrPosSize "+attrPosSize+" ==> "+newSize);
194 }
195 final boolean needsCopying = attrPosSize > 0;
196 String[] arr = null;
197
198 arr = new String[newSize];
199 if(needsCopying) System.arraycopy(attributeName, 0, arr, 0, attrPosSize);
200 attributeName = arr;
201
202 arr = new String[newSize];
203 if(needsCopying) System.arraycopy(attributePrefix, 0, arr, 0, attrPosSize);
204 attributePrefix = arr;
205
206 arr = new String[newSize];
207 if(needsCopying) System.arraycopy(attributeUri, 0, arr, 0, attrPosSize);
208 attributeUri = arr;
209
210 arr = new String[newSize];
211 if(needsCopying) System.arraycopy(attributeValue, 0, arr, 0, attrPosSize);
212 attributeValue = arr;
213
214 if( ! allStringsInterned ) {
215 final int[] iarr = new int[newSize];
216 if(needsCopying) System.arraycopy(attributeNameHash, 0, iarr, 0, attrPosSize);
217 attributeNameHash = iarr;
218 }
219
220 arr = null;
221
222 }
223 }
224
225
226 private int namespaceEnd;
227 private String namespacePrefix[];
228 private int namespacePrefixHash[];
229 private String namespaceUri[];
230
231 private void ensureNamespacesCapacity(int size) {
232 final int namespaceSize = namespacePrefix != null ? namespacePrefix.length : 0;
233 if(size >= namespaceSize) {
234 final int newSize = size > 7 ? 2 * size : 8;
235 if(TRACE_SIZING) {
236 System.err.println("TRACE_SIZING namespaceSize "+namespaceSize+" ==> "+newSize);
237 }
238 final String[] newNamespacePrefix = new String[newSize];
239 final String[] newNamespaceUri = new String[newSize];
240 if(namespacePrefix != null) {
241 System.arraycopy(
242 namespacePrefix, 0, newNamespacePrefix, 0, namespaceEnd);
243 System.arraycopy(
244 namespaceUri, 0, newNamespaceUri, 0, namespaceEnd);
245 }
246 namespacePrefix = newNamespacePrefix;
247 namespaceUri = newNamespaceUri;
248
249
250 if( ! allStringsInterned ) {
251 final int[] newNamespacePrefixHash = new int[newSize];
252 if(namespacePrefixHash != null) {
253 System.arraycopy(
254 namespacePrefixHash, 0, newNamespacePrefixHash, 0, namespaceEnd);
255 }
256 namespacePrefixHash = newNamespacePrefixHash;
257 }
258
259
260 }
261 }
262
263
264
265
266
267
268 private static final int fastHash( char ch[], int off, int len ) {
269 if(len == 0) return 0;
270
271 int hash = ch[off];
272
273 hash = (hash << 7) + ch[ off + len - 1 ];
274
275
276
277
278 if(len > 16) hash = (hash << 7) + ch[ off + (len / 4)];
279 if(len > 8) hash = (hash << 7) + ch[ off + (len / 2)];
280
281
282
283 return hash;
284 }
285
286
287 private int entityEnd;
288
289 private String entityName[];
290 private char[] entityNameBuf[];
291 private String entityReplacement[];
292 private char[] entityReplacementBuf[];
293
294 private int entityNameHash[];
295
296 private void ensureEntityCapacity() {
297 final int entitySize = entityReplacementBuf != null ? entityReplacementBuf.length : 0;
298 if(entityEnd >= entitySize) {
299 final int newSize = entityEnd > 7 ? 2 * entityEnd : 8;
300 if(TRACE_SIZING) {
301 System.err.println("TRACE_SIZING entitySize "+entitySize+" ==> "+newSize);
302 }
303 final String[] newEntityName = new String[newSize];
304 final char[] newEntityNameBuf[] = new char[newSize][];
305 final String[] newEntityReplacement = new String[newSize];
306 final char[] newEntityReplacementBuf[] = new char[newSize][];
307 if(entityName != null) {
308 System.arraycopy(entityName, 0, newEntityName, 0, entityEnd);
309 System.arraycopy(entityNameBuf, 0, newEntityNameBuf, 0, entityEnd);
310 System.arraycopy(entityReplacement, 0, newEntityReplacement, 0, entityEnd);
311 System.arraycopy(entityReplacementBuf, 0, newEntityReplacementBuf, 0, entityEnd);
312 }
313 entityName = newEntityName;
314 entityNameBuf = newEntityNameBuf;
315 entityReplacement = newEntityReplacement;
316 entityReplacementBuf = newEntityReplacementBuf;
317
318 if( ! allStringsInterned ) {
319 final int[] newEntityNameHash = new int[newSize];
320 if(entityNameHash != null) {
321 System.arraycopy(entityNameHash, 0, newEntityNameHash, 0, entityEnd);
322 }
323 entityNameHash = newEntityNameHash;
324 }
325 }
326 }
327
328
329 private static final int READ_CHUNK_SIZE = 8*1024;
330 private Reader reader;
331 private String inputEncoding;
332
333
334 private int bufLoadFactor = 95;
335
336 private float bufferLoadFactor = bufLoadFactor / 100f;
337
338 private char buf[] = new char[
339 Runtime.getRuntime().freeMemory() > 1000000L ? READ_CHUNK_SIZE : 256 ];
340 private int bufSoftLimit = (int)( bufferLoadFactor * buf.length ) /100;
341 private boolean preventBufferCompaction;
342
343 private int bufAbsoluteStart;
344 private int bufStart;
345 private int bufEnd;
346 private int pos;
347 private int posStart;
348 private int posEnd;
349
350 private char pc[] = new char[
351 Runtime.getRuntime().freeMemory() > 1000000L ? READ_CHUNK_SIZE : 64 ];
352 private int pcStart;
353 private int pcEnd;
354
355
356
357
358
359 private boolean usePC;
360
361
362 private boolean seenStartTag;
363 private boolean seenEndTag;
364 private boolean pastEndTag;
365 private boolean seenAmpersand;
366 private boolean seenMarkup;
367 private boolean seenDocdecl;
368
369
370 private boolean tokenize;
371 private String text;
372 private String entityRefName;
373
374 private String xmlDeclVersion;
375 private Boolean xmlDeclStandalone;
376 private String xmlDeclContent;
377
378 private static boolean noUnicode4;
379
380 private void reset() {
381
382 location = null;
383 lineNumber = 1;
384 columnNumber = 1;
385 seenRoot = false;
386 reachedEnd = false;
387 eventType = START_DOCUMENT;
388 emptyElementTag = false;
389
390 depth = 0;
391
392 attributeCount = 0;
393
394 namespaceEnd = 0;
395
396 entityEnd = 0;
397
398 reader = null;
399 inputEncoding = null;
400
401 preventBufferCompaction = false;
402 bufAbsoluteStart = 0;
403 bufEnd = bufStart = 0;
404 pos = posStart = posEnd = 0;
405
406 pcEnd = pcStart = 0;
407
408 usePC = false;
409
410 seenStartTag = false;
411 seenEndTag = false;
412 pastEndTag = false;
413 seenAmpersand = false;
414 seenMarkup = false;
415 seenDocdecl = false;
416
417 xmlDeclVersion = null;
418 xmlDeclStandalone = null;
419 xmlDeclContent = null;
420
421 resetStringCache();
422 }
423
424 public MXParser() {
425 }
426
427
428
429
430
431
432
433
434
435
436
437 public void setFeature(String name,
438 boolean state) throws XmlPullParserException
439 {
440 if(name == null) throw new IllegalArgumentException("feature name should not be null");
441 if(FEATURE_PROCESS_NAMESPACES.equals(name)) {
442 if(eventType != START_DOCUMENT) throw new XmlPullParserException(
443 "namespace processing feature can only be changed before parsing", this, null);
444 processNamespaces = state;
445
446
447
448
449 } else if(FEATURE_NAMES_INTERNED.equals(name)) {
450 if(state != false) {
451 throw new XmlPullParserException(
452 "interning names in this implementation is not supported");
453 }
454 } else if(FEATURE_PROCESS_DOCDECL.equals(name)) {
455 if(state != false) {
456 throw new XmlPullParserException(
457 "processing DOCDECL is not supported");
458 }
459
460
461 } else if(FEATURE_XML_ROUNDTRIP.equals(name)) {
462
463
464
465
466 roundtripSupported = state;
467 } else {
468 throw new XmlPullParserException("unsupported feature "+name);
469 }
470 }
471
472
473 public boolean getFeature(String name)
474 {
475 if(name == null) throw new IllegalArgumentException("feature name should not be null");
476 if(FEATURE_PROCESS_NAMESPACES.equals(name)) {
477 return processNamespaces;
478
479
480 } else if(FEATURE_NAMES_INTERNED.equals(name)) {
481 return false;
482 } else if(FEATURE_PROCESS_DOCDECL.equals(name)) {
483 return false;
484
485
486 } else if(FEATURE_XML_ROUNDTRIP.equals(name)) {
487
488 return roundtripSupported;
489 }
490 return false;
491 }
492
493 public void setProperty(String name,
494 Object value)
495 throws XmlPullParserException
496 {
497 if(PROPERTY_LOCATION.equals(name)) {
498 location = (String) value;
499 } else {
500 throw new XmlPullParserException("unsupported property: '"+name+"'");
501 }
502 }
503
504
505 public Object getProperty(String name)
506 {
507 if(name == null) throw new IllegalArgumentException("property name should not be null");
508 if(PROPERTY_XMLDECL_VERSION.equals(name)) {
509 return xmlDeclVersion;
510 } else if(PROPERTY_XMLDECL_STANDALONE.equals(name)) {
511 return xmlDeclStandalone;
512 } else if(PROPERTY_XMLDECL_CONTENT.equals(name)) {
513 return xmlDeclContent;
514 } else if(PROPERTY_LOCATION.equals(name)) {
515 return location;
516 }
517 return null;
518 }
519
520
521 public void setInput(Reader in) throws XmlPullParserException
522 {
523 reset();
524 reader = in;
525 }
526
527 public void setInput(InputStream inputStream, String inputEncoding)
528 throws XmlPullParserException
529 {
530 if(inputStream == null) {
531 throw new IllegalArgumentException("input stream can not be null");
532 }
533 Reader reader;
534
535 try {
536 if(inputEncoding != null) {
537 reader = new InputStreamReader(inputStream, inputEncoding);
538 } else {
539
540 reader = new InputStreamReader(inputStream, "UTF-8");
541 }
542 } catch (UnsupportedEncodingException une) {
543 throw new XmlPullParserException(
544 "could not create reader for encoding "+inputEncoding+" : "+une, this, une);
545 }
546
547
548
549 setInput(reader);
550
551 this.inputEncoding = inputEncoding;
552 }
553
554 public String getInputEncoding() {
555 return inputEncoding;
556 }
557
558 public void defineEntityReplacementText(String entityName,
559 String replacementText)
560 throws XmlPullParserException
561 {
562
563
564 if ( !replacementText.startsWith( "&#" ) && this.entityName != null && replacementText.length() > 1 )
565 {
566 String tmp = replacementText.substring( 1, replacementText.length() - 1 );
567 for ( int i = 0; i < this.entityName.length; i++ )
568 {
569 if ( this.entityName[i] != null && this.entityName[i].equals( tmp ) )
570 {
571 replacementText = this.entityReplacement[i];
572 }
573 }
574 }
575
576
577 ensureEntityCapacity();
578
579
580 this.entityName[entityEnd] = newString(entityName.toCharArray(), 0, entityName.length());
581 entityNameBuf[entityEnd] = entityName.toCharArray();
582
583 entityReplacement[entityEnd] = replacementText;
584 entityReplacementBuf[entityEnd] = replacementText.toCharArray();
585 if(!allStringsInterned) {
586 entityNameHash[ entityEnd ] =
587 fastHash(entityNameBuf[entityEnd], 0, entityNameBuf[entityEnd].length);
588 }
589 ++entityEnd;
590
591
592 }
593
594 public int getNamespaceCount(int depth)
595 throws XmlPullParserException
596 {
597 if(processNamespaces == false || depth == 0) {
598 return 0;
599 }
600
601
602 if(depth < 0 || depth > this.depth) throw new IllegalArgumentException(
603 "allowed namespace depth 0.."+this.depth+" not "+depth);
604 return elNamespaceCount[ depth ];
605 }
606
607 public String getNamespacePrefix(int pos)
608 throws XmlPullParserException
609 {
610
611
612
613 if(pos < namespaceEnd) {
614 return namespacePrefix[ pos ];
615 } else {
616 throw new XmlPullParserException(
617 "position "+pos+" exceeded number of available namespaces "+namespaceEnd);
618 }
619 }
620
621 public String getNamespaceUri(int pos) throws XmlPullParserException
622 {
623
624
625 if(pos < namespaceEnd) {
626 return namespaceUri[ pos ];
627 } else {
628 throw new XmlPullParserException(
629 "position "+pos+" exceeded number of available namespaces "+namespaceEnd);
630 }
631 }
632
633 public String getNamespace( String prefix )
634
635 {
636
637 if(prefix != null) {
638 for( int i = namespaceEnd -1; i >= 0; i--) {
639 if( prefix.equals( namespacePrefix[ i ] ) ) {
640 return namespaceUri[ i ];
641 }
642 }
643 if("xml".equals( prefix )) {
644 return XML_URI;
645 } else if("xmlns".equals( prefix )) {
646 return XMLNS_URI;
647 }
648 } else {
649 for( int i = namespaceEnd -1; i >= 0; i--) {
650 if( namespacePrefix[ i ] == null) {
651 return namespaceUri[ i ];
652 }
653 }
654
655 }
656 return null;
657 }
658
659
660 public int getDepth()
661 {
662 return depth;
663 }
664
665
666 private static int findFragment(int bufMinPos, char[] b, int start, int end) {
667
668 if(start < bufMinPos) {
669 start = bufMinPos;
670 if(start > end) start = end;
671 return start;
672 }
673 if(end - start > 65) {
674 start = end - 10;
675 }
676 int i = start + 1;
677 while(--i > bufMinPos) {
678 if((end - i) > 65) break;
679 final char c = b[i];
680 if(c == '<' && (start - i) > 10) break;
681 }
682 return i;
683 }
684
685
686
687
688
689
690 public String getPositionDescription ()
691 {
692 String fragment = null;
693 if(posStart <= pos) {
694 final int start = findFragment(0, buf, posStart, pos);
695
696 if(start < pos) {
697 fragment = new String(buf, start, pos - start);
698 }
699 if(bufAbsoluteStart > 0 || start > 0) fragment = "..." + fragment;
700 }
701
702
703
704 return " "+TYPES[ eventType ] +
705 (fragment != null ? " seen "+printable(fragment)+"..." : "")
706 +" "+(location != null ? location : "")
707 +"@"+getLineNumber()+":"+getColumnNumber();
708 }
709
710 public int getLineNumber()
711 {
712 return lineNumber;
713 }
714
715 public int getColumnNumber()
716 {
717 return columnNumber;
718 }
719
720
721 public boolean isWhitespace() throws XmlPullParserException
722 {
723 if(eventType == TEXT || eventType == CDSECT) {
724 if(usePC) {
725 for (int i = pcStart; i <pcEnd; i++)
726 {
727 if(!isS(pc[ i ])) return false;
728 }
729 return true;
730 } else {
731 for (int i = posStart; i <posEnd; i++)
732 {
733 if(!isS(buf[ i ])) return false;
734 }
735 return true;
736 }
737 } else if(eventType == IGNORABLE_WHITESPACE) {
738 return true;
739 }
740 throw new XmlPullParserException("no content available to check for white spaces");
741 }
742
743 public String getText()
744 {
745 if(eventType == START_DOCUMENT || eventType == END_DOCUMENT) {
746
747
748
749
750 return null;
751
752 } else if(eventType == ENTITY_REF) {
753 return text;
754 }
755 if(text == null) {
756 if(!usePC || eventType == START_TAG || eventType == END_TAG) {
757 text = new String(buf, posStart, posEnd - posStart);
758 } else {
759 text = new String(pc, pcStart, pcEnd - pcStart);
760 }
761 }
762 return text;
763 }
764
765 public char[] getTextCharacters(int [] holderForStartAndLength)
766 {
767 if( eventType == TEXT ) {
768 if(usePC) {
769 holderForStartAndLength[0] = pcStart;
770 holderForStartAndLength[1] = pcEnd - pcStart;
771 return pc;
772 } else {
773 holderForStartAndLength[0] = posStart;
774 holderForStartAndLength[1] = posEnd - posStart;
775 return buf;
776
777 }
778 } else if( eventType == START_TAG
779 || eventType == END_TAG
780 || eventType == CDSECT
781 || eventType == COMMENT
782 || eventType == ENTITY_REF
783 || eventType == PROCESSING_INSTRUCTION
784 || eventType == IGNORABLE_WHITESPACE
785 || eventType == DOCDECL)
786 {
787 holderForStartAndLength[0] = posStart;
788 holderForStartAndLength[1] = posEnd - posStart;
789 return buf;
790 } else if(eventType == START_DOCUMENT
791 || eventType == END_DOCUMENT) {
792
793 holderForStartAndLength[0] = holderForStartAndLength[1] = -1;
794 return null;
795 } else {
796 throw new IllegalArgumentException("unknown text eventType: "+eventType);
797 }
798
799
800
801
802
803
804
805
806
807 }
808
809 public String getNamespace()
810 {
811 if(eventType == START_TAG) {
812
813 return processNamespaces ? elUri[ depth ] : NO_NAMESPACE;
814 } else if(eventType == END_TAG) {
815 return processNamespaces ? elUri[ depth ] : NO_NAMESPACE;
816 }
817 return null;
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834 }
835
836 public String getName()
837 {
838 if(eventType == START_TAG) {
839
840 return elName[ depth ] ;
841 } else if(eventType == END_TAG) {
842 return elName[ depth ] ;
843 } else if(eventType == ENTITY_REF) {
844 if(entityRefName == null) {
845 entityRefName = newString(buf, posStart, posEnd - posStart);
846 }
847 return entityRefName;
848 } else {
849 return null;
850 }
851 }
852
853 public String getPrefix()
854 {
855 if(eventType == START_TAG) {
856
857 return elPrefix[ depth ] ;
858 } else if(eventType == END_TAG) {
859 return elPrefix[ depth ] ;
860 }
861 return null;
862
863
864
865 }
866
867
868 public boolean isEmptyElementTag() throws XmlPullParserException
869 {
870 if(eventType != START_TAG) throw new XmlPullParserException(
871 "parser must be on START_TAG to check for empty element", this, null);
872 return emptyElementTag;
873 }
874
875 public int getAttributeCount()
876 {
877 if(eventType != START_TAG) return -1;
878 return attributeCount;
879 }
880
881 public String getAttributeNamespace(int index)
882 {
883 if(eventType != START_TAG) throw new IndexOutOfBoundsException(
884 "only START_TAG can have attributes");
885 if(!processNamespaces) return NO_NAMESPACE;
886 if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException(
887 "attribute position must be 0.."+(attributeCount-1)+" and not "+index);
888 return attributeUri[ index ];
889 }
890
891 public String getAttributeName(int index)
892 {
893 if(eventType != START_TAG) throw new IndexOutOfBoundsException(
894 "only START_TAG can have attributes");
895 if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException(
896 "attribute position must be 0.."+(attributeCount-1)+" and not "+index);
897 return attributeName[ index ];
898 }
899
900 public String getAttributePrefix(int index)
901 {
902 if(eventType != START_TAG) throw new IndexOutOfBoundsException(
903 "only START_TAG can have attributes");
904 if(!processNamespaces) return null;
905 if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException(
906 "attribute position must be 0.."+(attributeCount-1)+" and not "+index);
907 return attributePrefix[ index ];
908 }
909
910 public String getAttributeType(int index) {
911 if(eventType != START_TAG) throw new IndexOutOfBoundsException(
912 "only START_TAG can have attributes");
913 if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException(
914 "attribute position must be 0.."+(attributeCount-1)+" and not "+index);
915 return "CDATA";
916 }
917
918 public boolean isAttributeDefault(int index) {
919 if(eventType != START_TAG) throw new IndexOutOfBoundsException(
920 "only START_TAG can have attributes");
921 if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException(
922 "attribute position must be 0.."+(attributeCount-1)+" and not "+index);
923 return false;
924 }
925
926 public String getAttributeValue(int index)
927 {
928 if(eventType != START_TAG) throw new IndexOutOfBoundsException(
929 "only START_TAG can have attributes");
930 if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException(
931 "attribute position must be 0.."+(attributeCount-1)+" and not "+index);
932 return attributeValue[ index ];
933 }
934
935 public String getAttributeValue(String namespace,
936 String name)
937 {
938 if(eventType != START_TAG) throw new IndexOutOfBoundsException(
939 "only START_TAG can have attributes"+getPositionDescription());
940 if(name == null) {
941 throw new IllegalArgumentException("attribute name can not be null");
942 }
943
944 if(processNamespaces) {
945 if(namespace == null) {
946 namespace = "";
947 }
948
949 for(int i = 0; i < attributeCount; ++i) {
950 if((namespace == attributeUri[ i ] ||
951 namespace.equals(attributeUri[ i ]) )
952
953
954 && name.equals(attributeName[ i ]) )
955 {
956 return attributeValue[i];
957 }
958 }
959 } else {
960 if(namespace != null && namespace.length() == 0) {
961 namespace = null;
962 }
963 if(namespace != null) throw new IllegalArgumentException(
964 "when namespaces processing is disabled attribute namespace must be null");
965 for(int i = 0; i < attributeCount; ++i) {
966 if(name.equals(attributeName[i]))
967 {
968 return attributeValue[i];
969 }
970 }
971 }
972 return null;
973 }
974
975
976 public int getEventType()
977 throws XmlPullParserException
978 {
979 return eventType;
980 }
981
982 public void require(int type, String namespace, String name)
983 throws XmlPullParserException, IOException
984 {
985 if(!processNamespaces && namespace != null) {
986 throw new XmlPullParserException(
987 "processing namespaces must be enabled on parser (or factory)"+
988 " to have possible namespaces declared on elements"
989 +(" (position:"+ getPositionDescription())+")");
990 }
991 if (type != getEventType()
992 || (namespace != null && !namespace.equals (getNamespace()))
993 || (name != null && !name.equals (getName ())) )
994 {
995 throw new XmlPullParserException (
996 "expected event "+TYPES[ type ]
997 +(name != null ? " with name '"+name+"'" : "")
998 +(namespace != null && name != null ? " and" : "")
999 +(namespace != null ? " with namespace '"+namespace+"'" : "")
1000 +" but got"
1001 +(type != getEventType() ? " "+TYPES[ getEventType() ] : "")
1002 +(name != null && getName() != null && !name.equals (getName ())
1003 ? " name '"+getName()+"'" : "")
1004 +(namespace != null && name != null
1005 && getName() != null && !name.equals (getName ())
1006 && getNamespace() != null && !namespace.equals (getNamespace())
1007 ? " and" : "")
1008 +(namespace != null && getNamespace() != null && !namespace.equals (getNamespace())
1009 ? " namespace '"+getNamespace()+"'" : "")
1010 +(" (position:"+ getPositionDescription())+")");
1011 }
1012 }
1013
1014
1015
1016
1017
1018
1019
1020 public void skipSubTree()
1021 throws XmlPullParserException, IOException
1022 {
1023 require(START_TAG, null, null);
1024 int level = 1;
1025 while(level > 0) {
1026 int eventType = next();
1027 if(eventType == END_TAG) {
1028 --level;
1029 } else if(eventType == START_TAG) {
1030 ++level;
1031 }
1032 }
1033 }
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043 public String nextText() throws XmlPullParserException, IOException
1044 {
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065 if(getEventType() != START_TAG) {
1066 throw new XmlPullParserException(
1067 "parser must be on START_TAG to read next text", this, null);
1068 }
1069 int eventType = next();
1070 if(eventType == TEXT) {
1071 final String result = getText();
1072 eventType = next();
1073 if(eventType != END_TAG) {
1074 throw new XmlPullParserException(
1075 "TEXT must be immediately followed by END_TAG and not "
1076 +TYPES[ getEventType() ], this, null);
1077 }
1078 return result;
1079 } else if(eventType == END_TAG) {
1080 return "";
1081 } else {
1082 throw new XmlPullParserException(
1083 "parser must be on START_TAG or TEXT to read text", this, null);
1084 }
1085 }
1086
1087 public int nextTag() throws XmlPullParserException, IOException
1088 {
1089 next();
1090 if(eventType == TEXT && isWhitespace()) {
1091 next();
1092 }
1093 if (eventType != START_TAG && eventType != END_TAG) {
1094 throw new XmlPullParserException("expected START_TAG or END_TAG not "
1095 +TYPES[ getEventType() ], this, null);
1096 }
1097 return eventType;
1098 }
1099
1100 public int next()
1101 throws XmlPullParserException, IOException
1102 {
1103 tokenize = false;
1104 return nextImpl();
1105 }
1106
1107 public int nextToken()
1108 throws XmlPullParserException, IOException
1109 {
1110 tokenize = true;
1111 return nextImpl();
1112 }
1113
1114
1115 private int nextImpl()
1116 throws XmlPullParserException, IOException
1117 {
1118 text = null;
1119 pcEnd = pcStart = 0;
1120 usePC = false;
1121 bufStart = posEnd;
1122 if(pastEndTag) {
1123 pastEndTag = false;
1124 --depth;
1125 namespaceEnd = elNamespaceCount[ depth ];
1126 }
1127 if(emptyElementTag) {
1128 emptyElementTag = false;
1129 pastEndTag = true;
1130 return eventType = END_TAG;
1131 }
1132
1133
1134 if(depth > 0) {
1135
1136 if(seenStartTag) {
1137 seenStartTag = false;
1138 return eventType = parseStartTag();
1139 }
1140 if(seenEndTag) {
1141 seenEndTag = false;
1142 return eventType = parseEndTag();
1143 }
1144
1145
1146
1147 char ch;
1148 if(seenMarkup) {
1149 seenMarkup = false;
1150 ch = '<';
1151 } else if(seenAmpersand) {
1152 seenAmpersand = false;
1153 ch = '&';
1154 } else {
1155 ch = more();
1156 }
1157 posStart = pos - 1;
1158
1159
1160 boolean hadCharData = false;
1161
1162
1163 boolean needsMerging = false;
1164
1165 MAIN_LOOP:
1166 while(true) {
1167
1168 if(ch == '<') {
1169 if(hadCharData) {
1170
1171 if(tokenize) {
1172 seenMarkup = true;
1173 return eventType = TEXT;
1174 }
1175 }
1176 ch = more();
1177 if(ch == '/') {
1178 if(!tokenize && hadCharData) {
1179 seenEndTag = true;
1180
1181 return eventType = TEXT;
1182 }
1183 return eventType = parseEndTag();
1184 } else if(ch == '!') {
1185 ch = more();
1186 if(ch == '-') {
1187
1188 parseComment();
1189 if(tokenize) return eventType = COMMENT;
1190 if( !usePC && hadCharData ) {
1191 needsMerging = true;
1192 } else {
1193 posStart = pos;
1194 }
1195 } else if(ch == '[') {
1196
1197
1198
1199
1200 parseCDSect(hadCharData);
1201 if(tokenize) return eventType = CDSECT;
1202 final int cdStart = posStart;
1203 final int cdEnd = posEnd;
1204 final int cdLen = cdEnd - cdStart;
1205
1206
1207 if(cdLen > 0) {
1208 hadCharData = true;
1209 if(!usePC) {
1210 needsMerging = true;
1211 }
1212 }
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251 } else {
1252 throw new XmlPullParserException(
1253 "unexpected character in markup "+printable(ch), this, null);
1254 }
1255 } else if(ch == '?') {
1256 parsePI();
1257 if(tokenize) return eventType = PROCESSING_INSTRUCTION;
1258 if( !usePC && hadCharData ) {
1259 needsMerging = true;
1260 } else {
1261 posStart = pos;
1262 }
1263
1264 } else if( isNameStartChar(ch) ) {
1265 if(!tokenize && hadCharData) {
1266 seenStartTag = true;
1267
1268 return eventType = TEXT;
1269 }
1270 return eventType = parseStartTag();
1271 } else {
1272 throw new XmlPullParserException(
1273 "unexpected character in markup "+printable(ch), this, null);
1274 }
1275
1276
1277 } else if(ch == '&') {
1278
1279
1280 if(tokenize && hadCharData) {
1281 seenAmpersand = true;
1282 return eventType = TEXT;
1283 }
1284 final int oldStart = posStart + bufAbsoluteStart;
1285 final int oldEnd = posEnd + bufAbsoluteStart;
1286 final char[] resolvedEntity = parseEntityRef();
1287 if(tokenize) return eventType = ENTITY_REF;
1288
1289 if(resolvedEntity == null) {
1290 if(entityRefName == null) {
1291 entityRefName = newString(buf, posStart, posEnd - posStart);
1292 }
1293 throw new XmlPullParserException(
1294 "could not resolve entity named '"+printable(entityRefName)+"'",
1295 this, null);
1296 }
1297
1298
1299 posStart = oldStart - bufAbsoluteStart;
1300 posEnd = oldEnd - bufAbsoluteStart;
1301 if(!usePC) {
1302 if(hadCharData) {
1303 joinPC();
1304 needsMerging = false;
1305 } else {
1306 usePC = true;
1307 pcStart = pcEnd = 0;
1308 }
1309 }
1310
1311
1312 for (int i = 0; i < resolvedEntity.length; i++)
1313 {
1314 if(pcEnd >= pc.length) ensurePC(pcEnd);
1315 pc[pcEnd++] = resolvedEntity[ i ];
1316
1317 }
1318 hadCharData = true;
1319
1320 } else {
1321
1322 if(needsMerging) {
1323
1324 joinPC();
1325
1326 needsMerging = false;
1327 }
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337 hadCharData = true;
1338
1339 boolean normalizedCR = false;
1340 final boolean normalizeInput = !tokenize || !roundtripSupported;
1341
1342 boolean seenBracket = false;
1343 boolean seenBracketBracket = false;
1344 do {
1345
1346
1347 if(ch == ']') {
1348 if(seenBracket) {
1349 seenBracketBracket = true;
1350 } else {
1351 seenBracket = true;
1352 }
1353 } else if(seenBracketBracket && ch == '>') {
1354 throw new XmlPullParserException(
1355 "characters ]]> are not allowed in content", this, null);
1356 } else {
1357 if(seenBracket) {
1358 seenBracketBracket = seenBracket = false;
1359 }
1360
1361 }
1362 if(normalizeInput) {
1363
1364 if(ch == '\r') {
1365 normalizedCR = true;
1366 posEnd = pos -1;
1367
1368 if(!usePC) {
1369 if(posEnd > posStart) {
1370 joinPC();
1371 } else {
1372 usePC = true;
1373 pcStart = pcEnd = 0;
1374 }
1375 }
1376
1377 if(pcEnd >= pc.length) ensurePC(pcEnd);
1378 pc[pcEnd++] = '\n';
1379 } else if(ch == '\n') {
1380
1381 if(!normalizedCR && usePC) {
1382 if(pcEnd >= pc.length) ensurePC(pcEnd);
1383 pc[pcEnd++] = '\n';
1384 }
1385 normalizedCR = false;
1386 } else {
1387 if(usePC) {
1388 if(pcEnd >= pc.length) ensurePC(pcEnd);
1389 pc[pcEnd++] = ch;
1390 }
1391 normalizedCR = false;
1392 }
1393 }
1394
1395 ch = more();
1396 } while(ch != '<' && ch != '&');
1397 posEnd = pos - 1;
1398 continue MAIN_LOOP;
1399 }
1400 ch = more();
1401 }
1402 } else {
1403 if(seenRoot) {
1404 return parseEpilog();
1405 } else {
1406 return parseProlog();
1407 }
1408 }
1409 }
1410
1411
1412 private int parseProlog()
1413 throws XmlPullParserException, IOException
1414 {
1415
1416
1417 char ch;
1418 if(seenMarkup) {
1419 ch = buf[ pos - 1 ];
1420 } else {
1421 ch = more();
1422 }
1423
1424 if(eventType == START_DOCUMENT) {
1425
1426
1427
1428 if(ch == '\uFFFE') {
1429 throw new XmlPullParserException(
1430 "first character in input was UNICODE noncharacter (0xFFFE)"+
1431 "- input requires int swapping", this, null);
1432 }
1433 if(ch == '\uFEFF') {
1434
1435 ch = more();
1436 }
1437 }
1438 seenMarkup = false;
1439 boolean gotS = false;
1440 posStart = pos - 1;
1441 final boolean normalizeIgnorableWS = tokenize && !roundtripSupported;
1442 boolean normalizedCR = false;
1443 while(true) {
1444
1445
1446
1447
1448 if(ch == '<') {
1449 if(gotS && tokenize) {
1450 posEnd = pos - 1;
1451 seenMarkup = true;
1452 return eventType = IGNORABLE_WHITESPACE;
1453 }
1454 ch = more();
1455 if(ch == '?') {
1456
1457
1458 boolean isXMLDecl = parsePI();
1459 if (tokenize) {
1460 if (isXMLDecl) {
1461 return eventType = START_DOCUMENT;
1462 }
1463 return eventType = PROCESSING_INSTRUCTION;
1464 }
1465 } else if(ch == '!') {
1466 ch = more();
1467 if(ch == 'D') {
1468 if(seenDocdecl) {
1469 throw new XmlPullParserException(
1470 "only one docdecl allowed in XML document", this, null);
1471 }
1472 seenDocdecl = true;
1473 parseDocdecl();
1474 if(tokenize) return eventType = DOCDECL;
1475 } else if(ch == '-') {
1476 parseComment();
1477 if(tokenize) return eventType = COMMENT;
1478 } else {
1479 throw new XmlPullParserException(
1480 "unexpected markup <!"+printable(ch), this, null);
1481 }
1482 } else if(ch == '/') {
1483 throw new XmlPullParserException(
1484 "expected start tag name and not "+printable(ch), this, null);
1485 } else if(isNameStartChar(ch)) {
1486 seenRoot = true;
1487 return parseStartTag();
1488 } else {
1489 throw new XmlPullParserException(
1490 "expected start tag name and not "+printable(ch), this, null);
1491 }
1492 } else if(isS(ch)) {
1493 gotS = true;
1494 if(normalizeIgnorableWS) {
1495 if(ch == '\r') {
1496 normalizedCR = true;
1497
1498
1499
1500 if(!usePC) {
1501 posEnd = pos -1;
1502 if(posEnd > posStart) {
1503 joinPC();
1504 } else {
1505 usePC = true;
1506 pcStart = pcEnd = 0;
1507 }
1508 }
1509
1510 if(pcEnd >= pc.length) ensurePC(pcEnd);
1511 pc[pcEnd++] = '\n';
1512 } else if(ch == '\n') {
1513 if(!normalizedCR && usePC) {
1514 if(pcEnd >= pc.length) ensurePC(pcEnd);
1515 pc[pcEnd++] = '\n';
1516 }
1517 normalizedCR = false;
1518 } else {
1519 if(usePC) {
1520 if(pcEnd >= pc.length) ensurePC(pcEnd);
1521 pc[pcEnd++] = ch;
1522 }
1523 normalizedCR = false;
1524 }
1525 }
1526 } else {
1527 throw new XmlPullParserException(
1528 "only whitespace content allowed before start tag and not "+printable(ch),
1529 this, null);
1530 }
1531 ch = more();
1532 }
1533 }
1534
1535 private int parseEpilog()
1536 throws XmlPullParserException, IOException
1537 {
1538 if(eventType == END_DOCUMENT) {
1539 throw new XmlPullParserException("already reached end of XML input", this, null);
1540 }
1541 if(reachedEnd) {
1542 return eventType = END_DOCUMENT;
1543 }
1544 boolean gotS = false;
1545 final boolean normalizeIgnorableWS = tokenize && !roundtripSupported;
1546 boolean normalizedCR = false;
1547 try {
1548
1549 char ch;
1550 if(seenMarkup) {
1551 ch = buf[ pos - 1 ];
1552 } else {
1553 ch = more();
1554 }
1555 seenMarkup = false;
1556 posStart = pos - 1;
1557 if(!reachedEnd) {
1558 while(true) {
1559
1560
1561 if(ch == '<') {
1562 if(gotS && tokenize) {
1563 posEnd = pos - 1;
1564 seenMarkup = true;
1565 return eventType = IGNORABLE_WHITESPACE;
1566 }
1567 ch = more();
1568 if(reachedEnd) {
1569 break;
1570 }
1571 if(ch == '?') {
1572
1573
1574 parsePI();
1575 if(tokenize) return eventType = PROCESSING_INSTRUCTION;
1576
1577 } else if(ch == '!') {
1578 ch = more();
1579 if(reachedEnd) {
1580 break;
1581 }
1582 if(ch == 'D') {
1583 parseDocdecl();
1584 if(tokenize) return eventType = DOCDECL;
1585 } else if(ch == '-') {
1586 parseComment();
1587 if(tokenize) return eventType = COMMENT;
1588 } else {
1589 throw new XmlPullParserException(
1590 "unexpected markup <!"+printable(ch), this, null);
1591 }
1592 } else if(ch == '/') {
1593 throw new XmlPullParserException(
1594 "end tag not allowed in epilog but got "+printable(ch), this, null);
1595 } else if(isNameStartChar(ch)) {
1596 throw new XmlPullParserException(
1597 "start tag not allowed in epilog but got "+printable(ch), this, null);
1598 } else {
1599 throw new XmlPullParserException(
1600 "in epilog expected ignorable content and not "+printable(ch),
1601 this, null);
1602 }
1603 } else if(isS(ch)) {
1604 gotS = true;
1605 if(normalizeIgnorableWS) {
1606 if(ch == '\r') {
1607 normalizedCR = true;
1608
1609
1610
1611 if(!usePC) {
1612 posEnd = pos -1;
1613 if(posEnd > posStart) {
1614 joinPC();
1615 } else {
1616 usePC = true;
1617 pcStart = pcEnd = 0;
1618 }
1619 }
1620
1621 if(pcEnd >= pc.length) ensurePC(pcEnd);
1622 pc[pcEnd++] = '\n';
1623 } else if(ch == '\n') {
1624 if(!normalizedCR && usePC) {
1625 if(pcEnd >= pc.length) ensurePC(pcEnd);
1626 pc[pcEnd++] = '\n';
1627 }
1628 normalizedCR = false;
1629 } else {
1630 if(usePC) {
1631 if(pcEnd >= pc.length) ensurePC(pcEnd);
1632 pc[pcEnd++] = ch;
1633 }
1634 normalizedCR = false;
1635 }
1636 }
1637 } else {
1638 throw new XmlPullParserException(
1639 "in epilog non whitespace content is not allowed but got "+printable(ch),
1640 this, null);
1641 }
1642 ch = more();
1643 if(reachedEnd) {
1644 break;
1645 }
1646
1647 }
1648 }
1649
1650
1651
1652
1653 } catch(EOFException ex) {
1654 reachedEnd = true;
1655 }
1656 if(tokenize && gotS) {
1657 posEnd = pos;
1658 return eventType = IGNORABLE_WHITESPACE;
1659 }
1660 return eventType = END_DOCUMENT;
1661 }
1662
1663
1664 public int parseEndTag() throws XmlPullParserException, IOException {
1665
1666
1667 char ch = more();
1668 if(!isNameStartChar(ch)) {
1669 throw new XmlPullParserException(
1670 "expected name start and not "+printable(ch), this, null);
1671 }
1672 posStart = pos - 3;
1673 final int nameStart = pos - 1 + bufAbsoluteStart;
1674 do {
1675 ch = more();
1676 } while(isNameChar(ch));
1677
1678
1679
1680
1681
1682
1683
1684
1685 int off = nameStart - bufAbsoluteStart;
1686
1687 final int len = (pos - 1) - off;
1688 final char[] cbuf = elRawName[depth];
1689 if(elRawNameEnd[depth] != len) {
1690
1691 final String startname = new String(cbuf, 0, elRawNameEnd[depth]);
1692 final String endname = new String(buf, off, len);
1693 throw new XmlPullParserException(
1694 "end tag name </"+endname+"> must match start tag name <"+startname+">"
1695 +" from line "+elRawNameLine[depth], this, null);
1696 }
1697 for (int i = 0; i < len; i++)
1698 {
1699 if(buf[off++] != cbuf[i]) {
1700
1701 final String startname = new String(cbuf, 0, len);
1702 final String endname = new String(buf, off - i - 1, len);
1703 throw new XmlPullParserException(
1704 "end tag name </"+endname+"> must be the same as start tag <"+startname+">"
1705 +" from line "+elRawNameLine[depth], this, null);
1706 }
1707 }
1708
1709 while(isS(ch)) { ch = more(); }
1710 if(ch != '>') {
1711 throw new XmlPullParserException(
1712 "expected > to finish end tag not "+printable(ch)
1713 +" from line "+elRawNameLine[depth], this, null);
1714 }
1715
1716
1717
1718
1719 posEnd = pos;
1720 pastEndTag = true;
1721 return eventType = END_TAG;
1722 }
1723
1724 public int parseStartTag() throws XmlPullParserException, IOException {
1725
1726
1727
1728 ++depth;
1729
1730 posStart = pos - 2;
1731
1732 emptyElementTag = false;
1733 attributeCount = 0;
1734
1735 final int nameStart = pos - 1 + bufAbsoluteStart;
1736 int colonPos = -1;
1737 char ch = buf[ pos - 1];
1738 if(ch == ':' && processNamespaces) throw new XmlPullParserException(
1739 "when namespaces processing enabled colon can not be at element name start",
1740 this, null);
1741 while(true) {
1742 ch = more();
1743 if(!isNameChar(ch)) break;
1744 if(ch == ':' && processNamespaces) {
1745 if(colonPos != -1) throw new XmlPullParserException(
1746 "only one colon is allowed in name of element when namespaces are enabled",
1747 this, null);
1748 colonPos = pos - 1 + bufAbsoluteStart;
1749 }
1750 }
1751
1752
1753 ensureElementsCapacity();
1754
1755
1756
1757
1758 int elLen = (pos - 1) - (nameStart - bufAbsoluteStart);
1759 if(elRawName[ depth ] == null || elRawName[ depth ].length < elLen) {
1760 elRawName[ depth ] = new char[ 2 * elLen ];
1761 }
1762 System.arraycopy(buf, nameStart - bufAbsoluteStart, elRawName[ depth ], 0, elLen);
1763 elRawNameEnd[ depth ] = elLen;
1764 elRawNameLine[ depth ] = lineNumber;
1765
1766 String name = null;
1767
1768
1769 String prefix = null;
1770 if(processNamespaces) {
1771 if(colonPos != -1) {
1772 prefix = elPrefix[ depth ] = newString(buf, nameStart - bufAbsoluteStart,
1773 colonPos - nameStart);
1774 name = elName[ depth ] = newString(buf, colonPos + 1 - bufAbsoluteStart,
1775
1776 pos - 2 - (colonPos - bufAbsoluteStart));
1777 } else {
1778 prefix = elPrefix[ depth ] = null;
1779 name = elName[ depth ] = newString(buf, nameStart - bufAbsoluteStart, elLen);
1780 }
1781 } else {
1782
1783 name = elName[ depth ] = newString(buf, nameStart - bufAbsoluteStart, elLen);
1784
1785 }
1786
1787
1788 while(true) {
1789
1790 while(isS(ch)) { ch = more(); }
1791
1792 if(ch == '>') {
1793 break;
1794 } else if(ch == '/') {
1795 if(emptyElementTag) throw new XmlPullParserException(
1796 "repeated / in tag declaration", this, null);
1797 emptyElementTag = true;
1798 ch = more();
1799 if(ch != '>') throw new XmlPullParserException(
1800 "expected > to end empty tag not "+printable(ch), this, null);
1801 break;
1802 } else if(isNameStartChar(ch)) {
1803 ch = parseAttribute();
1804 ch = more();
1805 } else {
1806 throw new XmlPullParserException(
1807 "start tag unexpected character "+printable(ch), this, null);
1808 }
1809
1810 }
1811
1812
1813 if(processNamespaces) {
1814 String uri = getNamespace(prefix);
1815 if(uri == null) {
1816 if(prefix == null) {
1817 uri = NO_NAMESPACE;
1818 } else {
1819 throw new XmlPullParserException(
1820 "could not determine namespace bound to element prefix "+prefix,
1821 this, null);
1822 }
1823
1824 }
1825 elUri[ depth ] = uri;
1826
1827
1828
1829
1830
1831
1832
1833 for (int i = 0; i < attributeCount; i++)
1834 {
1835 final String attrPrefix = attributePrefix[ i ];
1836 if(attrPrefix != null) {
1837 final String attrUri = getNamespace(attrPrefix);
1838 if(attrUri == null) {
1839 throw new XmlPullParserException(
1840 "could not determine namespace bound to attribute prefix "+attrPrefix,
1841 this, null);
1842
1843 }
1844 attributeUri[ i ] = attrUri;
1845 } else {
1846 attributeUri[ i ] = NO_NAMESPACE;
1847 }
1848 }
1849
1850
1851
1852
1853
1854 for (int i = 1; i < attributeCount; i++)
1855 {
1856 for (int j = 0; j < i; j++)
1857 {
1858 if( attributeUri[j] == attributeUri[i]
1859 && (allStringsInterned && attributeName[j].equals(attributeName[i])
1860 || (!allStringsInterned
1861 && attributeNameHash[ j ] == attributeNameHash[ i ]
1862 && attributeName[j].equals(attributeName[i])) )
1863
1864 ) {
1865
1866 String attr1 = attributeName[j];
1867 if(attributeUri[j] != null) attr1 = attributeUri[j]+":"+attr1;
1868 String attr2 = attributeName[i];
1869 if(attributeUri[i] != null) attr2 = attributeUri[i]+":"+attr2;
1870 throw new XmlPullParserException(
1871 "duplicated attributes "+attr1+" and "+attr2, this, null);
1872 }
1873 }
1874 }
1875
1876
1877 } else {
1878
1879
1880
1881 for (int i = 1; i < attributeCount; i++)
1882 {
1883 for (int j = 0; j < i; j++)
1884 {
1885 if((allStringsInterned && attributeName[j].equals(attributeName[i])
1886 || (!allStringsInterned
1887 && attributeNameHash[ j ] == attributeNameHash[ i ]
1888 && attributeName[j].equals(attributeName[i])) )
1889
1890 ) {
1891
1892 final String attr1 = attributeName[j];
1893 final String attr2 = attributeName[i];
1894 throw new XmlPullParserException(
1895 "duplicated attributes "+attr1+" and "+attr2, this, null);
1896 }
1897 }
1898 }
1899 }
1900
1901 elNamespaceCount[ depth ] = namespaceEnd;
1902 posEnd = pos;
1903 return eventType = START_TAG;
1904 }
1905
1906 private char parseAttribute() throws XmlPullParserException, IOException
1907 {
1908
1909
1910
1911
1912 final int prevPosStart = posStart + bufAbsoluteStart;
1913 final int nameStart = pos - 1 + bufAbsoluteStart;
1914 int colonPos = -1;
1915 char ch = buf[ pos - 1 ];
1916 if(ch == ':' && processNamespaces) throw new XmlPullParserException(
1917 "when namespaces processing enabled colon can not be at attribute name start",
1918 this, null);
1919
1920
1921 boolean startsWithXmlns = processNamespaces && ch == 'x';
1922 int xmlnsPos = 0;
1923
1924 ch = more();
1925 while(isNameChar(ch)) {
1926 if(processNamespaces) {
1927 if(startsWithXmlns && xmlnsPos < 5) {
1928 ++xmlnsPos;
1929 if(xmlnsPos == 1) { if(ch != 'm') startsWithXmlns = false; }
1930 else if(xmlnsPos == 2) { if(ch != 'l') startsWithXmlns = false; }
1931 else if(xmlnsPos == 3) { if(ch != 'n') startsWithXmlns = false; }
1932 else if(xmlnsPos == 4) { if(ch != 's') startsWithXmlns = false; }
1933 else if(xmlnsPos == 5) {
1934 if(ch != ':') throw new XmlPullParserException(
1935 "after xmlns in attribute name must be colon"
1936 +"when namespaces are enabled", this, null);
1937
1938 }
1939 }
1940 if(ch == ':') {
1941 if(colonPos != -1) throw new XmlPullParserException(
1942 "only one colon is allowed in attribute name"
1943 +" when namespaces are enabled", this, null);
1944 colonPos = pos - 1 + bufAbsoluteStart;
1945 }
1946 }
1947 ch = more();
1948 }
1949
1950 ensureAttributesCapacity(attributeCount);
1951
1952
1953 String name = null;
1954 String prefix = null;
1955
1956 if(processNamespaces) {
1957 if(xmlnsPos < 4) startsWithXmlns = false;
1958 if(startsWithXmlns) {
1959 if(colonPos != -1) {
1960
1961 final int nameLen = pos - 2 - (colonPos - bufAbsoluteStart);
1962 if(nameLen == 0) {
1963 throw new XmlPullParserException(
1964 "namespace prefix is required after xmlns: "
1965 +" when namespaces are enabled", this, null);
1966 }
1967 name =
1968 newString(buf, colonPos - bufAbsoluteStart + 1, nameLen);
1969
1970 }
1971 } else {
1972 if(colonPos != -1) {
1973 int prefixLen = colonPos - nameStart;
1974 prefix = attributePrefix[ attributeCount ] =
1975 newString(buf, nameStart - bufAbsoluteStart,prefixLen);
1976
1977 int nameLen = pos - 2 - (colonPos - bufAbsoluteStart);
1978 name = attributeName[ attributeCount ] =
1979 newString(buf, colonPos - bufAbsoluteStart + 1, nameLen);
1980
1981
1982
1983 } else {
1984 prefix = attributePrefix[ attributeCount ] = null;
1985 name = attributeName[ attributeCount ] =
1986 newString(buf, nameStart - bufAbsoluteStart,
1987 pos - 1 - (nameStart - bufAbsoluteStart));
1988 }
1989 if(!allStringsInterned) {
1990 attributeNameHash[ attributeCount ] = name.hashCode();
1991 }
1992 }
1993
1994 } else {
1995
1996 name = attributeName[ attributeCount ] =
1997 newString(buf, nameStart - bufAbsoluteStart,
1998 pos - 1 - (nameStart - bufAbsoluteStart));
1999
2000 if(!allStringsInterned) {
2001 attributeNameHash[ attributeCount ] = name.hashCode();
2002 }
2003 }
2004
2005
2006 while(isS(ch)) { ch = more(); }
2007 if(ch != '=') throw new XmlPullParserException(
2008 "expected = after attribute name", this, null);
2009 ch = more();
2010 while(isS(ch)) { ch = more(); }
2011
2012
2013
2014 final char delimit = ch;
2015 if(delimit != '"' && delimit != '\'') throw new XmlPullParserException(
2016 "attribute value must start with quotation or apostrophe not "
2017 +printable(delimit), this, null);
2018
2019
2020
2021
2022
2023 boolean normalizedCR = false;
2024 usePC = false;
2025 pcStart = pcEnd;
2026 posStart = pos;
2027
2028 while(true) {
2029 ch = more();
2030 if(ch == delimit) {
2031 break;
2032 } if(ch == '<') {
2033 throw new XmlPullParserException(
2034 "markup not allowed inside attribute value - illegal < ", this, null);
2035 } if(ch == '&') {
2036
2037 posEnd = pos - 1;
2038 if(!usePC) {
2039 final boolean hadCharData = posEnd > posStart;
2040 if(hadCharData) {
2041
2042 joinPC();
2043 } else {
2044 usePC = true;
2045 pcStart = pcEnd = 0;
2046 }
2047 }
2048
2049
2050 final char[] resolvedEntity = parseEntityRef();
2051
2052 if(resolvedEntity == null) {
2053 if(entityRefName == null) {
2054 entityRefName = newString(buf, posStart, posEnd - posStart);
2055 }
2056 throw new XmlPullParserException(
2057 "could not resolve entity named '"+printable(entityRefName)+"'",
2058 this, null);
2059 }
2060
2061 for (int i = 0; i < resolvedEntity.length; i++)
2062 {
2063 if(pcEnd >= pc.length) ensurePC(pcEnd);
2064 pc[pcEnd++] = resolvedEntity[ i ];
2065 }
2066 } else if(ch == '\t' || ch == '\n' || ch == '\r') {
2067
2068
2069
2070
2071 if(!usePC) {
2072 posEnd = pos - 1;
2073 if(posEnd > posStart) {
2074 joinPC();
2075 } else {
2076 usePC = true;
2077 pcEnd = pcStart = 0;
2078 }
2079 }
2080
2081 if(pcEnd >= pc.length) ensurePC(pcEnd);
2082 if(ch != '\n' || !normalizedCR) {
2083 pc[pcEnd++] = ' ';
2084 }
2085
2086 } else {
2087 if(usePC) {
2088 if(pcEnd >= pc.length) ensurePC(pcEnd);
2089 pc[pcEnd++] = ch;
2090 }
2091 }
2092 normalizedCR = ch == '\r';
2093 }
2094
2095
2096 if(processNamespaces && startsWithXmlns) {
2097 String ns = null;
2098 if(!usePC) {
2099 ns = newStringIntern(buf, posStart, pos - 1 - posStart);
2100 } else {
2101 ns = newStringIntern(pc, pcStart, pcEnd - pcStart);
2102 }
2103 ensureNamespacesCapacity(namespaceEnd);
2104 int prefixHash = -1;
2105 if(colonPos != -1) {
2106 if(ns.length() == 0) {
2107 throw new XmlPullParserException(
2108 "non-default namespace can not be declared to be empty string", this, null);
2109 }
2110
2111 namespacePrefix[ namespaceEnd ] = name;
2112 if(!allStringsInterned) {
2113 prefixHash = namespacePrefixHash[ namespaceEnd ] = name.hashCode();
2114 }
2115 } else {
2116
2117 namespacePrefix[ namespaceEnd ] = null;
2118 if(!allStringsInterned) {
2119 prefixHash = namespacePrefixHash[ namespaceEnd ] = -1;
2120 }
2121 }
2122 namespaceUri[ namespaceEnd ] = ns;
2123
2124
2125 final int startNs = elNamespaceCount[ depth - 1 ];
2126 for (int i = namespaceEnd - 1; i >= startNs; --i)
2127 {
2128 if(((allStringsInterned || name == null) && namespacePrefix[ i ] == name)
2129 || (!allStringsInterned && name != null &&
2130 namespacePrefixHash[ i ] == prefixHash
2131 && name.equals(namespacePrefix[ i ])
2132 ))
2133 {
2134 final String s = name == null ? "default" : "'"+name+"'";
2135 throw new XmlPullParserException(
2136 "duplicated namespace declaration for "+s+" prefix", this, null);
2137 }
2138 }
2139
2140 ++namespaceEnd;
2141
2142 } else {
2143 if(!usePC) {
2144 attributeValue[ attributeCount ] =
2145 new String(buf, posStart, pos - 1 - posStart);
2146 } else {
2147 attributeValue[ attributeCount ] =
2148 new String(pc, pcStart, pcEnd - pcStart);
2149 }
2150 ++attributeCount;
2151 }
2152 posStart = prevPosStart - bufAbsoluteStart;
2153 return ch;
2154 }
2155
2156 final private char[] charRefOneCharBuf = new char[1];
2157
2158 private char[] parseEntityRef()
2159 throws XmlPullParserException, IOException
2160 {
2161
2162
2163
2164
2165 entityRefName = null;
2166 posStart = pos;
2167 char ch = more();
2168 if(ch == '#') {
2169
2170 char charRef = 0;
2171 ch = more();
2172 StringBuffer sb = new StringBuffer();
2173 boolean isHex = (ch == 'x');
2174 if (isHex) {
2175
2176 while(true) {
2177 ch = more();
2178 if(ch >= '0' && ch <= '9') {
2179 charRef = (char)(charRef * 16 + (ch - '0'));
2180 sb.append(ch);
2181 } else if(ch >= 'a' && ch <= 'f') {
2182 charRef = (char)(charRef * 16 + (ch - ('a' - 10)));
2183 sb.append(ch);
2184 } else if(ch >= 'A' && ch <= 'F') {
2185 charRef = (char)(charRef * 16 + (ch - ('A' - 10)));
2186 sb.append(ch);
2187 } else if(ch == ';') {
2188 break;
2189 } else {
2190 throw new XmlPullParserException(
2191 "character reference (with hex value) may not contain "
2192 +printable(ch), this, null);
2193 }
2194 }
2195 } else {
2196
2197 while(true) {
2198 if(ch >= '0' && ch <= '9') {
2199 charRef = (char)(charRef * 10 + (ch - '0'));
2200 sb.append(ch);
2201 } else if(ch == ';') {
2202 break;
2203 } else {
2204 throw new XmlPullParserException(
2205 "character reference (with decimal value) may not contain "
2206 +printable(ch), this, null);
2207 }
2208 ch = more();
2209 }
2210 }
2211 posEnd = pos - 1;
2212 char[] result = null;
2213 if (!noUnicode4) {
2214 try {
2215 result = Character.toChars(Integer.parseInt(sb.toString(), isHex ? 16 : 10));
2216 } catch (IllegalArgumentException e) {
2217 throw new XmlPullParserException("character reference (with "
2218 + (isHex ? "hex" : "decimal")
2219 + " value "
2220 + sb.toString()
2221 + ") is invalid", this, null);
2222 } catch(NoSuchMethodError e) {
2223 noUnicode4 = true;
2224 }
2225 }
2226 if (noUnicode4) {
2227 int i = Integer.parseInt(sb.toString(), isHex ? 16 : 10);
2228 if (i > Character.MAX_VALUE) {
2229 throw new XmlPullParserException("Unicode character reference (with "
2230 + (isHex ? "hex" : "decimal")
2231 + " value "
2232 + sb.toString()
2233 + ") is not supported in this runtime", this, null);
2234 }
2235 result = charRefOneCharBuf;
2236 charRefOneCharBuf[0] = (char)i;
2237 }
2238 if(tokenize) {
2239 text = newString(result, 0, result.length);
2240 }
2241 return result;
2242 } else {
2243
2244
2245 if(!isNameStartChar(ch)) {
2246 throw new XmlPullParserException(
2247 "entity reference names can not start with character '"
2248 +printable(ch)+"'", this, null);
2249 }
2250 while(true) {
2251 ch = more();
2252 if(ch == ';') {
2253 break;
2254 }
2255 if(!isNameChar(ch)) {
2256 throw new XmlPullParserException(
2257 "entity reference name can not contain character "
2258 +printable(ch)+"'", this, null);
2259 }
2260 }
2261 posEnd = pos - 1;
2262
2263 final int len = posEnd - posStart;
2264 if(len == 2 && buf[posStart] == 'l' && buf[posStart+1] == 't') {
2265 if(tokenize) {
2266 text = "<";
2267 }
2268 charRefOneCharBuf[0] = '<';
2269 return charRefOneCharBuf;
2270
2271
2272
2273
2274 } else if(len == 3 && buf[posStart] == 'a'
2275 && buf[posStart+1] == 'm' && buf[posStart+2] == 'p') {
2276 if(tokenize) {
2277 text = "&";
2278 }
2279 charRefOneCharBuf[0] = '&';
2280 return charRefOneCharBuf;
2281 } else if(len == 2 && buf[posStart] == 'g' && buf[posStart+1] == 't') {
2282 if(tokenize) {
2283 text = ">";
2284 }
2285 charRefOneCharBuf[0] = '>';
2286 return charRefOneCharBuf;
2287 } else if(len == 4 && buf[posStart] == 'a' && buf[posStart+1] == 'p'
2288 && buf[posStart+2] == 'o' && buf[posStart+3] == 's')
2289 {
2290 if(tokenize) {
2291 text = "'";
2292 }
2293 charRefOneCharBuf[0] = '\'';
2294 return charRefOneCharBuf;
2295 } else if(len == 4 && buf[posStart] == 'q' && buf[posStart+1] == 'u'
2296 && buf[posStart+2] == 'o' && buf[posStart+3] == 't')
2297 {
2298 if(tokenize) {
2299 text = "\"";
2300 }
2301 charRefOneCharBuf[0] = '"';
2302 return charRefOneCharBuf;
2303 } else {
2304 final char[] result = lookupEntityReplacement(len);
2305 if(result != null) {
2306 return result;
2307 }
2308 }
2309 if(tokenize) text = null;
2310 return null;
2311 }
2312 }
2313
2314 private char[] lookupEntityReplacement(int entityNameLen)
2315 {
2316 if(!allStringsInterned) {
2317 final int hash = fastHash(buf, posStart, posEnd - posStart);
2318 LOOP:
2319 for (int i = entityEnd - 1; i >= 0; --i)
2320 {
2321 if(hash == entityNameHash[ i ] && entityNameLen == entityNameBuf[ i ].length) {
2322 final char[] entityBuf = entityNameBuf[ i ];
2323 for (int j = 0; j < entityNameLen; j++)
2324 {
2325 if(buf[posStart + j] != entityBuf[j]) continue LOOP;
2326 }
2327 if(tokenize) text = entityReplacement[ i ];
2328 return entityReplacementBuf[ i ];
2329 }
2330 }
2331 } else {
2332 entityRefName = newString(buf, posStart, posEnd - posStart);
2333 for (int i = entityEnd - 1; i >= 0; --i)
2334 {
2335
2336 if(entityRefName == entityName[ i ]) {
2337 if(tokenize) text = entityReplacement[ i ];
2338 return entityReplacementBuf[ i ];
2339 }
2340 }
2341 }
2342 return null;
2343 }
2344
2345
2346 private void parseComment()
2347 throws XmlPullParserException, IOException
2348 {
2349
2350
2351
2352 char ch = more();
2353 if(ch != '-') throw new XmlPullParserException(
2354 "expected <!-- for comment start", this, null);
2355 if(tokenize) posStart = pos;
2356
2357 final int curLine = lineNumber;
2358 final int curColumn = columnNumber - 4;
2359 try {
2360 final boolean normalizeIgnorableWS = tokenize && !roundtripSupported;
2361 boolean normalizedCR = false;
2362
2363 boolean seenDash = false;
2364 boolean seenDashDash = false;
2365 while(true) {
2366
2367 ch = more();
2368 if (ch == (char)-1) {
2369 throw new EOFException("no more data available"+getPositionDescription());
2370 }
2371 if(seenDashDash && ch != '>') {
2372 throw new XmlPullParserException(
2373 "in comment after two dashes (--) next character must be >"
2374 +" not "+printable(ch), this, null);
2375 }
2376 if(ch == '-') {
2377 if(!seenDash) {
2378 seenDash = true;
2379 } else {
2380 seenDashDash = true;
2381 }
2382 } else if(ch == '>') {
2383 if(seenDashDash) {
2384 break;
2385 }
2386 seenDash = false;
2387 } else {
2388 seenDash = false;
2389 }
2390 if(normalizeIgnorableWS) {
2391 if(ch == '\r') {
2392 normalizedCR = true;
2393
2394
2395
2396 if(!usePC) {
2397 posEnd = pos -1;
2398 if(posEnd > posStart) {
2399 joinPC();
2400 } else {
2401 usePC = true;
2402 pcStart = pcEnd = 0;
2403 }
2404 }
2405
2406 if(pcEnd >= pc.length) ensurePC(pcEnd);
2407 pc[pcEnd++] = '\n';
2408 } else if(ch == '\n') {
2409 if(!normalizedCR && usePC) {
2410 if(pcEnd >= pc.length) ensurePC(pcEnd);
2411 pc[pcEnd++] = '\n';
2412 }
2413 normalizedCR = false;
2414 } else {
2415 if(usePC) {
2416 if(pcEnd >= pc.length) ensurePC(pcEnd);
2417 pc[pcEnd++] = ch;
2418 }
2419 normalizedCR = false;
2420 }
2421 }
2422 }
2423
2424 } catch(EOFException ex) {
2425
2426 throw new XmlPullParserException(
2427 "comment started on line "+curLine+" and column "+curColumn+" was not closed",
2428 this, ex);
2429 }
2430 if(tokenize) {
2431 posEnd = pos - 3;
2432 if(usePC) {
2433 pcEnd -= 2;
2434 }
2435 }
2436 }
2437
2438 private boolean parsePI()
2439 throws XmlPullParserException, IOException
2440 {
2441
2442
2443
2444
2445
2446 if(tokenize) posStart = pos;
2447 final int curLine = lineNumber;
2448 final int curColumn = columnNumber - 2;
2449 int piTargetStart = pos;
2450 int piTargetEnd = -1;
2451 final boolean normalizeIgnorableWS = tokenize && !roundtripSupported;
2452 boolean normalizedCR = false;
2453
2454 try {
2455 boolean seenPITarget = false;
2456 boolean seenQ = false;
2457 char ch = more();
2458 if(isS(ch)) {
2459 throw new XmlPullParserException(
2460 "processing instruction PITarget must be exactly after <? and not white space character",
2461 this, null);
2462 }
2463 while(true) {
2464
2465
2466
2467 if (ch == (char)-1) {
2468 throw new EOFException("no more data available"+getPositionDescription());
2469 }
2470 if(ch == '?') {
2471 if (!seenPITarget) {
2472 throw new XmlPullParserException("processing instruction PITarget name not found", this, null);
2473 }
2474 seenQ = true;
2475 } else if(ch == '>') {
2476 if (seenQ) {
2477 break;
2478 }
2479 if (!seenPITarget) {
2480 throw new XmlPullParserException("processing instruction PITarget name not found", this, null);
2481 }
2482 } else {
2483 if(piTargetEnd == -1 && isS(ch)) {
2484 piTargetEnd = pos - 1;
2485
2486
2487 if((piTargetEnd - piTargetStart) == 3) {
2488 if((buf[piTargetStart] == 'x' || buf[piTargetStart] == 'X')
2489 && (buf[piTargetStart+1] == 'm' || buf[piTargetStart+1] == 'M')
2490 && (buf[piTargetStart+2] == 'l' || buf[piTargetStart+2] == 'L')
2491 )
2492 {
2493 if(piTargetStart > 3) {
2494 throw new XmlPullParserException(
2495 "processing instruction can not have PITarget with reserved xml name",
2496 this, null);
2497 } else {
2498 if(buf[piTargetStart] != 'x'
2499 && buf[piTargetStart+1] != 'm'
2500 && buf[piTargetStart+2] != 'l')
2501 {
2502 throw new XmlPullParserException(
2503 "XMLDecl must have xml name in lowercase",
2504 this, null);
2505 }
2506 }
2507 parseXmlDecl(ch);
2508 if(tokenize) posEnd = pos - 2;
2509 final int off = piTargetStart + 3;
2510 final int len = pos - 2 - off;
2511 xmlDeclContent = newString(buf, off, len);
2512 return true;
2513 }
2514 }
2515 }
2516 seenQ = false;
2517 }
2518 if(normalizeIgnorableWS) {
2519 if(ch == '\r') {
2520 normalizedCR = true;
2521
2522
2523
2524 if(!usePC) {
2525 posEnd = pos -1;
2526 if(posEnd > posStart) {
2527 joinPC();
2528 } else {
2529 usePC = true;
2530 pcStart = pcEnd = 0;
2531 }
2532 }
2533
2534 if(pcEnd >= pc.length) ensurePC(pcEnd);
2535 pc[pcEnd++] = '\n';
2536 } else if(ch == '\n') {
2537 if(!normalizedCR && usePC) {
2538 if(pcEnd >= pc.length) ensurePC(pcEnd);
2539 pc[pcEnd++] = '\n';
2540 }
2541 normalizedCR = false;
2542 } else {
2543 if(usePC) {
2544 if(pcEnd >= pc.length) ensurePC(pcEnd);
2545 pc[pcEnd++] = ch;
2546 }
2547 normalizedCR = false;
2548 }
2549 }
2550 seenPITarget = true;
2551 ch = more();
2552 }
2553 } catch(EOFException ex) {
2554
2555 throw new XmlPullParserException(
2556 "processing instruction started on line " + curLine +
2557 " and column " + curColumn + " was not closed",
2558 this, ex);
2559 }
2560 if(piTargetEnd == -1) {
2561 piTargetEnd = pos - 2 + bufAbsoluteStart;
2562
2563
2564 }
2565 if(tokenize) {
2566 posEnd = pos - 2;
2567 if(normalizeIgnorableWS) {
2568 --pcEnd;
2569 }
2570 }
2571 return false;
2572 }
2573
2574
2575
2576
2577
2578
2579
2580 private final static char[] VERSION = "version".toCharArray();
2581 private final static char[] NCODING = "ncoding".toCharArray();
2582 private final static char[] TANDALONE = "tandalone".toCharArray();
2583 private final static char[] YES = "yes".toCharArray();
2584 private final static char[] NO = "no".toCharArray();
2585
2586
2587
2588 private void parseXmlDecl(char ch)
2589 throws XmlPullParserException, IOException
2590 {
2591
2592
2593
2594 preventBufferCompaction = true;
2595 bufStart = 0;
2596
2597
2598
2599
2600
2601 ch = skipS(ch);
2602 ch = requireInput(ch, VERSION);
2603
2604 ch = skipS(ch);
2605 if(ch != '=') {
2606 throw new XmlPullParserException(
2607 "expected equals sign (=) after version and not "+printable(ch), this, null);
2608 }
2609 ch = more();
2610 ch = skipS(ch);
2611 if(ch != '\'' && ch != '"') {
2612 throw new XmlPullParserException(
2613 "expected apostrophe (') or quotation mark (\") after version and not "
2614 +printable(ch), this, null);
2615 }
2616 final char quotChar = ch;
2617
2618 final int versionStart = pos;
2619 ch = more();
2620
2621 while(ch != quotChar) {
2622 if((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z') && (ch < '0' || ch > '9')
2623 && ch != '_' && ch != '.' && ch != ':' && ch != '-')
2624 {
2625 throw new XmlPullParserException(
2626 "<?xml version value expected to be in ([a-zA-Z0-9_.:] | '-')"
2627 +" not "+printable(ch), this, null);
2628 }
2629 ch = more();
2630 }
2631 final int versionEnd = pos - 1;
2632 parseXmlDeclWithVersion(versionStart, versionEnd);
2633 preventBufferCompaction = false;
2634 }
2635
2636
2637 private void parseXmlDeclWithVersion(int versionStart, int versionEnd)
2638 throws XmlPullParserException, IOException
2639 {
2640 String oldEncoding = this.inputEncoding;
2641
2642
2643 if((versionEnd - versionStart != 3)
2644 || buf[versionStart] != '1'
2645 || buf[versionStart+1] != '.'
2646 || buf[versionStart+2] != '0')
2647 {
2648 throw new XmlPullParserException(
2649 "only 1.0 is supported as <?xml version not '"
2650 +printable(new String(buf, versionStart, versionEnd - versionStart))+"'", this, null);
2651 }
2652 xmlDeclVersion = newString(buf, versionStart, versionEnd - versionStart);
2653
2654
2655 char ch = more();
2656 ch = skipS(ch);
2657 if(ch == 'e') {
2658 ch = more();
2659 ch = requireInput(ch, NCODING);
2660 ch = skipS(ch);
2661 if(ch != '=') {
2662 throw new XmlPullParserException(
2663 "expected equals sign (=) after encoding and not "+printable(ch), this, null);
2664 }
2665 ch = more();
2666 ch = skipS(ch);
2667 if(ch != '\'' && ch != '"') {
2668 throw new XmlPullParserException(
2669 "expected apostrophe (') or quotation mark (\") after encoding and not "
2670 +printable(ch), this, null);
2671 }
2672 final char quotChar = ch;
2673 final int encodingStart = pos;
2674 ch = more();
2675
2676 if((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z'))
2677 {
2678 throw new XmlPullParserException(
2679 "<?xml encoding name expected to start with [A-Za-z]"
2680 +" not "+printable(ch), this, null);
2681 }
2682 ch = more();
2683 while(ch != quotChar) {
2684 if((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z') && (ch < '0' || ch > '9')
2685 && ch != '.' && ch != '_' && ch != '-')
2686 {
2687 throw new XmlPullParserException(
2688 "<?xml encoding value expected to be in ([A-Za-z0-9._] | '-')"
2689 +" not "+printable(ch), this, null);
2690 }
2691 ch = more();
2692 }
2693 final int encodingEnd = pos - 1;
2694
2695
2696
2697 inputEncoding = newString(buf, encodingStart, encodingEnd - encodingStart);
2698 ch = more();
2699 }
2700
2701 ch = skipS(ch);
2702
2703 if(ch == 's') {
2704 ch = more();
2705 ch = requireInput(ch, TANDALONE);
2706 ch = skipS(ch);
2707 if(ch != '=') {
2708 throw new XmlPullParserException(
2709 "expected equals sign (=) after standalone and not "+printable(ch),
2710 this, null);
2711 }
2712 ch = more();
2713 ch = skipS(ch);
2714 if(ch != '\'' && ch != '"') {
2715 throw new XmlPullParserException(
2716 "expected apostrophe (') or quotation mark (\") after encoding and not "
2717 +printable(ch), this, null);
2718 }
2719 char quotChar = ch;
2720 int standaloneStart = pos;
2721 ch = more();
2722 if(ch == 'y') {
2723 ch = requireInput(ch, YES);
2724
2725 xmlDeclStandalone = Boolean.TRUE;
2726 } else if(ch == 'n') {
2727 ch = requireInput(ch, NO);
2728
2729 xmlDeclStandalone = Boolean.FALSE;
2730 } else {
2731 throw new XmlPullParserException(
2732 "expected 'yes' or 'no' after standalone and not "
2733 +printable(ch), this, null);
2734 }
2735 if(ch != quotChar) {
2736 throw new XmlPullParserException(
2737 "expected "+quotChar+" after standalone value not "
2738 +printable(ch), this, null);
2739 }
2740 ch = more();
2741 }
2742
2743
2744 ch = skipS(ch);
2745 if(ch != '?') {
2746 throw new XmlPullParserException(
2747 "expected ?> as last part of <?xml not "
2748 +printable(ch), this, null);
2749 }
2750 ch = more();
2751 if(ch != '>') {
2752 throw new XmlPullParserException(
2753 "expected ?> as last part of <?xml not "
2754 +printable(ch), this, null);
2755 }
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775 }
2776 private void parseDocdecl()
2777 throws XmlPullParserException, IOException
2778 {
2779
2780 char ch = more();
2781 if(ch != 'O') throw new XmlPullParserException(
2782 "expected <!DOCTYPE", this, null);
2783 ch = more();
2784 if(ch != 'C') throw new XmlPullParserException(
2785 "expected <!DOCTYPE", this, null);
2786 ch = more();
2787 if(ch != 'T') throw new XmlPullParserException(
2788 "expected <!DOCTYPE", this, null);
2789 ch = more();
2790 if(ch != 'Y') throw new XmlPullParserException(
2791 "expected <!DOCTYPE", this, null);
2792 ch = more();
2793 if(ch != 'P') throw new XmlPullParserException(
2794 "expected <!DOCTYPE", this, null);
2795 ch = more();
2796 if(ch != 'E') throw new XmlPullParserException(
2797 "expected <!DOCTYPE", this, null);
2798 posStart = pos;
2799
2800
2801
2802
2803 int bracketLevel = 0;
2804 final boolean normalizeIgnorableWS = tokenize && !roundtripSupported;
2805 boolean normalizedCR = false;
2806 while(true) {
2807 ch = more();
2808 if(ch == '[') ++bracketLevel;
2809 if(ch == ']') --bracketLevel;
2810 if(ch == '>' && bracketLevel == 0) break;
2811 if(normalizeIgnorableWS) {
2812 if(ch == '\r') {
2813 normalizedCR = true;
2814
2815
2816
2817 if(!usePC) {
2818 posEnd = pos -1;
2819 if(posEnd > posStart) {
2820 joinPC();
2821 } else {
2822 usePC = true;
2823 pcStart = pcEnd = 0;
2824 }
2825 }
2826
2827 if(pcEnd >= pc.length) ensurePC(pcEnd);
2828 pc[pcEnd++] = '\n';
2829 } else if(ch == '\n') {
2830 if(!normalizedCR && usePC) {
2831 if(pcEnd >= pc.length) ensurePC(pcEnd);
2832 pc[pcEnd++] = '\n';
2833 }
2834 normalizedCR = false;
2835 } else {
2836 if(usePC) {
2837 if(pcEnd >= pc.length) ensurePC(pcEnd);
2838 pc[pcEnd++] = ch;
2839 }
2840 normalizedCR = false;
2841 }
2842 }
2843
2844 }
2845 posEnd = pos - 1;
2846 }
2847
2848 private void parseCDSect(boolean hadCharData)
2849 throws XmlPullParserException, IOException
2850 {
2851
2852
2853
2854
2855
2856
2857
2858
2859 char ch = more();
2860 if(ch != 'C') throw new XmlPullParserException(
2861 "expected <[CDATA[ for comment start", this, null);
2862 ch = more();
2863 if(ch != 'D') throw new XmlPullParserException(
2864 "expected <[CDATA[ for comment start", this, null);
2865 ch = more();
2866 if(ch != 'A') throw new XmlPullParserException(
2867 "expected <[CDATA[ for comment start", this, null);
2868 ch = more();
2869 if(ch != 'T') throw new XmlPullParserException(
2870 "expected <[CDATA[ for comment start", this, null);
2871 ch = more();
2872 if(ch != 'A') throw new XmlPullParserException(
2873 "expected <[CDATA[ for comment start", this, null);
2874 ch = more();
2875 if(ch != '[') throw new XmlPullParserException(
2876 "expected <![CDATA[ for comment start", this, null);
2877
2878
2879 final int cdStart = pos + bufAbsoluteStart;
2880 final int curLine = lineNumber;
2881 final int curColumn = columnNumber;
2882 final boolean normalizeInput = !tokenize || !roundtripSupported;
2883 try {
2884 if(normalizeInput) {
2885 if(hadCharData) {
2886 if(!usePC) {
2887
2888 if(posEnd > posStart) {
2889 joinPC();
2890 } else {
2891 usePC = true;
2892 pcStart = pcEnd = 0;
2893 }
2894 }
2895 }
2896 }
2897 boolean seenBracket = false;
2898 boolean seenBracketBracket = false;
2899 boolean normalizedCR = false;
2900 while(true) {
2901
2902 ch = more();
2903 if(ch == ']') {
2904 if(!seenBracket) {
2905 seenBracket = true;
2906 } else {
2907 seenBracketBracket = true;
2908
2909 }
2910 } else if(ch == '>') {
2911 if(seenBracket && seenBracketBracket) {
2912 break;
2913 } else {
2914 seenBracketBracket = false;
2915 }
2916 seenBracket = false;
2917 } else {
2918 if(seenBracket) {
2919 seenBracketBracket = seenBracket = false;
2920 }
2921 }
2922 if(normalizeInput) {
2923
2924 if(ch == '\r') {
2925 normalizedCR = true;
2926 posStart = cdStart - bufAbsoluteStart;
2927 posEnd = pos - 1;
2928 if(!usePC) {
2929 if(posEnd > posStart) {
2930 joinPC();
2931 } else {
2932 usePC = true;
2933 pcStart = pcEnd = 0;
2934 }
2935 }
2936
2937 if(pcEnd >= pc.length) ensurePC(pcEnd);
2938 pc[pcEnd++] = '\n';
2939 } else if(ch == '\n') {
2940 if(!normalizedCR && usePC) {
2941 if(pcEnd >= pc.length) ensurePC(pcEnd);
2942 pc[pcEnd++] = '\n';
2943 }
2944 normalizedCR = false;
2945 } else {
2946 if(usePC) {
2947 if(pcEnd >= pc.length) ensurePC(pcEnd);
2948 pc[pcEnd++] = ch;
2949 }
2950 normalizedCR = false;
2951 }
2952 }
2953 }
2954 } catch(EOFException ex) {
2955
2956 throw new XmlPullParserException(
2957 "CDATA section started on line "+curLine+" and column "+curColumn+" was not closed",
2958 this, ex);
2959 }
2960 if(normalizeInput) {
2961 if(usePC) {
2962 pcEnd = pcEnd - 2;
2963 }
2964 }
2965 posStart = cdStart - bufAbsoluteStart;
2966 posEnd = pos - 3;
2967 }
2968
2969 private void fillBuf() throws IOException, XmlPullParserException {
2970 if(reader == null) throw new XmlPullParserException(
2971 "reader must be set before parsing is started");
2972
2973
2974 if(bufEnd > bufSoftLimit) {
2975
2976
2977
2978 boolean compact = !preventBufferCompaction && (bufStart > bufSoftLimit || bufStart >= buf.length / 2);
2979
2980
2981 if(compact) {
2982
2983
2984 System.arraycopy(buf, bufStart, buf, 0, bufEnd - bufStart);
2985 if(TRACE_SIZING) System.out.println(
2986 "TRACE_SIZING fillBuf() compacting "+bufStart
2987 +" bufEnd="+bufEnd
2988 +" pos="+pos+" posStart="+posStart+" posEnd="+posEnd
2989 +" buf first 100 chars:"
2990 + new String(buf, 0, Math.min(bufEnd, 100)));;
2991
2992 } else {
2993 final int newSize = 2 * buf.length;
2994 final char newBuf[] = new char[ newSize ];
2995 if(TRACE_SIZING) System.out.println("TRACE_SIZING fillBuf() "+buf.length+" => "+newSize);
2996 System.arraycopy(buf, bufStart, newBuf, 0, bufEnd - bufStart);
2997 buf = newBuf;
2998 if(bufLoadFactor > 0) {
2999
3000
3001 bufSoftLimit = (int)(bufferLoadFactor * buf.length);
3002 }
3003 }
3004 bufEnd -= bufStart;
3005 pos -= bufStart;
3006 posStart -= bufStart;
3007 posEnd -= bufStart;
3008 bufAbsoluteStart += bufStart;
3009 bufStart = 0;
3010 if(TRACE_SIZING) System.out.println(
3011 "TRACE_SIZING fillBuf() after bufEnd="+bufEnd
3012 +" pos="+pos+" posStart="+posStart+" posEnd="+posEnd
3013 +" buf first 100 chars:"+new String(buf, 0, Math.min(bufEnd, 100)));
3014 }
3015
3016 final int len = Math.min(buf.length - bufEnd, READ_CHUNK_SIZE);
3017 final int ret = reader.read(buf, bufEnd, len);
3018 if(ret > 0) {
3019 bufEnd += ret;
3020 if(TRACE_SIZING) System.out.println(
3021 "TRACE_SIZING fillBuf() after filling in buffer"
3022 +" buf first 100 chars:"+new String(buf, 0, Math.min(bufEnd, 100)));
3023
3024 return;
3025 }
3026 if(ret == -1) {
3027 if(bufAbsoluteStart == 0 && pos == 0) {
3028 throw new EOFException("input contained no data");
3029 } else {
3030 if(seenRoot && depth == 0) {
3031 reachedEnd = true;
3032 return;
3033 } else {
3034 StringBuffer expectedTagStack = new StringBuffer();
3035 if(depth > 0) {
3036 if (elRawName == null || elRawName[depth] == null) {
3037 String tagName = new String(buf, posStart + 1, pos - posStart - 1);
3038 expectedTagStack.append(" - expected the opening tag <").append(tagName).append("...>");
3039 } else {
3040
3041
3042 expectedTagStack.append(" - expected end tag");
3043 if(depth > 1) {
3044 expectedTagStack.append("s");
3045 }
3046 expectedTagStack.append(" ");
3047 for (int i = depth; i > 0; i--)
3048 {
3049 if (elRawName == null || elRawName[i] == null) {
3050 String tagName = new String(buf, posStart + 1, pos - posStart - 1);
3051 expectedTagStack
3052 .append(" - expected the opening tag <")
3053 .append(tagName)
3054 .append("...>");
3055 } else {
3056 String tagName = new String(elRawName[i], 0, elRawNameEnd[i]);
3057 expectedTagStack.append("</").append(tagName).append('>');
3058 }
3059 }
3060 expectedTagStack.append(" to close");
3061 for (int i = depth; i > 0; i--)
3062 {
3063 if(i != depth) {
3064 expectedTagStack.append(" and");
3065 }
3066 if (elRawName == null || elRawName[i] == null) {
3067 String tagName = new String(elRawName[i], 0, elRawNameEnd[i]);
3068 expectedTagStack.append(" start tag <"+tagName+">");
3069 expectedTagStack.append(" from line "+elRawNameLine[i]);
3070 } else {
3071 String tagName = new String(elRawName[i], 0, elRawNameEnd[i]);
3072 expectedTagStack.append(" start tag <").append(tagName).append(">");
3073 expectedTagStack.append(" from line ").append(elRawNameLine[i]);
3074 }
3075 }
3076 expectedTagStack.append(", parser stopped on");
3077 }
3078 }
3079 throw new EOFException("no more data available"
3080 +expectedTagStack.toString()+getPositionDescription());
3081 }
3082 }
3083 } else {
3084 throw new IOException("error reading input, returned "+ret);
3085 }
3086 }
3087
3088 private char more() throws IOException, XmlPullParserException {
3089 if(pos >= bufEnd) {
3090 fillBuf();
3091
3092 if(reachedEnd) return (char)-1;
3093 }
3094 final char ch = buf[pos++];
3095
3096
3097 if(ch == '\n') { ++lineNumber; columnNumber = 1; }
3098 else { ++columnNumber; }
3099 return ch;
3100 }
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112 private void ensurePC(int end) {
3113
3114 final int newSize = end > READ_CHUNK_SIZE ? 2 * end : 2 * READ_CHUNK_SIZE;
3115 final char[] newPC = new char[ newSize ];
3116 if(TRACE_SIZING) System.out.println("TRACE_SIZING ensurePC() "+pc.length+" ==> "+newSize+" end="+end);
3117 System.arraycopy(pc, 0, newPC, 0, pcEnd);
3118 pc = newPC;
3119
3120 }
3121
3122 private void joinPC() {
3123
3124
3125 final int len = posEnd - posStart;
3126 final int newEnd = pcEnd + len + 1;
3127 if(newEnd >= pc.length) ensurePC(newEnd);
3128
3129 System.arraycopy(buf, posStart, pc, pcEnd, len);
3130 pcEnd += len;
3131 usePC = true;
3132
3133 }
3134
3135 private char requireInput(char ch, char[] input)
3136 throws XmlPullParserException, IOException
3137 {
3138 for (int i = 0; i < input.length; i++)
3139 {
3140 if(ch != input[i]) {
3141 throw new XmlPullParserException(
3142 "expected "+printable(input[i])+" in "+new String(input)
3143 +" and not "+printable(ch), this, null);
3144 }
3145 ch = more();
3146 }
3147 return ch;
3148 }
3149
3150 private char requireNextS()
3151 throws XmlPullParserException, IOException
3152 {
3153 final char ch = more();
3154 if(!isS(ch)) {
3155 throw new XmlPullParserException(
3156 "white space is required and not "+printable(ch), this, null);
3157 }
3158 return skipS(ch);
3159 }
3160
3161 private char skipS(char ch)
3162 throws XmlPullParserException, IOException
3163 {
3164 while(isS(ch)) { ch = more(); }
3165 return ch;
3166 }
3167
3168
3169 private static final int LOOKUP_MAX = 0x400;
3170 private static final char LOOKUP_MAX_CHAR = (char)LOOKUP_MAX;
3171
3172
3173 private static boolean lookupNameStartChar[] = new boolean[ LOOKUP_MAX ];
3174 private static boolean lookupNameChar[] = new boolean[ LOOKUP_MAX ];
3175
3176 private static final void setName(char ch)
3177
3178 { lookupNameChar[ ch ] = true; }
3179 private static final void setNameStart(char ch)
3180
3181 { lookupNameStartChar[ ch ] = true; setName(ch); }
3182
3183 static {
3184 setNameStart(':');
3185 for (char ch = 'A'; ch <= 'Z'; ++ch) setNameStart(ch);
3186 setNameStart('_');
3187 for (char ch = 'a'; ch <= 'z'; ++ch) setNameStart(ch);
3188 for (char ch = '\u00c0'; ch <= '\u02FF'; ++ch) setNameStart(ch);
3189 for (char ch = '\u0370'; ch <= '\u037d'; ++ch) setNameStart(ch);
3190 for (char ch = '\u037f'; ch < '\u0400'; ++ch) setNameStart(ch);
3191
3192 setName('-');
3193 setName('.');
3194 for (char ch = '0'; ch <= '9'; ++ch) setName(ch);
3195 setName('\u00b7');
3196 for (char ch = '\u0300'; ch <= '\u036f'; ++ch) setName(ch);
3197 }
3198
3199
3200 private boolean isNameStartChar(char ch) {
3201 return ch < LOOKUP_MAX_CHAR
3202 ? lookupNameStartChar[ch]
3203 : (ch <= '\u2027') || (ch >= '\u202A' && ch <= '\u218F') || (ch >= '\u2800' && ch <= '\uFFEF');
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225 }
3226
3227
3228 private boolean isNameChar(char ch) {
3229
3230
3231
3232
3233 return ch < LOOKUP_MAX_CHAR
3234 ? lookupNameChar[ch]
3235 : (ch <= '\u2027') || (ch >= '\u202A' && ch <= '\u218F') || (ch >= '\u2800' && ch <= '\uFFEF');
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250 }
3251
3252 private boolean isS(char ch) {
3253 return (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t');
3254
3255 }
3256
3257
3258
3259
3260
3261
3262 private String printable(char ch) {
3263 if(ch == '\n') {
3264 return "\\n";
3265 } else if(ch == '\r') {
3266 return "\\r";
3267 } else if(ch == '\t') {
3268 return "\\t";
3269 } else if(ch == '\'') {
3270 return "\\'";
3271 } if(ch > 127 || ch < 32) {
3272 return "\\u"+Integer.toHexString(ch);
3273 }
3274 return ""+ch;
3275 }
3276
3277 private String printable(String s) {
3278 if(s == null) return null;
3279 final int sLen = s.length();
3280 StringBuffer buf = new StringBuffer(sLen + 10);
3281 for(int i = 0; i < sLen; ++i) {
3282 buf.append(printable(s.charAt(i)));
3283 }
3284 s = buf.toString();
3285 return s;
3286 }
3287 }
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348