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.IsSingleton;
021    import org.apache.camel.Predicate;
022    import org.apache.camel.builder.ExpressionBuilder;
023    import org.apache.camel.spi.Language;
024    import org.apache.camel.util.ObjectHelper;
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 implements Language, IsSingleton {
092    
093        // singleton for expressions without a result type
094        private static final SimpleLanguage SIMPLE = new SimpleLanguage();
095    
096        protected Class<?> resultType;
097        protected boolean allowEscape = true;
098    
099        /**
100         * Default constructor.
101         */
102        public SimpleLanguage() {
103        }
104    
105        public Class<?> getResultType() {
106            return resultType;
107        }
108    
109        public void setResultType(Class<?> resultType) {
110            this.resultType = resultType;
111        }
112    
113        public boolean isAllowEscape() {
114            return allowEscape;
115        }
116    
117        public void setAllowEscape(boolean allowEscape) {
118            this.allowEscape = allowEscape;
119        }
120    
121        @Override
122        public boolean isSingleton() {
123            // we cannot be singleton as we have state
124            return false;
125        }
126    
127        public Predicate createPredicate(String expression) {
128            ObjectHelper.notNull(expression, "expression");
129    
130            // support old simple language syntax
131            @SuppressWarnings("deprecation")
132            Predicate answer = SimpleBackwardsCompatibleParser.parsePredicate(expression, allowEscape);
133            if (answer == null) {
134                // use the new parser
135                SimplePredicateParser parser = new SimplePredicateParser(expression, allowEscape);
136                answer = parser.parsePredicate();
137            }
138            return answer;
139        }
140    
141        public Expression createExpression(String expression) {
142            ObjectHelper.notNull(expression, "expression");
143    
144            // support old simple language syntax
145            @SuppressWarnings("deprecation")
146            Expression answer = SimpleBackwardsCompatibleParser.parseExpression(expression, allowEscape);
147            if (answer == null) {
148                // use the new parser
149                SimpleExpressionParser parser = new SimpleExpressionParser(expression, allowEscape);
150                answer = parser.parseExpression();
151            }
152            if (resultType != null) {
153                answer = ExpressionBuilder.convertToExpression(answer, resultType);
154            }
155            return answer;
156        }
157    
158        public static Expression simple(String expression) {
159            return SIMPLE.createExpression(expression);
160        }
161    
162        public static Expression simple(String expression, Class<?> resultType) {
163            SimpleLanguage answer = new SimpleLanguage();
164            answer.setResultType(resultType);
165            return answer.createExpression(expression);
166        }
167    
168        /**
169         * Change the start tokens used for functions.
170         * <p/>
171         * This can be used to alter the function tokens to avoid clashes with other
172         * frameworks etc.
173         * <p/>
174         * The default start tokens is <tt>${</tt> and <tt>$simple{</tt>.
175         *
176         * @param startToken new start token(s) to be used for functions
177         */
178        public static void changeFunctionStartToken(String... startToken) {
179            SimpleTokenizer.changeFunctionStartToken(startToken);
180        }
181        
182        /**
183         * Change the end 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 end token is <tt>}</tt>
189         *
190         * @param endToken new end token(s) to be used for functions
191         */
192        public static void changeFunctionEndToken(String... endToken) {
193            SimpleTokenizer.changeFunctionEndToken(endToken);
194        }
195    
196        /**
197         * Change the start token 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 start tokens is <tt>${</tt> and <tt>$simple{</tt>.
203         *
204         * @param startToken new start token to be used for functions
205         */
206        public void setFunctionStartToken(String startToken) {
207            changeFunctionStartToken(startToken);
208        }
209    
210        /**
211         * Change the end 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 end token is <tt>}</tt>
217         *
218         * @param endToken new end token to be used for functions
219         */
220        public void setFunctionEndToken(String endToken) {
221            changeFunctionEndToken(endToken);
222        }
223    }