001 /**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one or more
004 * contributor license agreements. See the NOTICE file distributed with
005 * this work for additional information regarding copyright ownership.
006 * The ASF licenses this file to You under the Apache License, Version 2.0
007 * (the "License"); you may not use this file except in compliance with
008 * the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.camel.builder.xpath;
019
020 import static org.apache.camel.converter.ObjectConverter.toBoolean;
021 import org.apache.camel.Exchange;
022 import org.apache.camel.Expression;
023 import org.apache.camel.Predicate;
024 import org.apache.camel.RuntimeExpressionException;
025 import org.apache.camel.Message;
026 import org.apache.camel.converter.ObjectConverter;
027 import org.w3c.dom.Document;
028 import org.xml.sax.InputSource;
029
030 import javax.xml.namespace.QName;
031 import javax.xml.xpath.XPath;
032 import javax.xml.xpath.XPathConstants;
033 import javax.xml.xpath.XPathExpression;
034 import javax.xml.xpath.XPathExpressionException;
035 import javax.xml.xpath.XPathFactory;
036 import javax.xml.xpath.XPathFactoryConfigurationException;
037 import javax.xml.xpath.XPathFunctionResolver;
038 import java.io.StringReader;
039
040 /**
041 * Creates an XPath expression builder
042 *
043 * @version $Revision: 531854 $
044 */
045 public class XPathBuilder<E extends Exchange> implements Expression<E>, Predicate<E> {
046 private final String text;
047 private XPathFactory xpathFactory;
048 private Class documentType = Document.class;
049 private QName resultType = null;
050 private String objectModelUri = null;
051 private DefaultNamespaceContext namespaceContext;
052 private XPathFunctionResolver functionResolver;
053 private XPathExpression expression;
054 private MessageVariableResolver variableResolver = new MessageVariableResolver();
055
056 public static XPathBuilder xpath(String text) {
057 return new XPathBuilder(text);
058 }
059
060 public XPathBuilder(String text) {
061 this.text = text;
062 }
063
064 @Override
065 public String toString() {
066 return "XPath: " + text;
067 }
068
069 public boolean matches(E exchange) {
070 Object booleanResult = evaluateAs(exchange, XPathConstants.BOOLEAN);
071 return toBoolean(booleanResult);
072 }
073
074 public void assertMatches(String text, E exchange) throws AssertionError {
075 Object booleanResult = evaluateAs(exchange, XPathConstants.BOOLEAN);
076 if (!toBoolean(booleanResult)) {
077 throw new AssertionError(this + " failed on " + exchange + " as returned <" + booleanResult + ">");
078 }
079 }
080
081 public Object evaluate(E exchange) {
082 return evaluateAs(exchange, resultType);
083 }
084
085
086 // Builder methods
087 //-------------------------------------------------------------------------
088
089 /**
090 * Sets the expression result type to boolean
091 *
092 * @return the current builder
093 */
094 public XPathBuilder<E> booleanResult() {
095 resultType = XPathConstants.BOOLEAN;
096 return this;
097 }
098
099 /**
100 * Sets the expression result type to boolean
101 *
102 * @return the current builder
103 */
104 public XPathBuilder<E> nodeResult() {
105 resultType = XPathConstants.NODE;
106 return this;
107 }
108
109 /**
110 * Sets the expression result type to boolean
111 *
112 * @return the current builder
113 */
114 public XPathBuilder<E> nodeSetResult() {
115 resultType = XPathConstants.NODESET;
116 return this;
117 }
118
119 /**
120 * Sets the expression result type to boolean
121 *
122 * @return the current builder
123 */
124 public XPathBuilder<E> numberResult() {
125 resultType = XPathConstants.NUMBER;
126 return this;
127 }
128
129 /**
130 * Sets the expression result type to boolean
131 *
132 * @return the current builder
133 */
134 public XPathBuilder<E> stringResult() {
135 resultType = XPathConstants.STRING;
136 return this;
137 }
138
139 /**
140 * Sets the object model URI to use
141 *
142 * @return the current builder
143 */
144 public XPathBuilder<E> objectModel(String uri) {
145 this.objectModelUri = uri;
146 return this;
147 }
148
149 /**
150 * Sets the {@link XPathFunctionResolver} instance to use on these XPath expressions
151 *
152 * @return the current builder
153 */
154 public XPathBuilder<E> functionResolver(XPathFunctionResolver functionResolver) {
155 this.functionResolver = functionResolver;
156 return this;
157 }
158
159 /**
160 * Registers the namespace prefix and URI with the builder so that the prefix can be used in XPath expressions
161 *
162 * @param prefix is the namespace prefix that can be used in the XPath expressions
163 * @param uri is the namespace URI to which the prefix refers
164 * @return the current builder
165 */
166 public XPathBuilder<E> namespace(String prefix, String uri) {
167 getNamespaceContext().add(prefix, uri);
168 return this;
169 }
170
171 /**
172 * Registers a variable (in the global namespace) which can be referred to from XPath expressions
173 */
174 public XPathBuilder<E> variable(String name, Object value) {
175 variableResolver.addVariable(name, value);
176 return this;
177 }
178
179
180 // Properties
181 //-------------------------------------------------------------------------
182 public XPathFactory getXPathFactory() throws XPathFactoryConfigurationException {
183 if (xpathFactory == null) {
184 if (objectModelUri != null) {
185 xpathFactory = XPathFactory.newInstance(objectModelUri);
186 }
187 xpathFactory = XPathFactory.newInstance();
188 }
189 return xpathFactory;
190 }
191
192 public void setXPathFactory(XPathFactory xpathFactory) {
193 this.xpathFactory = xpathFactory;
194 }
195
196 public Class getDocumentType() {
197 return documentType;
198 }
199
200 public void setDocumentType(Class documentType) {
201 this.documentType = documentType;
202 }
203
204 public String getText() {
205 return text;
206 }
207
208 public QName getResultType() {
209 return resultType;
210 }
211
212 public DefaultNamespaceContext getNamespaceContext() {
213 if (namespaceContext == null) {
214 try {
215 namespaceContext = new DefaultNamespaceContext(getXPathFactory());
216 }
217 catch (XPathFactoryConfigurationException e) {
218 throw new RuntimeExpressionException(e);
219 }
220 }
221 return namespaceContext;
222 }
223
224 public void setNamespaceContext(DefaultNamespaceContext namespaceContext) {
225 this.namespaceContext = namespaceContext;
226 }
227
228 public XPathFunctionResolver getFunctionResolver() {
229 return functionResolver;
230 }
231
232 public void setFunctionResolver(XPathFunctionResolver functionResolver) {
233 this.functionResolver = functionResolver;
234 }
235
236 public XPathExpression getExpression() throws XPathFactoryConfigurationException, XPathExpressionException {
237 if (expression == null) {
238 expression = createXPathExpression();
239 }
240 return expression;
241 }
242
243 // Implementation methods
244 //-------------------------------------------------------------------------
245
246
247 /**
248 * Evaluates the expression as the given result type
249 */
250 protected synchronized Object evaluateAs(E exchange, QName resultType) {
251 variableResolver.setExchange(exchange);
252 try {
253 Object document = getDocument(exchange);
254 if (resultType != null) {
255 if (document instanceof InputSource) {
256 InputSource inputSource = (InputSource) document;
257 return getExpression().evaluate(inputSource, resultType);
258 }
259 else {
260 return getExpression().evaluate(document, resultType);
261 }
262 }
263 else {
264 if (document instanceof InputSource) {
265 InputSource inputSource = (InputSource) document;
266 return getExpression().evaluate(inputSource);
267 }
268 else {
269 return getExpression().evaluate(document);
270 }
271 }
272 }
273 catch (XPathExpressionException e) {
274 throw new InvalidXPathExpression(getText(), e);
275 }
276 catch (XPathFactoryConfigurationException e) {
277 throw new InvalidXPathExpression(getText(), e);
278 }
279 }
280
281 protected XPathExpression createXPathExpression() throws XPathExpressionException, XPathFactoryConfigurationException {
282 XPath xPath = getXPathFactory().newXPath();
283
284 // lets now clear any factory references to avoid keeping them around
285 xpathFactory = null;
286
287 xPath.setNamespaceContext(getNamespaceContext());
288 xPath.setXPathVariableResolver(variableResolver);
289 if (functionResolver != null) {
290 xPath.setXPathFunctionResolver(functionResolver);
291 }
292 return xPath.compile(text);
293 }
294
295 /**
296 * Strategy method to extract the document from the exchange
297 */
298 protected Object getDocument(E exchange) {
299 Message in = exchange.getIn();
300 Class type = getDocumentType();
301 Object answer = null;
302 if (type != null) {
303 answer = in.getBody(type);
304 }
305 if (answer == null) {
306 answer = in.getBody();
307 }
308
309 // lets try coerce some common types into something JAXP can deal with
310 if (answer instanceof String) {
311 answer = new InputSource(new StringReader(answer.toString()));
312 }
313 return answer;
314 }
315
316
317 }