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.builder;
018
019import java.io.PrintWriter;
020import java.io.StringWriter;
021import java.text.SimpleDateFormat;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.Comparator;
025import java.util.Date;
026import java.util.Iterator;
027import java.util.List;
028import java.util.Random;
029import java.util.Scanner;
030import java.util.concurrent.atomic.AtomicReference;
031import java.util.regex.Pattern;
032
033import org.apache.camel.CamelContext;
034import org.apache.camel.Component;
035import org.apache.camel.Endpoint;
036import org.apache.camel.Exchange;
037import org.apache.camel.Expression;
038import org.apache.camel.InvalidPayloadException;
039import org.apache.camel.Message;
040import org.apache.camel.NoSuchEndpointException;
041import org.apache.camel.NoSuchLanguageException;
042import org.apache.camel.Producer;
043import org.apache.camel.component.bean.BeanInvocation;
044import org.apache.camel.component.properties.PropertiesComponent;
045import org.apache.camel.language.bean.BeanLanguage;
046import org.apache.camel.model.language.MethodCallExpression;
047import org.apache.camel.spi.Language;
048import org.apache.camel.spi.RouteContext;
049import org.apache.camel.spi.UnitOfWork;
050import org.apache.camel.support.ExpressionAdapter;
051import org.apache.camel.support.TokenPairExpressionIterator;
052import org.apache.camel.support.TokenXMLExpressionIterator;
053import org.apache.camel.support.XMLTokenExpressionIterator;
054import org.apache.camel.util.ExchangeHelper;
055import org.apache.camel.util.FileUtil;
056import org.apache.camel.util.GroupIterator;
057import org.apache.camel.util.IOHelper;
058import org.apache.camel.util.ObjectHelper;
059import org.apache.camel.util.OgnlHelper;
060
061/**
062 * A helper class for working with <a href="http://camel.apache.org/expression.html">expressions</a>.
063 *
064 * @version 
065 */
066public final class ExpressionBuilder {
067
068    /**
069     * Utility classes should not have a public constructor.
070     */
071    private ExpressionBuilder() {
072    }
073    
074    /**
075     * Returns an expression for the inbound message attachments
076     *
077     * @return an expression object which will return the inbound message attachments
078     */
079    public static Expression attachmentsExpression() {
080        return new ExpressionAdapter() {
081            public Object evaluate(Exchange exchange) {
082                return exchange.getIn().getAttachments();
083            }
084
085            @Override
086            public String toString() {
087                return "attachments";
088            }
089        };
090    }
091
092    /**
093     * Returns an expression for the inbound message attachments
094     *
095     * @return an expression object which will return the inbound message attachments
096     */
097    public static Expression attachmentValuesExpression() {
098        return new ExpressionAdapter() {
099            public Object evaluate(Exchange exchange) {
100                return exchange.getIn().getAttachments().values();
101            }
102
103            @Override
104            public String toString() {
105                return "attachments";
106            }
107        };
108    }
109
110    /**
111     * Returns an expression for the header value with the given name
112     * <p/>
113     * Will fallback and look in properties if not found in headers.
114     *
115     * @param headerName the name of the header the expression will return
116     * @return an expression object which will return the header value
117     */
118    public static Expression headerExpression(final String headerName) {
119        return new ExpressionAdapter() {
120            public Object evaluate(Exchange exchange) {
121                Object header = exchange.getIn().getHeader(headerName);
122                if (header == null) {
123                    // fall back on a property
124                    header = exchange.getProperty(headerName);
125                }
126                return header;
127            }
128
129            @Override
130            public String toString() {
131                return "header(" + headerName + ")";
132            }
133        };
134    }
135
136    /**
137     * Returns an expression for the header value with the given name converted to the given type
138     * <p/>
139     * Will fallback and look in properties if not found in headers.
140     *
141     * @param headerName the name of the header the expression will return
142     * @param type the type to convert to
143     * @return an expression object which will return the header value
144     */
145    public static <T> Expression headerExpression(final String headerName, final Class<T> type) {
146        return new ExpressionAdapter() {
147            public Object evaluate(Exchange exchange) {
148                Object header = exchange.getIn().getHeader(headerName, type);
149                if (header == null) {
150                    // fall back on a property
151                    header = exchange.getProperty(headerName, type);
152                }
153                return header;
154            }
155
156            @Override
157            public String toString() {
158                return "headerAs(" + headerName + ", " + type + ")";
159            }
160        };
161    }
162
163    /**
164     * Returns an expression for the header value with the given name converted to the given type
165     * <p/>
166     * Will fallback and look in properties if not found in headers.
167     *
168     * @param headerName the name of the header the expression will return
169     * @param name the type to convert to as a FQN class name
170     * @return an expression object which will return the header value
171     */
172    public static Expression headerExpression(final String headerName, final String name) {
173        return new ExpressionAdapter() {
174            public Object evaluate(Exchange exchange) {
175                Class<?> type;
176                try {
177                    type = exchange.getContext().getClassResolver().resolveMandatoryClass(name);
178                } catch (ClassNotFoundException e) {
179                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
180                }
181
182                Object header = exchange.getIn().getHeader(headerName, type);
183                if (header == null) {
184                    // fall back on a property
185                    header = exchange.getProperty(headerName, type);
186                }
187                return header;
188            }
189
190            @Override
191            public String toString() {
192                return "headerAs(" + headerName + ", " + name + ")";
193            }
194        };
195    }
196
197    /**
198     * Returns the expression for the exchanges inbound message header invoking methods defined
199     * in a simple OGNL notation
200     *
201     * @param ognl  methods to invoke on the header in a simple OGNL syntax
202     */
203    public static Expression headersOgnlExpression(final String ognl) {
204        return new KeyedOgnlExpressionAdapter(ognl, "headerOgnl(" + ognl + ")",
205            new KeyedOgnlExpressionAdapter.KeyedEntityRetrievalStrategy() {
206                public Object getKeyedEntity(Exchange exchange, String key) {
207                    return exchange.getIn().getHeader(key);
208                }
209            });
210    }
211
212    /**
213     * Returns an expression for the inbound message headers
214     *
215     * @return an expression object which will return the inbound headers
216     */
217    public static Expression headersExpression() {
218        return new ExpressionAdapter() {
219            public Object evaluate(Exchange exchange) {
220                return exchange.getIn().getHeaders();
221            }
222
223            @Override
224            public String toString() {
225                return "headers";
226            }
227        };
228    }
229
230    /**
231     * Returns an expression for the out header value with the given name
232     * <p/>
233     * Will fallback and look in properties if not found in headers.
234     *
235     * @param headerName the name of the header the expression will return
236     * @return an expression object which will return the header value
237     */
238    public static Expression outHeaderExpression(final String headerName) {
239        return new ExpressionAdapter() {
240            public Object evaluate(Exchange exchange) {
241                if (!exchange.hasOut()) {
242                    return null;
243                }
244
245                Message out = exchange.getOut();
246                Object header = out.getHeader(headerName);
247                if (header == null) {
248                    // let's try the exchange header
249                    header = exchange.getProperty(headerName);
250                }
251                return header;
252            }
253
254            @Override
255            public String toString() {
256                return "outHeader(" + headerName + ")";
257            }
258        };
259    }
260
261    /**
262     * Returns an expression for the outbound message headers
263     *
264     * @return an expression object which will return the headers, will be <tt>null</tt> if the
265     * exchange is not out capable.
266     */
267    public static Expression outHeadersExpression() {
268        return new ExpressionAdapter() {
269            public Object evaluate(Exchange exchange) {
270                // only get out headers if the MEP is out capable
271                if (ExchangeHelper.isOutCapable(exchange)) {
272                    return exchange.getOut().getHeaders();
273                } else {
274                    return null;
275                }
276            }
277
278            @Override
279            public String toString() {
280                return "outHeaders";
281            }
282        };
283    }
284
285    /**
286     * Returns an expression for the exchange pattern
287     *
288     * @see org.apache.camel.Exchange#getPattern()
289     * @return an expression object which will return the exchange pattern
290     */
291    public static Expression exchangePatternExpression() {
292        return new ExpressionAdapter() {
293            public Object evaluate(Exchange exchange) {
294                return exchange.getPattern();
295            }
296
297            @Override
298            public String toString() {
299                return "exchangePattern";
300            }
301        };
302    }   
303    
304    /**
305     * Returns an expression for an exception set on the exchange
306     *
307     * @see Exchange#getException()
308     * @return an expression object which will return the exception set on the exchange
309     */
310    public static Expression exchangeExceptionExpression() {
311        return new ExpressionAdapter() {
312            public Object evaluate(Exchange exchange) {
313                Exception exception = exchange.getException();
314                if (exception == null) {
315                    exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
316                }
317                return exception;
318            }
319
320            @Override
321            public String toString() {
322                return "exchangeException";
323            }
324        };
325    }
326
327    /**
328     * Returns an expression for an exception set on the exchange
329     * <p/>
330     * Is used to get the caused exception that typically have been wrapped in some sort
331     * of Camel wrapper exception
332     * @param type the exception type
333     * @see Exchange#getException(Class)
334     * @return an expression object which will return the exception set on the exchange
335     */
336    public static Expression exchangeExceptionExpression(final Class<Exception> type) {
337        return new ExpressionAdapter() {
338            public Object evaluate(Exchange exchange) {
339                Exception exception = exchange.getException(type);
340                if (exception == null) {
341                    exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
342                    return ObjectHelper.getException(type, exception);
343                }
344                return exception;
345            }
346
347            @Override
348            public String toString() {
349                return "exchangeException[" + type + "]";
350            }
351        };
352    }
353    
354    /**
355     * Returns the expression for the exchanges exception invoking methods defined
356     * in a simple OGNL notation
357     *
358     * @param ognl  methods to invoke on the body in a simple OGNL syntax
359     */
360    public static Expression exchangeExceptionOgnlExpression(final String ognl) {
361        return new ExpressionAdapter() {
362            public Object evaluate(Exchange exchange) {
363                Object exception = exchange.getException();
364                if (exception == null) {
365                    exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
366                }
367                
368                if (exception == null) {
369                    return null;
370                }
371                return new MethodCallExpression(exception, ognl).evaluate(exchange);
372            }
373
374            @Override
375            public String toString() {
376                return "exchangeExceptionOgnl(" + ognl + ")";
377            }
378        };
379    }
380
381    /**
382     * Returns an expression for the type converter
383     *
384     * @return an expression object which will return the type converter
385     */
386    public static Expression typeConverterExpression() {
387        return new ExpressionAdapter() {
388            public Object evaluate(Exchange exchange) {
389                return exchange.getContext().getTypeConverter();
390            }
391
392            @Override
393            public String toString() {
394                return "typeConverter";
395            }
396        };
397    }
398
399    /**
400     * Returns an expression for the {@link org.apache.camel.spi.Registry}
401     *
402     * @return an expression object which will return the registry
403     */
404    public static Expression registryExpression() {
405        return new ExpressionAdapter() {
406            public Object evaluate(Exchange exchange) {
407                return exchange.getContext().getRegistry();
408            }
409
410            @Override
411            public String toString() {
412                return "registry";
413            }
414        };
415    }
416
417    /**
418     * Returns an expression for lookup a bean in the {@link org.apache.camel.spi.Registry}
419     *
420     * @return an expression object which will return the bean
421     */
422    public static Expression refExpression(final String ref) {
423        return new ExpressionAdapter() {
424            public Object evaluate(Exchange exchange) {
425                return exchange.getContext().getRegistry().lookupByName(ref);
426            }
427
428            @Override
429            public String toString() {
430                return "ref(" + ref + ")";
431            }
432        };
433    }
434
435    /**
436     * Returns an expression for the {@link org.apache.camel.CamelContext}
437     *
438     * @return an expression object which will return the camel context
439     */
440    public static Expression camelContextExpression() {
441        return new ExpressionAdapter() {
442            public Object evaluate(Exchange exchange) {
443                return exchange.getContext();
444            }
445
446            @Override
447            public String toString() {
448                return "camelContext";
449            }
450        };
451    }
452
453    /**
454     * Returns an expression for the {@link org.apache.camel.CamelContext} name
455     *
456     * @return an expression object which will return the camel context name
457     */
458    public static Expression camelContextNameExpression() {
459        return new ExpressionAdapter() {
460            public Object evaluate(Exchange exchange) {
461                return exchange.getContext().getName();
462            }
463
464            @Override
465            public String toString() {
466                return "camelContextName";
467            }
468        };
469    }
470
471    /**
472     * Returns an expression for an exception message set on the exchange
473     *
474     * @see <tt>Exchange.getException().getMessage()</tt>
475     * @return an expression object which will return the exception message set on the exchange
476     */
477    public static Expression exchangeExceptionMessageExpression() {
478        return new ExpressionAdapter() {
479            public Object evaluate(Exchange exchange) {
480                Exception exception = exchange.getException();
481                if (exception == null) {
482                    exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
483                }
484                return exception != null ? exception.getMessage() : null;
485            }
486
487            @Override
488            public String toString() {
489                return "exchangeExceptionMessage";
490            }
491        };
492    }
493
494    /**
495     * Returns an expression for an exception stacktrace set on the exchange
496     *
497     * @return an expression object which will return the exception stacktrace set on the exchange
498     */
499    public static Expression exchangeExceptionStackTraceExpression() {
500        return new ExpressionAdapter() {
501            public Object evaluate(Exchange exchange) {
502                Exception exception = exchange.getException();
503                if (exception == null) {
504                    exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
505                }
506                if (exception != null) {
507                    StringWriter sw = new StringWriter();
508                    PrintWriter pw = new PrintWriter(sw);
509                    exception.printStackTrace(pw);
510                    IOHelper.close(pw, sw);
511                    return sw.toString();
512                } else {
513                    return null;
514                }
515            }
516
517            @Override
518            public String toString() {
519                return "exchangeExceptionStackTrace";
520            }
521        };
522    }
523
524    /**
525     * Returns an expression for the property value of exchange with the given name
526     *
527     * @param propertyName the name of the property the expression will return
528     * @return an expression object which will return the property value
529     * @deprecated use {@link #exchangePropertyExpression(String)} instead
530     */
531    @Deprecated
532    public static Expression propertyExpression(final String propertyName) {
533        return new ExpressionAdapter() {
534            public Object evaluate(Exchange exchange) {
535                return exchange.getProperty(propertyName);
536            }
537
538            @Override
539            public String toString() {
540                return "exchangeProperty(" + propertyName + ")";
541            }
542        };
543    }
544    
545    /**
546     * Returns an expression for the property value of exchange with the given name
547     *
548     * @param propertyName the name of the property the expression will return
549     * @return an expression object which will return the property value
550     */
551    public static Expression exchangePropertyExpression(final String propertyName) {
552        return new ExpressionAdapter() {
553            public Object evaluate(Exchange exchange) {
554                return exchange.getProperty(propertyName);
555            }
556
557            @Override
558            public String toString() {
559                return "exchangeProperty(" + propertyName + ")";
560            }
561        };
562    }
563
564    /**
565     * Returns an expression for the property value of exchange with the given name invoking methods defined
566     * in a simple OGNL notation
567     *
568     * @param ognl  methods to invoke on the property in a simple OGNL syntax
569     */
570    public static Expression propertyOgnlExpression(final String ognl) {
571        return new KeyedOgnlExpressionAdapter(ognl, "propertyOgnl(" + ognl + ")",
572            new KeyedOgnlExpressionAdapter.KeyedEntityRetrievalStrategy() {
573                public Object getKeyedEntity(Exchange exchange, String key) {
574                    return exchange.getProperty(key);
575                }
576            });
577    }
578
579    /**
580     * Returns an expression for the properties of exchange
581     *
582     * @return an expression object which will return the properties
583     */
584    public static Expression propertiesExpression() {
585        return new ExpressionAdapter() {
586            public Object evaluate(Exchange exchange) {
587                return exchange.getProperties();
588            }
589
590            @Override
591            public String toString() {
592                return "properties";
593            }
594        };
595    }
596    
597    /**
598     * Returns an expression for the properties of the camel context
599     *
600     * @return an expression object which will return the properties
601     */
602    public static Expression camelContextPropertiesExpression() {
603        return new ExpressionAdapter() {
604            public Object evaluate(Exchange exchange) {
605                return exchange.getContext().getProperties();
606            }
607
608            @Override
609            public String toString() {
610                return "camelContextProperties";
611            }
612        };
613    }
614    
615    /**
616     * Returns an expression for the property value of the camel context with the given name
617     *
618     * @param propertyName the name of the property the expression will return
619     * @return an expression object which will return the property value
620     */
621    public static Expression camelContextPropertyExpression(final String propertyName) {
622        return new ExpressionAdapter() {
623            public Object evaluate(Exchange exchange) {
624                return exchange.getContext().getProperty(propertyName);
625            }
626
627            @Override
628            public String toString() {
629                return "camelContextProperty(" + propertyName + ")";
630            }
631        };
632    }
633
634    /**
635     * Returns an expression for a system property value with the given name
636     *
637     * @param propertyName the name of the system property the expression will return
638     * @return an expression object which will return the system property value
639     */
640    public static Expression systemPropertyExpression(final String propertyName) {
641        return systemPropertyExpression(propertyName, null);
642    }
643
644    /**
645     * Returns an expression for a system property value with the given name
646     *
647     * @param propertyName the name of the system property the expression will return
648     * @param defaultValue default value to return if no system property exists
649     * @return an expression object which will return the system property value
650     */
651    public static Expression systemPropertyExpression(final String propertyName,
652                                                      final String defaultValue) {
653        return new ExpressionAdapter() {
654            public Object evaluate(Exchange exchange) {
655                return System.getProperty(propertyName, defaultValue);
656            }
657
658            @Override
659            public String toString() {
660                return "systemProperty(" + propertyName + ")";
661            }
662        };
663    }
664
665    /**
666     * Returns an expression for a system environment value with the given name
667     *
668     * @param propertyName the name of the system environment the expression will return
669     * @return an expression object which will return the system property value
670     */
671    public static Expression systemEnvironmentExpression(final String propertyName) {
672        return systemEnvironmentExpression(propertyName, null);
673    }
674
675    /**
676     * Returns an expression for a system environment value with the given name
677     *
678     * @param propertyName the name of the system environment the expression will return
679     * @param defaultValue default value to return if no system environment exists
680     * @return an expression object which will return the system environment value
681     */
682    public static Expression systemEnvironmentExpression(final String propertyName,
683                                                         final String defaultValue) {
684        return new ExpressionAdapter() {
685            public Object evaluate(Exchange exchange) {
686                String answer = System.getenv(propertyName);
687                if (answer == null) {
688                    answer = defaultValue;
689                }
690                return answer;
691            }
692
693            @Override
694            public String toString() {
695                return "systemEnvironment(" + propertyName + ")";
696            }
697        };
698    }
699
700    /**
701     * Returns an expression for the constant value
702     *
703     * @param value the value the expression will return
704     * @return an expression object which will return the constant value
705     */
706    public static Expression constantExpression(final Object value) {
707        return new ExpressionAdapter() {
708            public Object evaluate(Exchange exchange) {
709                return value;
710            }
711
712            @Override
713            public String toString() {
714                return "" + value;
715            }
716        };
717    }
718
719    /**
720     * Returns an expression for evaluating the expression/predicate using the given language
721     *
722     * @param expression  the expression or predicate
723     * @return an expression object which will evaluate the expression/predicate using the given language
724     */
725    public static Expression languageExpression(final String language, final String expression) {
726        return new ExpressionAdapter() {
727            public Object evaluate(Exchange exchange) {
728                Language lan = exchange.getContext().resolveLanguage(language);
729                if (lan != null) {
730                    return lan.createExpression(expression).evaluate(exchange, Object.class);
731                } else {
732                    throw new NoSuchLanguageException(language);
733                }
734            }
735
736            @Override
737            public boolean matches(Exchange exchange) {
738                Language lan = exchange.getContext().resolveLanguage(language);
739                if (lan != null) {
740                    return lan.createPredicate(expression).matches(exchange);
741                } else {
742                    throw new NoSuchLanguageException(language);
743                }
744            }
745
746            @Override
747            public String toString() {
748                return "language[" + language + ":" + expression + "]";
749            }
750        };
751    }
752
753    /**
754     * Returns an expression for a type value
755     *
756     * @param name the type name
757     * @return an expression object which will return the type value
758     */
759    public static Expression typeExpression(final String name) {
760        return new ExpressionAdapter() {
761            public Object evaluate(Exchange exchange) {
762                // it may refer to a class type
763                Class<?> type = exchange.getContext().getClassResolver().resolveClass(name);
764                if (type != null) {
765                    return type;
766                }
767
768                int pos = name.lastIndexOf(".");
769                if (pos > 0) {
770                    String before = name.substring(0, pos);
771                    String after = name.substring(pos + 1);
772                    type = exchange.getContext().getClassResolver().resolveClass(before);
773                    if (type != null) {
774                        return ObjectHelper.lookupConstantFieldValue(type, after);
775                    }
776                }
777
778                throw ObjectHelper.wrapCamelExecutionException(exchange, new ClassNotFoundException("Cannot find type " + name));
779            }
780
781            @Override
782            public String toString() {
783                return "type:" + name;
784            }
785        };
786    }
787
788    /**
789     * Returns an expression that caches the evaluation of another expression
790     * and returns the cached value, to avoid re-evaluating the expression.
791     *
792     * @param expression  the target expression to cache
793     * @return the cached value
794     */
795    public static Expression cacheExpression(final Expression expression) {
796        return new ExpressionAdapter() {
797            private final AtomicReference<Object> cache = new AtomicReference<Object>();
798
799            public Object evaluate(Exchange exchange) {
800                Object answer = cache.get();
801                if (answer == null) {
802                    answer = expression.evaluate(exchange, Object.class);
803                    cache.set(answer);
804                }
805                return answer;
806            }
807
808            @Override
809            public String toString() {
810                return expression.toString();
811            }
812        };
813    }
814
815    /**
816     * Returns the expression for the exchanges inbound message body
817     */
818    public static Expression bodyExpression() {
819        return new ExpressionAdapter() {
820            public Object evaluate(Exchange exchange) {
821                return exchange.getIn().getBody();
822            }
823
824            @Override
825            public String toString() {
826                return "body";
827            }
828        };
829    }
830
831    /**
832     * Returns the expression for the exchanges inbound message body invoking methods defined
833     * in a simple OGNL notation
834     *
835     * @param ognl  methods to invoke on the body in a simple OGNL syntax
836     */
837    public static Expression bodyOgnlExpression(final String ognl) {
838        return new ExpressionAdapter() {
839            public Object evaluate(Exchange exchange) {
840                Object body = exchange.getIn().getBody();
841                if (body == null) {
842                    return null;
843                }
844                return new MethodCallExpression(body, ognl).evaluate(exchange);
845            }
846
847            @Override
848            public String toString() {
849                return "bodyOgnl(" + ognl + ")";
850            }
851        };
852    }
853
854    /**
855     * Returns the expression for invoking a method (support OGNL syntax) on the given expression
856     *
857     * @param exp   the expression to evaluate and invoke the method on its result
858     * @param ognl  methods to invoke on the evaluated expression in a simple OGNL syntax
859     */
860    public static Expression ognlExpression(final Expression exp, final String ognl) {
861        return new ExpressionAdapter() {
862            public Object evaluate(Exchange exchange) {
863                Object value = exp.evaluate(exchange, Object.class);
864                if (value == null) {
865                    return null;
866                }
867                return new MethodCallExpression(value, ognl).evaluate(exchange);
868            }
869
870            @Override
871            public String toString() {
872                return "ognl(" + exp + ", " + ognl + ")";
873            }
874        };
875    }
876
877    /**
878     * Returns the expression for the exchanges camelContext invoking methods defined
879     * in a simple OGNL notation
880     *
881     * @param ognl  methods to invoke on the context in a simple OGNL syntax
882     */
883    public static Expression camelContextOgnlExpression(final String ognl) {
884        return new ExpressionAdapter() {
885            public Object evaluate(Exchange exchange) {
886                CamelContext context = exchange.getContext();
887                if (context == null) {
888                    return null;
889                }
890                return new MethodCallExpression(context, ognl).evaluate(exchange);
891            }
892
893            @Override
894            public String toString() {
895                return "camelContextOgnl(" + ognl + ")";
896            }
897        };
898    }
899
900    /**
901     * Returns the expression for the exchange invoking methods defined
902     * in a simple OGNL notation
903     *
904     * @param ognl  methods to invoke on the exchange in a simple OGNL syntax
905     */
906    public static Expression exchangeOgnlExpression(final String ognl) {
907        return new ExpressionAdapter() {
908            public Object evaluate(Exchange exchange) {
909                return new MethodCallExpression(exchange, ognl).evaluate(exchange);
910            }
911
912            @Override
913            public String toString() {
914                return "exchangeOgnl(" + ognl + ")";
915            }
916        };
917    }
918
919    /**
920     * Returns the expression for the exchanges inbound message body converted
921     * to the given type
922     */
923    public static <T> Expression bodyExpression(final Class<T> type) {
924        return new ExpressionAdapter() {
925            public Object evaluate(Exchange exchange) {
926                return exchange.getIn().getBody(type);
927            }
928
929            @Override
930            public String toString() {
931                return "bodyAs[" + type.getName() + "]";
932            }
933        };
934    }
935
936    /**
937     * Returns the expression for the exchanges inbound message body converted
938     * to the given type
939     */
940    public static Expression bodyExpression(final String name) {
941        return new ExpressionAdapter() {
942            public Object evaluate(Exchange exchange) {
943                Class<?> type;
944                try {
945                    type = exchange.getContext().getClassResolver().resolveMandatoryClass(name);
946                } catch (ClassNotFoundException e) {
947                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
948                }
949                return exchange.getIn().getBody(type);
950            }
951
952            @Override
953            public String toString() {
954                return "bodyAs[" + name + "]";
955            }
956        };
957    }
958
959    /**
960     * Returns the expression for the exchanges inbound message body converted
961     * to the given type
962     */
963    public static Expression mandatoryBodyExpression(final String name) {
964        return new ExpressionAdapter() {
965            public Object evaluate(Exchange exchange) {
966                Class<?> type;
967                try {
968                    type = exchange.getContext().getClassResolver().resolveMandatoryClass(name);
969                } catch (ClassNotFoundException e) {
970                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
971                }
972                try {
973                    return exchange.getIn().getMandatoryBody(type);
974                } catch (InvalidPayloadException e) {
975                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
976                }
977            }
978
979            @Override
980            public String toString() {
981                return "mandatoryBodyAs[" + name + "]";
982            }
983        };
984    }
985
986    /**
987     * Returns the expression for the current thread name
988     */
989    public static Expression threadNameExpression() {
990        return new ExpressionAdapter() {
991            public Object evaluate(Exchange exchange) {
992                return Thread.currentThread().getName();
993            }
994
995            @Override
996            public String toString() {
997                return "threadName";
998            }
999        };
1000    }
1001
1002    /**
1003     * Returns the expression for the {@code null} value
1004     */
1005    public static Expression nullExpression() {
1006        return new ExpressionAdapter() {
1007            public Object evaluate(Exchange exchange) {
1008                return null;
1009            }
1010
1011            @Override
1012            public String toString() {
1013                return "null";
1014            }
1015        };
1016    }
1017
1018    /**
1019     * Returns the expression for the exchanges inbound message body converted
1020     * to the given type.
1021     * <p/>
1022     * Does <b>not</b> allow null bodies.
1023     */
1024    public static <T> Expression mandatoryBodyExpression(final Class<T> type) {
1025        return mandatoryBodyExpression(type, false);
1026    }
1027
1028    /**
1029     * Returns the expression for the exchanges inbound message body converted
1030     * to the given type
1031     *
1032     * @param type the type
1033     * @param nullBodyAllowed whether null bodies is allowed and if so a null is returned,
1034     *                        otherwise an exception is thrown
1035     */
1036    public static <T> Expression mandatoryBodyExpression(final Class<T> type, final boolean nullBodyAllowed) {
1037        return new ExpressionAdapter() {
1038            public Object evaluate(Exchange exchange) {
1039                if (nullBodyAllowed) {
1040                    if (exchange.getIn().getBody() == null) {
1041                        return null;
1042                    }
1043
1044                    // if its a bean invocation then if it has no arguments then it should be threaded as null body allowed
1045                    if (exchange.getIn().getBody() instanceof BeanInvocation) {
1046                        // BeanInvocation would be stored directly as the message body
1047                        // do not force any type conversion attempts as it would just be unnecessary and cost a bit performance
1048                        // so a regular instanceof check is sufficient
1049                        BeanInvocation bi = (BeanInvocation) exchange.getIn().getBody();
1050                        if (bi.getArgs() == null || bi.getArgs().length == 0 || bi.getArgs()[0] == null) {
1051                            return null;
1052                        }
1053                    }
1054                }
1055
1056                try {
1057                    return exchange.getIn().getMandatoryBody(type);
1058                } catch (InvalidPayloadException e) {
1059                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
1060                }
1061            }
1062
1063            @Override
1064            public String toString() {
1065                return "mandatoryBodyAs[" + type.getName() + "]";
1066            }
1067        };
1068    }
1069
1070    /**
1071     * Returns the expression for the exchanges inbound message body type
1072     */
1073    public static Expression bodyTypeExpression() {
1074        return new ExpressionAdapter() {
1075            public Object evaluate(Exchange exchange) {
1076                return exchange.getIn().getBody().getClass();
1077            }
1078
1079            @Override
1080            public String toString() {
1081                return "bodyType";
1082            }
1083        };
1084    }
1085
1086    /**
1087     * Returns the expression for the out messages body
1088     */
1089    public static Expression outBodyExpression() {
1090        return new ExpressionAdapter() {
1091            public Object evaluate(Exchange exchange) {
1092                if (exchange.hasOut()) {
1093                    return exchange.getOut().getBody();
1094                } else {
1095                    return null;
1096                }
1097            }
1098
1099            @Override
1100            public String toString() {
1101                return "outBody";
1102            }
1103        };
1104    }
1105
1106    /**
1107     * Returns the expression for the exchanges outbound message body converted
1108     * to the given type
1109     */
1110    public static <T> Expression outBodyExpression(final Class<T> type) {
1111        return new ExpressionAdapter() {
1112            public Object evaluate(Exchange exchange) {
1113                if (exchange.hasOut()) {
1114                    return exchange.getOut().getBody(type);
1115                } else {
1116                    return null;
1117                }
1118            }
1119
1120            @Override
1121            public String toString() {
1122                return "outBodyAs[" + type.getName() + "]";
1123            }
1124        };
1125    }
1126
1127    /**
1128     * Returns the expression for the fault messages body
1129     */
1130    public static Expression faultBodyExpression() {
1131        return new ExpressionAdapter() {
1132            public Object evaluate(Exchange exchange) {
1133                Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
1134                return msg.isFault() ? msg.getBody() : null;
1135            }
1136
1137            @Override
1138            public String toString() {
1139                return "faultBody";
1140            }
1141        };
1142    }
1143
1144    /**
1145     * Returns the expression for the exchanges fault message body converted
1146     * to the given type
1147     */
1148    public static <T> Expression faultBodyExpression(final Class<T> type) {
1149        return new ExpressionAdapter() {
1150            public Object evaluate(Exchange exchange) {
1151                Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
1152                return msg.isFault() ? msg.getBody(type) : null;
1153            }
1154
1155            @Override
1156            public String toString() {
1157                return "faultBodyAs[" + type.getName() + "]";
1158            }
1159        };
1160    }
1161
1162    /**
1163     * Returns the expression for the exchange
1164     */
1165    public static Expression exchangeExpression() {
1166        return new ExpressionAdapter() {
1167            public Object evaluate(Exchange exchange) {
1168                return exchange;
1169            }
1170
1171            @Override
1172            public String toString() {
1173                return "exchange";
1174            }
1175        };
1176    }
1177
1178    /**
1179     * Returns the expression for the IN message
1180     */
1181    public static Expression inMessageExpression() {
1182        return new ExpressionAdapter() {
1183            public Object evaluate(Exchange exchange) {
1184                return exchange.getIn();
1185            }
1186
1187            @Override
1188            public String toString() {
1189                return "inMessage";
1190            }
1191        };
1192    }
1193
1194    /**
1195     * Returns the expression for the OUT message
1196     */
1197    public static Expression outMessageExpression() {
1198        return new ExpressionAdapter() {
1199            public Object evaluate(Exchange exchange) {
1200                return exchange.getOut();
1201            }
1202
1203            @Override
1204            public String toString() {
1205                return "outMessage";
1206            }
1207        };
1208    }
1209
1210    /**
1211     * Returns an expression which converts the given expression to the given type
1212     */
1213    public static Expression convertToExpression(final Expression expression, final Class<?> type) {
1214        return new ExpressionAdapter() {
1215            public Object evaluate(Exchange exchange) {
1216                if (type != null) {
1217                    return expression.evaluate(exchange, type);
1218                } else {
1219                    return expression;
1220                }
1221            }
1222
1223            @Override
1224            public String toString() {
1225                return "" + expression;
1226            }
1227        };
1228    }
1229
1230    /**
1231     * Returns an expression which converts the given expression to the given type the type
1232     * expression is evaluated to
1233     */
1234    public static Expression convertToExpression(final Expression expression, final Expression type) {
1235        return new ExpressionAdapter() {
1236            public Object evaluate(Exchange exchange) {
1237                Object result = type.evaluate(exchange, Object.class);
1238                if (result != null) {
1239                    return expression.evaluate(exchange, result.getClass());
1240                } else {
1241                    return expression;
1242                }
1243            }
1244
1245            @Override
1246            public String toString() {
1247                return "" + expression;
1248            }
1249        };
1250    }
1251
1252    /**
1253     * Returns a tokenize expression which will tokenize the string with the
1254     * given token
1255     */
1256    public static Expression tokenizeExpression(final Expression expression,
1257                                                final String token) {
1258        return new ExpressionAdapter() {
1259            public Object evaluate(Exchange exchange) {
1260                Object value = expression.evaluate(exchange, Object.class);
1261                Scanner scanner = ObjectHelper.getScanner(exchange, value);
1262                scanner.useDelimiter(token);
1263                return scanner;
1264            }
1265
1266            @Override
1267            public String toString() {
1268                return "tokenize(" + expression + ", " + token + ")";
1269            }
1270        };
1271    }
1272
1273    /**
1274     * Returns an {@link TokenPairExpressionIterator} expression
1275     */
1276    public static Expression tokenizePairExpression(String startToken, String endToken, boolean includeTokens) {
1277        return new TokenPairExpressionIterator(startToken, endToken, includeTokens);
1278    }
1279
1280    /**
1281     * Returns an {@link TokenXMLExpressionIterator} expression
1282     */
1283    public static Expression tokenizeXMLExpression(String tagName, String inheritNamespaceTagName) {
1284        ObjectHelper.notEmpty(tagName, "tagName");
1285
1286        // must be XML tokens
1287        if (!tagName.startsWith("<")) {
1288            tagName = "<" + tagName;
1289        }
1290        if (!tagName.endsWith(">")) {
1291            tagName = tagName + ">";
1292        }
1293
1294        if (inheritNamespaceTagName != null) {
1295            if (!inheritNamespaceTagName.startsWith("<")) {
1296                inheritNamespaceTagName = "<" + inheritNamespaceTagName;
1297            }
1298            if (!inheritNamespaceTagName.endsWith(">")) {
1299                inheritNamespaceTagName = inheritNamespaceTagName + ">";
1300            }
1301        }
1302        return new TokenXMLExpressionIterator(tagName, inheritNamespaceTagName);
1303    }
1304
1305    public static Expression tokenizeXMLAwareExpression(String path, char mode) {
1306        ObjectHelper.notEmpty(path, "path");
1307
1308        return new XMLTokenExpressionIterator(path, mode);
1309    }
1310    
1311    public static Expression tokenizeXMLAwareExpression(String path, char mode, int group) {
1312        ObjectHelper.notEmpty(path, "path");
1313
1314        return new XMLTokenExpressionIterator(path, mode, group);
1315    }
1316
1317    /**
1318     * Returns a tokenize expression which will tokenize the string with the
1319     * given regex
1320     */
1321    public static Expression regexTokenizeExpression(final Expression expression,
1322                                                     final String regexTokenizer) {
1323        final Pattern pattern = Pattern.compile(regexTokenizer);
1324        return new ExpressionAdapter() {
1325            public Object evaluate(Exchange exchange) {
1326                Object value = expression.evaluate(exchange, Object.class);
1327                Scanner scanner = ObjectHelper.getScanner(exchange, value);
1328                scanner.useDelimiter(pattern);
1329                return scanner;
1330            }
1331
1332            @Override
1333            public String toString() {
1334                return "regexTokenize(" + expression + ", " + pattern.pattern() + ")";
1335            }
1336        };
1337    }
1338
1339    public static Expression groupIteratorExpression(final Expression expression, final String token, final int group) {
1340        return new ExpressionAdapter() {
1341            public Object evaluate(Exchange exchange) {
1342                // evaluate expression as iterator
1343                Iterator<?> it = expression.evaluate(exchange, Iterator.class);
1344                ObjectHelper.notNull(it, "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator");
1345                return new GroupIterator(exchange, it, token, group);
1346            }
1347
1348            @Override
1349            public String toString() {
1350                return "group " + expression + " " + group + " times";
1351            }
1352        };
1353    }
1354
1355    /**
1356     * Returns a sort expression which will sort the expression with the given comparator.
1357     * <p/>
1358     * The expression is evaluated as a {@link List} object to allow sorting.
1359     */
1360    @SuppressWarnings({"unchecked", "rawtypes"})
1361    public static Expression sortExpression(final Expression expression, final Comparator comparator) {
1362        return new ExpressionAdapter() {
1363            public Object evaluate(Exchange exchange) {
1364                List<?> list = expression.evaluate(exchange, List.class);
1365                Collections.sort(list, comparator);
1366                return list;
1367            }
1368
1369            @Override
1370            public String toString() {
1371                return "sort(" + expression + " by: " + comparator + ")";
1372            }
1373        };
1374    }
1375
1376    /**
1377     * Transforms the expression into a String then performs the regex
1378     * replaceAll to transform the String and return the result
1379     */
1380    public static Expression regexReplaceAll(final Expression expression,
1381                                             final String regex, final String replacement) {
1382        final Pattern pattern = Pattern.compile(regex);
1383        return new ExpressionAdapter() {
1384            public Object evaluate(Exchange exchange) {
1385                String text = expression.evaluate(exchange, String.class);
1386                if (text == null) {
1387                    return null;
1388                }
1389                return pattern.matcher(text).replaceAll(replacement);
1390            }
1391
1392            @Override
1393            public String toString() {
1394                return "regexReplaceAll(" + expression + ", " + pattern.pattern() + ")";
1395            }
1396        };
1397    }
1398
1399    /**
1400     * Transforms the expression into a String then performs the regex
1401     * replaceAll to transform the String and return the result
1402     */
1403    public static Expression regexReplaceAll(final Expression expression,
1404                                             final String regex, final Expression replacementExpression) {
1405
1406        final Pattern pattern = Pattern.compile(regex);
1407        return new ExpressionAdapter() {
1408            public Object evaluate(Exchange exchange) {
1409                String text = expression.evaluate(exchange, String.class);
1410                String replacement = replacementExpression.evaluate(exchange, String.class);
1411                if (text == null || replacement == null) {
1412                    return null;
1413                }
1414                return pattern.matcher(text).replaceAll(replacement);
1415            }
1416
1417            @Override
1418            public String toString() {
1419                return "regexReplaceAll(" + expression + ", " + pattern.pattern() + ")";
1420            }
1421        };
1422    }
1423
1424    /**
1425     * Appends the String evaluations of the two expressions together
1426     */
1427    public static Expression append(final Expression left, final Expression right) {
1428        return new ExpressionAdapter() {
1429            public Object evaluate(Exchange exchange) {
1430                return left.evaluate(exchange, String.class) + right.evaluate(exchange, String.class);
1431            }
1432
1433            @Override
1434            public String toString() {
1435                return "append(" + left + ", " + right + ")";
1436            }
1437        };
1438    }
1439
1440    /**
1441     * Prepends the String evaluations of the two expressions together
1442     */
1443    public static Expression prepend(final Expression left, final Expression right) {
1444        return new ExpressionAdapter() {
1445            public Object evaluate(Exchange exchange) {
1446                return right.evaluate(exchange, String.class) + left.evaluate(exchange, String.class);
1447            }
1448
1449            @Override
1450            public String toString() {
1451                return "prepend(" + left + ", " + right + ")";
1452            }
1453        };
1454    }
1455
1456    /**
1457     * Returns an expression which returns the string concatenation value of the various
1458     * expressions
1459     *
1460     * @param expressions the expression to be concatenated dynamically
1461     * @return an expression which when evaluated will return the concatenated values
1462     */
1463    public static Expression concatExpression(final Collection<Expression> expressions) {
1464        return concatExpression(expressions, null);
1465    }
1466
1467    /**
1468     * Returns an expression which returns the string concatenation value of the various
1469     * expressions
1470     *
1471     * @param expressions the expression to be concatenated dynamically
1472     * @param expression the text description of the expression
1473     * @return an expression which when evaluated will return the concatenated values
1474     */
1475    public static Expression concatExpression(final Collection<Expression> expressions, final String expression) {
1476        return new ExpressionAdapter() {
1477            public Object evaluate(Exchange exchange) {
1478                StringBuilder buffer = new StringBuilder();
1479                for (Expression expression : expressions) {
1480                    String text = expression.evaluate(exchange, String.class);
1481                    if (text != null) {
1482                        buffer.append(text);
1483                    }
1484                }
1485                return buffer.toString();
1486            }
1487
1488            @Override
1489            public String toString() {
1490                if (expression != null) {
1491                    return expression;
1492                } else {
1493                    return "concat" + expressions;
1494                }
1495            }
1496        };
1497    }
1498
1499    /**
1500     * Returns an Expression for the inbound message id
1501     */
1502    public static Expression messageIdExpression() {
1503        return new ExpressionAdapter() {
1504            public Object evaluate(Exchange exchange) {
1505                return exchange.getIn().getMessageId();
1506            }
1507
1508            @Override
1509            public String toString() {
1510                return "messageId";
1511            }
1512        };
1513    }
1514
1515    /**
1516     * Returns an Expression for the exchange id
1517     */
1518    public static Expression exchangeIdExpression() {
1519        return new ExpressionAdapter() {
1520            public Object evaluate(Exchange exchange) {
1521                return exchange.getExchangeId();
1522            }
1523
1524            @Override
1525            public String toString() {
1526                return "exchangeId";
1527            }
1528        };
1529    }
1530
1531    /**
1532     * Returns an Expression for the route id
1533     */
1534    public static Expression routeIdExpression() {
1535        return new ExpressionAdapter() {
1536            public Object evaluate(Exchange exchange) {
1537                String answer = null;
1538                UnitOfWork uow = exchange.getUnitOfWork();
1539                RouteContext rc = uow != null ? uow.getRouteContext() : null;
1540                if (rc != null) {
1541                    answer = rc.getRoute().getId();
1542                }
1543                if (answer == null) {
1544                    // fallback and get from route id on the exchange
1545                    answer = exchange.getFromRouteId();
1546                }
1547                return answer;
1548            }
1549
1550            @Override
1551            public String toString() {
1552                return "routeId";
1553            }
1554        };
1555    }
1556
1557    public static Expression dateExpression(final String command, final String pattern) {
1558        return new ExpressionAdapter() {
1559            public Object evaluate(Exchange exchange) {
1560                Date date;
1561                if ("now".equals(command)) {
1562                    date = new Date();
1563                } else if (command.startsWith("header.") || command.startsWith("in.header.")) {
1564                    String key = command.substring(command.lastIndexOf('.') + 1);
1565                    date = exchange.getIn().getHeader(key, Date.class);
1566                    if (date == null) {
1567                        throw new IllegalArgumentException("Cannot find java.util.Date object at command: " + command);
1568                    }
1569                } else if (command.startsWith("out.header.")) {
1570                    String key = command.substring(command.lastIndexOf('.') + 1);
1571                    date = exchange.getOut().getHeader(key, Date.class);
1572                    if (date == null) {
1573                        throw new IllegalArgumentException("Cannot find java.util.Date object at command: " + command);
1574                    }
1575                } else if ("file".equals(command)) {
1576                    Long num = exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class);
1577                    if (num != null && num > 0) {
1578                        date = new Date(num.longValue());
1579                    } else {
1580                        date = exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Date.class);
1581                        if (date == null) {
1582                            throw new IllegalArgumentException("Cannot find " + Exchange.FILE_LAST_MODIFIED + " header at command: " + command);
1583                        }
1584                    }
1585                } else {
1586                    throw new IllegalArgumentException("Command not supported for dateExpression: " + command);
1587                }
1588
1589                SimpleDateFormat df = new SimpleDateFormat(pattern);
1590                return df.format(date);
1591            }
1592
1593            @Override
1594            public String toString() {
1595                return "date(" + command + ":" + pattern + ")";
1596            }
1597        };
1598    }
1599
1600    public static Expression simpleExpression(final String expression) {
1601        return new ExpressionAdapter() {
1602            public Object evaluate(Exchange exchange) {
1603                // resolve language using context to have a clear separation of packages
1604                // must call evaluate to return the nested language evaluate when evaluating
1605                // stacked expressions
1606                Language language = exchange.getContext().resolveLanguage("simple");
1607                return language.createExpression(expression).evaluate(exchange, Object.class);
1608            }
1609
1610            @Override
1611            public String toString() {
1612                return "simple(" + expression + ")";
1613            }
1614        };
1615    }
1616   
1617    public static Expression beanExpression(final String expression) {
1618        return new ExpressionAdapter() {
1619            public Object evaluate(Exchange exchange) {
1620                // resolve language using context to have a clear separation of packages
1621                // must call evaluate to return the nested language evaluate when evaluating
1622                // stacked expressions
1623                Language language = exchange.getContext().resolveLanguage("bean");
1624                return language.createExpression(expression).evaluate(exchange, Object.class);
1625            }
1626
1627            @Override
1628            public String toString() {
1629                return "bean(" + expression + ")";
1630            }
1631        };
1632    }
1633    
1634    public static Expression beanExpression(final Class<?> beanType, final String methodName) {
1635        return BeanLanguage.bean(beanType, methodName);        
1636    }
1637
1638    public static Expression beanExpression(final Object bean, final String methodName) {
1639        return BeanLanguage.bean(bean, methodName);        
1640    }
1641
1642    public static Expression beanExpression(final String beanRef, final String methodName) {
1643        String expression = methodName != null ? beanRef + "." + methodName : beanRef;
1644        return beanExpression(expression);
1645    }
1646
1647    /**
1648     * Returns an expression processing the exchange to the given endpoint uri
1649     *
1650     * @param uri endpoint uri to send the exchange to
1651     * @return an expression object which will return the OUT body
1652     */
1653    public static Expression toExpression(final String uri) {
1654        return new ExpressionAdapter() {
1655            public Object evaluate(Exchange exchange) {
1656                Endpoint endpoint = exchange.getContext().getEndpoint(uri);
1657                if (endpoint == null) {
1658                    throw new NoSuchEndpointException(uri);
1659                }
1660
1661                Producer producer;
1662                try {
1663                    producer = endpoint.createProducer();
1664                    producer.start();
1665                    producer.process(exchange);
1666                    producer.stop();
1667                } catch (Exception e) {
1668                    throw ObjectHelper.wrapRuntimeCamelException(e);
1669                }
1670
1671                // return the OUT body, but check for exchange pattern
1672                if (ExchangeHelper.isOutCapable(exchange)) {
1673                    return exchange.getOut().getBody();
1674                } else {
1675                    return exchange.getIn().getBody();
1676                }
1677            }
1678
1679            @Override
1680            public String toString() {
1681                return "to(" + uri + ")";
1682            }
1683        };
1684    }
1685
1686    public static Expression fileNameExpression() {
1687        return new ExpressionAdapter() {
1688            public Object evaluate(Exchange exchange) {
1689                return exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1690            }
1691
1692            @Override
1693            public String toString() {
1694                return "file:name";
1695            }
1696        };
1697    }
1698
1699    public static Expression fileOnlyNameExpression() {
1700        return new ExpressionAdapter() {
1701            public Object evaluate(Exchange exchange) {
1702                String answer = exchange.getIn().getHeader(Exchange.FILE_NAME_ONLY, String.class);
1703                if (answer == null) {
1704                    answer = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1705                    answer = FileUtil.stripPath(answer);
1706                }
1707                return answer;
1708            }
1709
1710            @Override
1711            public String toString() {
1712                return "file:onlyname";
1713            }
1714        };
1715    }
1716
1717    public static Expression fileNameNoExtensionExpression() {
1718        return new ExpressionAdapter() {
1719            public Object evaluate(Exchange exchange) {
1720                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1721                return FileUtil.stripExt(name);
1722            }
1723
1724            @Override
1725            public String toString() {
1726                return "file:name.noext";
1727            }
1728        };
1729    }
1730
1731    public static Expression fileNameNoExtensionSingleExpression() {
1732        return new ExpressionAdapter() {
1733            public Object evaluate(Exchange exchange) {
1734                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1735                return FileUtil.stripExt(name, true);
1736            }
1737
1738            @Override
1739            public String toString() {
1740                return "file:name.noext.single";
1741            }
1742        };
1743    }
1744
1745    public static Expression fileOnlyNameNoExtensionExpression() {
1746        return new ExpressionAdapter() {
1747            public Object evaluate(Exchange exchange) {
1748                String name = fileOnlyNameExpression().evaluate(exchange, String.class);
1749                return FileUtil.stripExt(name);
1750            }
1751
1752            @Override
1753            public String toString() {
1754                return "file:onlyname.noext";
1755            }
1756        };
1757    }
1758
1759    public static Expression fileOnlyNameNoExtensionSingleExpression() {
1760        return new ExpressionAdapter() {
1761            public Object evaluate(Exchange exchange) {
1762                String name = fileOnlyNameExpression().evaluate(exchange, String.class);
1763                return FileUtil.stripExt(name, true);
1764            }
1765
1766            @Override
1767            public String toString() {
1768                return "file:onlyname.noext.single";
1769            }
1770        };
1771    }
1772
1773    public static Expression fileExtensionExpression() {
1774        return new ExpressionAdapter() {
1775            public Object evaluate(Exchange exchange) {
1776                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1777                return FileUtil.onlyExt(name);
1778            }
1779
1780            @Override
1781            public String toString() {
1782                return "file:ext";
1783            }
1784        };
1785    }
1786
1787    public static Expression fileExtensionSingleExpression() {
1788        return new ExpressionAdapter() {
1789            public Object evaluate(Exchange exchange) {
1790                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1791                return FileUtil.onlyExt(name, true);
1792            }
1793
1794            @Override
1795            public String toString() {
1796                return "file:ext.single";
1797            }
1798        };
1799    }
1800
1801    public static Expression fileParentExpression() {
1802        return new ExpressionAdapter() {
1803            public Object evaluate(Exchange exchange) {
1804                return exchange.getIn().getHeader("CamelFileParent", String.class);
1805            }
1806
1807            @Override
1808            public String toString() {
1809                return "file:parent";
1810            }
1811        };
1812    }
1813
1814    public static Expression filePathExpression() {
1815        return new ExpressionAdapter() {
1816            public Object evaluate(Exchange exchange) {
1817                return exchange.getIn().getHeader("CamelFilePath", String.class);
1818            }
1819
1820            @Override
1821            public String toString() {
1822                return "file:path";
1823            }
1824        };
1825    }
1826
1827    public static Expression fileAbsolutePathExpression() {
1828        return new ExpressionAdapter() {
1829            public Object evaluate(Exchange exchange) {
1830                return exchange.getIn().getHeader("CamelFileAbsolutePath", String.class);
1831            }
1832
1833            @Override
1834            public String toString() {
1835                return "file:absolute.path";
1836            }
1837        };
1838    }
1839
1840    public static Expression fileAbsoluteExpression() {
1841        return new ExpressionAdapter() {
1842            public Object evaluate(Exchange exchange) {
1843                return exchange.getIn().getHeader("CamelFileAbsolute", Boolean.class);
1844            }
1845
1846            @Override
1847            public String toString() {
1848                return "file:absolute";
1849            }
1850        };
1851    }
1852
1853    public static Expression fileSizeExpression() {
1854        return new ExpressionAdapter() {
1855            public Object evaluate(Exchange exchange) {
1856                return exchange.getIn().getHeader(Exchange.FILE_LENGTH, Long.class);
1857            }
1858
1859            @Override
1860            public String toString() {
1861                return "file:length";
1862            }
1863        };
1864    }
1865
1866    public static Expression fileLastModifiedExpression() {
1867        return new ExpressionAdapter() {
1868            public Object evaluate(Exchange exchange) {
1869                return exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class);
1870            }
1871
1872            @Override
1873            public String toString() {
1874                return "file:modified";
1875            }
1876        };
1877    }
1878
1879    public static Expression propertiesComponentExpression(final String key, final String locations) {
1880        return new ExpressionAdapter() {
1881            public Object evaluate(Exchange exchange) {
1882                try {
1883                    if (locations != null) {
1884                        // the properties component is optional as we got locations
1885                        // getComponent will create a new component if none already exists
1886                        Component component = exchange.getContext().getComponent("properties");
1887                        PropertiesComponent pc = exchange.getContext().getTypeConverter()
1888                                .mandatoryConvertTo(PropertiesComponent.class, component);
1889                        // enclose key with {{ }} to force parsing
1890                        String[] paths = locations.split(",");
1891                        return pc.parseUri(pc.getPrefixToken() + key + pc.getSuffixToken(), paths);
1892                    } else {
1893                        // the properties component is mandatory if no locations provided
1894                        Component component = exchange.getContext().hasComponent("properties");
1895                        if (component == null) {
1896                            throw new IllegalArgumentException("PropertiesComponent with name properties must be defined"
1897                                    + " in CamelContext to support property placeholders in expressions");
1898                        }
1899                        PropertiesComponent pc = exchange.getContext().getTypeConverter()
1900                                .mandatoryConvertTo(PropertiesComponent.class, component);
1901                        // enclose key with {{ }} to force parsing
1902                        return pc.parseUri(pc.getPrefixToken() + key + pc.getSuffixToken());
1903                    }
1904                } catch (Exception e) {
1905                    throw ObjectHelper.wrapRuntimeCamelException(e);
1906                }
1907            }
1908
1909            @Override
1910            public String toString() {
1911                return "properties(" + key + ")";
1912            }
1913        };
1914    }
1915    
1916    /**
1917     * Returns a random number between min and max
1918     */
1919    public static Expression randomExpression(final int min, final int max) {
1920        return new ExpressionAdapter() {
1921            public Object evaluate(Exchange exchange) {
1922                Random random = new Random();
1923                int randomNum = random.nextInt(max - min) + min;
1924                return randomNum;
1925            }
1926
1927            @Override
1928            public String toString() {
1929                return "random";
1930            }
1931        };
1932    }
1933    
1934    /**
1935     * Returns a random number between 0 and upperbound (exclusive)
1936     */
1937    public static Expression randomExpression(final int upperbound) {
1938        return new ExpressionAdapter() {
1939            public Object evaluate(Exchange exchange) {
1940                Random random = new Random();
1941                int randomNum = random.nextInt(upperbound);
1942                return randomNum;
1943            }
1944
1945            @Override
1946            public String toString() {
1947                return "random";
1948            }
1949        };
1950    }
1951
1952    /**
1953     * Expression adapter for OGNL expression from Message Header or Exchange property
1954     */
1955    private static class KeyedOgnlExpressionAdapter extends ExpressionAdapter {
1956        private final String ognl;
1957        private final String toStringValue;
1958        private final KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy;
1959
1960        public KeyedOgnlExpressionAdapter(String ognl, String toStringValue, 
1961                                          KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy) {
1962            this.ognl = ognl;
1963            this.toStringValue = toStringValue;
1964            this.keyedEntityRetrievalStrategy = keyedEntityRetrievalStrategy;
1965        }
1966
1967        public Object evaluate(Exchange exchange) {
1968            // try with full name first
1969            Object property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, ognl);
1970            if (property != null) {
1971                return property;
1972            }
1973
1974            // Split ognl except when this is not a Map, Array
1975            // and we would like to keep the dots within the key name
1976            List<String> methods = OgnlHelper.splitOgnl(ognl);
1977
1978            // remove any OGNL operators so we got the pure key name
1979            String key = OgnlHelper.removeOperators(methods.get(0));
1980
1981            property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, key);
1982            if (property == null) {
1983                return null;
1984            }
1985            // the remainder is the rest of the ognl without the key
1986            String remainder = ObjectHelper.after(ognl, key);
1987            return new MethodCallExpression(property, remainder).evaluate(exchange);
1988        }
1989
1990        @Override
1991        public String toString() {
1992            return toStringValue;
1993        }
1994
1995        /**
1996         * Strategy to retrieve the value based on the key
1997         */
1998        public interface KeyedEntityRetrievalStrategy {
1999            Object getKeyedEntity(Exchange exchange, String key);
2000        }
2001    };
2002
2003}