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