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