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