001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.model.language;
018
019import javax.xml.bind.annotation.XmlAccessType;
020import javax.xml.bind.annotation.XmlAccessorType;
021import javax.xml.bind.annotation.XmlAttribute;
022import javax.xml.bind.annotation.XmlRootElement;
023import javax.xml.bind.annotation.XmlTransient;
024import javax.xml.xpath.XPathFactory;
025
026import org.apache.camel.CamelContext;
027import org.apache.camel.Expression;
028import org.apache.camel.Predicate;
029import org.apache.camel.builder.xml.XPathBuilder;
030import org.apache.camel.util.ObjectHelper;
031
032/**
033 * For XPath expressions and predicates
034 */
035@XmlRootElement(name = "xpath")
036@XmlAccessorType(XmlAccessType.FIELD)
037public class XPathExpression extends NamespaceAwareExpression {
038    @XmlAttribute(name = "resultType")
039    private String resultTypeName;
040    @XmlAttribute(name = "saxon")
041    private Boolean saxon;
042    @XmlAttribute(name = "factoryRef")
043    private String factoryRef;
044    @XmlAttribute(name = "objectModel")
045    private String objectModel;
046    @XmlAttribute(name = "logNamespaces")
047    private Boolean logNamespaces;
048    @XmlAttribute(name = "headerName")
049    private String headerName;
050    
051    @XmlTransient
052    private Class<?> resultType;
053    @XmlTransient
054    private XPathFactory xpathFactory;
055    
056
057    public XPathExpression() {
058    }
059
060    public XPathExpression(String expression) {
061        super(expression);
062    }
063
064    public XPathExpression(Expression expression) {
065        setExpressionValue(expression);
066    }
067
068    public String getLanguage() {
069        return "xpath";
070    }
071
072    public Class<?> getResultType() {
073        return resultType;
074    }
075
076    public void setResultType(Class<?> resultType) {
077        this.resultType = resultType;
078    }
079
080    public String getResultTypeName() {
081        return resultTypeName;
082    }
083
084    public void setResultTypeName(String resultTypeName) {
085        this.resultTypeName = resultTypeName;
086    }
087
088    public void setSaxon(Boolean saxon) {
089        this.saxon = saxon;
090    }
091
092    public Boolean getSaxon() {
093        return saxon;
094    }
095
096    public boolean isSaxon() {
097        return saxon != null && saxon;
098    }
099
100    public void setFactoryRef(String factoryRef) {
101        this.factoryRef = factoryRef;
102    }
103
104    public String getFactoryRef() {
105        return factoryRef;
106    }
107
108    public void setObjectModel(String objectModel) {
109        this.objectModel = objectModel;
110    }
111
112    public String getObjectModel() {
113        return objectModel;
114    }
115
116    public void setLogNamespaces(Boolean logNamespaces) {
117        this.logNamespaces = logNamespaces;
118    }
119
120    public Boolean getLogNamespaces() {
121        return logNamespaces;
122    }
123
124    public boolean isLogNamespaces() {
125        return logNamespaces != null && logNamespaces;
126    }
127    
128    public String getHeaderName() {
129        return headerName;
130    }
131
132    public void setHeaderName(String headerName) {
133        this.headerName = headerName;
134    }
135
136    @Override
137    public Expression createExpression(CamelContext camelContext) {
138        if (resultType == null && resultTypeName != null) {
139            try {
140                resultType = camelContext.getClassResolver().resolveMandatoryClass(resultTypeName);
141            } catch (ClassNotFoundException e) {
142                throw ObjectHelper.wrapRuntimeCamelException(e);
143            }
144        }
145        resolveXPathFactory(camelContext);
146        return super.createExpression(camelContext);
147    }
148
149    @Override
150    public Predicate createPredicate(CamelContext camelContext) {
151        resolveXPathFactory(camelContext);
152        return super.createPredicate(camelContext);
153    }
154
155    @Override
156    protected void configureExpression(CamelContext camelContext, Expression expression) {
157        if (resultType != null) {
158            setProperty(expression, "resultType", resultType);
159        }
160        if (isSaxon()) {
161            ObjectHelper.cast(XPathBuilder.class, expression).enableSaxon();
162        }
163        if (xpathFactory != null) {
164            setProperty(expression, "xPathFactory", xpathFactory);
165        }
166        if (objectModel != null) {
167            setProperty(expression, "objectModelUri", objectModel);
168        }
169        if (isLogNamespaces()) {
170            ObjectHelper.cast(XPathBuilder.class, expression).setLogNamespaces(true);
171        }
172        if (ObjectHelper.isNotEmpty(getHeaderName())) {
173            ObjectHelper.cast(XPathBuilder.class, expression).setHeaderName(getHeaderName());
174        }
175        // moved the super configuration to the bottom so that the namespace init picks up the newly set XPath Factory
176        super.configureExpression(camelContext, expression);
177
178    }
179
180    @Override
181    protected void configurePredicate(CamelContext camelContext, Predicate predicate) {
182        if (resultType != null) {
183            setProperty(predicate, "resultType", resultType);
184        }
185        if (isSaxon()) {
186            ObjectHelper.cast(XPathBuilder.class, predicate).enableSaxon();
187        }
188        if (xpathFactory != null) {
189            setProperty(predicate, "xPathFactory", xpathFactory);
190        }
191        if (objectModel != null) {
192            setProperty(predicate, "objectModelUri", objectModel);
193        }
194        if (isLogNamespaces()) {
195            ObjectHelper.cast(XPathBuilder.class, predicate).setLogNamespaces(true);
196        }
197        if (ObjectHelper.isNotEmpty(getHeaderName())) {
198            ObjectHelper.cast(XPathBuilder.class, predicate).setHeaderName(getHeaderName());
199        }
200        // moved the super configuration to the bottom so that the namespace init picks up the newly set XPath Factory
201        super.configurePredicate(camelContext, predicate);
202    }
203
204    private void resolveXPathFactory(CamelContext camelContext) {
205        // Factory and Object Model can be set simultaneously. The underlying XPathBuilder allows for setting Saxon too, as it is simply a shortcut for
206        // setting the appropriate Object Model, it is not wise to allow this in XML because the order of invocation of the setters by JAXB may cause undeterministic behaviour 
207        if ((ObjectHelper.isNotEmpty(factoryRef) || ObjectHelper.isNotEmpty(objectModel)) && (saxon != null)) {
208            throw new IllegalArgumentException("The saxon attribute cannot be set on the xpath element if any of the following is also set: factory, objectModel" + this);
209        }
210
211        // Validate the factory class
212        if (ObjectHelper.isNotEmpty(factoryRef)) {
213            xpathFactory = camelContext.getRegistry().lookupByNameAndType(factoryRef, XPathFactory.class);
214            if (xpathFactory == null) {
215                throw new IllegalArgumentException("The provided XPath Factory is invalid; either it cannot be resolved or it is not an XPathFactory instance");
216            }
217        }
218    }
219}