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