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     * @deprecated use {@link #exchangePropertyExpression(String)} instead
529     */
530    @Deprecated
531    public static Expression propertyExpression(final String propertyName) {
532        return new ExpressionAdapter() {
533            public Object evaluate(Exchange exchange) {
534                return exchange.getProperty(propertyName);
535            }
536
537            @Override
538            public String toString() {
539                return "exchangeProperty(" + propertyName + ")";
540            }
541        };
542    }
543    
544    /**
545     * Returns an expression for the property value of exchange with the given name
546     *
547     * @param propertyName the name of the property the expression will return
548     * @return an expression object which will return the property value
549     */
550    public static Expression exchangePropertyExpression(final String propertyName) {
551        return new ExpressionAdapter() {
552            public Object evaluate(Exchange exchange) {
553                return exchange.getProperty(propertyName);
554            }
555
556            @Override
557            public String toString() {
558                return "exchangeProperty(" + propertyName + ")";
559            }
560        };
561    }
562
563    /**
564     * Returns an expression for the property value of exchange with the given name invoking methods defined
565     * in a simple OGNL notation
566     *
567     * @param ognl  methods to invoke on the property in a simple OGNL syntax
568     */
569    public static Expression propertyOgnlExpression(final String ognl) {
570        return new KeyedOgnlExpressionAdapter(ognl, "propertyOgnl(" + ognl + ")",
571            new KeyedOgnlExpressionAdapter.KeyedEntityRetrievalStrategy() {
572                public Object getKeyedEntity(Exchange exchange, String key) {
573                    return exchange.getProperty(key);
574                }
575            });
576    }
577
578    /**
579     * Returns an expression for the properties of exchange
580     *
581     * @return an expression object which will return the properties
582     */
583    public static Expression propertiesExpression() {
584        return new ExpressionAdapter() {
585            public Object evaluate(Exchange exchange) {
586                return exchange.getProperties();
587            }
588
589            @Override
590            public String toString() {
591                return "properties";
592            }
593        };
594    }
595    
596    /**
597     * Returns an expression for the properties of the camel context
598     *
599     * @return an expression object which will return the properties
600     */
601    public static Expression camelContextPropertiesExpression() {
602        return new ExpressionAdapter() {
603            public Object evaluate(Exchange exchange) {
604                return exchange.getContext().getProperties();
605            }
606
607            @Override
608            public String toString() {
609                return "camelContextProperties";
610            }
611        };
612    }
613    
614    /**
615     * Returns an expression for the property value of the camel context with the given name
616     *
617     * @param propertyName the name of the property the expression will return
618     * @return an expression object which will return the property value
619     */
620    public static Expression camelContextPropertyExpression(final String propertyName) {
621        return new ExpressionAdapter() {
622            public Object evaluate(Exchange exchange) {
623                return exchange.getContext().getProperty(propertyName);
624            }
625
626            @Override
627            public String toString() {
628                return "camelContextProperty(" + propertyName + ")";
629            }
630        };
631    }
632
633    /**
634     * Returns an expression for a system property value with the given name
635     *
636     * @param propertyName the name of the system property the expression will return
637     * @return an expression object which will return the system property value
638     */
639    public static Expression systemPropertyExpression(final String propertyName) {
640        return systemPropertyExpression(propertyName, null);
641    }
642
643    /**
644     * Returns an expression for a system property value with the given name
645     *
646     * @param propertyName the name of the system property the expression will return
647     * @param defaultValue default value to return if no system property exists
648     * @return an expression object which will return the system property value
649     */
650    public static Expression systemPropertyExpression(final String propertyName,
651                                                      final String defaultValue) {
652        return new ExpressionAdapter() {
653            public Object evaluate(Exchange exchange) {
654                return System.getProperty(propertyName, defaultValue);
655            }
656
657            @Override
658            public String toString() {
659                return "systemProperty(" + propertyName + ")";
660            }
661        };
662    }
663
664    /**
665     * Returns an expression for a system environment value with the given name
666     *
667     * @param propertyName the name of the system environment the expression will return
668     * @return an expression object which will return the system property value
669     */
670    public static Expression systemEnvironmentExpression(final String propertyName) {
671        return systemEnvironmentExpression(propertyName, null);
672    }
673
674    /**
675     * Returns an expression for a system environment value with the given name
676     *
677     * @param propertyName the name of the system environment the expression will return
678     * @param defaultValue default value to return if no system environment exists
679     * @return an expression object which will return the system environment value
680     */
681    public static Expression systemEnvironmentExpression(final String propertyName,
682                                                         final String defaultValue) {
683        return new ExpressionAdapter() {
684            public Object evaluate(Exchange exchange) {
685                String answer = System.getenv(propertyName);
686                if (answer == null) {
687                    answer = defaultValue;
688                }
689                return answer;
690            }
691
692            @Override
693            public String toString() {
694                return "systemEnvironment(" + propertyName + ")";
695            }
696        };
697    }
698
699    /**
700     * Returns an expression for the constant value
701     *
702     * @param value the value the expression will return
703     * @return an expression object which will return the constant value
704     */
705    public static Expression constantExpression(final Object value) {
706        return new ExpressionAdapter() {
707            public Object evaluate(Exchange exchange) {
708                return value;
709            }
710
711            @Override
712            public String toString() {
713                return "" + value;
714            }
715        };
716    }
717
718    /**
719     * Returns an expression for evaluating the expression/predicate using the given language
720     *
721     * @param expression  the expression or predicate
722     * @return an expression object which will evaluate the expression/predicate using the given language
723     */
724    public static Expression languageExpression(final String language, final String expression) {
725        return new ExpressionAdapter() {
726            public Object evaluate(Exchange exchange) {
727                Language lan = exchange.getContext().resolveLanguage(language);
728                if (lan != null) {
729                    return lan.createExpression(expression).evaluate(exchange, Object.class);
730                } else {
731                    throw new NoSuchLanguageException(language);
732                }
733            }
734
735            @Override
736            public boolean matches(Exchange exchange) {
737                Language lan = exchange.getContext().resolveLanguage(language);
738                if (lan != null) {
739                    return lan.createPredicate(expression).matches(exchange);
740                } else {
741                    throw new NoSuchLanguageException(language);
742                }
743            }
744
745            @Override
746            public String toString() {
747                return "language[" + language + ":" + expression + "]";
748            }
749        };
750    }
751
752    /**
753     * Returns an expression for a type value
754     *
755     * @param name the type name
756     * @return an expression object which will return the type value
757     */
758    public static Expression typeExpression(final String name) {
759        return new ExpressionAdapter() {
760            public Object evaluate(Exchange exchange) {
761                // it may refer to a class type
762                Class<?> type = exchange.getContext().getClassResolver().resolveClass(name);
763                if (type != null) {
764                    return type;
765                }
766
767                int pos = name.lastIndexOf(".");
768                if (pos > 0) {
769                    String before = name.substring(0, pos);
770                    String after = name.substring(pos + 1);
771                    type = exchange.getContext().getClassResolver().resolveClass(before);
772                    if (type != null) {
773                        return ObjectHelper.lookupConstantFieldValue(type, after);
774                    }
775                }
776
777                throw ObjectHelper.wrapCamelExecutionException(exchange, new ClassNotFoundException("Cannot find type " + name));
778            }
779
780            @Override
781            public String toString() {
782                return "type:" + name;
783            }
784        };
785    }
786
787    /**
788     * Returns an expression that caches the evaluation of another expression
789     * and returns the cached value, to avoid re-evaluating the expression.
790     *
791     * @param expression  the target expression to cache
792     * @return the cached value
793     */
794    public static Expression cacheExpression(final Expression expression) {
795        return new ExpressionAdapter() {
796            private final AtomicReference<Object> cache = new AtomicReference<Object>();
797
798            public Object evaluate(Exchange exchange) {
799                Object answer = cache.get();
800                if (answer == null) {
801                    answer = expression.evaluate(exchange, Object.class);
802                    cache.set(answer);
803                }
804                return answer;
805            }
806
807            @Override
808            public String toString() {
809                return expression.toString();
810            }
811        };
812    }
813
814    /**
815     * Returns the expression for the exchanges inbound message body
816     */
817    public static Expression bodyExpression() {
818        return new ExpressionAdapter() {
819            public Object evaluate(Exchange exchange) {
820                return exchange.getIn().getBody();
821            }
822
823            @Override
824            public String toString() {
825                return "body";
826            }
827        };
828    }
829
830    /**
831     * Returns the expression for the exchanges inbound message body invoking methods defined
832     * in a simple OGNL notation
833     *
834     * @param ognl  methods to invoke on the body in a simple OGNL syntax
835     */
836    public static Expression bodyOgnlExpression(final String ognl) {
837        return new ExpressionAdapter() {
838            public Object evaluate(Exchange exchange) {
839                Object body = exchange.getIn().getBody();
840                if (body == null) {
841                    return null;
842                }
843                return new MethodCallExpression(body, ognl).evaluate(exchange);
844            }
845
846            @Override
847            public String toString() {
848                return "bodyOgnl(" + ognl + ")";
849            }
850        };
851    }
852
853    /**
854     * Returns the expression for invoking a method (support OGNL syntax) on the given expression
855     *
856     * @param exp   the expression to evaluate and invoke the method on its result
857     * @param ognl  methods to invoke on the evaluated expression in a simple OGNL syntax
858     */
859    public static Expression ognlExpression(final Expression exp, final String ognl) {
860        return new ExpressionAdapter() {
861            public Object evaluate(Exchange exchange) {
862                Object value = exp.evaluate(exchange, Object.class);
863                if (value == null) {
864                    return null;
865                }
866                return new MethodCallExpression(value, ognl).evaluate(exchange);
867            }
868
869            @Override
870            public String toString() {
871                return "ognl(" + exp + ", " + ognl + ")";
872            }
873        };
874    }
875
876    /**
877     * Returns the expression for the exchanges camelContext invoking methods defined
878     * in a simple OGNL notation
879     *
880     * @param ognl  methods to invoke on the body in a simple OGNL syntax
881     */
882    public static Expression camelContextOgnlExpression(final String ognl) {
883        return new ExpressionAdapter() {
884            public Object evaluate(Exchange exchange) {
885                CamelContext context = exchange.getContext();
886                if (context == null) {
887                    return null;
888                }
889                return new MethodCallExpression(context, ognl).evaluate(exchange);
890            }
891
892            @Override
893            public String toString() {
894                return "camelContextOgnl(" + ognl + ")";
895            }
896        };
897    }
898
899    /**
900     * Returns the expression for the exchanges inbound message body converted
901     * to the given type
902     */
903    public static <T> Expression bodyExpression(final Class<T> type) {
904        return new ExpressionAdapter() {
905            public Object evaluate(Exchange exchange) {
906                return exchange.getIn().getBody(type);
907            }
908
909            @Override
910            public String toString() {
911                return "bodyAs[" + type.getName() + "]";
912            }
913        };
914    }
915
916    /**
917     * Returns the expression for the exchanges inbound message body converted
918     * to the given type
919     */
920    public static Expression bodyExpression(final String name) {
921        return new ExpressionAdapter() {
922            public Object evaluate(Exchange exchange) {
923                Class<?> type;
924                try {
925                    type = exchange.getContext().getClassResolver().resolveMandatoryClass(name);
926                } catch (ClassNotFoundException e) {
927                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
928                }
929                return exchange.getIn().getBody(type);
930            }
931
932            @Override
933            public String toString() {
934                return "bodyAs[" + name + "]";
935            }
936        };
937    }
938
939    /**
940     * Returns the expression for the exchanges inbound message body converted
941     * to the given type
942     */
943    public static Expression mandatoryBodyExpression(final String name) {
944        return new ExpressionAdapter() {
945            public Object evaluate(Exchange exchange) {
946                Class<?> type;
947                try {
948                    type = exchange.getContext().getClassResolver().resolveMandatoryClass(name);
949                } catch (ClassNotFoundException e) {
950                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
951                }
952                try {
953                    return exchange.getIn().getMandatoryBody(type);
954                } catch (InvalidPayloadException e) {
955                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
956                }
957            }
958
959            @Override
960            public String toString() {
961                return "mandatoryBodyAs[" + name + "]";
962            }
963        };
964    }
965
966    /**
967     * Returns the expression for the current thread name
968     */
969    public static Expression threadNameExpression() {
970        return new ExpressionAdapter() {
971            public Object evaluate(Exchange exchange) {
972                return Thread.currentThread().getName();
973            }
974
975            @Override
976            public String toString() {
977                return "threadName";
978            }
979        };
980    }
981
982    /**
983     * Returns the expression for the {@code null} value
984     */
985    public static Expression nullExpression() {
986        return new ExpressionAdapter() {
987            public Object evaluate(Exchange exchange) {
988                return null;
989            }
990
991            @Override
992            public String toString() {
993                return "null";
994            }
995        };
996    }
997
998    /**
999     * Returns the expression for the exchanges inbound message body converted
1000     * to the given type.
1001     * <p/>
1002     * Does <b>not</b> allow null bodies.
1003     */
1004    public static <T> Expression mandatoryBodyExpression(final Class<T> type) {
1005        return mandatoryBodyExpression(type, false);
1006    }
1007
1008    /**
1009     * Returns the expression for the exchanges inbound message body converted
1010     * to the given type
1011     *
1012     * @param type the type
1013     * @param nullBodyAllowed whether null bodies is allowed and if so a null is returned,
1014     *                        otherwise an exception is thrown
1015     */
1016    public static <T> Expression mandatoryBodyExpression(final Class<T> type, final boolean nullBodyAllowed) {
1017        return new ExpressionAdapter() {
1018            public Object evaluate(Exchange exchange) {
1019                if (nullBodyAllowed) {
1020                    if (exchange.getIn().getBody() == null) {
1021                        return null;
1022                    }
1023
1024                    // if its a bean invocation then if it has no arguments then it should be threaded as null body allowed
1025                    if (exchange.getIn().getBody() instanceof BeanInvocation) {
1026                        // BeanInvocation would be stored directly as the message body
1027                        // do not force any type conversion attempts as it would just be unnecessary and cost a bit performance
1028                        // so a regular instanceof check is sufficient
1029                        BeanInvocation bi = (BeanInvocation) exchange.getIn().getBody();
1030                        if (bi.getArgs() == null || bi.getArgs().length == 0 || bi.getArgs()[0] == null) {
1031                            return null;
1032                        }
1033                    }
1034                }
1035
1036                try {
1037                    return exchange.getIn().getMandatoryBody(type);
1038                } catch (InvalidPayloadException e) {
1039                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
1040                }
1041            }
1042
1043            @Override
1044            public String toString() {
1045                return "mandatoryBodyAs[" + type.getName() + "]";
1046            }
1047        };
1048    }
1049
1050    /**
1051     * Returns the expression for the exchanges inbound message body type
1052     */
1053    public static Expression bodyTypeExpression() {
1054        return new ExpressionAdapter() {
1055            public Object evaluate(Exchange exchange) {
1056                return exchange.getIn().getBody().getClass();
1057            }
1058
1059            @Override
1060            public String toString() {
1061                return "bodyType";
1062            }
1063        };
1064    }
1065
1066    /**
1067     * Returns the expression for the out messages body
1068     */
1069    public static Expression outBodyExpression() {
1070        return new ExpressionAdapter() {
1071            public Object evaluate(Exchange exchange) {
1072                if (exchange.hasOut()) {
1073                    return exchange.getOut().getBody();
1074                } else {
1075                    return null;
1076                }
1077            }
1078
1079            @Override
1080            public String toString() {
1081                return "outBody";
1082            }
1083        };
1084    }
1085
1086    /**
1087     * Returns the expression for the exchanges outbound message body converted
1088     * to the given type
1089     */
1090    public static <T> Expression outBodyExpression(final Class<T> type) {
1091        return new ExpressionAdapter() {
1092            public Object evaluate(Exchange exchange) {
1093                if (exchange.hasOut()) {
1094                    return exchange.getOut().getBody(type);
1095                } else {
1096                    return null;
1097                }
1098            }
1099
1100            @Override
1101            public String toString() {
1102                return "outBodyAs[" + type.getName() + "]";
1103            }
1104        };
1105    }
1106
1107    /**
1108     * Returns the expression for the fault messages body
1109     */
1110    public static Expression faultBodyExpression() {
1111        return new ExpressionAdapter() {
1112            public Object evaluate(Exchange exchange) {
1113                Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
1114                return msg.isFault() ? msg.getBody() : null;
1115            }
1116
1117            @Override
1118            public String toString() {
1119                return "faultBody";
1120            }
1121        };
1122    }
1123
1124    /**
1125     * Returns the expression for the exchanges fault message body converted
1126     * to the given type
1127     */
1128    public static <T> Expression faultBodyExpression(final Class<T> type) {
1129        return new ExpressionAdapter() {
1130            public Object evaluate(Exchange exchange) {
1131                Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
1132                return msg.isFault() ? msg.getBody(type) : null;
1133            }
1134
1135            @Override
1136            public String toString() {
1137                return "faultBodyAs[" + type.getName() + "]";
1138            }
1139        };
1140    }
1141
1142    /**
1143     * Returns the expression for the exchange
1144     */
1145    public static Expression exchangeExpression() {
1146        return new ExpressionAdapter() {
1147            public Object evaluate(Exchange exchange) {
1148                return exchange;
1149            }
1150
1151            @Override
1152            public String toString() {
1153                return "exchange";
1154            }
1155        };
1156    }
1157
1158    /**
1159     * Returns the expression for the IN message
1160     */
1161    public static Expression inMessageExpression() {
1162        return new ExpressionAdapter() {
1163            public Object evaluate(Exchange exchange) {
1164                return exchange.getIn();
1165            }
1166
1167            @Override
1168            public String toString() {
1169                return "inMessage";
1170            }
1171        };
1172    }
1173
1174    /**
1175     * Returns the expression for the OUT message
1176     */
1177    public static Expression outMessageExpression() {
1178        return new ExpressionAdapter() {
1179            public Object evaluate(Exchange exchange) {
1180                return exchange.getOut();
1181            }
1182
1183            @Override
1184            public String toString() {
1185                return "outMessage";
1186            }
1187        };
1188    }
1189
1190    /**
1191     * Returns an expression which converts the given expression to the given type
1192     */
1193    public static Expression convertToExpression(final Expression expression, final Class<?> type) {
1194        return new ExpressionAdapter() {
1195            public Object evaluate(Exchange exchange) {
1196                if (type != null) {
1197                    return expression.evaluate(exchange, type);
1198                } else {
1199                    return expression;
1200                }
1201            }
1202
1203            @Override
1204            public String toString() {
1205                return "" + expression;
1206            }
1207        };
1208    }
1209
1210    /**
1211     * Returns an expression which converts the given expression to the given type the type
1212     * expression is evaluated to
1213     */
1214    public static Expression convertToExpression(final Expression expression, final Expression type) {
1215        return new ExpressionAdapter() {
1216            public Object evaluate(Exchange exchange) {
1217                Object result = type.evaluate(exchange, Object.class);
1218                if (result != null) {
1219                    return expression.evaluate(exchange, result.getClass());
1220                } else {
1221                    return expression;
1222                }
1223            }
1224
1225            @Override
1226            public String toString() {
1227                return "" + expression;
1228            }
1229        };
1230    }
1231
1232    /**
1233     * Returns a tokenize expression which will tokenize the string with the
1234     * given token
1235     */
1236    public static Expression tokenizeExpression(final Expression expression,
1237                                                final String token) {
1238        return new ExpressionAdapter() {
1239            public Object evaluate(Exchange exchange) {
1240                Object value = expression.evaluate(exchange, Object.class);
1241                Scanner scanner = ObjectHelper.getScanner(exchange, value);
1242                scanner.useDelimiter(token);
1243                return scanner;
1244            }
1245
1246            @Override
1247            public String toString() {
1248                return "tokenize(" + expression + ", " + token + ")";
1249            }
1250        };
1251    }
1252
1253    /**
1254     * Returns an {@link TokenPairExpressionIterator} expression
1255     */
1256    public static Expression tokenizePairExpression(String startToken, String endToken, boolean includeTokens) {
1257        return new TokenPairExpressionIterator(startToken, endToken, includeTokens);
1258    }
1259
1260    /**
1261     * Returns an {@link TokenXMLExpressionIterator} expression
1262     */
1263    public static Expression tokenizeXMLExpression(String tagName, String inheritNamespaceTagName) {
1264        ObjectHelper.notEmpty(tagName, "tagName");
1265
1266        // must be XML tokens
1267        if (!tagName.startsWith("<")) {
1268            tagName = "<" + tagName;
1269        }
1270        if (!tagName.endsWith(">")) {
1271            tagName = tagName + ">";
1272        }
1273
1274        if (inheritNamespaceTagName != null) {
1275            if (!inheritNamespaceTagName.startsWith("<")) {
1276                inheritNamespaceTagName = "<" + inheritNamespaceTagName;
1277            }
1278            if (!inheritNamespaceTagName.endsWith(">")) {
1279                inheritNamespaceTagName = inheritNamespaceTagName + ">";
1280            }
1281        }
1282        return new TokenXMLExpressionIterator(tagName, inheritNamespaceTagName);
1283    }
1284
1285    public static Expression tokenizeXMLAwareExpression(String path, char mode) {
1286        ObjectHelper.notEmpty(path, "path");
1287
1288        return new XMLTokenExpressionIterator(path, mode);
1289    }
1290    
1291    public static Expression tokenizeXMLAwareExpression(String path, char mode, int group) {
1292        ObjectHelper.notEmpty(path, "path");
1293
1294        return new XMLTokenExpressionIterator(path, mode, group);
1295    }
1296
1297    /**
1298     * Returns a tokenize expression which will tokenize the string with the
1299     * given regex
1300     */
1301    public static Expression regexTokenizeExpression(final Expression expression,
1302                                                     final String regexTokenizer) {
1303        final Pattern pattern = Pattern.compile(regexTokenizer);
1304        return new ExpressionAdapter() {
1305            public Object evaluate(Exchange exchange) {
1306                Object value = expression.evaluate(exchange, Object.class);
1307                Scanner scanner = ObjectHelper.getScanner(exchange, value);
1308                scanner.useDelimiter(pattern);
1309                return scanner;
1310            }
1311
1312            @Override
1313            public String toString() {
1314                return "regexTokenize(" + expression + ", " + pattern.pattern() + ")";
1315            }
1316        };
1317    }
1318
1319    public static Expression groupIteratorExpression(final Expression expression, final String token, final int group) {
1320        return new ExpressionAdapter() {
1321            public Object evaluate(Exchange exchange) {
1322                // evaluate expression as iterator
1323                Iterator<?> it = expression.evaluate(exchange, Iterator.class);
1324                ObjectHelper.notNull(it, "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator");
1325                return new GroupIterator(exchange, it, token, group);
1326            }
1327
1328            @Override
1329            public String toString() {
1330                return "group " + expression + " " + group + " times";
1331            }
1332        };
1333    }
1334
1335    /**
1336     * Returns a sort expression which will sort the expression with the given comparator.
1337     * <p/>
1338     * The expression is evaluated as a {@link List} object to allow sorting.
1339     */
1340    @SuppressWarnings({"unchecked", "rawtypes"})
1341    public static Expression sortExpression(final Expression expression, final Comparator comparator) {
1342        return new ExpressionAdapter() {
1343            public Object evaluate(Exchange exchange) {
1344                List<?> list = expression.evaluate(exchange, List.class);
1345                Collections.sort(list, comparator);
1346                return list;
1347            }
1348
1349            @Override
1350            public String toString() {
1351                return "sort(" + expression + " by: " + comparator + ")";
1352            }
1353        };
1354    }
1355
1356    /**
1357     * Transforms the expression into a String then performs the regex
1358     * replaceAll to transform the String and return the result
1359     */
1360    public static Expression regexReplaceAll(final Expression expression,
1361                                             final String regex, final String replacement) {
1362        final Pattern pattern = Pattern.compile(regex);
1363        return new ExpressionAdapter() {
1364            public Object evaluate(Exchange exchange) {
1365                String text = expression.evaluate(exchange, String.class);
1366                if (text == null) {
1367                    return null;
1368                }
1369                return pattern.matcher(text).replaceAll(replacement);
1370            }
1371
1372            @Override
1373            public String toString() {
1374                return "regexReplaceAll(" + expression + ", " + pattern.pattern() + ")";
1375            }
1376        };
1377    }
1378
1379    /**
1380     * Transforms the expression into a String then performs the regex
1381     * replaceAll to transform the String and return the result
1382     */
1383    public static Expression regexReplaceAll(final Expression expression,
1384                                             final String regex, final Expression replacementExpression) {
1385
1386        final Pattern pattern = Pattern.compile(regex);
1387        return new ExpressionAdapter() {
1388            public Object evaluate(Exchange exchange) {
1389                String text = expression.evaluate(exchange, String.class);
1390                String replacement = replacementExpression.evaluate(exchange, String.class);
1391                if (text == null || replacement == null) {
1392                    return null;
1393                }
1394                return pattern.matcher(text).replaceAll(replacement);
1395            }
1396
1397            @Override
1398            public String toString() {
1399                return "regexReplaceAll(" + expression + ", " + pattern.pattern() + ")";
1400            }
1401        };
1402    }
1403
1404    /**
1405     * Appends the String evaluations of the two expressions together
1406     */
1407    public static Expression append(final Expression left, final Expression right) {
1408        return new ExpressionAdapter() {
1409            public Object evaluate(Exchange exchange) {
1410                return left.evaluate(exchange, String.class) + right.evaluate(exchange, String.class);
1411            }
1412
1413            @Override
1414            public String toString() {
1415                return "append(" + left + ", " + right + ")";
1416            }
1417        };
1418    }
1419
1420    /**
1421     * Prepends the String evaluations of the two expressions together
1422     */
1423    public static Expression prepend(final Expression left, final Expression right) {
1424        return new ExpressionAdapter() {
1425            public Object evaluate(Exchange exchange) {
1426                return right.evaluate(exchange, String.class) + left.evaluate(exchange, String.class);
1427            }
1428
1429            @Override
1430            public String toString() {
1431                return "prepend(" + left + ", " + right + ")";
1432            }
1433        };
1434    }
1435
1436    /**
1437     * Returns an expression which returns the string concatenation value of the various
1438     * expressions
1439     *
1440     * @param expressions the expression to be concatenated dynamically
1441     * @return an expression which when evaluated will return the concatenated values
1442     */
1443    public static Expression concatExpression(final Collection<Expression> expressions) {
1444        return concatExpression(expressions, null);
1445    }
1446
1447    /**
1448     * Returns an expression which returns the string concatenation value of the various
1449     * expressions
1450     *
1451     * @param expressions the expression to be concatenated dynamically
1452     * @param expression the text description of the expression
1453     * @return an expression which when evaluated will return the concatenated values
1454     */
1455    public static Expression concatExpression(final Collection<Expression> expressions, final String expression) {
1456        return new ExpressionAdapter() {
1457            public Object evaluate(Exchange exchange) {
1458                StringBuilder buffer = new StringBuilder();
1459                for (Expression expression : expressions) {
1460                    String text = expression.evaluate(exchange, String.class);
1461                    if (text != null) {
1462                        buffer.append(text);
1463                    }
1464                }
1465                return buffer.toString();
1466            }
1467
1468            @Override
1469            public String toString() {
1470                if (expression != null) {
1471                    return expression;
1472                } else {
1473                    return "concat" + expressions;
1474                }
1475            }
1476        };
1477    }
1478
1479    /**
1480     * Returns an Expression for the inbound message id
1481     */
1482    public static Expression messageIdExpression() {
1483        return new ExpressionAdapter() {
1484            public Object evaluate(Exchange exchange) {
1485                return exchange.getIn().getMessageId();
1486            }
1487
1488            @Override
1489            public String toString() {
1490                return "messageId";
1491            }
1492        };
1493    }
1494
1495    /**
1496     * Returns an Expression for the exchange id
1497     */
1498    public static Expression exchangeIdExpression() {
1499        return new ExpressionAdapter() {
1500            public Object evaluate(Exchange exchange) {
1501                return exchange.getExchangeId();
1502            }
1503
1504            @Override
1505            public String toString() {
1506                return "exchangeId";
1507            }
1508        };
1509    }
1510
1511    /**
1512     * Returns an Expression for the route id
1513     */
1514    public static Expression routeIdExpression() {
1515        return new ExpressionAdapter() {
1516            public Object evaluate(Exchange exchange) {
1517                String answer = null;
1518                UnitOfWork uow = exchange.getUnitOfWork();
1519                RouteContext rc = uow != null ? uow.getRouteContext() : null;
1520                if (rc != null) {
1521                    answer = rc.getRoute().getId();
1522                }
1523                if (answer == null) {
1524                    // fallback and get from route id on the exchange
1525                    answer = exchange.getFromRouteId();
1526                }
1527                return answer;
1528            }
1529
1530            @Override
1531            public String toString() {
1532                return "routeId";
1533            }
1534        };
1535    }
1536
1537    public static Expression dateExpression(final String command, final String pattern) {
1538        return new ExpressionAdapter() {
1539            public Object evaluate(Exchange exchange) {
1540                Date date;
1541                if ("now".equals(command)) {
1542                    date = new Date();
1543                } else if (command.startsWith("header.") || command.startsWith("in.header.")) {
1544                    String key = command.substring(command.lastIndexOf('.') + 1);
1545                    date = exchange.getIn().getHeader(key, Date.class);
1546                    if (date == null) {
1547                        throw new IllegalArgumentException("Cannot find java.util.Date object at command: " + command);
1548                    }
1549                } else if (command.startsWith("out.header.")) {
1550                    String key = command.substring(command.lastIndexOf('.') + 1);
1551                    date = exchange.getOut().getHeader(key, Date.class);
1552                    if (date == null) {
1553                        throw new IllegalArgumentException("Cannot find java.util.Date object at command: " + command);
1554                    }
1555                } else if ("file".equals(command)) {
1556                    Long num = exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class);
1557                    if (num != null && num > 0) {
1558                        date = new Date(num.longValue());
1559                    } else {
1560                        date = exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Date.class);
1561                        if (date == null) {
1562                            throw new IllegalArgumentException("Cannot find " + Exchange.FILE_LAST_MODIFIED + " header at command: " + command);
1563                        }
1564                    }
1565                } else {
1566                    throw new IllegalArgumentException("Command not supported for dateExpression: " + command);
1567                }
1568
1569                SimpleDateFormat df = new SimpleDateFormat(pattern);
1570                return df.format(date);
1571            }
1572
1573            @Override
1574            public String toString() {
1575                return "date(" + command + ":" + pattern + ")";
1576            }
1577        };
1578    }
1579
1580    public static Expression simpleExpression(final String expression) {
1581        return new ExpressionAdapter() {
1582            public Object evaluate(Exchange exchange) {
1583                // resolve language using context to have a clear separation of packages
1584                // must call evaluate to return the nested language evaluate when evaluating
1585                // stacked expressions
1586                Language language = exchange.getContext().resolveLanguage("simple");
1587                return language.createExpression(expression).evaluate(exchange, Object.class);
1588            }
1589
1590            @Override
1591            public String toString() {
1592                return "simple(" + expression + ")";
1593            }
1594        };
1595    }
1596   
1597    public static Expression beanExpression(final String expression) {
1598        return new ExpressionAdapter() {
1599            public Object evaluate(Exchange exchange) {
1600                // resolve language using context to have a clear separation of packages
1601                // must call evaluate to return the nested language evaluate when evaluating
1602                // stacked expressions
1603                Language language = exchange.getContext().resolveLanguage("bean");
1604                return language.createExpression(expression).evaluate(exchange, Object.class);
1605            }
1606
1607            @Override
1608            public String toString() {
1609                return "bean(" + expression + ")";
1610            }
1611        };
1612    }
1613    
1614    public static Expression beanExpression(final Class<?> beanType, final String methodName) {
1615        return BeanLanguage.bean(beanType, methodName);        
1616    }
1617
1618    public static Expression beanExpression(final Object bean, final String methodName) {
1619        return BeanLanguage.bean(bean, methodName);        
1620    }
1621
1622    public static Expression beanExpression(final String beanRef, final String methodName) {
1623        String expression = methodName != null ? beanRef + "." + methodName : beanRef;
1624        return beanExpression(expression);
1625    }
1626
1627    /**
1628     * Returns an expression processing the exchange to the given endpoint uri
1629     *
1630     * @param uri endpoint uri to send the exchange to
1631     * @return an expression object which will return the OUT body
1632     */
1633    public static Expression toExpression(final String uri) {
1634        return new ExpressionAdapter() {
1635            public Object evaluate(Exchange exchange) {
1636                Endpoint endpoint = exchange.getContext().getEndpoint(uri);
1637                if (endpoint == null) {
1638                    throw new NoSuchEndpointException(uri);
1639                }
1640
1641                Producer producer;
1642                try {
1643                    producer = endpoint.createProducer();
1644                    producer.start();
1645                    producer.process(exchange);
1646                    producer.stop();
1647                } catch (Exception e) {
1648                    throw ObjectHelper.wrapRuntimeCamelException(e);
1649                }
1650
1651                // return the OUT body, but check for exchange pattern
1652                if (ExchangeHelper.isOutCapable(exchange)) {
1653                    return exchange.getOut().getBody();
1654                } else {
1655                    return exchange.getIn().getBody();
1656                }
1657            }
1658
1659            @Override
1660            public String toString() {
1661                return "to(" + uri + ")";
1662            }
1663        };
1664    }
1665
1666    public static Expression fileNameExpression() {
1667        return new ExpressionAdapter() {
1668            public Object evaluate(Exchange exchange) {
1669                return exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1670            }
1671
1672            @Override
1673            public String toString() {
1674                return "file:name";
1675            }
1676        };
1677    }
1678
1679    public static Expression fileOnlyNameExpression() {
1680        return new ExpressionAdapter() {
1681            public Object evaluate(Exchange exchange) {
1682                String answer = exchange.getIn().getHeader(Exchange.FILE_NAME_ONLY, String.class);
1683                if (answer == null) {
1684                    answer = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1685                    answer = FileUtil.stripPath(answer);
1686                }
1687                return answer;
1688            }
1689
1690            @Override
1691            public String toString() {
1692                return "file:onlyname";
1693            }
1694        };
1695    }
1696
1697    public static Expression fileNameNoExtensionExpression() {
1698        return new ExpressionAdapter() {
1699            public Object evaluate(Exchange exchange) {
1700                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1701                return FileUtil.stripExt(name);
1702            }
1703
1704            @Override
1705            public String toString() {
1706                return "file:name.noext";
1707            }
1708        };
1709    }
1710
1711    public static Expression fileNameNoExtensionSingleExpression() {
1712        return new ExpressionAdapter() {
1713            public Object evaluate(Exchange exchange) {
1714                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1715                return FileUtil.stripExt(name, true);
1716            }
1717
1718            @Override
1719            public String toString() {
1720                return "file:name.noext.single";
1721            }
1722        };
1723    }
1724
1725    public static Expression fileOnlyNameNoExtensionExpression() {
1726        return new ExpressionAdapter() {
1727            public Object evaluate(Exchange exchange) {
1728                String name = fileOnlyNameExpression().evaluate(exchange, String.class);
1729                return FileUtil.stripExt(name);
1730            }
1731
1732            @Override
1733            public String toString() {
1734                return "file:onlyname.noext";
1735            }
1736        };
1737    }
1738
1739    public static Expression fileOnlyNameNoExtensionSingleExpression() {
1740        return new ExpressionAdapter() {
1741            public Object evaluate(Exchange exchange) {
1742                String name = fileOnlyNameExpression().evaluate(exchange, String.class);
1743                return FileUtil.stripExt(name, true);
1744            }
1745
1746            @Override
1747            public String toString() {
1748                return "file:onlyname.noext.single";
1749            }
1750        };
1751    }
1752
1753    public static Expression fileExtensionExpression() {
1754        return new ExpressionAdapter() {
1755            public Object evaluate(Exchange exchange) {
1756                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1757                return FileUtil.onlyExt(name);
1758            }
1759
1760            @Override
1761            public String toString() {
1762                return "file:ext";
1763            }
1764        };
1765    }
1766
1767    public static Expression fileExtensionSingleExpression() {
1768        return new ExpressionAdapter() {
1769            public Object evaluate(Exchange exchange) {
1770                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1771                return FileUtil.onlyExt(name, true);
1772            }
1773
1774            @Override
1775            public String toString() {
1776                return "file:ext.single";
1777            }
1778        };
1779    }
1780
1781    public static Expression fileParentExpression() {
1782        return new ExpressionAdapter() {
1783            public Object evaluate(Exchange exchange) {
1784                return exchange.getIn().getHeader("CamelFileParent", String.class);
1785            }
1786
1787            @Override
1788            public String toString() {
1789                return "file:parent";
1790            }
1791        };
1792    }
1793
1794    public static Expression filePathExpression() {
1795        return new ExpressionAdapter() {
1796            public Object evaluate(Exchange exchange) {
1797                return exchange.getIn().getHeader("CamelFilePath", String.class);
1798            }
1799
1800            @Override
1801            public String toString() {
1802                return "file:path";
1803            }
1804        };
1805    }
1806
1807    public static Expression fileAbsolutePathExpression() {
1808        return new ExpressionAdapter() {
1809            public Object evaluate(Exchange exchange) {
1810                return exchange.getIn().getHeader("CamelFileAbsolutePath", String.class);
1811            }
1812
1813            @Override
1814            public String toString() {
1815                return "file:absolute.path";
1816            }
1817        };
1818    }
1819
1820    public static Expression fileAbsoluteExpression() {
1821        return new ExpressionAdapter() {
1822            public Object evaluate(Exchange exchange) {
1823                return exchange.getIn().getHeader("CamelFileAbsolute", Boolean.class);
1824            }
1825
1826            @Override
1827            public String toString() {
1828                return "file:absolute";
1829            }
1830        };
1831    }
1832
1833    public static Expression fileSizeExpression() {
1834        return new ExpressionAdapter() {
1835            public Object evaluate(Exchange exchange) {
1836                return exchange.getIn().getHeader(Exchange.FILE_LENGTH, Long.class);
1837            }
1838
1839            @Override
1840            public String toString() {
1841                return "file:length";
1842            }
1843        };
1844    }
1845
1846    public static Expression fileLastModifiedExpression() {
1847        return new ExpressionAdapter() {
1848            public Object evaluate(Exchange exchange) {
1849                return exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class);
1850            }
1851
1852            @Override
1853            public String toString() {
1854                return "file:modified";
1855            }
1856        };
1857    }
1858
1859    public static Expression propertiesComponentExpression(final String key, final String locations) {
1860        return new ExpressionAdapter() {
1861            public Object evaluate(Exchange exchange) {
1862                try {
1863                    if (locations != null) {
1864                        // the properties component is optional as we got locations
1865                        // getComponent will create a new component if none already exists
1866                        Component component = exchange.getContext().getComponent("properties");
1867                        PropertiesComponent pc = exchange.getContext().getTypeConverter()
1868                                .mandatoryConvertTo(PropertiesComponent.class, component);
1869                        // enclose key with {{ }} to force parsing
1870                        String[] paths = locations.split(",");
1871                        return pc.parseUri(pc.getPrefixToken() + key + pc.getSuffixToken(), paths);
1872                    } else {
1873                        // the properties component is mandatory if no locations provided
1874                        Component component = exchange.getContext().hasComponent("properties");
1875                        if (component == null) {
1876                            throw new IllegalArgumentException("PropertiesComponent with name properties must be defined"
1877                                    + " in CamelContext to support property placeholders in expressions");
1878                        }
1879                        PropertiesComponent pc = exchange.getContext().getTypeConverter()
1880                                .mandatoryConvertTo(PropertiesComponent.class, component);
1881                        // enclose key with {{ }} to force parsing
1882                        return pc.parseUri(pc.getPrefixToken() + key + pc.getSuffixToken());
1883                    }
1884                } catch (Exception e) {
1885                    throw ObjectHelper.wrapRuntimeCamelException(e);
1886                }
1887            }
1888
1889            @Override
1890            public String toString() {
1891                return "properties(" + key + ")";
1892            }
1893        };
1894    }
1895
1896    /**
1897     * Expression adapter for OGNL expression from Message Header or Exchange property
1898     */
1899    private static class KeyedOgnlExpressionAdapter extends ExpressionAdapter {
1900        private final String ognl;
1901        private final String toStringValue;
1902        private final KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy;
1903
1904        public KeyedOgnlExpressionAdapter(String ognl, String toStringValue, 
1905                                          KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy) {
1906            this.ognl = ognl;
1907            this.toStringValue = toStringValue;
1908            this.keyedEntityRetrievalStrategy = keyedEntityRetrievalStrategy;
1909        }
1910
1911        public Object evaluate(Exchange exchange) {
1912            // try with full name first
1913            Object property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, ognl);
1914            if (property != null) {
1915                return property;
1916            }
1917
1918            // Split ognl except when this is not a Map, Array
1919            // and we would like to keep the dots within the key name
1920            List<String> methods = OgnlHelper.splitOgnl(ognl);
1921
1922            // remove any OGNL operators so we got the pure key name
1923            String key = OgnlHelper.removeOperators(methods.get(0));
1924
1925            property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, key);
1926            if (property == null) {
1927                return null;
1928            }
1929            // the remainder is the rest of the ognl without the key
1930            String remainder = ObjectHelper.after(ognl, key);
1931            return new MethodCallExpression(property, remainder).evaluate(exchange);
1932        }
1933
1934        @Override
1935        public String toString() {
1936            return toStringValue;
1937        }
1938
1939        /**
1940         * Strategy to retrieve the value based on the key
1941         */
1942        public interface KeyedEntityRetrievalStrategy {
1943            Object getKeyedEntity(Exchange exchange, String key);
1944        }
1945    };
1946
1947}