Coverage Report - org.apache.camel.builder.xml.XPathBuilder
 
Classes in this File Line Coverage Branch Coverage Complexity
XPathBuilder
55% 
69% 
0
 
 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  
 package org.apache.camel.builder.xml;
 18  
 
 19  
 import java.io.StringReader;
 20  
 import java.util.List;
 21  
 
 22  
 import javax.xml.namespace.QName;
 23  
 import javax.xml.xpath.XPath;
 24  
 import javax.xml.xpath.XPathConstants;
 25  
 import javax.xml.xpath.XPathExpression;
 26  
 import javax.xml.xpath.XPathExpressionException;
 27  
 import javax.xml.xpath.XPathFactory;
 28  
 import javax.xml.xpath.XPathFactoryConfigurationException;
 29  
 import javax.xml.xpath.XPathFunction;
 30  
 import javax.xml.xpath.XPathFunctionException;
 31  
 import javax.xml.xpath.XPathFunctionResolver;
 32  
 
 33  
 import org.w3c.dom.Document;
 34  
 import org.w3c.dom.Element;
 35  
 
 36  
 import org.xml.sax.InputSource;
 37  
 
 38  
 import org.apache.camel.Exchange;
 39  
 import org.apache.camel.Expression;
 40  
 import org.apache.camel.Message;
 41  
 import org.apache.camel.Predicate;
 42  
 import org.apache.camel.RuntimeExpressionException;
 43  
 
 44  
 import static org.apache.camel.builder.xml.Namespaces.DEFAULT_NAMESPACE;
 45  
 import static org.apache.camel.builder.xml.Namespaces.IN_NAMESPACE;
 46  
 import static org.apache.camel.builder.xml.Namespaces.OUT_NAMESPACE;
 47  
 import static org.apache.camel.builder.xml.Namespaces.isMatchingNamespaceOrEmptyNamespace;
 48  
 import static org.apache.camel.converter.ObjectConverter.toBoolean;
 49  
 
 50  
 /**
 51  
  * Creates an XPath expression builder
 52  
  * 
 53  
  * @version $Revision: 531854 $
 54  
  */
 55  87
 public class XPathBuilder<E extends Exchange> implements Expression<E>, Predicate<E> {
 56  
     private final String text;
 57  
     private XPathFactory xpathFactory;
 58  66
     private Class documentType = Document.class;
 59  
     private QName resultType;
 60  
     private String objectModelUri;
 61  
     private DefaultNamespaceContext namespaceContext;
 62  
     private XPathFunctionResolver functionResolver;
 63  
     private XPathExpression expression;
 64  66
     private MessageVariableResolver variableResolver = new MessageVariableResolver();
 65  
     private E exchange;
 66  
     private XPathFunction bodyFunction;
 67  
     private XPathFunction headerFunction;
 68  
     private XPathFunction outBodyFunction;
 69  
     private XPathFunction outHeaderFunction;
 70  
 
 71  66
     public XPathBuilder(String text) {
 72  66
         this.text = text;
 73  66
     }
 74  
 
 75  
     public static XPathBuilder xpath(String text) {
 76  66
         return new XPathBuilder(text);
 77  
     }
 78  
 
 79  
     @Override
 80  
     public String toString() {
 81  123
         return "XPath: " + text;
 82  
     }
 83  
 
 84  
     public boolean matches(E exchange) {
 85  45
         Object booleanResult = evaluateAs(exchange, XPathConstants.BOOLEAN);
 86  45
         return toBoolean(booleanResult);
 87  
     }
 88  
 
 89  
     public void assertMatches(String text, E exchange) throws AssertionError {
 90  18
         Object booleanResult = evaluateAs(exchange, XPathConstants.BOOLEAN);
 91  18
         if (!toBoolean(booleanResult)) {
 92  3
             throw new AssertionError(this + " failed on " + exchange + " as returned <" + booleanResult + ">");
 93  
         }
 94  15
     }
 95  
 
 96  
     public Object evaluate(E exchange) {
 97  24
         return evaluateAs(exchange, resultType);
 98  
     }
 99  
 
 100  
     // Builder methods
 101  
     // -------------------------------------------------------------------------
 102  
 
 103  
     /**
 104  
      * Sets the expression result type to boolean
 105  
      * 
 106  
      * @return the current builder
 107  
      */
 108  
     public XPathBuilder<E> booleanResult() {
 109  0
         resultType = XPathConstants.BOOLEAN;
 110  0
         return this;
 111  
     }
 112  
 
 113  
     /**
 114  
      * Sets the expression result type to boolean
 115  
      * 
 116  
      * @return the current builder
 117  
      */
 118  
     public XPathBuilder<E> nodeResult() {
 119  0
         resultType = XPathConstants.NODE;
 120  0
         return this;
 121  
     }
 122  
 
 123  
     /**
 124  
      * Sets the expression result type to boolean
 125  
      * 
 126  
      * @return the current builder
 127  
      */
 128  
     public XPathBuilder<E> nodeSetResult() {
 129  0
         resultType = XPathConstants.NODESET;
 130  0
         return this;
 131  
     }
 132  
 
 133  
     /**
 134  
      * Sets the expression result type to boolean
 135  
      * 
 136  
      * @return the current builder
 137  
      */
 138  
     public XPathBuilder<E> numberResult() {
 139  0
         resultType = XPathConstants.NUMBER;
 140  0
         return this;
 141  
     }
 142  
 
 143  
     /**
 144  
      * Sets the expression result type to boolean
 145  
      * 
 146  
      * @return the current builder
 147  
      */
 148  
     public XPathBuilder<E> stringResult() {
 149  0
         resultType = XPathConstants.STRING;
 150  0
         return this;
 151  
     }
 152  
 
 153  
     /**
 154  
      * Sets the object model URI to use
 155  
      * 
 156  
      * @return the current builder
 157  
      */
 158  
     public XPathBuilder<E> objectModel(String uri) {
 159  0
         this.objectModelUri = uri;
 160  0
         return this;
 161  
     }
 162  
 
 163  
     /**
 164  
      * Sets the {@link XPathFunctionResolver} instance to use on these XPath
 165  
      * expressions
 166  
      * 
 167  
      * @return the current builder
 168  
      */
 169  
     public XPathBuilder<E> functionResolver(XPathFunctionResolver functionResolver) {
 170  0
         this.functionResolver = functionResolver;
 171  0
         return this;
 172  
     }
 173  
 
 174  
     /**
 175  
      * Registers the namespace prefix and URI with the builder so that the
 176  
      * prefix can be used in XPath expressions
 177  
      * 
 178  
      * @param prefix is the namespace prefix that can be used in the XPath
 179  
      *                expressions
 180  
      * @param uri is the namespace URI to which the prefix refers
 181  
      * @return the current builder
 182  
      */
 183  
     public XPathBuilder<E> namespace(String prefix, String uri) {
 184  18
         getNamespaceContext().add(prefix, uri);
 185  18
         return this;
 186  
     }
 187  
 
 188  
     /**
 189  
      * Registers a variable (in the global namespace) which can be referred to
 190  
      * from XPath expressions
 191  
      */
 192  
     public XPathBuilder<E> variable(String name, Object value) {
 193  3
         variableResolver.addVariable(name, value);
 194  3
         return this;
 195  
     }
 196  
 
 197  
     // Properties
 198  
     // -------------------------------------------------------------------------
 199  
     public XPathFactory getXPathFactory() throws XPathFactoryConfigurationException {
 200  132
         if (xpathFactory == null) {
 201  117
             if (objectModelUri != null) {
 202  0
                 xpathFactory = XPathFactory.newInstance(objectModelUri);
 203  
             }
 204  117
             xpathFactory = XPathFactory.newInstance();
 205  
         }
 206  132
         return xpathFactory;
 207  
     }
 208  
 
 209  
     public void setXPathFactory(XPathFactory xpathFactory) {
 210  0
         this.xpathFactory = xpathFactory;
 211  0
     }
 212  
 
 213  
     public Class getDocumentType() {
 214  87
         return documentType;
 215  
     }
 216  
 
 217  
     public void setDocumentType(Class documentType) {
 218  0
         this.documentType = documentType;
 219  0
     }
 220  
 
 221  
     public String getText() {
 222  0
         return text;
 223  
     }
 224  
 
 225  
     public QName getResultType() {
 226  0
         return resultType;
 227  
     }
 228  
 
 229  
     public DefaultNamespaceContext getNamespaceContext() {
 230  90
         if (namespaceContext == null) {
 231  
             try {
 232  66
                 DefaultNamespaceContext defaultNamespaceContext = new DefaultNamespaceContext(
 233  
                                                                                               getXPathFactory());
 234  66
                 populateDefaultNamespaces(defaultNamespaceContext);
 235  66
                 namespaceContext = defaultNamespaceContext;
 236  0
             } catch (XPathFactoryConfigurationException e) {
 237  0
                 throw new RuntimeExpressionException(e);
 238  66
             }
 239  
         }
 240  90
         return namespaceContext;
 241  
     }
 242  
 
 243  
     public void setNamespaceContext(DefaultNamespaceContext namespaceContext) {
 244  0
         this.namespaceContext = namespaceContext;
 245  0
     }
 246  
 
 247  
     public XPathFunctionResolver getFunctionResolver() {
 248  66
         return functionResolver;
 249  
     }
 250  
 
 251  
     public void setFunctionResolver(XPathFunctionResolver functionResolver) {
 252  0
         this.functionResolver = functionResolver;
 253  0
     }
 254  
 
 255  
     public XPathExpression getExpression() throws XPathFactoryConfigurationException,
 256  
         XPathExpressionException {
 257  87
         if (expression == null) {
 258  66
             expression = createXPathExpression();
 259  
         }
 260  87
         return expression;
 261  
     }
 262  
 
 263  
     public void setNamespacesFromDom(Element node) {
 264  3
         getNamespaceContext().setNamespacesFromDom(node);
 265  3
     }
 266  
 
 267  
     public XPathFunction getBodyFunction() {
 268  3
         if (bodyFunction == null) {
 269  3
             bodyFunction = new XPathFunction() {
 270  3
                 public Object evaluate(List list) throws XPathFunctionException {
 271  3
                     if (exchange == null) {
 272  0
                         return null;
 273  
                     }
 274  3
                     return exchange.getIn().getBody();
 275  
                 }
 276  
             };
 277  
         }
 278  3
         return bodyFunction;
 279  
     }
 280  
 
 281  
     public void setBodyFunction(XPathFunction bodyFunction) {
 282  0
         this.bodyFunction = bodyFunction;
 283  0
     }
 284  
 
 285  
     public XPathFunction getHeaderFunction() {
 286  9
         if (headerFunction == null) {
 287  6
             headerFunction = new XPathFunction() {
 288  6
                 public Object evaluate(List list) throws XPathFunctionException {
 289  9
                     if (exchange != null && !list.isEmpty()) {
 290  9
                         Object value = list.get(0);
 291  9
                         if (value != null) {
 292  9
                             return exchange.getIn().getHeader(value.toString());
 293  
                         }
 294  
                     }
 295  0
                     return null;
 296  
                 }
 297  
             };
 298  
         }
 299  9
         return headerFunction;
 300  
     }
 301  
 
 302  
     public void setHeaderFunction(XPathFunction headerFunction) {
 303  0
         this.headerFunction = headerFunction;
 304  0
     }
 305  
 
 306  
     public XPathFunction getOutBodyFunction() {
 307  0
         if (outBodyFunction == null) {
 308  0
             outBodyFunction = new XPathFunction() {
 309  0
                 public Object evaluate(List list) throws XPathFunctionException {
 310  0
                     if (exchange == null) {
 311  0
                         return null;
 312  
                     }
 313  0
                     return exchange.getOut().getBody();
 314  
                 }
 315  
             };
 316  
         }
 317  0
         return outBodyFunction;
 318  
     }
 319  
 
 320  
     public void setOutBodyFunction(XPathFunction outBodyFunction) {
 321  0
         this.outBodyFunction = outBodyFunction;
 322  0
     }
 323  
 
 324  
     public XPathFunction getOutHeaderFunction() {
 325  0
         if (outHeaderFunction == null) {
 326  0
             outHeaderFunction = new XPathFunction() {
 327  0
                 public Object evaluate(List list) throws XPathFunctionException {
 328  0
                     if (exchange != null && !list.isEmpty()) {
 329  0
                         Object value = list.get(0);
 330  0
                         if (value != null) {
 331  0
                             return exchange.getOut().getHeader(value.toString());
 332  
                         }
 333  
                     }
 334  0
                     return null;
 335  
                 }
 336  
             };
 337  
         }
 338  0
         return outHeaderFunction;
 339  
     }
 340  
 
 341  
     public void setOutHeaderFunction(XPathFunction outHeaderFunction) {
 342  0
         this.outHeaderFunction = outHeaderFunction;
 343  0
     }
 344  
 
 345  
     // Implementation methods
 346  
     // -------------------------------------------------------------------------
 347  
 
 348  
     /**
 349  
      * Evaluates the expression as the given result type
 350  
      */
 351  
     protected synchronized Object evaluateAs(E exchange, QName resultType) {
 352  87
         this.exchange = exchange;
 353  87
         variableResolver.setExchange(exchange);
 354  
         try {
 355  87
             Object document = getDocument(exchange);
 356  87
             if (resultType != null) {
 357  63
                 if (document instanceof InputSource) {
 358  0
                     InputSource inputSource = (InputSource)document;
 359  0
                     return getExpression().evaluate(inputSource, resultType);
 360  
                 } else {
 361  63
                     return getExpression().evaluate(document, resultType);
 362  
                 }
 363  
             } else {
 364  24
                 if (document instanceof InputSource) {
 365  0
                     InputSource inputSource = (InputSource)document;
 366  0
                     return getExpression().evaluate(inputSource);
 367  
                 } else {
 368  24
                     return getExpression().evaluate(document);
 369  
                 }
 370  
             }
 371  0
         } catch (XPathExpressionException e) {
 372  0
             throw new InvalidXPathExpression(getText(), e);
 373  0
         } catch (XPathFactoryConfigurationException e) {
 374  0
             throw new InvalidXPathExpression(getText(), e);
 375  
         }
 376  
     }
 377  
 
 378  
     protected XPathExpression createXPathExpression() throws XPathExpressionException,
 379  
         XPathFactoryConfigurationException {
 380  66
         XPath xPath = getXPathFactory().newXPath();
 381  
 
 382  
         // lets now clear any factory references to avoid keeping them around
 383  66
         xpathFactory = null;
 384  
 
 385  66
         xPath.setNamespaceContext(getNamespaceContext());
 386  
 
 387  66
         xPath.setXPathVariableResolver(variableResolver);
 388  
 
 389  66
         XPathFunctionResolver parentResolver = getFunctionResolver();
 390  66
         if (parentResolver == null) {
 391  66
             parentResolver = xPath.getXPathFunctionResolver();
 392  
         }
 393  66
         xPath.setXPathFunctionResolver(createDefaultFunctionResolver(parentResolver));
 394  66
         return xPath.compile(text);
 395  
     }
 396  
 
 397  
     /**
 398  
      * Lets populate a number of standard prefixes if they are not already there
 399  
      */
 400  
     protected void populateDefaultNamespaces(DefaultNamespaceContext context) {
 401  66
         setNamespaceIfNotPresent(context, "in", IN_NAMESPACE);
 402  66
         setNamespaceIfNotPresent(context, "out", OUT_NAMESPACE);
 403  66
         setNamespaceIfNotPresent(context, "env", Namespaces.ENVIRONMENT_VARIABLES);
 404  66
         setNamespaceIfNotPresent(context, "system", Namespaces.SYSTEM_PROPERTIES_NAMESPACE);
 405  66
     }
 406  
 
 407  
     protected void setNamespaceIfNotPresent(DefaultNamespaceContext context, String prefix, String uri) {
 408  264
         if (context != null) {
 409  264
             String current = context.getNamespaceURI(prefix);
 410  264
             if (current == null) {
 411  264
                 context.add(prefix, uri);
 412  
             }
 413  
         }
 414  264
     }
 415  
 
 416  
     protected XPathFunctionResolver createDefaultFunctionResolver(final XPathFunctionResolver parent) {
 417  66
         return new XPathFunctionResolver() {
 418  66
             public XPathFunction resolveFunction(QName qName, int argumentCount) {
 419  12
                 XPathFunction answer = null;
 420  12
                 if (parent != null) {
 421  0
                     answer = parent.resolveFunction(qName, argumentCount);
 422  
                 }
 423  12
                 if (answer == null) {
 424  12
                     if (isMatchingNamespaceOrEmptyNamespace(qName.getNamespaceURI(), IN_NAMESPACE)
 425  
                         || isMatchingNamespaceOrEmptyNamespace(qName.getNamespaceURI(), DEFAULT_NAMESPACE)) {
 426  12
                         String localPart = qName.getLocalPart();
 427  12
                         if (localPart.equals("body") && argumentCount == 0) {
 428  3
                             return getBodyFunction();
 429  
                         }
 430  9
                         if (localPart.equals("header") && argumentCount == 1) {
 431  9
                             return getHeaderFunction();
 432  
                         }
 433  
                     }
 434  0
                     if (isMatchingNamespaceOrEmptyNamespace(qName.getNamespaceURI(), OUT_NAMESPACE)) {
 435  0
                         String localPart = qName.getLocalPart();
 436  0
                         if (localPart.equals("body") && argumentCount == 0) {
 437  0
                             return getOutBodyFunction();
 438  
                         }
 439  0
                         if (localPart.equals("header") && argumentCount == 1) {
 440  0
                             return getOutHeaderFunction();
 441  
                         }
 442  
                     }
 443  0
                     if (isMatchingNamespaceOrEmptyNamespace(qName.getNamespaceURI(), DEFAULT_NAMESPACE)) {
 444  0
                         String localPart = qName.getLocalPart();
 445  0
                         if (localPart.equals("out-body") && argumentCount == 0) {
 446  0
                             return getOutBodyFunction();
 447  
                         }
 448  0
                         if (localPart.equals("out-header") && argumentCount == 1) {
 449  0
                             return getOutHeaderFunction();
 450  
                         }
 451  
                     }
 452  
                 }
 453  0
                 return answer;
 454  
             }
 455  
         };
 456  
     }
 457  
 
 458  
     /**
 459  
      * Strategy method to extract the document from the exchange
 460  
      */
 461  
     protected Object getDocument(E exchange) {
 462  87
         Message in = exchange.getIn();
 463  87
         Class type = getDocumentType();
 464  87
         Object answer = null;
 465  87
         if (type != null) {
 466  87
             answer = in.getBody(type);
 467  
         }
 468  87
         if (answer == null) {
 469  0
             answer = in.getBody();
 470  
         }
 471  
 
 472  
         // lets try coerce some common types into something JAXP can deal with
 473  87
         if (answer instanceof String) {
 474  0
             answer = new InputSource(new StringReader(answer.toString()));
 475  
         }
 476  87
         return answer;
 477  
     }
 478  
 
 479  
 }