1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.jasper.xmlparser;
19
20 import java.io.IOException;
21 import java.io.InputStream;
22
23 import javax.xml.parsers.DocumentBuilder;
24 import javax.xml.parsers.DocumentBuilderFactory;
25 import javax.xml.parsers.ParserConfigurationException;
26
27 import org.apache.jasper.Constants;
28 import org.apache.jasper.JasperException;
29 import org.apache.jasper.compiler.Localizer;
30 import org.apache.juli.logging.Log;
31 import org.apache.juli.logging.LogFactory;
32 import org.w3c.dom.Comment;
33 import org.w3c.dom.Document;
34 import org.w3c.dom.NamedNodeMap;
35 import org.w3c.dom.Node;
36 import org.w3c.dom.NodeList;
37 import org.w3c.dom.Text;
38 import org.xml.sax.EntityResolver;
39 import org.xml.sax.ErrorHandler;
40 import org.xml.sax.InputSource;
41 import org.xml.sax.SAXException;
42 import org.xml.sax.SAXParseException;
43
44
45 /**
46 * XML parsing utilities for processing web application deployment
47 * descriptor and tag library descriptor files. FIXME - make these
48 * use a separate class loader for the parser to be used.
49 *
50 * @author Craig R. McClanahan
51 * @version $Revision: 595799 $ $Date: 2007-11-16 21:02:12 +0100 (ven., 16 nov. 2007) $
52 */
53
54 public class ParserUtils {
55
56 /**
57 * An error handler for use when parsing XML documents.
58 */
59 static ErrorHandler errorHandler = new MyErrorHandler();
60
61 /**
62 * An entity resolver for use when parsing XML documents.
63 */
64 static EntityResolver entityResolver = new MyEntityResolver();
65
66 // Turn off for JSP 2.0 until switch over to using xschema.
67 public static boolean validating = false;
68
69
70 // --------------------------------------------------------- Public Methods
71
72 /**
73 * Parse the specified XML document, and return a <code>TreeNode</code>
74 * that corresponds to the root node of the document tree.
75 *
76 * @param uri URI of the XML document being parsed
77 * @param is Input source containing the deployment descriptor
78 *
79 * @exception JasperException if an input/output error occurs
80 * @exception JasperException if a parsing error occurs
81 */
82 public TreeNode parseXMLDocument(String uri, InputSource is)
83 throws JasperException {
84
85 Document document = null;
86
87 // Perform an XML parse of this document, via JAXP
88 try {
89 DocumentBuilderFactory factory =
90 DocumentBuilderFactory.newInstance();
91 factory.setNamespaceAware(true);
92 factory.setValidating(validating);
93 DocumentBuilder builder = factory.newDocumentBuilder();
94 builder.setEntityResolver(entityResolver);
95 builder.setErrorHandler(errorHandler);
96 document = builder.parse(is);
97 } catch (ParserConfigurationException ex) {
98 throw new JasperException
99 (Localizer.getMessage("jsp.error.parse.xml", uri), ex);
100 } catch (SAXParseException ex) {
101 throw new JasperException
102 (Localizer.getMessage("jsp.error.parse.xml.line",
103 uri,
104 Integer.toString(ex.getLineNumber()),
105 Integer.toString(ex.getColumnNumber())),
106 ex);
107 } catch (SAXException sx) {
108 throw new JasperException
109 (Localizer.getMessage("jsp.error.parse.xml", uri), sx);
110 } catch (IOException io) {
111 throw new JasperException
112 (Localizer.getMessage("jsp.error.parse.xml", uri), io);
113 }
114
115 // Convert the resulting document to a graph of TreeNodes
116 return (convert(null, document.getDocumentElement()));
117 }
118
119
120 /**
121 * Parse the specified XML document, and return a <code>TreeNode</code>
122 * that corresponds to the root node of the document tree.
123 *
124 * @param uri URI of the XML document being parsed
125 * @param is Input stream containing the deployment descriptor
126 *
127 * @exception JasperException if an input/output error occurs
128 * @exception JasperException if a parsing error occurs
129 */
130 public TreeNode parseXMLDocument(String uri, InputStream is)
131 throws JasperException {
132
133 return (parseXMLDocument(uri, new InputSource(is)));
134 }
135
136
137 // ------------------------------------------------------ Protected Methods
138
139
140 /**
141 * Create and return a TreeNode that corresponds to the specified Node,
142 * including processing all of the attributes and children nodes.
143 *
144 * @param parent The parent TreeNode (if any) for the new TreeNode
145 * @param node The XML document Node to be converted
146 */
147 protected TreeNode convert(TreeNode parent, Node node) {
148
149 // Construct a new TreeNode for this node
150 TreeNode treeNode = new TreeNode(node.getNodeName(), parent);
151
152 // Convert all attributes of this node
153 NamedNodeMap attributes = node.getAttributes();
154 if (attributes != null) {
155 int n = attributes.getLength();
156 for (int i = 0; i < n; i++) {
157 Node attribute = attributes.item(i);
158 treeNode.addAttribute(attribute.getNodeName(),
159 attribute.getNodeValue());
160 }
161 }
162
163 // Create and attach all children of this node
164 NodeList children = node.getChildNodes();
165 if (children != null) {
166 int n = children.getLength();
167 for (int i = 0; i < n; i++) {
168 Node child = children.item(i);
169 if (child instanceof Comment)
170 continue;
171 if (child instanceof Text) {
172 String body = ((Text) child).getData();
173 if (body != null) {
174 body = body.trim();
175 if (body.length() > 0)
176 treeNode.setBody(body);
177 }
178 } else {
179 TreeNode treeChild = convert(treeNode, child);
180 }
181 }
182 }
183
184 // Return the completed TreeNode graph
185 return (treeNode);
186 }
187 }
188
189
190 // ------------------------------------------------------------ Private Classes
191
192 class MyEntityResolver implements EntityResolver {
193
194 public InputSource resolveEntity(String publicId, String systemId)
195 throws SAXException {
196 for (int i = 0; i < Constants.CACHED_DTD_PUBLIC_IDS.length; i++) {
197 String cachedDtdPublicId = Constants.CACHED_DTD_PUBLIC_IDS[i];
198 if (cachedDtdPublicId.equals(publicId)) {
199 String resourcePath = Constants.CACHED_DTD_RESOURCE_PATHS[i];
200 InputStream input = this.getClass().getResourceAsStream(
201 resourcePath);
202 if (input == null) {
203 throw new SAXException(Localizer.getMessage(
204 "jsp.error.internal.filenotfound", resourcePath));
205 }
206 InputSource isrc = new InputSource(input);
207 return isrc;
208 }
209 }
210 Log log = LogFactory.getLog(MyEntityResolver.class);
211 if (log.isDebugEnabled())
212 log.debug("Resolve entity failed" + publicId + " " + systemId);
213 log.error(Localizer.getMessage("jsp.error.parse.xml.invalidPublicId",
214 publicId));
215 return null;
216 }
217 }
218
219 class MyErrorHandler implements ErrorHandler {
220
221 public void warning(SAXParseException ex) throws SAXException {
222 Log log = LogFactory.getLog(MyErrorHandler.class);
223 if (log.isDebugEnabled())
224 log.debug("ParserUtils: warning ", ex);
225 // We ignore warnings
226 }
227
228 public void error(SAXParseException ex) throws SAXException {
229 throw ex;
230 }
231
232 public void fatalError(SAXParseException ex) throws SAXException {
233 throw ex;
234 }
235 }