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.ast;
018    
019    import org.apache.camel.Expression;
020    import org.apache.camel.builder.ExpressionBuilder;
021    import org.apache.camel.language.simple.types.SimpleParserException;
022    import org.apache.camel.language.simple.types.SimpleToken;
023    import org.apache.camel.util.ObjectHelper;
024    import org.apache.camel.util.OgnlHelper;
025    import org.apache.camel.util.StringHelper;
026    
027    /**
028     * Represents one of built-in functions of the
029     * <a href="http://camel.apache.org/simple.html">simple language</a>
030     */
031    public class SimpleFunctionExpression extends LiteralExpression {
032    
033        public SimpleFunctionExpression(SimpleToken token) {
034            super(token);
035        }
036    
037        @Override
038        public Expression createExpression(String expression) {
039            String function = text.toString();
040            return createSimpleExpression(function, true);
041        }
042    
043        /**
044         * Creates a Camel {@link Expression} based on this model.
045         *
046         * @param expression the input string
047         * @param strict whether to throw exception if the expression was not a function,
048         *          otherwise <tt>null</tt> is returned
049         * @return the created {@link Expression}
050         * @throws org.apache.camel.language.simple.types.SimpleParserException
051         *          should be thrown if error parsing the model
052         */
053        public Expression createExpression(String expression, boolean strict) {
054            String function = text.toString();
055            return createSimpleExpression(function, strict);
056        }
057    
058        private Expression createSimpleExpression(String function, boolean strict) {
059            // return the function directly if we can create function without analyzing the prefix
060            Expression answer = createSimpleExpressionDirectly(function);
061            if (answer != null) {
062                return answer;
063            }
064    
065            // bodyAs
066            String remainder = ifStartsWithReturnRemainder("bodyAs", function);
067            if (remainder != null) {
068                String type = ObjectHelper.between(remainder, "(", ")");
069                if (type == null) {
070                    throw new SimpleParserException("Valid syntax: ${bodyAs(type)} was: " + function, token.getIndex());
071                }
072                type = StringHelper.removeQuotes(type);
073                return ExpressionBuilder.bodyExpression(type);
074            }
075            // mandatoryBodyAs
076            remainder = ifStartsWithReturnRemainder("mandatoryBodyAs", function);
077            if (remainder != null) {
078                String type = ObjectHelper.between(remainder, "(", ")");
079                if (type == null) {
080                    throw new SimpleParserException("Valid syntax: ${mandatoryBodyAs(type)} was: " + function, token.getIndex());
081                }
082                type = StringHelper.removeQuotes(type);
083                return ExpressionBuilder.mandatoryBodyExpression(type);
084            }
085    
086            // body OGNL
087            remainder = ifStartsWithReturnRemainder("body", function);
088            if (remainder == null) {
089                remainder = ifStartsWithReturnRemainder("in.body", function);
090            }
091            if (remainder != null) {
092                boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder);
093                if (invalid) {
094                    throw new SimpleParserException("Valid syntax: ${body.OGNL} was: " + function, token.getIndex());
095                }
096                return ExpressionBuilder.bodyOgnlExpression(remainder);
097            }
098    
099            // Exception OGNL
100            remainder = ifStartsWithReturnRemainder("exception", function);
101            if (remainder != null) {
102                boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder);
103                if (invalid) {
104                    throw new SimpleParserException("Valid syntax: ${exception.OGNL} was: " + function, token.getIndex());
105                }
106                return ExpressionBuilder.exchangeExceptionOgnlExpression(remainder);
107            }
108    
109            // headerAs
110            remainder = ifStartsWithReturnRemainder("headerAs", function);
111            if (remainder != null) {
112                String keyAndType = ObjectHelper.between(remainder, "(", ")");
113                if (keyAndType == null) {
114                    throw new SimpleParserException("Valid syntax: ${headerAs(key, type)} was: " + function, token.getIndex());
115                }
116    
117                String key = ObjectHelper.before(keyAndType, ",");
118                String type = ObjectHelper.after(keyAndType, ",");
119                if (ObjectHelper.isEmpty(key) || ObjectHelper.isEmpty(type)) {
120                    throw new SimpleParserException("Valid syntax: ${headerAs(key, type)} was: " + function, token.getIndex());
121                }
122                key = StringHelper.removeQuotes(key);
123                type = StringHelper.removeQuotes(type);
124                return ExpressionBuilder.headerExpression(key, type);
125            }
126    
127            // headers function
128            if ("in.headers".equals(function) || "headers".equals(function)) {
129                return ExpressionBuilder.headersExpression();
130            }
131    
132            // in header function
133            remainder = ifStartsWithReturnRemainder("in.headers", function);
134            if (remainder == null) {
135                remainder = ifStartsWithReturnRemainder("in.header", function);
136            }
137            if (remainder == null) {
138                remainder = ifStartsWithReturnRemainder("headers", function);
139            }
140            if (remainder == null) {
141                remainder = ifStartsWithReturnRemainder("header", function);
142            }
143            if (remainder != null) {
144                // remove leading character (dot or ?)
145                if (remainder.startsWith(".") || remainder.startsWith("?")) {
146                    remainder = remainder.substring(1);
147                }
148                // remove starting and ending brackets
149                if (remainder.startsWith("[") && remainder.endsWith("]")) {
150                    remainder = remainder.substring(1, remainder.length() - 1);
151                }
152    
153                // validate syntax
154                boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder);
155                if (invalid) {
156                    throw new SimpleParserException("Valid syntax: ${header.name[key]} was: " + function, token.getIndex());
157                }
158    
159                if (OgnlHelper.isValidOgnlExpression(remainder)) {
160                    // ognl based header
161                    return ExpressionBuilder.headersOgnlExpression(remainder);
162                } else {
163                    // regular header
164                    return ExpressionBuilder.headerExpression(remainder);
165                }
166            }
167    
168            // out header function
169            remainder = ifStartsWithReturnRemainder("out.header.", function);
170            if (remainder == null) {
171                remainder = ifStartsWithReturnRemainder("out.headers.", function);
172            }
173            if (remainder != null) {
174                return ExpressionBuilder.outHeaderExpression(remainder);
175            }
176    
177            // property
178            remainder = ifStartsWithReturnRemainder("property", function);
179            if (remainder != null) {
180                // remove leading character (dot or ?)
181                if (remainder.startsWith(".") || remainder.startsWith("?")) {
182                    remainder = remainder.substring(1);
183                }
184                // remove starting and ending brackets
185                if (remainder.startsWith("[") && remainder.endsWith("]")) {
186                    remainder = remainder.substring(1, remainder.length() - 1);
187                }
188    
189                // validate syntax
190                boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder);
191                if (invalid) {
192                    throw new SimpleParserException("Valid syntax: ${property.OGNL} was: " + function, token.getIndex());
193                }
194    
195                if (OgnlHelper.isValidOgnlExpression(remainder)) {
196                    // ognl based property
197                    return ExpressionBuilder.propertyOgnlExpression(remainder);
198                } else {
199                    // regular property
200                    return ExpressionBuilder.propertyExpression(remainder);
201                }
202            }
203    
204            // system property
205            remainder = ifStartsWithReturnRemainder("sys.", function);
206            if (remainder != null) {
207                return ExpressionBuilder.systemPropertyExpression(remainder);
208            }
209    
210            // system property
211            remainder = ifStartsWithReturnRemainder("sysenv.", function);
212            if (remainder != null) {
213                return ExpressionBuilder.systemEnvironmentExpression(remainder);
214            }
215    
216            // file: prefix
217            remainder = ifStartsWithReturnRemainder("file:", function);
218            if (remainder != null) {
219                Expression fileExpression = createSimpleFileExpression(remainder);
220                if (function != null) {
221                    return fileExpression;
222                }
223            }
224    
225            // date: prefix
226            remainder = ifStartsWithReturnRemainder("date:", function);
227            if (remainder != null) {
228                String[] parts = remainder.split(":");
229                if (parts.length < 2) {
230                    throw new SimpleParserException("Valid syntax: ${date:command:pattern} was: " + function, token.getIndex());
231                }
232                String command = ObjectHelper.before(remainder, ":");
233                String pattern = ObjectHelper.after(remainder, ":");
234                return ExpressionBuilder.dateExpression(command, pattern);
235            }
236    
237            // bean: prefix
238            remainder = ifStartsWithReturnRemainder("bean:", function);
239            if (remainder != null) {
240                return ExpressionBuilder.beanExpression(remainder);
241            }
242    
243            // properties: prefix
244            remainder = ifStartsWithReturnRemainder("properties:", function);
245            if (remainder != null) {
246                String[] parts = remainder.split(":");
247                if (parts.length > 2) {
248                    throw new SimpleParserException("Valid syntax: ${properties:[locations]:key} was: " + function, token.getIndex());
249                }
250    
251                String locations = null;
252                String key = remainder;
253                if (parts.length == 2) {
254                    locations = ObjectHelper.before(remainder, ":");
255                    key = ObjectHelper.after(remainder, ":");
256                }
257                return ExpressionBuilder.propertiesComponentExpression(key, locations);
258            }
259    
260            // ref: prefix
261            remainder = ifStartsWithReturnRemainder("ref:", function);
262            if (remainder != null) {
263                return ExpressionBuilder.refExpression(remainder);
264            }
265    
266            if (strict) {
267                throw new SimpleParserException("Unknown function: " + function, token.getIndex());
268            } else {
269                return null;
270            }
271        }
272    
273        private Expression createSimpleExpressionDirectly(String expression) {
274            if (ObjectHelper.isEqualToAny(expression, "body", "in.body")) {
275                return ExpressionBuilder.bodyExpression();
276            } else if (ObjectHelper.equal(expression, "out.body")) {
277                return ExpressionBuilder.outBodyExpression();
278            } else if (ObjectHelper.equal(expression, "id")) {
279                return ExpressionBuilder.messageIdExpression();
280            } else if (ObjectHelper.equal(expression, "exchangeId")) {
281                return ExpressionBuilder.exchangeIdExpression();
282            } else if (ObjectHelper.equal(expression, "exception")) {
283                return ExpressionBuilder.exchangeExceptionExpression();
284            } else if (ObjectHelper.equal(expression, "exception.message")) {
285                return ExpressionBuilder.exchangeExceptionMessageExpression();
286            } else if (ObjectHelper.equal(expression, "exception.stacktrace")) {
287                return ExpressionBuilder.exchangeExceptionStackTraceExpression();
288            } else if (ObjectHelper.equal(expression, "threadName")) {
289                return ExpressionBuilder.threadNameExpression();
290            } else if (ObjectHelper.equal(expression, "camelId")) {
291                return ExpressionBuilder.camelContextNameExpression();
292            }
293    
294            return null;
295        }
296    
297        private Expression createSimpleFileExpression(String remainder) {
298            if (ObjectHelper.equal(remainder, "name")) {
299                return ExpressionBuilder.fileNameExpression();
300            } else if (ObjectHelper.equal(remainder, "name.noext")) {
301                return ExpressionBuilder.fileNameNoExtensionExpression();
302            } else if (ObjectHelper.equal(remainder, "name.ext")) {
303                return ExpressionBuilder.fileExtensionExpression();
304            } else if (ObjectHelper.equal(remainder, "onlyname")) {
305                return ExpressionBuilder.fileOnlyNameExpression();
306            } else if (ObjectHelper.equal(remainder, "onlyname.noext")) {
307                return ExpressionBuilder.fileOnlyNameNoExtensionExpression();
308            } else if (ObjectHelper.equal(remainder, "ext")) {
309                return ExpressionBuilder.fileExtensionExpression();
310            } else if (ObjectHelper.equal(remainder, "parent")) {
311                return ExpressionBuilder.fileParentExpression();
312            } else if (ObjectHelper.equal(remainder, "path")) {
313                return ExpressionBuilder.filePathExpression();
314            } else if (ObjectHelper.equal(remainder, "absolute")) {
315                return ExpressionBuilder.fileAbsoluteExpression();
316            } else if (ObjectHelper.equal(remainder, "absolute.path")) {
317                return ExpressionBuilder.fileAbsolutePathExpression();
318            } else if (ObjectHelper.equal(remainder, "length") || ObjectHelper.equal(remainder, "size")) {
319                return ExpressionBuilder.fileSizeExpression();
320            } else if (ObjectHelper.equal(remainder, "modified")) {
321                return ExpressionBuilder.fileLastModifiedExpression();
322            }
323            throw new SimpleParserException("Unknown file language syntax: " + remainder, token.getIndex());
324        }
325    
326        private String ifStartsWithReturnRemainder(String prefix, String text) {
327            if (text.startsWith(prefix)) {
328                String remainder = text.substring(prefix.length());
329                if (remainder.length() > 0) {
330                    return remainder;
331                }
332            }
333            return null;
334        }
335    
336    }