WellFormednessFilter.java [plain text]
package gnu.xml.pipeline;
import java.util.EmptyStackException;
import java.util.Stack;
import org.xml.sax.Attributes;
import org.xml.sax.ErrorHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
public final class WellFormednessFilter extends EventFilter
{
private boolean startedDoc;
private Stack elementStack = new Stack ();
private boolean startedCDATA;
private String dtdState = "before";
public WellFormednessFilter ()
{ this (null); }
public WellFormednessFilter (EventConsumer consumer)
{
super (consumer);
setContentHandler (this);
setDTDHandler (this);
try {
setProperty (LEXICAL_HANDLER, this);
} catch (SAXException e) { }
}
public void reset ()
{
startedDoc = false;
startedCDATA = false;
elementStack.removeAllElements ();
}
private SAXParseException getException (String message)
{
SAXParseException e;
Locator locator = getDocumentLocator ();
if (locator == null)
return new SAXParseException (message, null, null, -1, -1);
else
return new SAXParseException (message, locator);
}
private void fatalError (String message)
throws SAXException
{
SAXParseException e = getException (message);
ErrorHandler handler = getErrorHandler ();
if (handler != null)
handler.fatalError (e);
throw e;
}
public void setDocumentLocator (Locator locator)
{
if (startedDoc)
throw new IllegalStateException (
"setDocumentLocator called after startDocument");
super.setDocumentLocator (locator);
}
public void startDocument () throws SAXException
{
if (startedDoc)
fatalError ("startDocument called more than once");
startedDoc = true;
startedCDATA = false;
elementStack.removeAllElements ();
super.startDocument ();
}
public void startElement (
String uri, String localName,
String qName, Attributes atts
) throws SAXException
{
if (!startedDoc)
fatalError ("callback outside of document?");
if ("inside".equals (dtdState))
fatalError ("element inside DTD?");
else
dtdState = "after";
if (startedCDATA)
fatalError ("element inside CDATA section");
if (qName == null || "".equals (qName))
fatalError ("startElement name missing");
elementStack.push (qName);
super.startElement (uri, localName, qName, atts);
}
public void endElement (String uri, String localName, String qName)
throws SAXException
{
if (!startedDoc)
fatalError ("callback outside of document?");
if (startedCDATA)
fatalError ("element inside CDATA section");
if (qName == null || "".equals (qName))
fatalError ("endElement name missing");
try {
String top = (String) elementStack.pop ();
if (!qName.equals (top))
fatalError ("<" + top + " ...>...</" + qName + ">");
} catch (EmptyStackException e) {
fatalError ("endElement without startElement: </" + qName + ">");
}
super.endElement (uri, localName, qName);
}
public void endDocument () throws SAXException
{
if (!startedDoc)
fatalError ("callback outside of document?");
dtdState = "before";
startedDoc = false;
super.endDocument ();
}
public void startDTD (String root, String publicId, String systemId)
throws SAXException
{
if (!startedDoc)
fatalError ("callback outside of document?");
if ("before" != dtdState)
fatalError ("two DTDs?");
if (!elementStack.empty ())
fatalError ("DTD must precede root element");
dtdState = "inside";
super.startDTD (root, publicId, systemId);
}
public void notationDecl (String name, String publicId, String systemId)
throws SAXException
{
if ("after" == dtdState)
fatalError ("not inside DTD");
super.notationDecl (name, publicId, systemId);
}
public void unparsedEntityDecl (String name,
String publicId, String systemId, String notationName)
throws SAXException
{
if ("after" == dtdState)
fatalError ("not inside DTD");
super.unparsedEntityDecl (name, publicId, systemId, notationName);
}
public void endDTD ()
throws SAXException
{
if (!startedDoc)
fatalError ("callback outside of document?");
if ("inside" != dtdState)
fatalError ("DTD ends without start?");
dtdState = "after";
super.endDTD ();
}
public void characters (char ch [], int start, int length)
throws SAXException
{
int here = start, end = start + length;
if (elementStack.empty ())
fatalError ("characters must be in an element");
while (here < end) {
if (ch [here++] != ']')
continue;
if (here == end) continue;
if (ch [here++] != ']')
continue;
if (here == end) continue;
if (ch [here++] == '>')
fatalError ("character data can't contain \"]]>\"");
}
super.characters (ch, start, length);
}
public void ignorableWhitespace (char ch [], int start, int length)
throws SAXException
{
int here = start, end = start + length;
if (elementStack.empty ())
fatalError ("characters must be in an element");
while (here < end) {
if (ch [here++] == '\r')
fatalError ("whitespace can't contain CR");
}
super.ignorableWhitespace (ch, start, length);
}
public void processingInstruction (String target, String data)
throws SAXException
{
if (data.indexOf ('\r') > 0)
fatalError ("PIs can't contain CR");
if (data.indexOf ("?>") > 0)
fatalError ("PIs can't contain \"?>\"");
}
public void comment (char ch [], int start, int length)
throws SAXException
{
if (!startedDoc)
fatalError ("callback outside of document?");
if (startedCDATA)
fatalError ("comments can't nest in CDATA");
int here = start, end = start + length;
while (here < end) {
if (ch [here] == '\r')
fatalError ("comments can't contain CR");
if (ch [here++] != '-')
continue;
if (here == end)
fatalError ("comments can't end with \"--->\"");
if (ch [here++] == '-')
fatalError ("comments can't contain \"--\"");
}
super.comment (ch, start, length);
}
public void startCDATA ()
throws SAXException
{
if (!startedDoc)
fatalError ("callback outside of document?");
if (startedCDATA)
fatalError ("CDATA starts can't nest");
startedCDATA = true;
super.startCDATA ();
}
public void endCDATA ()
throws SAXException
{
if (!startedDoc)
fatalError ("callback outside of document?");
if (!startedCDATA)
fatalError ("CDATA end without start?");
startedCDATA = false;
super.endCDATA ();
}
}