737651077b8819ca597a728505926e518419264e
[org.ibex.core.git] / src / org / xwt / XML.java
1 package org.xwt;
2
3 import java.io.*;
4 import java.net.*;
5 import java.util.*;
6 import org.xwt.util.*;
7
8 /** an event-driven XML parser, derived from MinML (http://www.wilson.co.uk/xml/minml.htm) */
9 public abstract class XML {
10     
11     /////////////////////////////////////////////////////////////////////////////////////////////
12     // The following code was copied from the w3c's org.xml.sax.* classes
13     /////////////////////////////////////////////////////////////////////////////////////////////
14     
15     protected static interface AttributeList {
16         public abstract int getLength ();
17         public abstract String getName (int i);
18         public abstract String getType (int i);
19         public abstract String getValue (int i);
20         public abstract String getType (String name);
21         public abstract String getValue (String name);
22     }
23     
24     protected static interface DTDHandler {
25         public abstract void notationDecl (String name, String publicId, String systemId) throws SAXException;
26         public abstract void unparsedEntityDecl (String name, String publicId, String systemId, String notationName) throws SAXException;
27     }
28     
29     protected static interface EntityResolver {
30         public abstract InputSource resolveEntity (String publicId, String systemId) throws SAXException, IOException;
31     }
32     
33     protected static interface ErrorHandler {
34         public abstract void warning (SAXParseException exception) throws SAXException;
35         public abstract void error (SAXParseException exception) throws SAXException;
36         public abstract void fatalError (SAXParseException exception) throws SAXException;
37     }
38     
39     private static class InputSource {
40         public InputSource () { }
41         public InputSource (String systemId) { setSystemId(systemId); }
42         public InputSource (InputStream byteStream) { setByteStream(byteStream); }
43         public InputSource (Reader characterStream) { setCharacterStream(characterStream); }
44         public void setPublicId (String publicId) { this.publicId = publicId; }
45         public String getPublicId () { return publicId; }
46         public void setSystemId (String systemId) { this.systemId = systemId; }
47         public String getSystemId() { return systemId; }
48         public void setByteStream (InputStream byteStream) { this.byteStream = byteStream; }
49         public InputStream getByteStream() { return byteStream; }
50         public void setEncoding (String encoding) { this.encoding = encoding; }
51         public String getEncoding() { return encoding; }
52         public void setCharacterStream (Reader characterStream) { this.characterStream = characterStream; }
53         public Reader getCharacterStream () { return characterStream; }
54         private String publicId;
55         private String systemId;
56         private InputStream byteStream;
57         private String encoding;
58         private Reader characterStream;
59     }
60     
61     protected static interface Locator {
62         public abstract String getPublicId ();
63         public abstract String getSystemId ();
64         public abstract int getLineNumber ();
65         public abstract int getColumnNumber ();
66     }
67     
68     protected static interface Parser {
69         public abstract void setLocale (Locale locale) throws SAXException;
70         public abstract void setEntityResolver (EntityResolver resolver);
71         public abstract void setDTDHandler (DTDHandler handler);
72         public abstract void setDocumentHandler (DocumentHandler handler);
73         public abstract void setErrorHandler (ErrorHandler handler);
74         public abstract void parse (InputSource source) throws SAXException, IOException;
75         public abstract void parse (String systemId) throws SAXException, IOException;
76     }
77     
78     public static class SAXException extends Exception {
79         public SAXException (String message) {
80             super();
81             this.message = message;
82             this.exception = null;
83         }
84         
85         public SAXException (Exception e) {
86             super();
87             this.message = null;
88             this.exception = e;
89         }
90         
91         public SAXException (String message, Exception e) {
92             super();
93             this.message = message;
94             this.exception = e;
95         }
96         
97         public String getMessage () {
98             if (message == null && exception != null) {
99                 return exception.getMessage();
100             } else {
101                 return this.message;
102             }
103         }
104         
105         public Exception getException () { return exception; }
106         public String toString () { return getMessage(); }
107         private String message;
108         private Exception exception;
109     }
110     
111     static class SAXParseException extends SAXException {
112         public SAXParseException (String message, Locator locator) {
113             super(message);
114             this.publicId = locator.getPublicId();
115             this.systemId = locator.getSystemId();
116             this.lineNumber = locator.getLineNumber();
117             this.columnNumber = locator.getColumnNumber();
118         }
119         public SAXParseException (String message, Locator locator, Exception e) {
120             super(message, e);
121             this.publicId = locator.getPublicId();
122             this.systemId = locator.getSystemId();
123             this.lineNumber = locator.getLineNumber();
124             this.columnNumber = locator.getColumnNumber();
125         }
126         public SAXParseException (String message, String publicId, String systemId, int lineNumber, int columnNumber) {
127             super(message);
128             this.publicId = publicId;
129             this.systemId = systemId;
130             this.lineNumber = lineNumber;
131             this.columnNumber = columnNumber;
132         }
133         public SAXParseException (String message, String publicId, String systemId, int lineNumber, int columnNumber, Exception e) {
134             super(message, e);
135             this.publicId = publicId;
136             this.systemId = systemId;
137             this.lineNumber = lineNumber;
138             this.columnNumber = columnNumber;
139         }
140         public String getPublicId() { return this.publicId; }
141         public String getSystemId() { return this.systemId; }
142         public int getLineNumber () { return this.lineNumber; }
143         public int getColumnNumber () { return this.columnNumber; }
144         private String publicId;
145         private String systemId;
146         private int lineNumber;
147         private int columnNumber;
148     }
149     
150     protected static interface DocumentHandler {
151         public abstract void setDocumentLocator (Locator locator);
152         public abstract void startDocument() throws SAXException;
153         public abstract void endDocument() throws SAXException;
154         public abstract void startElement(String name, AttributeList atts) throws SAXException;
155         public abstract void endElement(String name) throws SAXException;
156         public abstract void characters(char ch[], int start, int length) throws SAXException;
157         public abstract void ignorableWhitespace(char ch[], int start, int length) throws SAXException;
158         public abstract void processingInstruction (String target, String data) throws SAXException;
159         Writer startDocument(final Writer writer) throws SAXException;
160         Writer startElement(final String name, final AttributeList attributes, final Writer writer) throws SAXException;
161     }
162     
163     
164     
165     /////////////////////////////////////////////////////////////////////////////////////////////
166     // Everything from here down is copied verbatim from the MinML 1.7
167     // distribution, except for these modifications:
168     //   - some classes have been changed from 'public' to 'private static'
169     //   - extraneous import and package declarations have been removed
170     //   - the advertising clause of the copyright notice has been removed
171     //     as approved by John Wilson in an email to Adam Megacz.
172     /////////////////////////////////////////////////////////////////////////////////////////////
173     
174     // Copyright (c) 2000, 2001, 2002 The Wilson Partnership.
175     // All Rights Reserved.
176     // @(#)MinML.java, 1.8(provisional), 2nd March 2002
177     // Author: John Wilson - tug@wilson.co.uk
178     
179     /*
180       Copyright (c) 2000, 2001 John Wilson (tug@wilson.co.uk).
181       All rights reserved.
182       Redistribution and use in source and binary forms,
183       with or without modification, are permitted provided
184       that the following conditions are met:
185       
186       Redistributions of source code must retain the above
187       copyright notice, this list of conditions and the
188       following disclaimer.
189       
190       Redistributions in binary form must reproduce the
191       above copyright notice, this list of conditions and
192       the following disclaimer in the documentation and/or
193       other materials provided with the distribution.
194       
195       THIS SOFTWARE IS PROVIDED BY JOHN WILSON ``AS IS'' AND ANY
196       EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
197       THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
198       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JOHN WILSON
199       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
200       EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
201       TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
202       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
203       ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
204       LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
205       IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
206       OF THE POSSIBILITY OF SUCH DAMAGE
207     */
208     
209     private static class MinML implements Parser, Locator, DocumentHandler, ErrorHandler {
210         public static final int endStartName = 0;
211         public static final int emitStartElement = 1;
212         public static final int emitEndElement = 2;
213         public static final int possiblyEmitCharacters = 3;
214         public static final int emitCharacters = 4;
215         public static final int emitCharactersSave = 5;
216         public static final int saveAttributeName = 6;
217         public static final int saveAttributeValue = 7;
218         public static final int startComment = 8;
219         public static final int endComment = 9;
220         public static final int incLevel = 10;
221         public static final int decLevel = 11;
222         public static final int startCDATA = 12;
223         public static final int endCDATA = 13;
224         public static final int processCharRef = 14;
225         public static final int writeCdata = 15;
226         public static final int exitParser = 16;
227         public static final int parseError = 17;
228         public static final int discardAndChange = 18;
229         public static final int discardSaveAndChange = 19;
230         public static final int saveAndChange = 20;
231         public static final int change = 21;
232         
233         public static final int inSkipping = 0;
234         public static final int inSTag = 1;
235         public static final int inPossiblyAttribute = 2;
236         public static final int inNextAttribute = 3;
237         public static final int inAttribute = 4;
238         public static final int inAttribute1 = 5;
239         public static final int inAttributeValue = 6;
240         public static final int inAttributeQuoteValue = 7;
241         public static final int inAttributeQuotesValue = 8;
242         public static final int inETag = 9;
243         public static final int inETag1 = 10;
244         public static final int inMTTag = 11;
245         public static final int inTag = 12;
246         public static final int inTag1 = 13;
247         public static final int inPI = 14;
248         public static final int inPI1 = 15;
249         public static final int inPossiblySkipping = 16;
250         public static final int inCharData = 17;
251         public static final int inCDATA = 18;
252         public static final int inCDATA1 = 19;
253         public static final int inComment =20;
254         public static final int inDTD = 21;
255         
256         public MinML(final int initialBufferSize, final int bufferIncrement) {
257             this.initialBufferSize = initialBufferSize;
258             this.bufferIncrement = bufferIncrement;
259         }
260         
261         public MinML() {
262             this(256, 128);
263         }
264         
265         public void parse(final Reader in) throws SAXException, IOException {
266             final Vector attributeNames = new Vector();
267             final Vector attributeValues = new Vector();
268             
269             final AttributeList attrs = new AttributeList() {
270                     public int getLength() {
271                         return attributeNames.size();
272                     }
273                     
274                     public String getName(final int i) {
275                         return (String)attributeNames.elementAt(i);
276                     }
277                     
278                     public String getType(final int i) {
279                         return "CDATA";
280                     }
281                     
282                     public String getValue(final int i) {
283                         return (String)attributeValues.elementAt(i);
284                     }
285                     
286                     public String getType(final String name) {
287                         return "CDATA";
288                     }
289                     
290                     public String getValue(final String name) {
291                         final int index = attributeNames.indexOf(name);
292                         
293                         return (index == -1) ? null : (String)attributeValues.elementAt(index);
294                     }
295                 };
296             
297             final MinMLBuffer buffer = new MinMLBuffer(in);
298             int currentChar = 0, charCount = 0;
299             int level = 0;
300             int mixedContentLevel = -1;
301             String elementName = null;
302             String state = operands[inSkipping];
303             
304             this.lineNumber = 1;
305             this.columnNumber = 0;
306             
307             try {
308                 while(true) {
309                     charCount++;
310                     
311                     //
312                     // this is to try and make the loop a bit faster
313                     // currentChar = buffer.read(); is simpler but is a bit slower.
314                     //
315                     currentChar = (buffer.nextIn == buffer.lastIn) ? buffer.read() : buffer.chars[buffer.nextIn++];
316                     
317                     final int transition;
318                     
319                     if (currentChar > ']') {
320                         transition = state.charAt(14);
321                     } else {
322                         final int charClass = charClasses[currentChar + 1];
323                         
324                         if (charClass == -1) fatalError("Document contains illegal control character with value " + currentChar, this.lineNumber, this.columnNumber);
325                         
326                         if (charClass == 12) {
327                             if (currentChar == '\r') {
328                                 currentChar = '\n';
329                                 charCount = -1;
330                             }
331                             
332                             if (currentChar == '\n') {
333                                 if (charCount == 0) continue;  // preceeded by '\r' so ignore
334                                 
335                                 if (charCount != -1) charCount = 0;
336                                 
337                                 this.lineNumber++;
338                                 this.columnNumber = 0;
339                             }
340                         }
341                         
342                         transition = state.charAt(charClass);
343                     }
344                     
345                     this.columnNumber++;
346                     
347                     final String operand = operands[transition >>> 8];
348                     
349                     switch (transition & 0XFF) {
350                     case endStartName:
351                         // end of start element name
352                         elementName = buffer.getString();
353                         if (currentChar != '>' && currentChar != '/') break;  // change state to operand
354                         // drop through to emit start element (we have no attributes)
355                         
356                     case emitStartElement:
357                         // emit start element
358                         
359                         final Writer newWriter = this.extDocumentHandler.startElement(elementName, attrs,
360                                                                                       (this.tags.empty()) ?
361                                                                                       this.extDocumentHandler.startDocument(buffer)
362                                                                                       :
363                                                                                       buffer.getWriter());
364                         
365                         buffer.pushWriter(newWriter);
366                         this.tags.push(elementName);
367                         
368                         attributeValues.removeAllElements();
369                         attributeNames.removeAllElements();
370                         
371                         if (mixedContentLevel != -1) mixedContentLevel++;
372                         
373                         if (currentChar != '/') break;  // change state to operand
374                         
375                         // <element/> drop through
376                         
377                     case emitEndElement:
378                         // emit end element
379                         
380                         try {
381                             final String begin = (String)this.tags.pop();
382                             
383                             buffer.popWriter();
384                             elementName = buffer.getString();
385                             
386                             if (currentChar != '/' && !elementName.equals(begin)) {
387                                 fatalError("end tag </" + elementName + "> does not match begin tag <" + begin + ">",
388                                            this.lineNumber, this.columnNumber);
389                             } else {
390                                 this.documentHandler.endElement(begin);
391                                 
392                                 if (this.tags.empty()) {
393                                     this.documentHandler.endDocument();
394                                     return;
395                                 }
396                             }
397                         }
398                         catch (final EmptyStackException e) {
399                             fatalError("end tag at begining of document", this.lineNumber, this.columnNumber);
400                         }
401                         
402                         if (mixedContentLevel != -1) --mixedContentLevel;
403                         
404                         break;  // change state to operand
405                         
406                     case emitCharacters:
407                         // emit characters
408                         
409                         buffer.flush();
410                         break;  // change state to operand
411                         
412                     case emitCharactersSave:
413                         // emit characters and save current character
414                         
415                         if (mixedContentLevel == -1) mixedContentLevel = 0;
416                         
417                         buffer.flush();
418                         
419                         buffer.saveChar((char)currentChar);
420                         
421                         break;  // change state to operand
422                         
423                     case possiblyEmitCharacters:
424                         // write any skipped whitespace if in mixed content
425                         
426                         if (mixedContentLevel != -1) buffer.flush();
427                         break;  // change state to operand
428                         
429                     case saveAttributeName:
430                         // save attribute name
431                         
432                         attributeNames.addElement(buffer.getString());
433                         break;  // change state to operand
434                         
435                     case saveAttributeValue:
436                         // save attribute value
437                         
438                         attributeValues.addElement(buffer.getString());
439                         break;  // change state to operand
440                         
441                     case startComment:
442                         // change state if we have found "<!--"
443                         
444                         if (buffer.read() != '-') continue; // not "<!--"
445
446                         char[] lastthree = new char[3];
447                         int pos = 0;
448                         while(true) {
449                             currentChar = buffer.read();
450                             lastthree[pos] = (char)currentChar; 
451                             if (lastthree[pos] == '>' && lastthree[(pos + 2) % 3] == '-' && lastthree[(pos + 1) % 3] == '-') break;
452                             pos = (pos + 1) % 3;
453                         }
454
455                         state = operands[inCharData];
456                         continue;  // change state to operand
457
458                     case endComment:
459                         // change state if we find "-->"
460                         
461                         if ((currentChar = buffer.read()) == '-') {
462                             // deal with the case where we might have "------->"
463                             while ((currentChar = buffer.read()) == '-');
464                             
465                             if (currentChar == '>') break;  // end of comment, change state to operand
466                         }
467                         
468                         continue;   // not end of comment, don't change state
469                         
470                     case incLevel:
471                         
472                         level++;
473                         
474                         break;
475                         
476                     case decLevel:
477                         
478                         if (level == 0) break; // outer level <> change state
479                         
480                         level--;
481                         
482                         continue; // in nested <>, don't change state
483                         
484                     case startCDATA:
485                         // change state if we have found "<![CDATA["
486                         
487                         if (buffer.read() != 'C') continue;   // don't change state
488                         if (buffer.read() != 'D') continue;   // don't change state
489                         if (buffer.read() != 'A') continue;   // don't change state
490                         if (buffer.read() != 'T') continue;   // don't change state
491                         if (buffer.read() != 'A') continue;   // don't change state
492                         if (buffer.read() != '[') continue;   // don't change state
493                         break;  // change state to operand
494                         
495                     case endCDATA:
496                         // change state if we find "]]>"
497                         
498                         if ((currentChar = buffer.read()) == ']') {
499                             // deal with the case where we might have "]]]]]]]>"
500                             while ((currentChar = buffer.read()) == ']') buffer.write(']');
501                             
502                             if (currentChar == '>') break;  // end of CDATA section, change state to operand
503                             
504                             buffer.write(']');
505                         }
506                         
507                         buffer.write(']');
508                         buffer.write(currentChar);
509                         continue;   // not end of CDATA section, don't change state
510                         
511                     case processCharRef:
512                         // process character entity
513                         
514                         int crefState = 0;
515                         
516                         currentChar = buffer.read();
517                         
518                         while (true) {
519                             if ("#amp;&pos;'quot;\"gt;>lt;<".charAt(crefState) == currentChar) {
520                                 crefState++;
521                                 
522                                 if (currentChar == ';') {
523                                     buffer.write("#amp;&pos;'quot;\"gt;>lt;<".charAt(crefState));
524                                     break;
525                                     
526                                 } else if (currentChar == '#') {
527                                     final int radix;
528                                     
529                                     currentChar = buffer.read();
530                                     
531                                     if (currentChar == 'x') {
532                                         radix = 16;
533                                         currentChar = buffer.read();
534                                     } else {
535                                         radix = 10;
536                                     }
537                                     
538                                     int charRef = Character.digit((char)currentChar, radix);
539                                     
540                                     while (true) {
541                                         currentChar = buffer.read();
542                                         
543                                         final int digit = Character.digit((char)currentChar, radix);
544                                         
545                                         if (digit == -1) break;
546                                         
547                                         charRef = (char)((charRef * radix) + digit);
548                                     }
549                                     
550                                     if (currentChar == ';' && charRef != -1) {
551                                         buffer.write(charRef);
552                                         break;
553                                     }
554                                     
555                                     fatalError("invalid Character Entitiy", this.lineNumber, this.columnNumber);
556                                 } else {
557                                     currentChar = buffer.read();
558                                 }
559                             } else {
560                                 crefState = ("\u0001\u000b\u0006\u00ff\u00ff\u00ff\u00ff\u00ff\u00ff\u00ff\u00ff" +
561                                              //                               #     a     m     p     ;     &     p     o     s     ;     '
562                                              //                               0     1     2     3     4     5     6     7     8     9     a
563                                              "\u0011\u00ff\u00ff\u00ff\u00ff\u00ff\u0015\u00ff\u00ff\u00ff" +
564                                              //                               q     u     o     t     ;     "     g     t     ;     >
565                                              //                               b     b     d     e     f     10    11    12    13    14
566                                              "\u00ff\u00ff\u00ff").charAt(crefState);
567                                 //                               l     t     ;
568                                 //                               15    16    17
569                                 
570                                 if (crefState == 255) fatalError("invalid Character Entitiy", this.lineNumber, this.columnNumber);
571                             }
572                         }
573                         
574                         break;
575                         
576                     case parseError:
577                         // report fatal error
578                         
579                         fatalError(operand, this.lineNumber, this.columnNumber);
580                         // drop through to exit parser
581                         
582                     case exitParser:
583                         // exit parser
584                         
585                         return;
586                         
587                     case writeCdata:
588                         // write character data
589                         // this will also write any skipped whitespace
590                         
591                         buffer.write(currentChar);
592                         break;  // change state to operand
593                         
594                     case discardAndChange:
595                         // throw saved characters away and change state
596                         
597                         buffer.reset();
598                         break;  // change state to operand
599                         
600                     case discardSaveAndChange:
601                         // throw saved characters away, save character and change state
602                         
603                         buffer.reset();
604                         // drop through to save character and change state
605                         
606                     case saveAndChange:
607                         // save character and change state
608                         
609                         buffer.saveChar((char)currentChar);
610                         break;  // change state to operand
611                         
612                     case change:
613                         // change state to operand
614                         
615                         break;  // change state to operand
616                     }
617                     
618                     state = operand;
619                 }
620             }
621             catch (final IOException e) {
622                 this.errorHandler.fatalError(new SAXParseException(e.toString(), null, null, this.lineNumber, this.columnNumber, e));
623             }
624             finally {
625                 this.errorHandler = this;
626                 this.documentHandler = this.extDocumentHandler = this;
627                 this.tags.removeAllElements();
628             }
629         }
630         
631         public void parse(final InputSource source) throws SAXException, IOException {
632             if (source.getCharacterStream() != null)
633                 parse(source.getCharacterStream());
634             else if (source.getByteStream() != null)
635                 parse(new InputStreamReader(source.getByteStream()));
636             else
637                 parse(new InputStreamReader(new URL(source.getSystemId()).openStream()));
638         }
639         
640         public void parse(final String systemId) throws SAXException, IOException {
641             parse(new InputSource(systemId));
642         }
643         
644         public void setLocale(final Locale locale) throws SAXException {
645             throw new SAXException("Not supported");
646         }
647         
648         public void setEntityResolver(final EntityResolver resolver) {
649             // not supported
650         }
651         
652         public void setDTDHandler(final DTDHandler handler) {
653             // not supported
654         }
655         
656         public void setDocumentHandler(final DocumentHandler handler) {
657             this.documentHandler = (handler == null) ? this : handler;
658             if (handler != null) handler.setDocumentLocator(this);
659             this.extDocumentHandler = this;
660         }
661
662         public void setErrorHandler(final ErrorHandler handler) {
663             this.errorHandler = (handler == null) ? this : handler;
664         }
665         
666         public void setDocumentLocator(final Locator locator) {
667         }
668         
669         public void startDocument() throws SAXException {
670         }
671         
672         public Writer startDocument(final Writer writer) throws SAXException {
673             this.documentHandler.startDocument();
674             return writer;
675         }
676         
677         public void endDocument() throws SAXException {
678         }
679         
680         public void startElement(final String name, final AttributeList attributes) throws SAXException {
681         }
682         
683         public Writer startElement(final String name, final AttributeList attributes, final Writer writer)
684             throws SAXException
685         {
686             this.documentHandler.startElement(name, attributes);
687             return writer;
688         }
689         
690         public void endElement(final String name) throws SAXException {
691         }
692         
693         public void characters(final char ch[], final int start, final int length) throws SAXException {
694         }
695         
696         public void ignorableWhitespace(final char ch[], final int start, final int length) throws SAXException {
697         }
698         
699         public void processingInstruction(final String target, final String data) throws SAXException {
700         }
701         
702         public void warning(final SAXParseException e) throws SAXException {
703         }
704         
705         public void error(final SAXParseException e) throws SAXException {
706         }
707         
708         public void fatalError(final SAXParseException e) throws SAXException {
709             throw e;
710         }
711         
712         public String getPublicId() {
713             return "";
714         }
715         
716         
717         public String getSystemId() {
718             return "";
719         }
720         
721         public int getLineNumber () {
722             return this.lineNumber;
723         }
724         
725         public int getColumnNumber () {
726             return this.columnNumber;
727         }
728         
729         private void fatalError(final String msg, final int lineNumber, final int columnNumber) throws SAXException {
730             this.errorHandler.fatalError(new SAXParseException(msg, null, null, lineNumber, columnNumber));
731         }
732         
733         private class MinMLBuffer extends Writer {
734             public MinMLBuffer(final Reader in) {
735                 this.in = in;
736             }
737             
738             public void close() throws IOException {
739                 flush();
740             }
741             
742             public void flush() throws IOException {
743                 try {
744                     _flush();
745                     if (writer != this) writer.flush();
746                 }
747                 finally {
748                     flushed = true;
749                 }
750             }
751             
752             public void write(final int c) throws IOException {
753                 written = true;
754                 chars[count++] = (char)c;
755             }
756             
757             public void write(final char[] cbuf, final int off, final int len) throws IOException {
758                 written = true;
759                 System.arraycopy(cbuf, off, chars, count, len);
760                 count += len;
761             }
762             
763             public void saveChar(final char c) {
764                 written = false;
765                 chars[count++] = c;
766             }
767             
768             public void pushWriter(final Writer writer) {
769                 MinML.this.tags.push(this.writer);
770                 
771                 this.writer = (writer == null) ? this : writer;
772                 
773                 flushed = written = false;
774             }
775             
776             public Writer getWriter() {
777                 return writer;
778             }
779             
780             public void popWriter() throws IOException {
781                 try {
782                     if (!flushed && writer != this) writer.flush();
783                 }
784                 finally {
785                     writer = (Writer)MinML.this.tags.pop();
786                     flushed = written = false;
787                 }
788             }
789             
790             public String getString() {
791                 final String result = new String(chars, 0, count);
792                 
793                 count = 0;
794                 return result;
795             }
796             
797             public void reset() {
798                 count = 0;
799             }
800             
801             public int read() throws IOException {
802                 if (nextIn == lastIn) {
803                     if (count != 0) {
804                         if (written) {
805                             _flush();
806                         } else if (count >= (chars.length - MinML.this.bufferIncrement)) {
807                             final char[] newChars = new char[chars.length + MinML.this.bufferIncrement];
808                             
809                             System.arraycopy(chars, 0, newChars, 0, count);
810                             chars = newChars;
811                         }
812                     }
813                     
814                     final int numRead = in.read(chars, count, chars.length - count);
815                     
816                     if (numRead == -1) return -1;
817                     
818                     nextIn = count;
819                     lastIn = count + numRead;
820                 }
821                 
822                 return chars[nextIn++];
823             }
824             
825             private void _flush() throws IOException {
826                 if (count != 0) {
827                     try {
828                         if (writer == this) {
829                             try {
830                                 MinML.this.documentHandler.characters(chars, 0, count);
831                             }
832                             catch (final SAXException e) {
833                                 throw new IOException(e.toString());
834                             }
835                         } else {
836                             writer.write(chars, 0, count);
837                         }
838                     }
839                     finally {
840                         count = 0;
841                     }
842                 }
843             }
844             
845             private int nextIn = 0, lastIn = 0;
846             private char[] chars = new char[MinML.this.initialBufferSize];
847             private final Reader in;
848             private int count = 0;
849             private Writer writer = this;
850             private boolean flushed = false;
851             private boolean written = false;
852         }
853         
854         private DocumentHandler extDocumentHandler = this;
855         private DocumentHandler documentHandler = this;
856         private ErrorHandler errorHandler = this;
857         private final Stack tags = new Stack();
858         private int lineNumber = 1;
859         private int columnNumber = 0;
860         private final int initialBufferSize;
861         private final int bufferIncrement;
862         
863         private static final byte[] charClasses = {
864             //  EOF
865             13,
866             //                                      \t  \n          \r
867             -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 12, -1, -1, 12, -1, -1,
868             //
869             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
870             //  SP   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /
871             12,  8,  7, 14, 14, 14,  3,  6, 14, 14, 14, 14, 14, 11, 14,  2,
872             //   0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?
873             14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  5,  1,  4,
874             //
875             14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
876             //                                               [   \   ]
877             14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,  9, 14, 10
878         };
879         
880         private static final String[] operands = {
881             "\u0d15\u1611\u1611\u1611\u1611\u1611\u1611\u1611\u1611\u1611\u1611\u1611\u0015\u0010\u1611",
882             "\u1711\u1000\u0b00\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u0114\u0200\u1811\u0114",
883             "\u1711\u1001\u0b01\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u0215\u1811\u0414",
884             "\u1711\u1001\u0b01\u1711\u1911\u1911\u1911\u1911\u1911\u1911\u1911\u1911\u0315\u1811\u0414",
885             "\u1911\u1911\u1911\u1911\u1911\u0606\u1911\u1911\u1911\u1911\u1911\u0414\u0515\u1811\u0414",
886             "\u1911\u1911\u1911\u1911\u1911\u0606\u1911\u1911\u1911\u1911\u1911\u1911\u0515\u1811\u1911",
887             "\u1a11\u1a11\u1a11\u1a11\u1a11\u1a11\u0715\u0815\u1a11\u1a11\u1a11\u1a11\u0615\u1811\u1a11",
888             "\u0714\u0714\u0714\u070e\u0714\u0714\u0307\u0714\u0714\u0714\u0714\u0714\u0714\u1811\u0714",
889             "\u0814\u0814\u0814\u080e\u0814\u0814\u0814\u0307\u0814\u0814\u0814\u0814\u0814\u1811\u0814",
890             "\u1711\u1002\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u0914\u0915\u1811\u0914",
891             "\u1b11\u1b11\u0904\u1b11\u1b11\u1b11\u1b11\u1b11\u1215\u1b11\u1b11\u1b11\u1b11\u1811\u0105",
892             "\u1711\u1012\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1811\u1711",
893             "\u1711\u1c11\u0912\u1711\u0e12\u1711\u1711\u1711\u1212\u1711\u1711\u1711\u1711\u1811\u0113",
894             "\u1711\u1c11\u0912\u1711\u0e12\u1711\u1711\u1711\u1212\u1711\u1711\u1711\u1711\u1811\u0113",
895             "\u0e15\u0e15\u0e15\u0e15\u0f15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u1811\u0e15",
896             "\u0e15\u0015\u0e15\u0e15\u0f15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u1811\u0e15",
897             "\u0c03\u110f\u110f\u110e\u110f\u110f\u110f\u110f\u110f\u110f\u110f\u110f\u1014\u1811\u110f",
898             "\u0a15\u110f\u110f\u110e\u110f\u110f\u110f\u110f\u110f\u110f\u110f\u110f\u110f\u1811\u110f",
899             "\u1d11\u1d11\u1d11\u1d11\u1d11\u1d11\u1d11\u1d11\u1d11\u130c\u1d11\u1408\u1d11\u1811\u1515",
900             "\u130f\u130f\u130f\u130f\u130f\u130f\u130f\u130f\u130f\u130f\u110d\u130f\u130f\u1811\u130f",
901             "\u1415\u1415\u1415\u1415\u1415\u1415\u1415\u1415\u1415\u1415\u1415\u0009\u1415\u1811\u1415",
902             "\u150a\u000b\u1515\u1515\u1515\u1515\u1515\u1515\u1515\u1515\u1515\u1515\u1515\u1811\u1515",
903             "expected Element",
904             "unexpected character in tag",
905             "unexpected end of file found",
906             "attribute name not followed by '='",
907             "invalid attribute value",
908             "expecting end tag",
909             "empty tag",
910             "unexpected character after <!"
911         };
912     }
913     ///////////////////////////////////////////////////////////////////////////////
914
915     private class XMLHelper implements DocumentHandler {
916         private MinML minml = new MinML();
917         XMLHelper() { }
918         public void parse(Reader r) throws IOException, XML.SAXException {
919             minml.setDocumentHandler(this);
920             minml.parse(new InputSource(r));
921         }
922         
923         public void startDocument() throws SAXException { }
924         public void endDocument() throws SAXException { }
925         public void processingInstruction (String target, String data) throws SAXException { }
926         public Writer startDocument(final Writer writer) throws SAXException { return null; }
927         public Writer startElement(final String name, final AttributeList attributes, final Writer writer) throws SAXException { return null; }
928
929         public void setDocumentLocator (Locator locator) {
930             this.locator = locator;
931         }
932
933         private Locator locator = null;
934
935         public void startElement(String name, AttributeList atts) throws SAXException {
936             String[] keys = new String[atts.getLength()];
937             Object[] vals = new Object[atts.getLength()];
938             for (int i=0; i <atts.getLength(); i++) {
939                 keys[i] = atts.getName(i);
940                 vals[i] = atts.getValue(i).toString();
941             }
942             XML.this.startElement(name, keys, vals,
943                                   locator == null ? 0 : locator.getLineNumber(), locator == null ? 0 : locator.getColumnNumber());
944         }
945
946         public void endElement(String name) throws SAXException {
947             XML.this.endElement(name, locator == null ? 0 : locator.getLineNumber(), locator == null ? 0 : locator.getColumnNumber());
948         }
949
950         public void characters(char ch[], int start, int length) throws SAXException {
951             XML.this.content(ch, start, length, locator == null ? 0 : locator.getLineNumber(), locator == null ? 0 : locator.getColumnNumber());
952         }
953         public void ignorableWhitespace(char ch[], int start, int length) throws SAXException {
954             XML.this.content(ch, start, length, locator == null ? 0 : locator.getLineNumber(), locator == null ? 0 : locator.getColumnNumber());
955         }
956     }
957
958     public XML() { }
959     public void parse(Reader r) throws IOException, XML.SAXException {
960         XMLHelper helper = new XMLHelper();
961         helper.parse(r);
962         helper = null;
963     }
964
965     /** indicates the start of an element with name <tt>name</tt>, and attributes <tt>attributes</tt>, starting on line <tt>line</tt> */
966     public abstract void startElement(String name, String[] keys, Object[] vals, int line, int col) throws SAXException;
967
968     /** indicates the end of an element with name <tt>name</tt>, starting on line <tt>line</tt> */
969     public abstract void endElement(String name, int line, int col) throws SAXException;
970
971     /** indicates a chunk of CDATA content, starting on line <tt>line</tt> */
972     public abstract void content(char[] content, int start, int length, int line, int col) throws SAXException; 
973 }