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.ArrayList;
023import java.util.Collection;
024import java.util.Comparator;
025import java.util.Date;
026import java.util.Iterator;
027import java.util.List;
028import java.util.Map;
029import java.util.Random;
030import java.util.Scanner;
031import java.util.Set;
032import java.util.TimeZone;
033import java.util.concurrent.atomic.AtomicReference;
034import java.util.function.BiFunction;
035import java.util.function.Function;
036import java.util.regex.Matcher;
037import java.util.regex.Pattern;
038
039import org.apache.camel.CamelContext;
040import org.apache.camel.Component;
041import org.apache.camel.Endpoint;
042import org.apache.camel.Exchange;
043import org.apache.camel.Expression;
044import org.apache.camel.InvalidPayloadException;
045import org.apache.camel.Message;
046import org.apache.camel.NoSuchEndpointException;
047import org.apache.camel.NoSuchLanguageException;
048import org.apache.camel.NoTypeConversionAvailableException;
049import org.apache.camel.Producer;
050import org.apache.camel.RuntimeExchangeException;
051import org.apache.camel.component.bean.BeanInvocation;
052import org.apache.camel.component.properties.PropertiesComponent;
053import org.apache.camel.language.bean.BeanLanguage;
054import org.apache.camel.language.simple.SimpleLanguage;
055import org.apache.camel.model.language.MethodCallExpression;
056import org.apache.camel.processor.DefaultExchangeFormatter;
057import org.apache.camel.spi.ExchangeFormatter;
058import org.apache.camel.spi.Language;
059import org.apache.camel.spi.RouteContext;
060import org.apache.camel.spi.UnitOfWork;
061import org.apache.camel.support.ExpressionAdapter;
062import org.apache.camel.support.TokenPairExpressionIterator;
063import org.apache.camel.support.TokenXMLExpressionIterator;
064import org.apache.camel.support.XMLTokenExpressionIterator;
065import org.apache.camel.util.CamelContextHelper;
066import org.apache.camel.util.ExchangeHelper;
067import org.apache.camel.util.FileUtil;
068import org.apache.camel.util.GroupIterator;
069import org.apache.camel.util.GroupTokenIterator;
070import org.apache.camel.util.IOHelper;
071import org.apache.camel.util.MessageHelper;
072import org.apache.camel.util.ObjectHelper;
073import org.apache.camel.util.OgnlHelper;
074import org.apache.camel.util.SkipIterator;
075import org.apache.camel.util.StringHelper;
076
077/**
078 * A helper class for working with <a href="http://camel.apache.org/expression.html">expressions</a>.
079 *
080 * @version
081 */
082public final class ExpressionBuilder {
083
084    private static final Pattern OFFSET_PATTERN = Pattern.compile("([+-])([^+-]+)");
085
086    /**
087     * Utility classes should not have a public constructor.
088     */
089    private ExpressionBuilder() {
090    }
091
092    /**
093     * Returns an expression for the inbound message attachments
094     *
095     * @return an expression object which will return the inbound message attachments
096     */
097    public static Expression attachmentObjectsExpression() {
098        return new ExpressionAdapter() {
099            public Object evaluate(Exchange exchange) {
100                return exchange.getIn().getAttachmentObjects();
101            }
102
103            @Override
104            public String toString() {
105                return "attachmentObjects";
106            }
107        };
108    }
109
110    /**
111     * Returns an expression for the inbound message attachments
112     *
113     * @return an expression object which will return the inbound message attachments
114     */
115    public static Expression attachmentObjectValuesExpression() {
116        return new ExpressionAdapter() {
117            public Object evaluate(Exchange exchange) {
118                return exchange.getIn().getAttachmentObjects().values();
119            }
120
121            @Override
122            public String toString() {
123                return "attachmentObjects";
124            }
125        };
126    }
127
128    /**
129     * Returns an expression for the inbound message attachments
130     *
131     * @return an expression object which will return the inbound message attachments
132     */
133    public static Expression attachmentsExpression() {
134        return new ExpressionAdapter() {
135            public Object evaluate(Exchange exchange) {
136                return exchange.getIn().getAttachments();
137            }
138
139            @Override
140            public String toString() {
141                return "attachments";
142            }
143        };
144    }
145
146    /**
147     * Returns an expression for the inbound message attachments
148     *
149     * @return an expression object which will return the inbound message attachments
150     */
151    public static Expression attachmentValuesExpression() {
152        return new ExpressionAdapter() {
153            public Object evaluate(Exchange exchange) {
154                return exchange.getIn().getAttachments().values();
155            }
156
157            @Override
158            public String toString() {
159                return "attachments";
160            }
161        };
162    }
163
164    /**
165     * Returns an expression for the header value with the given name
166     * <p/>
167     * Will fallback and look in properties if not found in headers.
168     *
169     * @param headerName the name of the header the expression will return
170     * @return an expression object which will return the header value
171     */
172    public static Expression headerExpression(final String headerName) {
173        return new ExpressionAdapter() {
174            public Object evaluate(Exchange exchange) {
175                String name = simpleExpression(headerName).evaluate(exchange, String.class);
176                Object header = exchange.getIn().getHeader(name);
177                if (header == null) {
178                    // fall back on a property
179                    header = exchange.getProperty(name);
180                }
181                return header;
182            }
183
184            @Override
185            public String toString() {
186                return "header(" + headerName + ")";
187            }
188        };
189    }
190
191    /**
192     * Returns an expression for the header value with the given name converted to the given type
193     * <p/>
194     * Will fallback and look in properties if not found in headers.
195     *
196     * @param headerName the name of the header the expression will return
197     * @param type the type to convert to
198     * @return an expression object which will return the header value
199     */
200    public static <T> Expression headerExpression(final String headerName, final Class<T> type) {
201        return new ExpressionAdapter() {
202            public Object evaluate(Exchange exchange) {
203                String name = simpleExpression(headerName).evaluate(exchange, String.class);
204                Object header = exchange.getIn().getHeader(name, type);
205                if (header == null) {
206                    // fall back on a property
207                    header = exchange.getProperty(name, type);
208                }
209                return header;
210            }
211
212            @Override
213            public String toString() {
214                return "headerAs(" + headerName + ", " + type + ")";
215            }
216        };
217    }
218
219    /**
220     * Returns an expression for the header value with the given name converted to the given type
221     * <p/>
222     * Will fallback and look in properties if not found in headers.
223     *
224     * @param headerName the name of the header the expression will return
225     * @param typeName the type to convert to as a FQN class name
226     * @return an expression object which will return the header value
227     */
228    public static Expression headerExpression(final String headerName, final String typeName) {
229        return new ExpressionAdapter() {
230            public Object evaluate(Exchange exchange) {
231                Class<?> type;
232                try {
233                    String text = simpleExpression(typeName).evaluate(exchange, String.class);
234                    type = exchange.getContext().getClassResolver().resolveMandatoryClass(text);
235                } catch (ClassNotFoundException e) {
236                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
237                }
238
239                String text = simpleExpression(headerName).evaluate(exchange, String.class);
240                Object header = exchange.getIn().getHeader(text, type);
241                if (header == null) {
242                    // fall back on a property
243                    header = exchange.getProperty(text, type);
244                }
245                return header;
246            }
247
248            @Override
249            public String toString() {
250                return "headerAs(" + headerName + ", " + typeName + ")";
251            }
252        };
253    }
254
255    /**
256     * Returns the expression for the exchanges inbound message header invoking methods defined
257     * in a simple OGNL notation
258     *
259     * @param ognl  methods to invoke on the header in a simple OGNL syntax
260     */
261    public static Expression headersOgnlExpression(final String ognl) {
262        return new KeyedOgnlExpressionAdapter(ognl, "headerOgnl(" + ognl + ")",
263            new KeyedOgnlExpressionAdapter.KeyedEntityRetrievalStrategy() {
264                public Object getKeyedEntity(Exchange exchange, String key) {
265                    String text = simpleExpression(key).evaluate(exchange, String.class);
266                    return exchange.getIn().getHeader(text);
267                }
268            });
269    }
270
271    /**
272     * Returns an expression for the inbound message headers
273     *
274     * @return an expression object which will return the inbound headers
275     */
276    public static Expression headersExpression() {
277        return new ExpressionAdapter() {
278            public Object evaluate(Exchange exchange) {
279                return exchange.getIn().getHeaders();
280            }
281
282            @Override
283            public String toString() {
284                return "headers";
285            }
286        };
287    }
288
289    /**
290     * Returns an expression for the out header value with the given name
291     * <p/>
292     * Will fallback and look in properties if not found in headers.
293     *
294     * @param headerName the name of the header the expression will return
295     * @return an expression object which will return the header value
296     */
297    public static Expression outHeaderExpression(final String headerName) {
298        return new ExpressionAdapter() {
299            public Object evaluate(Exchange exchange) {
300                if (!exchange.hasOut()) {
301                    return null;
302                }
303
304                String text = simpleExpression(headerName).evaluate(exchange, String.class);
305                Message out = exchange.getOut();
306                Object header = out.getHeader(text);
307                if (header == null) {
308                    // let's try the exchange header
309                    header = exchange.getProperty(text);
310                }
311                return header;
312            }
313
314            @Override
315            public String toString() {
316                return "outHeader(" + headerName + ")";
317            }
318        };
319    }
320
321    /**
322     * Returns an expression for the outbound message headers
323     *
324     * @return an expression object which will return the headers, will be <tt>null</tt> if the
325     * exchange is not out capable.
326     */
327    public static Expression outHeadersExpression() {
328        return new ExpressionAdapter() {
329            public Object evaluate(Exchange exchange) {
330                // only get out headers if the MEP is out capable
331                if (ExchangeHelper.isOutCapable(exchange)) {
332                    return exchange.getOut().getHeaders();
333                } else {
334                    return null;
335                }
336            }
337
338            @Override
339            public String toString() {
340                return "outHeaders";
341            }
342        };
343    }
344
345    /**
346     * Returns an expression for the exchange pattern
347     *
348     * @see org.apache.camel.Exchange#getPattern()
349     * @return an expression object which will return the exchange pattern
350     */
351    public static Expression exchangePatternExpression() {
352        return new ExpressionAdapter() {
353            public Object evaluate(Exchange exchange) {
354                return exchange.getPattern();
355            }
356
357            @Override
358            public String toString() {
359                return "exchangePattern";
360            }
361        };
362    }
363
364    /**
365     * Returns an expression for an exception set on the exchange
366     *
367     * @see Exchange#getException()
368     * @return an expression object which will return the exception set on the exchange
369     */
370    public static Expression exchangeExceptionExpression() {
371        return new ExpressionAdapter() {
372            public Object evaluate(Exchange exchange) {
373                Exception exception = exchange.getException();
374                if (exception == null) {
375                    exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
376                }
377                return exception;
378            }
379
380            @Override
381            public String toString() {
382                return "exchangeException";
383            }
384        };
385    }
386
387    /**
388     * Returns an expression for an exception set on the exchange
389     * <p/>
390     * Is used to get the caused exception that typically have been wrapped in some sort
391     * of Camel wrapper exception
392     * @param type the exception type
393     * @see Exchange#getException(Class)
394     * @return an expression object which will return the exception set on the exchange
395     */
396    public static Expression exchangeExceptionExpression(final Class<Exception> type) {
397        return new ExpressionAdapter() {
398            public Object evaluate(Exchange exchange) {
399                Exception exception = exchange.getException(type);
400                if (exception == null) {
401                    exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
402                    return ObjectHelper.getException(type, exception);
403                }
404                return exception;
405            }
406
407            @Override
408            public String toString() {
409                return "exchangeException[" + type + "]";
410            }
411        };
412    }
413
414    /**
415     * Returns the expression for the exchanges exception invoking methods defined
416     * in a simple OGNL notation
417     *
418     * @param ognl  methods to invoke on the body in a simple OGNL syntax
419     */
420    public static Expression exchangeExceptionOgnlExpression(final String ognl) {
421        return new ExpressionAdapter() {
422            public Object evaluate(Exchange exchange) {
423                Object exception = exchange.getException();
424                if (exception == null) {
425                    exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
426                }
427
428                if (exception == null) {
429                    return null;
430                }
431
432                // ognl is able to evaluate method name if it contains nested functions
433                // so we should not eager evaluate ognl as a string
434                return new MethodCallExpression(exception, ognl).evaluate(exchange);
435            }
436
437            @Override
438            public String toString() {
439                return "exchangeExceptionOgnl(" + ognl + ")";
440            }
441        };
442    }
443
444    /**
445     * Returns an expression for the type converter
446     *
447     * @return an expression object which will return the type converter
448     */
449    public static Expression typeConverterExpression() {
450        return new ExpressionAdapter() {
451            public Object evaluate(Exchange exchange) {
452                return exchange.getContext().getTypeConverter();
453            }
454
455            @Override
456            public String toString() {
457                return "typeConverter";
458            }
459        };
460    }
461
462    /**
463     * Returns an expression for the {@link org.apache.camel.spi.Registry}
464     *
465     * @return an expression object which will return the registry
466     */
467    public static Expression registryExpression() {
468        return new ExpressionAdapter() {
469            public Object evaluate(Exchange exchange) {
470                return exchange.getContext().getRegistry();
471            }
472
473            @Override
474            public String toString() {
475                return "registry";
476            }
477        };
478    }
479
480    /**
481     * Returns an expression for lookup a bean in the {@link org.apache.camel.spi.Registry}
482     *
483     * @return an expression object which will return the bean
484     */
485    public static Expression refExpression(final String ref) {
486        return new ExpressionAdapter() {
487            public Object evaluate(Exchange exchange) {
488                String text = simpleExpression(ref).evaluate(exchange, String.class);
489                return exchange.getContext().getRegistry().lookupByName(text);
490            }
491
492            @Override
493            public String toString() {
494                return "ref(" + ref + ")";
495            }
496        };
497    }
498
499    /**
500     * Returns an expression for the {@link org.apache.camel.CamelContext}
501     *
502     * @return an expression object which will return the camel context
503     */
504    public static Expression camelContextExpression() {
505        return new ExpressionAdapter() {
506            public Object evaluate(Exchange exchange) {
507                return exchange.getContext();
508            }
509
510            @Override
511            public String toString() {
512                return "camelContext";
513            }
514        };
515    }
516
517    /**
518     * Returns an expression for the {@link org.apache.camel.CamelContext} name
519     *
520     * @return an expression object which will return the camel context name
521     */
522    public static Expression camelContextNameExpression() {
523        return new ExpressionAdapter() {
524            public Object evaluate(Exchange exchange) {
525                return exchange.getContext().getName();
526            }
527
528            @Override
529            public String toString() {
530                return "camelContextName";
531            }
532        };
533    }
534
535    /**
536     * Returns an expression for an exception message set on the exchange
537     *
538     * @see <tt>Exchange.getException().getMessage()</tt>
539     * @return an expression object which will return the exception message set on the exchange
540     */
541    public static Expression exchangeExceptionMessageExpression() {
542        return new ExpressionAdapter() {
543            public Object evaluate(Exchange exchange) {
544                Exception exception = exchange.getException();
545                if (exception == null) {
546                    exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
547                }
548                return exception != null ? exception.getMessage() : null;
549            }
550
551            @Override
552            public String toString() {
553                return "exchangeExceptionMessage";
554            }
555        };
556    }
557
558    /**
559     * Returns an expression for an exception stacktrace set on the exchange
560     *
561     * @return an expression object which will return the exception stacktrace set on the exchange
562     */
563    public static Expression exchangeExceptionStackTraceExpression() {
564        return new ExpressionAdapter() {
565            public Object evaluate(Exchange exchange) {
566                Exception exception = exchange.getException();
567                if (exception == null) {
568                    exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
569                }
570                if (exception != null) {
571                    StringWriter sw = new StringWriter();
572                    PrintWriter pw = new PrintWriter(sw);
573                    exception.printStackTrace(pw);
574                    IOHelper.close(pw, sw);
575                    return sw.toString();
576                } else {
577                    return null;
578                }
579            }
580
581            @Override
582            public String toString() {
583                return "exchangeExceptionStackTrace";
584            }
585        };
586    }
587
588    /**
589     * Returns an expression for the property value of exchange with the given name
590     *
591     * @param propertyName the name of the property the expression will return
592     * @return an expression object which will return the property value
593     * @deprecated use {@link #exchangePropertyExpression(String)} instead
594     */
595    @Deprecated
596    public static Expression propertyExpression(final String propertyName) {
597        return new ExpressionAdapter() {
598            public Object evaluate(Exchange exchange) {
599                String text = simpleExpression(propertyName).evaluate(exchange, String.class);
600                return exchange.getProperty(text);
601            }
602
603            @Override
604            public String toString() {
605                return "exchangeProperty(" + propertyName + ")";
606            }
607        };
608    }
609
610    /**
611     * Returns an expression for the property value of exchange with the given name
612     *
613     * @param propertyName the name of the property the expression will return
614     * @return an expression object which will return the property value
615     */
616    public static Expression exchangePropertyExpression(final String propertyName) {
617        return new ExpressionAdapter() {
618            public Object evaluate(Exchange exchange) {
619                String text = simpleExpression(propertyName).evaluate(exchange, String.class);
620                return exchange.getProperty(text);
621            }
622
623            @Override
624            public String toString() {
625                return "exchangeProperty(" + propertyName + ")";
626            }
627        };
628    }
629
630    /**
631     * Returns an expression for the property value of exchange with the given name invoking methods defined
632     * in a simple OGNL notation
633     *
634     * @param ognl  methods to invoke on the property in a simple OGNL syntax
635     */
636    public static Expression propertyOgnlExpression(final String ognl) {
637        return new KeyedOgnlExpressionAdapter(ognl, "propertyOgnl(" + ognl + ")",
638            new KeyedOgnlExpressionAdapter.KeyedEntityRetrievalStrategy() {
639                public Object getKeyedEntity(Exchange exchange, String key) {
640                    String text = simpleExpression(key).evaluate(exchange, String.class);
641                    return exchange.getProperty(text);
642                }
643            });
644    }
645
646    /**
647     * Returns an expression for the properties of exchange
648     *
649     * @return an expression object which will return the properties
650     * @deprecated use {@link #exchangeExceptionExpression()} instead
651     */
652    @Deprecated
653    public static Expression propertiesExpression() {
654        return exchangeExceptionExpression();
655    }
656
657    /**
658     * Returns an expression for the exchange properties of exchange
659     *
660     * @return an expression object which will return the exchange properties
661     */
662    public static Expression exchangePropertiesExpression() {
663        return new ExpressionAdapter() {
664            public Object evaluate(Exchange exchange) {
665                return exchange.getProperties();
666            }
667
668            @Override
669            public String toString() {
670                return "exchangeProperties";
671            }
672        };
673    }
674
675    /**
676     * Returns an expression for the properties of the camel context
677     *
678     * @return an expression object which will return the properties
679     */
680    public static Expression camelContextPropertiesExpression() {
681        return new ExpressionAdapter() {
682            public Object evaluate(Exchange exchange) {
683                return exchange.getContext().getGlobalOptions();
684            }
685
686            @Override
687            public String toString() {
688                return "camelContextProperties";
689            }
690        };
691    }
692
693    /**
694     * Returns an expression for the property value of the camel context with the given name
695     *
696     * @param propertyName the name of the property the expression will return
697     * @return an expression object which will return the property value
698     */
699    public static Expression camelContextPropertyExpression(final String propertyName) {
700        return new ExpressionAdapter() {
701            public Object evaluate(Exchange exchange) {
702                String text = simpleExpression(propertyName).evaluate(exchange, String.class);
703                return exchange.getContext().getGlobalOption(text);
704            }
705
706            @Override
707            public String toString() {
708                return "camelContextProperty(" + propertyName + ")";
709            }
710        };
711    }
712
713    /**
714     * Returns an expression for a system property value with the given name
715     *
716     * @param propertyName the name of the system property the expression will return
717     * @return an expression object which will return the system property value
718     */
719    public static Expression systemPropertyExpression(final String propertyName) {
720        return systemPropertyExpression(propertyName, null);
721    }
722
723    /**
724     * Returns an expression for a system property value with the given name
725     *
726     * @param propertyName the name of the system property the expression will return
727     * @param defaultValue default value to return if no system property exists
728     * @return an expression object which will return the system property value
729     */
730    public static Expression systemPropertyExpression(final String propertyName,
731                                                      final String defaultValue) {
732        return new ExpressionAdapter() {
733            public Object evaluate(Exchange exchange) {
734                String text = simpleExpression(propertyName).evaluate(exchange, String.class);
735                String text2 = simpleExpression(defaultValue).evaluate(exchange, String.class);
736                return System.getProperty(text, text2);
737            }
738
739            @Override
740            public String toString() {
741                return "systemProperty(" + propertyName + ")";
742            }
743        };
744    }
745
746    /**
747     * Returns an expression for a system environment value with the given name
748     *
749     * @param propertyName the name of the system environment the expression will return
750     * @return an expression object which will return the system property value
751     */
752    public static Expression systemEnvironmentExpression(final String propertyName) {
753        return systemEnvironmentExpression(propertyName, null);
754    }
755
756    /**
757     * Returns an expression for a system environment value with the given name
758     *
759     * @param propertyName the name of the system environment the expression will return
760     * @param defaultValue default value to return if no system environment exists
761     * @return an expression object which will return the system environment value
762     */
763    public static Expression systemEnvironmentExpression(final String propertyName,
764                                                         final String defaultValue) {
765        return new ExpressionAdapter() {
766            public Object evaluate(Exchange exchange) {
767                String text = simpleExpression(propertyName).evaluate(exchange, String.class);
768                String answer = System.getenv(text);
769                if (answer == null) {
770                    String text2 = simpleExpression(defaultValue).evaluate(exchange, String.class);
771                    answer = text2;
772                }
773                return answer;
774            }
775
776            @Override
777            public String toString() {
778                return "systemEnvironment(" + propertyName + ")";
779            }
780        };
781    }
782
783    /**
784     * Returns an expression for the constant value
785     *
786     * @param value the value the expression will return
787     * @return an expression object which will return the constant value
788     */
789    public static Expression constantExpression(final Object value) {
790        return new ExpressionAdapter() {
791            public Object evaluate(Exchange exchange) {
792                return value;
793            }
794
795            @Override
796            public String toString() {
797                return "" + value;
798            }
799        };
800    }
801
802    /**
803     * Returns an expression for evaluating the expression/predicate using the given language
804     *
805     * @param expression  the expression or predicate
806     * @return an expression object which will evaluate the expression/predicate using the given language
807     */
808    public static Expression languageExpression(final String language, final String expression) {
809        return new ExpressionAdapter() {
810            public Object evaluate(Exchange exchange) {
811                Language lan = exchange.getContext().resolveLanguage(language);
812                if (lan != null) {
813                    return lan.createExpression(expression).evaluate(exchange, Object.class);
814                } else {
815                    throw new NoSuchLanguageException(language);
816                }
817            }
818
819            @Override
820            public boolean matches(Exchange exchange) {
821                Language lan = exchange.getContext().resolveLanguage(language);
822                if (lan != null) {
823                    return lan.createPredicate(expression).matches(exchange);
824                } else {
825                    throw new NoSuchLanguageException(language);
826                }
827            }
828
829            @Override
830            public String toString() {
831                return "language[" + language + ":" + expression + "]";
832            }
833        };
834    }
835
836    /**
837     * Returns an expression for a type value
838     *
839     * @param name the type name
840     * @return an expression object which will return the type value
841     */
842    public static Expression typeExpression(final String name) {
843        return new ExpressionAdapter() {
844            public Object evaluate(Exchange exchange) {
845                // it may refer to a class type
846                String text = simpleExpression(name).evaluate(exchange, String.class);
847                Class<?> type = exchange.getContext().getClassResolver().resolveClass(text);
848                if (type != null) {
849                    return type;
850                }
851
852                int pos = text.lastIndexOf(".");
853                if (pos > 0) {
854                    String before = text.substring(0, pos);
855                    String after = text.substring(pos + 1);
856                    type = exchange.getContext().getClassResolver().resolveClass(before);
857                    if (type != null) {
858                        return ObjectHelper.lookupConstantFieldValue(type, after);
859                    }
860                }
861
862                throw ObjectHelper.wrapCamelExecutionException(exchange, new ClassNotFoundException("Cannot find type " + text));
863            }
864
865            @Override
866            public String toString() {
867                return "type:" + name;
868            }
869        };
870    }
871
872    /**
873     * Returns an expression that caches the evaluation of another expression
874     * and returns the cached value, to avoid re-evaluating the expression.
875     *
876     * @param expression  the target expression to cache
877     * @return the cached value
878     */
879    public static Expression cacheExpression(final Expression expression) {
880        return new ExpressionAdapter() {
881            private final AtomicReference<Object> cache = new AtomicReference<Object>();
882
883            public Object evaluate(Exchange exchange) {
884                Object answer = cache.get();
885                if (answer == null) {
886                    answer = expression.evaluate(exchange, Object.class);
887                    cache.set(answer);
888                }
889                return answer;
890            }
891
892            @Override
893            public String toString() {
894                return expression.toString();
895            }
896        };
897    }
898
899    /**
900     * Returns the expression for the exchanges inbound message body
901     */
902    public static Expression bodyExpression() {
903        return new ExpressionAdapter() {
904            public Object evaluate(Exchange exchange) {
905                return exchange.getIn().getBody();
906            }
907
908            @Override
909            public String toString() {
910                return "body";
911            }
912        };
913    }
914
915    /**
916     * Returns a functional expression for the exchanges inbound message body
917     */
918    public static Expression bodyExpression(final Function<Object, Object> function) {
919        return new ExpressionAdapter() {
920            public Object evaluate(Exchange exchange) {
921                return function.apply(
922                    exchange.getIn().getBody()
923                );
924            }
925
926            @Override
927            public String toString() {
928                return "bodyExpression";
929            }
930        };
931    }
932
933    /**
934     * Returns a functional expression for the exchanges inbound message body and headers
935     */
936    public static Expression bodyExpression(final BiFunction<Object, Map<String, Object>, Object> function) {
937        return new ExpressionAdapter() {
938            public Object evaluate(Exchange exchange) {
939                return function.apply(
940                    exchange.getIn().getBody(),
941                    exchange.getIn().getHeaders()
942                );
943            }
944
945            @Override
946            public String toString() {
947                return "bodyExpression";
948            }
949        };
950    }
951
952    /**
953     * Returns a functional expression for the exchanges inbound message body converted to a desired type
954     */
955    public static <T> Expression bodyExpression(final Class<T> bodyType, final Function<T, Object> function) {
956        return new ExpressionAdapter() {
957            public Object evaluate(Exchange exchange) {
958                return function.apply(
959                    exchange.getIn().getBody(bodyType)
960                );
961            }
962
963            @Override
964            public String toString() {
965                return "bodyExpression (" + bodyType + ")";
966            }
967        };
968    }
969
970    /**
971     * Returns a functional expression for the exchanges inbound message body converted to a desired type and headers
972     */
973    public static <T> Expression bodyExpression(final Class<T> bodyType, final BiFunction<T, Map<String, Object>, Object> function) {
974        return new ExpressionAdapter() {
975            public Object evaluate(Exchange exchange) {
976                return function.apply(
977                    exchange.getIn().getBody(bodyType),
978                    exchange.getIn().getHeaders()
979                );
980            }
981
982            @Override
983            public String toString() {
984                return "bodyExpression (" + bodyType + ")";
985            }
986        };
987    }
988
989    /**
990     * Returns the expression for the exchanges inbound message body invoking methods defined
991     * in a simple OGNL notation
992     *
993     * @param ognl  methods to invoke on the body in a simple OGNL syntax
994     */
995    public static Expression bodyOgnlExpression(final String ognl) {
996        return new ExpressionAdapter() {
997            public Object evaluate(Exchange exchange) {
998                Object body = exchange.getIn().getBody();
999                if (body == null) {
1000                    return null;
1001                }
1002                // ognl is able to evaluate method name if it contains nested functions
1003                // so we should not eager evaluate ognl as a string
1004                return new MethodCallExpression(body, ognl).evaluate(exchange);
1005            }
1006
1007            @Override
1008            public String toString() {
1009                return "bodyOgnl(" + ognl + ")";
1010            }
1011        };
1012    }
1013
1014    /**
1015     * Returns the expression for invoking a method (support OGNL syntax) on the given expression
1016     *
1017     * @param exp   the expression to evaluate and invoke the method on its result
1018     * @param ognl  methods to invoke on the evaluated expression in a simple OGNL syntax
1019     */
1020    public static Expression ognlExpression(final Expression exp, final String ognl) {
1021        return new ExpressionAdapter() {
1022            public Object evaluate(Exchange exchange) {
1023                Object value = exp.evaluate(exchange, Object.class);
1024                if (value == null) {
1025                    return null;
1026                }
1027                // ognl is able to evaluate method name if it contains nested functions
1028                // so we should not eager evaluate ognl as a string
1029                return new MethodCallExpression(value, ognl).evaluate(exchange);
1030            }
1031
1032            @Override
1033            public String toString() {
1034                return "ognl(" + exp + ", " + ognl + ")";
1035            }
1036        };
1037    }
1038
1039    /**
1040     * Returns the expression for the exchanges camelContext invoking methods defined
1041     * in a simple OGNL notation
1042     *
1043     * @param ognl  methods to invoke on the context in a simple OGNL syntax
1044     */
1045    public static Expression camelContextOgnlExpression(final String ognl) {
1046        return new ExpressionAdapter() {
1047            public Object evaluate(Exchange exchange) {
1048                CamelContext context = exchange.getContext();
1049                if (context == null) {
1050                    return null;
1051                }
1052                // ognl is able to evaluate method name if it contains nested functions
1053                // so we should not eager evaluate ognl as a string
1054                return new MethodCallExpression(context, ognl).evaluate(exchange);
1055            }
1056
1057            @Override
1058            public String toString() {
1059                return "camelContextOgnl(" + ognl + ")";
1060            }
1061        };
1062    }
1063
1064    /**
1065     * Returns the expression for the exchange invoking methods defined
1066     * in a simple OGNL notation
1067     *
1068     * @param ognl  methods to invoke on the exchange in a simple OGNL syntax
1069     */
1070    public static Expression exchangeOgnlExpression(final String ognl) {
1071        return new ExpressionAdapter() {
1072            public Object evaluate(Exchange exchange) {
1073                // ognl is able to evaluate method name if it contains nested functions
1074                // so we should not eager evaluate ognl as a string
1075                return new MethodCallExpression(exchange, ognl).evaluate(exchange);
1076            }
1077
1078            @Override
1079            public String toString() {
1080                return "exchangeOgnl(" + ognl + ")";
1081            }
1082        };
1083    }
1084
1085    /**
1086     * Returns the expression for the exchanges inbound message body converted
1087     * to the given type
1088     */
1089    public static <T> Expression bodyExpression(final Class<T> type) {
1090        return new ExpressionAdapter() {
1091            public Object evaluate(Exchange exchange) {
1092                return exchange.getIn().getBody(type);
1093            }
1094
1095            @Override
1096            public String toString() {
1097                return "bodyAs[" + type.getName() + "]";
1098            }
1099        };
1100    }
1101
1102    /**
1103     * Returns the expression for the exchanges inbound message body converted
1104     * to the given type
1105     */
1106    public static Expression bodyExpression(final String name) {
1107        return new ExpressionAdapter() {
1108            public Object evaluate(Exchange exchange) {
1109                String text = simpleExpression(name).evaluate(exchange, String.class);
1110                Class<?> type;
1111                try {
1112                    type = exchange.getContext().getClassResolver().resolveMandatoryClass(text);
1113                } catch (ClassNotFoundException e) {
1114                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
1115                }
1116                return exchange.getIn().getBody(type);
1117            }
1118
1119            @Override
1120            public String toString() {
1121                return "bodyAs[" + name + "]";
1122            }
1123        };
1124    }
1125
1126    /**
1127     * Returns the expression for the exchanges inbound message body converted
1128     * to the given type and invoking methods on the converted body defined in a simple OGNL notation
1129     */
1130    public static Expression bodyOgnlExpression(final String name, final String ognl) {
1131        return new ExpressionAdapter() {
1132            public Object evaluate(Exchange exchange) {
1133                String text = simpleExpression(name).evaluate(exchange, String.class);
1134                Class<?> type;
1135                try {
1136                    type = exchange.getContext().getClassResolver().resolveMandatoryClass(text);
1137                } catch (ClassNotFoundException e) {
1138                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
1139                }
1140                Object body = exchange.getIn().getBody(type);
1141                if (body != null) {
1142                    // ognl is able to evaluate method name if it contains nested functions
1143                    // so we should not eager evaluate ognl as a string
1144                    MethodCallExpression call = new MethodCallExpression(exchange, ognl);
1145                    // set the instance to use
1146                    call.setInstance(body);
1147                    return call.evaluate(exchange);
1148                } else {
1149                    return null;
1150                }
1151            }
1152
1153            @Override
1154            public String toString() {
1155                return "bodyOgnlAs[" + name + "](" + ognl + ")";
1156            }
1157        };
1158    }
1159
1160    /**
1161     * Returns the expression for the exchanges inbound message body converted
1162     * to the given type
1163     */
1164    public static Expression mandatoryBodyExpression(final String name) {
1165        return new ExpressionAdapter() {
1166            public Object evaluate(Exchange exchange) {
1167                String text = simpleExpression(name).evaluate(exchange, String.class);
1168                Class<?> type;
1169                try {
1170                    type = exchange.getContext().getClassResolver().resolveMandatoryClass(text);
1171                } catch (ClassNotFoundException e) {
1172                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
1173                }
1174                try {
1175                    return exchange.getIn().getMandatoryBody(type);
1176                } catch (InvalidPayloadException e) {
1177                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
1178                }
1179            }
1180
1181            @Override
1182            public String toString() {
1183                return "mandatoryBodyAs[" + name + "]";
1184            }
1185        };
1186    }
1187
1188    /**
1189     * Returns the expression for the exchanges inbound message body converted
1190     * to the given type and invoking methods on the converted body defined in a simple OGNL notation
1191     */
1192    public static Expression mandatoryBodyOgnlExpression(final String name, final String ognl) {
1193        return new ExpressionAdapter() {
1194            public Object evaluate(Exchange exchange) {
1195                String text = simpleExpression(name).evaluate(exchange, String.class);
1196                Class<?> type;
1197                try {
1198                    type = exchange.getContext().getClassResolver().resolveMandatoryClass(text);
1199                } catch (ClassNotFoundException e) {
1200                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
1201                }
1202                Object body;
1203                try {
1204                    body = exchange.getIn().getMandatoryBody(type);
1205                } catch (InvalidPayloadException e) {
1206                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
1207                }
1208                // ognl is able to evaluate method name if it contains nested functions
1209                // so we should not eager evaluate ognl as a string
1210                MethodCallExpression call = new MethodCallExpression(exchange, ognl);
1211                // set the instance to use
1212                call.setInstance(body);
1213                return call.evaluate(exchange);
1214            }
1215
1216            @Override
1217            public String toString() {
1218                return "mandatoryBodyAs[" + name + "](" + ognl + ")";
1219            }
1220        };
1221    }
1222
1223    /**
1224     * Returns the expression for the current thread name
1225     */
1226    public static Expression threadNameExpression() {
1227        return new ExpressionAdapter() {
1228            public Object evaluate(Exchange exchange) {
1229                return Thread.currentThread().getName();
1230            }
1231
1232            @Override
1233            public String toString() {
1234                return "threadName";
1235            }
1236        };
1237    }
1238
1239    /**
1240     * Returns the expression for the {@code null} value
1241     */
1242    public static Expression nullExpression() {
1243        return new ExpressionAdapter() {
1244            public Object evaluate(Exchange exchange) {
1245                return null;
1246            }
1247
1248            @Override
1249            public String toString() {
1250                return "null";
1251            }
1252        };
1253    }
1254
1255    /**
1256     * Returns the expression for the exchanges inbound message body converted
1257     * to the given type.
1258     * <p/>
1259     * Does <b>not</b> allow null bodies.
1260     */
1261    public static <T> Expression mandatoryBodyExpression(final Class<T> type) {
1262        return mandatoryBodyExpression(type, false);
1263    }
1264
1265    /**
1266     * Returns the expression for the exchanges inbound message body converted
1267     * to the given type
1268     *
1269     * @param type the type
1270     * @param nullBodyAllowed whether null bodies is allowed and if so a null is returned,
1271     *                        otherwise an exception is thrown
1272     */
1273    public static <T> Expression mandatoryBodyExpression(final Class<T> type, final boolean nullBodyAllowed) {
1274        return new ExpressionAdapter() {
1275            public Object evaluate(Exchange exchange) {
1276                if (nullBodyAllowed) {
1277                    if (exchange.getIn().getBody() == null) {
1278                        return null;
1279                    }
1280
1281                    // if its a bean invocation then if it has no arguments then it should be threaded as null body allowed
1282                    if (exchange.getIn().getBody() instanceof BeanInvocation) {
1283                        // BeanInvocation would be stored directly as the message body
1284                        // do not force any type conversion attempts as it would just be unnecessary and cost a bit performance
1285                        // so a regular instanceof check is sufficient
1286                        BeanInvocation bi = (BeanInvocation) exchange.getIn().getBody();
1287                        if (bi.getArgs() == null || bi.getArgs().length == 0 || bi.getArgs()[0] == null) {
1288                            return null;
1289                        }
1290                    }
1291                }
1292
1293                try {
1294                    return exchange.getIn().getMandatoryBody(type);
1295                } catch (InvalidPayloadException e) {
1296                    throw ObjectHelper.wrapCamelExecutionException(exchange, e);
1297                }
1298            }
1299
1300            @Override
1301            public String toString() {
1302                return "mandatoryBodyAs[" + type.getName() + "]";
1303            }
1304        };
1305    }
1306
1307    /**
1308     * Returns the expression for the exchanges inbound message body type
1309     */
1310    public static Expression bodyTypeExpression() {
1311        return new ExpressionAdapter() {
1312            public Object evaluate(Exchange exchange) {
1313                return exchange.getIn().getBody().getClass();
1314            }
1315
1316            @Override
1317            public String toString() {
1318                return "bodyType";
1319            }
1320        };
1321    }
1322
1323    /**
1324     * Returns the expression for the out messages body
1325     */
1326    public static Expression outBodyExpression() {
1327        return new ExpressionAdapter() {
1328            public Object evaluate(Exchange exchange) {
1329                if (exchange.hasOut()) {
1330                    return exchange.getOut().getBody();
1331                } else {
1332                    return null;
1333                }
1334            }
1335
1336            @Override
1337            public String toString() {
1338                return "outBody";
1339            }
1340        };
1341    }
1342
1343    /**
1344     * Returns the expression for the exchanges outbound message body converted
1345     * to the given type
1346     */
1347    public static <T> Expression outBodyExpression(final Class<T> type) {
1348        return new ExpressionAdapter() {
1349            public Object evaluate(Exchange exchange) {
1350                if (exchange.hasOut()) {
1351                    return exchange.getOut().getBody(type);
1352                } else {
1353                    return null;
1354                }
1355            }
1356
1357            @Override
1358            public String toString() {
1359                return "outBodyAs[" + type.getName() + "]";
1360            }
1361        };
1362    }
1363
1364    /**
1365     * Returns the expression for the fault messages body
1366     */
1367    public static Expression faultBodyExpression() {
1368        return new ExpressionAdapter() {
1369            public Object evaluate(Exchange exchange) {
1370                Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
1371                return msg.isFault() ? msg.getBody() : null;
1372            }
1373
1374            @Override
1375            public String toString() {
1376                return "faultBody";
1377            }
1378        };
1379    }
1380
1381    /**
1382     * Returns the expression for the exchanges fault message body converted
1383     * to the given type
1384     */
1385    public static <T> Expression faultBodyExpression(final Class<T> type) {
1386        return new ExpressionAdapter() {
1387            public Object evaluate(Exchange exchange) {
1388                Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
1389                return msg.isFault() ? msg.getBody(type) : null;
1390            }
1391
1392            @Override
1393            public String toString() {
1394                return "faultBodyAs[" + type.getName() + "]";
1395            }
1396        };
1397    }
1398
1399    /**
1400     * Returns the expression for the exchange
1401     */
1402    public static Expression exchangeExpression() {
1403        return new ExpressionAdapter() {
1404            public Object evaluate(Exchange exchange) {
1405                return exchange;
1406            }
1407
1408            @Override
1409            public String toString() {
1410                return "exchange";
1411            }
1412        };
1413    }
1414
1415    /**
1416     * Returns a functional expression for the exchange
1417     */
1418    public static Expression exchangeExpression(final Function<Exchange, Object> function) {
1419        return new ExpressionAdapter() {
1420            public Object evaluate(Exchange exchange) {
1421                return function.apply(exchange);
1422            }
1423
1424            @Override
1425            public String toString() {
1426                return "exchangeExpression";
1427            }
1428        };
1429    }
1430
1431    /**
1432     * Returns the expression for the IN message
1433     */
1434    public static Expression messageExpression() {
1435        return inMessageExpression();
1436    }
1437
1438    /**
1439     * Returns a functional expression for the IN message
1440     */
1441    public static Expression messageExpression(final Function<Message, Object> function) {
1442        return inMessageExpression(function);
1443    }
1444
1445    /**
1446     * Returns the expression for the IN message
1447     */
1448    public static Expression inMessageExpression() {
1449        return new ExpressionAdapter() {
1450            public Object evaluate(Exchange exchange) {
1451                return exchange.getIn();
1452            }
1453
1454            @Override
1455            public String toString() {
1456                return "inMessage";
1457            }
1458        };
1459    }
1460
1461    /**
1462     * Returns a functional expression for the IN message
1463     */
1464    public static Expression inMessageExpression(final Function<Message, Object> function) {
1465        return new ExpressionAdapter() {
1466            public Object evaluate(Exchange exchange) {
1467                return function.apply(exchange.getIn());
1468            }
1469
1470            @Override
1471            public String toString() {
1472                return "inMessageExpression";
1473            }
1474        };
1475    }
1476
1477    /**
1478     * Returns the expression for the OUT message
1479     */
1480    public static Expression outMessageExpression() {
1481        return new ExpressionAdapter() {
1482            public Object evaluate(Exchange exchange) {
1483                return exchange.getOut();
1484            }
1485
1486            @Override
1487            public String toString() {
1488                return "outMessage";
1489            }
1490        };
1491    }
1492
1493    /**
1494     * Returns a functional expression for the OUT message
1495     */
1496    public static Expression outMessageExpression(final Function<Message, Object> function) {
1497        return new ExpressionAdapter() {
1498            public Object evaluate(Exchange exchange) {
1499                return function.apply(exchange.getOut());
1500            }
1501
1502            @Override
1503            public String toString() {
1504                return "outMessageExpression";
1505            }
1506        };
1507    }
1508
1509    /**
1510     * Returns an expression which converts the given expression to the given type
1511     */
1512    public static Expression convertToExpression(final Expression expression, final Class<?> type) {
1513        return new ExpressionAdapter() {
1514            public Object evaluate(Exchange exchange) {
1515                if (type != null) {
1516                    return expression.evaluate(exchange, type);
1517                } else {
1518                    return expression;
1519                }
1520            }
1521
1522            @Override
1523            public String toString() {
1524                return "" + expression;
1525            }
1526        };
1527    }
1528
1529    /**
1530     * Returns an expression which converts the given expression to the given type the type
1531     * expression is evaluated to
1532     */
1533    public static Expression convertToExpression(final Expression expression, final Expression type) {
1534        return new ExpressionAdapter() {
1535            public Object evaluate(Exchange exchange) {
1536                Object result = type.evaluate(exchange, Object.class);
1537                if (result != null) {
1538                    return expression.evaluate(exchange, result.getClass());
1539                } else {
1540                    return expression;
1541                }
1542            }
1543
1544            @Override
1545            public String toString() {
1546                return "" + expression;
1547            }
1548        };
1549    }
1550
1551    /**
1552     * Returns a tokenize expression which will tokenize the string with the
1553     * given token
1554     */
1555    public static Expression tokenizeExpression(final Expression expression,
1556                                                final String token) {
1557        return new ExpressionAdapter() {
1558            public Object evaluate(Exchange exchange) {
1559                String text = simpleExpression(token).evaluate(exchange, String.class);
1560                Object value = expression.evaluate(exchange, Object.class);
1561                Scanner scanner = ObjectHelper.getScanner(exchange, value);
1562                scanner.useDelimiter(text);
1563                return scanner;
1564            }
1565
1566            @Override
1567            public String toString() {
1568                return "tokenize(" + expression + ", " + token + ")";
1569            }
1570        };
1571    }
1572
1573    /**
1574     * Returns an expression that skips the first element
1575     */
1576    public static Expression skipFirstExpression(final Expression expression) {
1577        return new ExpressionAdapter() {
1578            public Object evaluate(Exchange exchange) {
1579                Object value = expression.evaluate(exchange, Object.class);
1580                Iterator it = exchange.getContext().getTypeConverter().tryConvertTo(Iterator.class, exchange, value);
1581                if (it != null) {
1582                    // skip first
1583                    it.next();
1584                    return it;
1585                } else {
1586                    return value;
1587                }
1588            }
1589
1590            @Override
1591            public String toString() {
1592                return "skipFirst(" + expression + ")";
1593            }
1594        };
1595    }
1596
1597    /**
1598     * Returns an {@link TokenPairExpressionIterator} expression
1599     */
1600    public static Expression tokenizePairExpression(String startToken, String endToken, boolean includeTokens) {
1601        return new TokenPairExpressionIterator(startToken, endToken, includeTokens);
1602    }
1603
1604    /**
1605     * Returns an {@link TokenXMLExpressionIterator} expression
1606     */
1607    public static Expression tokenizeXMLExpression(String tagName, String inheritNamespaceTagName) {
1608        ObjectHelper.notEmpty(tagName, "tagName");
1609        return new TokenXMLExpressionIterator(tagName, inheritNamespaceTagName);
1610    }
1611
1612    public static Expression tokenizeXMLAwareExpression(String path, char mode) {
1613        ObjectHelper.notEmpty(path, "path");
1614        return new XMLTokenExpressionIterator(path, mode);
1615    }
1616
1617    public static Expression tokenizeXMLAwareExpression(String path, char mode, int group) {
1618        ObjectHelper.notEmpty(path, "path");
1619        return new XMLTokenExpressionIterator(path, mode, group);
1620    }
1621
1622    /**
1623     * Returns a tokenize expression which will tokenize the string with the
1624     * given regex
1625     */
1626    public static Expression regexTokenizeExpression(final Expression expression,
1627                                                     final String regexTokenizer) {
1628        final Pattern pattern = Pattern.compile(regexTokenizer);
1629        return new ExpressionAdapter() {
1630            public Object evaluate(Exchange exchange) {
1631                Object value = expression.evaluate(exchange, Object.class);
1632                Scanner scanner = ObjectHelper.getScanner(exchange, value);
1633                scanner.useDelimiter(pattern);
1634                return scanner;
1635            }
1636
1637            @Override
1638            public String toString() {
1639                return "regexTokenize(" + expression + ", " + pattern.pattern() + ")";
1640            }
1641        };
1642    }
1643
1644    public static Expression groupXmlIteratorExpression(final Expression expression, final String group) {
1645        return new ExpressionAdapter() {
1646            public Object evaluate(Exchange exchange) {
1647                // evaluate expression as iterator
1648                Iterator<?> it = expression.evaluate(exchange, Iterator.class);
1649                ObjectHelper.notNull(it, "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator");
1650                // must use GroupTokenIterator in xml mode as we want to concat the xml parts into a single message
1651                // the group can be a simple expression so evaluate it as a number
1652                Integer parts = exchange.getContext().resolveLanguage("simple").createExpression(group).evaluate(exchange, Integer.class);
1653                if (parts == null) {
1654                    throw new RuntimeExchangeException("Group evaluated as null, must be evaluated as a positive Integer value from expression: " + group, exchange);
1655                } else if (parts <= 0) {
1656                    throw new RuntimeExchangeException("Group must be a positive number, was: " + parts, exchange);
1657                }
1658                return new GroupTokenIterator(exchange, it, null, parts, false);
1659            }
1660
1661            @Override
1662            public String toString() {
1663                return "group " + expression + " " + group + " times";
1664            }
1665        };
1666    }
1667
1668    public static Expression groupIteratorExpression(final Expression expression, final String token, final String group, final boolean skipFirst) {
1669        return new ExpressionAdapter() {
1670            public Object evaluate(Exchange exchange) {
1671                // evaluate expression as iterator
1672                Iterator<?> it = expression.evaluate(exchange, Iterator.class);
1673                ObjectHelper.notNull(it, "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator");
1674                // the group can be a simple expression so evaluate it as a number
1675                Integer parts = exchange.getContext().resolveLanguage("simple").createExpression(group).evaluate(exchange, Integer.class);
1676                if (parts == null) {
1677                    throw new RuntimeExchangeException("Group evaluated as null, must be evaluated as a positive Integer value from expression: " + group, exchange);
1678                } else if (parts <= 0) {
1679                    throw new RuntimeExchangeException("Group must be a positive number, was: " + parts, exchange);
1680                }
1681                if (token != null) {
1682                    return new GroupTokenIterator(exchange, it, token, parts, skipFirst);
1683                } else {
1684                    return new GroupIterator(exchange, it, parts, skipFirst);
1685                }
1686            }
1687
1688            @Override
1689            public String toString() {
1690                return "group " + expression + " " + group + " times";
1691            }
1692        };
1693    }
1694
1695    public static Expression skipIteratorExpression(final Expression expression, final int skip) {
1696        return new ExpressionAdapter() {
1697            public Object evaluate(Exchange exchange) {
1698                // evaluate expression as iterator
1699                Iterator<?> it = expression.evaluate(exchange, Iterator.class);
1700                ObjectHelper.notNull(it, "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator");
1701                return new SkipIterator(exchange, it, skip);
1702            }
1703
1704            @Override
1705            public String toString() {
1706                return "skip " + expression + " " + skip + " times";
1707            }
1708        };
1709    }
1710
1711    /**
1712     * Returns a sort expression which will sort the expression with the given comparator.
1713     * <p/>
1714     * The expression is evaluated as a {@link List} object to allow sorting.
1715     */
1716    @SuppressWarnings({"unchecked", "rawtypes"})
1717    public static Expression sortExpression(final Expression expression, final Comparator comparator) {
1718        return new ExpressionAdapter() {
1719            public Object evaluate(Exchange exchange) {
1720                List<?> list = expression.evaluate(exchange, List.class);
1721                list.sort(comparator);
1722                return list;
1723            }
1724
1725            @Override
1726            public String toString() {
1727                return "sort(" + expression + " by: " + comparator + ")";
1728            }
1729        };
1730    }
1731
1732    /**
1733     * Transforms the expression into a String then performs the regex
1734     * replaceAll to transform the String and return the result
1735     */
1736    public static Expression regexReplaceAll(final Expression expression,
1737                                             final String regex, final String replacement) {
1738        final Pattern pattern = Pattern.compile(regex);
1739        return new ExpressionAdapter() {
1740            public Object evaluate(Exchange exchange) {
1741                String text = expression.evaluate(exchange, String.class);
1742                if (text == null) {
1743                    return null;
1744                }
1745                return pattern.matcher(text).replaceAll(replacement);
1746            }
1747
1748            @Override
1749            public String toString() {
1750                return "regexReplaceAll(" + expression + ", " + pattern.pattern() + ")";
1751            }
1752        };
1753    }
1754
1755    /**
1756     * Transforms the expression into a String then performs the regex
1757     * replaceAll to transform the String and return the result
1758     */
1759    public static Expression regexReplaceAll(final Expression expression,
1760                                             final String regex, final Expression replacementExpression) {
1761
1762        final Pattern pattern = Pattern.compile(regex);
1763        return new ExpressionAdapter() {
1764            public Object evaluate(Exchange exchange) {
1765                String text = expression.evaluate(exchange, String.class);
1766                String replacement = replacementExpression.evaluate(exchange, String.class);
1767                if (text == null || replacement == null) {
1768                    return null;
1769                }
1770                return pattern.matcher(text).replaceAll(replacement);
1771            }
1772
1773            @Override
1774            public String toString() {
1775                return "regexReplaceAll(" + expression + ", " + pattern.pattern() + ")";
1776            }
1777        };
1778    }
1779
1780    /**
1781     * Appends the String evaluations of the two expressions together
1782     */
1783    public static Expression append(final Expression left, final Expression right) {
1784        return new ExpressionAdapter() {
1785            public Object evaluate(Exchange exchange) {
1786                return left.evaluate(exchange, String.class) + right.evaluate(exchange, String.class);
1787            }
1788
1789            @Override
1790            public String toString() {
1791                return "append(" + left + ", " + right + ")";
1792            }
1793        };
1794    }
1795
1796    /**
1797     * Prepends the String evaluations of the two expressions together
1798     */
1799    public static Expression prepend(final Expression left, final Expression right) {
1800        return new ExpressionAdapter() {
1801            public Object evaluate(Exchange exchange) {
1802                return right.evaluate(exchange, String.class) + left.evaluate(exchange, String.class);
1803            }
1804
1805            @Override
1806            public String toString() {
1807                return "prepend(" + left + ", " + right + ")";
1808            }
1809        };
1810    }
1811
1812    /**
1813     * Returns an expression which returns the string concatenation value of the various
1814     * expressions
1815     *
1816     * @param expressions the expression to be concatenated dynamically
1817     * @return an expression which when evaluated will return the concatenated values
1818     */
1819    public static Expression concatExpression(final Collection<Expression> expressions) {
1820        return concatExpression(expressions, null);
1821    }
1822
1823    /**
1824     * Returns an expression which returns the string concatenation value of the various
1825     * expressions
1826     *
1827     * @param expressions the expression to be concatenated dynamically
1828     * @param desription the text description of the expression
1829     * @return an expression which when evaluated will return the concatenated values
1830     */
1831    public static Expression concatExpression(final Collection<Expression> expressions, final String desription) {
1832        return new ExpressionAdapter() {
1833            public Object evaluate(Exchange exchange) {
1834                StringBuilder buffer = new StringBuilder();
1835                for (Expression expression : expressions) {
1836                    String text = expression.evaluate(exchange, String.class);
1837                    if (text != null) {
1838                        buffer.append(text);
1839                    }
1840                }
1841                return buffer.toString();
1842            }
1843
1844            @Override
1845            public String toString() {
1846                if (desription != null) {
1847                    return desription;
1848                } else {
1849                    return "concat" + expressions;
1850                }
1851            }
1852        };
1853    }
1854
1855    /**
1856     * Returns an Expression for the inbound message id
1857     */
1858    public static Expression messageIdExpression() {
1859        return new ExpressionAdapter() {
1860            public Object evaluate(Exchange exchange) {
1861                return exchange.getIn().getMessageId();
1862            }
1863
1864            @Override
1865            public String toString() {
1866                return "messageId";
1867            }
1868        };
1869    }
1870
1871    /**
1872     * Returns an Expression for the exchange id
1873     */
1874    public static Expression exchangeIdExpression() {
1875        return new ExpressionAdapter() {
1876            public Object evaluate(Exchange exchange) {
1877                return exchange.getExchangeId();
1878            }
1879
1880            @Override
1881            public String toString() {
1882                return "exchangeId";
1883            }
1884        };
1885    }
1886
1887    /**
1888     * Returns an Expression for the route id
1889     */
1890    public static Expression routeIdExpression() {
1891        return new ExpressionAdapter() {
1892            public Object evaluate(Exchange exchange) {
1893                String answer = null;
1894                UnitOfWork uow = exchange.getUnitOfWork();
1895                RouteContext rc = uow != null ? uow.getRouteContext() : null;
1896                if (rc != null) {
1897                    answer = rc.getRoute().getId();
1898                }
1899                if (answer == null) {
1900                    // fallback and get from route id on the exchange
1901                    answer = exchange.getFromRouteId();
1902                }
1903                return answer;
1904            }
1905
1906            @Override
1907            public String toString() {
1908                return "routeId";
1909            }
1910        };
1911    }
1912
1913    public static Expression dateExpression(final String command) {
1914        return dateExpression(command, null, null);
1915    }
1916
1917    public static Expression dateExpression(final String command, final String pattern) {
1918        return dateExpression(command, null, pattern);
1919    }
1920
1921    public static Expression dateExpression(final String commandWithOffsets, final String timezone, final String pattern) {
1922        return new ExpressionAdapter() {
1923            public Object evaluate(Exchange exchange) {
1924                // Capture optional time offsets
1925                String command = commandWithOffsets.split("[+-]", 2)[0].trim();
1926                List<Long> offsets = new ArrayList<>();
1927                Matcher offsetMatcher = OFFSET_PATTERN.matcher(commandWithOffsets);
1928                while (offsetMatcher.find()) {
1929                    try {
1930                        long value = exchange.getContext().getTypeConverter().mandatoryConvertTo(long.class, exchange, offsetMatcher.group(2).trim());
1931                        offsets.add(offsetMatcher.group(1).equals("+") ? value : -value);
1932                    } catch (NoTypeConversionAvailableException e) {
1933                        throw ObjectHelper.wrapCamelExecutionException(exchange, e);
1934                    }
1935                }
1936
1937                Date date;
1938                if ("now".equals(command)) {
1939                    date = new Date();
1940                } else if (command.startsWith("header.") || command.startsWith("in.header.")) {
1941                    String key = command.substring(command.lastIndexOf('.') + 1);
1942                    date = exchange.getIn().getHeader(key, Date.class);
1943                    if (date == null) {
1944                        throw new IllegalArgumentException("Cannot find java.util.Date object at command: " + command);
1945                    }
1946                } else if (command.startsWith("out.header.")) {
1947                    String key = command.substring(command.lastIndexOf('.') + 1);
1948                    date = exchange.getOut().getHeader(key, Date.class);
1949                    if (date == null) {
1950                        throw new IllegalArgumentException("Cannot find java.util.Date object at command: " + command);
1951                    }
1952                } else if (command.startsWith("property.")) {
1953                    String key = command.substring(command.lastIndexOf('.') + 1);
1954                    date = exchange.getProperty(key, Date.class);
1955                    if (date == null) {
1956                        throw new IllegalArgumentException("Cannot find java.util.Date object at command: " + command);
1957                    }
1958                } else if ("file".equals(command)) {
1959                    Long num = exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class);
1960                    if (num != null && num > 0) {
1961                        date = new Date(num);
1962                    } else {
1963                        date = exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Date.class);
1964                        if (date == null) {
1965                            throw new IllegalArgumentException("Cannot find " + Exchange.FILE_LAST_MODIFIED + " header at command: " + command);
1966                        }
1967                    }
1968                } else {
1969                    throw new IllegalArgumentException("Command not supported for dateExpression: " + command);
1970                }
1971
1972                // Apply offsets
1973                long dateAsLong = date.getTime();
1974                for (long offset : offsets) {
1975                    dateAsLong += offset;
1976                }
1977                date = new Date(dateAsLong);
1978
1979                if (pattern != null && !pattern.isEmpty()) {
1980                    SimpleDateFormat df = new SimpleDateFormat(pattern);
1981                    if (timezone != null && !timezone.isEmpty()) {
1982                        df.setTimeZone(TimeZone.getTimeZone(timezone));
1983                    }
1984                    return df.format(date);
1985                } else {
1986                    return date;
1987                }
1988            }
1989
1990            @Override
1991            public String toString() {
1992                return "date(" + commandWithOffsets + ":" + pattern + ":" + timezone + ")";
1993            }
1994        };
1995    }
1996
1997    public static Expression simpleExpression(final String expression) {
1998        return new ExpressionAdapter() {
1999            public Object evaluate(Exchange exchange) {
2000                if (SimpleLanguage.hasSimpleFunction(expression)) {
2001                    // resolve language using context to have a clear separation of packages
2002                    // must call evaluate to return the nested language evaluate when evaluating
2003                    // stacked expressions
2004                    Language language = exchange.getContext().resolveLanguage("simple");
2005                    return language.createExpression(expression).evaluate(exchange, Object.class);
2006                } else {
2007                    return expression;
2008                }
2009            }
2010
2011            @Override
2012            public String toString() {
2013                return "simple(" + expression + ")";
2014            }
2015        };
2016    }
2017
2018    public static Expression beanExpression(final String expression) {
2019        return new ExpressionAdapter() {
2020            public Object evaluate(Exchange exchange) {
2021                // bean is able to evaluate method name if it contains nested functions
2022                // so we should not eager evaluate expression as a string
2023                // resolve language using context to have a clear separation of packages
2024                // must call evaluate to return the nested language evaluate when evaluating
2025                // stacked expressions
2026                Language language = exchange.getContext().resolveLanguage("bean");
2027                return language.createExpression(expression).evaluate(exchange, Object.class);
2028            }
2029
2030            @Override
2031            public String toString() {
2032                return "bean(" + expression + ")";
2033            }
2034        };
2035    }
2036
2037    public static Expression beanExpression(final Class<?> beanType, final String methodName) {
2038        return BeanLanguage.bean(beanType, methodName);
2039    }
2040
2041    public static Expression beanExpression(final Object bean, final String methodName) {
2042        return BeanLanguage.bean(bean, methodName);
2043    }
2044
2045    public static Expression beanExpression(final String beanRef, final String methodName) {
2046        String expression = methodName != null ? beanRef + "." + methodName : beanRef;
2047        return beanExpression(expression);
2048    }
2049
2050    /**
2051     * Returns an expression processing the exchange to the given endpoint uri
2052     *
2053     * @param uri endpoint uri to send the exchange to
2054     * @return an expression object which will return the OUT body
2055     */
2056    public static Expression toExpression(final String uri) {
2057        return new ExpressionAdapter() {
2058            public Object evaluate(Exchange exchange) {
2059                String text = simpleExpression(uri).evaluate(exchange, String.class);
2060                Endpoint endpoint = exchange.getContext().getEndpoint(text);
2061                if (endpoint == null) {
2062                    throw new NoSuchEndpointException(text);
2063                }
2064
2065                Producer producer;
2066                try {
2067                    producer = endpoint.createProducer();
2068                    producer.start();
2069                    producer.process(exchange);
2070                    producer.stop();
2071                } catch (Exception e) {
2072                    throw ObjectHelper.wrapRuntimeCamelException(e);
2073                }
2074
2075                // return the OUT body, but check for exchange pattern
2076                if (ExchangeHelper.isOutCapable(exchange)) {
2077                    return exchange.getOut().getBody();
2078                } else {
2079                    return exchange.getIn().getBody();
2080                }
2081            }
2082
2083            @Override
2084            public String toString() {
2085                return "to(" + uri + ")";
2086            }
2087        };
2088    }
2089
2090    public static Expression fileNameExpression() {
2091        return new ExpressionAdapter() {
2092            public Object evaluate(Exchange exchange) {
2093                return exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
2094            }
2095
2096            @Override
2097            public String toString() {
2098                return "file:name";
2099            }
2100        };
2101    }
2102
2103    public static Expression fileOnlyNameExpression() {
2104        return new ExpressionAdapter() {
2105            public Object evaluate(Exchange exchange) {
2106                String answer = exchange.getIn().getHeader(Exchange.FILE_NAME_ONLY, String.class);
2107                if (answer == null) {
2108                    answer = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
2109                    answer = FileUtil.stripPath(answer);
2110                }
2111                return answer;
2112            }
2113
2114            @Override
2115            public String toString() {
2116                return "file:onlyname";
2117            }
2118        };
2119    }
2120
2121    public static Expression fileNameNoExtensionExpression() {
2122        return new ExpressionAdapter() {
2123            public Object evaluate(Exchange exchange) {
2124                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
2125                return FileUtil.stripExt(name);
2126            }
2127
2128            @Override
2129            public String toString() {
2130                return "file:name.noext";
2131            }
2132        };
2133    }
2134
2135    public static Expression fileNameNoExtensionSingleExpression() {
2136        return new ExpressionAdapter() {
2137            public Object evaluate(Exchange exchange) {
2138                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
2139                return FileUtil.stripExt(name, true);
2140            }
2141
2142            @Override
2143            public String toString() {
2144                return "file:name.noext.single";
2145            }
2146        };
2147    }
2148
2149    public static Expression fileOnlyNameNoExtensionExpression() {
2150        return new ExpressionAdapter() {
2151            public Object evaluate(Exchange exchange) {
2152                String name = fileOnlyNameExpression().evaluate(exchange, String.class);
2153                return FileUtil.stripExt(name);
2154            }
2155
2156            @Override
2157            public String toString() {
2158                return "file:onlyname.noext";
2159            }
2160        };
2161    }
2162
2163    public static Expression fileOnlyNameNoExtensionSingleExpression() {
2164        return new ExpressionAdapter() {
2165            public Object evaluate(Exchange exchange) {
2166                String name = fileOnlyNameExpression().evaluate(exchange, String.class);
2167                return FileUtil.stripExt(name, true);
2168            }
2169
2170            @Override
2171            public String toString() {
2172                return "file:onlyname.noext.single";
2173            }
2174        };
2175    }
2176
2177    public static Expression fileExtensionExpression() {
2178        return new ExpressionAdapter() {
2179            public Object evaluate(Exchange exchange) {
2180                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
2181                return FileUtil.onlyExt(name);
2182            }
2183
2184            @Override
2185            public String toString() {
2186                return "file:ext";
2187            }
2188        };
2189    }
2190
2191    public static Expression fileExtensionSingleExpression() {
2192        return new ExpressionAdapter() {
2193            public Object evaluate(Exchange exchange) {
2194                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
2195                return FileUtil.onlyExt(name, true);
2196            }
2197
2198            @Override
2199            public String toString() {
2200                return "file:ext.single";
2201            }
2202        };
2203    }
2204
2205    public static Expression fileParentExpression() {
2206        return new ExpressionAdapter() {
2207            public Object evaluate(Exchange exchange) {
2208                return exchange.getIn().getHeader("CamelFileParent", String.class);
2209            }
2210
2211            @Override
2212            public String toString() {
2213                return "file:parent";
2214            }
2215        };
2216    }
2217
2218    public static Expression filePathExpression() {
2219        return new ExpressionAdapter() {
2220            public Object evaluate(Exchange exchange) {
2221                return exchange.getIn().getHeader("CamelFilePath", String.class);
2222            }
2223
2224            @Override
2225            public String toString() {
2226                return "file:path";
2227            }
2228        };
2229    }
2230
2231    public static Expression fileAbsolutePathExpression() {
2232        return new ExpressionAdapter() {
2233            public Object evaluate(Exchange exchange) {
2234                return exchange.getIn().getHeader("CamelFileAbsolutePath", String.class);
2235            }
2236
2237            @Override
2238            public String toString() {
2239                return "file:absolute.path";
2240            }
2241        };
2242    }
2243
2244    public static Expression fileAbsoluteExpression() {
2245        return new ExpressionAdapter() {
2246            public Object evaluate(Exchange exchange) {
2247                return exchange.getIn().getHeader("CamelFileAbsolute", Boolean.class);
2248            }
2249
2250            @Override
2251            public String toString() {
2252                return "file:absolute";
2253            }
2254        };
2255    }
2256
2257    public static Expression fileSizeExpression() {
2258        return new ExpressionAdapter() {
2259            public Object evaluate(Exchange exchange) {
2260                return exchange.getIn().getHeader(Exchange.FILE_LENGTH, Long.class);
2261            }
2262
2263            @Override
2264            public String toString() {
2265                return "file:length";
2266            }
2267        };
2268    }
2269
2270    public static Expression fileLastModifiedExpression() {
2271        return new ExpressionAdapter() {
2272            public Object evaluate(Exchange exchange) {
2273                return exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class);
2274            }
2275
2276            @Override
2277            public String toString() {
2278                return "file:modified";
2279            }
2280        };
2281    }
2282
2283    /**
2284     * Returns Simple expression or fallback to Constant expression if expression str is not Simple expression.
2285     */
2286    public static Expression parseSimpleOrFallbackToConstantExpression(String str, CamelContext camelContext) {
2287        if (StringHelper.hasStartToken(str, "simple")) {
2288            return camelContext.resolveLanguage("simple").createExpression(str);
2289        }
2290        return constantExpression(str);
2291    }
2292
2293    public static Expression propertiesComponentExpression(final String key, final String locations, final String defaultValue) {
2294        return new ExpressionAdapter() {
2295            public Object evaluate(Exchange exchange) {
2296                String text = simpleExpression(key).evaluate(exchange, String.class);
2297                String text2 = simpleExpression(locations).evaluate(exchange, String.class);
2298                try {
2299                    if (text2 != null) {
2300                        // the properties component is optional as we got locations
2301                        // getComponent will create a new component if none already exists
2302                        Component component = exchange.getContext().getComponent("properties");
2303                        PropertiesComponent pc = exchange.getContext().getTypeConverter()
2304                            .mandatoryConvertTo(PropertiesComponent.class, component);
2305                        // enclose key with {{ }} to force parsing
2306                        String[] paths = text2.split(",");
2307                        return pc.parseUri(pc.getPrefixToken() + text + pc.getSuffixToken(), paths);
2308                    } else {
2309                        // the properties component is mandatory if no locations provided
2310                        Component component = exchange.getContext().hasComponent("properties");
2311                        if (component == null) {
2312                            throw new IllegalArgumentException("PropertiesComponent with name properties must be defined"
2313                                + " in CamelContext to support property placeholders in expressions");
2314                        }
2315                        PropertiesComponent pc = exchange.getContext().getTypeConverter()
2316                            .mandatoryConvertTo(PropertiesComponent.class, component);
2317                        // enclose key with {{ }} to force parsing
2318                        return pc.parseUri(pc.getPrefixToken() + text + pc.getSuffixToken());
2319                    }
2320                } catch (Exception e) {
2321                    // property with key not found, use default value if provided
2322                    if (defaultValue != null) {
2323                        return defaultValue;
2324                    }
2325                    throw ObjectHelper.wrapRuntimeCamelException(e);
2326                }
2327            }
2328
2329            @Override
2330            public String toString() {
2331                return "properties(" + key + ")";
2332            }
2333        };
2334    }
2335
2336    /**
2337     * Returns a random number between 0 and max (exclusive)
2338     */
2339    public static Expression randomExpression(final int max) {
2340        return randomExpression(0, max);
2341    }
2342
2343    /**
2344     * Returns a random number between min and max (exclusive)
2345     */
2346    public static Expression randomExpression(final int min, final int max) {
2347        return new ExpressionAdapter() {
2348            public Object evaluate(Exchange exchange) {
2349                Random random = new Random();
2350                int randomNum = random.nextInt(max - min) + min;
2351                return randomNum;
2352            }
2353
2354            @Override
2355            public String toString() {
2356                return "random(" + min + "," + max + ")";
2357            }
2358        };
2359    }
2360
2361    /**
2362     * Returns a random number between min and max (exclusive)
2363     */
2364    public static Expression randomExpression(final String min, final String max) {
2365        return new ExpressionAdapter() {
2366            public Object evaluate(Exchange exchange) {
2367                int num1 = simpleExpression(min).evaluate(exchange, Integer.class);
2368                int num2 = simpleExpression(max).evaluate(exchange, Integer.class);
2369                Random random = new Random();
2370                int randomNum = random.nextInt(num2 - num1) + num1;
2371                return randomNum;
2372            }
2373
2374            @Override
2375            public String toString() {
2376                return "random(" + min + "," + max + ")";
2377            }
2378        };
2379    }
2380
2381    /**
2382     * Returns an iterator to skip (iterate) the given expression
2383     */
2384    public static Expression skipExpression(final String expression, final int number) {
2385        return new ExpressionAdapter() {
2386            public Object evaluate(Exchange exchange) {
2387                // use simple language
2388                Expression exp = exchange.getContext().resolveLanguage("simple").createExpression(expression);
2389                return ExpressionBuilder.skipIteratorExpression(exp, number).evaluate(exchange, Object.class);
2390            }
2391
2392            @Override
2393            public String toString() {
2394                return "skip(" + expression + "," + number + ")";
2395            }
2396        };
2397    }
2398
2399    /**
2400     * Returns an iterator to collate (iterate) the given expression
2401     */
2402    public static Expression collateExpression(final String expression, final int group) {
2403        return new ExpressionAdapter() {
2404            public Object evaluate(Exchange exchange) {
2405                // use simple language
2406                Expression exp = exchange.getContext().resolveLanguage("simple").createExpression(expression);
2407                return ExpressionBuilder.groupIteratorExpression(exp, null, "" + group, false).evaluate(exchange, Object.class);
2408            }
2409
2410            @Override
2411            public String toString() {
2412                return "collate(" + expression + "," + group + ")";
2413            }
2414        };
2415    }
2416
2417    /**
2418     * Returns the message history (including exchange details or not)
2419     */
2420    public static Expression messageHistoryExpression(final boolean detailed) {
2421        return new ExpressionAdapter() {
2422
2423            private ExchangeFormatter formatter;
2424
2425            public Object evaluate(Exchange exchange) {
2426                ExchangeFormatter ef = null;
2427                if (detailed) {
2428                    // use the exchange formatter to log exchange details
2429                    ef = getOrCreateExchangeFormatter(exchange.getContext());
2430                }
2431                return MessageHelper.dumpMessageHistoryStacktrace(exchange, ef, false);
2432            }
2433
2434            private ExchangeFormatter getOrCreateExchangeFormatter(CamelContext camelContext) {
2435                if (formatter == null) {
2436                    Set<ExchangeFormatter> formatters = camelContext.getRegistry().findByType(ExchangeFormatter.class);
2437                    if (formatters != null && formatters.size() == 1) {
2438                        formatter = formatters.iterator().next();
2439                    } else {
2440                        // setup exchange formatter to be used for message history dump
2441                        DefaultExchangeFormatter def = new DefaultExchangeFormatter();
2442                        def.setShowExchangeId(true);
2443                        def.setMultiline(true);
2444                        def.setShowHeaders(true);
2445                        def.setStyle(DefaultExchangeFormatter.OutputStyle.Fixed);
2446                        try {
2447                            Integer maxChars = CamelContextHelper.parseInteger(camelContext, camelContext.getGlobalOption(Exchange.LOG_DEBUG_BODY_MAX_CHARS));
2448                            if (maxChars != null) {
2449                                def.setMaxChars(maxChars);
2450                            }
2451                        } catch (Exception e) {
2452                            throw ObjectHelper.wrapRuntimeCamelException(e);
2453                        }
2454                        formatter = def;
2455                    }
2456                }
2457                return formatter;
2458            }
2459
2460            @Override
2461            public String toString() {
2462                return "messageHistory(" + detailed + ")";
2463            }
2464        };
2465    }
2466
2467    /**
2468     * Expression adapter for OGNL expression from Message Header or Exchange property
2469     */
2470    private static class KeyedOgnlExpressionAdapter extends ExpressionAdapter {
2471        private final String ognl;
2472        private final String toStringValue;
2473        private final KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy;
2474
2475        KeyedOgnlExpressionAdapter(String ognl, String toStringValue,
2476                                   KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy) {
2477            this.ognl = ognl;
2478            this.toStringValue = toStringValue;
2479            this.keyedEntityRetrievalStrategy = keyedEntityRetrievalStrategy;
2480        }
2481
2482        public Object evaluate(Exchange exchange) {
2483            // try with full name first
2484            Object property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, ognl);
2485            if (property != null) {
2486                return property;
2487            }
2488
2489
2490            // Split ognl except when this is not a Map, Array
2491            // and we would like to keep the dots within the key name
2492            List<String> methods = OgnlHelper.splitOgnl(ognl);
2493
2494            String key = methods.get(0);
2495            String keySuffix = "";
2496            // if ognl starts with a key inside brackets (eg: [foo.bar])
2497            // remove starting and ending brackets from key
2498            if (key.startsWith("[") && key.endsWith("]")) {
2499                key = StringHelper.removeLeadingAndEndingQuotes(key.substring(1, key.length() - 1));
2500                keySuffix = StringHelper.after(methods.get(0), key);
2501            }
2502            // remove any OGNL operators so we got the pure key name
2503            key = OgnlHelper.removeOperators(key);
2504
2505
2506            property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, key);
2507            if (property == null) {
2508                return null;
2509            }
2510            // the remainder is the rest of the ognl without the key
2511            String remainder = ObjectHelper.after(ognl, key + keySuffix);
2512            return new MethodCallExpression(property, remainder).evaluate(exchange);
2513        }
2514
2515        @Override
2516        public String toString() {
2517            return toStringValue;
2518        }
2519
2520        /**
2521         * Strategy to retrieve the value based on the key
2522         */
2523        public interface KeyedEntityRetrievalStrategy {
2524            Object getKeyedEntity(Exchange exchange, String key);
2525        }
2526    }
2527
2528}