package gnu.xml.aelfred2;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale;
import java.util.Stack;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import org.xml.sax.*;
import org.xml.sax.ext.*;
import org.xml.sax.helpers.NamespaceSupport;
final public class SAXDriver
implements Locator, Attributes2, XMLReader, Parser, AttributeList
{
private final DefaultHandler2 base = new DefaultHandler2();
private XmlParser parser;
private EntityResolver entityResolver = base;
private EntityResolver2 resolver2 = null;
private ContentHandler contentHandler = base;
private DTDHandler dtdHandler = base;
private ErrorHandler errorHandler = base;
private DeclHandler declHandler = base;
private LexicalHandler lexicalHandler = base;
private String elementName;
private Stack entityStack;
private List attributesList;
private boolean namespaces = true;
private boolean xmlNames = false;
private boolean extGE = true;
private boolean extPE = true;
private boolean resolveAll = true;
private boolean useResolver2 = true;
boolean stringInterning = true;
private int attributeCount;
private boolean attributes;
private String[] nsTemp;
private NamespaceSupport prefixStack;
public SAXDriver()
{
reset();
}
private void reset()
{
elementName = null;
entityStack = new Stack();
attributesList = Collections.synchronizedList(new ArrayList());
attributeCount = 0;
attributes = false;
nsTemp = new String[3];
prefixStack = null;
}
public void setLocale(Locale locale)
throws SAXException
{
if ("en".equals(locale.getLanguage()))
{
return;
}
throw new SAXException ("AElfred2 only supports English locales.");
}
public EntityResolver getEntityResolver()
{
return (entityResolver == base) ? null : entityResolver;
}
public void setEntityResolver(EntityResolver resolver)
{
if (resolver instanceof EntityResolver2)
{
resolver2 = (EntityResolver2) resolver;
}
else
{
resolver2 = null;
}
if (resolver == null)
{
resolver = base;
}
entityResolver = resolver;
}
public DTDHandler getDTDHandler()
{
return (dtdHandler == base) ? null : dtdHandler;
}
public void setDTDHandler(DTDHandler handler)
{
if (handler == null)
{
handler = base;
}
this.dtdHandler = handler;
}
public void setDocumentHandler(DocumentHandler handler)
{
contentHandler = new Adapter(handler);
xmlNames = true;
}
public ContentHandler getContentHandler()
{
return (contentHandler == base) ? null : contentHandler;
}
public void setContentHandler(ContentHandler handler)
{
if (handler == null)
{
handler = base;
}
contentHandler = handler;
}
public void setErrorHandler(ErrorHandler handler)
{
if (handler == null)
{
handler = base;
}
this.errorHandler = handler;
}
public ErrorHandler getErrorHandler()
{
return (errorHandler == base) ? null : errorHandler;
}
public void parse(InputSource source)
throws SAXException, IOException
{
synchronized (base)
{
parser = new XmlParser();
if (namespaces)
{
prefixStack = new NamespaceSupport();
}
else if (!xmlNames)
{
throw new IllegalStateException();
}
parser.setHandler(this);
try
{
Reader r = source.getCharacterStream();
InputStream in = source.getByteStream();
parser.doParse(source.getSystemId(),
source.getPublicId(),
r,
in,
source.getEncoding());
}
catch (SAXException e)
{
throw e;
}
catch (IOException e)
{
throw e;
}
catch (RuntimeException e)
{
throw e;
}
catch (Exception e)
{
throw new SAXParseException(e.getMessage(), this, e);
}
finally
{
contentHandler.endDocument();
reset();
}
}
}
public void parse(String systemId)
throws SAXException, IOException
{
parse(new InputSource(systemId));
}
static final String FEATURE = "http://xml.org/sax/features/";
static final String PROPERTY = "http://xml.org/sax/properties/";
public boolean getFeature(String featureId)
throws SAXNotRecognizedException, SAXNotSupportedException
{
if ((FEATURE + "validation").equals(featureId))
{
return false;
}
if ((FEATURE + "external-general-entities").equals(featureId))
{
return extGE;
}
if ((FEATURE + "external-parameter-entities").equals(featureId))
{
return extPE;
}
if ((FEATURE + "namespace-prefixes").equals(featureId))
{
return xmlNames;
}
if ((FEATURE + "namespaces").equals(featureId))
{
return namespaces;
}
if ((FEATURE + "lexical-handler/parameter-entities").equals(featureId))
{
return true;
}
if ((FEATURE + "string-interning").equals(featureId))
{
return stringInterning;
}
if ((FEATURE + "use-attributes2").equals(featureId))
{
return true;
}
if ((FEATURE + "is-standalone").equals(featureId))
{
if (parser == null)
{
throw new SAXNotSupportedException(featureId);
}
return parser.isStandalone();
}
if ((FEATURE + "resolve-dtd-uris").equals(featureId))
{
return resolveAll;
}
if ((FEATURE + "use-entity-resolver2").equals(featureId))
{
return useResolver2;
}
throw new SAXNotRecognizedException(featureId);
}
DeclHandler getDeclHandler()
{
return declHandler;
}
boolean resolveURIs()
{
return resolveAll;
}
public Object getProperty(String propertyId)
throws SAXNotRecognizedException
{
if ((PROPERTY + "declaration-handler").equals(propertyId))
{
return (declHandler == base) ? null : declHandler;
}
if ((PROPERTY + "lexical-handler").equals(propertyId))
{
return (lexicalHandler == base) ? null : lexicalHandler;
}
throw new SAXNotRecognizedException(propertyId);
}
public void setFeature(String featureId, boolean value)
throws SAXNotRecognizedException, SAXNotSupportedException
{
boolean state;
state = getFeature (featureId);
if (state == value)
{
return;
}
if (parser != null)
{
throw new SAXNotSupportedException("not while parsing");
}
if ((FEATURE + "namespace-prefixes").equals(featureId))
{
xmlNames = value;
if (!xmlNames)
{
namespaces = true;
}
return;
}
if ((FEATURE + "namespaces").equals(featureId))
{
namespaces = value;
if (!namespaces)
{
xmlNames = true;
}
return;
}
if ((FEATURE + "external-general-entities").equals(featureId))
{
extGE = value;
return;
}
if ((FEATURE + "external-parameter-entities").equals(featureId))
{
extPE = value;
return;
}
if ((FEATURE + "resolve-dtd-uris").equals(featureId))
{
resolveAll = value;
return;
}
if ((FEATURE + "use-entity-resolver2").equals(featureId))
{
useResolver2 = value;
return;
}
throw new SAXNotRecognizedException(featureId);
}
public void setProperty(String propertyId, Object value)
throws SAXNotRecognizedException, SAXNotSupportedException
{
getProperty(propertyId);
if ((PROPERTY + "declaration-handler").equals(propertyId))
{
if (value == null)
{
declHandler = base;
}
else if (!(value instanceof DeclHandler))
{
throw new SAXNotSupportedException(propertyId);
}
else
{
declHandler = (DeclHandler) value;
}
return ;
}
if ((PROPERTY + "lexical-handler").equals(propertyId))
{
if (value == null)
{
lexicalHandler = base;
}
else if (!(value instanceof LexicalHandler))
{
throw new SAXNotSupportedException(propertyId);
}
else
{
lexicalHandler = (LexicalHandler) value;
}
return;
}
throw new SAXNotSupportedException(propertyId);
}
void startDocument()
throws SAXException
{
contentHandler.setDocumentLocator(this);
contentHandler.startDocument();
attributesList.clear();
}
void xmlDecl(String version,
String encoding,
boolean standalone,
String inputEncoding)
throws SAXException
{
if (contentHandler instanceof ContentHandler2)
{
((ContentHandler2) contentHandler).xmlDecl(version,
encoding,
standalone,
inputEncoding);
}
}
void skippedEntity(String name)
throws SAXException
{
contentHandler.skippedEntity(name);
}
InputSource getExternalSubset(String name, String baseURI)
throws SAXException, IOException
{
if (resolver2 == null || !useResolver2 || !extPE)
{
return null;
}
return resolver2.getExternalSubset(name, baseURI);
}
InputSource resolveEntity(boolean isPE, String name,
InputSource in, String baseURI)
throws SAXException, IOException
{
InputSource source;
if (isPE && !extPE)
{
return null;
}
if (!isPE && !extGE)
{
return null;
}
lexicalHandler.startEntity(name);
if (resolver2 != null && useResolver2)
{
source = resolver2.resolveEntity(name, in.getPublicId(),
baseURI, in.getSystemId());
if (source == null)
{
in.setSystemId(absolutize(baseURI,
in.getSystemId(), false));
source = in;
}
}
else
{
in.setSystemId(absolutize(baseURI, in.getSystemId(), false));
source = entityResolver.resolveEntity(in.getPublicId(),
in.getSystemId());
if (source == null)
{
source = in;
}
}
startExternalEntity(name, source.getSystemId(), true);
return source;
}
String absolutize(String baseURI, String systemId, boolean nice)
throws MalformedURLException, SAXException
{
try
{
if (baseURI == null)
{
if (XmlParser.uriWarnings)
{
warn ("No base URI; hope this SYSTEM id is absolute: "
+ systemId);
}
return new URL(systemId).toString();
}
else
{
return new URL(new URL(baseURI), systemId).toString();
}
}
catch (MalformedURLException e)
{
if (!nice)
{
throw e;
}
warn("Can't absolutize SYSTEM id: " + e.getMessage());
return systemId;
}
}
void startExternalEntity(String name, String systemId, boolean stackOnly)
throws SAXException
{
if (!stackOnly) {
lexicalHandler.startEntity(name);
}
entityStack.push(systemId);
}
void endExternalEntity(String name)
throws SAXException
{
if (!"[document]".equals(name))
{
lexicalHandler.endEntity(name);
}
entityStack.pop();
}
void startInternalEntity(String name)
throws SAXException
{
lexicalHandler.startEntity(name);
}
void endInternalEntity(String name)
throws SAXException
{
lexicalHandler.endEntity(name);
}
void doctypeDecl(String name, String publicId, String systemId)
throws SAXException
{
lexicalHandler.startDTD(name, publicId, systemId);
}
void notationDecl(String name, String publicId, String systemId,
String baseUri)
throws SAXException
{
try
{
dtdHandler.notationDecl(name, publicId,
(resolveAll && systemId != null)
? absolutize(baseUri, systemId, true)
: systemId);
}
catch (IOException e)
{
throw new SAXParseException(e.getMessage(), this, e);
}
}
void unparsedEntityDecl(String name, String publicId, String systemId,
String baseUri, String notation)
throws SAXException
{
try
{
dtdHandler.unparsedEntityDecl(name, publicId,
resolveAll
? absolutize(baseUri, systemId, true)
: systemId,
notation);
}
catch (IOException e)
{
throw new SAXParseException(e.getMessage(), this, e);
}
}
void endDoctype()
throws SAXException
{
lexicalHandler.endDTD();
}
private void declarePrefix(String prefix, String uri)
throws SAXException
{
int index = uri.indexOf(':');
if (index < 1 && uri.length() != 0)
{
warn("relative URI for namespace: " + uri);
}
boolean prefixEquality = prefix.equals("xml");
boolean uriEquality = uri.equals("http://www.w3.org/XML/1998/namespace");
if ((prefixEquality || uriEquality) && !(prefixEquality && uriEquality))
{
fatal("xml is by definition bound to the namespace name " +
"http://www.w3.org/XML/1998/namespace");
}
if (prefixEquality && uriEquality)
{
return;
}
prefixEquality = prefix.equals("xmlns");
uriEquality = uri.equals("http://www.w3.org/2000/xmlns/");
if ((prefixEquality || uriEquality) && !(prefixEquality && uriEquality))
{
fatal("http://www.w3.org/2000/xmlns/ is by definition bound" +
" to prefix xmlns");
}
if (prefixEquality && uriEquality)
{
fatal ("declaring the xmlns prefix is illegal");
}
uri = uri.intern();
prefixStack.declarePrefix(prefix, uri);
contentHandler.startPrefixMapping(prefix, uri);
}
void attribute(String qname, String value, boolean isSpecified)
throws SAXException
{
if (!attributes)
{
attributes = true;
if (namespaces)
{
prefixStack.pushContext();
}
}
if (namespaces)
{
int index;
if (stringInterning)
{
if ("xmlns" == qname)
{
declarePrefix("", value);
if (!xmlNames)
{
return;
}
}
else if ((index = qname.indexOf(':')) == 5
&& qname.startsWith("xmlns"))
{
String prefix = qname.substring(6);
if (prefix.equals(""))
{
fatal("missing prefix " +
"in namespace declaration attribute");
}
if (value.length() == 0)
{
verror("missing URI in namespace declaration attribute: "
+ qname);
}
else
{
declarePrefix(prefix, value);
}
if (!xmlNames)
{
return;
}
}
}
else
{
if ("xmlns".equals(qname))
{
declarePrefix("", value);
if (!xmlNames)
{
return;
}
}
else if ((index = qname.indexOf(':')) == 5
&& qname.startsWith("xmlns"))
{
String prefix = qname.substring(6);
if (value.length() == 0)
{
verror("missing URI in namespace decl attribute: "
+ qname);
}
else
{
declarePrefix(prefix, value);
}
if (!xmlNames)
{
return;
}
}
}
}
attributeCount++;
attributesList.add(new Attribute(qname, value, isSpecified));
}
void startElement(String elname)
throws SAXException
{
ContentHandler handler = contentHandler;
if (!attributes)
{
if (namespaces)
{
prefixStack.pushContext();
}
}
else if (namespaces)
{
Iterator itt = attributesList.iterator();
while (itt.hasNext())
{
Attribute attribute = (Attribute) itt.next();
String qname = attribute.name;
int index;
if (stringInterning)
{
if ("xmlns" == qname)
{
continue;
}
}
else
{
if ("xmlns".equals(qname))
{
continue;
}
}
if (qname.equals (":"))
{
fatal("namespace names consisting of a single colon " +
"character are invalid");
}
index = qname.indexOf(':');
if (index == 5 && qname.startsWith("xmlns"))
{
continue;
}
if (prefixStack.processName(qname, nsTemp, true) == null)
{
fatal("undeclared attribute prefix in: " + qname);
}
else
{
attribute.nameSpace = nsTemp[0];
attribute.localName = nsTemp[1];
}
}
}
elementName = elname;
if (namespaces)
{
if (prefixStack.processName(elname, nsTemp, false) == null)
{
fatal("undeclared element prefix in: " + elname);
nsTemp[0] = nsTemp[1] = "";
}
handler.startElement(nsTemp[0], nsTemp[1], elname, this);
}
else
{
handler.startElement("", "", elname, this);
}
if (attributes)
{
attributesList.clear();
attributeCount = 0;
attributes = false;
}
}
void endElement(String elname)
throws SAXException
{
ContentHandler handler = contentHandler;
if (!namespaces)
{
handler.endElement("", "", elname);
return;
}
prefixStack.processName(elname, nsTemp, false);
handler.endElement(nsTemp[0], nsTemp[1], elname);
Enumeration prefixes = prefixStack.getDeclaredPrefixes();
while (prefixes.hasMoreElements())
{
handler.endPrefixMapping((String) prefixes.nextElement());
}
prefixStack.popContext();
}
void startCDATA()
throws SAXException
{
lexicalHandler.startCDATA();
}
void charData(char[] ch, int start, int length)
throws SAXException
{
contentHandler.characters(ch, start, length);
}
void endCDATA()
throws SAXException
{
lexicalHandler.endCDATA();
}
void ignorableWhitespace(char[] ch, int start, int length)
throws SAXException
{
contentHandler.ignorableWhitespace(ch, start, length);
}
void processingInstruction(String target, String data)
throws SAXException
{
contentHandler.processingInstruction(target, data);
}
void comment(char[] ch, int start, int length)
throws SAXException
{
if (lexicalHandler != base)
{
lexicalHandler.comment(ch, start, length);
}
}
void fatal(String message)
throws SAXException
{
SAXParseException fatal;
fatal = new SAXParseException(message, this);
errorHandler.fatalError(fatal);
throw fatal;
}
void verror(String message)
throws SAXException
{
SAXParseException err;
err = new SAXParseException(message, this);
errorHandler.error(err);
}
void warn(String message)
throws SAXException
{
SAXParseException err;
err = new SAXParseException(message, this);
errorHandler.warning(err);
}
public int getLength()
{
return attributesList.size();
}
public String getURI(int index)
{
if (index < 0 || index >= attributesList.size())
{
return null;
}
return ((Attribute) attributesList.get(index)).nameSpace;
}
public String getLocalName(int index)
{
if (index < 0 || index >= attributesList.size())
{
return null;
}
Attribute attr = (Attribute) attributesList.get(index);
if (namespaces && attr.localName == null)
{
int ci = attr.name.indexOf(':');
attr.localName = (ci == -1) ? attr.name :
attr.name.substring(ci + 1);
}
return (attr.localName == null) ? "" : attr.localName;
}
public String getQName(int index)
{
if (index < 0 || index >= attributesList.size())
{
return null;
}
Attribute attr = (Attribute) attributesList.get(index);
return (attr.name == null) ? "" : attr.name;
}
public String getName(int index)
{
return getQName(index);
}
public String getType(int index)
{
if (index < 0 || index >= attributesList.size())
{
return null;
}
String type = parser.getAttributeType(elementName, getQName(index));
if (type == null)
{
return "CDATA";
}
if (type == "ENUMERATION")
{
return "NMTOKEN";
}
return type;
}
public String getValue(int index)
{
if (index < 0 || index >= attributesList.size())
{
return null;
}
return ((Attribute) attributesList.get(index)).value;
}
public int getIndex(String uri, String local)
{
int length = getLength();
for (int i = 0; i < length; i++)
{
if (!getURI(i).equals(uri))
{
continue;
}
if (getLocalName(i).equals(local))
{
return i;
}
}
return -1;
}
public int getIndex(String xmlName)
{
int length = getLength();
for (int i = 0; i < length; i++)
{
if (getQName(i).equals(xmlName))
{
return i;
}
}
return -1;
}
public String getType(String uri, String local)
{
int index = getIndex(uri, local);
if (index < 0)
{
return null;
}
return getType(index);
}
public String getType(String xmlName)
{
int index = getIndex(xmlName);
if (index < 0)
{
return null;
}
return getType(index);
}
public String getValue(String uri, String local)
{
int index = getIndex(uri, local);
if (index < 0)
{
return null;
}
return getValue(index);
}
public String getValue(String xmlName)
{
int index = getIndex(xmlName);
if (index < 0)
{
return null;
}
return getValue(index);
}
public boolean isDeclared(int index)
{
if (index < 0 || index >= attributeCount)
{
throw new ArrayIndexOutOfBoundsException();
}
String type = parser.getAttributeType(elementName, getQName(index));
return (type != null);
}
public boolean isDeclared(String qName)
{
int index = getIndex(qName);
if (index < 0)
{
throw new IllegalArgumentException();
}
String type = parser.getAttributeType(elementName, qName);
return (type != null);
}
public boolean isDeclared(String uri, String localName)
{
int index = getIndex(uri, localName);
return isDeclared(index);
}
public boolean isSpecified(int index)
{
return ((Attribute) attributesList.get(index)).specified;
}
public boolean isSpecified(String uri, String local)
{
int index = getIndex (uri, local);
return isSpecified(index);
}
public boolean isSpecified(String xmlName)
{
int index = getIndex (xmlName);
return isSpecified(index);
}
public String getPublicId()
{
return null; }
public String getSystemId()
{
if (entityStack.empty())
{
return null;
}
else
{
return (String) entityStack.peek();
}
}
public int getLineNumber()
{
return parser.getLineNumber();
}
public int getColumnNumber()
{
return parser.getColumnNumber();
}
private static class Adapter
implements ContentHandler
{
private DocumentHandler docHandler;
Adapter(DocumentHandler dh)
{
docHandler = dh;
}
public void setDocumentLocator(Locator l)
{
docHandler.setDocumentLocator(l);
}
public void startDocument()
throws SAXException
{
docHandler.startDocument();
}
public void processingInstruction(String target, String data)
throws SAXException
{
docHandler.processingInstruction(target, data);
}
public void startPrefixMapping(String prefix, String uri)
{
}
public void startElement(String namespace,
String local,
String name,
Attributes attrs)
throws SAXException
{
docHandler.startElement(name, (AttributeList) attrs);
}
public void characters(char[] buf, int offset, int len)
throws SAXException
{
docHandler.characters(buf, offset, len);
}
public void ignorableWhitespace(char[] buf, int offset, int len)
throws SAXException
{
docHandler.ignorableWhitespace(buf, offset, len);
}
public void skippedEntity(String name)
{
}
public void endElement(String u, String l, String name)
throws SAXException
{
docHandler.endElement(name);
}
public void endPrefixMapping(String prefix)
{
}
public void endDocument()
throws SAXException
{
docHandler.endDocument();
}
}
private static class Attribute
{
String name;
String value;
String nameSpace;
String localName;
boolean specified;
Attribute(String name, String value, boolean specified)
{
this.name = name;
this.value = value;
this.nameSpace = "";
this.specified = specified;
}
}
}