View Javadoc

1   /*
2    * Copyright 2005-2012 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  
17  package org.springframework.xml.xpath;
18  
19  import java.io.InputStream;
20  import java.io.Reader;
21  import java.util.ArrayList;
22  import java.util.List;
23  import javax.xml.namespace.QName;
24  import javax.xml.stream.XMLEventReader;
25  import javax.xml.stream.XMLStreamException;
26  import javax.xml.stream.XMLStreamReader;
27  import javax.xml.transform.Source;
28  import javax.xml.transform.TransformerException;
29  import javax.xml.transform.dom.DOMResult;
30  import javax.xml.xpath.XPath;
31  import javax.xml.xpath.XPathConstants;
32  import javax.xml.xpath.XPathExpressionException;
33  import javax.xml.xpath.XPathFactory;
34  import javax.xml.xpath.XPathFactoryConfigurationException;
35  
36  import org.springframework.util.xml.StaxUtils;
37  import org.springframework.xml.namespace.SimpleNamespaceContext;
38  import org.springframework.xml.transform.TransformerHelper;
39  import org.springframework.xml.transform.TraxUtils;
40  
41  import org.w3c.dom.DOMException;
42  import org.w3c.dom.Document;
43  import org.w3c.dom.Element;
44  import org.w3c.dom.Node;
45  import org.w3c.dom.NodeList;
46  import org.xml.sax.InputSource;
47  import org.xml.sax.XMLReader;
48  
49  /**
50   * Implementation of {@link XPathOperations} that uses JAXP 1.3. JAXP 1.3 is part of Java SE since 1.5.
51   * <p/>
52   * Namespaces can be set using the {@code namespaces} property.
53   *
54   * @author Arjen Poutsma
55   * @see #setNamespaces(java.util.Map)
56   * @since 1.0.0
57   */
58  public class Jaxp13XPathTemplate extends AbstractXPathTemplate {
59  
60      private XPathFactory xpathFactory;
61  
62      public Jaxp13XPathTemplate() {
63          this(XPathFactory.DEFAULT_OBJECT_MODEL_URI);
64      }
65  
66      public Jaxp13XPathTemplate(String xpathFactoryUri) {
67          try {
68              xpathFactory = XPathFactory.newInstance(xpathFactoryUri);
69          }
70          catch (XPathFactoryConfigurationException ex) {
71              throw new XPathException("Could not create XPathFactory", ex);
72          }
73      }
74  
75      public boolean evaluateAsBoolean(String expression, Source context) throws XPathException {
76          Boolean result = (Boolean) evaluate(expression, context, XPathConstants.BOOLEAN);
77          return result != null && result;
78      }
79  
80      public Node evaluateAsNode(String expression, Source context) throws XPathException {
81          return (Node) evaluate(expression, context, XPathConstants.NODE);
82      }
83  
84      public List<Node> evaluateAsNodeList(String expression, Source context) throws XPathException {
85          NodeList result = (NodeList) evaluate(expression, context, XPathConstants.NODESET);
86          List<Node> nodes = new ArrayList<Node>(result.getLength());
87          for (int i = 0; i < result.getLength(); i++) {
88              nodes.add(result.item(i));
89          }
90          return nodes;
91      }
92  
93      public double evaluateAsDouble(String expression, Source context) throws XPathException {
94          Double result = (Double) evaluate(expression, context, XPathConstants.NUMBER);
95          return result != null ? result : Double.NaN;
96      }
97  
98      public String evaluateAsString(String expression, Source context) throws XPathException {
99          return (String) evaluate(expression, context, XPathConstants.STRING);
100     }
101 
102     public <T> T evaluateAsObject(String expression, Source context, NodeMapper<T> nodeMapper) throws XPathException {
103         Node node = evaluateAsNode(expression, context);
104         if (node != null) {
105             try {
106                 return nodeMapper.mapNode(node, 0);
107             }
108             catch (DOMException ex) {
109                 throw new XPathException("Mapping resulted in DOMException", ex);
110             }
111         }
112         else {
113             return null;
114         }
115     }
116 
117     public <T> List<T> evaluate(String expression, Source context, NodeMapper<T> nodeMapper) throws XPathException {
118         NodeList nodes = (NodeList) evaluate(expression, context, XPathConstants.NODESET);
119         List<T> results = new ArrayList<T>(nodes.getLength());
120         for (int i = 0; i < nodes.getLength(); i++) {
121             try {
122                 results.add(nodeMapper.mapNode(nodes.item(i), i));
123             }
124             catch (DOMException ex) {
125                 throw new XPathException("Mapping resulted in DOMException", ex);
126             }
127         }
128         return results;
129     }
130 
131     private Object evaluate(String expression, Source context, QName returnType) throws XPathException {
132         XPath xpath = createXPath();
133         if (getNamespaces() != null && !getNamespaces().isEmpty()) {
134             SimpleNamespaceContext namespaceContext = new SimpleNamespaceContext();
135             namespaceContext.setBindings(getNamespaces());
136             xpath.setNamespaceContext(namespaceContext);
137         }
138         try {
139             EvaluationCallback callback = new EvaluationCallback(xpath, expression, returnType);
140             TraxUtils.doWithSource(context, callback);
141             return callback.result;
142         }
143         catch (javax.xml.xpath.XPathException ex) {
144             throw new XPathException("Could not evaluate XPath expression [" + expression + "]", ex);
145         }
146         catch (TransformerException ex) {
147             throw new XPathException("Could not transform context to DOM Node", ex);
148         }
149         catch (Exception ex) {
150             throw new XPathException(ex.getMessage(), ex);
151         }
152     }
153 
154     private synchronized XPath createXPath() {
155         return xpathFactory.newXPath();
156     }
157 
158     private static class EvaluationCallback implements TraxUtils.SourceCallback {
159 
160         private final XPath xpath;
161 
162         private final String expression;
163 
164         private final QName returnType;
165 
166         private final TransformerHelper transformerHelper = new TransformerHelper();
167 
168         private Object result;
169 
170         private EvaluationCallback(XPath xpath, String expression, QName returnType) {
171             this.xpath = xpath;
172             this.expression = expression;
173             this.returnType = returnType;
174         }
175 
176         public void domSource(Node node) throws XPathExpressionException {
177             result = xpath.evaluate(expression, node, returnType);
178         }
179 
180         public void saxSource(XMLReader reader, InputSource inputSource) throws XPathExpressionException {
181             inputSource(inputSource);
182         }
183 
184         public void staxSource(XMLEventReader eventReader)
185                 throws XPathExpressionException, XMLStreamException, TransformerException {
186             Element element = getRootElement(StaxUtils.createCustomStaxSource(eventReader));
187             domSource(element);
188         }
189 
190         public void staxSource(XMLStreamReader streamReader) throws TransformerException, XPathExpressionException {
191             Element element = getRootElement(StaxUtils.createCustomStaxSource(streamReader));
192             domSource(element);
193         }
194 
195         public void streamSource(InputStream inputStream) throws XPathExpressionException {
196             inputSource(new InputSource(inputStream));
197         }
198 
199         public void streamSource(Reader reader) throws XPathExpressionException {
200             inputSource(new InputSource(reader));
201         }
202 
203         public void source(String systemId) throws XPathExpressionException {
204             inputSource(new InputSource(systemId));
205         }
206 
207         private void inputSource(InputSource inputSource) throws XPathExpressionException {
208             result = xpath.evaluate(expression, inputSource, returnType);
209         }
210 
211         private Element getRootElement(Source source) throws TransformerException {
212             DOMResult domResult = new DOMResult();
213             transformerHelper.transform(source, domResult);
214             Document document = (Document) domResult.getNode();
215             return document.getDocumentElement();
216         }
217 
218     }
219 
220 
221 
222 }