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