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.language.simple;
018    
019    import org.apache.camel.Expression;
020    import org.apache.camel.Predicate;
021    import org.apache.camel.builder.ExpressionBuilder;
022    import org.apache.camel.support.LanguageSupport;
023    import org.apache.camel.util.ObjectHelper;
024    import org.apache.camel.util.PredicateToExpressionAdapter;
025    
026    /**
027     * A <a href="http://camel.apache.org/simple.html">simple language</a>
028     * which maps simple property style notations to access headers and bodies.
029     * Examples of supported expressions are:
030     * <ul>
031     * <li>exchangeId to access the exchange id</li>
032     * <li>id to access the inbound message id</li>
033     * <li>in.body or body to access the inbound body</li>
034     * <li>in.body.OGNL or body.OGNL to access the inbound body using an OGNL expression</li>
035     * <li>mandatoryBodyAs(&lt;classname&gt;) to convert the in body to the given type, will throw exception if not possible to convert</li>
036     * <li>bodyAs(&lt;classname&gt;) to convert the in body to the given type, will return null if not possible to convert</li>
037     * <li>headerAs(&lt;key&gt;, &lt;classname&gt;) to convert the in header to the given type, will return null if not possible to convert</li>
038     * <li>out.body to access the inbound body</li>
039     * <li>in.header.foo or header.foo to access an inbound header called 'foo'</li>
040     * <li>in.header.foo[bar] or header.foo[bar] to access an inbound header called 'foo' as a Map and lookup the map with 'bar' as key</li>
041     * <li>in.header.foo.OGNL or header.OGNL to access an inbound header called 'foo' using an OGNL expression</li>
042     * <li>out.header.foo to access an outbound header called 'foo'</li>
043     * <li>property.foo to access the exchange property called 'foo'</li>
044     * <li>property.foo.OGNL to access the exchange property called 'foo' using an OGNL expression</li>
045     * <li>sys.foo to access the system property called 'foo'</li>
046     * <li>sysenv.foo to access the system environment called 'foo'</li>
047     * <li>exception.messsage to access the exception message</li>
048     * <li>threadName to access the current thread name</li>
049     * <li>date:&lt;command&gt;:&lt;pattern&gt; for date formatting using the {@link java.text.SimpleDateFormat} patterns.
050     *     Supported commands are: <tt>now</tt> for current timestamp,
051     *     <tt>in.header.xxx</tt> or <tt>header.xxx</tt> to use the Date object in the in header.
052     *     <tt>out.header.xxx</tt> to use the Date object in the out header.
053     * </li>
054     * <li>bean:&lt;bean expression&gt; to invoke a bean using the
055     * {@link org.apache.camel.language.bean.BeanLanguage BeanLanguage}</li>
056     * <li>properties:&lt;[locations]&gt;:&lt;key&gt; for using property placeholders using the
057     *     {@link org.apache.camel.component.properties.PropertiesComponent}.
058     *     The locations parameter is optional and you can enter multiple locations separated with comma.
059     * </li>
060    * </ul>
061     * <p/>
062     * The simple language supports OGNL notation when accessing either body or header.
063     * <p/>
064     * The simple language now also includes file language out of the box which means the following expression is also
065     * supported:
066     * <ul>
067     *   <li><tt>file:name</tt> to access the file name (is relative, see note below))</li>
068     *   <li><tt>file:name.noext</tt> to access the file name with no extension</li>
069     *   <li><tt>file:name.ext</tt> to access the file extension</li>
070     *   <li><tt>file:ext</tt> to access the file extension</li>
071     *   <li><tt>file:onlyname</tt> to access the file name (no paths)</li>
072     *   <li><tt>file:onlyname.noext</tt> to access the file name (no paths) with no extension </li>
073     *   <li><tt>file:parent</tt> to access the parent file name</li>
074     *   <li><tt>file:path</tt> to access the file path name</li>
075     *   <li><tt>file:absolute</tt> is the file regarded as absolute or relative</li>
076     *   <li><tt>file:absolute.path</tt> to access the absolute file path name</li>
077     *   <li><tt>file:length</tt> to access the file length as a Long type</li>
078     *   <li><tt>file:size</tt> to access the file length as a Long type</li>
079     *   <li><tt>file:modified</tt> to access the file last modified as a Date type</li>
080     *   <li><tt>date:&lt;command&gt;:&lt;pattern&gt;</tt> for date formatting using the {@link java.text.SimpleDateFormat} patterns.
081     *     Additional Supported commands are: <tt>file</tt> for the last modified timestamp of the file.
082     *     All the commands from {@link SimpleLanguage} is also available.
083     *   </li>
084     * </ul>
085     * The <b>relative</b> file is the filename with the starting directory clipped, as opposed to <b>path</b> that will
086     * return the full path including the starting directory.
087     * <br/>
088     * The <b>only</b> file is the filename only with all paths clipped.
089     *
090     */
091    public class SimpleLanguage extends LanguageSupport {
092    
093        // singleton for expressions without a result type
094        private static final SimpleLanguage SIMPLE = new SimpleLanguage();
095    
096        protected boolean allowEscape = true;
097    
098        /**
099         * Default constructor.
100         */
101        public SimpleLanguage() {
102        }
103    
104        public Predicate createPredicate(String expression) {
105            ObjectHelper.notNull(expression, "expression");
106    
107            expression = loadResource(expression);
108    
109            // support old simple language syntax
110            @SuppressWarnings("deprecation")
111            Predicate answer = SimpleBackwardsCompatibleParser.parsePredicate(expression, allowEscape);
112            if (answer == null) {
113                // use the new parser
114                SimplePredicateParser parser = new SimplePredicateParser(expression, allowEscape);
115                answer = parser.parsePredicate();
116            }
117            return answer;
118        }
119    
120        public Expression createExpression(String expression) {
121            ObjectHelper.notNull(expression, "expression");
122    
123            expression = loadResource(expression);
124    
125            // support old simple language syntax
126            @SuppressWarnings("deprecation")
127            Expression answer = SimpleBackwardsCompatibleParser.parseExpression(expression, allowEscape);
128            if (answer == null) {
129                // use the new parser
130                SimpleExpressionParser parser = new SimpleExpressionParser(expression, allowEscape);
131                answer = parser.parseExpression();
132            }
133            return answer;
134        }
135    
136        /**
137         * Creates a new {@link Expression}.
138         * <p/>
139         * <b>Important:</b> If you need to use a predicate (function to return true|false) then use
140         * {@link #predicate(String)} instead.
141         */
142        public static Expression simple(String expression) {
143            return expression(expression);
144        }
145    
146        /**
147         * Creates a new {@link Expression} (or {@link Predicate}
148         * if the resultType is a <tt>Boolean</tt>, or <tt>boolean</tt> type).
149         */
150        public static Expression simple(String expression, Class<?> resultType) {
151            SimpleLanguage answer = new SimpleLanguage();
152            if (resultType == Boolean.class || resultType == boolean.class) {
153                // if its a boolean as result then its a predicate
154                Predicate predicate = answer.createPredicate(expression);
155                return PredicateToExpressionAdapter.toExpression(predicate);
156            } else {
157                Expression exp = answer.createExpression(expression);
158                if (resultType != null) {
159                    exp = ExpressionBuilder.convertToExpression(exp, resultType);
160                }
161                return exp;
162            }
163        }
164    
165        /**
166         * Creates a new {@link Expression}.
167         * <p/>
168         * <b>Important:</b> If you need to use a predicate (function to return true|false) then use
169         * {@link #predicate(String)} instead.
170         */
171        public static Expression expression(String expression) {
172            return SIMPLE.createExpression(expression);
173        }
174    
175        /**
176         * Creates a new {@link Predicate}.
177         */
178        public static Predicate predicate(String predicate) {
179            return SIMPLE.createPredicate(predicate);
180        }
181    
182        /**
183         * Change the start tokens used for functions.
184         * <p/>
185         * This can be used to alter the function tokens to avoid clashes with other
186         * frameworks etc.
187         * <p/>
188         * The default start tokens is <tt>${</tt> and <tt>$simple{</tt>.
189         *
190         * @param startToken new start token(s) to be used for functions
191         */
192        public static void changeFunctionStartToken(String... startToken) {
193            SimpleTokenizer.changeFunctionStartToken(startToken);
194        }
195        
196        /**
197         * Change the end tokens used for functions.
198         * <p/>
199         * This can be used to alter the function tokens to avoid clashes with other
200         * frameworks etc.
201         * <p/>
202         * The default end token is <tt>}</tt>
203         *
204         * @param endToken new end token(s) to be used for functions
205         */
206        public static void changeFunctionEndToken(String... endToken) {
207            SimpleTokenizer.changeFunctionEndToken(endToken);
208        }
209    
210        /**
211         * Change the start token used for functions.
212         * <p/>
213         * This can be used to alter the function tokens to avoid clashes with other
214         * frameworks etc.
215         * <p/>
216         * The default start tokens is <tt>${</tt> and <tt>$simple{</tt>.
217         *
218         * @param startToken new start token to be used for functions
219         */
220        public void setFunctionStartToken(String startToken) {
221            changeFunctionStartToken(startToken);
222        }
223    
224        /**
225         * Change the end token used for functions.
226         * <p/>
227         * This can be used to alter the function tokens to avoid clashes with other
228         * frameworks etc.
229         * <p/>
230         * The default end token is <tt>}</tt>
231         *
232         * @param endToken new end token to be used for functions
233         */
234        public void setFunctionEndToken(String endToken) {
235            changeFunctionEndToken(endToken);
236        }
237    }