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