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; // no Unicode in Java 1.4
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     final 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             char[] result = null;
2213             if (!noUnicode4) {
2214                 try {
2215                     result = Character.toChars(Integer.parseInt(sb.toString(), isHex ? 16 : 10));
2216                 } catch (IllegalArgumentException e) {
2217                     throw new XmlPullParserException("character reference (with "
2218                         + (isHex ? "hex" : "decimal")
2219                         + " value "
2220                         + sb.toString()
2221                         + ") is invalid", this, null);
2222                 } catch(NoSuchMethodError e) {
2223                     noUnicode4 = true;
2224                 }
2225             }
2226             if (noUnicode4) {
2227                 int i = Integer.parseInt(sb.toString(), isHex ? 16 : 10);
2228                 if (i > Character.MAX_VALUE) {
2229                     throw new XmlPullParserException("Unicode character reference (with "
2230                             + (isHex ? "hex" : "decimal")
2231                             + " value "
2232                             + sb.toString()
2233                             + ") is not supported in this runtime", this, null);
2234                 }
2235                 result = charRefOneCharBuf;
2236                 charRefOneCharBuf[0] = (char)i;
2237             }
2238             if(tokenize) {
2239                 text = newString(result, 0, result.length);
2240             }
2241             return result;
2242         } else {
2243             // [68]     EntityRef          ::=          '&' Name ';'
2244             // scan name until ;
2245             if(!isNameStartChar(ch)) {
2246                 throw new XmlPullParserException(
2247                     "entity reference names can not start with character '"
2248                         +printable(ch)+"'", this, null);
2249             }
2250             while(true) {
2251                 ch = more();
2252                 if(ch == ';') {
2253                     break;
2254                 }
2255                 if(!isNameChar(ch)) {
2256                     throw new XmlPullParserException(
2257                         "entity reference name can not contain character "
2258                             +printable(ch)+"'", this, null);
2259                 }
2260             }
2261             posEnd = pos - 1;
2262             // determine what name maps to
2263             final int len = posEnd - posStart;
2264             if(len == 2 && buf[posStart] == 'l' && buf[posStart+1] == 't') {
2265                 if(tokenize) {
2266                     text = "<";
2267                 }
2268                 charRefOneCharBuf[0] = '<';
2269                 return charRefOneCharBuf;
2270                 //if(paramPC || isParserTokenizing) {
2271                 //    if(pcEnd >= pc.length) ensurePC();
2272                 //   pc[pcEnd++] = '<';
2273                 //}
2274             } else if(len == 3 && buf[posStart] == 'a'
2275                           && buf[posStart+1] == 'm' && buf[posStart+2] == 'p') {
2276                 if(tokenize) {
2277                     text = "&";
2278                 }
2279                 charRefOneCharBuf[0] = '&';
2280                 return charRefOneCharBuf;
2281             } else if(len == 2 && buf[posStart] == 'g' && buf[posStart+1] == 't') {
2282                 if(tokenize) {
2283                     text = ">";
2284                 }
2285                 charRefOneCharBuf[0] = '>';
2286                 return charRefOneCharBuf;
2287             } else if(len == 4 && buf[posStart] == 'a' && buf[posStart+1] == 'p'
2288                           && buf[posStart+2] == 'o' && buf[posStart+3] == 's')
2289             {
2290                 if(tokenize) {
2291                     text = "'";
2292                 }
2293                 charRefOneCharBuf[0] = '\'';
2294                 return charRefOneCharBuf;
2295             } else if(len == 4 && buf[posStart] == 'q' && buf[posStart+1] == 'u'
2296                           && buf[posStart+2] == 'o' && buf[posStart+3] == 't')
2297             {
2298                 if(tokenize) {
2299                     text = "\"";
2300                 }
2301                 charRefOneCharBuf[0] = '"';
2302                 return charRefOneCharBuf;
2303             } else {
2304                 final char[] result = lookupEntityReplacement(len);
2305                 if(result != null) {
2306                     return result;
2307                 }
2308             }
2309             if(tokenize) text = null;
2310             return null;
2311         }
2312     }
2313 
2314     private char[] lookupEntityReplacement(int entityNameLen)
2315     {
2316         if(!allStringsInterned) {
2317             final int hash = fastHash(buf, posStart, posEnd - posStart);
2318             LOOP:
2319             for (int i = entityEnd - 1; i >= 0; --i)
2320             {
2321                 if(hash == entityNameHash[ i ] && entityNameLen == entityNameBuf[ i ].length) {
2322                     final char[] entityBuf = entityNameBuf[ i ];
2323                     for (int j = 0; j < entityNameLen; j++)
2324                     {
2325                         if(buf[posStart + j] != entityBuf[j]) continue LOOP;
2326                     }
2327                     if(tokenize) text = entityReplacement[ i ];
2328                     return entityReplacementBuf[ i ];
2329                 }
2330             }
2331         } else {
2332             entityRefName = newString(buf, posStart, posEnd - posStart);
2333             for (int i = entityEnd - 1; i >= 0; --i)
2334             {
2335                 // take advantage that interning for newStirng is enforced
2336                 if(entityRefName == entityName[ i ]) {
2337                     if(tokenize) text = entityReplacement[ i ];
2338                     return entityReplacementBuf[ i ];
2339                 }
2340             }
2341         }
2342         return null;
2343     }
2344 
2345 
2346     private void parseComment()
2347         throws XmlPullParserException, IOException
2348     {
2349         // implements XML 1.0 Section 2.5 Comments
2350 
2351         //ASSUMPTION: seen <!-
2352         char ch = more();
2353         if(ch != '-') throw new XmlPullParserException(
2354                 "expected <!-- for comment start", this, null);
2355         if(tokenize) posStart = pos;
2356 
2357         final int curLine = lineNumber;
2358         final int curColumn = columnNumber - 4;
2359         try {
2360             final boolean normalizeIgnorableWS = tokenize && !roundtripSupported;
2361             boolean normalizedCR = false;
2362 
2363             boolean seenDash = false;
2364             boolean seenDashDash = false;
2365             while(true) {
2366                 // scan until it hits -->
2367                 ch = more();
2368                 if (ch == (char)-1) {
2369                     throw new EOFException("no more data available"+getPositionDescription());
2370                 }
2371                 if(seenDashDash && ch != '>') {
2372                     throw new XmlPullParserException(
2373                         "in comment after two dashes (--) next character must be >"
2374                             +" not "+printable(ch), this, null);
2375                 }
2376                 if(ch == '-') {
2377                     if(!seenDash) {
2378                         seenDash = true;
2379                     } else {
2380                         seenDashDash = true;
2381                     }
2382                 } else if(ch == '>') {
2383                     if(seenDashDash) {
2384                         break;  // found end sequence!!!!
2385                     }
2386                     seenDash = false;
2387                 } else {
2388                     seenDash = false;
2389                 }
2390                 if(normalizeIgnorableWS) {
2391                     if(ch == '\r') {
2392                         normalizedCR = true;
2393                         //posEnd = pos -1;
2394                         //joinPC();
2395                         // posEnd is already set
2396                         if(!usePC) {
2397                             posEnd = pos -1;
2398                             if(posEnd > posStart) {
2399                                 joinPC();
2400                             } else {
2401                                 usePC = true;
2402                                 pcStart = pcEnd = 0;
2403                             }
2404                         }
2405                         //assert usePC == true;
2406                         if(pcEnd >= pc.length) ensurePC(pcEnd);
2407                         pc[pcEnd++] = '\n';
2408                     } else if(ch == '\n') {
2409                         if(!normalizedCR && usePC) {
2410                             if(pcEnd >= pc.length) ensurePC(pcEnd);
2411                             pc[pcEnd++] = '\n';
2412                         }
2413                         normalizedCR = false;
2414                     } else {
2415                         if(usePC) {
2416                             if(pcEnd >= pc.length) ensurePC(pcEnd);
2417                             pc[pcEnd++] = ch;
2418                         }
2419                         normalizedCR = false;
2420                     }
2421                 }
2422             }
2423 
2424         } catch(EOFException ex) {
2425             // detect EOF and create meaningful error ...
2426             throw new XmlPullParserException(
2427                 "comment started on line "+curLine+" and column "+curColumn+" was not closed",
2428                 this, ex);
2429         }
2430         if(tokenize) {
2431             posEnd = pos - 3;
2432             if(usePC) {
2433                 pcEnd -= 2;
2434             }
2435         }
2436     }
2437 
2438     private boolean parsePI()
2439         throws XmlPullParserException, IOException
2440     {
2441         // implements XML 1.0 Section 2.6 Processing Instructions
2442 
2443         // [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
2444         // [17] PITarget         ::=    Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
2445         //ASSUMPTION: seen <?
2446         if(tokenize) posStart = pos;
2447         final int curLine = lineNumber;
2448         final int curColumn = columnNumber - 2;
2449         int piTargetStart = pos;
2450         int piTargetEnd = -1;
2451         final boolean normalizeIgnorableWS = tokenize && !roundtripSupported;
2452         boolean normalizedCR = false;
2453 
2454         try {
2455             boolean seenPITarget = false;
2456             boolean seenQ = false;
2457             char ch = more();
2458             if(isS(ch)) {
2459                 throw new XmlPullParserException(
2460                     "processing instruction PITarget must be exactly after <? and not white space character",
2461                     this, null);
2462             }
2463             while(true) {
2464                 // scan until it hits ?>
2465                 //ch = more();
2466 
2467                 if (ch == (char)-1) {
2468                     throw new EOFException("no more data available"+getPositionDescription());
2469                 }
2470                 if(ch == '?') {
2471                     if (!seenPITarget) {
2472                         throw new XmlPullParserException("processing instruction PITarget name not found", this, null);
2473                     }
2474                     seenQ = true;
2475                 } else if(ch == '>') {
2476                     if (seenQ) {
2477                         break;  // found end sequence!!!!
2478                     }
2479                     if (!seenPITarget) {
2480                         throw new XmlPullParserException("processing instruction PITarget name not found", this, null);
2481                     }
2482                 } else {
2483                     if(piTargetEnd == -1 && isS(ch)) {
2484                         piTargetEnd = pos - 1;
2485 
2486                         // [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
2487                         if((piTargetEnd - piTargetStart) == 3) {
2488                             if((buf[piTargetStart] == 'x' || buf[piTargetStart] == 'X')
2489                                    && (buf[piTargetStart+1] == 'm' || buf[piTargetStart+1] == 'M')
2490                                    && (buf[piTargetStart+2] == 'l' || buf[piTargetStart+2] == 'L')
2491                               )
2492                             {
2493                                 if(piTargetStart > 3) {  //<?xml is allowed as first characters in input ...
2494                                     throw new XmlPullParserException(
2495                                         "processing instruction can not have PITarget with reserved xml name",
2496                                         this, null);
2497                                 } else {
2498                                     if(buf[piTargetStart] != 'x'
2499                                            && buf[piTargetStart+1] != 'm'
2500                                            && buf[piTargetStart+2] != 'l')
2501                                     {
2502                                         throw new XmlPullParserException(
2503                                             "XMLDecl must have xml name in lowercase",
2504                                             this, null);
2505                                     }
2506                                 }
2507                                 parseXmlDecl(ch);
2508                                 if(tokenize) posEnd = pos - 2;
2509                                 final int off = piTargetStart + 3;
2510                                 final int len = pos - 2 - off;
2511                                 xmlDeclContent = newString(buf, off, len);
2512                                 return true;
2513                             }
2514                         }
2515                     }
2516                     seenQ = false;
2517                 }
2518                 if(normalizeIgnorableWS) {
2519                     if(ch == '\r') {
2520                         normalizedCR = true;
2521                         //posEnd = pos -1;
2522                         //joinPC();
2523                         // posEnd is already set
2524                         if(!usePC) {
2525                             posEnd = pos -1;
2526                             if(posEnd > posStart) {
2527                                 joinPC();
2528                             } else {
2529                                 usePC = true;
2530                                 pcStart = pcEnd = 0;
2531                             }
2532                         }
2533                         //assert usePC == true;
2534                         if(pcEnd >= pc.length) ensurePC(pcEnd);
2535                         pc[pcEnd++] = '\n';
2536                     } else if(ch == '\n') {
2537                         if(!normalizedCR && usePC) {
2538                             if(pcEnd >= pc.length) ensurePC(pcEnd);
2539                             pc[pcEnd++] = '\n';
2540                         }
2541                         normalizedCR = false;
2542                     } else {
2543                         if(usePC) {
2544                             if(pcEnd >= pc.length) ensurePC(pcEnd);
2545                             pc[pcEnd++] = ch;
2546                         }
2547                         normalizedCR = false;
2548                     }
2549                 }
2550                 seenPITarget = true;
2551                 ch = more();
2552             }
2553         } catch(EOFException ex) {
2554             // detect EOF and create meaningful error ...
2555             throw new XmlPullParserException(
2556                 "processing instruction started on line " + curLine +
2557                 " and column " + curColumn + " was not closed",
2558                 this, ex);
2559         }
2560         if(piTargetEnd == -1) {
2561             piTargetEnd = pos - 2 + bufAbsoluteStart;
2562             //throw new XmlPullParserException(
2563             //    "processing instruction must have PITarget name", this, null);
2564         }
2565         if(tokenize) {
2566             posEnd = pos - 2;
2567             if(normalizeIgnorableWS) {
2568                 --pcEnd;
2569             }
2570         }
2571         return false;
2572     }
2573 
2574     //    private final static char[] VERSION = {'v','e','r','s','i','o','n'};
2575     //    private final static char[] NCODING = {'n','c','o','d','i','n','g'};
2576     //    private final static char[] TANDALONE = {'t','a','n','d','a','l','o','n','e'};
2577     //    private final static char[] YES = {'y','e','s'};
2578     //    private final static char[] NO = {'n','o'};
2579 
2580     private final static char[] VERSION = "version".toCharArray();
2581     private final static char[] NCODING = "ncoding".toCharArray();
2582     private final static char[] TANDALONE = "tandalone".toCharArray();
2583     private final static char[] YES = "yes".toCharArray();
2584     private final static char[] NO = "no".toCharArray();
2585 
2586 
2587 
2588     private void parseXmlDecl(char ch)
2589         throws XmlPullParserException, IOException
2590     {
2591         // [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
2592 
2593         // first make sure that relative positions will stay OK
2594         preventBufferCompaction = true;
2595         bufStart = 0; // necessary to keep pos unchanged during expansion!
2596 
2597         // --- parse VersionInfo
2598 
2599         // [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"')
2600         // parse is positioned just on first S past <?xml
2601         ch = skipS(ch);
2602         ch = requireInput(ch, VERSION);
2603         // [25] Eq ::= S? '=' S?
2604         ch = skipS(ch);
2605         if(ch != '=') {
2606             throw new XmlPullParserException(
2607                 "expected equals sign (=) after version and not "+printable(ch), this, null);
2608         }
2609         ch = more();
2610         ch = skipS(ch);
2611         if(ch != '\'' && ch != '"') {
2612             throw new XmlPullParserException(
2613                 "expected apostrophe (') or quotation mark (\") after version and not "
2614                     +printable(ch), this, null);
2615         }
2616         final char quotChar = ch;
2617         //int versionStart = pos + bufAbsoluteStart;  // required if preventBufferCompaction==false
2618         final int versionStart = pos;
2619         ch = more();
2620         // [26] VersionNum ::= ([a-zA-Z0-9_.:] | '-')+
2621         while(ch != quotChar) {
2622             if((ch  < 'a' || ch > 'z') && (ch  < 'A' || ch > 'Z') && (ch  < '0' || ch > '9')
2623                    && ch != '_' && ch != '.' && ch != ':' && ch != '-')
2624             {
2625                 throw new XmlPullParserException(
2626                     "<?xml version value expected to be in ([a-zA-Z0-9_.:] | '-')"
2627                         +" not "+printable(ch), this, null);
2628             }
2629             ch = more();
2630         }
2631         final int versionEnd = pos - 1;
2632         parseXmlDeclWithVersion(versionStart, versionEnd);
2633         preventBufferCompaction = false; // allow again buffer compaction - pos MAY change
2634     }
2635     //private String xmlDeclVersion;
2636 
2637     private void parseXmlDeclWithVersion(int versionStart, int versionEnd)
2638         throws XmlPullParserException, IOException
2639     {
2640         String oldEncoding = this.inputEncoding;
2641 
2642         // check version is "1.0"
2643         if((versionEnd - versionStart != 3)
2644                || buf[versionStart] != '1'
2645                || buf[versionStart+1] != '.'
2646                || buf[versionStart+2] != '0')
2647         {
2648             throw new XmlPullParserException(
2649                 "only 1.0 is supported as <?xml version not '"
2650                     +printable(new String(buf, versionStart, versionEnd - versionStart))+"'", this, null);
2651         }
2652         xmlDeclVersion = newString(buf, versionStart, versionEnd - versionStart);
2653 
2654         // [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" )
2655         char ch = more();
2656         ch = skipS(ch);
2657         if(ch == 'e') {
2658             ch = more();
2659             ch = requireInput(ch, NCODING);
2660             ch = skipS(ch);
2661             if(ch != '=') {
2662                 throw new XmlPullParserException(
2663                     "expected equals sign (=) after encoding and not "+printable(ch), this, null);
2664             }
2665             ch = more();
2666             ch = skipS(ch);
2667             if(ch != '\'' && ch != '"') {
2668                 throw new XmlPullParserException(
2669                     "expected apostrophe (') or quotation mark (\") after encoding and not "
2670                         +printable(ch), this, null);
2671             }
2672             final char quotChar = ch;
2673             final int encodingStart = pos;
2674             ch = more();
2675             // [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
2676             if((ch  < 'a' || ch > 'z') && (ch  < 'A' || ch > 'Z'))
2677             {
2678                 throw new XmlPullParserException(
2679                     "<?xml encoding name expected to start with [A-Za-z]"
2680                         +" not "+printable(ch), this, null);
2681             }
2682             ch = more();
2683             while(ch != quotChar) {
2684                 if((ch  < 'a' || ch > 'z') && (ch  < 'A' || ch > 'Z') && (ch  < '0' || ch > '9')
2685                        && ch != '.' && ch != '_' && ch != '-')
2686                 {
2687                     throw new XmlPullParserException(
2688                         "<?xml encoding value expected to be in ([A-Za-z0-9._] | '-')"
2689                             +" not "+printable(ch), this, null);
2690                 }
2691                 ch = more();
2692             }
2693             final int encodingEnd = pos - 1;
2694 
2695 
2696             // TODO reconcile with setInput encodingName
2697             inputEncoding = newString(buf, encodingStart, encodingEnd - encodingStart);
2698             ch = more();
2699         }
2700 
2701         ch = skipS(ch);
2702         // [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"'))
2703         if(ch == 's') {
2704             ch = more();
2705             ch = requireInput(ch, TANDALONE);
2706             ch = skipS(ch);
2707             if(ch != '=') {
2708                 throw new XmlPullParserException(
2709                     "expected equals sign (=) after standalone and not "+printable(ch),
2710                     this, null);
2711             }
2712             ch = more();
2713             ch = skipS(ch);
2714             if(ch != '\'' && ch != '"') {
2715                 throw new XmlPullParserException(
2716                     "expected apostrophe (') or quotation mark (\") after encoding and not "
2717                         +printable(ch), this, null);
2718             }
2719             char quotChar = ch;
2720             int standaloneStart = pos;
2721             ch = more();
2722             if(ch == 'y') {
2723                 ch = requireInput(ch, YES);
2724                 //Boolean standalone = new Boolean(true);
2725                 xmlDeclStandalone = Boolean.TRUE;
2726             } else if(ch == 'n') {
2727                 ch = requireInput(ch, NO);
2728                 //Boolean standalone = new Boolean(false);
2729                 xmlDeclStandalone = Boolean.FALSE;
2730             } else {
2731                 throw new XmlPullParserException(
2732                     "expected 'yes' or 'no' after standalone and not "
2733                         +printable(ch), this, null);
2734             }
2735             if(ch != quotChar) {
2736                 throw new XmlPullParserException(
2737                     "expected "+quotChar+" after standalone value not "
2738                         +printable(ch), this, null);
2739             }
2740             ch = more();
2741         }
2742 
2743 
2744         ch = skipS(ch);
2745         if(ch != '?') {
2746             throw new XmlPullParserException(
2747                 "expected ?> as last part of <?xml not "
2748                     +printable(ch), this, null);
2749         }
2750         ch = more();
2751         if(ch != '>') {
2752             throw new XmlPullParserException(
2753                 "expected ?> as last part of <?xml not "
2754                     +printable(ch), this, null);
2755         }
2756 
2757 // NOTE: this code is broken as for some types of input streams (URLConnection ...)
2758 // it is not possible to do more than once new InputStreamReader(inputStream)
2759 // as it somehow detects it and closes underlying input stream (b.....d!)
2760 // In future one will need better low level byte-by-byte reading of prolog and then doing InputStream ...
2761 // for more details see http://www.extreme.indiana.edu/bugzilla/show_bug.cgi?id=135
2762         //        //reset input stream
2763 //        if ((this.inputEncoding != oldEncoding) && (this.inputStream != null)) {
2764 //            if ((this.inputEncoding != null) && (!this.inputEncoding.equalsIgnoreCase(oldEncoding))) {
2765 //                //              //there is need to reparse input to set location OK
2766 //                //              reset();
2767 //                this.reader = new InputStreamReader(this.inputStream, this.inputEncoding);
2768 //                //              //skip <?xml
2769 //                //              for (int i = 0; i < 5; i++){
2770 //                //                  ch=more();
2771 //                //              }
2772 //                //              parseXmlDecl(ch);
2773 //            }
2774 //        }
2775     }
2776     private void parseDocdecl()
2777         throws XmlPullParserException, IOException
2778     {
2779         //ASSUMPTION: seen <!D
2780         char ch = more();
2781         if(ch != 'O') throw new XmlPullParserException(
2782                 "expected <!DOCTYPE", this, null);
2783         ch = more();
2784         if(ch != 'C') throw new XmlPullParserException(
2785                 "expected <!DOCTYPE", this, null);
2786         ch = more();
2787         if(ch != 'T') throw new XmlPullParserException(
2788                 "expected <!DOCTYPE", this, null);
2789         ch = more();
2790         if(ch != 'Y') throw new XmlPullParserException(
2791                 "expected <!DOCTYPE", this, null);
2792         ch = more();
2793         if(ch != 'P') throw new XmlPullParserException(
2794                 "expected <!DOCTYPE", this, null);
2795         ch = more();
2796         if(ch != 'E') throw new XmlPullParserException(
2797                 "expected <!DOCTYPE", this, null);
2798         posStart = pos;
2799         // do simple and crude scanning for end of doctype
2800 
2801         // [28]  doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('['
2802         //                      (markupdecl | DeclSep)* ']' S?)? '>'
2803         int bracketLevel = 0;
2804         final boolean normalizeIgnorableWS = tokenize && !roundtripSupported;
2805         boolean normalizedCR = false;
2806         while(true) {
2807             ch = more();
2808             if(ch == '[') ++bracketLevel;
2809             if(ch == ']') --bracketLevel;
2810             if(ch == '>' && bracketLevel == 0) break;
2811             if(normalizeIgnorableWS) {
2812                 if(ch == '\r') {
2813                     normalizedCR = true;
2814                     //posEnd = pos -1;
2815                     //joinPC();
2816                     // posEnd is already set
2817                     if(!usePC) {
2818                         posEnd = pos -1;
2819                         if(posEnd > posStart) {
2820                             joinPC();
2821                         } else {
2822                             usePC = true;
2823                             pcStart = pcEnd = 0;
2824                         }
2825                     }
2826                     //assert usePC == true;
2827                     if(pcEnd >= pc.length) ensurePC(pcEnd);
2828                     pc[pcEnd++] = '\n';
2829                 } else if(ch == '\n') {
2830                     if(!normalizedCR && usePC) {
2831                         if(pcEnd >= pc.length) ensurePC(pcEnd);
2832                         pc[pcEnd++] = '\n';
2833                     }
2834                     normalizedCR = false;
2835                 } else {
2836                     if(usePC) {
2837                         if(pcEnd >= pc.length) ensurePC(pcEnd);
2838                         pc[pcEnd++] = ch;
2839                     }
2840                     normalizedCR = false;
2841                 }
2842             }
2843 
2844         }
2845         posEnd = pos - 1;
2846     }
2847 
2848     private void parseCDSect(boolean hadCharData)
2849         throws XmlPullParserException, IOException
2850     {
2851         // implements XML 1.0 Section 2.7 CDATA Sections
2852 
2853         // [18] CDSect ::= CDStart CData CDEnd
2854         // [19] CDStart ::=  '<![CDATA['
2855         // [20] CData ::= (Char* - (Char* ']]>' Char*))
2856         // [21] CDEnd ::= ']]>'
2857 
2858         //ASSUMPTION: seen <![
2859         char ch = more();
2860         if(ch != 'C') throw new XmlPullParserException(
2861                 "expected <[CDATA[ for comment start", this, null);
2862         ch = more();
2863         if(ch != 'D') throw new XmlPullParserException(
2864                 "expected <[CDATA[ for comment start", this, null);
2865         ch = more();
2866         if(ch != 'A') throw new XmlPullParserException(
2867                 "expected <[CDATA[ for comment start", this, null);
2868         ch = more();
2869         if(ch != 'T') throw new XmlPullParserException(
2870                 "expected <[CDATA[ for comment start", this, null);
2871         ch = more();
2872         if(ch != 'A') throw new XmlPullParserException(
2873                 "expected <[CDATA[ for comment start", this, null);
2874         ch = more();
2875         if(ch != '[') throw new XmlPullParserException(
2876                 "expected <![CDATA[ for comment start", this, null);
2877 
2878         //if(tokenize) {
2879         final int cdStart = pos + bufAbsoluteStart;
2880         final int curLine = lineNumber;
2881         final int curColumn = columnNumber;
2882         final boolean normalizeInput = !tokenize || !roundtripSupported;
2883         try {
2884             if(normalizeInput) {
2885                 if(hadCharData) {
2886                     if(!usePC) {
2887                         // posEnd is correct already!!!
2888                         if(posEnd > posStart) {
2889                             joinPC();
2890                         } else {
2891                             usePC = true;
2892                             pcStart = pcEnd = 0;
2893                         }
2894                     }
2895                 }
2896             }
2897             boolean seenBracket = false;
2898             boolean seenBracketBracket = false;
2899             boolean normalizedCR = false;
2900             while(true) {
2901                 // scan until it hits "]]>"
2902                 ch = more();
2903                 if(ch == ']') {
2904                     if(!seenBracket) {
2905                         seenBracket = true;
2906                     } else {
2907                         seenBracketBracket = true;
2908                         //seenBracket = false;
2909                     }
2910                 } else if(ch == '>') {
2911                     if(seenBracket && seenBracketBracket) {
2912                         break;  // found end sequence!!!!
2913                     } else {
2914                         seenBracketBracket = false;
2915                     }
2916                     seenBracket = false;
2917                 } else {
2918                     if(seenBracket) {
2919                         seenBracketBracket = seenBracket = false;
2920                     }
2921                 }
2922                 if(normalizeInput) {
2923                     // deal with normalization issues ...
2924                     if(ch == '\r') {
2925                         normalizedCR = true;
2926                         posStart = cdStart - bufAbsoluteStart;
2927                         posEnd = pos - 1; // posEnd is already set
2928                         if(!usePC) {
2929                             if(posEnd > posStart) {
2930                                 joinPC();
2931                             } else {
2932                                 usePC = true;
2933                                 pcStart = pcEnd = 0;
2934                             }
2935                         }
2936                         //assert usePC == true;
2937                         if(pcEnd >= pc.length) ensurePC(pcEnd);
2938                         pc[pcEnd++] = '\n';
2939                     } else if(ch == '\n') {
2940                         if(!normalizedCR && usePC) {
2941                             if(pcEnd >= pc.length) ensurePC(pcEnd);
2942                             pc[pcEnd++] = '\n';
2943                         }
2944                         normalizedCR = false;
2945                     } else {
2946                         if(usePC) {
2947                             if(pcEnd >= pc.length) ensurePC(pcEnd);
2948                             pc[pcEnd++] = ch;
2949                         }
2950                         normalizedCR = false;
2951                     }
2952                 }
2953             }
2954         } catch(EOFException ex) {
2955             // detect EOF and create meaningful error ...
2956             throw new XmlPullParserException(
2957                 "CDATA section started on line "+curLine+" and column "+curColumn+" was not closed",
2958                 this, ex);
2959         }
2960         if(normalizeInput) {
2961             if(usePC) {
2962                 pcEnd = pcEnd - 2;
2963             }
2964         }
2965         posStart = cdStart - bufAbsoluteStart;
2966         posEnd = pos - 3;
2967     }
2968 
2969     private void fillBuf() throws IOException, XmlPullParserException {
2970         if(reader == null) throw new XmlPullParserException(
2971                 "reader must be set before parsing is started");
2972 
2973         // see if we are in compaction area
2974         if(bufEnd > bufSoftLimit) {
2975 
2976             // expand buffer it makes sense!!!!
2977             // check if we need to compact or expand the buffer
2978             boolean compact = !preventBufferCompaction && (bufStart > bufSoftLimit || bufStart >= buf.length / 2);
2979 
2980             // if buffer almost full then compact it
2981             if(compact) {
2982                 //TODO: look on trashing
2983                 // //assert bufStart > 0
2984                 System.arraycopy(buf, bufStart, buf, 0, bufEnd - bufStart);
2985                 if(TRACE_SIZING) System.out.println(
2986                         "TRACE_SIZING fillBuf() compacting "+bufStart
2987                             +" bufEnd="+bufEnd
2988                             +" pos="+pos+" posStart="+posStart+" posEnd="+posEnd
2989                             +" buf first 100 chars:"
2990                             + new String(buf, 0, Math.min(bufEnd, 100)));;
2991 
2992             } else {
2993                 final int newSize = 2 * buf.length;
2994                 final char newBuf[] = new char[ newSize ];
2995                 if(TRACE_SIZING) System.out.println("TRACE_SIZING fillBuf() "+buf.length+" => "+newSize);
2996                 System.arraycopy(buf, bufStart, newBuf, 0, bufEnd - bufStart);
2997                 buf = newBuf;
2998                 if(bufLoadFactor > 0) {
2999                     // Include a fix for
3000                     // https://web.archive.org/web/20070831191548/http://www.extreme.indiana.edu/bugzilla/show_bug.cgi?id=228
3001                     bufSoftLimit = (int)(bufferLoadFactor * buf.length);
3002                 }
3003             }
3004             bufEnd -= bufStart;
3005             pos -= bufStart;
3006             posStart -= bufStart;
3007             posEnd -= bufStart;
3008             bufAbsoluteStart += bufStart;
3009             bufStart = 0;
3010             if(TRACE_SIZING) System.out.println(
3011                     "TRACE_SIZING fillBuf() after bufEnd="+bufEnd
3012                         +" pos="+pos+" posStart="+posStart+" posEnd="+posEnd
3013                         +" buf first 100 chars:"+new String(buf, 0, Math.min(bufEnd, 100)));
3014         }
3015         // at least one character must be read or error
3016         final int len = Math.min(buf.length - bufEnd, READ_CHUNK_SIZE);
3017         final int ret = reader.read(buf, bufEnd, len);
3018         if(ret > 0) {
3019             bufEnd += ret;
3020             if(TRACE_SIZING) System.out.println(
3021                     "TRACE_SIZING fillBuf() after filling in buffer"
3022                         +" buf first 100 chars:"+new String(buf, 0, Math.min(bufEnd, 100)));
3023 
3024             return;
3025         }
3026         if(ret == -1) {
3027             if(bufAbsoluteStart == 0 && pos == 0) {
3028                 throw new EOFException("input contained no data");
3029             } else {
3030                 if(seenRoot && depth == 0) { // inside parsing epilog!!!
3031                     reachedEnd = true;
3032                     return;
3033                 } else {
3034                     StringBuffer expectedTagStack = new StringBuffer();
3035                     if(depth > 0) {
3036                         if (elRawName == null || elRawName[depth] == null) {
3037                             String tagName = new String(buf, posStart + 1, pos - posStart - 1);
3038                             expectedTagStack.append(" - expected the opening tag <").append(tagName).append("...>");
3039                         } else {
3040                             //final char[] cbuf = elRawName[depth];
3041                             //final String startname = new String(cbuf, 0, elRawNameEnd[depth]);
3042                             expectedTagStack.append(" - expected end tag");
3043                             if(depth > 1) {
3044                                 expectedTagStack.append("s"); //more than one end tag
3045                             }
3046                             expectedTagStack.append(" ");
3047                             for (int i = depth; i > 0; i--)
3048                             {
3049                                 if (elRawName == null || elRawName[i] == null) {
3050                                     String tagName = new String(buf, posStart + 1, pos - posStart - 1);
3051                                     expectedTagStack
3052                                         .append(" - expected the opening tag <")
3053                                         .append(tagName)
3054                                         .append("...>");
3055                                 } else {
3056                                     String tagName = new String(elRawName[i], 0, elRawNameEnd[i]);
3057                                     expectedTagStack.append("</").append(tagName).append('>');
3058                                 }
3059                             }
3060                             expectedTagStack.append(" to close");
3061                             for (int i = depth; i > 0; i--)
3062                             {
3063                                 if(i != depth) {
3064                                     expectedTagStack.append(" and"); //more than one end tag
3065                                 }
3066                                 if (elRawName == null || elRawName[i] == null) {
3067                                     String tagName = new String(elRawName[i], 0, elRawNameEnd[i]);
3068                                     expectedTagStack.append(" start tag <"+tagName+">");
3069                                     expectedTagStack.append(" from line "+elRawNameLine[i]);
3070                                 } else {
3071                                     String tagName = new String(elRawName[i], 0, elRawNameEnd[i]);
3072                                     expectedTagStack.append(" start tag <").append(tagName).append(">");
3073                                     expectedTagStack.append(" from line ").append(elRawNameLine[i]);
3074                                 }
3075                             }
3076                             expectedTagStack.append(", parser stopped on");
3077                         }
3078                     }
3079                     throw new EOFException("no more data available"
3080                                                +expectedTagStack.toString()+getPositionDescription());
3081                 }
3082             }
3083         } else {
3084             throw new IOException("error reading input, returned "+ret);
3085         }
3086     }
3087 
3088     private char more() throws IOException, XmlPullParserException {
3089         if(pos >= bufEnd) {
3090             fillBuf();
3091             // this return value should be ignored as it is used in epilog parsing ...
3092             if(reachedEnd) return (char)-1;
3093         }
3094         final char ch = buf[pos++];
3095         //System.out.println(lineNumber +"/"+columnNumber+": "+printable(ch));
3096         //line/columnNumber
3097         if(ch == '\n') { ++lineNumber; columnNumber = 1; }
3098         else { ++columnNumber; }
3099         return ch;
3100     }
3101 
3102     //    /**
3103     //     * This function returns position of parser in XML input stream
3104     //     * (how many <b>characters</b> were processed.
3105     //     * <p><b>NOTE:</b> this logical position and not byte offset as encodings
3106     //     * such as UTF8 may use more than one byte to encode one character.
3107     //     */
3108     //    public int getCurrentInputPosition() {
3109     //        return pos + bufAbsoluteStart;
3110     //    }
3111 
3112     private void ensurePC(int end) {
3113         //assert end >= pc.length;
3114         final int newSize = end > READ_CHUNK_SIZE ? 2 * end : 2 * READ_CHUNK_SIZE;
3115         final char[] newPC = new char[ newSize ];
3116         if(TRACE_SIZING) System.out.println("TRACE_SIZING ensurePC() "+pc.length+" ==> "+newSize+" end="+end);
3117         System.arraycopy(pc, 0, newPC, 0, pcEnd);
3118         pc = newPC;
3119         //assert end < pc.length;
3120     }
3121 
3122     private void joinPC() {
3123         //assert usePC == false;
3124         //assert posEnd > posStart;
3125         final int len = posEnd - posStart;
3126         final int newEnd = pcEnd + len + 1;
3127         if(newEnd >= pc.length) ensurePC(newEnd); // add 1 for extra space for one char
3128         //assert newEnd < pc.length;
3129         System.arraycopy(buf, posStart, pc, pcEnd, len);
3130         pcEnd += len;
3131         usePC = true;
3132 
3133     }
3134 
3135     private char requireInput(char ch, char[] input)
3136         throws XmlPullParserException, IOException
3137     {
3138         for (int i = 0; i < input.length; i++)
3139         {
3140             if(ch != input[i]) {
3141                 throw new XmlPullParserException(
3142                     "expected "+printable(input[i])+" in "+new String(input)
3143                         +" and not "+printable(ch), this, null);
3144             }
3145             ch = more();
3146         }
3147         return ch;
3148     }
3149 
3150     private char requireNextS()
3151         throws XmlPullParserException, IOException
3152     {
3153         final char ch = more();
3154         if(!isS(ch)) {
3155             throw new XmlPullParserException(
3156                 "white space is required and not "+printable(ch), this, null);
3157         }
3158         return skipS(ch);
3159     }
3160 
3161     private char skipS(char ch)
3162         throws XmlPullParserException, IOException
3163     {
3164         while(isS(ch)) { ch = more(); } // skip additional spaces
3165         return ch;
3166     }
3167 
3168     // nameStart / name lookup tables based on XML 1.1 http://www.w3.org/TR/2001/WD-xml11-20011213/
3169     private static final int LOOKUP_MAX = 0x400;
3170     private static final char LOOKUP_MAX_CHAR = (char)LOOKUP_MAX;
3171     //    private static int lookupNameStartChar[] = new int[ LOOKUP_MAX_CHAR / 32 ];
3172     //    private static int lookupNameChar[] = new int[ LOOKUP_MAX_CHAR / 32 ];
3173     private static boolean lookupNameStartChar[] = new boolean[ LOOKUP_MAX ];
3174     private static boolean lookupNameChar[] = new boolean[ LOOKUP_MAX ];
3175 
3176     private static final void setName(char ch)
3177         //{ lookupNameChar[ (int)ch / 32 ] |= (1 << (ch % 32)); }
3178     { lookupNameChar[ ch ] = true; }
3179     private static final void setNameStart(char ch)
3180         //{ lookupNameStartChar[ (int)ch / 32 ] |= (1 << (ch % 32)); setName(ch); }
3181     { lookupNameStartChar[ ch ] = true; setName(ch); }
3182 
3183     static {
3184         setNameStart(':');
3185         for (char ch = 'A'; ch <= 'Z'; ++ch) setNameStart(ch);
3186         setNameStart('_');
3187         for (char ch = 'a'; ch <= 'z'; ++ch) setNameStart(ch);
3188         for (char ch = '\u00c0'; ch <= '\u02FF'; ++ch) setNameStart(ch);
3189         for (char ch = '\u0370'; ch <= '\u037d'; ++ch) setNameStart(ch);
3190         for (char ch = '\u037f'; ch < '\u0400'; ++ch) setNameStart(ch);
3191 
3192         setName('-');
3193         setName('.');
3194         for (char ch = '0'; ch <= '9'; ++ch) setName(ch);
3195         setName('\u00b7');
3196         for (char ch = '\u0300'; ch <= '\u036f'; ++ch) setName(ch);
3197     }
3198 
3199     //private final static boolean isNameStartChar(char ch) {
3200     private boolean isNameStartChar(char ch) {
3201         return ch < LOOKUP_MAX_CHAR
3202                 ? lookupNameStartChar[ch]
3203                 : (ch <= '\u2027') || (ch >= '\u202A' && ch <= '\u218F') || (ch >= '\u2800' && ch <= '\uFFEF');
3204 
3205         //      if(ch < LOOKUP_MAX_CHAR) return lookupNameStartChar[ ch ];
3206         //      else return ch <= '\u2027'
3207         //              || (ch >= '\u202A' &&  ch <= '\u218F')
3208         //              || (ch >= '\u2800' &&  ch <= '\uFFEF')
3209         //              ;
3210         //return false;
3211         //        return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == ':'
3212         //          || (ch >= '0' && ch <= '9');
3213         //        if(ch < LOOKUP_MAX_CHAR) return (lookupNameStartChar[ (int)ch / 32 ] & (1 << (ch % 32))) != 0;
3214         //        if(ch <= '\u2027') return true;
3215         //        //[#x202A-#x218F]
3216         //        if(ch < '\u202A') return false;
3217         //        if(ch <= '\u218F') return true;
3218         //        // added parts [#x2800-#xD7FF] | [#xE000-#xFDCF] | [#xFDE0-#xFFEF] | [#x10000-#x10FFFF]
3219         //        if(ch < '\u2800') return false;
3220         //        if(ch <= '\uFFEF') return true;
3221         //        return false;
3222 
3223 
3224         // else return (supportXml11 && ( (ch < '\u2027') || (ch > '\u2029' && ch < '\u2200') ...
3225     }
3226 
3227     //private final static boolean isNameChar(char ch) {
3228     private boolean isNameChar(char ch) {
3229         //return isNameStartChar(ch);
3230 
3231         //        if(ch < LOOKUP_MAX_CHAR) return (lookupNameChar[ (int)ch / 32 ] & (1 << (ch % 32))) != 0;
3232 
3233         return ch < LOOKUP_MAX_CHAR
3234                 ? lookupNameChar[ch]
3235                 : (ch <= '\u2027') || (ch >= '\u202A' && ch <= '\u218F') || (ch >= '\u2800' && ch <= '\uFFEF');
3236         //return false;
3237         //        return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == ':'
3238         //          || (ch >= '0' && ch <= '9');
3239         //        if(ch < LOOKUP_MAX_CHAR) return (lookupNameStartChar[ (int)ch / 32 ] & (1 << (ch % 32))) != 0;
3240 
3241         //else return
3242         //  else if(ch <= '\u2027') return true;
3243         //        //[#x202A-#x218F]
3244         //        else if(ch < '\u202A') return false;
3245         //        else if(ch <= '\u218F') return true;
3246         //        // added parts [#x2800-#xD7FF] | [#xE000-#xFDCF] | [#xFDE0-#xFFEF] | [#x10000-#x10FFFF]
3247         //        else if(ch < '\u2800') return false;
3248         //        else if(ch <= '\uFFEF') return true;
3249         //else return false;
3250     }
3251 
3252     private boolean isS(char ch) {
3253         return (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t');
3254         // || (supportXml11 && (ch == '\u0085' || ch == '\u2028');
3255     }
3256 
3257     //private boolean isChar(char ch) { return (ch < '\uD800' || ch > '\uDFFF')
3258     //  ch != '\u0000' ch < '\uFFFE'
3259 
3260 
3261     //private char printable(char ch) { return ch; }
3262     private String printable(char ch) {
3263         if(ch == '\n') {
3264             return "\\n";
3265         } else if(ch == '\r') {
3266             return "\\r";
3267         } else if(ch == '\t') {
3268             return "\\t";
3269         } else if(ch == '\'') {
3270             return "\\'";
3271         } if(ch > 127 || ch < 32) {
3272             return "\\u"+Integer.toHexString(ch);
3273         }
3274         return ""+ch;
3275     }
3276 
3277     private String printable(String s) {
3278         if(s == null) return null;
3279         final int sLen = s.length();
3280         StringBuffer buf = new StringBuffer(sLen + 10);
3281         for(int i = 0; i < sLen; ++i) {
3282             buf.append(printable(s.charAt(i)));
3283         }
3284         s = buf.toString();
3285         return s;
3286     }
3287 }
3288 
3289 
3290 /*
3291  * Indiana University Extreme! Lab Software License, Version 1.2
3292  *
3293  * Copyright (C) 2003 The Trustees of Indiana University.
3294  * All rights reserved.
3295  *
3296  * Redistribution and use in source and binary forms, with or without
3297  * modification, are permitted provided that the following conditions are
3298  * met:
3299  *
3300  * 1) All redistributions of source code must retain the above
3301  *    copyright notice, the list of authors in the original source
3302  *    code, this list of conditions and the disclaimer listed in this
3303  *    license;
3304  *
3305  * 2) All redistributions in binary form must reproduce the above
3306  *    copyright notice, this list of conditions and the disclaimer
3307  *    listed in this license in the documentation and/or other
3308  *    materials provided with the distribution;
3309  *
3310  * 3) Any documentation included with all redistributions must include
3311  *    the following acknowledgement:
3312  *
3313  *      "This product includes software developed by the Indiana
3314  *      University Extreme! Lab.  For further information please visit
3315  *      http://www.extreme.indiana.edu/"
3316  *
3317  *    Alternatively, this acknowledgment may appear in the software
3318  *    itself, and wherever such third-party acknowledgments normally
3319  *    appear.
3320  *
3321  * 4) The name "Indiana University" or "Indiana University
3322  *    Extreme! Lab" shall not be used to endorse or promote
3323  *    products derived from this software without prior written
3324  *    permission from Indiana University.  For written permission,
3325  *    please contact http://www.extreme.indiana.edu/.
3326  *
3327  * 5) Products derived from this software may not use "Indiana
3328  *    University" name nor may "Indiana University" appear in their name,
3329  *    without prior written permission of the Indiana University.
3330  *
3331  * Indiana University provides no reassurances that the source code
3332  * provided does not infringe the patent or any other intellectual
3333  * property rights of any other entity.  Indiana University disclaims any
3334  * liability to any recipient for claims brought by any other entity
3335  * based on infringement of intellectual property rights or otherwise.
3336  *
3337  * LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH
3338  * NO WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA
3339  * UNIVERSITY GIVES NO WARRANTIES AND MAKES NO REPRESENTATION THAT
3340  * SOFTWARE IS FREE OF INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR
3341  * OTHER PROPRIETARY RIGHTS.  INDIANA UNIVERSITY MAKES NO WARRANTIES THAT
3342  * SOFTWARE IS FREE FROM "BUGS", "VIRUSES", "TROJAN HORSES", "TRAP
3343  * DOORS", "WORMS", OR OTHER HARMFUL CODE.  LICENSEE ASSUMES THE ENTIRE
3344  * RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR ASSOCIATED MATERIALS,
3345  * AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION GENERATED USING
3346  * SOFTWARE.
3347  */
3348