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.Set;
031import java.util.TimeZone;
032import java.util.concurrent.atomic.AtomicReference;
033import java.util.function.BiFunction;
034import java.util.function.Function;
035import java.util.regex.Matcher;
036import java.util.regex.Pattern;
037
038import org.apache.camel.CamelContext;
039import org.apache.camel.Component;
040import org.apache.camel.Endpoint;
041import org.apache.camel.Exchange;
042import org.apache.camel.Expression;
043import org.apache.camel.InvalidPayloadException;
044import org.apache.camel.Message;
045import org.apache.camel.NoSuchEndpointException;
046import org.apache.camel.NoSuchLanguageException;
047import org.apache.camel.NoTypeConversionAvailableException;
048import org.apache.camel.Producer;
049import org.apache.camel.RuntimeExchangeException;
050import org.apache.camel.component.bean.BeanInvocation;
051import org.apache.camel.component.properties.PropertiesComponent;
052import org.apache.camel.language.bean.BeanLanguage;
053import org.apache.camel.language.simple.SimpleLanguage;
054import org.apache.camel.model.language.MethodCallExpression;
055import org.apache.camel.processor.DefaultExchangeFormatter;
056import org.apache.camel.spi.ExchangeFormatter;
057import org.apache.camel.spi.Language;
058import org.apache.camel.spi.RouteContext;
059import org.apache.camel.spi.UnitOfWork;
060import org.apache.camel.support.ExpressionAdapter;
061import org.apache.camel.support.TokenPairExpressionIterator;
062import org.apache.camel.support.TokenXMLExpressionIterator;
063import org.apache.camel.support.XMLTokenExpressionIterator;
064import org.apache.camel.util.CamelContextHelper;
065import org.apache.camel.util.ExchangeHelper;
066import org.apache.camel.util.FileUtil;
067import org.apache.camel.util.GroupIterator;
068import org.apache.camel.util.GroupTokenIterator;
069import org.apache.camel.util.IOHelper;
070import org.apache.camel.util.MessageHelper;
071import org.apache.camel.util.ObjectHelper;
072import org.apache.camel.util.OgnlHelper;
073import org.apache.camel.util.Scanner;
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.toUpperCase());
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<>();
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, text);
1562                return scanner;
1563            }
1564
1565            @Override
1566            public String toString() {
1567                return "tokenize(" + expression + ", " + token + ")";
1568            }
1569        };
1570    }
1571
1572    /**
1573     * Returns an expression that skips the first element
1574     */
1575    public static Expression skipFirstExpression(final Expression expression) {
1576        return new ExpressionAdapter() {
1577            public Object evaluate(Exchange exchange) {
1578                Object value = expression.evaluate(exchange, Object.class);
1579                Iterator it = exchange.getContext().getTypeConverter().tryConvertTo(Iterator.class, exchange, value);
1580                if (it != null) {
1581                    // skip first
1582                    it.next();
1583                    return it;
1584                } else {
1585                    return value;
1586                }
1587            }
1588
1589            @Override
1590            public String toString() {
1591                return "skipFirst(" + expression + ")";
1592            }
1593        };
1594    }
1595
1596    /**
1597     * Returns an {@link TokenPairExpressionIterator} expression
1598     */
1599    public static Expression tokenizePairExpression(String startToken, String endToken, boolean includeTokens) {
1600        return new TokenPairExpressionIterator(startToken, endToken, includeTokens);
1601    }
1602
1603    /**
1604     * Returns an {@link TokenXMLExpressionIterator} expression
1605     */
1606    public static Expression tokenizeXMLExpression(String tagName, String inheritNamespaceTagName) {
1607        StringHelper.notEmpty(tagName, "tagName");
1608        return new TokenXMLExpressionIterator(tagName, inheritNamespaceTagName);
1609    }
1610
1611    public static Expression tokenizeXMLAwareExpression(String path, char mode) {
1612        StringHelper.notEmpty(path, "path");
1613        return new XMLTokenExpressionIterator(path, mode);
1614    }
1615
1616    public static Expression tokenizeXMLAwareExpression(String path, char mode, int group) {
1617        StringHelper.notEmpty(path, "path");
1618        return new XMLTokenExpressionIterator(path, mode, group);
1619    }
1620
1621    /**
1622     * Returns a tokenize expression which will tokenize the string with the
1623     * given regex
1624     */
1625    public static Expression regexTokenizeExpression(final Expression expression,
1626                                                     final String regexTokenizer) {
1627        return new ExpressionAdapter() {
1628            public Object evaluate(Exchange exchange) {
1629                Object value = expression.evaluate(exchange, Object.class);
1630                Scanner scanner = ObjectHelper.getScanner(exchange, value, regexTokenizer);
1631                return scanner;
1632            }
1633
1634            @Override
1635            public String toString() {
1636                return "regexTokenize(" + expression + ", " + regexTokenizer + ")";
1637            }
1638        };
1639    }
1640
1641    public static Expression groupXmlIteratorExpression(final Expression expression, final String group) {
1642        return new ExpressionAdapter() {
1643            public Object evaluate(Exchange exchange) {
1644                // evaluate expression as iterator
1645                Iterator<?> it = expression.evaluate(exchange, Iterator.class);
1646                ObjectHelper.notNull(it, "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator");
1647                // must use GroupTokenIterator in xml mode as we want to concat the xml parts into a single message
1648                // the group can be a simple expression so evaluate it as a number
1649                Integer parts = exchange.getContext().resolveLanguage("simple").createExpression(group).evaluate(exchange, Integer.class);
1650                if (parts == null) {
1651                    throw new RuntimeExchangeException("Group evaluated as null, must be evaluated as a positive Integer value from expression: " + group, exchange);
1652                } else if (parts <= 0) {
1653                    throw new RuntimeExchangeException("Group must be a positive number, was: " + parts, exchange);
1654                }
1655                return new GroupTokenIterator(exchange, it, null, parts, false);
1656            }
1657
1658            @Override
1659            public String toString() {
1660                return "group " + expression + " " + group + " times";
1661            }
1662        };
1663    }
1664
1665    public static Expression groupIteratorExpression(final Expression expression, final String token, final String group, final boolean skipFirst) {
1666        return new ExpressionAdapter() {
1667            public Object evaluate(Exchange exchange) {
1668                // evaluate expression as iterator
1669                Iterator<?> it = expression.evaluate(exchange, Iterator.class);
1670                ObjectHelper.notNull(it, "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator");
1671                // the group can be a simple expression so evaluate it as a number
1672                Integer parts = exchange.getContext().resolveLanguage("simple").createExpression(group).evaluate(exchange, Integer.class);
1673                if (parts == null) {
1674                    throw new RuntimeExchangeException("Group evaluated as null, must be evaluated as a positive Integer value from expression: " + group, exchange);
1675                } else if (parts <= 0) {
1676                    throw new RuntimeExchangeException("Group must be a positive number, was: " + parts, exchange);
1677                }
1678                if (token != null) {
1679                    return new GroupTokenIterator(exchange, it, token, parts, skipFirst);
1680                } else {
1681                    return new GroupIterator(exchange, it, parts, skipFirst);
1682                }
1683            }
1684
1685            @Override
1686            public String toString() {
1687                return "group " + expression + " " + group + " times";
1688            }
1689        };
1690    }
1691
1692    public static Expression skipIteratorExpression(final Expression expression, final int skip) {
1693        return new ExpressionAdapter() {
1694            public Object evaluate(Exchange exchange) {
1695                // evaluate expression as iterator
1696                Iterator<?> it = expression.evaluate(exchange, Iterator.class);
1697                ObjectHelper.notNull(it, "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator");
1698                return new SkipIterator(exchange, it, skip);
1699            }
1700
1701            @Override
1702            public String toString() {
1703                return "skip " + expression + " " + skip + " times";
1704            }
1705        };
1706    }
1707
1708    /**
1709     * Returns a sort expression which will sort the expression with the given comparator.
1710     * <p/>
1711     * The expression is evaluated as a {@link List} object to allow sorting.
1712     */
1713    @SuppressWarnings({"unchecked", "rawtypes"})
1714    public static Expression sortExpression(final Expression expression, final Comparator comparator) {
1715        return new ExpressionAdapter() {
1716            public Object evaluate(Exchange exchange) {
1717                List<?> list = expression.evaluate(exchange, List.class);
1718                list.sort(comparator);
1719                return list;
1720            }
1721
1722            @Override
1723            public String toString() {
1724                return "sort(" + expression + " by: " + comparator + ")";
1725            }
1726        };
1727    }
1728
1729    /**
1730     * Transforms the expression into a String then performs the regex
1731     * replaceAll to transform the String and return the result
1732     */
1733    public static Expression regexReplaceAll(final Expression expression,
1734                                             final String regex, final String replacement) {
1735        final Pattern pattern = Pattern.compile(regex);
1736        return new ExpressionAdapter() {
1737            public Object evaluate(Exchange exchange) {
1738                String text = expression.evaluate(exchange, String.class);
1739                if (text == null) {
1740                    return null;
1741                }
1742                return pattern.matcher(text).replaceAll(replacement);
1743            }
1744
1745            @Override
1746            public String toString() {
1747                return "regexReplaceAll(" + expression + ", " + pattern.pattern() + ")";
1748            }
1749        };
1750    }
1751
1752    /**
1753     * Transforms the expression into a String then performs the regex
1754     * replaceAll to transform the String and return the result
1755     */
1756    public static Expression regexReplaceAll(final Expression expression,
1757                                             final String regex, final Expression replacementExpression) {
1758
1759        final Pattern pattern = Pattern.compile(regex);
1760        return new ExpressionAdapter() {
1761            public Object evaluate(Exchange exchange) {
1762                String text = expression.evaluate(exchange, String.class);
1763                String replacement = replacementExpression.evaluate(exchange, String.class);
1764                if (text == null || replacement == null) {
1765                    return null;
1766                }
1767                return pattern.matcher(text).replaceAll(replacement);
1768            }
1769
1770            @Override
1771            public String toString() {
1772                return "regexReplaceAll(" + expression + ", " + pattern.pattern() + ")";
1773            }
1774        };
1775    }
1776
1777    /**
1778     * Appends the String evaluations of the two expressions together
1779     */
1780    public static Expression append(final Expression left, final Expression right) {
1781        return new ExpressionAdapter() {
1782            public Object evaluate(Exchange exchange) {
1783                return left.evaluate(exchange, String.class) + right.evaluate(exchange, String.class);
1784            }
1785
1786            @Override
1787            public String toString() {
1788                return "append(" + left + ", " + right + ")";
1789            }
1790        };
1791    }
1792
1793    /**
1794     * Prepends the String evaluations of the two expressions together
1795     */
1796    public static Expression prepend(final Expression left, final Expression right) {
1797        return new ExpressionAdapter() {
1798            public Object evaluate(Exchange exchange) {
1799                return right.evaluate(exchange, String.class) + left.evaluate(exchange, String.class);
1800            }
1801
1802            @Override
1803            public String toString() {
1804                return "prepend(" + left + ", " + right + ")";
1805            }
1806        };
1807    }
1808
1809    /**
1810     * Returns an expression which returns the string concatenation value of the various
1811     * expressions
1812     *
1813     * @param expressions the expression to be concatenated dynamically
1814     * @return an expression which when evaluated will return the concatenated values
1815     */
1816    public static Expression concatExpression(final Collection<Expression> expressions) {
1817        return concatExpression(expressions, null);
1818    }
1819
1820    /**
1821     * Returns an expression which returns the string concatenation value of the various
1822     * expressions
1823     *
1824     * @param expressions the expression to be concatenated dynamically
1825     * @param desription the text description of the expression
1826     * @return an expression which when evaluated will return the concatenated values
1827     */
1828    public static Expression concatExpression(final Collection<Expression> expressions, final String desription) {
1829        return new ExpressionAdapter() {
1830            public Object evaluate(Exchange exchange) {
1831                StringBuilder buffer = new StringBuilder();
1832                for (Expression expression : expressions) {
1833                    String text = expression.evaluate(exchange, String.class);
1834                    if (text != null) {
1835                        buffer.append(text);
1836                    }
1837                }
1838                return buffer.toString();
1839            }
1840
1841            @Override
1842            public String toString() {
1843                if (desription != null) {
1844                    return desription;
1845                } else {
1846                    return "concat" + expressions;
1847                }
1848            }
1849        };
1850    }
1851
1852    /**
1853     * Returns an Expression for the inbound message id
1854     */
1855    public static Expression messageIdExpression() {
1856        return new ExpressionAdapter() {
1857            public Object evaluate(Exchange exchange) {
1858                return exchange.getIn().getMessageId();
1859            }
1860
1861            @Override
1862            public String toString() {
1863                return "messageId";
1864            }
1865        };
1866    }
1867
1868    /**
1869     * Returns an Expression for the exchange id
1870     */
1871    public static Expression exchangeIdExpression() {
1872        return new ExpressionAdapter() {
1873            public Object evaluate(Exchange exchange) {
1874                return exchange.getExchangeId();
1875            }
1876
1877            @Override
1878            public String toString() {
1879                return "exchangeId";
1880            }
1881        };
1882    }
1883
1884    /**
1885     * Returns an Expression for the route id
1886     */
1887    public static Expression routeIdExpression() {
1888        return new ExpressionAdapter() {
1889            public Object evaluate(Exchange exchange) {
1890                String answer = null;
1891                UnitOfWork uow = exchange.getUnitOfWork();
1892                RouteContext rc = uow != null ? uow.getRouteContext() : null;
1893                if (rc != null) {
1894                    answer = rc.getRoute().getId();
1895                }
1896                if (answer == null) {
1897                    // fallback and get from route id on the exchange
1898                    answer = exchange.getFromRouteId();
1899                }
1900                return answer;
1901            }
1902
1903            @Override
1904            public String toString() {
1905                return "routeId";
1906            }
1907        };
1908    }
1909
1910    public static Expression dateExpression(final String command) {
1911        return dateExpression(command, null, null);
1912    }
1913
1914    public static Expression dateExpression(final String command, final String pattern) {
1915        return dateExpression(command, null, pattern);
1916    }
1917
1918    public static Expression dateExpression(final String commandWithOffsets, final String timezone, final String pattern) {
1919        return new ExpressionAdapter() {
1920            public Object evaluate(Exchange exchange) {
1921                // Capture optional time offsets
1922                String command = commandWithOffsets.split("[+-]", 2)[0].trim();
1923                List<Long> offsets = new ArrayList<>();
1924                Matcher offsetMatcher = OFFSET_PATTERN.matcher(commandWithOffsets);
1925                while (offsetMatcher.find()) {
1926                    try {
1927                        long value = exchange.getContext().getTypeConverter().mandatoryConvertTo(long.class, exchange, offsetMatcher.group(2).trim());
1928                        offsets.add(offsetMatcher.group(1).equals("+") ? value : -value);
1929                    } catch (NoTypeConversionAvailableException e) {
1930                        throw ObjectHelper.wrapCamelExecutionException(exchange, e);
1931                    }
1932                }
1933
1934                Date date;
1935                if ("now".equals(command)) {
1936                    date = new Date();
1937                } else if (command.startsWith("header.") || command.startsWith("in.header.")) {
1938                    String key = command.substring(command.lastIndexOf('.') + 1);
1939                    date = exchange.getIn().getHeader(key, Date.class);
1940                    if (date == null) {
1941                        throw new IllegalArgumentException("Cannot find java.util.Date object at command: " + command);
1942                    }
1943                } else if (command.startsWith("out.header.")) {
1944                    String key = command.substring(command.lastIndexOf('.') + 1);
1945                    date = exchange.getOut().getHeader(key, Date.class);
1946                    if (date == null) {
1947                        throw new IllegalArgumentException("Cannot find java.util.Date object at command: " + command);
1948                    }
1949                } else if (command.startsWith("property.") || command.startsWith("exchangeProperty.")) {
1950                    String key = command.substring(command.lastIndexOf('.') + 1);
1951                    date = exchange.getProperty(key, Date.class);
1952                    if (date == null) {
1953                        throw new IllegalArgumentException("Cannot find java.util.Date object at command: " + command);
1954                    }
1955                } else if ("file".equals(command)) {
1956                    Long num = exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class);
1957                    if (num != null && num > 0) {
1958                        date = new Date(num);
1959                    } else {
1960                        date = exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Date.class);
1961                        if (date == null) {
1962                            throw new IllegalArgumentException("Cannot find " + Exchange.FILE_LAST_MODIFIED + " header at command: " + command);
1963                        }
1964                    }
1965                } else {
1966                    throw new IllegalArgumentException("Command not supported for dateExpression: " + command);
1967                }
1968
1969                // Apply offsets
1970                long dateAsLong = date.getTime();
1971                for (long offset : offsets) {
1972                    dateAsLong += offset;
1973                }
1974                date = new Date(dateAsLong);
1975
1976                if (pattern != null && !pattern.isEmpty()) {
1977                    SimpleDateFormat df = new SimpleDateFormat(pattern);
1978                    if (timezone != null && !timezone.isEmpty()) {
1979                        df.setTimeZone(TimeZone.getTimeZone(timezone));
1980                    }
1981                    return df.format(date);
1982                } else {
1983                    return date;
1984                }
1985            }
1986
1987            @Override
1988            public String toString() {
1989                return "date(" + commandWithOffsets + ":" + pattern + ":" + timezone + ")";
1990            }
1991        };
1992    }
1993
1994    public static Expression simpleExpression(final String expression) {
1995        return new ExpressionAdapter() {
1996            public Object evaluate(Exchange exchange) {
1997                if (SimpleLanguage.hasSimpleFunction(expression)) {
1998                    // resolve language using context to have a clear separation of packages
1999                    // must call evaluate to return the nested language evaluate when evaluating
2000                    // stacked expressions
2001                    Language language = exchange.getContext().resolveLanguage("simple");
2002                    return language.createExpression(expression).evaluate(exchange, Object.class);
2003                } else {
2004                    return expression;
2005                }
2006            }
2007
2008            @Override
2009            public String toString() {
2010                return "simple(" + expression + ")";
2011            }
2012        };
2013    }
2014
2015    public static Expression beanExpression(final String expression) {
2016        return new ExpressionAdapter() {
2017            public Object evaluate(Exchange exchange) {
2018                // bean is able to evaluate method name if it contains nested functions
2019                // so we should not eager evaluate expression as a string
2020                // resolve language using context to have a clear separation of packages
2021                // must call evaluate to return the nested language evaluate when evaluating
2022                // stacked expressions
2023                Language language = exchange.getContext().resolveLanguage("bean");
2024                return language.createExpression(expression).evaluate(exchange, Object.class);
2025            }
2026
2027            @Override
2028            public String toString() {
2029                return "bean(" + expression + ")";
2030            }
2031        };
2032    }
2033
2034    public static Expression beanExpression(final Class<?> beanType, final String methodName) {
2035        return BeanLanguage.bean(beanType, methodName);
2036    }
2037
2038    public static Expression beanExpression(final Object bean, final String methodName) {
2039        return BeanLanguage.bean(bean, methodName);
2040    }
2041
2042    public static Expression beanExpression(final String beanRef, final String methodName) {
2043        String expression = methodName != null ? beanRef + "." + methodName : beanRef;
2044        return beanExpression(expression);
2045    }
2046
2047    /**
2048     * Returns an expression processing the exchange to the given endpoint uri
2049     *
2050     * @param uri endpoint uri to send the exchange to
2051     * @return an expression object which will return the OUT body
2052     * @deprecated not in use, and not available in XML DSL
2053     */
2054    @Deprecated
2055    public static Expression toExpression(final String uri) {
2056        return new ExpressionAdapter() {
2057            public Object evaluate(Exchange exchange) {
2058                String text = simpleExpression(uri).evaluate(exchange, String.class);
2059                Endpoint endpoint = exchange.getContext().getEndpoint(text);
2060                if (endpoint == null) {
2061                    throw new NoSuchEndpointException(text);
2062                }
2063
2064                Producer producer;
2065                try {
2066                    producer = endpoint.createProducer();
2067                    producer.start();
2068                    producer.process(exchange);
2069                    producer.stop();
2070                } catch (Exception e) {
2071                    throw ObjectHelper.wrapRuntimeCamelException(e);
2072                }
2073
2074                // return the OUT body, but check for exchange pattern
2075                if (ExchangeHelper.isOutCapable(exchange)) {
2076                    return exchange.getOut().getBody();
2077                } else {
2078                    return exchange.getIn().getBody();
2079                }
2080            }
2081
2082            @Override
2083            public String toString() {
2084                return "to(" + uri + ")";
2085            }
2086        };
2087    }
2088
2089    public static Expression fileNameExpression() {
2090        return new ExpressionAdapter() {
2091            public Object evaluate(Exchange exchange) {
2092                return exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
2093            }
2094
2095            @Override
2096            public String toString() {
2097                return "file:name";
2098            }
2099        };
2100    }
2101
2102    public static Expression fileOnlyNameExpression() {
2103        return new ExpressionAdapter() {
2104            public Object evaluate(Exchange exchange) {
2105                String answer = exchange.getIn().getHeader(Exchange.FILE_NAME_ONLY, String.class);
2106                if (answer == null) {
2107                    answer = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
2108                    answer = FileUtil.stripPath(answer);
2109                }
2110                return answer;
2111            }
2112
2113            @Override
2114            public String toString() {
2115                return "file:onlyname";
2116            }
2117        };
2118    }
2119
2120    public static Expression fileNameNoExtensionExpression() {
2121        return new ExpressionAdapter() {
2122            public Object evaluate(Exchange exchange) {
2123                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
2124                return FileUtil.stripExt(name);
2125            }
2126
2127            @Override
2128            public String toString() {
2129                return "file:name.noext";
2130            }
2131        };
2132    }
2133
2134    public static Expression fileNameNoExtensionSingleExpression() {
2135        return new ExpressionAdapter() {
2136            public Object evaluate(Exchange exchange) {
2137                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
2138                return FileUtil.stripExt(name, true);
2139            }
2140
2141            @Override
2142            public String toString() {
2143                return "file:name.noext.single";
2144            }
2145        };
2146    }
2147
2148    public static Expression fileOnlyNameNoExtensionExpression() {
2149        return new ExpressionAdapter() {
2150            public Object evaluate(Exchange exchange) {
2151                String name = fileOnlyNameExpression().evaluate(exchange, String.class);
2152                return FileUtil.stripExt(name);
2153            }
2154
2155            @Override
2156            public String toString() {
2157                return "file:onlyname.noext";
2158            }
2159        };
2160    }
2161
2162    public static Expression fileOnlyNameNoExtensionSingleExpression() {
2163        return new ExpressionAdapter() {
2164            public Object evaluate(Exchange exchange) {
2165                String name = fileOnlyNameExpression().evaluate(exchange, String.class);
2166                return FileUtil.stripExt(name, true);
2167            }
2168
2169            @Override
2170            public String toString() {
2171                return "file:onlyname.noext.single";
2172            }
2173        };
2174    }
2175
2176    public static Expression fileExtensionExpression() {
2177        return new ExpressionAdapter() {
2178            public Object evaluate(Exchange exchange) {
2179                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
2180                return FileUtil.onlyExt(name);
2181            }
2182
2183            @Override
2184            public String toString() {
2185                return "file:ext";
2186            }
2187        };
2188    }
2189
2190    public static Expression fileExtensionSingleExpression() {
2191        return new ExpressionAdapter() {
2192            public Object evaluate(Exchange exchange) {
2193                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
2194                return FileUtil.onlyExt(name, true);
2195            }
2196
2197            @Override
2198            public String toString() {
2199                return "file:ext.single";
2200            }
2201        };
2202    }
2203
2204    public static Expression fileParentExpression() {
2205        return new ExpressionAdapter() {
2206            public Object evaluate(Exchange exchange) {
2207                return exchange.getIn().getHeader("CamelFileParent", String.class);
2208            }
2209
2210            @Override
2211            public String toString() {
2212                return "file:parent";
2213            }
2214        };
2215    }
2216
2217    public static Expression filePathExpression() {
2218        return new ExpressionAdapter() {
2219            public Object evaluate(Exchange exchange) {
2220                return exchange.getIn().getHeader("CamelFilePath", String.class);
2221            }
2222
2223            @Override
2224            public String toString() {
2225                return "file:path";
2226            }
2227        };
2228    }
2229
2230    public static Expression fileAbsolutePathExpression() {
2231        return new ExpressionAdapter() {
2232            public Object evaluate(Exchange exchange) {
2233                return exchange.getIn().getHeader("CamelFileAbsolutePath", String.class);
2234            }
2235
2236            @Override
2237            public String toString() {
2238                return "file:absolute.path";
2239            }
2240        };
2241    }
2242
2243    public static Expression fileAbsoluteExpression() {
2244        return new ExpressionAdapter() {
2245            public Object evaluate(Exchange exchange) {
2246                return exchange.getIn().getHeader("CamelFileAbsolute", Boolean.class);
2247            }
2248
2249            @Override
2250            public String toString() {
2251                return "file:absolute";
2252            }
2253        };
2254    }
2255
2256    public static Expression fileSizeExpression() {
2257        return new ExpressionAdapter() {
2258            public Object evaluate(Exchange exchange) {
2259                return exchange.getIn().getHeader(Exchange.FILE_LENGTH, Long.class);
2260            }
2261
2262            @Override
2263            public String toString() {
2264                return "file:length";
2265            }
2266        };
2267    }
2268
2269    public static Expression fileLastModifiedExpression() {
2270        return new ExpressionAdapter() {
2271            public Object evaluate(Exchange exchange) {
2272                return exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class);
2273            }
2274
2275            @Override
2276            public String toString() {
2277                return "file:modified";
2278            }
2279        };
2280    }
2281
2282    /**
2283     * Returns Simple expression or fallback to Constant expression if expression str is not Simple expression.
2284     */
2285    public static Expression parseSimpleOrFallbackToConstantExpression(String str, CamelContext camelContext) {
2286        if (StringHelper.hasStartToken(str, "simple")) {
2287            return camelContext.resolveLanguage("simple").createExpression(str);
2288        }
2289        return constantExpression(str);
2290    }
2291
2292    public static Expression propertiesComponentExpression(final String key, final String locations, final String defaultValue) {
2293        return new ExpressionAdapter() {
2294            public Object evaluate(Exchange exchange) {
2295                String text = simpleExpression(key).evaluate(exchange, String.class);
2296                String text2 = simpleExpression(locations).evaluate(exchange, String.class);
2297                try {
2298                    if (text2 != null) {
2299                        // the properties component is optional as we got locations
2300                        // getComponent will create a new component if none already exists
2301                        Component component = exchange.getContext().getComponent("properties");
2302                        PropertiesComponent pc = exchange.getContext().getTypeConverter()
2303                            .mandatoryConvertTo(PropertiesComponent.class, component);
2304                        // enclose key with {{ }} to force parsing
2305                        String[] paths = text2.split(",");
2306                        return pc.parseUri(pc.getPrefixToken() + text + pc.getSuffixToken(), paths);
2307                    } else {
2308                        // the properties component is mandatory if no locations provided
2309                        Component component = exchange.getContext().hasComponent("properties");
2310                        if (component == null) {
2311                            throw new IllegalArgumentException("PropertiesComponent with name properties must be defined"
2312                                + " in CamelContext to support property placeholders in expressions");
2313                        }
2314                        PropertiesComponent pc = exchange.getContext().getTypeConverter()
2315                            .mandatoryConvertTo(PropertiesComponent.class, component);
2316                        // enclose key with {{ }} to force parsing
2317                        return pc.parseUri(pc.getPrefixToken() + text + pc.getSuffixToken());
2318                    }
2319                } catch (Exception e) {
2320                    // property with key not found, use default value if provided
2321                    if (defaultValue != null) {
2322                        return defaultValue;
2323                    }
2324                    throw ObjectHelper.wrapRuntimeCamelException(e);
2325                }
2326            }
2327
2328            @Override
2329            public String toString() {
2330                return "properties(" + key + ")";
2331            }
2332        };
2333    }
2334
2335    /**
2336     * Returns a random number between 0 and max (exclusive)
2337     */
2338    public static Expression randomExpression(final int max) {
2339        return randomExpression(0, max);
2340    }
2341
2342    /**
2343     * Returns a random number between min and max (exclusive)
2344     */
2345    public static Expression randomExpression(final int min, final int max) {
2346        return new ExpressionAdapter() {
2347            public Object evaluate(Exchange exchange) {
2348                Random random = new Random();
2349                int randomNum = random.nextInt(max - min) + min;
2350                return randomNum;
2351            }
2352
2353            @Override
2354            public String toString() {
2355                return "random(" + min + "," + max + ")";
2356            }
2357        };
2358    }
2359
2360    /**
2361     * Returns a random number between min and max (exclusive)
2362     */
2363    public static Expression randomExpression(final String min, final String max) {
2364        return new ExpressionAdapter() {
2365            public Object evaluate(Exchange exchange) {
2366                int num1 = simpleExpression(min).evaluate(exchange, Integer.class);
2367                int num2 = simpleExpression(max).evaluate(exchange, Integer.class);
2368                Random random = new Random();
2369                int randomNum = random.nextInt(num2 - num1) + num1;
2370                return randomNum;
2371            }
2372
2373            @Override
2374            public String toString() {
2375                return "random(" + min + "," + max + ")";
2376            }
2377        };
2378    }
2379
2380    /**
2381     * Returns an iterator to skip (iterate) the given expression
2382     */
2383    public static Expression skipExpression(final String expression, final int number) {
2384        return new ExpressionAdapter() {
2385            public Object evaluate(Exchange exchange) {
2386                // use simple language
2387                Expression exp = exchange.getContext().resolveLanguage("simple").createExpression(expression);
2388                return ExpressionBuilder.skipIteratorExpression(exp, number).evaluate(exchange, Object.class);
2389            }
2390
2391            @Override
2392            public String toString() {
2393                return "skip(" + expression + "," + number + ")";
2394            }
2395        };
2396    }
2397
2398    /**
2399     * Returns an iterator to collate (iterate) the given expression
2400     */
2401    public static Expression collateExpression(final String expression, final int group) {
2402        return new ExpressionAdapter() {
2403            public Object evaluate(Exchange exchange) {
2404                // use simple language
2405                Expression exp = exchange.getContext().resolveLanguage("simple").createExpression(expression);
2406                return ExpressionBuilder.groupIteratorExpression(exp, null, "" + group, false).evaluate(exchange, Object.class);
2407            }
2408
2409            @Override
2410            public String toString() {
2411                return "collate(" + expression + "," + group + ")";
2412            }
2413        };
2414    }
2415
2416    /**
2417     * Returns the message history (including exchange details or not)
2418     */
2419    public static Expression messageHistoryExpression(final boolean detailed) {
2420        return new ExpressionAdapter() {
2421
2422            private ExchangeFormatter formatter;
2423
2424            public Object evaluate(Exchange exchange) {
2425                ExchangeFormatter ef = null;
2426                if (detailed) {
2427                    // use the exchange formatter to log exchange details
2428                    ef = getOrCreateExchangeFormatter(exchange.getContext());
2429                }
2430                return MessageHelper.dumpMessageHistoryStacktrace(exchange, ef, false);
2431            }
2432
2433            private ExchangeFormatter getOrCreateExchangeFormatter(CamelContext camelContext) {
2434                if (formatter == null) {
2435                    Set<ExchangeFormatter> formatters = camelContext.getRegistry().findByType(ExchangeFormatter.class);
2436                    if (formatters != null && formatters.size() == 1) {
2437                        formatter = formatters.iterator().next();
2438                    } else {
2439                        // setup exchange formatter to be used for message history dump
2440                        DefaultExchangeFormatter def = new DefaultExchangeFormatter();
2441                        def.setShowExchangeId(true);
2442                        def.setMultiline(true);
2443                        def.setShowHeaders(true);
2444                        def.setStyle(DefaultExchangeFormatter.OutputStyle.Fixed);
2445                        try {
2446                            Integer maxChars = CamelContextHelper.parseInteger(camelContext, camelContext.getGlobalOption(Exchange.LOG_DEBUG_BODY_MAX_CHARS));
2447                            if (maxChars != null) {
2448                                def.setMaxChars(maxChars);
2449                            }
2450                        } catch (Exception e) {
2451                            throw ObjectHelper.wrapRuntimeCamelException(e);
2452                        }
2453                        formatter = def;
2454                    }
2455                }
2456                return formatter;
2457            }
2458
2459            @Override
2460            public String toString() {
2461                return "messageHistory(" + detailed + ")";
2462            }
2463        };
2464    }
2465
2466    /**
2467     * Expression adapter for OGNL expression from Message Header or Exchange property
2468     */
2469    private static class KeyedOgnlExpressionAdapter extends ExpressionAdapter {
2470        private final String ognl;
2471        private final String toStringValue;
2472        private final KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy;
2473
2474        KeyedOgnlExpressionAdapter(String ognl, String toStringValue,
2475                                   KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy) {
2476            this.ognl = ognl;
2477            this.toStringValue = toStringValue;
2478            this.keyedEntityRetrievalStrategy = keyedEntityRetrievalStrategy;
2479        }
2480
2481        public Object evaluate(Exchange exchange) {
2482            // try with full name first
2483            Object property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, ognl);
2484            if (property != null) {
2485                return property;
2486            }
2487
2488
2489            // Split ognl except when this is not a Map, Array
2490            // and we would like to keep the dots within the key name
2491            List<String> methods = OgnlHelper.splitOgnl(ognl);
2492
2493            String key = methods.get(0);
2494            String keySuffix = "";
2495            // if ognl starts with a key inside brackets (eg: [foo.bar])
2496            // remove starting and ending brackets from key
2497            if (key.startsWith("[") && key.endsWith("]")) {
2498                key = StringHelper.removeLeadingAndEndingQuotes(key.substring(1, key.length() - 1));
2499                keySuffix = StringHelper.after(methods.get(0), key);
2500            }
2501            // remove any OGNL operators so we got the pure key name
2502            key = OgnlHelper.removeOperators(key);
2503
2504
2505            property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, key);
2506            if (property == null) {
2507                return null;
2508            }
2509            // the remainder is the rest of the ognl without the key
2510            String remainder = StringHelper.after(ognl, key + keySuffix);
2511            return new MethodCallExpression(property, remainder).evaluate(exchange);
2512        }
2513
2514        @Override
2515        public String toString() {
2516            return toStringValue;
2517        }
2518
2519        /**
2520         * Strategy to retrieve the value based on the key
2521         */
2522        public interface KeyedEntityRetrievalStrategy {
2523            Object getKeyedEntity(Exchange exchange, String key);
2524        }
2525    }
2526
2527}