/*-- 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.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; /** *

SAXHandler builds a JDOM tree using SAX.

* * @author Brett McLaughlin * @author Jason Hunter * @author Dan Schaffer * @author James Strachan * @version 1.0 */ public class SAXHandler extends DefaultHandler implements LexicalHandler { /** SAXFactory used to build JDOM objects */ private SAXFactory factory; /** Document object being built */ private Document document; /** Element stack */ private Stack stack; /** Indicator of where in the document we are */ private boolean atRoot; /** Indicator of whether we are in a DTD */ private boolean inDTD; /** Indicator of whether we are in a CDATA */ private boolean inCDATA; /** Indicator of whether we are in an Entity */ private boolean inEntity; /** Namespaces declared, but not available */ private List declaredNamespaces; /** Available namespaces */ private List availableNamespaces; /** *

* This will set the Document to use. *

* * @param document Document being parsed. * @throws IOException when errors occur. */ public SAXHandler(SAXFactory factory, Document document) throws IOException { this.factory = factory; this.document = document; atRoot = true; stack = new Stack(); declaredNamespaces = new LinkedList(); availableNamespaces = new LinkedList(); availableNamespaces.add(Namespace.XML_NAMESPACE); inEntity = false; inDTD = false; inCDATA = false; } /** *

* This will indicate that a processing instruction (other than * the XML declaration) has been encountered. *

* * @param target String target of PI * @param data StringSAXException when things go wrong */ public void processingInstruction(String target, String data) throws SAXException { if (atRoot) { document.addContent(createProcessingInstruction(target, data)); } else { ((Element)stack.peek()).addContent(createProcessingInstruction(target, data)); } } /** *

* This will add the prefix mapping to the JDOM * Document object. *

* * @param prefix String namespace prefix. * @param uri String namespace URI. */ public void startPrefixMapping(String prefix, String uri) throws SAXException { Namespace ns = Namespace.getNamespace(prefix, uri); declaredNamespaces.add(ns); } /** *

* This will add the prefix mapping to the JDOM * Document object. *

* * @param prefix String namespace prefix. * @param uri String namespace URI. */ public void endPrefixMapping(String prefix, String uri) throws SAXException { // This removes the namespace for the actual element Namespace ns = Namespace.getNamespace(prefix, uri); availableNamespaces.remove(ns); } /** *

* This reports the occurrence of an actual element. It will include * the element's attributes, with the exception of XML vocabulary * specific attributes, such as * xmlns:[namespace prefix] and * xsi:schemaLocation. *

* * @param namespaceURI String namespace URI this element * is associated with, or an empty * String * @param localName String name of element (with no * namespace prefix, if one is present) * @param rawName String XML 1.0 version of element name: * [namespace prefix]:[localName] * @param atts Attributes list for this element * @throws SAXException when things go wrong */ public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { Element element = null; if ((namespaceURI != null) && (!namespaceURI.equals(""))) { String prefix = ""; // Determine any prefix on the Element if (localName != qName) { int split = qName.indexOf(":"); prefix = qName.substring(0, split); } Namespace elementNamespace = Namespace.getNamespace(prefix, namespaceURI); element = createElement(localName, elementNamespace); // We want to remove this namespace from those declared, as we've handled that declaredNamespaces.remove(elementNamespace); // We do need to make it available for others to use, though availableNamespaces.add(elementNamespace); } else { element = createElement(localName); } // We need to take all declared namespaces and add them to this element transferNamespaces(element); // Handle attributes for (int i=0, len=atts.getLength(); i0; i--) { Namespace ns = (Namespace)availableNamespaces.get(i-1); if (prefix.equals(ns.getPrefix())) { return ns; } } return Namespace.NO_NAMESPACE; } /** *

* This will report character data (within an element). *

* * @param ch char[] character array with character data * @param start int index in array where data starts. * @param end int index in array where data ends. * @throws SAXException when things go wrong */ public void characters(char[] ch, int start, int end) throws SAXException { String data = new String(ch, start, end); if (inCDATA) { ((Element)stack.peek()).addContent(createCDATA(data)); /** * This is commented out because of some problems with * the inline DTDs that Xerces seems to have. } else if (!inDTD) { if (inEntity) { ((Entity)stack.peek()).setContent(data); } else { Element e = (Element)stack.peek(); e.addContent(data); } */ } else if (inEntity) { ((Entity)stack.peek()).setContent(data); } else { Element e = (Element)stack.peek(); e.addContent(data); } } /** *

* Indicates the end of an element * (</[element name]>) is reached. Note that * the parser does not distinguish between empty * elements and non-empty elements, so this will occur uniformly. *

* * @param namespaceURI String URI of namespace this * element is associated with * @param localName String name of element without prefix * @param rawName String name of element in XML 1.0 form * @throws SAXException when things go wrong */ public void endElement(String namespaceURI, String localName, String rawName) { Element element = (Element)stack.pop(); // Remove the namespaces that this element makes available availableNamespaces.remove(element.getAdditionalNamespaces()); } /** *

* This will report an error that has occurred; this indicates * that a rule was broken, typically in validation, but that * parsing can reasonably continue. *

* * @param exception SAXParseException that occurred. * @throws SAXException when things go wrong */ public void error(SAXParseException exception) throws SAXException { throw exception; } /** *

* This will report an error that has occurred; this indicates * that a rule was broken, typically in validation, but that * parsing can reasonably continue. *

* * @param exception SAXParseException that occurred. * @throws SAXException when things go wrong */ public void warning(SAXParseException exception) throws SAXException { throw exception; } /** *

* This will report an error that has occurred; this indicates * that a rule was broken, typically in validation, but that * parsing can reasonably continue. *

* * @param exception SAXParseException that occurred. * @throws SAXException when things go wrong */ public void fatalError(SAXParseException exception) throws SAXException { throw exception; } /** *

* This will signify that a DTD is being parsed, and can be * used to ensure that comments and other lexical structures * in the DTD are not added to the JDOM Document * object. *

* * @param name String name of element listed in DTD * @param publicId String public ID of DTD * @param systemId String syste ID of DTD */ public void startDTD(String name, String publicId, String systemId) throws SAXException { document.setDocType(createDocType(name, publicId, systemId)); inDTD = true; /* Debug System.out.println("startDTD invoked..."); */ } /** *

* This signifies that the reading of the DTD is complete. *

*/ public void endDTD() throws SAXException { inDTD = false; /* Debug System.out.println("endDTD invoked..."); */ } public void startEntity(String name) throws SAXException { // Ignore DTD references, and translate the standard 5 if ((!inDTD) && (!name.equals("amp")) && (!name.equals("lt")) && (!name.equals("gt")) && (!name.equals("apos")) && (!name.equals("quot"))) { Entity entity = createEntity(name); ((Element)stack.peek()).addContent(entity); stack.push(entity); inEntity = true; } } public void endEntity(String name) throws SAXException { if (inEntity) { stack.pop(); inEntity = false; } } /** *

* Report a CDATA section - ignored in SAXBuilder. *

*/ public void startCDATA() throws SAXException { inCDATA = true; } /** *

* Report a CDATA section - ignored in SAXBuilder. *

*/ public void endCDATA() throws SAXException { inCDATA = false; } /** *

* This reports that a comments is parsed. If not in the * DTD, this comment is added to the current JDOM * Element, or the Document itself * if at that level. *

* * @param ch ch[] array of comment characters. * @param start int index to start reading from. * @param end int index to end reading at. */ public void comment(char[] ch, int start, int end) throws SAXException { String commentText = new String(ch, start, end); if ((!inDTD) && (!commentText.equals(""))) { if (stack.empty()) { document.addContent( createComment(commentText)); } else { ((Element)stack.peek()).addContent( createComment(commentText)); } } } // FACTORY METHODS // =============== // These methods allow derived classes to overload // which implementations of Element and Attribute classes // to be used, for example to add user defined data // or to allow extra navigation information to be stored // (such as for XPath expression evaluation etc.) /** Factory Method to allow user defined Element * instances to be used. */ protected Element createElement(String localName, Namespace elementNamespace) { return factory.createElement(localName, elementNamespace); } /** Factory Method to allow user defined Element * instances to be used */ protected Element createElement(String localName) { return factory.createElement(localName); } /** Factory Method to allow user defined Attribute * instances to be used. */ protected Attribute createAttribute(Element element, String attLocalName, String value, Namespace attNamespace) { return factory.createAttribute(element, attLocalName, value, attNamespace); } /** Factory Method to allow user defined Attribute * instances to be used */ protected Attribute createAttribute(Element element, String attLocalName, String value) { return factory.createAttribute(element, attLocalName, value); } protected ProcessingInstruction createProcessingInstruction(String target, String data) { return factory.createProcessingInstruction(target, data); } protected CDATA createCDATA(String data) { return factory.createCDATA(data); } protected DocType createDocType(String name, String publicId, String systemId) { return factory.createDocType(name, publicId, systemId); } protected Entity createEntity(String name) { return factory.createEntity(name); } protected Comment createComment(String commentText) { return factory.createComment(commentText); } }