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