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