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.saxon;
019    
020    import net.sf.saxon.Configuration;
021    import net.sf.saxon.om.DocumentInfo;
022    import net.sf.saxon.query.DynamicQueryContext;
023    import net.sf.saxon.query.StaticQueryContext;
024    import net.sf.saxon.query.XQueryExpression;
025    import net.sf.saxon.trans.XPathException;
026    import org.apache.camel.Exchange;
027    import org.apache.camel.Expression;
028    import org.apache.camel.Predicate;
029    import org.apache.camel.RuntimeExpressionException;
030    import org.apache.camel.util.ObjectHelper;
031    import org.apache.camel.converter.jaxp.BytesSource;
032    import org.apache.camel.converter.jaxp.XmlConverter;
033    import org.apache.camel.converter.jaxp.StringSource;
034    import org.w3c.dom.Node;
035    
036    import javax.xml.transform.Result;
037    import javax.xml.transform.Source;
038    import javax.xml.transform.dom.DOMResult;
039    import javax.xml.transform.stream.StreamResult;
040    import java.io.ByteArrayOutputStream;
041    import java.io.IOException;
042    import java.io.InputStream;
043    import java.io.Reader;
044    import java.io.StringWriter;
045    import java.util.HashMap;
046    import java.util.List;
047    import java.util.Map;
048    import java.util.Properties;
049    import java.util.Set;
050    
051    /**
052     * Creates an XQuery builder
053     *
054     * @version $Revision: 534053 $
055     */
056    public abstract class XQueryBuilder<E extends Exchange> implements Expression<E>, Predicate<E> {
057        private Configuration configuration;
058        private XQueryExpression expression;
059        private StaticQueryContext staticQueryContext;
060        private Map<String, Object> parameters = new HashMap<String, Object>();
061        private XmlConverter converter = new XmlConverter();
062        private ResultFormat resultsFormat = ResultFormat.DOM;
063        private Properties properties = new Properties();
064    
065        @Override
066        public String toString() {
067            return "XQuery[" + expression + "]";
068        }
069    
070        public Object evaluate(E exchange) {
071            try {
072                switch (resultsFormat) {
073                    case Bytes:
074                        return evaluateAsBytes(exchange);
075                    case BytesSource:
076                        return evaluateAsBytesSource(exchange);
077                    case DOM:
078                        return evaluateAsDOM(exchange);
079                    case List:
080                        return evaluateAsList(exchange);
081                    case StringSource:
082                        return evaluateAsStringSource(exchange);
083                    case String:
084                    default:
085                        return evaluateAsString(exchange);
086                }
087            }
088            catch (Exception e) {
089                throw new RuntimeExpressionException(e);
090            }
091        }
092    
093        public List evaluateAsList(E exchange) throws Exception {
094            return getExpression().evaluate(createDynamicContext(exchange));
095        }
096    
097        public Object evaluateAsStringSource(E exchange) throws Exception {
098            String text = evaluateAsString(exchange);
099            return new StringSource(text);
100        }
101    
102        public Object evaluateAsBytesSource(E exchange) throws Exception {
103            byte[] bytes = evaluateAsBytes(exchange);
104            return new BytesSource(bytes);
105        }
106    
107        public Node evaluateAsDOM(E exchange) throws Exception {
108            DOMResult result = new DOMResult();
109            getExpression().pull(createDynamicContext(exchange), result, properties);
110            return result.getNode();
111        }
112    
113        public byte[] evaluateAsBytes(E exchange) throws Exception {
114            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
115            Result result = new StreamResult(buffer);
116            getExpression().pull(createDynamicContext(exchange), result, properties);
117            byte[] bytes = buffer.toByteArray();
118            return bytes;
119        }
120    
121        public String evaluateAsString(E exchange) throws Exception {
122            StringWriter buffer = new StringWriter();
123            Result result = new StreamResult(buffer);
124            getExpression().pull(createDynamicContext(exchange), result, properties);
125            String text = buffer.toString();
126            return text;
127        }
128    
129        public boolean matches(E exchange) {
130            try {
131                List list = evaluateAsList(exchange);
132                return matches(exchange, list);
133            }
134            catch (Exception e) {
135                throw new RuntimeExpressionException(e);
136            }
137        }
138    
139        public void assertMatches(String text, E exchange) throws AssertionError {
140            try {
141                List list = evaluateAsList(exchange);
142                if (!matches(exchange, list)) {
143                    throw new AssertionError(this + " failed on " + exchange + " as evaluated: " + list);
144                }
145            }
146            catch (Exception e) {
147                throw new AssertionError(e);
148            }
149        }
150    
151        // Builder methods
152        //-------------------------------------------------------------------------
153        public static <E extends Exchange> XQueryBuilder<E> xquery(final String queryText) {
154            return new XQueryBuilder<E>() {
155                protected XQueryExpression createQueryExpression(StaticQueryContext staticQueryContext) throws XPathException {
156                    return staticQueryContext.compileQuery(queryText);
157                }
158            };
159        }
160    
161        public static <E extends Exchange> XQueryBuilder<E> xquery(final Reader reader) {
162            return new XQueryBuilder<E>() {
163                protected XQueryExpression createQueryExpression(StaticQueryContext staticQueryContext) throws XPathException, IOException {
164                    return staticQueryContext.compileQuery(reader);
165                }
166            };
167        }
168    
169        public static <E extends Exchange> XQueryBuilder<E> xquery(final InputStream in, final String characterSet) {
170            return new XQueryBuilder<E>() {
171                protected XQueryExpression createQueryExpression(StaticQueryContext staticQueryContext) throws XPathException, IOException {
172                    return staticQueryContext.compileQuery(in, characterSet);
173                }
174            };
175        }
176    
177        public XQueryBuilder<E> asBytes() {
178            setResultsFormat(ResultFormat.Bytes);
179            return this;
180        }
181    
182        public XQueryBuilder<E> asBytesSource() {
183            setResultsFormat(ResultFormat.BytesSource);
184            return this;
185        }
186    
187        public XQueryBuilder<E> asDOM() {
188            setResultsFormat(ResultFormat.DOM);
189            return this;
190        }
191    
192        public XQueryBuilder<E> asDOMSource() {
193            setResultsFormat(ResultFormat.DOMSource);
194            return this;
195        }
196    
197        public XQueryBuilder<E> asList() {
198            setResultsFormat(ResultFormat.List);
199            return this;
200        }
201    
202        public XQueryBuilder<E> asString() {
203            setResultsFormat(ResultFormat.String);
204            return this;
205        }
206    
207        public XQueryBuilder<E> asStringSource() {
208            setResultsFormat(ResultFormat.StringSource);
209            return this;
210        }
211    
212        public XQueryBuilder<E> parameter(String name, Object value) {
213            parameters.put(name, value);
214            return this;
215        }
216    
217        // Properties
218        //-------------------------------------------------------------------------
219    
220        public XQueryExpression getExpression() throws IOException, XPathException {
221            if (expression == null) {
222                expression = createQueryExpression(getStaticQueryContext());
223                clearBuilderReferences();
224            }
225            return expression;
226        }
227    
228        public Configuration getConfiguration() {
229            if (configuration == null) {
230                configuration = new Configuration();
231                configuration.setHostLanguage(Configuration.XQUERY);
232            }
233            return configuration;
234        }
235    
236        public void setConfiguration(Configuration configuration) {
237            this.configuration = configuration;
238        }
239    
240        public StaticQueryContext getStaticQueryContext() {
241            if (staticQueryContext == null) {
242                staticQueryContext = new StaticQueryContext(getConfiguration());
243            }
244            return staticQueryContext;
245        }
246    
247        public void setStaticQueryContext(StaticQueryContext staticQueryContext) {
248            this.staticQueryContext = staticQueryContext;
249        }
250    
251        public Map<String, Object> getParameters() {
252            return parameters;
253        }
254    
255        public void setParameters(Map<String, Object> parameters) {
256            this.parameters = parameters;
257        }
258    
259        public Properties getProperties() {
260            return properties;
261        }
262    
263        public void setProperties(Properties properties) {
264            this.properties = properties;
265        }
266    
267        public ResultFormat getResultsFormat() {
268            return resultsFormat;
269        }
270    
271        public void setResultsFormat(ResultFormat resultsFormat) {
272            this.resultsFormat = resultsFormat;
273        }
274    
275        // Implementation methods
276        // -------------------------------------------------------------------------
277    
278        /**
279         * A factory method to create the XQuery expression
280         */
281        protected abstract XQueryExpression createQueryExpression(StaticQueryContext staticQueryContext) throws XPathException, IOException;
282    
283        /**
284         * Creates a dynamic context for the given exchange
285         */
286        protected DynamicQueryContext createDynamicContext(E exchange) throws Exception {
287            Configuration config = getConfiguration();
288            DynamicQueryContext dynamicQueryContext = new DynamicQueryContext(config);
289    
290            Source source = exchange.getIn().getBody(Source.class);
291            if (source == null) {
292                source = converter.toSource(converter.createDocument());
293            }
294    
295            DocumentInfo doc = getStaticQueryContext().buildDocument(source);
296            dynamicQueryContext.setContextItem(doc);
297            configureQuery(dynamicQueryContext, exchange);
298            return dynamicQueryContext;
299        }
300    
301        /**
302         * Configures the dynamic context with exchange specific parameters
303         *
304         * @param dynamicQueryContext
305         * @param exchange
306         * @throws Exception
307         */
308        protected void configureQuery(DynamicQueryContext dynamicQueryContext, Exchange exchange) throws Exception {
309            addParameters(dynamicQueryContext, exchange.getProperties());
310            addParameters(dynamicQueryContext, exchange.getIn().getHeaders());
311            addParameters(dynamicQueryContext, getParameters());
312    
313            dynamicQueryContext.setParameter("exchange", exchange);
314            dynamicQueryContext.setParameter("in", exchange.getIn());
315            dynamicQueryContext.setParameter("out", exchange.getOut());
316        }
317    
318        protected void addParameters(DynamicQueryContext dynamicQueryContext, Map<String, Object> map) {
319            Set<Map.Entry<String, Object>> propertyEntries = map.entrySet();
320            for (Map.Entry<String, Object> entry : propertyEntries) {
321                dynamicQueryContext.setParameter(entry.getKey(), entry.getValue());
322            }
323        }
324    
325        /**
326         * To avoid keeping around any unnecessary objects after the expresion has been created lets
327         * nullify references here
328         */
329        protected void clearBuilderReferences() {
330            staticQueryContext = null;
331            configuration = null;
332        }
333    
334        protected boolean matches(E exchange, List results) {
335            return ObjectHelper.matches(results);
336        }
337    }