View Javadoc
1   /* -*-             c-basic-offset: 4; indent-tabs-mode: nil; -*-  //------100-columns-wide------>|*/
2   /*
3    * Copyright (c) 2003 Extreme! Lab, Indiana University. All rights reserved.
4    *
5    * This software is open source. See the bottom of this file for the licence.
6    *
7    * $Id$
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  //import java.util.Hashtable;
22  
23  //TODO best handling of interning issues
24  //   have isAllNewStringInterned ???
25  
26  //TODO handling surrogate pairs: http://www.unicode.org/unicode/faq/utf_bom.html#6
27  
28  //TODO review code for use of bufAbsoluteStart when keeping pos between next()/fillBuf()
29  
30  /**
31   * Absolutely minimal implementation of XMLPULL V1 API. Encoding handling done with XmlReader
32   *
33   * @see org.codehaus.plexus.util.xml.XmlReader
34   * @author <a href="http://www.extreme.indiana.edu/~aslom/">Aleksander Slominski</a>
35   */
36  
37  public class MXParser
38      implements XmlPullParser
39  {
40      // NOTE: no interning of those strings --> by Java leng spec they MUST be already interned
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          // "http://xmlpull.org/v1/doc/features.html#xml-roundtrip";
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       * Implementation notice: the is instance variable that controls if newString() is interning.
64       * <p>
65       * <b>NOTE:</b> newStringIntern <b>always</b> returns interned strings and newString MAY return interned String
66       * depending on this variable.
67       * <p>
68       * <b>NOTE:</b> by default in this minimal implementation it is false!
69       */
70      private boolean allStringsInterned;
71  
72      private void resetStringCache()
73      {
74          // System.out.println("resetStringCache() minimum called");
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      // NOTE: features are not resetable and typically defaults to false ...
90      private boolean processNamespaces;
91  
92      private boolean roundtripSupported;
93  
94      // global parser state
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     // element stack
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     // private String elValue[];
125     private int elNamespaceCount[];
126 
127     private String fileEncoding = null;
128 
129     /**
130      * Make sure that we have enough space to keep element stack if passed size. It will always create one additional
131      * slot then current depth
132      */
133     private void ensureElementsCapacity()
134     {
135         final int elStackSize = elName != null ? elName.length : 0;
136         if ( ( depth + 1 ) >= elStackSize )
137         {
138             // we add at least one extra slot ...
139             final int newSize = ( depth >= 7 ? 2 * depth : 8 ) + 2; // = lucky 7 + 1 //25
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             // resue arr local variable slot
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                 // special initialization
168                 iarr[0] = 0;
169             }
170             elNamespaceCount = iarr;
171 
172             // TODO: avoid using element raw name ...
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             // arr = new String[newSize];
194             // if(needsCopying) System.arraycopy(elLocalName, 0, arr, 0, elStackSize);
195             // elLocalName = arr;
196             // arr = new String[newSize];
197             // if(needsCopying) System.arraycopy(elDefaultNs, 0, arr, 0, elStackSize);
198             // elDefaultNs = arr;
199             // int[] iarr = new int[newSize];
200             // if(needsCopying) System.arraycopy(elNsStackPos, 0, iarr, 0, elStackSize);
201             // for (int i = elStackSize; i < iarr.length; i++)
202             // {
203             // iarr[i] = (i > 0) ? -1 : 0;
204             // }
205             // elNsStackPos = iarr;
206             // assert depth < elName.length;
207         }
208     }
209 
210     // attribute stack
211     private int attributeCount;
212 
213     private String attributeName[];
214 
215     private int attributeNameHash[];
216 
217     // private int attributeNameStart[];
218     // private int attributeNameEnd[];
219     private String attributePrefix[];
220 
221     private String attributeUri[];
222 
223     private String attributeValue[];
224     // private int attributeValueStart[];
225     // private int attributeValueEnd[];
226 
227     // Make sure that in attributes temporary array is enough space.
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; // = lucky 7 + 1 //25
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             // //assert attrUri.length > size
271         }
272     }
273 
274     // namespace stack
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; // = lucky 7 + 1 //25
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             // prefixesSize = newSize;
313             // //assert nsPrefixes.length > size && nsPrefixes.length == newSize
314         }
315     }
316 
317 
318      // simplistic implementation of hash function that has <b>constant</b> time to compute - so it also means
319      // diminishing hash quality for long strings but for XML parsing it should be good enough ...
320 
321     private static final int fastHash( char ch[], int off, int len )
322     {
323         if ( len == 0 )
324             return 0;
325         // assert len >0
326         int hash = ch[off]; // hash at beginning
327         // try {
328         hash = ( hash << 7 ) + ch[off + len - 1]; // hash at the end
329         // } catch(ArrayIndexOutOfBoundsException aie) {
330         // aie.printStackTrace(); //should never happen ...
331         // throw new RuntimeException("this is violation of pre-condition");
332         // }
333         if ( len > 16 )
334             hash = ( hash << 7 ) + ch[off + ( len / 4 )]; // 1/4 from beginning
335         if ( len > 8 )
336             hash = ( hash << 7 ) + ch[off + ( len / 2 )]; // 1/2 of string size ...
337         // notice that hash is at most done 3 times <<7 so shifted by 21 bits 8 bit value
338         // so max result == 29 bits so it is quite just below 31 bits for long (2^32) ...
339         // assert hash >= 0;
340         return hash;
341     }
342 
343     // entity replacement stack
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; // = lucky 7 + 1 //25
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     // input buffer management
397     private static final int READ_CHUNK_SIZE = 8 * 1024; // max data chars in one read() call
398 
399     private Reader reader;
400 
401     private String inputEncoding;
402 
403     private int bufLoadFactor = 95; // 99%
404     // private int bufHardLimit; // only matters when expanding
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 ); // desirable size of buffer
411 
412     private boolean preventBufferCompaction;
413 
414     private int bufAbsoluteStart; // this is buf
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     // parsing state
433     // private boolean needsMore;
434     // private boolean seenMarkup;
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     // transient variable set during each call to next/Token()
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         // System.out.println("reset() called");
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             // This is a bit cheeky, since the EntityReplacementMap contains exact-sized arrays,
525             // and elements are always added to the array, we can use the array from the template.
526             // Kids; dont do this at home.
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      * Method setFeature
538      *
539      * @param name a String
540      * @param state a boolean
541      * @throws XmlPullParserException issue
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             // } else if(FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) {
556             // if(type != START_DOCUMENT) throw new XmlPullParserException(
557             // "namespace reporting feature can only be changed before parsing", this, null);
558             // reportNsAttribs = state;
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             // } else if(REPORT_DOCDECL.equals(name)) {
574             // paramNotifyDoctype = state;
575         }
576         else if ( FEATURE_XML_ROUNDTRIP.equals( name ) )
577         {
578             // if(state == false) {
579             // throw new XmlPullParserException(
580             // "roundtrip feature can not be switched off");
581             // }
582             roundtripSupported = state;
583         }
584         else
585         {
586             throw new XmlPullParserException( "unsupported feature " + name );
587         }
588     }
589 
590     /**
591      * Unknown properties are <strong>always</strong> returned as false
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             // } else if(FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) {
602             // return reportNsAttribs;
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             // } else if(REPORT_DOCDECL.equals(name)) {
612             // return paramNotifyDoctype;
613         }
614         else if ( FEATURE_XML_ROUNDTRIP.equals( name ) )
615         {
616             // return true;
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             // encoding already detected
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         // must be here as reset() was called in setInput() and has set this.inputEncoding to null ...
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         // throw new XmlPullParserException("not allowed");
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         // private char[] entityReplacement[];
741         ensureEntityCapacity();
742 
743         // this is to make sure that if interning works we will take advantage of it ...
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         // TODO disallow < or & in entity replacement text (or ]]>???)
756         // TOOD keepEntityNormalizedForAttributeValue cached as well ...
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         // int maxDepth = eventType == END_TAG ? this.depth + 1 : this.depth;
768         // if(depth < 0 || depth > maxDepth) throw new IllegalArgumentException(
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         // int end = eventType == END_TAG ? elNamespaceCount[ depth + 1 ] : namespaceEnd;
780         // if(pos < end) {
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         // int end = eventType == END_TAG ? elNamespaceCount[ depth + 1 ] : namespaceEnd;
797         // if(pos < end) {
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     // throws XmlPullParserException
812     {
813         // int count = namespaceCount[ depth ];
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                 { // "") { //null ) { //TODO check FIXME Alek
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         // System.err.println("bufStart="+bufStart+" b="+printable(new String(b, start, end - start))+" start="+start+"
855         // end="+end);
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; // try to find good location
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      * Return string describing current position of parsers as text 'STATE [seen %s...] @line:column'.
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             // System.err.println("start="+start);
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         // return " at line "+tokenizerPosRow
898         // +" and column "+(tokenizerPosCol-1)
899         // +(fragment != null ? " seen "+printable(fragment)+"..." : "");
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             // throw new XmlPullParserException("no content available to read");
954             // if(roundtripSupported) {
955             // text = new String(buf, posStart, posEnd - posStart);
956             // } else {
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             // throw new XmlPullParserException("no content available to read");
1008             holderForStartAndLength[0] = holderForStartAndLength[1] = -1;
1009             return null;
1010         }
1011         else
1012         {
1013             throw new IllegalArgumentException( "unknown text eventType: " + eventType );
1014         }
1015         // String s = getText();
1016         // char[] cb = null;
1017         // if(s!= null) {
1018         // cb = s.toCharArray();
1019         // holderForStartAndLength[0] = 0;
1020         // holderForStartAndLength[1] = s.length();
1021         // } else {
1022         // }
1023         // return cb;
1024     }
1025 
1026     @Override
1027     public String getNamespace()
1028     {
1029         if ( eventType == START_TAG )
1030         {
1031             // return processNamespaces ? elUri[ depth - 1 ] : NO_NAMESPACE;
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         // String prefix = elPrefix[ maxDepth ];
1040         // if(prefix != null) {
1041         // for( int i = namespaceEnd -1; i >= 0; i--) {
1042         // if( prefix.equals( namespacePrefix[ i ] ) ) {
1043         // return namespaceUri[ i ];
1044         // }
1045         // }
1046         // } else {
1047         // for( int i = namespaceEnd -1; i >= 0; i--) {
1048         // if( namespacePrefix[ i ] == null ) {
1049         // return namespaceUri[ i ];
1050         // }
1051         // }
1052         //
1053         // }
1054         // return "";
1055     }
1056 
1057     @Override
1058     public String getName()
1059     {
1060         if ( eventType == START_TAG )
1061         {
1062             // return elName[ depth - 1 ] ;
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             // return elPrefix[ depth - 1 ] ;
1089             return elPrefix[depth];
1090         }
1091         else if ( eventType == END_TAG )
1092         {
1093             return elPrefix[depth];
1094         }
1095         return null;
1096         // if(eventType != START_TAG && eventType != END_TAG) return null;
1097         // int maxDepth = eventType == END_TAG ? depth : depth - 1;
1098         // return elPrefix[ maxDepth ];
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         // TODO make check if namespace is interned!!! etc. for names!!!
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                     // (namespace != null && namespace.equals(attributeUri[ i ]))
1209                     // taking advantage of String.intern()
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      * <p>Skip sub tree that is currently parser positioned on.</p>
1272      * NOTE: parser must be on START_TAG and when function returns parser will be positioned on corresponding END_TAG
1273      * @throws XmlPullParserException issue
1274      * @throws IOException io
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     // public String readText() throws XmlPullParserException, IOException
1296     // {
1297     // if (getEventType() != TEXT) return "";
1298     // String result = getText();
1299     // next();
1300     // return result;
1301     // }
1302 
1303     @Override
1304     public String nextText()
1305         throws XmlPullParserException, IOException
1306     {
1307         // String result = null;
1308         // boolean onStartTag = false;
1309         // if(eventType == START_TAG) {
1310         // onStartTag = true;
1311         // next();
1312         // }
1313         // if(eventType == TEXT) {
1314         // result = getText();
1315         // next();
1316         // } else if(onStartTag && eventType == END_TAG) {
1317         // result = "";
1318         // } else {
1319         // throw new XmlPullParserException(
1320         // "parser must be on START_TAG or TEXT to read text", this, null);
1321         // }
1322         // if(eventType != END_TAG) {
1323         // throw new XmlPullParserException(
1324         // "event TEXT it must be immediately followed by END_TAG", this, null);
1325         // }
1326         // return result;
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         { // skip whitespace
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]; // less namespaces available
1398         }
1399         if ( emptyElementTag )
1400         {
1401             emptyElementTag = false;
1402             pastEndTag = true;
1403             return eventType = END_TAG;
1404         }
1405 
1406         // [1] document ::= prolog element Misc*
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             // ASSUMPTION: we are _on_ first character of content or markup!!!!
1422             // [43] content ::= CharData? ((element | Reference | CDSect | PI | Comment) CharData?)*
1423             char ch;
1424             if ( seenMarkup )
1425             { // we have read ahead ...
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; // VERY IMPORTANT: this is correct start of event!!!
1439 
1440             // when true there is some potential event TEXT to return - keep gathering
1441             boolean hadCharData = false;
1442 
1443             // when true TEXT data is not continuous (like <![CDATA[text]]>) and requires PC merging
1444             boolean needsMerging = false;
1445 
1446             MAIN_LOOP: while ( true )
1447             {
1448                 // work on MARKUP
1449                 if ( ch == '<' )
1450                 {
1451                     if ( hadCharData )
1452                     {
1453                         // posEnd = pos - 1;
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                             // posEnd = pos - 2;
1467                             return eventType = TEXT;
1468                         }
1469                         return eventType = parseEndTag();
1470                     }
1471                     else if ( ch == '!' )
1472                     {
1473                         ch = more();
1474                         if ( ch == '-' )
1475                         {
1476                             // note: if(tokenize == false) posStart/End is NOT changed!!!!
1477                             parseComment();
1478                             if ( tokenize )
1479                                 return eventType = COMMENT;
1480                             if ( !usePC && hadCharData )
1481                             {
1482                                 needsMerging = true;
1483                             }
1484                             else
1485                             {
1486                                 posStart = pos; // completely ignore comment
1487                             }
1488                         }
1489                         else if ( ch == '[' )
1490                         {
1491                             // posEnd = pos - 3;
1492                             // must remember previous posStart/End as it merges with content of CDATA
1493                             // int oldStart = posStart + bufAbsoluteStart;
1494                             // int oldEnd = posEnd + bufAbsoluteStart;
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                             { // was there anything inside CDATA section?
1504                                 hadCharData = true;
1505                                 if ( !usePC )
1506                                 {
1507                                     needsMerging = true;
1508                                 }
1509                             }
1510 
1511                             // posStart = oldStart;
1512                             // posEnd = oldEnd;
1513                             // if(cdLen > 0) { // was there anything inside CDATA section?
1514                             // if(hadCharData) {
1515                             // // do merging if there was anything in CDSect!!!!
1516                             // // if(!usePC) {
1517                             // // // posEnd is correct already!!!
1518                             // // if(posEnd > posStart) {
1519                             // // joinPC();
1520                             // // } else {
1521                             // // usePC = true;
1522                             // // pcStart = pcEnd = 0;
1523                             // // }
1524                             // // }
1525                             // // if(pcEnd + cdLen >= pc.length) ensurePC(pcEnd + cdLen);
1526                             // // // copy [cdStart..cdEnd) into PC
1527                             // // System.arraycopy(buf, cdStart, pc, pcEnd, cdLen);
1528                             // // pcEnd += cdLen;
1529                             // if(!usePC) {
1530                             // needsMerging = true;
1531                             // posStart = cdStart;
1532                             // posEnd = cdEnd;
1533                             // }
1534                             // } else {
1535                             // if(!usePC) {
1536                             // needsMerging = true;
1537                             // posStart = cdStart;
1538                             // posEnd = cdEnd;
1539                             // hadCharData = true;
1540                             // }
1541                             // }
1542                             // //hadCharData = true;
1543                             // } else {
1544                             // if( !usePC && hadCharData ) {
1545                             // needsMerging = true;
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; // completely ignore PI
1567                         }
1568 
1569                     }
1570                     else if ( isNameStartChar( ch ) )
1571                     {
1572                         if ( !tokenize && hadCharData )
1573                         {
1574                             seenStartTag = true;
1575                             // posEnd = pos - 2;
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                     // do content compaction if it makes sense!!!!
1586 
1587                 }
1588                 else if ( ch == '&' )
1589                 {
1590                     // work on ENTITY
1591                     // posEnd = pos - 1;
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                     // check if replacement text can be resolved !!!
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                     // int entStart = posStart;
1613                     // int entEnd = posEnd;
1614                     posStart = oldStart - bufAbsoluteStart;
1615                     posEnd = oldEnd - bufAbsoluteStart;
1616                     if ( !usePC )
1617                     {
1618                         if ( hadCharData )
1619                         {
1620                             joinPC(); // posEnd is already set correctly!!!
1621                             needsMerging = false;
1622                         }
1623                         else
1624                         {
1625                             usePC = true;
1626                             pcStart = pcEnd = 0;
1627                         }
1628                     }
1629                     // assert usePC == true;
1630                     // write into PC replacement text - do merge for replacement text!!!!
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                     // assert needsMerging == false;
1642                 }
1643                 else
1644                 {
1645 
1646                     if ( needsMerging )
1647                     {
1648                         // assert usePC == false;
1649                         joinPC(); // posEnd is already set correctly!!!
1650                         // posStart = pos - 1;
1651                         needsMerging = false;
1652                     }
1653 
1654                     // no MARKUP not ENTITIES so work on character data ...
1655 
1656                     // [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
1657 
1658                     hadCharData = true;
1659 
1660                     boolean normalizedCR = false;
1661                     final boolean normalizeInput = !tokenize || !roundtripSupported;
1662                     // use loop locality here!!!!
1663                     boolean seenBracket = false;
1664                     boolean seenBracketBracket = false;
1665                     do
1666                     {
1667 
1668                         // check that ]]> does not show in
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                             // assert seenTwoBrackets == seenBracket == false;
1691                         }
1692                         if ( normalizeInput )
1693                         {
1694                             // deal with normalization issues ...
1695                             if ( ch == '\r' )
1696                             {
1697                                 normalizedCR = true;
1698                                 posEnd = pos - 1;
1699                                 // posEnd is already set
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                                 // assert usePC == true;
1713                                 if ( pcEnd >= pc.length )
1714                                     ensurePC( pcEnd );
1715                                 pc[pcEnd++] = '\n';
1716                             }
1717                             else if ( ch == '\n' )
1718                             {
1719                                 // if(!usePC) { joinPC(); } else { if(pcEnd >= pc.length) ensurePC(); }
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; // skip ch = more() from below - we are already ahead ...
1745                 }
1746                 ch = more();
1747             } // endless while(true)
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         // [2] prolog: ::= XMLDecl? Misc* (doctypedecl Misc*)? and look for [39] element
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             // bootstrap parsing with getting first character input!
1780             // deal with BOM
1781             // detect BOM and crop it (Unicode int Order Mark)
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                 // skipping UNICODE int Order Mark (so called BOM)
1790                 ch = more();
1791             }
1792             else if ( ch == '\uFFFD' )
1793             {
1794                 // UTF-16 BOM in an UTF-8 encoded file?
1795                 // This is a hack...not the best way to check for BOM in UTF-16
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             // deal with Misc
1812             // [27] Misc ::= Comment | PI | S
1813             // deal with docdecl --> mark it!
1814             // else parseStartTag seen <[^/]
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                     // check if it is 'xml'
1827                     // deal with XMLDecl
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                         // posEnd = pos -1;
1888                         // joinPC();
1889                         // posEnd is already set
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                         // assert usePC == true;
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             // epilog: Misc*
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                     // deal with Misc
1972                     // [27] Misc ::= Comment | PI | S
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                             // check if it is 'xml'
1989                             // deal with XMLDecl
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(); // FIXME
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                                 // posEnd = pos -1;
2045                                 // joinPC();
2046                                 // posEnd is already set
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                                 // assert usePC == true;
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             // throw Exception("unexpected content in epilog
2102             // catch EOFException return END_DOCUMENT
2103             // try {
2104         }
2105         catch ( EOFException ex )
2106         {
2107             reachedEnd = true;
2108         }
2109         if ( tokenize && gotS )
2110         {
2111             posEnd = pos; // well - this is LAST available character pos
2112             return eventType = IGNORABLE_WHITESPACE;
2113         }
2114         return eventType = END_DOCUMENT;
2115     }
2116 
2117     public int parseEndTag()
2118         throws XmlPullParserException, IOException
2119     {
2120         // ASSUMPTION ch is past "</"
2121         // [42] ETag ::= '</' Name S? '>'
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         // now we go one level down -- do checks
2136         // --depth; //FIXME
2137 
2138         // check that end tag name is the same as start tag
2139         // String name = new String(buf, nameStart - bufAbsoluteStart,
2140         // (pos - 1) - (nameStart - bufAbsoluteStart));
2141         // int last = pos - 1;
2142         int off = nameStart - bufAbsoluteStart;
2143         // final int len = last - off;
2144         final int len = ( pos - 1 ) - off;
2145         final char[] cbuf = elRawName[depth];
2146         if ( elRawNameEnd[depth] != len )
2147         {
2148             // construct strings for exception
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                 // construct strings for exception
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         } // skip additional white spaces
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         // namespaceEnd = elNamespaceCount[ depth ]; //FIXME
2177 
2178         posEnd = pos;
2179         pastEndTag = true;
2180         return eventType = END_TAG;
2181     }
2182 
2183     public int parseStartTag()
2184         throws XmlPullParserException, IOException
2185     {
2186         // ASSUMPTION ch is past <T
2187         // [40] STag ::= '<' Name (S Attribute)* S? '>'
2188         // [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
2189         ++depth; // FIXME
2190 
2191         posStart = pos - 2;
2192 
2193         emptyElementTag = false;
2194         attributeCount = 0;
2195         // retrieve name
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         // retrieve name
2217         ensureElementsCapacity();
2218 
2219         // TODO check for efficient interning and then use elRawNameInterned!!!!
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         // work on prefixes and namespace URI
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                                                   // (pos -1) - (colonPos + 1));
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             } // skip additional white spaces
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             // ch = more(); // skip space
2289         }
2290 
2291         // now when namespaces were declared we can resolve them
2292         if ( processNamespaces )
2293         {
2294             String uri = getNamespace( prefix );
2295             if ( uri == null )
2296             {
2297                 if ( prefix == null )
2298                 { // no prefix and no uri => use default namespace
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             // String uri = getNamespace(prefix);
2311             // if(uri == null && prefix == null) { // no prefix and no uri => use default namespace
2312             // uri = "";
2313             // }
2314             // resolve attribute namespaces
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             // TODO
2336             // [ WFC: Unique Att Spec ]
2337             // check namespaced attribute uniqueness constraint!!!
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                         // prepare data for nice error message?
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         { // ! processNamespaces
2366 
2367             // [ WFC: Unique Att Spec ]
2368             // check raw attribute uniqueness constraint!!!
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                         // prepare data for nice error message?
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         // parse attribute
2398         // [41] Attribute ::= Name Eq AttValue
2399         // [WFC: No External Entity References]
2400         // [WFC: No < in Attribute Values]
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                         // colonPos = pos - 1 + bufAbsoluteStart;
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         // --- start processing attributes
2462         String name = null;
2463         String prefix = null;
2464         // work on prefixes and namespace URI
2465         if ( processNamespaces )
2466         {
2467             if ( xmlnsPos < 4 )
2468                 startsWithXmlns = false;
2469             if ( startsWithXmlns )
2470             {
2471                 if ( colonPos != -1 )
2472                 {
2473                     // prefix = attributePrefix[ attributeCount ] = null;
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 = // attributeName[ attributeCount ] =
2481                         newString( buf, colonPos - bufAbsoluteStart + 1, nameLen );
2482                     // pos - 1 - (colonPos + 1 - bufAbsoluteStart)
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                     // colonPos - (nameStart - bufAbsoluteStart));
2493                     int nameLen = pos - 2 - ( colonPos - bufAbsoluteStart );
2494                     name = attributeName[attributeCount] = newString( buf, colonPos - bufAbsoluteStart + 1, nameLen );
2495                     // pos - 1 - (colonPos + 1 - bufAbsoluteStart));
2496 
2497                     // name.substring(0, colonPos-nameStart);
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             // retrieve name
2515             name = attributeName[attributeCount] =
2516                 newString( buf, nameStart - bufAbsoluteStart, pos - 1 - ( nameStart - bufAbsoluteStart ) );
2517             //// assert name != null;
2518             if ( !allStringsInterned )
2519             {
2520                 attributeNameHash[attributeCount] = name.hashCode();
2521             }
2522         }
2523 
2524         // [25] Eq ::= S? '=' S?
2525         while ( isS( ch ) )
2526         {
2527             ch = more();
2528         } // skip additional spaces
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         } // skip additional spaces
2536 
2537         // [10] AttValue ::= '"' ([^<&"] | Reference)* '"'
2538         // | "'" ([^<&'] | Reference)* "'"
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         // parse until delimit or < and resolve Reference
2544         // [67] Reference ::= EntityRef | CharRef
2545         // int valueStart = pos + bufAbsoluteStart;
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                 // do attribute value normalization
2571                 // as described in http://www.w3.org/TR/REC-xml#AVNormalize
2572                 // TODO add test for it form spec ...
2573                 // handle EOL normalization ...
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                 // assert usePC == true;
2588                 if ( pcEnd >= pc.length )
2589                     ensurePC( pcEnd );
2590                 if ( ch != '\n' || !normalizedCR )
2591                 {
2592                     pc[pcEnd++] = ' '; // '\n';
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                 // declare new namespace
2629                 namespacePrefix[namespaceEnd] = name;
2630                 if ( !allStringsInterned )
2631                 {
2632                     prefixHash = namespacePrefixHash[namespaceEnd] = name.hashCode();
2633                 }
2634             }
2635             else
2636             {
2637                 // declare new default namespace...
2638                 namespacePrefix[namespaceEnd] = null; // ""; //null; //TODO check FIXME Alek
2639                 if ( !allStringsInterned )
2640                 {
2641                     prefixHash = namespacePrefixHash[namespaceEnd] = -1;
2642                 }
2643             }
2644             namespaceUri[namespaceEnd] = ns;
2645 
2646             // detect duplicate namespace declarations!!!
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     // state representing that no entity ref have been resolved
2679     private static final char[] BUF_NOT_RESOLVED = new char[0];
2680 
2681     // predefined entity refs
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      * parse Entity Ref, either a character entity or one of the predefined name entities.
2692      *
2693      * @return the length of the valid found character reference, which may be one of the predefined character reference
2694      *         names (resolvedEntityRefCharBuf contains the replaced chars). Returns the length of the not found entity
2695      *         name, otherwise.
2696      * @throws XmlPullParserException if invalid XML is detected.
2697      * @throws IOException if an I/O error is found.
2698      */
2699     private int parseCharOrPredefinedEntityRef()
2700         throws XmlPullParserException, IOException
2701     {
2702         // entity reference http://www.w3.org/TR/2000/REC-xml-20001006#NT-Reference
2703         // [67] Reference ::= EntityRef | CharRef
2704 
2705         // ASSUMPTION just after &
2706         entityRefName = null;
2707         posStart = pos;
2708         int len = 0;
2709         resolvedEntityRefCharBuf = BUF_NOT_RESOLVED;
2710         char ch = more();
2711         if ( ch == '#' )
2712         {
2713             // parse character reference
2714 
2715             char charRef = 0;
2716             ch = more();
2717             StringBuilder sb = new StringBuilder();
2718             boolean isHex = ( ch == 'x' );
2719 
2720             if ( isHex )
2721             {
2722                 // encoded in hex
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                 // encoded in decimal
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             // [68] EntityRef ::= '&' Name ';'
2805             // scan name until ;
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             // determine what name maps to
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                 // if(paramPC || isParserTokenizing) {
2834                 // if(pcEnd >= pc.length) ensurePC();
2835                 // pc[pcEnd++] = '<';
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      * Parse an entity reference inside the DOCDECL section.
2881      *
2882      * @throws XmlPullParserException if invalid XML is detected.
2883      * @throws IOException if an I/O error is found.
2884      */
2885     private void parseEntityRefInDocDecl()
2886         throws XmlPullParserException, IOException
2887     {
2888         parseCharOrPredefinedEntityRef();
2889         if (usePC) {
2890             posStart--; // include in PC the starting '&' of the entity
2891             joinPC();
2892         }
2893 
2894         if ( resolvedEntityRefCharBuf != BUF_NOT_RESOLVED )
2895             return;
2896         if ( tokenize )
2897             text = null;
2898     }
2899 
2900     /**
2901      * Parse an entity reference inside a tag or attribute.
2902      *
2903      * @throws XmlPullParserException if invalid XML is detected.
2904      * @throws IOException if an I/O error is found.
2905      */
2906     private void parseEntityRef()
2907         throws XmlPullParserException, IOException
2908     {
2909         final int len = parseCharOrPredefinedEntityRef();
2910 
2911         posEnd--; // don't involve the final ';' from the entity in the search
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      * Check if the provided parameter is a valid Char. According to
2928      * <a href="https://www.w3.org/TR/REC-xml/#NT-Char">https://www.w3.org/TR/REC-xml/#NT-Char</a>
2929      *
2930      * @param codePoint the numeric value to check
2931      * @return true if it is a valid numeric character reference. False otherwise.
2932      */
2933     private static boolean isValidCodePoint( int codePoint )
2934     {
2935         // Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
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                 // take advantage that interning for newString is enforced
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         // implements XML 1.0 Section 2.5 Comments
2982 
2983         // ASSUMPTION: seen <!-
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                 // scan until it hits -->
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; // found end sequence!!!!
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                         // posEnd = pos -1;
3041                         // joinPC();
3042                         // posEnd is alreadys set
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                         // assert usePC == true;
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             // detect EOF and create meaningful error ...
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         // implements XML 1.0 Section 2.6 Processing Instructions
3105 
3106         // [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
3107         // [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
3108         // ASSUMPTION: seen <?
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                 // scan until it hits ?>
3132                 // ch = more();
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; // found end sequence!!!!
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                         // seenPITarget && !seenQ
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                         // [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
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                                 { // <?xml is allowed as first characters in input ...
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                         // posEnd = pos -1;
3216                         // joinPC();
3217                         // posEnd is alreadys set
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                         // assert usePC == true;
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             // detect EOF and create meaningful error ...
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             // throw new XmlPullParserException(
3271             // "processing instruction must have PITarget name", this, null);
3272         }
3273         if ( tokenize )
3274         {
3275             posEnd = pos - 2;
3276             if ( normalizeIgnorableWS )
3277             {
3278                 --pcEnd;
3279             }
3280         }
3281         return true;
3282     }
3283 
3284     // protected final static char[] VERSION = {'v','e','r','s','i','o','n'};
3285     // protected final static char[] NCODING = {'n','c','o','d','i','n','g'};
3286     // protected final static char[] TANDALONE = {'t','a','n','d','a','l','o','n','e'};
3287     // protected final static char[] YES = {'y','e','s'};
3288     // protected final static char[] NO = {'n','o'};
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         // [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
3304 
3305         // first make sure that relative positions will stay OK
3306         preventBufferCompaction = true;
3307         bufStart = 0; // necessary to keep pos unchanged during expansion!
3308 
3309         // --- parse VersionInfo
3310 
3311         // [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"')
3312         // parse is positioned just on first S past <?xml
3313         ch = skipS( ch );
3314         ch = requireInput( ch, VERSION );
3315         // [25] Eq ::= S? '=' S?
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         // int versionStart = pos + bufAbsoluteStart; // required if preventBufferCompaction==false
3331         final int versionStart = pos;
3332         ch = more();
3333         // [26] VersionNum ::= ([a-zA-Z0-9_.:] | '-')+
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; // allow again buffer compaction - pos MAY change
3347     }
3348     // private String xmlDeclVersion;
3349 
3350     private void parseXmlDeclWithVersion( int versionStart, int versionEnd )
3351         throws XmlPullParserException, IOException
3352     {
3353         // check version is "1.0"
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         // [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" )
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             // [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
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             // TODO reconcile with setInput encodingName
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         // [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"'))
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                 // Boolean standalone = new Boolean(true);
3469                 xmlDeclStandalone = true;
3470             }
3471             else if ( ch == 'n' )
3472             {
3473                 ch = requireInput( ch, NO );
3474                 // Boolean standalone = new Boolean(false);
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         // ASSUMPTION: seen <!D
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         // do simple and crude scanning for end of doctype
3527 
3528         // [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('['
3529         // (markupdecl | DeclSep)* ']' S?)? '>'
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                     // posEnd = pos -1;
3553                     // joinPC();
3554                     // posEnd is alreadys set
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                     // assert usePC == true;
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         // extractEntityRef
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         // extractEntityRef
3616         posEnd = pos - 1;
3617         if ( !usePC )
3618         {
3619             final boolean hadCharData = posEnd > posStart;
3620             if ( hadCharData )
3621             {
3622                 // posEnd is already set correctly!!!
3623                 joinPC();
3624             }
3625             else
3626             {
3627                 usePC = true;
3628                 pcStart = pcEnd = 0;
3629             }
3630         }
3631         // assert usePC == true;
3632 
3633         parseEntityRef();
3634         // check if replacement text can be resolved !!!
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         // write into PC replacement text - do merge for replacement text!!!!
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         // implements XML 1.0 Section 2.7 CDATA Sections
3659 
3660         // [18] CDSect ::= CDStart CData CDEnd
3661         // [19] CDStart ::= '<![CDATA['
3662         // [20] CData ::= (Char* - (Char* ']]>' Char*))
3663         // [21] CDEnd ::= ']]>'
3664 
3665         // ASSUMPTION: seen <![
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         // if(tokenize) {
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                         // posEnd is correct already!!!
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                 // scan until it hits "]]>"
3717                 ch = more();
3718                 if ( ch == ']' )
3719                 {
3720                     if ( !seenBracket )
3721                     {
3722                         seenBracket = true;
3723                     }
3724                     else
3725                     {
3726                         seenBracketBracket = true;
3727                         // seenBracket = false;
3728                     }
3729                 }
3730                 else if ( ch == '>' )
3731                 {
3732                     if ( seenBracket && seenBracketBracket )
3733                     {
3734                         break; // found end sequence!!!!
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                     // deal with normalization issues ...
3752                     if ( ch == '\r' )
3753                     {
3754                         normalizedCR = true;
3755                         posStart = cdStart - bufAbsoluteStart;
3756                         posEnd = pos - 1; // posEnd is alreadys set
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                         // assert usePC == true;
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             // detect EOF and create meaningful error ...
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         // see if we are in compaction area
3821         if ( bufEnd > bufSoftLimit )
3822         {
3823 
3824             // check if we need to compact or expand the buffer
3825             boolean compact = !preventBufferCompaction
3826                     && ( bufStart > bufSoftLimit || bufStart >= buf.length / 2 );
3827 
3828             // if buffer almost full then compact it
3829             if ( compact )
3830             {
3831                 // TODO: look on trashing
3832                 // //assert bufStart > 0
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                     // Include a fix for https://web.archive.org/web/20070831191548/http://www.extreme.indiana.edu/bugzilla/show_bug.cgi?id=228
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         // at least one character must be read or error
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                 { // inside parsing epilog!!!
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                             // final char[] cbuf = elRawName[depth];
3904                             // final String startname = new String(cbuf, 0, elRawNameEnd[depth]);
3905                             expectedTagStack.append( " - expected end tag" );
3906                             if ( depth > 1 )
3907                             {
3908                                 expectedTagStack.append( "s" ); // more than one end tag
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" ); // more than one end tag
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             // this return value should be ignored as it is used in epilog parsing ...
3966             if ( reachedEnd )
3967                 throw new EOFException( "no more data available" + getPositionDescription() );
3968         }
3969         final char ch = buf[pos++];
3970         // line/columnNumber
3971         if ( ch == '\n' )
3972         {
3973             ++lineNumber;
3974             columnNumber = 1;
3975         }
3976         else
3977         {
3978             ++columnNumber;
3979         }
3980         // System.out.print(ch);
3981         return ch;
3982     }
3983 
3984     // /**
3985     // * This function returns position of parser in XML input stream
3986     // * (how many <b>characters</b> were processed.
3987     // * <p><b>NOTE:</b> this logical position and not byte offset as encodings
3988     // * such as UTF8 may use more than one byte to encode one character.
3989     // */
3990     // public int getCurrentInputPosition() {
3991     // return pos + bufAbsoluteStart;
3992     // }
3993 
3994     private void ensurePC( int end )
3995     {
3996         // assert end >= pc.length;
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         // assert end < pc.length;
4004     }
4005 
4006     private void joinPC()
4007     {
4008         // assert usePC == false;
4009         // assert posEnd > posStart;
4010         final int len = posEnd - posStart;
4011         final int newEnd = pcEnd + len + 1;
4012         if ( newEnd >= pc.length )
4013             ensurePC( newEnd ); // add 1 for extra space for one char
4014         // assert newEnd < pc.length;
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         } // skip additional spaces
4043         return ch;
4044     }
4045 
4046     // nameStart / name lookup tables based on XML 1.1 http://www.w3.org/TR/2001/WD-xml11-20011213/
4047     private static final int LOOKUP_MAX = 0x400;
4048 
4049     private static final char LOOKUP_MAX_CHAR = (char) LOOKUP_MAX;
4050 
4051     // private static int lookupNameStartChar[] = new int[ LOOKUP_MAX_CHAR / 32 ];
4052     // private static int lookupNameChar[] = new int[ LOOKUP_MAX_CHAR / 32 ];
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     // { lookupNameChar[ (int)ch / 32 ] |= (1 << (ch % 32)); }
4059     {
4060         lookupNameChar[ch] = true;
4061     }
4062 
4063     private static void setNameStart( char ch )
4064     // { lookupNameStartChar[ (int)ch / 32 ] |= (1 << (ch % 32)); setName(ch); }
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     // protected boolean isNameStartChar( char ch )
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         // if(ch < LOOKUP_MAX_CHAR) return lookupNameStartChar[ ch ];
4101         // else return ch <= '\u2027'
4102         // || (ch >= '\u202A' && ch <= '\u218F')
4103         // || (ch >= '\u2800' && ch <= '\uFFEF')
4104         // ;
4105         // return false;
4106         // return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == ':'
4107         // || (ch >= '0' && ch <= '9');
4108         // if(ch < LOOKUP_MAX_CHAR) return (lookupNameStartChar[ (int)ch / 32 ] & (1 << (ch % 32))) != 0;
4109         // if(ch <= '\u2027') return true;
4110         // //[#x202A-#x218F]
4111         // if(ch < '\u202A') return false;
4112         // if(ch <= '\u218F') return true;
4113         // // added parts [#x2800-#xD7FF] | [#xE000-#xFDCF] | [#xFDE0-#xFFEF] | [#x10000-#x10FFFF]
4114         // if(ch < '\u2800') return false;
4115         // if(ch <= '\uFFEF') return true;
4116         // return false;
4117 
4118         // else return (supportXml11 && ( (ch < '\u2027') || (ch > '\u2029' && ch < '\u2200') ...
4119     }
4120 
4121     // protected boolean isNameChar( char ch )
4122     private static boolean isNameChar( char ch )
4123     {
4124         // return isNameStartChar(ch);
4125 
4126         // if(ch < LOOKUP_MAX_CHAR) return (lookupNameChar[ (int)ch / 32 ] & (1 << (ch % 32))) != 0;
4127 
4128         return ch < LOOKUP_MAX_CHAR ? lookupNameChar[ch] : ( ch <= '\u2027' )
4129             || ( ch >= '\u202A' && ch <= '\u218F' ) || ( ch >= '\u2800' && ch <= '\uFFEF' );
4130         // return false;
4131         // return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == ':'
4132         // || (ch >= '0' && ch <= '9');
4133         // if(ch < LOOKUP_MAX_CHAR) return (lookupNameStartChar[ (int)ch / 32 ] & (1 << (ch % 32))) != 0;
4134 
4135         // else return
4136         // else if(ch <= '\u2027') return true;
4137         // //[#x202A-#x218F]
4138         // else if(ch < '\u202A') return false;
4139         // else if(ch <= '\u218F') return true;
4140         // // added parts [#x2800-#xD7FF] | [#xE000-#xFDCF] | [#xFDE0-#xFFEF] | [#x10000-#x10FFFF]
4141         // else if(ch < '\u2800') return false;
4142         // else if(ch <= '\uFFEF') return true;
4143         // else return false;
4144     }
4145 
4146     private static boolean isS( char ch )
4147     {
4148         return ( ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' );
4149         // || (supportXml11 && (ch == '\u0085' || ch == '\u2028');
4150     }
4151 
4152     // protected boolean isChar(char ch) { return (ch < '\uD800' || ch > '\uDFFF')
4153     // ch != '\u0000' ch < '\uFFFE'
4154 
4155     // private char printable(char ch) { return ch; }
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  * Indiana University Extreme! Lab Software License, Version 1.2 Copyright (C) 2003 The Trustees of Indiana University.
4199  * All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted
4200  * provided that the following conditions are met: 1) All redistributions of source code must retain the above copyright
4201  * notice, the list of authors in the original source code, this list of conditions and the disclaimer listed in this
4202  * license; 2) All redistributions in binary form must reproduce the above copyright notice, this list of conditions and
4203  * the disclaimer listed in this license in the documentation and/or other materials provided with the distribution; 3)
4204  * Any documentation included with all redistributions must include the following acknowledgement: "This product
4205  * includes software developed by the Indiana University Extreme! Lab. For further information please visit
4206  * http://www.extreme.indiana.edu/" Alternatively, this acknowledgment may appear in the software itself, and wherever
4207  * such third-party acknowledgments normally appear. 4) The name "Indiana University" or "Indiana University Extreme!
4208  * Lab" shall not be used to endorse or promote products derived from this software without prior written permission
4209  * from Indiana University. For written permission, please contact http://www.extreme.indiana.edu/. 5) Products derived
4210  * from this software may not use "Indiana University" name nor may "Indiana University" appear in their name, without
4211  * prior written permission of the Indiana University. Indiana University provides no reassurances that the source code
4212  * provided does not infringe the patent or any other intellectual property rights of any other entity. Indiana
4213  * University disclaims any liability to any recipient for claims brought by any other entity based on infringement of
4214  * intellectual property rights or otherwise. LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO
4215  * WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES NO WARRANTIES AND MAKES NO
4216  * REPRESENTATION THAT SOFTWARE IS FREE OF INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
4217  * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", "VIRUSES", "TROJAN HORSES", "TRAP
4218  * DOORS", "WORMS", OR OTHER HARMFUL CODE. LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
4219  * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION GENERATED USING SOFTWARE.
4220  */