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