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