package org.jdom.input; import java.io.*; import java.lang.reflect.*; import java.net.*; import java.util.*; import org.jdom.*; import org.xml.sax.*; import org.xml.sax.ext.LexicalHandler; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; /** *
SAXBuilder
builds a JDOM tree using SAX.
Known issues: Relative paths for a DocType or EntityRef may be * converted by the SAX parser into absolute paths
* * @author Brett McLaughlin * @author Jason Hunter * @author Dan Schaffer * @author Philip Nelson * @version 1.0 */ public class SAXBuilder { private static final String CVS_ID = "@(#) $RCSfile: SAXBuilder.java,v $ $Revision: 1.46 $ $Date: 2001/05/09 07:23:24 $ $Name: $"; /** * Default parser class to use. This is used when no other parser * is given and JAXP isn't available. */ private static final String DEFAULT_SAX_DRIVER = "org.apache.xerces.parsers.SAXParser"; /** Whether validation should occur */ private boolean validate; /** Whether expansion of entities should occur */ private boolean expand = true; /** Adapter class to use */ private String saxDriverClass; /** ErrorHandler class to use */ private ErrorHandler saxErrorHandler = null; /** EntityResolver class to use */ private EntityResolver saxEntityResolver = null; /** DTDHandler class to use */ private DTDHandler saxDTDHandler = null; /** XMLFilter instance to use */ private XMLFilter saxXMLFilter = null; /** The factory for creating new JDOM objects */ private JDOMFactory factory = new DefaultJDOMFactory(); /** ** Creates a new SAXBuilder which will attempt to first locate * a parser via JAXP, then will try to use a set of default * SAX Drivers. The underlying parser will not validate. *
*/ public SAXBuilder() { this(false); } /** ** Creates a new SAXBuilder using the specified SAX parser. * The underlying parser will not validate. *
* * @param saxDriverClassString
name of SAX Driver
* to use for parsing.
*/
public SAXBuilder(String saxDriverClass) {
this(saxDriverClass, false);
}
/**
* * Creates a new SAXBuilder using the specified SAX parser. * The underlying parser will validate or not * according to the given parameter. *
* * @param saxDriverClassString
name of SAX Driver
* to use for parsing.
* @param validate boolean
indicating if
* validation should occur.
*/
public SAXBuilder(String saxDriverClass, boolean validate) {
this.saxDriverClass = saxDriverClass;
this.validate = validate;
}
/**
* * Creates a new SAXBuilder which will attempt to first locate * a parser via JAXP, then will try to use a set of default * SAX Drivers. The underlying parser will validate or not * according to the given parameter. *
* * @param validateboolean
indicating if
* validation should occur.
*/
public SAXBuilder(boolean validate) {
this.validate = validate;
}
/**
* * This builds a document from the supplied * filename. *
* * @param fileFile
to read from.
* @return Document
- resultant Document object.
* @throws JDOMException when errors occur in parsing.
*/
public Document build(File file) throws JDOMException {
try {
URL url = fileToURL(file);
return build(url);
} catch (MalformedURLException e) {
throw new JDOMException("Error in building", e);
}
}
/**
* * This builds a document from the supplied * input stream. *
* * @param inInputStream
to read from.
* @return Document
- resultant Document object.
* @throws JDOMException when errors occur in parsing.
*/
public Document build(InputStream in) throws JDOMException {
return build(new InputSource(in));
}
/**
* * This builds a document from the supplied * input stream. *
* * @param inInputStream
to read from.
* @param systemId base for resolving relative URIs
* @return Document
- resultant Document object.
* @throws JDOMException when errors occur in parsing.
*/
public Document build(InputStream in, String systemId)
throws JDOMException {
InputSource src = new InputSource(in);
src.setSystemId(systemId);
return build(src);
}
/**
* * This builds a document from the supplied * Reader. *
* * @param inReader
to read from.
* @return Document
- resultant Document object.
* @throws JDOMException when errors occur in parsing.
*/
public Document build(Reader characterStream) throws JDOMException {
return build(new InputSource(characterStream));
}
/**
* * This builds a document from the supplied * Reader. *
* * @param inReader
to read from.
* @param systemId base for resolving relative URIs
* @return Document
- resultant Document object.
* @throws JDOMException when errors occur in parsing.
*/
public Document build(Reader characterStream, String SystemId)
throws JDOMException {
InputSource src = new InputSource(characterStream);
src.setSystemId(SystemId);
return build(src);
}
/**
* * This builds a document from the supplied * URI. *
* @param systemId URI for the input * @returnDocument
- resultant Document object.
* @throws JDOMException when errors occur in parsing.
*/
public Document build(String systemId) throws JDOMException {
return build(new InputSource(systemId));
}
/**
* * This builds a document from the supplied * URL. *
* * @param urlURL
to read from.
* @return Document
- resultant Document object.
* @throws JDOMException when errors occur in parsing.
*/
public Document build(URL url) throws JDOMException {
String systemID = url.toExternalForm();
return build(new InputSource(systemID));
}
/**
* * This builds a document from the supplied * input source. *
* * @param inInputSource
to read from.
* @return Document
- resultant Document object.
* @throws JDOMException when errors occur in parsing.
*/
protected Document build(InputSource in) throws JDOMException {
Document doc = factory.document((Element)null);
try {
XMLReader parser = null;
if (saxDriverClass != null) {
// The user knows that they want to use a particular class
parser = XMLReaderFactory.createXMLReader(saxDriverClass);
// System.out.println("using specific " + saxDriverClass);
} else {
// Try using JAXP...
// Note we need JAXP 1.1, and if JAXP 1.0 is all that's
// available then the getXMLReader call fails and we skip
// to the hard coded default parser
try {
Class factoryClass =
Class.forName("javax.xml.parsers.SAXParserFactory");
// factory = SAXParserFactory.newInstance();
Method newParserInstance =
factoryClass.getMethod("newInstance", null);
Object factory = newParserInstance.invoke(null, null);
// factory.setValidating(validate);
Method setValidating =
factoryClass.getMethod("setValidating",
new Class[]{boolean.class});
setValidating.invoke(factory,
new Object[]{new Boolean(validate)});
// jaxpParser = factory.newSAXParser();
Method newSAXParser =
factoryClass.getMethod("newSAXParser", null);
Object jaxpParser = newSAXParser.invoke(factory, null);
// parser = jaxpParser.getXMLReader();
Class parserClass = jaxpParser.getClass();
Method getXMLReader =
parserClass.getMethod("getXMLReader", null);
parser = (XMLReader)getXMLReader.invoke(jaxpParser, null);
saxDriverClass = parser.getClass().getName();
// System.out.println("Using jaxp " +
// parser.getClass().getName());
} catch (ClassNotFoundException e) {
//e.printStackTrace();
} catch (InvocationTargetException e) {
//e.printStackTrace();
} catch (NoSuchMethodException e) {
//e.printStackTrace();
}
}
// Check to see if we got a parser yet, if not, try to use a
// hard coded default
if (parser == null) {
parser = XMLReaderFactory.createXMLReader(DEFAULT_SAX_DRIVER);
// System.out.println("using default " + DEFAULT_SAX_DRIVER);
saxDriverClass = parser.getClass().getName();
}
// Install optional filter
if (saxXMLFilter != null) {
// Connect filter chain to parser
XMLFilter root = saxXMLFilter;
while (root.getParent() instanceof XMLFilter) {
root = (XMLFilter)root.getParent();
}
root.setParent(parser);
// Read from filter
parser = saxXMLFilter;
}
SAXHandler contentHandler = new SAXHandler(doc,factory);
contentHandler.setExpandEntities(expand); // pass thru behavior
parser.setContentHandler(contentHandler);
if (saxEntityResolver != null) {
parser.setEntityResolver(saxEntityResolver);
}
if (saxDTDHandler != null) {
parser.setDTDHandler(saxDTDHandler);
}
boolean lexicalReporting = false;
try {
parser.setProperty("http://xml.org/sax/handlers/LexicalHandler",
contentHandler);
lexicalReporting = true;
} catch (SAXNotSupportedException e) {
// No lexical reporting available
} catch (SAXNotRecognizedException e) {
// No lexical reporting available
}
// Some parsers use alternate property for lexical handling (grr...)
if (!lexicalReporting) {
try {
parser.setProperty(
"http://xml.org/sax/properties/lexical-handler",
contentHandler);
lexicalReporting = true;
} catch (SAXNotSupportedException e) {
// No lexical reporting available
} catch (SAXNotRecognizedException e) {
// No lexical reporting available
}
}
// Try setting the DeclHandler if entity expansion is off
if (!expand) {
try {
parser.setProperty(
"http://xml.org/sax/properties/declaration-handler",
contentHandler);
} catch (SAXNotSupportedException e) {
// No lexical reporting available
} catch (SAXNotRecognizedException e) {
// No lexical reporting available
}
}
// Set validation
try {
parser.setFeature(
"http://xml.org/sax/features/validation", validate);
parser.setFeature(
"http://xml.org/sax/features/namespaces", true);
parser.setFeature(
"http://xml.org/sax/features/namespace-prefixes", false);
if (saxErrorHandler != null) {
parser.setErrorHandler(saxErrorHandler);
} else {
parser.setErrorHandler(new BuilderErrorHandler());
}
}
catch (SAXNotRecognizedException e) {
// No validation available
if (validate) {
throw new JDOMException(
"Validation feature not recognized by " +
saxDriverClass);
}
}
catch (SAXNotSupportedException e) {
// No validation available
if (validate) {
throw new JDOMException(
"Validation not supported by " + saxDriverClass);
}
}
/*
// Set entity expansion
try {
//Crimson doesn't support this feature but does report it
// as true so...
if (parser.getFeature("http://xml.org/sax/features/external-general-entities") != expand) {
parser.setFeature("http://xml.org/sax/features/external-general-entities", expand);
}
}
catch (SAXNotRecognizedException e) {
// No entity expansion available
throw new JDOMException(
"Entity expansion feature not recognized by " +
saxDriverClass);
}
catch (SAXNotSupportedException e) {
// No entity expansion available
throw new JDOMException(
"Entity expansion feature not supported by " +
saxDriverClass);
}
*/
parser.parse(in);
return doc;
}
catch (Exception e) {
if (e instanceof SAXParseException) {
SAXParseException p = (SAXParseException)e;
String systemId = p.getSystemId();
if (systemId != null) {
throw new JDOMException("Error on line " +
p.getLineNumber() + " of document "
+ systemId, e);
} else {
throw new JDOMException("Error on line " +
p.getLineNumber(), e);
}
} else {
throw new JDOMException("Error in building", e);
}
}
}
/**
* Imitation of File.toURL(), a JDK 1.2 method, reimplemented
* here to work with JDK 1.1.
*
* @see java.io.File
*
* @param f the file to convert
* @return the file path converted to a file: URL
*/
protected URL fileToURL(File f) throws MalformedURLException {
String path = f.getAbsolutePath();
if (File.separatorChar != '/') {
path = path.replace(File.separatorChar, '/');
}
if (!path.startsWith("/")) {
path = "/" + path;
}
if (!path.endsWith("/") && f.isDirectory()) {
path = path + "/";
}
return new URL("file", "", path);
}
/**
*
* This sets custom DTDHandler for the Builder
.
*
DTDHandler
*/
public void setDTDHandler(DTDHandler dtdHandler) {
saxDTDHandler = dtdHandler;
}
/**
*
* This sets custom EntityResolver for the Builder
.
*
EntityResolver
*/
public void setEntityResolver(EntityResolver entityResolver) {
saxEntityResolver = entityResolver;
}
/**
*
* This sets custom ErrorHandler for the Builder
.
*
ErrorHandler
*/
public void setErrorHandler(ErrorHandler errorHandler) {
saxErrorHandler = errorHandler;
}
/**
*
* This sets whether or not to expand entities for the builder.
* A true means to expand entities as normal content. A false means to
* leave entities unexpanded as EntityRef
objects. The
* default is true.
*
boolean
indicating whether entity expansion
* should occur.
*/
public void setExpandEntities(boolean expand) {
this.expand = expand;
}
/**
* * This sets validation for the builder. *
* * @param validateboolean
indicating whether validation
* should occur.
*/
public void setValidation(boolean validate) {
this.validate = validate;
}
/**
*
* This sets custom XMLFilter for the Builder
.
*
XMLFilter
*/
public void setXMLFilter(XMLFilter xmlFilter) {
saxXMLFilter = xmlFilter;
}
/* This sets a custom JDOMFactory for the Builder
.
* Use this to build the tree with you own subclasses of the
* JDOM classes.
*
*
* @param factory JDOMFactory
*/
public void setFactory(JDOMFactory factory) {
this.factory = factory;
}
}