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