View Javadoc

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.struts2.jasper.xmlparser;
19  
20  import com.opensymphony.xwork2.util.logging.LoggerFactory;
21  import org.apache.struts2.jasper.Constants;
22  import org.apache.struts2.jasper.JasperException;
23  import org.apache.struts2.jasper.compiler.Localizer;
24  import org.w3c.dom.*;
25  import org.xml.sax.*;
26  
27  import javax.xml.parsers.DocumentBuilder;
28  import javax.xml.parsers.DocumentBuilderFactory;
29  import javax.xml.parsers.ParserConfigurationException;
30  import java.io.IOException;
31  import java.io.InputStream;
32  
33  
34  /***
35   * XML parsing utilities for processing web application deployment
36   * descriptor and tag library descriptor files.  FIXME - make these
37   * use a separate class loader for the parser to be used.
38   *
39   * @author Craig R. McClanahan
40   * @version $Revision: 609409 $ $Date: 2008-01-06 14:31:23 -0700 (Sun, 06 Jan 2008) $
41   */
42  
43  public class ParserUtils {
44  
45      /***
46       * An error handler for use when parsing XML documents.
47       */
48      static ErrorHandler errorHandler = new MyErrorHandler();
49  
50      /***
51       * An entity resolver for use when parsing XML documents.
52       */
53      static EntityResolver entityResolver = new MyEntityResolver();
54  
55      // Turn off for JSP 2.0 until switch over to using xschema.
56      public static boolean validating = false;
57  
58  
59      // --------------------------------------------------------- Public Methods
60  
61      /***
62       * Parse the specified XML document, and return a <code>TreeNode</code>
63       * that corresponds to the root node of the document tree.
64       *
65       * @param uri URI of the XML document being parsed
66       * @param is  Input source containing the deployment descriptor
67       * @throws JasperException if an input/output error occurs
68       * @throws JasperException if a parsing error occurs
69       */
70      public TreeNode parseXMLDocument(String uri, InputSource is)
71              throws JasperException {
72  
73          Document document = null;
74  
75          // Perform an XML parse of this document, via JAXP
76          try {
77              DocumentBuilderFactory factory =
78                      DocumentBuilderFactory.newInstance();
79              factory.setNamespaceAware(true);
80              factory.setValidating(validating);
81              DocumentBuilder builder = factory.newDocumentBuilder();
82              builder.setEntityResolver(entityResolver);
83              builder.setErrorHandler(errorHandler);
84              document = builder.parse(is);
85          } catch (ParserConfigurationException ex) {
86              throw new JasperException
87                      (Localizer.getMessage("jsp.error.parse.xml", uri), ex);
88          } catch (SAXParseException ex) {
89              throw new JasperException
90                      (Localizer.getMessage("jsp.error.parse.xml.line",
91                              uri,
92                              Integer.toString(ex.getLineNumber()),
93                              Integer.toString(ex.getColumnNumber())),
94                              ex);
95          } catch (SAXException sx) {
96              throw new JasperException
97                      (Localizer.getMessage("jsp.error.parse.xml", uri), sx);
98          } catch (IOException io) {
99              throw new JasperException
100                     (Localizer.getMessage("jsp.error.parse.xml", uri), io);
101         }
102 
103         // Convert the resulting document to a graph of TreeNodes
104         return (convert(null, document.getDocumentElement()));
105     }
106 
107 
108     /***
109      * Parse the specified XML document, and return a <code>TreeNode</code>
110      * that corresponds to the root node of the document tree.
111      *
112      * @param uri URI of the XML document being parsed
113      * @param is  Input stream containing the deployment descriptor
114      * @throws JasperException if an input/output error occurs
115      * @throws JasperException if a parsing error occurs
116      */
117     public TreeNode parseXMLDocument(String uri, InputStream is)
118             throws JasperException {
119 
120         return (parseXMLDocument(uri, new InputSource(is)));
121     }
122 
123 
124     // ------------------------------------------------------ Protected Methods
125 
126 
127     /***
128      * Create and return a TreeNode that corresponds to the specified Node,
129      * including processing all of the attributes and children nodes.
130      *
131      * @param parent The parent TreeNode (if any) for the new TreeNode
132      * @param node   The XML document Node to be converted
133      */
134     protected TreeNode convert(TreeNode parent, Node node) {
135 
136         // Construct a new TreeNode for this node
137         TreeNode treeNode = new TreeNode(node.getNodeName(), parent);
138 
139         // Convert all attributes of this node
140         NamedNodeMap attributes = node.getAttributes();
141         if (attributes != null) {
142             int n = attributes.getLength();
143             for (int i = 0; i < n; i++) {
144                 Node attribute = attributes.item(i);
145                 treeNode.addAttribute(attribute.getNodeName(),
146                         attribute.getNodeValue());
147             }
148         }
149 
150         // Create and attach all children of this node
151         NodeList children = node.getChildNodes();
152         if (children != null) {
153             int n = children.getLength();
154             for (int i = 0; i < n; i++) {
155                 Node child = children.item(i);
156                 if (child instanceof Comment)
157                     continue;
158                 if (child instanceof Text) {
159                     String body = ((Text) child).getData();
160                     if (body != null) {
161                         body = body.trim();
162                         if (body.length() > 0)
163                             treeNode.setBody(body);
164                     }
165                 } else {
166                     TreeNode treeChild = convert(treeNode, child);
167                 }
168             }
169         }
170 
171         // Return the completed TreeNode graph
172         return (treeNode);
173     }
174 }
175 
176 
177 // ------------------------------------------------------------ Private Classes
178 
179 class MyEntityResolver implements EntityResolver {
180 
181     public InputSource resolveEntity(String publicId, String systemId)
182             throws SAXException {
183         for (int i = 0; i < Constants.CACHED_DTD_PUBLIC_IDS.length; i++) {
184             String cachedDtdPublicId = Constants.CACHED_DTD_PUBLIC_IDS[i];
185             if (cachedDtdPublicId.equals(publicId)) {
186                 String resourcePath = Constants.CACHED_DTD_RESOURCE_PATHS[i];
187                 InputStream input = this.getClass().getResourceAsStream(
188                         resourcePath);
189                 if (input == null) {
190                     throw new SAXException(Localizer.getMessage(
191                             "jsp.error.internal.filenotfound", resourcePath));
192                 }
193                 InputSource isrc = new InputSource(input);
194                 return isrc;
195             }
196         }
197         com.opensymphony.xwork2.util.logging.Logger log = LoggerFactory.getLogger(MyEntityResolver.class);
198         if (log.isDebugEnabled())
199             log.debug("Resolve entity failed" + publicId + " " + systemId);
200         log.error(Localizer.getMessage("jsp.error.parse.xml.invalidPublicId",
201                 publicId));
202         return null;
203     }
204 }
205 
206 class MyErrorHandler implements ErrorHandler {
207 
208     public void warning(SAXParseException ex) throws SAXException {
209         com.opensymphony.xwork2.util.logging.Logger log = LoggerFactory.getLogger(MyErrorHandler.class);
210         if (log.isDebugEnabled())
211             log.debug("ParserUtils: warning ", ex);
212         // We ignore warnings
213     }
214 
215     public void error(SAXParseException ex) throws SAXException {
216         throw ex;
217     }
218 
219     public void fatalError(SAXParseException ex) throws SAXException {
220         throw ex;
221     }
222 }