/*-- Copyright (C) 2000 Brett McLaughlin & Jason Hunter. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the disclaimer that follows these conditions in the documentation and/or other materials provided with the distribution. 3. The name "JDOM" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact license@jdom.org. 4. Products derived from this software may not be called "JDOM", nor may "JDOM" appear in their name, without prior written permission from the JDOM Project Management (pm@jdom.org). In addition, we request (but do not require) that you include in the end-user documentation provided with the redistribution and/or in the software itself an acknowledgement equivalent to the following: "This product includes software developed by the JDOM Project (http://www.jdom.org/)." Alternatively, the acknowledgment may be graphical using the logos available at http://www.jdom.org/images/logos. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 THE APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS 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. This software consists of voluntary contributions made by many individuals on behalf of the JDOM Project and was originally created by Brett McLaughlin and Jason Hunter . For more information on the JDOM Project, please see . */ package org.jdom.input; import java.io.File; import java.io.InputStream; import java.io.IOException; import java.io.Reader; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.LinkedList; import java.util.Stack; import org.jdom.Attribute; import org.jdom.CDATA; import org.jdom.Comment; import org.jdom.DocType; import org.jdom.Document; import org.jdom.Element; import org.jdom.Entity; import org.jdom.JDOMException; import org.jdom.Namespace; import org.jdom.ProcessingInstruction; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.DTDHandler; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.SAXParseException; import org.xml.sax.XMLFilter; import org.xml.sax.XMLReader; 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.

* * @author Brett McLaughlin * @author Jason Hunter * @author Dan Schaffer * @version 1.0 */ public class SAXBuilder { /** Default parser class to use */ private static final String DEFAULT_SAX_DRIVER = "org.apache.xerces.parsers.SAXParser"; /** Whether validation should occur */ private boolean validate; /** 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; /** *

* This allows the validation features to be turned on/off * in the builder at creation, as well as set the * DOM Adapter class to use. *

* * @param saxDriverClass String 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; } /** *

* This sets the SAX Driver class to use, and leaves * validation off. *

* * @param saxDriverClass String name of SAX Driver * to use for parsing. */ public SAXBuilder(String saxDriverClass) { this(saxDriverClass, false); } /** *

* This sets validation for the Builder. *

* * @param validate boolean indicating if * validation should occur. */ public SAXBuilder(boolean validate) { this(DEFAULT_SAX_DRIVER, validate); } /** *

* This creates a SAXBuilder with * the default SAX driver and no validation. *

*/ public SAXBuilder() { this(DEFAULT_SAX_DRIVER, false); } /** *

* This sets validation for the Builder. *

* * @param errorHandler boolean indicating whether validation should occur. */ public void setValidation(boolean validate) { this.validate = validate; } /** *

* This sets custom ErrorHandler for the Builder. *

* * @param errorHandler ErrorHandler */ public void setErrorHandler(ErrorHandler errorHandler) { saxErrorHandler = errorHandler; } /** *

* This sets custom EntityResolver for the Builder. *

* * @param entityResolver EntityResolver */ public void setEntityResolver(EntityResolver entityResolver) { saxEntityResolver = entityResolver; } /** *

* This sets custom DTDHandler for the Builder. *

* * @param dtdHandler DTDHandler */ public void setDTDHandler(DTDHandler dtdHandler) { saxDTDHandler = dtdHandler; } /** *

* This sets custom XMLFilter for the Builder. *

* * @param xmlFilter XMLFilter */ public void setXMLFilter(XMLFilter xmlFilter) { saxXMLFilter = xmlFilter; } /** *

* This builds a document from the supplied * input source. *

* * @param in InputSource to read from. * @return Document - resultant Document object. * @throws JDOMException when errors occur in * parsing. */ protected Document build(InputSource in) throws JDOMException { Document doc = new Document(null); try { XMLReader parser = XMLReaderFactory.createXMLReader(saxDriverClass); // 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; } DefaultHandler contentHandler = createContentHandler( doc ); 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 propety 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 } } // 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(contentHandler); } } catch (SAXNotSupportedException e) { // No validation available if (validate) { throw new JDOMException( "Validation not supported for " + saxDriverClass + " SAX Driver"); } } catch (SAXNotRecognizedException e) { // No validation available if (validate) { throw new JDOMException( "Validation feature not recognized for " + saxDriverClass + " SAX Driver"); } } 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(e.getMessage(), new JDOMException("Error on line " + p.getLineNumber() + " of document " + systemId + ": " + p.getMessage(), e)); } else { throw new JDOMException(e.getMessage(), new JDOMException("Error on line " + p.getLineNumber() + ": " + p.getMessage(), e)); } } else { throw new JDOMException(e.getMessage(), e); } } } /** *

* This builds a document from the supplied * input stream. *

* * @param in InputStream 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 * filename. *

* * @param file File 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(e.getMessage(), e); } } /** *

* This builds a document from the supplied * URL. *

* * @param url URL 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 stream. *

* * @param in InputStream 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 in Reader 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 in Reader 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 * @return Document - resultant Document object. * @throws JDOMException when errors occur in * parsing. */ public Document build(String systemId) throws JDOMException { return build(new InputSource(systemId)); } /** * 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); } /** Factory Method to allow user derived SAXHandlers to be used */ protected DefaultHandler createContentHandler( Document doc ) throws IOException { return new SAXHandler(doc); } }