/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.sling.scripting.jsp.jasper.xmlparser; import java.io.IOException; import java.io.InputStream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.sling.scripting.jsp.jasper.Constants; import org.apache.sling.scripting.jsp.jasper.JasperException; import org.apache.sling.scripting.jsp.jasper.compiler.Localizer; import org.w3c.dom.Comment; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; /** * XML parsing utilities for processing web application deployment * descriptor and tag library descriptor files. FIXME - make these * use a separate class loader for the parser to be used. * * @author Craig R. McClanahan * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (Die, 24 Okt 2006) $ */ public class ParserUtils { /** * An error handler for use when parsing XML documents. */ static ErrorHandler errorHandler = new MyErrorHandler(); /** * An entity resolver for use when parsing XML documents. */ static EntityResolver entityResolver = new MyEntityResolver(); // Turn off for JSP 2.0 until switch over to using xschema. public static boolean validating = false; // --------------------------------------------------------- Public Methods /** * Parse the specified XML document, and return a <code>TreeNode</code> * that corresponds to the root node of the document tree. * * @param uri URI of the XML document being parsed * @param is Input source containing the deployment descriptor * * @exception JasperException if an input/output error occurs * @exception JasperException if a parsing error occurs */ public TreeNode parseXMLDocument(String uri, InputSource is) throws JasperException { Document document = null; // Perform an XML parse of this document, via JAXP try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); factory.setValidating(validating); DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(entityResolver); builder.setErrorHandler(errorHandler); document = builder.parse(is); } catch (ParserConfigurationException ex) { throw new JasperException (Localizer.getMessage("jsp.error.parse.xml", uri), ex); } catch (SAXParseException ex) { throw new JasperException (Localizer.getMessage("jsp.error.parse.xml.line", uri, Integer.toString(ex.getLineNumber()), Integer.toString(ex.getColumnNumber())), ex); } catch (SAXException sx) { throw new JasperException (Localizer.getMessage("jsp.error.parse.xml", uri), sx); } catch (IOException io) { throw new JasperException (Localizer.getMessage("jsp.error.parse.xml", uri), io); } // Convert the resulting document to a graph of TreeNodes return (convert(null, document.getDocumentElement())); } /** * Parse the specified XML document, and return a <code>TreeNode</code> * that corresponds to the root node of the document tree. * * @param uri URI of the XML document being parsed * @param is Input stream containing the deployment descriptor * * @exception JasperException if an input/output error occurs * @exception JasperException if a parsing error occurs */ public TreeNode parseXMLDocument(String uri, InputStream is) throws JasperException { return (parseXMLDocument(uri, new InputSource(is))); } // ------------------------------------------------------ Protected Methods /** * Create and return a TreeNode that corresponds to the specified Node, * including processing all of the attributes and children nodes. * * @param parent The parent TreeNode (if any) for the new TreeNode * @param node The XML document Node to be converted */ protected TreeNode convert(TreeNode parent, Node node) { // Construct a new TreeNode for this node TreeNode treeNode = new TreeNode(node.getNodeName(), parent); // Convert all attributes of this node NamedNodeMap attributes = node.getAttributes(); if (attributes != null) { int n = attributes.getLength(); for (int i = 0; i < n; i++) { Node attribute = attributes.item(i); treeNode.addAttribute(attribute.getNodeName(), attribute.getNodeValue()); } } // Create and attach all children of this node NodeList children = node.getChildNodes(); if (children != null) { int n = children.getLength(); for (int i = 0; i < n; i++) { Node child = children.item(i); if (child instanceof Comment) continue; if (child instanceof Text) { String body = ((Text) child).getData(); if (body != null) { body = body.trim(); if (body.length() > 0) treeNode.setBody(body); } } else { TreeNode treeChild = convert(treeNode, child); } } } // Return the completed TreeNode graph return (treeNode); } } // ------------------------------------------------------------ Private Classes class MyEntityResolver implements EntityResolver { // Logger private Log log = LogFactory.getLog(MyEntityResolver.class); public InputSource resolveEntity(String publicId, String systemId) throws SAXException { for (int i = 0; i < Constants.CACHED_DTD_PUBLIC_IDS.length; i++) { String cachedDtdPublicId = Constants.CACHED_DTD_PUBLIC_IDS[i]; if (cachedDtdPublicId.equals(publicId)) { String resourcePath = Constants.CACHED_DTD_RESOURCE_PATHS[i]; InputStream input = this.getClass().getResourceAsStream( resourcePath); if (input == null) { throw new SAXException(Localizer.getMessage( "jsp.error.internal.filenotfound", resourcePath)); } InputSource isrc = new InputSource(input); return isrc; } } if (log.isDebugEnabled()) log.debug("Resolve entity failed" + publicId + " " + systemId); log.error(Localizer.getMessage("jsp.error.parse.xml.invalidPublicId", publicId)); return null; } } class MyErrorHandler implements ErrorHandler { // Logger private Log log = LogFactory.getLog(MyErrorHandler.class); public void warning(SAXParseException ex) throws SAXException { if (log.isDebugEnabled()) log.debug("ParserUtils: warning ", ex); // We ignore warnings } public void error(SAXParseException ex) throws SAXException { throw ex; } public void fatalError(SAXParseException ex) throws SAXException { throw ex; } }