2003/11/18 08:02:31
[org.ibex.core.git] / src / org / xwt / util / XML.java
1 package org.xwt.util;
2
3 import java.io.Reader;
4 import java.io.IOException;
5 import java.io.EOFException;
6
7 /**
8  * An Event-Driving, Non-Validating XML Parser with Namespace support.
9  *
10  * A subclass can implement the abstract functions for receiving details
11  * about an xml file as it is parsed. To initate a parse, use the parse()
12  * function. 
13  *
14  * <h3>IMPLEMENTATION NOTES</h3>
15  * <p>As the parser traverses into an element, it adds it to the linked list
16  * called <tt>elements</tt>. However, <tt>elements</tt> has been pre-filled
17  * with instances of the Element inner class. So in the vast majority of
18  * cases, the pointer current is moved along one, and the values for the
19  * new element are filled into the current object.</p>
20  *
21  * <p>This parser supports all the unicode ranges required by the XML
22  * Specification. However, it is optimised for well-formed ASCII documents.
23  * Documents containing unicode Names and Attributes will take much longer
24  * to process, and invalid documents (badly formed Names or invalid attributes)
25  * will be run through a test on every single unicode character range before 
26  * being declared invalid.</p> 
27  *
28  * <h3>IMPLEMENTATION RULES</h3> 
29  * <ul>
30  *  <li>Each time the buffer offset <tt>off</tt> is moved, the length
31  *   <tt>len</tt> must be decreased.</li>
32  *  <li>Each time the buffer length is decreased, it must be checked to make
33  *   sure it is &gt;0.</li>
34  * </ul> 
35  *
36  * <h3>Other Notes</h3>
37  * <ul>
38  *  <li><i>error</i> is defined as a Validity Constraint Violation and is recoverable</li>
39  *  <li><i>fatal error</i> is defined as a Well-formedness Constraint Violation and is not recoverable</li>
40  * </ul> 
41  *
42  * @author David Crawshaw 
43  * @see XML-Specification-1.0 http://w3.org/TR/REC-xml
44  */
45 public abstract class XML
46 {
47     /////////////////////////////////////////////////////////////////////////////////////////////
48     // XML Parser
49     /////////////////////////////////////////////////////////////////////////////////////////////
50
51     public static final int BUFFER_SIZE = 255;
52
53     /** static pool of XML.Element instances shared by all XML Parsers.
54      * elements in the queue have dirty prev and next references, that need cleaning before use. */
55     private static final Queue elements = new Queue(30);
56
57     private static final char[] single_amp  = new char[] { '&'  };
58     private static final char[] single_apos = new char[] { '\'' };
59     private static final char[] single_gt   = new char[] { '>'  };
60     private static final char[] single_lt   = new char[] { '<'  };
61     private static final char[] single_quot = new char[] { '"'  };
62
63     private int line;
64     private int col;
65
66     private Reader in;
67     private char[] buf;
68     private int    off;
69     private int    len;
70
71     private Element current;
72
73     // used in readEntity() to process a single character without creating a new array
74     private char[] singlechar = new char[1];
75
76
77     public XML() { this(BUFFER_SIZE); }
78
79     public XML(int bSize) {
80         buf = new char[bSize];
81
82         current = (Element)elements.remove(false);
83         if (current == null) current = new Element();
84         current.prev = current.next = null;
85     }
86
87
88     /** Returns the line number at the beginning of the last process call. */
89     public int getLine() { return line; }
90
91     /** Returns the column number at the beginning of the last process call. */
92     public int getCol()  { return col; }
93
94     /**
95      * Parse given input and call the abstract event functions.
96      *
97      * Careful with threading, as this function is not synchronized.
98      */ 
99     public final void parse(Reader reader) throws IOException, XMLException {
100         in  = reader;
101         off = len = 0;
102         line = col = 1;
103
104         clean(); // clean up possible mid-way linked-list element
105
106         try {
107             // process the stream
108             while (true) {
109                 if (!buffer(1)) {
110                     if (current.qName == null) break;
111                     throw new WFCException("reached eof without closing <"+current.qName+"> element", getLine(), getCol());
112                 }
113
114                 if (buf[off] == '<') readTag();
115                 readChars(current.qName != null);
116             }
117         } finally { clean(); } // clean up elements
118     }
119
120     /** remove any leftover elements from the linked list and queue them */
121     private final void clean() {
122         while (current.prev != null) elements.append((current = current.prev).next);
123         current.next = null;
124         current.qName = null;
125     }
126
127     /** reads in a tag. expects <tt>buf[off] == '&#60;'</tt> */
128     private final void readTag() throws IOException, XMLException {
129         // Start Tag    '<' Name (S Attribute)* S? '>'
130         boolean starttag  = true;
131
132         // End Tag     '</' Name S? '>'
133         boolean endtag    = false;
134
135         // if (starttag & endtag) then: EmptyElemTag '<' Name (S Attribute)* S? '/>'
136
137         // Position in the name of the ':' namespace prefix
138         int prefix = -1;
139
140         int namelen   = 0;
141
142         col++; off++; len--;
143         if (!buffer(1)) throw new EOFException("Unexpected EOF processing element tag");
144
145         // work out what we can from the beginning of the tag
146         char s = buf[off]; 
147         if (s == '!') {
148             // definitions here don't necessarily conform to xml spec (as DTDs not yet implemented)
149             col++; off++; len--; 
150             if (!buffer(4)) throw new EOFException("Unexpected EOF processing <! element");
151
152             boolean bad = false;
153             switch (buf[off]) {
154                 case '-':
155                     if (buf[off+1] != '-') { bad = true; break; }
156                     col += 2; off += 2; len -= 2;
157
158                     // Comment        '<!--'      ((Char - '-') | ('-' (Char - '-')))* '-->'
159                     readChars(false, "-->", false); 
160                     col += 3; off += 3; len -= 3;
161                     break;
162
163                 // we don't care about the following definitions
164
165                 case 'A':
166                     if (!buffer(7)
167                             || buf[off+1] != 'T' || buf[off+2] != 'T' || buf[off+3] != 'L'
168                             || buf[off+4] != 'I' || buf[off+5] != 'S' || buf[off+6] != 'T') {
169                         bad = true; break;
170                     } 
171                     col += 7; off += 7; len -= 7; 
172
173                     // ATTLIST        '<!ATTLIST'   (Char* - '>') '>'
174                     readChars(false, ">", true); 
175                     col++; off++; len--;
176                     break;
177                 case 'D':
178                     if (!buffer(7)
179                             || buf[off+1] != 'O' || buf[off+2] != 'C' || buf[off+3] != 'T'
180                             || buf[off+4] != 'Y' || buf[off+5] != 'P' || buf[off+6] != 'E') {
181                         bad = true; break;
182                     }
183                     col += 7; off += 7; len -= 7;
184
185                     // DTD            '<!DOCTYPE'   (Char* - '>') '>'
186                     readChars(false, ">", true); 
187                     col++; off++; len--;
188                     break; 
189                 case 'E':
190                     if (!buffer(7)) {
191                         bad = true;
192                     } else if (buf[off+1] == 'L' && buf[off+2] == 'E' && buf[off+3] == 'M'
193                             && buf[off+4] == 'E' && buf[off+5] == 'N' && buf[off+6] == 'T') {
194                         // ELEMENT        '<!ELEMENT'   (Char* - '>') '>'
195                         readChars(false, ">", true); 
196                         col++; off++; len--;
197
198                     } else if (buf[off+1] == 'N' && buf[off+2] == 'T' && buf[off+3] == 'I'
199                             && buf[off+4] == 'T' && buf[off+5] == 'Y') {
200                         // ENTITY         '<!ENTITY'    (Char* - '>') '>'
201                         readChars(false, ">", true); 
202                         col++; off++; len--;
203
204                     } else {
205                         bad = true;
206                     }
207                     break;
208
209                 case 'N':
210                     if (!buffer(8)
211                             || buf[off+1] != 'O' || buf[off+2] != 'T' || buf[off+3] != 'A' || buf[off+4] != 'T'
212                             || buf[off+5] != 'I' || buf[off+6] != 'O' || buf[off+7] != 'N') {
213                         bad = true; break;
214                     }
215                     col += 8; off += 8; len -= 8;
216                     // NOTATION       '<!NOTATION'  (Char* - '>') '>'
217                     readChars(false, ">", true); 
218                     col++; off++; len--;
219
220                     break;
221                 default: bad = true;
222             }
223
224             if (bad) throw new MarkupException("element tag start character is invalid", getLine(), getCol());
225
226         } else if (s == '?') {
227             // PI (Ignored)   '<?'  (Char* - (Char* '?>' Char*))  '?>'
228             col++; off++; len--;
229             readChars(false, "?>", true);
230             if (!buffer(2)) throw new EOFException("Unexpected EOF at end of Processing Instruction");
231             col += 2; off += 2; len -= 2;
232
233         } else if (s == '[') {
234             if (!buffer(7)
235                     || buf[off+1] != 'C' || buf[off+2] != 'D' || buf[off+3] != 'A'
236                     || buf[off+4] != 'T' || buf[off+5] != 'A' || buf[off+6] != '[') {
237                 col++; off--; len++; 
238                 // Conditional    '<![' (Char* - (Char* ']]>' Char*)) ']]>'
239                 readChars(false, "]]>", false); 
240             } else {
241                 col += 7; off += 7; len -=7;
242                 // CDATA          '<![CDATA[' (Char* - (Char* ']]>' Char*))        ']]>'
243                 readChars(true, "]]>", false);
244             } 
245             col += 3; off += 3; len -= 3;
246         } else {
247             if (s == '/') {
248                 // End Tag        '</' Name S? '>'
249                 starttag = false; 
250                 endtag = true;
251
252                 col++; off++; len--;
253                 if (!buffer(1)) throw new EOFException("Unexpected EOF processing end tag");
254                 s = buf[off];
255             }
256
257             if (!Name(s)) throw new MarkupException("invalid starting character in element name", getLine(), getCol()); 
258
259             // find the element name (defined in XML Spec: section 2.3)
260             for (namelen = 0; ; namelen++) {
261                 if (!buffer(namelen+1)) throw new EOFException("Unexpected EOF in element tag name");
262
263                 s = buf[off+namelen];
264
265                 if (S(s) || s == '>') {
266                     break;
267                 } else if (s == '/') {
268                     endtag = true;
269                     break;
270                 } else if (s == ':' && namelen > 0 && prefix < 1) {
271                     // we have a definition of the prefix range available
272                     prefix = namelen; 
273                 } else if (!NameChar(s)) {
274                     throw new MarkupException("element name contains invalid character", getLine(), getCol());
275                 }
276             }
277
278             // process name (based on calculated region)
279             if (namelen < 1) throw new MarkupException("element name is null", getLine(), getCol()); 
280
281             // we have marked out the name region, so turn it into a string and move on
282             String qName = new String(buf, off, namelen);
283
284             col += namelen; off += namelen; len -= namelen;
285
286             if (starttag) {
287                 // create the in-memory element representation of this beast
288                 // if current.qName == null then this is the root element we're dealing with
289                 if (current.qName != null) {
290                     if (current.next == null)  {
291                         // we're at the end of the default element depth
292                         current.next = (Element)elements.remove(false);
293                         if (current.next == null) current.next = new Element();
294                         current.next.prev = current;
295                         current.next.next = null;
296                     }
297                     current = current.next;
298                 }
299
300                 current.clear();
301                 current.qName = qName;
302                 current.defaultUri = current.uri = null;
303
304                 if (prefix > 0) {
305                     current.prefix = current.qName.substring(0, prefix);
306                     current.localName = current.qName.substring(prefix+1);
307                 } else {
308                     current.prefix = null;
309                     current.localName = current.qName;
310                 }
311
312                 // process attributes
313                 readWhitespace(); 
314                 if (!buffer(1)) throw new EOFException("Unexpected EOF - processing attributes part 1");
315                 while (buf[off] != '/' && buf[off] != '>') {
316                     readAttribute();
317                     if (!buffer(1)) throw new EOFException("Unexpected EOF - processing attributes part 2");
318                     readWhitespace();
319                 }
320
321                 // inherit namespace default uri if attribute was not provided
322                 if (current.defaultUri == null) {
323                     current.defaultUri = (current.prev != null) ? current.prev.defaultUri : null;
324                 }
325
326                 // work out the uri of this element
327                 if (current.prefix == null) {
328                     // element has no prefix, therefore is the default uri
329                     current.uri = current.defaultUri;
330                 } else {
331                     // work back through the hashtables until uri is found
332                     for (Element e = current; e != null && current.uri == null; e = e.prev) {
333                         current.uri = (String)e.urimap.get(current.prefix);
334                     }
335                     if (current.uri == null) current.addError(new NCException("undefined prefix '"+current.prefix+"'", getLine(), getCol()));
336                 }
337
338             } else {
339                 // this is an end-of-element tag
340                 if (!qName.equals(current.qName)) throw new WFCException(
341                     "end tag </"+qName+"> does not line up with start tag <"+current.qName+">", getLine(), getCol()
342                 );
343             }
344
345             // deal with whitespace
346             readWhitespace(); 
347
348             // process tag close
349             if (!buffer(1)) throw new EOFException("Unexpected EOF before end of tag"); 
350             if (buf[off] == '/') {
351                 endtag = true;
352                 off++; len--; col++;
353             }
354             if (!buffer(1)) throw new EOFException("Unexpected EOF before end of endtag"); 
355             if (buf[off] == '>') {
356                 off++; len--; col++;
357             } else {
358                 throw new MarkupException("missing '>' character from element '"+qName+"'", getLine(), getCol());
359             }
360
361             // send element signals
362             if (starttag) startElement(current);
363             if (endtag) {
364                 endElement(current);
365
366                 // we just closed an element, so remove it from the element 'stack'
367                 if (current.prev == null) {
368                     // we just finished the root element
369                     current.qName = null;
370                 } else {
371                     elements.append((current = current.prev).next);
372                     current.next = null;
373                 }
374             }
375         }
376     }
377
378     /** reads in an attribute of an element. expects Name(buf[off]) */
379     private final void readAttribute() throws IOException, XMLException {
380         int ref = 0;
381         int prefix = 0;
382         String n, v, p, u; // attribute name, value, prefix and uri respectively
383         n = v = p = u = null;
384         char s;
385
386         // find the element name (defined in XML Spec: section 2.3)
387         for (ref= 0; ; ref++) {
388             if (!buffer(ref+1)) throw new EOFException("Unexpected EOF in read attribute loop part 1");
389
390             s = buf[off+ref];
391
392             if (s == '=' || S(s)) {
393                 break;
394             } else if (s == ':' && ref > 0 && prefix < 1) {
395                 // we have a definition of the prefix range available
396                 prefix = ref+1;
397             } else if (!NameChar(s)) {
398                 throw new MarkupException("attribute name contains invalid characters", getLine(), getCol());
399             }
400         }
401
402         // determine prefix and key name
403         if (prefix > 0) {
404             p = new String(buf, off, prefix-1);
405             col += prefix; off += prefix; len -= prefix; ref -= prefix;
406         }
407         n = new String(buf, off, ref);
408         col += ref; off += ref; len -= ref;
409
410         // find name/value divider ('=')
411         readWhitespace();
412         if (!buffer(1)) throw new EOFException("Unexpected EOF before attribute '=' divider");
413         if (buf[off] != '=') throw new MarkupException("attribute name not followed by '=' sign", getLine(), getCol());
414
415         col++; off++; len--;
416         readWhitespace();
417
418         if (!buffer(1)) throw new EOFException("Unexpected EOF after attribute '=' divider");
419
420         char wrap;
421         if (buf[off] == '\'' || buf[off] == '"') {
422             wrap = buf[off];
423         } else {
424             throw new MarkupException("attribute '"+n+"' must have attribute wrapped in ' or \"", getLine(), getCol());
425         }
426         col++; off++; len--;
427
428         // find the attribute value
429         attval: for (ref = 0; ; ref++) {
430             if (!buffer(ref+1)) throw new EOFException("Unexpected EOF in attribute value");
431
432             if (buf[off+ref] == wrap) {
433                 break attval;
434             } else if (buf[off+ref] == '<') {
435                 throw new WFCException("attribute value for '"+n+"' must not contain '<'", getLine(), getCol());
436             } 
437         }
438
439         v = new String(buf, off, ref);
440         col += ref; off += ref; len -= ref;
441
442         // remove end wrapper character
443         col++; off++; len--;
444
445         // process attribute
446         if (p != null && p.equals("xmlns")) {
447             current.urimap.put(n, v);
448         } else if (n.equals("xmlns")) {
449             if (current.defaultUri != null) {
450                 current.addError(new NCException("default namespace definition repeated", getLine(), getCol()));
451             } else {
452                 current.defaultUri = v;
453             }
454         } else {
455             // check to see if attribute is a repeat
456             for (int i=0; current.len > i; i++) if (n.equals(current.keys[i])) throw new WFCException(
457                 "attribute name '"+n+"' may not appear more than once in the same element tag", getLine(), getCol()
458             );
459
460             // find attribute uri
461             if (p == null) {
462                 u = current.uri;
463             } else {
464                 for (Element e = current; e != null && u == null; e = e.prev) {
465                     u = (String)e.urimap.get(p);
466                 }
467                 if (u == null) current.addError(new NCException("undefined attribute prefix '"+current.prefix+"'", getLine(), getCol()));
468             }
469
470             // add attribute to the attribute arrays
471             if (current.len == current.keys.length) current.morekeys();
472             current.keys[current.len] = n;
473             current.vals[current.len] = v;
474             current.uris[current.len] = u;
475             current.len++;
476         }
477     }
478
479     /** reads an entity and processes out its value. expects buf[off] == '&amp;' */
480     private final void readEntity() throws IOException, XMLException {
481         off++; len--;
482         if (!buffer(2)) throw new EOFException("Unexpected EOF reading entity");
483
484         boolean unknown = false;
485         switch (buf[off]) {
486             case '#':
487                 off++; len--;
488
489                 int radix;
490                 if (buf[off] == 'x') { off++; len--; radix = 16; } else { radix = 10; }
491                 int c = 0;
492
493                 // read in each char, then shift total value to the left and add the extra
494                 // style of loop is slightly different from all the others, as this should run a limited number of times 
495                 findchar: while (true) {
496                     if (!buffer(1)) throw new EOFException("Unexpected EOF reading entity");
497                     int d = Character.digit(buf[off], radix);
498                     if (d == -1) {
499                         if (buf[off] != ';') throw new WFCException("illegal characters in entity reference", getLine(), getCol());
500                         off++; len--; col++;
501                         break findchar;
502                     }
503                     c = (c * radix) + d;
504
505                     off++; len--;
506                 }
507
508                 singlechar[0] = Character.forDigit(c, radix);
509                 characters(singlechar, 0, 1);
510                 break;
511
512             case 'a':
513                 if (buffer(4) && buf[off+1] == 'm' && buf[off+2] == 'p' && buf[off+3] == ';') {
514                     characters(single_amp, 0, 1); // &amp;
515                     off += 4; len -= 4; col++;
516                 } else if (buffer(5) && buf[off+1] == 'p' && buf[off+2] == 'o' && buf[off+3] == 's' && buf[off+4] == ';') {
517                     characters(single_apos, 0, 1); // &apos;
518                     off += 5; len -= 5; col++;
519                 } else {
520                     unknown = true;
521                 }
522                 break;
523
524             case 'g':
525                 if (buffer(3) && buf[off+1] == 't' && buf[off+2] == ';') {
526                     characters(single_gt, 0, 1); // &gt;
527                     off += 3; len -= 3; col++;
528                 } else {
529                     unknown = true;
530                 }
531                 break;
532
533             case 'l':
534                 if (buffer(3) && buf[off+1] == 't' && buf[off+2] == ';') {
535                     characters(single_lt, 0, 1); // &lt;
536                     off += 3; len -= 3; col++;
537                 } else {
538                     unknown = true;
539                 }
540                 break;
541
542             case 'q':
543                 if (buffer(5) && buf[off+1] == 'u' && buf[off+2] == 'o' && buf[off+3] == 't' && buf[off+4] == ';') {
544                     characters(single_quot, 0, 1); // &quot;
545                     off += 5; len -= 5; col++;
546                 } else {
547                     unknown = true;
548                 }
549                 break;
550
551             // TODO: check a parser-level Hash of defined entities
552         }
553
554         if (unknown) throw new WFCException("unknown entity (<!ENTITY> not supported)", getLine(), getCol());
555     }
556
557     /** reads until the passed string is encountered. */
558     private final void readChars(boolean p, String match, boolean entities) throws IOException, XMLException {
559         int ref;
560         char[] end = match.toCharArray();
561
562         for (boolean more = true; more;) {
563             if (!buffer(1)) return;
564
565             buf: for (ref = 0; ref < len; ref++) {
566                 switch (buf[off+ref]) {
567                     case '\r': // windows or macos9 newline
568                         // normalise and process
569                         buf[off+ref] = '\n'; ref++;
570                         if (p) characters(buf, off, ref);
571                         off += ref; len -= ref; ref = -1;
572                         line++; col = 1;
573
574                         // windows double-char newline; skip the next char
575                         if (!buffer(1)) return;
576                         if (buf[off] == '\n') { off++; len--; }
577                         break;
578
579                     case '\n': // unix newline
580                         ref++;
581                         if (p) characters(buf, off, ref);
582                         off += ref; len -= ref; ref = -1;
583                         line++; col = 1;
584                         break;
585
586                     case '&':  // entity
587                         if (entities) {
588                             if (p) {
589                                 if (ref > 0) characters(buf, off, ref);
590                                 off += ref; len -= ref; ref = -1;
591                                 readEntity();
592                             }
593                             break;
594                         }
595
596                     default:
597                         if (!buffer(ref+end.length)) continue buf;
598                         for (int i=0; end.length > i; i++) if (end[i] != buf[off+ref+i]) continue buf;
599                         more = false;
600                         break buf;
601                 }
602             }
603
604             if (p && ref > 0) characters(buf, off, ref);
605             off += ref; len -= ref; col += ref;
606         }
607     }
608
609     /**
610      * reads until a <tt>&#60;</tt> symbol is encountered
611      * @param p If true call the characters(char[],int,int) funciton for the processed characters 
612      */
613     private final void readChars(boolean p) throws IOException, XMLException {
614         int ref;
615
616         for (boolean more = true; more;) {
617             if (!buffer(1)) return;
618
619             buf: for (ref = 0; ref < len; ref++) {
620                 switch (buf[off+ref]) {
621                     case '\r': // windows or macos9 newline
622                         // normalise and process
623                         buf[off+ref] = '\n'; ref++;
624                         if (p) characters(buf, off, ref);
625                         off += ref; len -= ref; ref = -1;
626                         line++; col = 1;
627
628                         // windows double-char newline; skip the next char
629                         if (!buffer(1)) return;
630                         if (buf[off] == '\n') { off++; len--; }
631                         break;
632
633                     case '\n': // unix newline
634                         ref++;
635                         if (p) characters(buf, off, ref);
636                         off += ref; len -= ref; ref = -1;
637                         line++; col = 1;
638                         break;
639
640                     case '&':  // entity
641                         if (p) {
642                             if (ref > 0) characters(buf, off, ref);
643                             off += ref; len -= ref; ref = -1;
644                             readEntity();
645                         }
646                         break;
647
648                     case '<':  // end of chars section
649                         more = false;
650                         break buf;
651                 }
652             }
653
654             if (p && ref > 0) characters(buf, off, ref);
655             off += ref; len -= ref; col += ref;
656         }
657     }
658
659     /** reads until a non-whitespace symbol is encountered */
660     private final void readWhitespace() throws IOException, XMLException {
661         int ref;
662
663         for (boolean more = true; more;) {
664             if (!buffer(1)) return;
665
666             buf: for (ref = 0; ref < len; ref++) {
667                 switch (buf[off+ref]) {
668                     case '\r': // windows or macos9 newline
669                         // normalise and process
670                         buf[off+ref] = '\n';
671                         whitespace(buf, off, ++ref);
672                         off += ref; len -= ref; ref = -1;
673                         line++; col = 1;
674
675                         // windows double-char newline; skip the next char
676                         if (!buffer(1)) return;
677                         if (buf[off] == '\n') { off++; len--; }
678                         break;
679
680                     case '\n': // unix newline
681                         whitespace(buf, off, ++ref);
682                         off += ref; len -= ref; ref = -1;
683                         line++; col = 1;
684                         break;
685
686                     case ' ':  // space
687                     case '\t': // tab
688                         break;
689
690                     default:   // end of whitespace
691                         more = false;
692                         break buf;
693                 }
694             }
695
696             off += ref; len -= ref; col += ref;
697         }
698     }
699
700     /**
701      * attempt to fill the buffer.
702      *
703      * @param min Minimum number of characters to read (even if we have to block to do it).
704      * @return return false if min can't be reached.
705      */
706     private final boolean buffer(int min) throws IOException {
707         if (len > min) return true;
708
709         if (buf.length - (off+len) >= min) {
710             // plenty of space left on the end of the buffer
711         } else if (off >= min) {
712             // moving offset data to start will leave enough free space on the end
713             System.arraycopy(buf, off, buf, 0, len); 
714             off = 0;
715         } else {
716             // buffer size will have to be increased
717             char[] newbuf = new char[buf.length * 2];
718             System.arraycopy(buf, off, newbuf, 0, len);
719             buf = newbuf;
720             off = 0;
721         }
722
723         while (min > len) {
724             int newlen = in.read(buf, off+len, buf.length-(off+len));
725             if (newlen < 0) return false; 
726             len += newlen;
727         }
728
729         return true;
730     }
731
732
733     /////////////////////////////////////////////////////////////////////////////////////////////
734     // Abstract SAX-Like Interface
735     /////////////////////////////////////////////////////////////////////////////////////////////
736
737     /**
738      * Called when the start of an element is processed.
739      *
740      * <p>The array of Attribute names and values may be longer than the
741      * number of entries they contain, but all the entries will be
742      * packed at the top.</p>
743      *
744      * <p><b>DO NOT</b> store a reference to the attribute arrays, as
745      * they are reused by other elements.</p>
746      */ 
747     public abstract void startElement(Element e) throws SchemaException;
748
749     /**
750      * Represents a line of character data. 
751      *
752      * <p>Newlines are all normalised to the Unix \n as per the XML Spec,
753      * and a newline will only appear as the last character in the passed
754      * array segment.</p>
755      *
756      * <p>XML.getLine() and XML.getCol() report the position at the
757      * beginning of this character segment, which can be processed in a
758      * line-by-line fashion due to the above newline restriction.</p>
759      */
760     public abstract void characters(char[] ch, int start, int length) throws SchemaException;
761
762     /** Represents a line of ignorable whitespace. */
763     public abstract void whitespace(char[] ch, int start, int length) throws SchemaException;
764
765     /** Represents the end of an Element. */
766     public abstract void endElement(Element e) throws SchemaException;
767
768
769     /////////////////////////////////////////////////////////////////////////////////////////////
770     // Inner Classes for Parser Support
771     /////////////////////////////////////////////////////////////////////////////////////////////
772
773     /**
774      * Used as a struct for holding information about a current element,
775      * and acts as a linked list entry.
776      *
777      * <p>Each element stores a hashtable of namespace definitions against
778      * their respective prefix, and a variable holding their default
779      * uri. If they did not specify a default uri, their
780      * parent's uri is copied in to keep up the sembelence of speedy
781      * parsing.</p>
782      *
783      * <h3>SLOWEST PART OF THE XML PARSER</h3> 
784      * <p>To implement the Namespace Specification exactly, we have to
785      * store prefix mappings for elements away from its parents and
786      * siblings. This means if a child of a child of-a child uses
787      * a prefix defined in the root, we have to search each Hashtable
788      * in each Element until we get to the root.</p> 
789      *
790      * <p>Unfortunetally, every other solution I can think of requires
791      * more work than this one, shifted to different parts of the
792      * parser.</p>
793      */
794     public static final class Element
795     {
796         public Element next, prev;
797
798         /** A hashtable of all namespace prefixes that are defined by this element. */
799         public Hash urimap;
800
801         /** An array of attribute names. */
802         public String[] keys;
803
804         /** An array of attribute values. */
805         public String[] vals;
806
807         /** An array of attribute uris. */
808         public String[] uris;
809
810         /** An array of non-fatal errors related to this element. */
811         public XMLException[] errors;
812
813         /** Current number of attributes in the <tt>keys</tt> and <tt>vals</tt> arrays. */
814         public int len;
815
816         /** Default URI for this element and its children with no prefix. */
817         public String defaultUri;
818
819         /** URI of current tag. XML Namespace Spec 14-Jan-1999 section 1 */
820         public String uri;
821
822         /** LocalPart of current element. XML Namespace Spec 14-Jan-1999 [8] */
823         public String localName;
824
825         /** Qualified Name of current element.  XML Namespace Spec 14-Jan-1999 [6] */
826         public String qName;
827
828         /** Prefix of current element. Substring of qName. XML Namespace Spec 14-Jan-1999 [7] */
829         public String prefix;
830
831         public Element() {
832             defaultUri = uri = prefix = localName = qName = null;
833             urimap = new Hash(3,3);
834             keys = new String[10];
835             vals = new String[10];
836             uris = new String[10];
837             errors = new XMLException[] {};
838             len = 0;
839         }
840
841         /** increase the size of the attributes arrays */
842         void morekeys() {
843             String[] newkeys = new String[keys.length+5];
844             String[] newvals = new String[vals.length+5];
845             String[] newuris = new String[uris.length+5];
846             System.arraycopy(keys, 0, newkeys, 0, keys.length);
847             System.arraycopy(vals, 0, newvals, 0, vals.length);
848             System.arraycopy(uris, 0, newuris, 0, uris.length);
849             keys = newkeys; vals = newvals; uris = newuris;
850         }
851
852         /** empty out the arrays */
853         void clear() {
854             for (int i=0; len > i; i++) { keys[i] = null; vals[i] = null; uris[i] = null; }; len = 0;
855             errors = new XMLException[] {};
856         }
857
858         /** add an error to the errors array */
859         void addError(XMLException e) {
860             // it doesn't really matter about continually expanding the array, as this case is quite rare
861             XMLException[] newe = new XMLException[errors.length+1];
862             System.arraycopy(errors, 0, newe, 0, errors.length);
863             newe[errors.length] = e;
864             errors = newe;
865         }
866     }
867
868     /** Parse or Structural Error */
869     public static class XMLException extends Exception
870     {
871         private int line;
872         private int col;
873         private String error;
874
875         public XMLException(String e) { this(e, -1, -1); }
876
877         public XMLException(String e, int l, int c) {
878             this.error = e;
879             this.line  = l;
880             this.col   = c;
881         }
882
883         public int getLine()     { return this.line;  }
884         public int getCol()      { return this.col;   }
885         public String getMessage() { return this.error; }
886     }
887
888     /** Violation of Markup restrictions in XML Specification - Fatal Error */
889     public static class MarkupException extends XMLException { public MarkupException(String e, int l, int c) { super(e,l,c); } }
890
891     /** Well-Formedness Constraint Violation - Fatal Error */
892     public static final class WFCException extends MarkupException { public WFCException(String e, int l, int c) { super(e,l,c); } }
893
894     /** Namespace Constraint Violation - Recoverable Error */
895     public static final class NCException extends XMLException { public NCException(String e, int l, int c) { super(e,l,c); } }
896
897     /** Schema Violation - Fatal Error */
898     public static class SchemaException extends XMLException {
899         public SchemaException(String e) { this(e, -1, -1); }
900         public SchemaException(String e, int l, int c) { super(e,l,c); }
901     }
902
903
904     /////////////////////////////////////////////////////////////////////////////////////////////
905     // Static Support JSFunctions for the XML Specification 
906     /////////////////////////////////////////////////////////////////////////////////////////////
907  
908     // attempt to avoid these functions unless you *expect* the input to fall in the given range.
909
910     /** First Character of Name - XML Specification 1.0 [5] */
911     private static final boolean Name(char c) {
912         return BaseCharAscii(c) || c == '_' || c == ':' || Letter(c);
913     } 
914
915     /** NameChar - XML Specification 1.0 [4] */
916     private static final boolean NameChar(char c) {
917         return BaseCharAscii(c) || c == '.' || c == '-' || c == '_' || c == ':'
918             || Digit(c) || Letter(c) || Extender(c); // TODO: || CombiningChar(c);
919     } 
920
921     /** BaseChar - XMl Specification 1.0 [84] */
922     private static final boolean Letter(char c) {
923         return BaseChar(c) || Ideographic(c);
924     }
925
926     /** Elements of BaseChar that exist in ASCII. */
927     private static final boolean BaseCharAscii(char c) {
928         return (c >= '\u0041' && c <= '\u005A') || (c >= '\u0061' && c <= '\u007A');
929     }
930
931     /** Char - XML Specification 1.0 [2] */
932     private static final boolean Char(char c) {
933         // u000A == r and u000D == n, but the javac compiler can't handle the \ u form
934         return c == '\u0009' || c == '\r' || c == '\n'
935             || (c >= '\u0020' && c <= '\uD7FF')
936             || (c >= '\uE000' && c <= '\uFFFD');
937     }
938
939     /** BaseChar - XML Specification 1.0 [85] */
940     private static final boolean BaseChar(char c) {
941         return  BaseCharAscii(c) || (c >= '\u00C0' && c <= '\u00D6')
942             || (c >= '\u00D8' && c <= '\u00F6') || (c >= '\u00F8' && c <= '\u00FF') || (c >= '\u0100' && c <= '\u0131')
943             || (c >= '\u0134' && c <= '\u013E') || (c >= '\u0141' && c <= '\u0148') || (c >= '\u014A' && c <= '\u017E')
944             || (c >= '\u0180' && c <= '\u01C3') || (c >= '\u01CD' && c <= '\u01F0') || (c >= '\u01F4' && c <= '\u01F5')
945             || (c >= '\u01FA' && c <= '\u0217') || (c >= '\u0250' && c <= '\u02A8') || (c >= '\u02BB' && c <= '\u02C1')
946             || (c == '\u0386')                  || (c >= '\u0388' && c <= '\u038A') || (c == '\u038C')
947             || (c >= '\u038E' && c <= '\u03A1') || (c >= '\u03A3' && c <= '\u03CE') || (c >= '\u03D0' && c <= '\u03D6')
948             || (c == '\u03DA')                  || (c == '\u03DC')                  || (c == '\u03DE')
949             || (c == '\u03E0')
950             || (c >= '\u03E2' && c <= '\u03F3') || (c >= '\u0401' && c <= '\u040C') || (c >= '\u040E' && c <= '\u044F')
951             || (c >= '\u0451' && c <= '\u045C') || (c >= '\u045E' && c <= '\u0481') || (c >= '\u0490' && c <= '\u04C4')
952             || (c >= '\u04C7' && c <= '\u04C8') || (c >= '\u04CB' && c <= '\u04CC') || (c >= '\u04D0' && c <= '\u04EB')
953             || (c >= '\u04EE' && c <= '\u04F5') || (c >= '\u04F8' && c <= '\u04F9') || (c >= '\u0531' && c <= '\u0556')
954             || (c == '\u0559')
955             || (c >= '\u0561' && c <= '\u0586') || (c >= '\u05D0' && c <= '\u05EA') || (c >= '\u05F0' && c <= '\u05F2')
956             || (c >= '\u0621' && c <= '\u063A') || (c >= '\u0641' && c <= '\u064A') || (c >= '\u0671' && c <= '\u06B7')
957             || (c >= '\u06BA' && c <= '\u06BE') || (c >= '\u06C0' && c <= '\u06CE') || (c >= '\u06D0' && c <= '\u06D3')
958             || (c == '\u06D5')
959             || (c >= '\u06E5' && c <= '\u06E6') || (c >= '\u0905' && c <= '\u0939')
960             || (c == '\u093D')
961             || (c >= '\u0958' && c <= '\u0961') || (c >= '\u0985' && c <= '\u098C') || (c >= '\u098F' && c <= '\u0990')
962             || (c >= '\u0993' && c <= '\u09A8') || (c >= '\u09AA' && c <= '\u09B0')
963             || (c == '\u09B2')
964             || (c >= '\u09B6' && c <= '\u09B9') || (c >= '\u09DF' && c <= '\u09E1') || (c >= '\u09F0' && c <= '\u09F1')
965             || (c >= '\u0A05' && c <= '\u0A0A') || (c >= '\u0A0F' && c <= '\u0A10') || (c >= '\u0A13' && c <= '\u0A28')
966             || (c >= '\u0A2A' && c <= '\u0A30') || (c >= '\u0A32' && c <= '\u0A33') || (c >= '\u0A35' && c <= '\u0A36')
967             || (c >= '\u0A38' && c <= '\u0A39') || (c >= '\u0A59' && c <= '\u0A5C')
968             || (c == '\u0A5E')
969             || (c >= '\u0A72' && c <= '\u0A74') || (c >= '\u0A85' && c <= '\u0A8B')
970             || (c == '\u0A8D')
971             || (c >= '\u0A8F' && c <= '\u0A91') || (c >= '\u0A93' && c <= '\u0AA8') || (c >= '\u0AAA' && c <= '\u0AB0') 
972             || (c >= '\u0AB2' && c <= '\u0AB3') || (c >= '\u0AB5' && c <= '\u0AB9') 
973             || (c == '\u0ABD') 
974             || (c == '\u0AE0') 
975             || (c >= '\u0B05' && c <= '\u0B0C') || (c >= '\u0B0F' && c <= '\u0B10') || (c >= '\u0B13' && c <= '\u0B28') 
976             || (c >= '\u0B2A' && c <= '\u0B30') || (c >= '\u0B32' && c <= '\u0B33') || (c >= '\u0B36' && c <= '\u0B39') 
977             || (c == '\u0B3D') 
978             || (c >= '\u0B5C' && c <= '\u0B5D') || (c >= '\u0B5F' && c <= '\u0B61') || (c >= '\u0B85' && c <= '\u0B8A') 
979             || (c >= '\u0B8E' && c <= '\u0B90') || (c >= '\u0B92' && c <= '\u0B95') || (c >= '\u0B99' && c <= '\u0B9A') 
980             || (c == '\u0B9C') 
981             || (c >= '\u0B9E' && c <= '\u0B9F') || (c >= '\u0BA3' && c <= '\u0BA4') || (c >= '\u0BA8' && c <= '\u0BAA') 
982             || (c >= '\u0BAE' && c <= '\u0BB5') || (c >= '\u0BB7' && c <= '\u0BB9') || (c >= '\u0C05' && c <= '\u0C0C') 
983             || (c >= '\u0C0E' && c <= '\u0C10') || (c >= '\u0C12' && c <= '\u0C28') || (c >= '\u0C2A' && c <= '\u0C33') 
984             || (c >= '\u0C35' && c <= '\u0C39') || (c >= '\u0C60' && c <= '\u0C61') || (c >= '\u0C85' && c <= '\u0C8C') 
985             || (c >= '\u0C8E' && c <= '\u0C90') || (c >= '\u0C92' && c <= '\u0CA8') || (c >= '\u0CAA' && c <= '\u0CB3') 
986             || (c >= '\u0CB5' && c <= '\u0CB9') 
987             || (c == '\u0CDE') 
988             || (c >= '\u0CE0' && c <= '\u0CE1') || (c >= '\u0D05' && c <= '\u0D0C') || (c >= '\u0D0E' && c <= '\u0D10') 
989             || (c >= '\u0D12' && c <= '\u0D28') || (c >= '\u0D2A' && c <= '\u0D39') || (c >= '\u0D60' && c <= '\u0D61') 
990             || (c >= '\u0E01' && c <= '\u0E2E') 
991             || (c == '\u0E30') 
992             || (c >= '\u0E32' && c <= '\u0E33') || (c >= '\u0E40' && c <= '\u0E45') || (c >= '\u0E81' && c <= '\u0E82') 
993             || (c == '\u0E84') 
994             || (c >= '\u0E87' && c <= '\u0E88') 
995             || (c == '\u0E8A') 
996             || (c == '\u0E8D') 
997             || (c >= '\u0E94' && c <= '\u0E97') || (c >= '\u0E99' && c <= '\u0E9F') || (c >= '\u0EA1' && c <= '\u0EA3') 
998             || (c == '\u0EA5') 
999             || (c == '\u0EA7') 
1000             || (c >= '\u0EAA' && c <= '\u0EAB') || (c >= '\u0EAD' && c <= '\u0EAE') 
1001             || (c == '\u0EB0') 
1002             || (c >= '\u0EB2' && c <= '\u0EB3') 
1003             || (c == '\u0EBD') 
1004             || (c >= '\u0EC0' && c <= '\u0EC4') || (c >= '\u0F40' && c <= '\u0F47') || (c >= '\u0F49' && c <= '\u0F69') 
1005             || (c >= '\u10A0' && c <= '\u10C5') || (c >= '\u10D0' && c <= '\u10F6') 
1006             || (c == '\u1100') 
1007             || (c >= '\u1102' && c <= '\u1103') || (c >= '\u1105' && c <= '\u1107') 
1008             || (c == '\u1109') 
1009             || (c >= '\u110B' && c <= '\u110C') || (c >= '\u110E' && c <= '\u1112') 
1010             || (c == '\u113C') 
1011             || (c == '\u113E') 
1012             || (c == '\u1140') 
1013             || (c == '\u114C') 
1014             || (c == '\u114E') 
1015             || (c == '\u1150') 
1016             || (c >= '\u1154' && c <= '\u1155') 
1017             || (c == '\u1159') 
1018             || (c >= '\u115F' && c <= '\u1161') 
1019             || (c == '\u1163') 
1020             || (c == '\u1165') 
1021             || (c == '\u1167') 
1022             || (c == '\u1169') 
1023             || (c >= '\u116D' && c <= '\u116E') || (c >= '\u1172' && c <= '\u1173') 
1024             || (c == '\u1175') 
1025             || (c == '\u119E') 
1026             || (c == '\u11A8') 
1027             || (c == '\u11AB') 
1028             || (c >= '\u11AE' && c <= '\u11AF') || (c >= '\u11B7' && c <= '\u11B8') 
1029             || (c == '\u11BA') 
1030             || (c >= '\u11BC' && c <= '\u11C2') 
1031             || (c == '\u11EB') 
1032             || (c == '\u11F0') 
1033             || (c == '\u11F9') 
1034             || (c >= '\u1E00' && c <= '\u1E9B') || (c >= '\u1EA0' && c <= '\u1EF9') || (c >= '\u1F00' && c <= '\u1F15') 
1035             || (c >= '\u1F18' && c <= '\u1F1D') || (c >= '\u1F20' && c <= '\u1F45') || (c >= '\u1F48' && c <= '\u1F4D') 
1036             || (c >= '\u1F50' && c <= '\u1F57') 
1037             || (c == '\u1F59') 
1038             || (c == '\u1F5B') 
1039             || (c == '\u1F5D') 
1040             || (c >= '\u1F5F' && c <= '\u1F7D') || (c >= '\u1F80' && c <= '\u1FB4') || (c >= '\u1FB6' && c <= '\u1FBC') 
1041             || (c == '\u1FBE') 
1042             || (c >= '\u1FC2' && c <= '\u1FC4') || (c >= '\u1FC6' && c <= '\u1FCC') || (c >= '\u1FD0' && c <= '\u1FD3') 
1043             || (c >= '\u1FD6' && c <= '\u1FDB') || (c >= '\u1FE0' && c <= '\u1FEC') || (c >= '\u1FF2' && c <= '\u1FF4') 
1044             || (c >= '\u1FF6' && c <= '\u1FFC') 
1045             || (c == '\u2126') 
1046             || (c >= '\u212A' && c <= '\u212B') 
1047             || (c == '\u212E') 
1048             || (c >= '\u2180' && c <= '\u2182') || (c >= '\u3041' && c <= '\u3094') || (c >= '\u30A1' && c <= '\u30FA') 
1049             || (c >= '\u3105' && c <= '\u312C') || (c >= '\uAC00' && c <= '\uD7A3');
1050     }
1051
1052     /** BaseChar - XMl Specification 1.0 [86] */
1053     private static final boolean Ideographic(char c) {
1054         return (c >= '\u4E00' && c <= '\u9FA5') || c == '\u3007' || (c >= '\u3021' && c <= '\u3029');
1055     }
1056  
1057     /** CombiningChar - XMl Specification 1.0 [87] */
1058     /*private static final boolean CombiningChar(char c) {
1059         return (c >= '\u0300' && c <= '\u0345')
1060             || (c >= '\u0360' && c <= '\u0361') || (c >= '\u0483' && c <= '\u0486') || (c >= '\u0591' && c <= '\u05A1') 
1061             || (c >= '\u05A3' && c <= '\u05B9') || (c >= '\u05BB' && c <= '\u05BD') 
1062             || (c == '\u05BF') 
1063             || (c >= '\u05C1' && c <= '\u05C2') 
1064             || (c == '\u05C4') 
1065             || (c >= '\u064B' && c <= '\u0652') 
1066             || (c == '\u0670') 
1067             || (c >= '\u06D6' && c <= '\u06DC') || (c >= '\u06DD' && c <= '\u06DF') || (c >= '\u06E0' && c <= '\u06E4') 
1068             || (c >= '\u06E7' && c <= '\u06E8') || (c >= '\u06EA' && c <= '\u06ED') || (c >= '\u0901' && c <= '\u0903') 
1069             || (c == '\u093C') 
1070             || (c >= '\u093E' && c <= '\u094C') 
1071             || (c == '\u094D') 
1072             || (c >= '\u0951' && c <= '\u0954') || (c >= '\u0962' && c <= '\u0963') || (c >= '\u0981' && c <= '\u0983') 
1073             || (c == '\u09BC') 
1074             || (c == '\u09BE') 
1075             || (c == '\u09BF') 
1076             || (c >= '\u09C0' && c <= '\u09C4') || (c >= '\u09C7' && c <= '\u09C8') || (c >= '\u09CB' && c <= '\u09CD') 
1077             || (c == '\u09D7') 
1078             || (c >= '\u09E2' && c <= '\u09E3') 
1079             || (c == '\u0A02') 
1080             || (c == '\u0A3C') 
1081             || (c == '\u0A3E') 
1082             || (c == '\u0A3F') 
1083             || (c >= '\u0A40' && c <= '\u0A42') || (c >= '\u0A47' && c <= '\u0A48') || (c >= '\u0A4B' && c <= '\u0A4D') 
1084             || (c >= '\u0A70' && c <= '\u0A71') || (c >= '\u0A81' && c <= '\u0A83') 
1085             || (c == '\u0ABC') 
1086             || (c >= '\u0ABE' && c <= '\u0AC5') || (c >= '\u0AC7' && c <= '\u0AC9') || (c >= '\u0ACB' && c <= '\u0ACD') 
1087             || (c >= '\u0B01' && c <= '\u0B03') 
1088             || (c == '\u0B3C') 
1089             || (c >= '\u0B3E' && c <= '\u0B43') || (c >= '\u0B47' && c <= '\u0B48') || (c >= '\u0B4B' && c <= '\u0B4D') 
1090             || (c >= '\u0B56' && c <= '\u0B57') || (c >= '\u0B82' && c <= '\u0B83') || (c >= '\u0BBE' && c <= '\u0BC2') 
1091             || (c >= '\u0BC6' && c <= '\u0BC8') || (c >= '\u0BCA' && c <= '\u0BCD') 
1092             || (c == '\u0BD7') 
1093             || (c >= '\u0C01' && c <= '\u0C03') || (c >= '\u0C3E' && c <= '\u0C44') || (c >= '\u0C46' && c <= '\u0C48') 
1094             || (c >= '\u0C4A' && c <= '\u0C4D') || (c >= '\u0C55' && c <= '\u0C56') || (c >= '\u0C82' && c <= '\u0C83') 
1095             || (c >= '\u0CBE' && c <= '\u0CC4') || (c >= '\u0CC6' && c <= '\u0CC8') || (c >= '\u0CCA' && c <= '\u0CCD') 
1096             || (c >= '\u0CD5' && c <= '\u0CD6') || (c >= '\u0D02' && c <= '\u0D03') || (c >= '\u0D3E' && c <= '\u0D43') 
1097             || (c >= '\u0D46' && c <= '\u0D48') || (c >= '\u0D4A' && c <= '\u0D4D') 
1098             || (c == '\u0D57') 
1099             || (c == '\u0E31') 
1100             || (c >= '\u0E34' && c <= '\u0E3A') || (c >= '\u0E47' && c <= '\u0E4E') 
1101             || (c == '\u0EB1') 
1102             || (c >= '\u0EB4' && c <= '\u0EB9') || (c >= '\u0EBB' && c <= '\u0EBC') || (c >= '\u0EC8' && c <= '\u0ECD') 
1103             || (c >= '\u0F18' && c <= '\u0F19') 
1104             || (c == '\u0F35') 
1105             || (c == '\u0F37') 
1106             || (c == '\u0F39') 
1107             || (c == '\u0F3E') 
1108             || (c == '\u0F3F') 
1109             || (c >= '\u0F71' && c <= '\u0F84') || (c >= '\u0F86' && c <= '\u0F8B') || (c >= '\u0F90' && c <= '\u0F95') 
1110             || (c == '\u0F97') 
1111             || (c >= '\u0F99' && c <= '\u0FAD') || (c >= '\u0FB1' && c <= '\u0FB7') 
1112             || (c == '\u0FB9') 
1113             || (c >= '\u20D0' && c <= '\u20DC') 
1114             || (c == '\u20E1') 
1115             || (c >= '\u302A' && c <= '\u302F') 
1116             || (c == '\u3099') 
1117             || (c == '\u309A');
1118     }*/
1119
1120     /** Digit - XMl Specification 1.0 [88] */
1121     private static final boolean Digit(char c) {
1122         return (c >= '\u0030' && c <= '\u0039') || (c >= '\u0660' && c <= '\u0669') || (c >= '\u06F0' && c <= '\u06F9')
1123             || (c >= '\u0966' && c <= '\u096F') || (c >= '\u09E6' && c <= '\u09EF') || (c >= '\u0A66' && c <= '\u0A6F')
1124             || (c >= '\u0AE6' && c <= '\u0AEF') || (c >= '\u0B66' && c <= '\u0B6F') || (c >= '\u0BE7' && c <= '\u0BEF')
1125             || (c >= '\u0C66' && c <= '\u0C6F') || (c >= '\u0CE6' && c <= '\u0CEF') || (c >= '\u0D66' && c <= '\u0D6F')
1126             || (c >= '\u0E50' && c <= '\u0E59') || (c >= '\u0ED0' && c <= '\u0ED9') || (c >= '\u0F20' && c <= '\u0F29');
1127     }
1128
1129     /** Extender - XMl Specification 1.0 [89] */
1130     private static final boolean Extender(char c) {
1131         return c == '\u00B7' || c == '\u02D0' || c == '\u02D1' || c == '\u0387'
1132             || c == '\u0640' || c == '\u0E46' || c == '\u0EC6' || c == '\u3005'
1133             || (c >= '\u3031' && c <= '\u3035') || (c >= '\u309D' && c <= '\u309E') || (c >= '\u30FC' && c <= '\u30FE');
1134     }
1135
1136     /** Whitespace - XML Specification 1.0 [3] */
1137     private static final boolean S(char c) {
1138         return c == '\u0020' || c == '\u0009' || c == '\r' || c == '\n';
1139     }
1140 }