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                return exchange.getOut().isFault() ? exchange.getOut().getBody() : null;
1114            }
1115
1116            @Override
1117            public String toString() {
1118                return "faultBody";
1119            }
1120        };
1121    }
1122
1123    /**
1124     * Returns the expression for the exchanges fault message body converted
1125     * to the given type
1126     */
1127    public static <T> Expression faultBodyExpression(final Class<T> type) {
1128        return new ExpressionAdapter() {
1129            public Object evaluate(Exchange exchange) {
1130                return exchange.getOut().isFault() ? exchange.getOut().getBody(type) : null;
1131            }
1132
1133            @Override
1134            public String toString() {
1135                return "faultBodyAs[" + type.getName() + "]";
1136            }
1137        };
1138    }
1139
1140    /**
1141     * Returns the expression for the exchange
1142     */
1143    public static Expression exchangeExpression() {
1144        return new ExpressionAdapter() {
1145            public Object evaluate(Exchange exchange) {
1146                return exchange;
1147            }
1148
1149            @Override
1150            public String toString() {
1151                return "exchange";
1152            }
1153        };
1154    }
1155
1156    /**
1157     * Returns the expression for the IN message
1158     */
1159    public static Expression inMessageExpression() {
1160        return new ExpressionAdapter() {
1161            public Object evaluate(Exchange exchange) {
1162                return exchange.getIn();
1163            }
1164
1165            @Override
1166            public String toString() {
1167                return "inMessage";
1168            }
1169        };
1170    }
1171
1172    /**
1173     * Returns the expression for the OUT message
1174     */
1175    public static Expression outMessageExpression() {
1176        return new ExpressionAdapter() {
1177            public Object evaluate(Exchange exchange) {
1178                return exchange.getOut();
1179            }
1180
1181            @Override
1182            public String toString() {
1183                return "outMessage";
1184            }
1185        };
1186    }
1187
1188    /**
1189     * Returns an expression which converts the given expression to the given type
1190     */
1191    public static Expression convertToExpression(final Expression expression, final Class<?> type) {
1192        return new ExpressionAdapter() {
1193            public Object evaluate(Exchange exchange) {
1194                if (type != null) {
1195                    return expression.evaluate(exchange, type);
1196                } else {
1197                    return expression;
1198                }
1199            }
1200
1201            @Override
1202            public String toString() {
1203                return "" + expression;
1204            }
1205        };
1206    }
1207
1208    /**
1209     * Returns an expression which converts the given expression to the given type the type
1210     * expression is evaluated to
1211     */
1212    public static Expression convertToExpression(final Expression expression, final Expression type) {
1213        return new ExpressionAdapter() {
1214            public Object evaluate(Exchange exchange) {
1215                Object result = type.evaluate(exchange, Object.class);
1216                if (result != null) {
1217                    return expression.evaluate(exchange, result.getClass());
1218                } else {
1219                    return expression;
1220                }
1221            }
1222
1223            @Override
1224            public String toString() {
1225                return "" + expression;
1226            }
1227        };
1228    }
1229
1230    /**
1231     * Returns a tokenize expression which will tokenize the string with the
1232     * given token
1233     */
1234    public static Expression tokenizeExpression(final Expression expression,
1235                                                final String token) {
1236        return new ExpressionAdapter() {
1237            public Object evaluate(Exchange exchange) {
1238                Object value = expression.evaluate(exchange, Object.class);
1239                Scanner scanner = ObjectHelper.getScanner(exchange, value);
1240                scanner.useDelimiter(token);
1241                return scanner;
1242            }
1243
1244            @Override
1245            public String toString() {
1246                return "tokenize(" + expression + ", " + token + ")";
1247            }
1248        };
1249    }
1250
1251    /**
1252     * Returns an {@link TokenPairExpressionIterator} expression
1253     */
1254    public static Expression tokenizePairExpression(String startToken, String endToken, boolean includeTokens) {
1255        return new TokenPairExpressionIterator(startToken, endToken, includeTokens);
1256    }
1257
1258    /**
1259     * Returns an {@link TokenXMLExpressionIterator} expression
1260     */
1261    public static Expression tokenizeXMLExpression(String tagName, String inheritNamespaceTagName) {
1262        ObjectHelper.notEmpty(tagName, "tagName");
1263
1264        // must be XML tokens
1265        if (!tagName.startsWith("<")) {
1266            tagName = "<" + tagName;
1267        }
1268        if (!tagName.endsWith(">")) {
1269            tagName = tagName + ">";
1270        }
1271
1272        if (inheritNamespaceTagName != null) {
1273            if (!inheritNamespaceTagName.startsWith("<")) {
1274                inheritNamespaceTagName = "<" + inheritNamespaceTagName;
1275            }
1276            if (!inheritNamespaceTagName.endsWith(">")) {
1277                inheritNamespaceTagName = inheritNamespaceTagName + ">";
1278            }
1279        }
1280        return new TokenXMLExpressionIterator(tagName, inheritNamespaceTagName);
1281    }
1282
1283    public static Expression tokenizeXMLAwareExpression(String path, char mode) {
1284        ObjectHelper.notEmpty(path, "path");
1285
1286        return new XMLTokenExpressionIterator(path, mode);
1287    }
1288    
1289    public static Expression tokenizeXMLAwareExpression(String path, char mode, int group) {
1290        ObjectHelper.notEmpty(path, "path");
1291
1292        return new XMLTokenExpressionIterator(path, mode, group);
1293    }
1294
1295    /**
1296     * Returns a tokenize expression which will tokenize the string with the
1297     * given regex
1298     */
1299    public static Expression regexTokenizeExpression(final Expression expression,
1300                                                     final String regexTokenizer) {
1301        final Pattern pattern = Pattern.compile(regexTokenizer);
1302        return new ExpressionAdapter() {
1303            public Object evaluate(Exchange exchange) {
1304                Object value = expression.evaluate(exchange, Object.class);
1305                Scanner scanner = ObjectHelper.getScanner(exchange, value);
1306                scanner.useDelimiter(pattern);
1307                return scanner;
1308            }
1309
1310            @Override
1311            public String toString() {
1312                return "regexTokenize(" + expression + ", " + pattern.pattern() + ")";
1313            }
1314        };
1315    }
1316
1317    public static Expression groupIteratorExpression(final Expression expression, final String token, final int group) {
1318        return new ExpressionAdapter() {
1319            public Object evaluate(Exchange exchange) {
1320                // evaluate expression as iterator
1321                Iterator<?> it = expression.evaluate(exchange, Iterator.class);
1322                ObjectHelper.notNull(it, "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator");
1323                return new GroupIterator(exchange, it, token, group);
1324            }
1325
1326            @Override
1327            public String toString() {
1328                return "group " + expression + " " + group + " times";
1329            }
1330        };
1331    }
1332
1333    /**
1334     * Returns a sort expression which will sort the expression with the given comparator.
1335     * <p/>
1336     * The expression is evaluated as a {@link List} object to allow sorting.
1337     */
1338    @SuppressWarnings({"unchecked", "rawtypes"})
1339    public static Expression sortExpression(final Expression expression, final Comparator comparator) {
1340        return new ExpressionAdapter() {
1341            public Object evaluate(Exchange exchange) {
1342                List<?> list = expression.evaluate(exchange, List.class);
1343                Collections.sort(list, comparator);
1344                return list;
1345            }
1346
1347            @Override
1348            public String toString() {
1349                return "sort(" + expression + " by: " + comparator + ")";
1350            }
1351        };
1352    }
1353
1354    /**
1355     * Transforms the expression into a String then performs the regex
1356     * replaceAll to transform the String and return the result
1357     */
1358    public static Expression regexReplaceAll(final Expression expression,
1359                                             final String regex, final String replacement) {
1360        final Pattern pattern = Pattern.compile(regex);
1361        return new ExpressionAdapter() {
1362            public Object evaluate(Exchange exchange) {
1363                String text = expression.evaluate(exchange, String.class);
1364                if (text == null) {
1365                    return null;
1366                }
1367                return pattern.matcher(text).replaceAll(replacement);
1368            }
1369
1370            @Override
1371            public String toString() {
1372                return "regexReplaceAll(" + expression + ", " + pattern.pattern() + ")";
1373            }
1374        };
1375    }
1376
1377    /**
1378     * Transforms the expression into a String then performs the regex
1379     * replaceAll to transform the String and return the result
1380     */
1381    public static Expression regexReplaceAll(final Expression expression,
1382                                             final String regex, final Expression replacementExpression) {
1383
1384        final Pattern pattern = Pattern.compile(regex);
1385        return new ExpressionAdapter() {
1386            public Object evaluate(Exchange exchange) {
1387                String text = expression.evaluate(exchange, String.class);
1388                String replacement = replacementExpression.evaluate(exchange, String.class);
1389                if (text == null || replacement == null) {
1390                    return null;
1391                }
1392                return pattern.matcher(text).replaceAll(replacement);
1393            }
1394
1395            @Override
1396            public String toString() {
1397                return "regexReplaceAll(" + expression + ", " + pattern.pattern() + ")";
1398            }
1399        };
1400    }
1401
1402    /**
1403     * Appends the String evaluations of the two expressions together
1404     */
1405    public static Expression append(final Expression left, final Expression right) {
1406        return new ExpressionAdapter() {
1407            public Object evaluate(Exchange exchange) {
1408                return left.evaluate(exchange, String.class) + right.evaluate(exchange, String.class);
1409            }
1410
1411            @Override
1412            public String toString() {
1413                return "append(" + left + ", " + right + ")";
1414            }
1415        };
1416    }
1417
1418    /**
1419     * Prepends the String evaluations of the two expressions together
1420     */
1421    public static Expression prepend(final Expression left, final Expression right) {
1422        return new ExpressionAdapter() {
1423            public Object evaluate(Exchange exchange) {
1424                return right.evaluate(exchange, String.class) + left.evaluate(exchange, String.class);
1425            }
1426
1427            @Override
1428            public String toString() {
1429                return "prepend(" + left + ", " + right + ")";
1430            }
1431        };
1432    }
1433
1434    /**
1435     * Returns an expression which returns the string concatenation value of the various
1436     * expressions
1437     *
1438     * @param expressions the expression to be concatenated dynamically
1439     * @return an expression which when evaluated will return the concatenated values
1440     */
1441    public static Expression concatExpression(final Collection<Expression> expressions) {
1442        return concatExpression(expressions, null);
1443    }
1444
1445    /**
1446     * Returns an expression which returns the string concatenation value of the various
1447     * expressions
1448     *
1449     * @param expressions the expression to be concatenated dynamically
1450     * @param expression the text description of the expression
1451     * @return an expression which when evaluated will return the concatenated values
1452     */
1453    public static Expression concatExpression(final Collection<Expression> expressions, final String expression) {
1454        return new ExpressionAdapter() {
1455            public Object evaluate(Exchange exchange) {
1456                StringBuilder buffer = new StringBuilder();
1457                for (Expression expression : expressions) {
1458                    String text = expression.evaluate(exchange, String.class);
1459                    if (text != null) {
1460                        buffer.append(text);
1461                    }
1462                }
1463                return buffer.toString();
1464            }
1465
1466            @Override
1467            public String toString() {
1468                if (expression != null) {
1469                    return expression;
1470                } else {
1471                    return "concat" + expressions;
1472                }
1473            }
1474        };
1475    }
1476
1477    /**
1478     * Returns an Expression for the inbound message id
1479     */
1480    public static Expression messageIdExpression() {
1481        return new ExpressionAdapter() {
1482            public Object evaluate(Exchange exchange) {
1483                return exchange.getIn().getMessageId();
1484            }
1485
1486            @Override
1487            public String toString() {
1488                return "messageId";
1489            }
1490        };
1491    }
1492
1493    /**
1494     * Returns an Expression for the exchange id
1495     */
1496    public static Expression exchangeIdExpression() {
1497        return new ExpressionAdapter() {
1498            public Object evaluate(Exchange exchange) {
1499                return exchange.getExchangeId();
1500            }
1501
1502            @Override
1503            public String toString() {
1504                return "exchangeId";
1505            }
1506        };
1507    }
1508
1509    /**
1510     * Returns an Expression for the route id
1511     */
1512    public static Expression routeIdExpression() {
1513        return new ExpressionAdapter() {
1514            public Object evaluate(Exchange exchange) {
1515                String answer = null;
1516                UnitOfWork uow = exchange.getUnitOfWork();
1517                RouteContext rc = uow != null ? uow.getRouteContext() : null;
1518                if (rc != null) {
1519                    answer = rc.getRoute().getId();
1520                }
1521                if (answer == null) {
1522                    // fallback and get from route id on the exchange
1523                    answer = exchange.getFromRouteId();
1524                }
1525                return answer;
1526            }
1527
1528            @Override
1529            public String toString() {
1530                return "routeId";
1531            }
1532        };
1533    }
1534
1535    public static Expression dateExpression(final String command, final String pattern) {
1536        return new ExpressionAdapter() {
1537            public Object evaluate(Exchange exchange) {
1538                Date date;
1539                if ("now".equals(command)) {
1540                    date = new Date();
1541                } else if (command.startsWith("header.") || command.startsWith("in.header.")) {
1542                    String key = command.substring(command.lastIndexOf('.') + 1);
1543                    date = exchange.getIn().getHeader(key, Date.class);
1544                    if (date == null) {
1545                        throw new IllegalArgumentException("Cannot find java.util.Date object at command: " + command);
1546                    }
1547                } else if (command.startsWith("out.header.")) {
1548                    String key = command.substring(command.lastIndexOf('.') + 1);
1549                    date = exchange.getOut().getHeader(key, Date.class);
1550                    if (date == null) {
1551                        throw new IllegalArgumentException("Cannot find java.util.Date object at command: " + command);
1552                    }
1553                } else if ("file".equals(command)) {
1554                    Long num = exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class);
1555                    if (num != null && num > 0) {
1556                        date = new Date(num.longValue());
1557                    } else {
1558                        date = exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Date.class);
1559                        if (date == null) {
1560                            throw new IllegalArgumentException("Cannot find " + Exchange.FILE_LAST_MODIFIED + " header at command: " + command);
1561                        }
1562                    }
1563                } else {
1564                    throw new IllegalArgumentException("Command not supported for dateExpression: " + command);
1565                }
1566
1567                SimpleDateFormat df = new SimpleDateFormat(pattern);
1568                return df.format(date);
1569            }
1570
1571            @Override
1572            public String toString() {
1573                return "date(" + command + ":" + pattern + ")";
1574            }
1575        };
1576    }
1577
1578    public static Expression simpleExpression(final String expression) {
1579        return new ExpressionAdapter() {
1580            public Object evaluate(Exchange exchange) {
1581                // resolve language using context to have a clear separation of packages
1582                // must call evaluate to return the nested language evaluate when evaluating
1583                // stacked expressions
1584                Language language = exchange.getContext().resolveLanguage("simple");
1585                return language.createExpression(expression).evaluate(exchange, Object.class);
1586            }
1587
1588            @Override
1589            public String toString() {
1590                return "simple(" + expression + ")";
1591            }
1592        };
1593    }
1594   
1595    public static Expression beanExpression(final String expression) {
1596        return new ExpressionAdapter() {
1597            public Object evaluate(Exchange exchange) {
1598                // resolve language using context to have a clear separation of packages
1599                // must call evaluate to return the nested language evaluate when evaluating
1600                // stacked expressions
1601                Language language = exchange.getContext().resolveLanguage("bean");
1602                return language.createExpression(expression).evaluate(exchange, Object.class);
1603            }
1604
1605            @Override
1606            public String toString() {
1607                return "bean(" + expression + ")";
1608            }
1609        };
1610    }
1611    
1612    public static Expression beanExpression(final Class<?> beanType, final String methodName) {
1613        return BeanLanguage.bean(beanType, methodName);        
1614    }
1615
1616    public static Expression beanExpression(final Object bean, final String methodName) {
1617        return BeanLanguage.bean(bean, methodName);        
1618    }
1619
1620    public static Expression beanExpression(final String beanRef, final String methodName) {
1621        String expression = methodName != null ? beanRef + "." + methodName : beanRef;
1622        return beanExpression(expression);
1623    }
1624
1625    /**
1626     * Returns an expression processing the exchange to the given endpoint uri
1627     *
1628     * @param uri endpoint uri to send the exchange to
1629     * @return an expression object which will return the OUT body
1630     */
1631    public static Expression toExpression(final String uri) {
1632        return new ExpressionAdapter() {
1633            public Object evaluate(Exchange exchange) {
1634                Endpoint endpoint = exchange.getContext().getEndpoint(uri);
1635                if (endpoint == null) {
1636                    throw new NoSuchEndpointException(uri);
1637                }
1638
1639                Producer producer;
1640                try {
1641                    producer = endpoint.createProducer();
1642                    producer.start();
1643                    producer.process(exchange);
1644                    producer.stop();
1645                } catch (Exception e) {
1646                    throw ObjectHelper.wrapRuntimeCamelException(e);
1647                }
1648
1649                // return the OUT body, but check for exchange pattern
1650                if (ExchangeHelper.isOutCapable(exchange)) {
1651                    return exchange.getOut().getBody();
1652                } else {
1653                    return exchange.getIn().getBody();
1654                }
1655            }
1656
1657            @Override
1658            public String toString() {
1659                return "to(" + uri + ")";
1660            }
1661        };
1662    }
1663
1664    public static Expression fileNameExpression() {
1665        return new ExpressionAdapter() {
1666            public Object evaluate(Exchange exchange) {
1667                return exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1668            }
1669
1670            @Override
1671            public String toString() {
1672                return "file:name";
1673            }
1674        };
1675    }
1676
1677    public static Expression fileOnlyNameExpression() {
1678        return new ExpressionAdapter() {
1679            public Object evaluate(Exchange exchange) {
1680                String answer = exchange.getIn().getHeader(Exchange.FILE_NAME_ONLY, String.class);
1681                if (answer == null) {
1682                    answer = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1683                    answer = FileUtil.stripPath(answer);
1684                }
1685                return answer;
1686            }
1687
1688            @Override
1689            public String toString() {
1690                return "file:onlyname";
1691            }
1692        };
1693    }
1694
1695    public static Expression fileNameNoExtensionExpression() {
1696        return new ExpressionAdapter() {
1697            public Object evaluate(Exchange exchange) {
1698                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1699                return FileUtil.stripExt(name);
1700            }
1701
1702            @Override
1703            public String toString() {
1704                return "file:name.noext";
1705            }
1706        };
1707    }
1708
1709    public static Expression fileOnlyNameNoExtensionExpression() {
1710        return new ExpressionAdapter() {
1711            public Object evaluate(Exchange exchange) {
1712                String name = fileOnlyNameExpression().evaluate(exchange, String.class);
1713                return FileUtil.stripExt(name);
1714            }
1715
1716            @Override
1717            public String toString() {
1718                return "file:onlyname.noext";
1719            }
1720        };
1721    }
1722
1723    public static Expression fileExtensionExpression() {
1724        return new ExpressionAdapter() {
1725            public Object evaluate(Exchange exchange) {
1726                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
1727                if (name != null) {
1728                    return name.substring(name.lastIndexOf('.') + 1);
1729                } else {
1730                    return null;
1731                }
1732            }
1733
1734            @Override
1735            public String toString() {
1736                return "file:ext";
1737            }
1738        };
1739    }
1740
1741    public static Expression fileParentExpression() {
1742        return new ExpressionAdapter() {
1743            public Object evaluate(Exchange exchange) {
1744                return exchange.getIn().getHeader("CamelFileParent", String.class);
1745            }
1746
1747            @Override
1748            public String toString() {
1749                return "file:parent";
1750            }
1751        };
1752    }
1753
1754    public static Expression filePathExpression() {
1755        return new ExpressionAdapter() {
1756            public Object evaluate(Exchange exchange) {
1757                return exchange.getIn().getHeader("CamelFilePath", String.class);
1758            }
1759
1760            @Override
1761            public String toString() {
1762                return "file:path";
1763            }
1764        };
1765    }
1766
1767    public static Expression fileAbsolutePathExpression() {
1768        return new ExpressionAdapter() {
1769            public Object evaluate(Exchange exchange) {
1770                return exchange.getIn().getHeader("CamelFileAbsolutePath", String.class);
1771            }
1772
1773            @Override
1774            public String toString() {
1775                return "file:absolute.path";
1776            }
1777        };
1778    }
1779
1780    public static Expression fileAbsoluteExpression() {
1781        return new ExpressionAdapter() {
1782            public Object evaluate(Exchange exchange) {
1783                return exchange.getIn().getHeader("CamelFileAbsolute", Boolean.class);
1784            }
1785
1786            @Override
1787            public String toString() {
1788                return "file:absolute";
1789            }
1790        };
1791    }
1792
1793    public static Expression fileSizeExpression() {
1794        return new ExpressionAdapter() {
1795            public Object evaluate(Exchange exchange) {
1796                return exchange.getIn().getHeader(Exchange.FILE_LENGTH, Long.class);
1797            }
1798
1799            @Override
1800            public String toString() {
1801                return "file:length";
1802            }
1803        };
1804    }
1805
1806    public static Expression fileLastModifiedExpression() {
1807        return new ExpressionAdapter() {
1808            public Object evaluate(Exchange exchange) {
1809                return exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class);
1810            }
1811
1812            @Override
1813            public String toString() {
1814                return "file:modified";
1815            }
1816        };
1817    }
1818
1819    public static Expression propertiesComponentExpression(final String key, final String locations) {
1820        return new ExpressionAdapter() {
1821            public Object evaluate(Exchange exchange) {
1822                try {
1823                    if (locations != null) {
1824                        // the properties component is optional as we got locations
1825                        // getComponent will create a new component if none already exists
1826                        Component component = exchange.getContext().getComponent("properties");
1827                        PropertiesComponent pc = exchange.getContext().getTypeConverter()
1828                                .mandatoryConvertTo(PropertiesComponent.class, component);
1829                        // enclose key with {{ }} to force parsing
1830                        String[] paths = locations.split(",");
1831                        return pc.parseUri(pc.getPrefixToken() + key + pc.getSuffixToken(), paths);
1832                    } else {
1833                        // the properties component is mandatory if no locations provided
1834                        Component component = exchange.getContext().hasComponent("properties");
1835                        if (component == null) {
1836                            throw new IllegalArgumentException("PropertiesComponent with name properties must be defined"
1837                                    + " in CamelContext to support property placeholders in expressions");
1838                        }
1839                        PropertiesComponent pc = exchange.getContext().getTypeConverter()
1840                                .mandatoryConvertTo(PropertiesComponent.class, component);
1841                        // enclose key with {{ }} to force parsing
1842                        return pc.parseUri(pc.getPrefixToken() + key + pc.getSuffixToken());
1843                    }
1844                } catch (Exception e) {
1845                    throw ObjectHelper.wrapRuntimeCamelException(e);
1846                }
1847            }
1848
1849            @Override
1850            public String toString() {
1851                return "properties(" + key + ")";
1852            }
1853        };
1854    }
1855
1856    /**
1857     * Expression adapter for OGNL expression from Message Header or Exchange property
1858     */
1859    private static class KeyedOgnlExpressionAdapter extends ExpressionAdapter {
1860        private final String ognl;
1861        private final String toStringValue;
1862        private final KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy;
1863
1864        public KeyedOgnlExpressionAdapter(String ognl, String toStringValue, 
1865                                          KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy) {
1866            this.ognl = ognl;
1867            this.toStringValue = toStringValue;
1868            this.keyedEntityRetrievalStrategy = keyedEntityRetrievalStrategy;
1869        }
1870
1871        public Object evaluate(Exchange exchange) {
1872            // try with full name first
1873            Object property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, ognl);
1874            if (property != null) {
1875                return property;
1876            }
1877
1878            // Split ognl except when this is not a Map, Array
1879            // and we would like to keep the dots within the key name
1880            List<String> methods = OgnlHelper.splitOgnl(ognl);
1881
1882            // remove any OGNL operators so we got the pure key name
1883            String key = OgnlHelper.removeOperators(methods.get(0));
1884
1885            property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, key);
1886            if (property == null) {
1887                return null;
1888            }
1889            // the remainder is the rest of the ognl without the key
1890            String remainder = ObjectHelper.after(ognl, key);
1891            return new MethodCallExpression(property, remainder).evaluate(exchange);
1892        }
1893
1894        @Override
1895        public String toString() {
1896            return toStringValue;
1897        }
1898
1899        /**
1900         * Strategy to retrieve the value based on the key
1901         */
1902        public interface KeyedEntityRetrievalStrategy {
1903            Object getKeyedEntity(Exchange exchange, String key);
1904        }
1905    };
1906
1907}