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