// Static data/methods ///////////////////////////////////////////////////////////////////
+ /** maximum length of a line */
+ private static final int MAX_COLUMN = 150;
+
/** a template cache so that only one Template object is created for each xwt */
private static Hashtable cache = new Hashtable(1000);
}
public static Template buildTemplate(InputStream is, String nodeName) {
+ return buildTemplate(is, nodeName, new TemplateHelper());
+ }
+
+ public static Template buildTemplate(InputStream is, String nodeName, TemplateHelper t) {
try {
- return new Template(is, nodeName);
- } catch (XML.SAXParseException e) {
- if (Log.on) Log.log(Template.class, "error parsing template at " + nodeName + ":" + e.getLineNumber() + "," + e.getColumnNumber());
- if (Log.on) Log.log(Template.class, e);
- return null;
- } catch (XML.SAXException e) {
+ return new Template(is, nodeName, t);
+ } catch (XML.SchemaException e) {
if (Log.on) Log.log(Template.class, "error parsing template " + nodeName);
- if (Log.on) Log.log(Template.class, e);
+ if (Log.on) Log.log(Template.class, e.getMessage());
return null;
- } catch (TemplateException te) {
- if (Log.on) Log.log(Template.class, "error parsing template " + nodeName);
- if (Log.on) Log.log(Template.class, te);
+ } catch (XML.XMLException e) {
+ if (Log.on) Log.log(Template.class, "error parsing template at " + nodeName + ":" + e.getLine() + "," + e.getCol());
+ if (Log.on) Log.log(Template.class, e.getMessage());
return null;
} catch (IOException e) {
if (Log.on) Log.log(Template.class, "IOException while parsing template " + nodeName + " -- this should never happen");
this.nodeName = nodeName;
cache.put(nodeName, this);
}
- private Template(InputStream is, String nodeName) throws XML.SAXException, IOException {
+ private Template(InputStream is, String nodeName, TemplateHelper th) throws XML.XMLException, IOException {
this(nodeName);
- new TemplateHelper().parseit(is, this);
+ th.parseit(is, this);
}
/** calculates, caches, and returns an integer approximation of how long it will take to apply this template, including pre/post and children */
// XML Parsing /////////////////////////////////////////////////////////////////
/** handles XML parsing; builds a Template tree as it goes */
- private static class TemplateHelper extends XML {
+ static final class TemplateHelper extends XML {
- TemplateHelper() {
- for(int i=0; i<defaultImportList.length; i++) importlist.addElement(defaultImportList[i]);
- }
+ TemplateHelper() { }
/** parse an XML input stream, building a Template tree off of <tt>root</tt> */
- void parseit(InputStream is, Template root) throws XML.SAXException, IOException {
+ void parseit(InputStream is, Template root) throws XML.XMLException, IOException {
+ rootNodeHasBeenEncountered = false;
+ templateNodeHasBeenEncountered = false;
+ staticNodeHasBeenEncountered = false;
+ templateNodeHasBeenFinished = false;
+ nameOfHeaderNodeBeingProcessed = null;
+
+ nodeStack.setSize(0);
+ importlist.setSize(0);
+ preapply.setSize(0);
+ postapply.setSize(0);
+
+ importlist.fromArray(defaultImportList);
+
t = root;
- parse(new TabAndMaxColumnEnforcingReader(new InputStreamReader(is), root.nodeName));
+ parse(new InputStreamReader(is));
}
/** parsing state: true iff we have already encountered the <xwt> open-tag */
/** the template we're currently working on */
Template t = null;
- public void startElement(String name, String[] keys, Object[] vals, int line, int col) throws XML.SAXException {
-
+ public void startElement(XML.Element c) throws XML.SchemaException {
if (templateNodeHasBeenFinished) {
- throw new XML.SAXException("no elements may appear after the <template> node");
+ throw new XML.SchemaException("no elements may appear after the <template> node");
} else if (!rootNodeHasBeenEncountered) {
- if (!"xwt".equals(name)) throw new XML.SAXException("root element was not <xwt>");
- if (keys.length != 0) throw new XML.SAXException("root element must not have attributes");
+ if (!"xwt".equals(c.localName)) throw new XML.SchemaException("root element was not <xwt>");
+ if (c.len != 0) throw new XML.SchemaException("root element must not have attributes");
rootNodeHasBeenEncountered = true;
return;
} else if (!templateNodeHasBeenEncountered) {
- if (nameOfHeaderNodeBeingProcessed != null) throw new XML.SAXException("can't nest header nodes");
- nameOfHeaderNodeBeingProcessed = name;
+ if (nameOfHeaderNodeBeingProcessed != null) throw new XML.SchemaException("can't nest header nodes");
+ nameOfHeaderNodeBeingProcessed = c.localName;
- if (name.equals("import")) {
- if (keys.length != 1 || !keys[0].equals("name"))
- throw new XML.SAXException("<import> node must have exactly one attribute, which must be called 'name'");
- String importpackage = vals[0].toString();
+ if (c.localName.equals("import")) {
+ if (c.len != 1 || !c.keys[0].equals("name"))
+ throw new XML.SchemaException("<import> node must have exactly one attribute, which must be called 'name'");
+ String importpackage = c.vals[0].toString();
if (importpackage.endsWith(".*")) importpackage = importpackage.substring(0, importpackage.length() - 2);
importlist.addElement(importpackage);
return;
- } else if (name.equals("redirect")) {
- if (keys.length != 1 || !keys[0].equals("target"))
- throw new XML.SAXException("<redirect> node must have exactly one attribute, which must be called 'target'");
+ } else if (c.localName.equals("redirect")) {
+ if (c.len != 1 || !c.keys[0].equals("target"))
+ throw new XML.SchemaException("<redirect> node must have exactly one attribute, which must be called 'target'");
if (t.redirect != null)
- throw new XML.SAXException("the <redirect> header element may not appear more than once");
- t.redirect = vals[0].toString();
+ throw new XML.SchemaException("the <redirect> header element may not appear more than once");
+ t.redirect = c.vals[0].toString();
return;
- } else if (name.equals("preapply")) {
- if (keys.length != 1 || !keys[0].equals("name"))
- throw new XML.SAXException("<preapply> node must have exactly one attribute, which must be called 'name'");
- preapply.addElement(vals[0]);
+ } else if (c.localName.equals("preapply")) {
+ if (c.len != 1 || !c.keys[0].equals("name"))
+ throw new XML.SchemaException("<preapply> node must have exactly one attribute, which must be called 'name'");
+ preapply.addElement(c.vals[0]);
return;
- } else if (name.equals("postapply")) {
- if (keys.length != 1 || !keys[0].equals("name"))
- throw new XML.SAXException("<postapply> node must have exactly one attribute, which must be called 'name'");
- postapply.addElement(vals[0]);
+ } else if (c.localName.equals("postapply")) {
+ if (c.len != 1 || !c.keys[0].equals("name"))
+ throw new XML.SchemaException("<postapply> node must have exactly one attribute, which must be called 'name'");
+ postapply.addElement(c.vals[0]);
return;
- } else if (name.equals("static")) {
+ } else if (c.localName.equals("static")) {
if (staticNodeHasBeenEncountered)
- throw new XML.SAXException("the <static> header node may not appear more than once");
- if (keys.length > 0)
- throw new XML.SAXException("the <static> node may not have attributes");
+ throw new XML.SchemaException("the <static> header node may not appear more than once");
+ if (c.len > 0)
+ throw new XML.SchemaException("the <static> node may not have attributes");
staticNodeHasBeenEncountered = true;
return;
- } else if (name.equals("preserve")) {
- if (keys.length != 1 || !keys[0].equals("attributes"))
- throw new XML.SAXException("<preserve> node must have exactly one attribute, which must be called 'attributes'");
+ } else if (c.localName.equals("preserve")) {
+ if (c.len != 1 || !c.keys[0].equals("attributes"))
+ throw new XML.SchemaException("<preserve> node must have exactly one attribute, which must be called 'attributes'");
if (t.preserve != null)
- throw new XML.SAXException("<preserve> header element may not appear more than once");
+ throw new XML.SchemaException("<preserve> header element may not appear more than once");
- StringTokenizer tok = new StringTokenizer(vals[0].toString(), ",", false);
+ StringTokenizer tok = new StringTokenizer(c.vals[0].toString(), ",", false);
t.preserve = new String[tok.countTokens()];
for(int i=0; i<t.preserve.length; i++) t.preserve[i] = tok.nextToken();
return;
- } else if (name.equals("template")) {
+ } else if (c.localName.equals("template")) {
// finalize importlist/preapply/postapply, since they can't change from here on
- t.startLine = line;
+ t.startLine = getLine();
importlist.toArray(t.importlist = new String[importlist.size()]);
if (preapply.size() > 0) preapply.copyInto(t.preapply = new String[preapply.size()]);
if (postapply.size() > 0) postapply.copyInto(t.postapply = new String[postapply.size()]);
- importlist = preapply = postapply = null;
+ importlist.setSize(0); preapply.setSize(0); postapply.setSize(0);
templateNodeHasBeenEncountered = true;
} else {
- throw new XML.SAXException("unrecognized header node \"" + name + "\"");
+ throw new XML.SchemaException("unrecognized header node \"" + c.localName + "\"");
}
// instantiate a new node, and set its nodeName/importlist/preapply
Template t2 = new Template(t.nodeName + "." + t.childvect.size());
t2.importlist = t.importlist;
- t2.startLine = line;
- if (!name.equals("box")) t2.preapply = new String[] { name };
+ t2.startLine = getLine();
+ if (!c.localName.equals("box")) t2.preapply = new String[] { c.localName };
// make the new node the current node
t = t2;
}
- t.keys = keys;
- t.vals = vals;
-
+ // TODO: Sort contents straight from one array to another
+ t.keys = new String[c.len];
+ t.vals = new Object[c.len];
+ System.arraycopy(c.keys, 0, t.keys, 0, c.len);
+ System.arraycopy(c.vals, 0, t.vals, 0, c.len);
quickSortAttributes(0, t.keys.length - 1);
for(int i=0; i<t.keys.length; i++) {
if (t.keys[i].equals("id")) {
- t.id = vals[i].toString().intern();
+ t.id = t.vals[i].toString().intern();
t.keys[i] = null;
continue;
}
t.keys[i] = t.keys[i].intern();
- String valString = vals[i].toString();
+ String valString = t.vals[i].toString();
if (valString.equals("true")) t.vals[i] = Boolean.TRUE;
else if (valString.equals("false")) t.vals[i] = Boolean.FALSE;
hasNonNumeral = true;
break;
}
- if (valString.length() > 0 && !hasNonNumeral) vals[i] = new Double(valString);
- else vals[i] = valString.intern();
+ if (valString.length() > 0 && !hasNonNumeral) t.vals[i] = new Double(valString);
+ else t.vals[i] = valString.intern();
}
// bump thisbox to the front of the pack
o = t.vals[right]; t.vals[right] = t.vals[i]; t.vals[i] = o;
return i;
}
-
+
/** simple quicksort, from http://sourceforge.net/snippet/detail.php?type=snippet&id=100240 */
private void quickSortAttributes(int left, int right) {
if (left >= right) return;
quickSortAttributes(left, p - 1);
quickSortAttributes(p + 1, right);
}
-
- public void endElement(String name, int line, int col) throws XML.SAXException {
-
- boolean hasNonWhitespace = false;
-
- int len = t == null || t.content == null ? 0 : t.content.length();
- for(int i=0; t.content != null && i<len; i++)
-
- // ignore double-slash comment blocks
- if (t.content.charAt(i) == '/' && t.content.charAt(i + 1) == '/') {
- while(t.content.charAt(i) != '\n' && i<len) i++;
- i--;
-
- // ignore /* .. */ comment blocks
- } else if (i<len - 1 && t.content.charAt(i) == '/' && t.content.charAt(i + 1) == '*') {
- i += 2;
- while(i<len - 1 && !(t.content.charAt(i) == '*' && t.content.charAt(i + 1) == '/')) i++;
- if (i<len - 1 && t.content.charAt(i) == '*' && t.content.charAt(i + 1) == '/') i += 2;
- i--;
-
- // check for named functions
- } else if (i + 8 <= len && t.content.charAt(i) == 'f' && t.content.charAt(i+1) == 'u' &&
- t.content.charAt(i+2) == 'n' && t.content.charAt(i+3) == 'c' && t.content.charAt(i+4) == 't' &&
- t.content.charAt(i+5) == 'i' && t.content.charAt(i+6) == 'o' && t.content.charAt(i+7) == 'n') {
- int j = i + 8;
- while(j<len && Character.isWhitespace(t.content.charAt(j))) j++;
- if (j<len && t.content.charAt(j) != '(')
- throw new XML.SAXException("named functions are not permitted in XWT -- instead of \"function foo() { ... }\"," +
- " use \"foo = function() { ... }\"");
-
- // replace " and " with " && "
- } else if (i + 5 < len && Character.isWhitespace(t.content.charAt(i)) &&
- t.content.charAt(i+1) == 'a' && t.content.charAt(i+2) == 'n' && t.content.charAt(i+3) == 'd' &&
- Character.isWhitespace(t.content.charAt(i + 4))) {
- t.content.setCharAt(i+1, '&');
- t.content.setCharAt(i+2, '&');
- t.content.setCharAt(i+3, ' ');
- hasNonWhitespace = true;
-
- // generic check for nonwhitespace
- } else if (!Character.isWhitespace(t.content.charAt(i))) {
- hasNonWhitespace = true;
- }
-
+ public void endElement(XML.Element c) throws XML.SchemaException {
if (rootNodeHasBeenEncountered && !templateNodeHasBeenEncountered) {
- if ("static".equals(nameOfHeaderNodeBeingProcessed) && hasNonWhitespace) t.staticscript = genscript(true);
+ if ("static".equals(nameOfHeaderNodeBeingProcessed) && t.content != null) t.staticscript = genscript(true);
nameOfHeaderNodeBeingProcessed = null;
} else if (templateNodeHasBeenEncountered && !templateNodeHasBeenFinished) {
-
// turn our childvect into a Template[]
t.childvect.copyInto(t.children = new Template[t.childvect.size()]);
t.childvect = null;
- if (hasNonWhitespace) t.script = genscript(false);
+ if (t.content != null) t.script = genscript(false);
if (nodeStack.size() == 0) {
// </template>
if (Log.on) Log.log(this, " ERROR: " + ee.getMessage());
thisscript = null;
} catch (IOException ioe) {
- if (Log.on) Log.log(this, "IOException while compiling script; this should never happen");
- if (Log.on) Log.log(this, ioe);
+ if (Log.on) Log.log(this, " ERROR: " + ioe.getMessage());
thisscript = null;
}
return thisscript;
}
- public void content(char[] ch, int start, int length, int line, int col) throws XML.SAXException {
- if ("static".equals(nameOfHeaderNodeBeingProcessed) || templateNodeHasBeenEncountered) {
- int contentlines = 0;
- for(int i=start; i<start + length; i++) if (ch[i] == '\n') contentlines++;
- line -= contentlines;
+ public void characters(char[] ch, int start, int length) throws XML.SchemaException {
+ // invoke the max-column-length and no-tab crusade
+ if (getCol() + length > MAX_COLUMN) throw new XML.SchemaException(
+ t.nodeName+ ":" + getLine() + ": lines longer than " + MAX_COLUMN + " characters not allowed");
+
+ for (int i=0; length >i; i++) if (ch[start+i] == '\t') throw new XML.SchemaException(
+ t.nodeName+ ":" + getLine() + "," + getCol() + ": tabs are not allowed in XWT files");
+ if ("static".equals(nameOfHeaderNodeBeingProcessed) || templateNodeHasBeenEncountered) {
if (t.content == null) {
- t.content_start = line;
+ t.content_start = getLine();
t.content_lines = 0;
t.content = new StringBuffer();
}
- for(int i=t.content_start + t.content_lines; i<line; i++) {
- t.content.append('\n');
- t.content_lines++;
- }
-
t.content.append(ch, start, length);
- t.content_lines += contentlines;
+ t.content_lines++;
} else if (nameOfHeaderNodeBeingProcessed != null) {
- throw new XML.SAXException("header node <" + nameOfHeaderNodeBeingProcessed + "> cannot have text content");
-
+ throw new XML.SchemaException("header node <" + nameOfHeaderNodeBeingProcessed + "> cannot have text content");
}
-
}
- }
-
- /** a filtering reader that watches for tabs and long lines */
- private static class TabAndMaxColumnEnforcingReader extends FilterReader {
- private int MAX_COLUMN = 150;
- private int column = 0;
- private int line = 1;
- private boolean lastCharWasCR = false;
- private String filename;
- public TabAndMaxColumnEnforcingReader(Reader r, String filename) { super(r); this.filename = filename; }
- public int read() {
- if (Log.on) Log.log(this, this.getClass().getName() + ".read() not supported, this should never happen");
- return -1;
+ public void whitespace(char[] ch, int start, int length) throws XML.SchemaException {
}
- public long skip(long numskip) {
- if (Log.on) Log.log(this, this.getClass().getName() + ".skip() not supported; this should never happen");
- return numskip;
- }
- public int read(char[] buf, int off, int len) throws IOException {
- int ret = super.read(buf, off, len);
- for(int i=off; i<off + ret; i++)
- if (buf[i] == '\t') {
- throw new TemplateException(filename + ":" + line + "," + column + ": tabs are not allowed in XWT files");
- } else if (buf[i] == '\r') {
- column = 0;
- line++;
- lastCharWasCR = true;
- } else if (buf[i] == '\n') {
- column = 0;
- if (!lastCharWasCR) line++;
- } else if (++column > MAX_COLUMN) {
- throw new TemplateException(filename + ":" + line + ": lines longer than " + MAX_COLUMN + " characters not allowed");
- } else {
- lastCharWasCR = false;
- }
- return ret;
- }
- }
-
- private static class TemplateException extends IOException {
- TemplateException(String s) { super(s); }
}
}
+++ /dev/null
-package org.xwt;
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-
-/** an event-driven XML parser, derived from MinML (http://www.wilson.co.uk/xml/minml.htm) */
-public abstract class XML {
-
- /////////////////////////////////////////////////////////////////////////////////////////////
- // The following code was copied from the w3c's org.xml.sax.* classes
- /////////////////////////////////////////////////////////////////////////////////////////////
-
- protected static interface AttributeList {
- public abstract int getLength ();
- public abstract String getName (int i);
- public abstract String getType (int i);
- public abstract String getValue (int i);
- public abstract String getType (String name);
- public abstract String getValue (String name);
- }
-
- protected static interface DTDHandler {
- public abstract void notationDecl (String name, String publicId, String systemId) throws SAXException;
- public abstract void unparsedEntityDecl (String name, String publicId, String systemId, String notationName) throws SAXException;
- }
-
- protected static interface EntityResolver {
- public abstract InputSource resolveEntity (String publicId, String systemId) throws SAXException, IOException;
- }
-
- protected static interface ErrorHandler {
- public abstract void warning (SAXParseException exception) throws SAXException;
- public abstract void error (SAXParseException exception) throws SAXException;
- public abstract void fatalError (SAXParseException exception) throws SAXException;
- }
-
- private static class InputSource {
- public InputSource () { }
- public InputSource (String systemId) { setSystemId(systemId); }
- public InputSource (InputStream byteStream) { setByteStream(byteStream); }
- public InputSource (Reader characterStream) { setCharacterStream(characterStream); }
- public void setPublicId (String publicId) { this.publicId = publicId; }
- public String getPublicId () { return publicId; }
- public void setSystemId (String systemId) { this.systemId = systemId; }
- public String getSystemId() { return systemId; }
- public void setByteStream (InputStream byteStream) { this.byteStream = byteStream; }
- public InputStream getByteStream() { return byteStream; }
- public void setEncoding (String encoding) { this.encoding = encoding; }
- public String getEncoding() { return encoding; }
- public void setCharacterStream (Reader characterStream) { this.characterStream = characterStream; }
- public Reader getCharacterStream () { return characterStream; }
- private String publicId;
- private String systemId;
- private InputStream byteStream;
- private String encoding;
- private Reader characterStream;
- }
-
- protected static interface Locator {
- public abstract String getPublicId ();
- public abstract String getSystemId ();
- public abstract int getLineNumber ();
- public abstract int getColumnNumber ();
- }
-
- protected static interface Parser {
- public abstract void setLocale (Locale locale) throws SAXException;
- public abstract void setEntityResolver (EntityResolver resolver);
- public abstract void setDTDHandler (DTDHandler handler);
- public abstract void setDocumentHandler (DocumentHandler handler);
- public abstract void setErrorHandler (ErrorHandler handler);
- public abstract void parse (InputSource source) throws SAXException, IOException;
- public abstract void parse (String systemId) throws SAXException, IOException;
- }
-
- public static class SAXException extends Exception {
- public SAXException (String message) {
- super();
- this.message = message;
- this.exception = null;
- }
-
- public SAXException (Exception e) {
- super();
- this.message = null;
- this.exception = e;
- }
-
- public SAXException (String message, Exception e) {
- super();
- this.message = message;
- this.exception = e;
- }
-
- public String getMessage () {
- if (message == null && exception != null) {
- return exception.getMessage();
- } else {
- return this.message;
- }
- }
-
- public Exception getException () { return exception; }
- public String toString () { return getMessage(); }
- private String message;
- private Exception exception;
- }
-
- static class SAXParseException extends SAXException {
- public SAXParseException (String message, Locator locator) {
- super(message);
- this.publicId = locator.getPublicId();
- this.systemId = locator.getSystemId();
- this.lineNumber = locator.getLineNumber();
- this.columnNumber = locator.getColumnNumber();
- }
- public SAXParseException (String message, Locator locator, Exception e) {
- super(message, e);
- this.publicId = locator.getPublicId();
- this.systemId = locator.getSystemId();
- this.lineNumber = locator.getLineNumber();
- this.columnNumber = locator.getColumnNumber();
- }
- public SAXParseException (String message, String publicId, String systemId, int lineNumber, int columnNumber) {
- super(message);
- this.publicId = publicId;
- this.systemId = systemId;
- this.lineNumber = lineNumber;
- this.columnNumber = columnNumber;
- }
- public SAXParseException (String message, String publicId, String systemId, int lineNumber, int columnNumber, Exception e) {
- super(message, e);
- this.publicId = publicId;
- this.systemId = systemId;
- this.lineNumber = lineNumber;
- this.columnNumber = columnNumber;
- }
- public String getPublicId() { return this.publicId; }
- public String getSystemId() { return this.systemId; }
- public int getLineNumber () { return this.lineNumber; }
- public int getColumnNumber () { return this.columnNumber; }
- private String publicId;
- private String systemId;
- private int lineNumber;
- private int columnNumber;
- }
-
- protected static interface DocumentHandler {
- public abstract void setDocumentLocator (Locator locator);
- public abstract void startDocument() throws SAXException;
- public abstract void endDocument() throws SAXException;
- public abstract void startElement(String name, AttributeList atts) throws SAXException;
- public abstract void endElement(String name) throws SAXException;
- public abstract void characters(char ch[], int start, int length) throws SAXException;
- public abstract void ignorableWhitespace(char ch[], int start, int length) throws SAXException;
- public abstract void processingInstruction (String target, String data) throws SAXException;
- Writer startDocument(final Writer writer) throws SAXException;
- Writer startElement(final String name, final AttributeList attributes, final Writer writer) throws SAXException;
- }
-
-
-
- /////////////////////////////////////////////////////////////////////////////////////////////
- // Everything from here down is copied verbatim from the MinML 1.7
- // distribution, except for these modifications:
- // - some classes have been changed from 'public' to 'private static'
- // - extraneous import and package declarations have been removed
- // - the advertising clause of the copyright notice has been removed
- // as approved by John Wilson in an email to Adam Megacz.
- /////////////////////////////////////////////////////////////////////////////////////////////
-
- // Copyright (c) 2000, 2001, 2002 The Wilson Partnership.
- // All Rights Reserved.
- // @(#)MinML.java, 1.8(provisional), 2nd March 2002
- // Author: John Wilson - tug@wilson.co.uk
-
- /*
- Copyright (c) 2000, 2001 John Wilson (tug@wilson.co.uk).
- All rights reserved.
- Redistribution and use in source and binary forms,
- with or without modification, are permitted provided
- that the following conditions are met:
-
- Redistributions of source code must retain the above
- copyright notice, this list of conditions and the
- following disclaimer.
-
- Redistributions in binary form must reproduce the
- above copyright notice, this list of conditions and
- the following disclaimer in the documentation and/or
- other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY JOHN WILSON ``AS IS'' AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JOHN WILSON
- BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- OF THE POSSIBILITY OF SUCH DAMAGE
- */
-
- private static class MinML implements Parser, Locator, DocumentHandler, ErrorHandler {
- public static final int endStartName = 0;
- public static final int emitStartElement = 1;
- public static final int emitEndElement = 2;
- public static final int possiblyEmitCharacters = 3;
- public static final int emitCharacters = 4;
- public static final int emitCharactersSave = 5;
- public static final int saveAttributeName = 6;
- public static final int saveAttributeValue = 7;
- public static final int startComment = 8;
- public static final int endComment = 9;
- public static final int incLevel = 10;
- public static final int decLevel = 11;
- public static final int startCDATA = 12;
- public static final int endCDATA = 13;
- public static final int processCharRef = 14;
- public static final int writeCdata = 15;
- public static final int exitParser = 16;
- public static final int parseError = 17;
- public static final int discardAndChange = 18;
- public static final int discardSaveAndChange = 19;
- public static final int saveAndChange = 20;
- public static final int change = 21;
- public static final int writeAsWS = 22;
-
- public static final int inSkipping = 0;
- public static final int inSTag = 1;
- public static final int inPossiblyAttribute = 2;
- public static final int inNextAttribute = 3;
- public static final int inAttribute = 4;
- public static final int inAttribute1 = 5;
- public static final int inAttributeValue = 6;
- public static final int inAttributeQuoteValue = 7;
- public static final int inAttributeQuotesValue = 8;
- public static final int inETag = 9;
- public static final int inETag1 = 10;
- public static final int inMTTag = 11;
- public static final int inTag = 12;
- public static final int inTag1 = 13;
- public static final int inPI = 14;
- public static final int inPI1 = 15;
- public static final int inPossiblySkipping = 16;
- public static final int inCharData = 17;
- public static final int inCDATA = 18;
- public static final int inCDATA1 = 19;
- public static final int inComment =20;
- public static final int inDTD = 21;
-
- public MinML(final int initialBufferSize, final int bufferIncrement) {
- this.initialBufferSize = initialBufferSize;
- this.bufferIncrement = bufferIncrement;
- }
-
- public MinML() {
- this(256, 128);
- }
-
- public void parse(final Reader in) throws SAXException, IOException {
- final Vector attributeNames = new Vector();
- final Vector attributeValues = new Vector();
-
- final AttributeList attrs = new AttributeList() {
- public int getLength() {
- return attributeNames.size();
- }
-
- public String getName(final int i) {
- return (String)attributeNames.elementAt(i);
- }
-
- public String getType(final int i) {
- return "CDATA";
- }
-
- public String getValue(final int i) {
- return (String)attributeValues.elementAt(i);
- }
-
- public String getType(final String name) {
- return "CDATA";
- }
-
- public String getValue(final String name) {
- final int index = attributeNames.indexOf(name);
-
- return (index == -1) ? null : (String)attributeValues.elementAt(index);
- }
- };
-
- final MinMLBuffer buffer = new MinMLBuffer(in);
- int currentChar = 0, charCount = 0;
- int level = 0;
- int mixedContentLevel = -1;
- String elementName = null;
- String state = operands[inSkipping];
-
- this.lineNumber = 1;
- this.columnNumber = 0;
-
- try {
- while(true) {
- charCount++;
-
- //
- // this is to try and make the loop a bit faster
- // currentChar = buffer.read(); is simpler but is a bit slower.
- //
- currentChar = (buffer.nextIn == buffer.lastIn) ? buffer.read() : buffer.chars[buffer.nextIn++];
-
- final int transition;
-
- if (currentChar > ']') {
- transition = state.charAt(14);
- } else {
- final int charClass = charClasses[currentChar + 1];
-
- if (charClass == -1) fatalError("Document contains illegal control character with value " + currentChar, this.lineNumber, this.columnNumber);
-
- if (charClass == 12) {
- if (currentChar == '\r') {
- currentChar = '\n';
- charCount = -1;
- }
-
- if (currentChar == '\n') {
- if (charCount == 0) continue; // preceeded by '\r' so ignore
-
- if (charCount != -1) charCount = 0;
-
- this.lineNumber++;
- this.columnNumber = 0;
- }
- }
-
- transition = state.charAt(charClass);
- }
-
- this.columnNumber++;
-
- final String operand = operands[transition >>> 8];
-
- switch (transition & 0XFF) {
- case endStartName:
- // end of start element name
- elementName = buffer.getString();
- if (currentChar != '>' && currentChar != '/') break; // change state to operand
- // drop through to emit start element (we have no attributes)
-
- case emitStartElement:
- // emit start element
-
- final Writer newWriter = this.extDocumentHandler.startElement(elementName, attrs,
- (this.tags.empty()) ?
- this.extDocumentHandler.startDocument(buffer)
- :
- buffer.getWriter());
-
- buffer.pushWriter(newWriter);
- this.tags.push(elementName);
-
- attributeValues.removeAllElements();
- attributeNames.removeAllElements();
-
- if (mixedContentLevel != -1) mixedContentLevel++;
-
- if (currentChar != '/') break; // change state to operand
-
- // <element/> drop through
-
- case emitEndElement:
- // emit end element
-
- try {
- final String begin = (String)this.tags.pop();
-
- buffer.popWriter();
- elementName = buffer.getString();
-
- if (currentChar != '/' && !elementName.equals(begin)) {
- fatalError("end tag </" + elementName + "> does not match begin tag <" + begin + ">",
- this.lineNumber, this.columnNumber);
- } else {
- this.documentHandler.endElement(begin);
-
- if (this.tags.empty()) {
- this.documentHandler.endDocument();
- return;
- }
- }
- }
- catch (final EmptyStackException e) {
- fatalError("end tag at begining of document", this.lineNumber, this.columnNumber);
- }
-
- if (mixedContentLevel != -1) --mixedContentLevel;
-
- break; // change state to operand
-
- case emitCharacters:
- // emit characters
-
- buffer.flush();
- break; // change state to operand
-
- case emitCharactersSave:
- // emit characters and save current character
-
- if (mixedContentLevel == -1) mixedContentLevel = 0;
-
- buffer.flush();
-
- buffer.saveChar((char)currentChar);
-
- break; // change state to operand
-
- case possiblyEmitCharacters:
- // write any skipped whitespace if in mixed content
-
- if (mixedContentLevel != -1) buffer.flush();
- break; // change state to operand
-
- case saveAttributeName:
- // save attribute name
-
- attributeNames.addElement(buffer.getString());
- break; // change state to operand
-
- case saveAttributeValue:
- // save attribute value
-
- attributeValues.addElement(buffer.getString());
- break; // change state to operand
-
- case startComment:
- // change state if we have found "<!--"
-
- if (buffer.read() != '-') continue; // not "<!--"
-
- state = operands[inComment];
- continue; // change state to operand
-
- case endComment:
- // change state if we find "-->"
-
- if ((currentChar = buffer.read()) == '-') {
- // deal with the case where we might have "------->"
- while ((currentChar = buffer.read()) == '-');
-
- if (currentChar == '>') {
- state = operands[inCharData];
- }
- }
-
- continue; // not end of comment, don't change state
-
- case incLevel:
-
- level++;
-
- break;
-
- case decLevel:
-
- if (level == 0) break; // outer level <> change state
-
- level--;
-
- continue; // in nested <>, don't change state
-
- case startCDATA:
- // change state if we have found "<![CDATA["
-
- if (buffer.read() != 'C') continue; // don't change state
- if (buffer.read() != 'D') continue; // don't change state
- if (buffer.read() != 'A') continue; // don't change state
- if (buffer.read() != 'T') continue; // don't change state
- if (buffer.read() != 'A') continue; // don't change state
- if (buffer.read() != '[') continue; // don't change state
- break; // change state to operand
-
- case endCDATA:
- // change state if we find "]]>"
-
- if ((currentChar = buffer.read()) == ']') {
- // deal with the case where we might have "]]]]]]]>"
- while ((currentChar = buffer.read()) == ']') buffer.write(']');
-
- if (currentChar == '>') break; // end of CDATA section, change state to operand
-
- buffer.write(']');
- }
-
- buffer.write(']');
- buffer.write(currentChar);
- continue; // not end of CDATA section, don't change state
-
- case processCharRef:
- // process character entity
-
- int crefState = 0;
-
- currentChar = buffer.read();
-
- while (true) {
- if ("#amp;&pos;'quot;\"gt;>lt;<".charAt(crefState) == currentChar) {
- crefState++;
-
- if (currentChar == ';') {
- buffer.write("#amp;&pos;'quot;\"gt;>lt;<".charAt(crefState));
- break;
-
- } else if (currentChar == '#') {
- final int radix;
-
- currentChar = buffer.read();
-
- if (currentChar == 'x') {
- radix = 16;
- currentChar = buffer.read();
- } else {
- radix = 10;
- }
-
- int charRef = Character.digit((char)currentChar, radix);
-
- while (true) {
- currentChar = buffer.read();
-
- final int digit = Character.digit((char)currentChar, radix);
-
- if (digit == -1) break;
-
- charRef = (char)((charRef * radix) + digit);
- }
-
- if (currentChar == ';' && charRef != -1) {
- buffer.write(charRef);
- break;
- }
-
- fatalError("invalid Character Entitiy", this.lineNumber, this.columnNumber);
- } else {
- currentChar = buffer.read();
- }
- } else {
- crefState = ("\u0001\u000b\u0006\u00ff\u00ff\u00ff\u00ff\u00ff\u00ff\u00ff\u00ff" +
- // # a m p ; & p o s ; '
- // 0 1 2 3 4 5 6 7 8 9 a
- "\u0011\u00ff\u00ff\u00ff\u00ff\u00ff\u0015\u00ff\u00ff\u00ff" +
- // q u o t ; " g t ; >
- // b b d e f 10 11 12 13 14
- "\u00ff\u00ff\u00ff").charAt(crefState);
- // l t ;
- // 15 16 17
-
- if (crefState == 255) fatalError("invalid Character Entitiy", this.lineNumber, this.columnNumber);
- }
- }
-
- break;
-
- case parseError:
- // report fatal error
-
- fatalError(operand, this.lineNumber, this.columnNumber);
- // drop through to exit parser
-
- case exitParser:
- // exit parser
-
- return;
-
- case writeCdata:
- // write character data
- // this will also write any skipped whitespace
-
- buffer.write(currentChar);
- break; // change state to operand
-
- case writeAsWS:
- buffer.write((currentChar == '\n' ? currentChar : (char)32));
- break;
-
- case discardAndChange:
- // throw saved characters away and change state
-
- buffer.reset();
- break; // change state to operand
-
- case discardSaveAndChange:
- // throw saved characters away, save character and change state
-
- buffer.reset();
- // drop through to save character and change state
-
- case saveAndChange:
- // save character and change state
-
- buffer.saveChar((char)currentChar);
- break; // change state to operand
-
- case change:
- // change state to operand
-
- break; // change state to operand
- }
-
- state = operand;
- }
- }
- catch (final IOException e) {
- this.errorHandler.fatalError(new SAXParseException(e.toString(), null, null, this.lineNumber, this.columnNumber, e));
- }
- finally {
- this.errorHandler = this;
- this.documentHandler = this.extDocumentHandler = this;
- this.tags.removeAllElements();
- }
- }
-
- public void parse(final InputSource source) throws SAXException, IOException {
- if (source.getCharacterStream() != null)
- parse(source.getCharacterStream());
- else if (source.getByteStream() != null)
- parse(new InputStreamReader(source.getByteStream()));
- else
- parse(new InputStreamReader(new URL(source.getSystemId()).openStream()));
- }
-
- public void parse(final String systemId) throws SAXException, IOException {
- parse(new InputSource(systemId));
- }
-
- public void setLocale(final Locale locale) throws SAXException {
- throw new SAXException("Not supported");
- }
-
- public void setEntityResolver(final EntityResolver resolver) {
- // not supported
- }
-
- public void setDTDHandler(final DTDHandler handler) {
- // not supported
- }
-
- public void setDocumentHandler(final DocumentHandler handler) {
- this.documentHandler = (handler == null) ? this : handler;
- if (handler != null) handler.setDocumentLocator(this);
- this.extDocumentHandler = this;
- }
-
- public void setErrorHandler(final ErrorHandler handler) {
- this.errorHandler = (handler == null) ? this : handler;
- }
-
- public void setDocumentLocator(final Locator locator) {
- }
-
- public void startDocument() throws SAXException {
- }
-
- public Writer startDocument(final Writer writer) throws SAXException {
- this.documentHandler.startDocument();
- return writer;
- }
-
- public void endDocument() throws SAXException {
- }
-
- public void startElement(final String name, final AttributeList attributes) throws SAXException {
- }
-
- public Writer startElement(final String name, final AttributeList attributes, final Writer writer)
- throws SAXException
- {
- this.documentHandler.startElement(name, attributes);
- return writer;
- }
-
- public void endElement(final String name) throws SAXException {
- }
-
- public void characters(final char ch[], final int start, final int length) throws SAXException {
- }
-
- public void ignorableWhitespace(final char ch[], final int start, final int length) throws SAXException {
- }
-
- public void processingInstruction(final String target, final String data) throws SAXException {
- }
-
- public void warning(final SAXParseException e) throws SAXException {
- }
-
- public void error(final SAXParseException e) throws SAXException {
- }
-
- public void fatalError(final SAXParseException e) throws SAXException {
- throw e;
- }
-
- public String getPublicId() {
- return "";
- }
-
-
- public String getSystemId() {
- return "";
- }
-
- public int getLineNumber () {
- return this.lineNumber;
- }
-
- public int getColumnNumber () {
- return this.columnNumber;
- }
-
- private void fatalError(final String msg, final int lineNumber, final int columnNumber) throws SAXException {
- this.errorHandler.fatalError(new SAXParseException(msg, null, null, lineNumber, columnNumber));
- }
-
- private class MinMLBuffer extends Writer {
- public MinMLBuffer(final Reader in) {
- this.in = in;
- }
-
- public void close() throws IOException {
- flush();
- }
-
- public void flush() throws IOException {
- try {
- _flush();
- if (writer != this) writer.flush();
- }
- finally {
- flushed = true;
- }
- }
-
- public void write(final int c) throws IOException {
- written = true;
- chars[count++] = (char)c;
- }
-
- public void write(final char[] cbuf, final int off, final int len) throws IOException {
- written = true;
- System.arraycopy(cbuf, off, chars, count, len);
- count += len;
- }
-
- public void saveChar(final char c) {
- written = false;
- chars[count++] = c;
- }
-
- public void pushWriter(final Writer writer) {
- MinML.this.tags.push(this.writer);
-
- this.writer = (writer == null) ? this : writer;
-
- flushed = written = false;
- }
-
- public Writer getWriter() {
- return writer;
- }
-
- public void popWriter() throws IOException {
- try {
- if (!flushed && writer != this) writer.flush();
- }
- finally {
- writer = (Writer)MinML.this.tags.pop();
- flushed = written = false;
- }
- }
-
- public String getString() {
- final String result = new String(chars, 0, count);
-
- count = 0;
- return result;
- }
-
- public void reset() {
- count = 0;
- }
-
- public int read() throws IOException {
- if (nextIn == lastIn) {
- if (count != 0) {
- if (written) {
- _flush();
- } else if (count >= (chars.length - MinML.this.bufferIncrement)) {
- final char[] newChars = new char[chars.length + MinML.this.bufferIncrement];
-
- System.arraycopy(chars, 0, newChars, 0, count);
- chars = newChars;
- }
- }
-
- final int numRead = in.read(chars, count, chars.length - count);
-
- if (numRead == -1) return -1;
-
- nextIn = count;
- lastIn = count + numRead;
- }
-
- return chars[nextIn++];
- }
-
- private void _flush() throws IOException {
- if (count != 0) {
- try {
- if (writer == this) {
- try {
- MinML.this.documentHandler.characters(chars, 0, count);
- }
- catch (final SAXException e) {
- throw new IOException(e.toString());
- }
- } else {
- writer.write(chars, 0, count);
- }
- }
- finally {
- count = 0;
- }
- }
- }
-
- private int nextIn = 0, lastIn = 0;
- private char[] chars = new char[MinML.this.initialBufferSize];
- private final Reader in;
- private int count = 0;
- private Writer writer = this;
- private boolean flushed = false;
- private boolean written = false;
- }
-
- private DocumentHandler extDocumentHandler = this;
- private DocumentHandler documentHandler = this;
- private ErrorHandler errorHandler = this;
- private final Stack tags = new Stack();
- private int lineNumber = 1;
- private int columnNumber = 0;
- private final int initialBufferSize;
- private final int bufferIncrement;
-
- private static final byte[] charClasses = {
- // EOF
- 13,
- // \t \n \r
- -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 12, -1, -1, 12, -1, -1,
- //
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- // SP ! " # $ % & ' ( ) * + , - . /
- 12, 8, 7, 14, 14, 14, 3, 6, 14, 14, 14, 14, 14, 11, 14, 2,
- // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 5, 1, 4,
- //
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- // [ \ ]
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 9, 14, 10
- };
-
- private static final String[] operands = {
- "\u0d15\u1611\u1611\u1611\u1611\u1611\u1611\u1611\u1611\u1611\u1611\u1611\u0015\u0010\u1611",
- "\u1711\u1000\u0b00\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u0114\u0200\u1811\u0114",
- "\u1711\u1001\u0b01\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u0215\u1811\u0414",
- "\u1711\u1001\u0b01\u1711\u1911\u1911\u1911\u1911\u1911\u1911\u1911\u1911\u0315\u1811\u0414",
- "\u1911\u1911\u1911\u1911\u1911\u0606\u1911\u1911\u1911\u1911\u1911\u0414\u0515\u1811\u0414",
- "\u1911\u1911\u1911\u1911\u1911\u0606\u1911\u1911\u1911\u1911\u1911\u1911\u0515\u1811\u1911",
- "\u1a11\u1a11\u1a11\u1a11\u1a11\u1a11\u0715\u0815\u1a11\u1a11\u1a11\u1a11\u0615\u1811\u1a11",
- "\u0714\u0714\u0714\u070e\u0714\u0714\u0307\u0714\u0714\u0714\u0714\u0714\u0714\u1811\u0714",
- "\u0814\u0814\u0814\u080e\u0814\u0814\u0814\u0307\u0814\u0814\u0814\u0814\u0814\u1811\u0814",
- "\u1711\u1002\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u0914\u0915\u1811\u0914",
- "\u1b11\u1b11\u0904\u1b11\u1b11\u1b11\u1b11\u1b11\u1215\u1b11\u1b11\u1b11\u1b11\u1811\u0105",
- "\u1711\u1012\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1811\u1711",
- "\u1711\u1c11\u0912\u1711\u0e12\u1711\u1711\u1711\u1212\u1711\u1711\u1711\u1711\u1811\u0113",
- "\u1711\u1c11\u0912\u1711\u0e12\u1711\u1711\u1711\u1212\u1711\u1711\u1711\u1711\u1811\u0113",
- "\u0e15\u0e15\u0e15\u0e15\u0f15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u1811\u0e15",
- "\u0e15\u0015\u0e15\u0e15\u0f15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u1811\u0e15",
- "\u0c03\u110f\u110f\u110e\u110f\u110f\u110f\u110f\u110f\u110f\u110f\u110f\u1014\u1811\u110f",
- "\u0a15\u110f\u110f\u110e\u110f\u110f\u110f\u110f\u110f\u110f\u110f\u110f\u110f\u1811\u110f",
- "\u1d11\u1d11\u1d11\u1d11\u1d11\u1d11\u1d11\u1d11\u1d11\u130c\u1d11\u1408\u1d11\u1811\u1515",
- "\u130f\u130f\u130f\u130f\u130f\u130f\u130f\u130f\u130f\u130f\u110d\u130f\u130f\u1811\u130f",
- "\u1416\u1416\u1416\u1416\u1416\u1416\u1416\u1416\u1416\u1416\u1416\u0009\u1416\u1811\u1416",
-// "\u1415\u1415\u1415\u1415\u1415\u1415\u1415\u1415\u1415\u1415\u1415\u0009\u1415\u1811\u1415",
- "\u150a\u000b\u1515\u1515\u1515\u1515\u1515\u1515\u1515\u1515\u1515\u1515\u1515\u1811\u1515",
- "expected Element",
- "unexpected character in tag",
- "unexpected end of file found",
- "attribute name not followed by '='",
- "invalid attribute value",
- "expecting end tag",
- "empty tag",
- "unexpected character after <!"
- };
- }
- ///////////////////////////////////////////////////////////////////////////////
-
- private class XMLHelper implements DocumentHandler {
- private MinML minml = new MinML();
- XMLHelper() { }
- public void parse(Reader r) throws IOException, XML.SAXException {
- minml.setDocumentHandler(this);
- minml.parse(new InputSource(r));
- }
-
- public void startDocument() throws SAXException { }
- public void endDocument() throws SAXException { }
- public void processingInstruction (String target, String data) throws SAXException { }
- public Writer startDocument(final Writer writer) throws SAXException { return null; }
- public Writer startElement(final String name, final AttributeList attributes, final Writer writer) throws SAXException { return null; }
-
- public void setDocumentLocator (Locator locator) {
- this.locator = locator;
- }
-
- private Locator locator = null;
-
- public void startElement(String name, AttributeList atts) throws SAXException {
- String[] keys = new String[atts.getLength()];
- Object[] vals = new Object[atts.getLength()];
- for (int i=0; i <atts.getLength(); i++) {
- keys[i] = atts.getName(i);
- vals[i] = atts.getValue(i).toString();
- }
- XML.this.startElement(name, keys, vals,
- locator == null ? 0 : locator.getLineNumber(), locator == null ? 0 : locator.getColumnNumber());
- }
-
- public void endElement(String name) throws SAXException {
- XML.this.endElement(name, locator == null ? 0 : locator.getLineNumber(), locator == null ? 0 : locator.getColumnNumber());
- }
-
- public void characters(char ch[], int start, int length) throws SAXException {
- XML.this.content(ch, start, length, locator == null ? 0 : locator.getLineNumber(), locator == null ? 0 : locator.getColumnNumber());
- }
- public void ignorableWhitespace(char ch[], int start, int length) throws SAXException {
- XML.this.content(ch, start, length, locator == null ? 0 : locator.getLineNumber(), locator == null ? 0 : locator.getColumnNumber());
- }
- }
-
- public XML() { }
- public void parse(Reader r) throws IOException, XML.SAXException {
- XMLHelper helper = new XMLHelper();
- helper.parse(r);
- helper = null;
- }
-
- /** indicates the start of an element with name <tt>name</tt>, and attributes <tt>attributes</tt>, starting on line <tt>line</tt> */
- public abstract void startElement(String name, String[] keys, Object[] vals, int line, int col) throws SAXException;
-
- /** indicates the end of an element with name <tt>name</tt>, starting on line <tt>line</tt> */
- public abstract void endElement(String name, int line, int col) throws SAXException;
-
- /** indicates a chunk of CDATA content, starting on line <tt>line</tt> */
- public abstract void content(char[] content, int start, int length, int line, int col) throws SAXException;
-}