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     */
017    package org.apache.camel.model.language;
018    
019    import javax.xml.bind.annotation.XmlAccessType;
020    import javax.xml.bind.annotation.XmlAccessorType;
021    import javax.xml.bind.annotation.XmlAttribute;
022    import javax.xml.bind.annotation.XmlRootElement;
023    import javax.xml.bind.annotation.XmlTransient;
024    import javax.xml.xpath.XPathFactory;
025    
026    import org.apache.camel.CamelContext;
027    import org.apache.camel.Expression;
028    import org.apache.camel.Predicate;
029    import org.apache.camel.builder.xml.XPathBuilder;
030    import org.apache.camel.util.ObjectHelper;
031    
032    /**
033     * For XPath expressions and predicates
034     */
035    @XmlRootElement(name = "xpath")
036    @XmlAccessorType(XmlAccessType.FIELD)
037    public 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    
049        @XmlTransient
050        private Class<?> resultType;
051        @XmlTransient
052        private XPathFactory xpathFactory;
053    
054        public XPathExpression() {
055        }
056    
057        public XPathExpression(String expression) {
058            super(expression);
059        }
060    
061        public XPathExpression(Expression expression) {
062            setExpressionValue(expression);
063        }
064    
065        public String getLanguage() {
066            return "xpath";
067        }
068    
069        public Class<?> getResultType() {
070            return resultType;
071        }
072    
073        public void setResultType(Class<?> resultType) {
074            this.resultType = resultType;
075        }
076    
077        public String getResultTypeName() {
078            return resultTypeName;
079        }
080    
081        public void setResultTypeName(String resultTypeName) {
082            this.resultTypeName = resultTypeName;
083        }
084    
085        public void setSaxon(Boolean saxon) {
086            this.saxon = saxon;
087        }
088    
089        public Boolean getSaxon() {
090            return saxon;
091        }
092    
093        public boolean isSaxon() {
094            return saxon != null && saxon;
095        }
096    
097        public void setFactoryRef(String factoryRef) {
098            this.factoryRef = factoryRef;
099        }
100    
101        public String getFactoryRef() {
102            return factoryRef;
103        }
104    
105        public void setObjectModel(String objectModel) {
106            this.objectModel = objectModel;
107        }
108    
109        public String getObjectModel() {
110            return objectModel;
111        }
112    
113        public void setLogNamespaces(Boolean logNamespaces) {
114            this.logNamespaces = logNamespaces;
115        }
116    
117        public Boolean getLogNamespaces() {
118            return logNamespaces;
119        }
120    
121        public boolean isLogNamespaces() {
122            return logNamespaces != null && logNamespaces;
123        }
124    
125        @Override
126        public Expression createExpression(CamelContext camelContext) {
127            if (resultType == null && resultTypeName != null) {
128                try {
129                    resultType = camelContext.getClassResolver().resolveMandatoryClass(resultTypeName);
130                } catch (ClassNotFoundException e) {
131                    throw ObjectHelper.wrapRuntimeCamelException(e);
132                }
133            }
134            resolveXPathFactory(camelContext);
135            return super.createExpression(camelContext);
136        }
137    
138        @Override
139        public Predicate createPredicate(CamelContext camelContext) {
140            resolveXPathFactory(camelContext);
141            return super.createPredicate(camelContext);
142        }
143    
144        @Override
145        protected void configureExpression(CamelContext camelContext, Expression expression) {
146            if (resultType != null) {
147                setProperty(expression, "resultType", resultType);
148            }
149            if (isSaxon()) {
150                ObjectHelper.cast(XPathBuilder.class, expression).enableSaxon();
151            }
152            if (xpathFactory != null) {
153                setProperty(expression, "xPathFactory", xpathFactory);
154            }
155            if (objectModel != null) {
156                setProperty(expression, "objectModelUri", objectModel);
157            }
158            if (isLogNamespaces()) {
159                ObjectHelper.cast(XPathBuilder.class, expression).setLogNamespaces(true);
160            }
161            // moved the super configuration to the bottom so that the namespace init picks up the newly set XPath Factory
162            super.configureExpression(camelContext, expression);
163    
164        }
165    
166        @Override
167        protected void configurePredicate(CamelContext camelContext, Predicate predicate) {
168            if (resultType != null) {
169                setProperty(predicate, "resultType", resultType);
170            }
171            if (isSaxon()) {
172                ObjectHelper.cast(XPathBuilder.class, predicate).enableSaxon();
173            }
174            if (xpathFactory != null) {
175                setProperty(predicate, "xPathFactory", xpathFactory);
176            }
177            if (objectModel != null) {
178                setProperty(predicate, "objectModelUri", objectModel);
179            }
180            if (isLogNamespaces()) {
181                ObjectHelper.cast(XPathBuilder.class, predicate).setLogNamespaces(true);
182            }
183            // moved the super configuration to the bottom so that the namespace init picks up the newly set XPath Factory
184            super.configurePredicate(camelContext, predicate);
185        }
186    
187        private void resolveXPathFactory(CamelContext camelContext) {
188            // Factory and Object Model can be set simultaneously. The underlying XPathBuilder allows for setting Saxon too, as it is simply a shortcut for
189            // 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 
190            if ((ObjectHelper.isNotEmpty(factoryRef) || ObjectHelper.isNotEmpty(objectModel)) && (saxon != null)) {
191                throw new IllegalArgumentException("The saxon attribute cannot be set on the xpath element if any of the following is also set: factory, objectModel" + this);
192            }
193    
194            // Validate the factory class
195            if (ObjectHelper.isNotEmpty(factoryRef)) {
196                xpathFactory = camelContext.getRegistry().lookup(factoryRef, XPathFactory.class);
197                if (xpathFactory == null) {
198                    throw new IllegalArgumentException("The provided XPath Factory is invalid; either it cannot be resolved or it is not an XPathFactory instance");
199                }
200            }
201        }
202    }