View Javadoc
1   /*
2    *    Copyright 2009-2021 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.parsing;
17  
18  import java.io.InputStream;
19  import java.io.Reader;
20  import java.io.StringReader;
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.Properties;
24  
25  import javax.xml.XMLConstants;
26  import javax.xml.namespace.QName;
27  import javax.xml.parsers.DocumentBuilder;
28  import javax.xml.parsers.DocumentBuilderFactory;
29  import javax.xml.xpath.XPath;
30  import javax.xml.xpath.XPathConstants;
31  import javax.xml.xpath.XPathFactory;
32  
33  import org.apache.ibatis.builder.BuilderException;
34  import org.w3c.dom.Document;
35  import org.w3c.dom.Node;
36  import org.w3c.dom.NodeList;
37  import org.xml.sax.EntityResolver;
38  import org.xml.sax.ErrorHandler;
39  import org.xml.sax.InputSource;
40  import org.xml.sax.SAXException;
41  import org.xml.sax.SAXParseException;
42  
43  /**
44   * @author Clinton Begin
45   * @author Kazuki Shimizu
46   */
47  public class XPathParser {
48  
49    private final Document document;
50    private boolean validation;
51    private EntityResolver entityResolver;
52    private Properties variables;
53    private XPath xpath;
54  
55    public XPathParser(String xml) {
56      commonConstructor(false, null, null);
57      this.document = createDocument(new InputSource(new StringReader(xml)));
58    }
59  
60    public XPathParser(Reader reader) {
61      commonConstructor(false, null, null);
62      this.document = createDocument(new InputSource(reader));
63    }
64  
65    public XPathParser(InputStream inputStream) {
66      commonConstructor(false, null, null);
67      this.document = createDocument(new InputSource(inputStream));
68    }
69  
70    public XPathParser(Document document) {
71      commonConstructor(false, null, null);
72      this.document = document;
73    }
74  
75    public XPathParser(String xml, boolean validation) {
76      commonConstructor(validation, null, null);
77      this.document = createDocument(new InputSource(new StringReader(xml)));
78    }
79  
80    public XPathParser(Reader reader, boolean validation) {
81      commonConstructor(validation, null, null);
82      this.document = createDocument(new InputSource(reader));
83    }
84  
85    public XPathParser(InputStream inputStream, boolean validation) {
86      commonConstructor(validation, null, null);
87      this.document = createDocument(new InputSource(inputStream));
88    }
89  
90    public XPathParser(Document document, boolean validation) {
91      commonConstructor(validation, null, null);
92      this.document = document;
93    }
94  
95    public XPathParser(String xml, boolean validation, Properties variables) {
96      commonConstructor(validation, variables, null);
97      this.document = createDocument(new InputSource(new StringReader(xml)));
98    }
99  
100   public XPathParser(Reader reader, boolean validation, Properties variables) {
101     commonConstructor(validation, variables, null);
102     this.document = createDocument(new InputSource(reader));
103   }
104 
105   public XPathParser(InputStream inputStream, boolean validation, Properties variables) {
106     commonConstructor(validation, variables, null);
107     this.document = createDocument(new InputSource(inputStream));
108   }
109 
110   public XPathParser(Document document, boolean validation, Properties variables) {
111     commonConstructor(validation, variables, null);
112     this.document = document;
113   }
114 
115   public XPathParser(String xml, boolean validation, Properties variables, EntityResolver entityResolver) {
116     commonConstructor(validation, variables, entityResolver);
117     this.document = createDocument(new InputSource(new StringReader(xml)));
118   }
119 
120   public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
121     commonConstructor(validation, variables, entityResolver);
122     this.document = createDocument(new InputSource(reader));
123   }
124 
125   public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
126     commonConstructor(validation, variables, entityResolver);
127     this.document = createDocument(new InputSource(inputStream));
128   }
129 
130   public XPathParser(Document document, boolean validation, Properties variables, EntityResolver entityResolver) {
131     commonConstructor(validation, variables, entityResolver);
132     this.document = document;
133   }
134 
135   public void setVariables(Properties variables) {
136     this.variables = variables;
137   }
138 
139   public String evalString(String expression) {
140     return evalString(document, expression);
141   }
142 
143   public String evalString(Object root, String expression) {
144     String result = (String) evaluate(expression, root, XPathConstants.STRING);
145     result = PropertyParser.parse(result, variables);
146     return result;
147   }
148 
149   public Boolean evalBoolean(String expression) {
150     return evalBoolean(document, expression);
151   }
152 
153   public Boolean evalBoolean(Object root, String expression) {
154     return (Boolean) evaluate(expression, root, XPathConstants.BOOLEAN);
155   }
156 
157   public Short evalShort(String expression) {
158     return evalShort(document, expression);
159   }
160 
161   public Short evalShort(Object root, String expression) {
162     return Short.valueOf(evalString(root, expression));
163   }
164 
165   public Integer evalInteger(String expression) {
166     return evalInteger(document, expression);
167   }
168 
169   public Integer evalInteger(Object root, String expression) {
170     return Integer.valueOf(evalString(root, expression));
171   }
172 
173   public Long evalLong(String expression) {
174     return evalLong(document, expression);
175   }
176 
177   public Long evalLong(Object root, String expression) {
178     return Long.valueOf(evalString(root, expression));
179   }
180 
181   public Float evalFloat(String expression) {
182     return evalFloat(document, expression);
183   }
184 
185   public Float evalFloat(Object root, String expression) {
186     return Float.valueOf(evalString(root, expression));
187   }
188 
189   public Double evalDouble(String expression) {
190     return evalDouble(document, expression);
191   }
192 
193   public Double evalDouble(Object root, String expression) {
194     return (Double) evaluate(expression, root, XPathConstants.NUMBER);
195   }
196 
197   public List<XNode> evalNodes(String expression) {
198     return evalNodes(document, expression);
199   }
200 
201   public List<XNode> evalNodes(Object root, String expression) {
202     List<XNode> xnodes = new ArrayList<>();
203     NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET);
204     for (int i = 0; i < nodes.getLength(); i++) {
205       xnodes.add(new XNode(this, nodes.item(i), variables));
206     }
207     return xnodes;
208   }
209 
210   public XNode evalNode(String expression) {
211     return evalNode(document, expression);
212   }
213 
214   public XNode evalNode(Object root, String expression) {
215     Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
216     if (node == null) {
217       return null;
218     }
219     return new XNode(this, node, variables);
220   }
221 
222   private Object evaluate(String expression, Object root, QName returnType) {
223     try {
224       return xpath.evaluate(expression, root, returnType);
225     } catch (Exception e) {
226       throw new BuilderException("Error evaluating XPath.  Cause: " + e, e);
227     }
228   }
229 
230   private Document createDocument(InputSource inputSource) {
231     // important: this must only be called AFTER common constructor
232     try {
233       DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
234       factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
235       factory.setValidating(validation);
236 
237       factory.setNamespaceAware(false);
238       factory.setIgnoringComments(true);
239       factory.setIgnoringElementContentWhitespace(false);
240       factory.setCoalescing(false);
241       factory.setExpandEntityReferences(true);
242 
243       DocumentBuilder builder = factory.newDocumentBuilder();
244       builder.setEntityResolver(entityResolver);
245       builder.setErrorHandler(new ErrorHandler() {
246         @Override
247         public void error(SAXParseException exception) throws SAXException {
248           throw exception;
249         }
250 
251         @Override
252         public void fatalError(SAXParseException exception) throws SAXException {
253           throw exception;
254         }
255 
256         @Override
257         public void warning(SAXParseException exception) throws SAXException {
258           // NOP
259         }
260       });
261       return builder.parse(inputSource);
262     } catch (Exception e) {
263       throw new BuilderException("Error creating document instance.  Cause: " + e, e);
264     }
265   }
266 
267   private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
268     this.validation = validation;
269     this.entityResolver = entityResolver;
270     this.variables = variables;
271     XPathFactory factory = XPathFactory.newInstance();
272     this.xpath = factory.newXPath();
273   }
274 
275 }