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 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 if (!noUnicode4) {
2213 try {
2214 charRefOneCharBuf = Character.toChars(Integer.parseInt(sb.toString(), isHex ? 16 : 10));
2215 } catch (IllegalArgumentException e) {
2216 throw new XmlPullParserException("character reference (with "
2217 + (isHex ? "hex" : "decimal")
2218 + " value "
2219 + sb.toString()
2220 + ") is invalid", this, null);
2221 } catch(NoSuchMethodError e) {
2222 noUnicode4 = true;
2223 }
2224 }
2225 if (noUnicode4) {
2226 int i = Integer.parseInt(sb.toString(), isHex ? 16 : 10);
2227 if (i > Character.MAX_VALUE) {
2228 throw new XmlPullParserException("Unicode character reference (with "
2229 + (isHex ? "hex" : "decimal")
2230 + " value "
2231 + sb.toString()
2232 + ") is not supported in this runtime", this, null);
2233 }
2234 charRefOneCharBuf = new char[1];
2235 charRefOneCharBuf[0] = (char)i;
2236 }
2237 if(tokenize) {
2238 text = newString(charRefOneCharBuf, 0, charRefOneCharBuf.length);
2239 }
2240 return charRefOneCharBuf;
2241 } else {
2242
2243
2244 if(!isNameStartChar(ch)) {
2245 throw new XmlPullParserException(
2246 "entity reference names can not start with character '"
2247 +printable(ch)+"'", this, null);
2248 }
2249 while(true) {
2250 ch = more();
2251 if(ch == ';') {
2252 break;
2253 }
2254 if(!isNameChar(ch)) {
2255 throw new XmlPullParserException(
2256 "entity reference name can not contain character "
2257 +printable(ch)+"'", this, null);
2258 }
2259 }
2260 posEnd = pos - 1;
2261
2262 final int len = posEnd - posStart;
2263 if(len == 2 && buf[posStart] == 'l' && buf[posStart+1] == 't') {
2264 if(tokenize) {
2265 text = "<";
2266 }
2267 charRefOneCharBuf[0] = '<';
2268 return charRefOneCharBuf;
2269
2270
2271
2272
2273 } else if(len == 3 && buf[posStart] == 'a'
2274 && buf[posStart+1] == 'm' && buf[posStart+2] == 'p') {
2275 if(tokenize) {
2276 text = "&";
2277 }
2278 charRefOneCharBuf[0] = '&';
2279 return charRefOneCharBuf;
2280 } else if(len == 2 && buf[posStart] == 'g' && buf[posStart+1] == 't') {
2281 if(tokenize) {
2282 text = ">";
2283 }
2284 charRefOneCharBuf[0] = '>';
2285 return charRefOneCharBuf;
2286 } else if(len == 4 && buf[posStart] == 'a' && buf[posStart+1] == 'p'
2287 && buf[posStart+2] == 'o' && buf[posStart+3] == 's')
2288 {
2289 if(tokenize) {
2290 text = "'";
2291 }
2292 charRefOneCharBuf[0] = '\'';
2293 return charRefOneCharBuf;
2294 } else if(len == 4 && buf[posStart] == 'q' && buf[posStart+1] == 'u'
2295 && buf[posStart+2] == 'o' && buf[posStart+3] == 't')
2296 {
2297 if(tokenize) {
2298 text = "\"";
2299 }
2300 charRefOneCharBuf[0] = '"';
2301 return charRefOneCharBuf;
2302 } else {
2303 final char[] result = lookupEntityReplacement(len);
2304 if(result != null) {
2305 return result;
2306 }
2307 }
2308 if(tokenize) text = null;
2309 return null;
2310 }
2311 }
2312
2313 private char[] lookupEntityReplacement(int entityNameLen)
2314 {
2315 if(!allStringsInterned) {
2316 final int hash = fastHash(buf, posStart, posEnd - posStart);
2317 LOOP:
2318 for (int i = entityEnd - 1; i >= 0; --i)
2319 {
2320 if(hash == entityNameHash[ i ] && entityNameLen == entityNameBuf[ i ].length) {
2321 final char[] entityBuf = entityNameBuf[ i ];
2322 for (int j = 0; j < entityNameLen; j++)
2323 {
2324 if(buf[posStart + j] != entityBuf[j]) continue LOOP;
2325 }
2326 if(tokenize) text = entityReplacement[ i ];
2327 return entityReplacementBuf[ i ];
2328 }
2329 }
2330 } else {
2331 entityRefName = newString(buf, posStart, posEnd - posStart);
2332 for (int i = entityEnd - 1; i >= 0; --i)
2333 {
2334
2335 if(entityRefName == entityName[ i ]) {
2336 if(tokenize) text = entityReplacement[ i ];
2337 return entityReplacementBuf[ i ];
2338 }
2339 }
2340 }
2341 return null;
2342 }
2343
2344
2345 private void parseComment()
2346 throws XmlPullParserException, IOException
2347 {
2348
2349
2350
2351 char ch = more();
2352 if(ch != '-') throw new XmlPullParserException(
2353 "expected <!-- for comment start", this, null);
2354 if(tokenize) posStart = pos;
2355
2356 final int curLine = lineNumber;
2357 final int curColumn = columnNumber - 4;
2358 try {
2359 final boolean normalizeIgnorableWS = tokenize && !roundtripSupported;
2360 boolean normalizedCR = false;
2361
2362 boolean seenDash = false;
2363 boolean seenDashDash = false;
2364 while(true) {
2365
2366 ch = more();
2367 if (ch == (char)-1) {
2368 throw new EOFException("no more data available"+getPositionDescription());
2369 }
2370 if(seenDashDash && ch != '>') {
2371 throw new XmlPullParserException(
2372 "in comment after two dashes (--) next character must be >"
2373 +" not "+printable(ch), this, null);
2374 }
2375 if(ch == '-') {
2376 if(!seenDash) {
2377 seenDash = true;
2378 } else {
2379 seenDashDash = true;
2380 }
2381 } else if(ch == '>') {
2382 if(seenDashDash) {
2383 break;
2384 }
2385 seenDash = false;
2386 } else {
2387 seenDash = false;
2388 }
2389 if(normalizeIgnorableWS) {
2390 if(ch == '\r') {
2391 normalizedCR = true;
2392
2393
2394
2395 if(!usePC) {
2396 posEnd = pos -1;
2397 if(posEnd > posStart) {
2398 joinPC();
2399 } else {
2400 usePC = true;
2401 pcStart = pcEnd = 0;
2402 }
2403 }
2404
2405 if(pcEnd >= pc.length) ensurePC(pcEnd);
2406 pc[pcEnd++] = '\n';
2407 } else if(ch == '\n') {
2408 if(!normalizedCR && usePC) {
2409 if(pcEnd >= pc.length) ensurePC(pcEnd);
2410 pc[pcEnd++] = '\n';
2411 }
2412 normalizedCR = false;
2413 } else {
2414 if(usePC) {
2415 if(pcEnd >= pc.length) ensurePC(pcEnd);
2416 pc[pcEnd++] = ch;
2417 }
2418 normalizedCR = false;
2419 }
2420 }
2421 }
2422
2423 } catch(EOFException ex) {
2424
2425 throw new XmlPullParserException(
2426 "comment started on line "+curLine+" and column "+curColumn+" was not closed",
2427 this, ex);
2428 }
2429 if(tokenize) {
2430 posEnd = pos - 3;
2431 if(usePC) {
2432 pcEnd -= 2;
2433 }
2434 }
2435 }
2436
2437 private boolean parsePI()
2438 throws XmlPullParserException, IOException
2439 {
2440
2441
2442
2443
2444
2445 if(tokenize) posStart = pos;
2446 final int curLine = lineNumber;
2447 final int curColumn = columnNumber - 2;
2448 int piTargetStart = pos;
2449 int piTargetEnd = -1;
2450 final boolean normalizeIgnorableWS = tokenize && !roundtripSupported;
2451 boolean normalizedCR = false;
2452
2453 try {
2454 boolean seenPITarget = false;
2455 boolean seenQ = false;
2456 char ch = more();
2457 if(isS(ch)) {
2458 throw new XmlPullParserException(
2459 "processing instruction PITarget must be exactly after <? and not white space character",
2460 this, null);
2461 }
2462 while(true) {
2463
2464
2465
2466 if (ch == (char)-1) {
2467 throw new EOFException("no more data available"+getPositionDescription());
2468 }
2469 if(ch == '?') {
2470 if (!seenPITarget) {
2471 throw new XmlPullParserException("processing instruction PITarget name not found", this, null);
2472 }
2473 seenQ = true;
2474 } else if(ch == '>') {
2475 if (seenQ) {
2476 break;
2477 }
2478 if (!seenPITarget) {
2479 throw new XmlPullParserException("processing instruction PITarget name not found", this, null);
2480 }
2481 } else {
2482 if(piTargetEnd == -1 && isS(ch)) {
2483 piTargetEnd = pos - 1;
2484
2485
2486 if((piTargetEnd - piTargetStart) == 3) {
2487 if((buf[piTargetStart] == 'x' || buf[piTargetStart] == 'X')
2488 && (buf[piTargetStart+1] == 'm' || buf[piTargetStart+1] == 'M')
2489 && (buf[piTargetStart+2] == 'l' || buf[piTargetStart+2] == 'L')
2490 )
2491 {
2492 if(piTargetStart > 3) {
2493 throw new XmlPullParserException(
2494 "processing instruction can not have PITarget with reserved xml name",
2495 this, null);
2496 } else {
2497 if(buf[piTargetStart] != 'x'
2498 && buf[piTargetStart+1] != 'm'
2499 && buf[piTargetStart+2] != 'l')
2500 {
2501 throw new XmlPullParserException(
2502 "XMLDecl must have xml name in lowercase",
2503 this, null);
2504 }
2505 }
2506 parseXmlDecl(ch);
2507 if(tokenize) posEnd = pos - 2;
2508 final int off = piTargetStart + 3;
2509 final int len = pos - 2 - off;
2510 xmlDeclContent = newString(buf, off, len);
2511 return true;
2512 }
2513 }
2514 }
2515 seenQ = false;
2516 }
2517 if(normalizeIgnorableWS) {
2518 if(ch == '\r') {
2519 normalizedCR = true;
2520
2521
2522
2523 if(!usePC) {
2524 posEnd = pos -1;
2525 if(posEnd > posStart) {
2526 joinPC();
2527 } else {
2528 usePC = true;
2529 pcStart = pcEnd = 0;
2530 }
2531 }
2532
2533 if(pcEnd >= pc.length) ensurePC(pcEnd);
2534 pc[pcEnd++] = '\n';
2535 } else if(ch == '\n') {
2536 if(!normalizedCR && usePC) {
2537 if(pcEnd >= pc.length) ensurePC(pcEnd);
2538 pc[pcEnd++] = '\n';
2539 }
2540 normalizedCR = false;
2541 } else {
2542 if(usePC) {
2543 if(pcEnd >= pc.length) ensurePC(pcEnd);
2544 pc[pcEnd++] = ch;
2545 }
2546 normalizedCR = false;
2547 }
2548 }
2549 seenPITarget = true;
2550 ch = more();
2551 }
2552 } catch(EOFException ex) {
2553
2554 throw new XmlPullParserException(
2555 "processing instruction started on line " + curLine +
2556 " and column " + curColumn + " was not closed",
2557 this, ex);
2558 }
2559 if(piTargetEnd == -1) {
2560 piTargetEnd = pos - 2 + bufAbsoluteStart;
2561
2562
2563 }
2564 if(tokenize) {
2565 posEnd = pos - 2;
2566 if(normalizeIgnorableWS) {
2567 --pcEnd;
2568 }
2569 }
2570 return false;
2571 }
2572
2573
2574
2575
2576
2577
2578
2579 private final static char[] VERSION = "version".toCharArray();
2580 private final static char[] NCODING = "ncoding".toCharArray();
2581 private final static char[] TANDALONE = "tandalone".toCharArray();
2582 private final static char[] YES = "yes".toCharArray();
2583 private final static char[] NO = "no".toCharArray();
2584
2585
2586
2587 private void parseXmlDecl(char ch)
2588 throws XmlPullParserException, IOException
2589 {
2590
2591
2592
2593 preventBufferCompaction = true;
2594 bufStart = 0;
2595
2596
2597
2598
2599
2600 ch = skipS(ch);
2601 ch = requireInput(ch, VERSION);
2602
2603 ch = skipS(ch);
2604 if(ch != '=') {
2605 throw new XmlPullParserException(
2606 "expected equals sign (=) after version and not "+printable(ch), this, null);
2607 }
2608 ch = more();
2609 ch = skipS(ch);
2610 if(ch != '\'' && ch != '"') {
2611 throw new XmlPullParserException(
2612 "expected apostrophe (') or quotation mark (\") after version and not "
2613 +printable(ch), this, null);
2614 }
2615 final char quotChar = ch;
2616
2617 final int versionStart = pos;
2618 ch = more();
2619
2620 while(ch != quotChar) {
2621 if((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z') && (ch < '0' || ch > '9')
2622 && ch != '_' && ch != '.' && ch != ':' && ch != '-')
2623 {
2624 throw new XmlPullParserException(
2625 "<?xml version value expected to be in ([a-zA-Z0-9_.:] | '-')"
2626 +" not "+printable(ch), this, null);
2627 }
2628 ch = more();
2629 }
2630 final int versionEnd = pos - 1;
2631 parseXmlDeclWithVersion(versionStart, versionEnd);
2632 preventBufferCompaction = false;
2633 }
2634
2635
2636 private void parseXmlDeclWithVersion(int versionStart, int versionEnd)
2637 throws XmlPullParserException, IOException
2638 {
2639 String oldEncoding = this.inputEncoding;
2640
2641
2642 if((versionEnd - versionStart != 3)
2643 || buf[versionStart] != '1'
2644 || buf[versionStart+1] != '.'
2645 || buf[versionStart+2] != '0')
2646 {
2647 throw new XmlPullParserException(
2648 "only 1.0 is supported as <?xml version not '"
2649 +printable(new String(buf, versionStart, versionEnd - versionStart))+"'", this, null);
2650 }
2651 xmlDeclVersion = newString(buf, versionStart, versionEnd - versionStart);
2652
2653
2654 char ch = more();
2655 ch = skipS(ch);
2656 if(ch == 'e') {
2657 ch = more();
2658 ch = requireInput(ch, NCODING);
2659 ch = skipS(ch);
2660 if(ch != '=') {
2661 throw new XmlPullParserException(
2662 "expected equals sign (=) after encoding and not "+printable(ch), this, null);
2663 }
2664 ch = more();
2665 ch = skipS(ch);
2666 if(ch != '\'' && ch != '"') {
2667 throw new XmlPullParserException(
2668 "expected apostrophe (') or quotation mark (\") after encoding and not "
2669 +printable(ch), this, null);
2670 }
2671 final char quotChar = ch;
2672 final int encodingStart = pos;
2673 ch = more();
2674
2675 if((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z'))
2676 {
2677 throw new XmlPullParserException(
2678 "<?xml encoding name expected to start with [A-Za-z]"
2679 +" not "+printable(ch), this, null);
2680 }
2681 ch = more();
2682 while(ch != quotChar) {
2683 if((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z') && (ch < '0' || ch > '9')
2684 && ch != '.' && ch != '_' && ch != '-')
2685 {
2686 throw new XmlPullParserException(
2687 "<?xml encoding value expected to be in ([A-Za-z0-9._] | '-')"
2688 +" not "+printable(ch), this, null);
2689 }
2690 ch = more();
2691 }
2692 final int encodingEnd = pos - 1;
2693
2694
2695
2696 inputEncoding = newString(buf, encodingStart, encodingEnd - encodingStart);
2697 ch = more();
2698 }
2699
2700 ch = skipS(ch);
2701
2702 if(ch == 's') {
2703 ch = more();
2704 ch = requireInput(ch, TANDALONE);
2705 ch = skipS(ch);
2706 if(ch != '=') {
2707 throw new XmlPullParserException(
2708 "expected equals sign (=) after standalone and not "+printable(ch),
2709 this, null);
2710 }
2711 ch = more();
2712 ch = skipS(ch);
2713 if(ch != '\'' && ch != '"') {
2714 throw new XmlPullParserException(
2715 "expected apostrophe (') or quotation mark (\") after encoding and not "
2716 +printable(ch), this, null);
2717 }
2718 char quotChar = ch;
2719 int standaloneStart = pos;
2720 ch = more();
2721 if(ch == 'y') {
2722 ch = requireInput(ch, YES);
2723
2724 xmlDeclStandalone = Boolean.TRUE;
2725 } else if(ch == 'n') {
2726 ch = requireInput(ch, NO);
2727
2728 xmlDeclStandalone = Boolean.FALSE;
2729 } else {
2730 throw new XmlPullParserException(
2731 "expected 'yes' or 'no' after standalone and not "
2732 +printable(ch), this, null);
2733 }
2734 if(ch != quotChar) {
2735 throw new XmlPullParserException(
2736 "expected "+quotChar+" after standalone value not "
2737 +printable(ch), this, null);
2738 }
2739 ch = more();
2740 }
2741
2742
2743 ch = skipS(ch);
2744 if(ch != '?') {
2745 throw new XmlPullParserException(
2746 "expected ?> as last part of <?xml not "
2747 +printable(ch), this, null);
2748 }
2749 ch = more();
2750 if(ch != '>') {
2751 throw new XmlPullParserException(
2752 "expected ?> as last part of <?xml not "
2753 +printable(ch), this, null);
2754 }
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774 }
2775 private void parseDocdecl()
2776 throws XmlPullParserException, IOException
2777 {
2778
2779 char ch = more();
2780 if(ch != 'O') throw new XmlPullParserException(
2781 "expected <!DOCTYPE", this, null);
2782 ch = more();
2783 if(ch != 'C') throw new XmlPullParserException(
2784 "expected <!DOCTYPE", this, null);
2785 ch = more();
2786 if(ch != 'T') throw new XmlPullParserException(
2787 "expected <!DOCTYPE", this, null);
2788 ch = more();
2789 if(ch != 'Y') throw new XmlPullParserException(
2790 "expected <!DOCTYPE", this, null);
2791 ch = more();
2792 if(ch != 'P') throw new XmlPullParserException(
2793 "expected <!DOCTYPE", this, null);
2794 ch = more();
2795 if(ch != 'E') throw new XmlPullParserException(
2796 "expected <!DOCTYPE", this, null);
2797 posStart = pos;
2798
2799
2800
2801
2802 int bracketLevel = 0;
2803 final boolean normalizeIgnorableWS = tokenize && !roundtripSupported;
2804 boolean normalizedCR = false;
2805 while(true) {
2806 ch = more();
2807 if(ch == '[') ++bracketLevel;
2808 if(ch == ']') --bracketLevel;
2809 if(ch == '>' && bracketLevel == 0) break;
2810 if(normalizeIgnorableWS) {
2811 if(ch == '\r') {
2812 normalizedCR = true;
2813
2814
2815
2816 if(!usePC) {
2817 posEnd = pos -1;
2818 if(posEnd > posStart) {
2819 joinPC();
2820 } else {
2821 usePC = true;
2822 pcStart = pcEnd = 0;
2823 }
2824 }
2825
2826 if(pcEnd >= pc.length) ensurePC(pcEnd);
2827 pc[pcEnd++] = '\n';
2828 } else if(ch == '\n') {
2829 if(!normalizedCR && usePC) {
2830 if(pcEnd >= pc.length) ensurePC(pcEnd);
2831 pc[pcEnd++] = '\n';
2832 }
2833 normalizedCR = false;
2834 } else {
2835 if(usePC) {
2836 if(pcEnd >= pc.length) ensurePC(pcEnd);
2837 pc[pcEnd++] = ch;
2838 }
2839 normalizedCR = false;
2840 }
2841 }
2842
2843 }
2844 posEnd = pos - 1;
2845 }
2846
2847 private void parseCDSect(boolean hadCharData)
2848 throws XmlPullParserException, IOException
2849 {
2850
2851
2852
2853
2854
2855
2856
2857
2858 char ch = more();
2859 if(ch != 'C') throw new XmlPullParserException(
2860 "expected <[CDATA[ for comment start", this, null);
2861 ch = more();
2862 if(ch != 'D') throw new XmlPullParserException(
2863 "expected <[CDATA[ for comment start", this, null);
2864 ch = more();
2865 if(ch != 'A') throw new XmlPullParserException(
2866 "expected <[CDATA[ for comment start", this, null);
2867 ch = more();
2868 if(ch != 'T') throw new XmlPullParserException(
2869 "expected <[CDATA[ for comment start", this, null);
2870 ch = more();
2871 if(ch != 'A') throw new XmlPullParserException(
2872 "expected <[CDATA[ for comment start", this, null);
2873 ch = more();
2874 if(ch != '[') throw new XmlPullParserException(
2875 "expected <![CDATA[ for comment start", this, null);
2876
2877
2878 final int cdStart = pos + bufAbsoluteStart;
2879 final int curLine = lineNumber;
2880 final int curColumn = columnNumber;
2881 final boolean normalizeInput = !tokenize || !roundtripSupported;
2882 try {
2883 if(normalizeInput) {
2884 if(hadCharData) {
2885 if(!usePC) {
2886
2887 if(posEnd > posStart) {
2888 joinPC();
2889 } else {
2890 usePC = true;
2891 pcStart = pcEnd = 0;
2892 }
2893 }
2894 }
2895 }
2896 boolean seenBracket = false;
2897 boolean seenBracketBracket = false;
2898 boolean normalizedCR = false;
2899 while(true) {
2900
2901 ch = more();
2902 if(ch == ']') {
2903 if(!seenBracket) {
2904 seenBracket = true;
2905 } else {
2906 seenBracketBracket = true;
2907
2908 }
2909 } else if(ch == '>') {
2910 if(seenBracket && seenBracketBracket) {
2911 break;
2912 } else {
2913 seenBracketBracket = false;
2914 }
2915 seenBracket = false;
2916 } else {
2917 if(seenBracket) {
2918 seenBracketBracket = seenBracket = false;
2919 }
2920 }
2921 if(normalizeInput) {
2922
2923 if(ch == '\r') {
2924 normalizedCR = true;
2925 posStart = cdStart - bufAbsoluteStart;
2926 posEnd = pos - 1;
2927 if(!usePC) {
2928 if(posEnd > posStart) {
2929 joinPC();
2930 } else {
2931 usePC = true;
2932 pcStart = pcEnd = 0;
2933 }
2934 }
2935
2936 if(pcEnd >= pc.length) ensurePC(pcEnd);
2937 pc[pcEnd++] = '\n';
2938 } else if(ch == '\n') {
2939 if(!normalizedCR && usePC) {
2940 if(pcEnd >= pc.length) ensurePC(pcEnd);
2941 pc[pcEnd++] = '\n';
2942 }
2943 normalizedCR = false;
2944 } else {
2945 if(usePC) {
2946 if(pcEnd >= pc.length) ensurePC(pcEnd);
2947 pc[pcEnd++] = ch;
2948 }
2949 normalizedCR = false;
2950 }
2951 }
2952 }
2953 } catch(EOFException ex) {
2954
2955 throw new XmlPullParserException(
2956 "CDATA section started on line "+curLine+" and column "+curColumn+" was not closed",
2957 this, ex);
2958 }
2959 if(normalizeInput) {
2960 if(usePC) {
2961 pcEnd = pcEnd - 2;
2962 }
2963 }
2964 posStart = cdStart - bufAbsoluteStart;
2965 posEnd = pos - 3;
2966 }
2967
2968 private void fillBuf() throws IOException, XmlPullParserException {
2969 if(reader == null) throw new XmlPullParserException(
2970 "reader must be set before parsing is started");
2971
2972
2973 if(bufEnd > bufSoftLimit) {
2974
2975
2976
2977 boolean compact = !preventBufferCompaction && (bufStart > bufSoftLimit || bufStart >= buf.length / 2);
2978
2979
2980 if(compact) {
2981
2982
2983 System.arraycopy(buf, bufStart, buf, 0, bufEnd - bufStart);
2984 if(TRACE_SIZING) System.out.println(
2985 "TRACE_SIZING fillBuf() compacting "+bufStart
2986 +" bufEnd="+bufEnd
2987 +" pos="+pos+" posStart="+posStart+" posEnd="+posEnd
2988 +" buf first 100 chars:"
2989 + new String(buf, 0, Math.min(bufEnd, 100)));;
2990
2991 } else {
2992 final int newSize = 2 * buf.length;
2993 final char newBuf[] = new char[ newSize ];
2994 if(TRACE_SIZING) System.out.println("TRACE_SIZING fillBuf() "+buf.length+" => "+newSize);
2995 System.arraycopy(buf, bufStart, newBuf, 0, bufEnd - bufStart);
2996 buf = newBuf;
2997 if(bufLoadFactor > 0) {
2998
2999
3000 bufSoftLimit = (int)(bufferLoadFactor * buf.length);
3001 }
3002 }
3003 bufEnd -= bufStart;
3004 pos -= bufStart;
3005 posStart -= bufStart;
3006 posEnd -= bufStart;
3007 bufAbsoluteStart += bufStart;
3008 bufStart = 0;
3009 if(TRACE_SIZING) System.out.println(
3010 "TRACE_SIZING fillBuf() after bufEnd="+bufEnd
3011 +" pos="+pos+" posStart="+posStart+" posEnd="+posEnd
3012 +" buf first 100 chars:"+new String(buf, 0, Math.min(bufEnd, 100)));
3013 }
3014
3015 final int len = Math.min(buf.length - bufEnd, READ_CHUNK_SIZE);
3016 final int ret = reader.read(buf, bufEnd, len);
3017 if(ret > 0) {
3018 bufEnd += ret;
3019 if(TRACE_SIZING) System.out.println(
3020 "TRACE_SIZING fillBuf() after filling in buffer"
3021 +" buf first 100 chars:"+new String(buf, 0, Math.min(bufEnd, 100)));
3022
3023 return;
3024 }
3025 if(ret == -1) {
3026 if(bufAbsoluteStart == 0 && pos == 0) {
3027 throw new EOFException("input contained no data");
3028 } else {
3029 if(seenRoot && depth == 0) {
3030 reachedEnd = true;
3031 return;
3032 } else {
3033 StringBuffer expectedTagStack = new StringBuffer();
3034 if(depth > 0) {
3035 if (elRawName == null || elRawName[depth] == null) {
3036 String tagName = new String(buf, posStart + 1, pos - posStart - 1);
3037 expectedTagStack.append(" - expected the opening tag <").append(tagName).append("...>");
3038 } else {
3039
3040
3041 expectedTagStack.append(" - expected end tag");
3042 if(depth > 1) {
3043 expectedTagStack.append("s");
3044 }
3045 expectedTagStack.append(" ");
3046 for (int i = depth; i > 0; i--)
3047 {
3048 if (elRawName == null || elRawName[i] == null) {
3049 String tagName = new String(buf, posStart + 1, pos - posStart - 1);
3050 expectedTagStack
3051 .append(" - expected the opening tag <")
3052 .append(tagName)
3053 .append("...>");
3054 } else {
3055 String tagName = new String(elRawName[i], 0, elRawNameEnd[i]);
3056 expectedTagStack.append("</").append(tagName).append('>');
3057 }
3058 }
3059 expectedTagStack.append(" to close");
3060 for (int i = depth; i > 0; i--)
3061 {
3062 if(i != depth) {
3063 expectedTagStack.append(" and");
3064 }
3065 if (elRawName == null || elRawName[i] == null) {
3066 String tagName = new String(elRawName[i], 0, elRawNameEnd[i]);
3067 expectedTagStack.append(" start tag <"+tagName+">");
3068 expectedTagStack.append(" from line "+elRawNameLine[i]);
3069 } else {
3070 String tagName = new String(elRawName[i], 0, elRawNameEnd[i]);
3071 expectedTagStack.append(" start tag <").append(tagName).append(">");
3072 expectedTagStack.append(" from line ").append(elRawNameLine[i]);
3073 }
3074 }
3075 expectedTagStack.append(", parser stopped on");
3076 }
3077 }
3078 throw new EOFException("no more data available"
3079 +expectedTagStack.toString()+getPositionDescription());
3080 }
3081 }
3082 } else {
3083 throw new IOException("error reading input, returned "+ret);
3084 }
3085 }
3086
3087 private char more() throws IOException, XmlPullParserException {
3088 if(pos >= bufEnd) {
3089 fillBuf();
3090
3091 if(reachedEnd) return (char)-1;
3092 }
3093 final char ch = buf[pos++];
3094
3095
3096 if(ch == '\n') { ++lineNumber; columnNumber = 1; }
3097 else { ++columnNumber; }
3098 return ch;
3099 }
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111 private void ensurePC(int end) {
3112
3113 final int newSize = end > READ_CHUNK_SIZE ? 2 * end : 2 * READ_CHUNK_SIZE;
3114 final char[] newPC = new char[ newSize ];
3115 if(TRACE_SIZING) System.out.println("TRACE_SIZING ensurePC() "+pc.length+" ==> "+newSize+" end="+end);
3116 System.arraycopy(pc, 0, newPC, 0, pcEnd);
3117 pc = newPC;
3118
3119 }
3120
3121 private void joinPC() {
3122
3123
3124 final int len = posEnd - posStart;
3125 final int newEnd = pcEnd + len + 1;
3126 if(newEnd >= pc.length) ensurePC(newEnd);
3127
3128 System.arraycopy(buf, posStart, pc, pcEnd, len);
3129 pcEnd += len;
3130 usePC = true;
3131
3132 }
3133
3134 private char requireInput(char ch, char[] input)
3135 throws XmlPullParserException, IOException
3136 {
3137 for (int i = 0; i < input.length; i++)
3138 {
3139 if(ch != input[i]) {
3140 throw new XmlPullParserException(
3141 "expected "+printable(input[i])+" in "+new String(input)
3142 +" and not "+printable(ch), this, null);
3143 }
3144 ch = more();
3145 }
3146 return ch;
3147 }
3148
3149 private char requireNextS()
3150 throws XmlPullParserException, IOException
3151 {
3152 final char ch = more();
3153 if(!isS(ch)) {
3154 throw new XmlPullParserException(
3155 "white space is required and not "+printable(ch), this, null);
3156 }
3157 return skipS(ch);
3158 }
3159
3160 private char skipS(char ch)
3161 throws XmlPullParserException, IOException
3162 {
3163 while(isS(ch)) { ch = more(); }
3164 return ch;
3165 }
3166
3167
3168 private static final int LOOKUP_MAX = 0x400;
3169 private static final char LOOKUP_MAX_CHAR = (char)LOOKUP_MAX;
3170
3171
3172 private static boolean lookupNameStartChar[] = new boolean[ LOOKUP_MAX ];
3173 private static boolean lookupNameChar[] = new boolean[ LOOKUP_MAX ];
3174
3175 private static final void setName(char ch)
3176
3177 { lookupNameChar[ ch ] = true; }
3178 private static final void setNameStart(char ch)
3179
3180 { lookupNameStartChar[ ch ] = true; setName(ch); }
3181
3182 static {
3183 setNameStart(':');
3184 for (char ch = 'A'; ch <= 'Z'; ++ch) setNameStart(ch);
3185 setNameStart('_');
3186 for (char ch = 'a'; ch <= 'z'; ++ch) setNameStart(ch);
3187 for (char ch = '\u00c0'; ch <= '\u02FF'; ++ch) setNameStart(ch);
3188 for (char ch = '\u0370'; ch <= '\u037d'; ++ch) setNameStart(ch);
3189 for (char ch = '\u037f'; ch < '\u0400'; ++ch) setNameStart(ch);
3190
3191 setName('-');
3192 setName('.');
3193 for (char ch = '0'; ch <= '9'; ++ch) setName(ch);
3194 setName('\u00b7');
3195 for (char ch = '\u0300'; ch <= '\u036f'; ++ch) setName(ch);
3196 }
3197
3198
3199 private boolean isNameStartChar(char ch) {
3200 return ch < LOOKUP_MAX_CHAR
3201 ? lookupNameStartChar[ch]
3202 : (ch <= '\u2027') || (ch >= '\u202A' && ch <= '\u218F') || (ch >= '\u2800' && ch <= '\uFFEF');
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224 }
3225
3226
3227 private boolean isNameChar(char ch) {
3228
3229
3230
3231
3232 return ch < LOOKUP_MAX_CHAR
3233 ? lookupNameChar[ch]
3234 : (ch <= '\u2027') || (ch >= '\u202A' && ch <= '\u218F') || (ch >= '\u2800' && ch <= '\uFFEF');
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249 }
3250
3251 private boolean isS(char ch) {
3252 return (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t');
3253
3254 }
3255
3256
3257
3258
3259
3260
3261 private String printable(char ch) {
3262 if(ch == '\n') {
3263 return "\\n";
3264 } else if(ch == '\r') {
3265 return "\\r";
3266 } else if(ch == '\t') {
3267 return "\\t";
3268 } else if(ch == '\'') {
3269 return "\\'";
3270 } if(ch > 127 || ch < 32) {
3271 return "\\u"+Integer.toHexString(ch);
3272 }
3273 return ""+ch;
3274 }
3275
3276 private String printable(String s) {
3277 if(s == null) return null;
3278 final int sLen = s.length();
3279 StringBuffer buf = new StringBuffer(sLen + 10);
3280 for(int i = 0; i < sLen; ++i) {
3281 buf.append(printable(s.charAt(i)));
3282 }
3283 s = buf.toString();
3284 return s;
3285 }
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