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            // body and headers first
066            answer = createSimpleExpressionBodyOrHeader(function, strict);
067            if (answer != null) {
068                return answer;
069            }
070    
071            // camelContext OGNL
072            String remainder = ifStartsWithReturnRemainder("camelContext", function);
073            if (remainder != null) {
074                boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder);
075                if (invalid) {
076                    throw new SimpleParserException("Valid syntax: ${camelContext.OGNL} was: " + function, token.getIndex());
077                }
078                return ExpressionBuilder.camelContextOgnlExpression(remainder);
079            }
080    
081            // Exception OGNL
082            remainder = ifStartsWithReturnRemainder("exception", function);
083            if (remainder != null) {
084                boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder);
085                if (invalid) {
086                    throw new SimpleParserException("Valid syntax: ${exception.OGNL} was: " + function, token.getIndex());
087                }
088                return ExpressionBuilder.exchangeExceptionOgnlExpression(remainder);
089            }
090    
091            // property
092            remainder = ifStartsWithReturnRemainder("property", function);
093            if (remainder != null) {
094                // remove leading character (dot or ?)
095                if (remainder.startsWith(".") || remainder.startsWith("?")) {
096                    remainder = remainder.substring(1);
097                }
098                // remove starting and ending brackets
099                if (remainder.startsWith("[") && remainder.endsWith("]")) {
100                    remainder = remainder.substring(1, remainder.length() - 1);
101                }
102    
103                // validate syntax
104                boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder);
105                if (invalid) {
106                    throw new SimpleParserException("Valid syntax: ${property.OGNL} was: " + function, token.getIndex());
107                }
108    
109                if (OgnlHelper.isValidOgnlExpression(remainder)) {
110                    // ognl based property
111                    return ExpressionBuilder.propertyOgnlExpression(remainder);
112                } else {
113                    // regular property
114                    return ExpressionBuilder.propertyExpression(remainder);
115                }
116            }
117    
118            // system property
119            remainder = ifStartsWithReturnRemainder("sys.", function);
120            if (remainder != null) {
121                return ExpressionBuilder.systemPropertyExpression(remainder);
122            }
123            remainder = ifStartsWithReturnRemainder("sysenv.", function);
124            if (remainder != null) {
125                return ExpressionBuilder.systemEnvironmentExpression(remainder);
126            }
127    
128            // file: prefix
129            remainder = ifStartsWithReturnRemainder("file:", function);
130            if (remainder != null) {
131                Expression fileExpression = createSimpleFileExpression(remainder);
132                if (function != null) {
133                    return fileExpression;
134                }
135            }
136    
137            // date: prefix
138            remainder = ifStartsWithReturnRemainder("date:", function);
139            if (remainder != null) {
140                String[] parts = remainder.split(":");
141                if (parts.length < 2) {
142                    throw new SimpleParserException("Valid syntax: ${date:command:pattern} was: " + function, token.getIndex());
143                }
144                String command = ObjectHelper.before(remainder, ":");
145                String pattern = ObjectHelper.after(remainder, ":");
146                return ExpressionBuilder.dateExpression(command, pattern);
147            }
148    
149            // bean: prefix
150            remainder = ifStartsWithReturnRemainder("bean:", function);
151            if (remainder != null) {
152                return ExpressionBuilder.beanExpression(remainder);
153            }
154    
155            // properties: prefix
156            remainder = ifStartsWithReturnRemainder("properties:", function);
157            if (remainder != null) {
158                String[] parts = remainder.split(":");
159                if (parts.length > 2) {
160                    throw new SimpleParserException("Valid syntax: ${properties:[locations]:key} was: " + function, token.getIndex());
161                }
162    
163                String locations = null;
164                String key = remainder;
165                if (parts.length == 2) {
166                    locations = ObjectHelper.before(remainder, ":");
167                    key = ObjectHelper.after(remainder, ":");
168                }
169                return ExpressionBuilder.propertiesComponentExpression(key, locations);
170            }
171    
172            // ref: prefix
173            remainder = ifStartsWithReturnRemainder("ref:", function);
174            if (remainder != null) {
175                return ExpressionBuilder.refExpression(remainder);
176            }
177    
178            // const: prefix
179            remainder = ifStartsWithReturnRemainder("type:", function);
180            if (remainder != null) {
181                Expression exp = ExpressionBuilder.typeExpression(remainder);
182                // we want to cache this expression so we wont re-evaluate it as the type/constant wont change
183                return ExpressionBuilder.cacheExpression(exp);
184            }
185    
186            if (strict) {
187                throw new SimpleParserException("Unknown function: " + function, token.getIndex());
188            } else {
189                return null;
190            }
191        }
192    
193        private Expression createSimpleExpressionBodyOrHeader(String function, boolean strict) {
194            // bodyAs
195            String remainder = ifStartsWithReturnRemainder("bodyAs", function);
196            if (remainder != null) {
197                String type = ObjectHelper.between(remainder, "(", ")");
198                if (type == null) {
199                    throw new SimpleParserException("Valid syntax: ${bodyAs(type)} was: " + function, token.getIndex());
200                }
201                type = StringHelper.removeQuotes(type);
202                return ExpressionBuilder.bodyExpression(type);
203            }
204            // mandatoryBodyAs
205            remainder = ifStartsWithReturnRemainder("mandatoryBodyAs", function);
206            if (remainder != null) {
207                String type = ObjectHelper.between(remainder, "(", ")");
208                if (type == null) {
209                    throw new SimpleParserException("Valid syntax: ${mandatoryBodyAs(type)} was: " + function, token.getIndex());
210                }
211                type = StringHelper.removeQuotes(type);
212                return ExpressionBuilder.mandatoryBodyExpression(type);
213            }
214    
215            // body OGNL
216            remainder = ifStartsWithReturnRemainder("body", function);
217            if (remainder == null) {
218                remainder = ifStartsWithReturnRemainder("in.body", function);
219            }
220            if (remainder != null) {
221                boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder);
222                if (invalid) {
223                    throw new SimpleParserException("Valid syntax: ${body.OGNL} was: " + function, token.getIndex());
224                }
225                return ExpressionBuilder.bodyOgnlExpression(remainder);
226            }
227    
228            // headerAs
229            remainder = ifStartsWithReturnRemainder("headerAs", function);
230            if (remainder != null) {
231                String keyAndType = ObjectHelper.between(remainder, "(", ")");
232                if (keyAndType == null) {
233                    throw new SimpleParserException("Valid syntax: ${headerAs(key, type)} was: " + function, token.getIndex());
234                }
235    
236                String key = ObjectHelper.before(keyAndType, ",");
237                String type = ObjectHelper.after(keyAndType, ",");
238                if (ObjectHelper.isEmpty(key) || ObjectHelper.isEmpty(type)) {
239                    throw new SimpleParserException("Valid syntax: ${headerAs(key, type)} was: " + function, token.getIndex());
240                }
241                key = StringHelper.removeQuotes(key);
242                type = StringHelper.removeQuotes(type);
243                return ExpressionBuilder.headerExpression(key, type);
244            }
245    
246            // headers function
247            if ("in.headers".equals(function) || "headers".equals(function)) {
248                return ExpressionBuilder.headersExpression();
249            }
250    
251            // in header function
252            remainder = ifStartsWithReturnRemainder("in.headers", function);
253            if (remainder == null) {
254                remainder = ifStartsWithReturnRemainder("in.header", function);
255            }
256            if (remainder == null) {
257                remainder = ifStartsWithReturnRemainder("headers", function);
258            }
259            if (remainder == null) {
260                remainder = ifStartsWithReturnRemainder("header", function);
261            }
262            if (remainder != null) {
263                // remove leading character (dot or ?)
264                if (remainder.startsWith(".") || remainder.startsWith("?")) {
265                    remainder = remainder.substring(1);
266                }
267                // remove starting and ending brackets
268                if (remainder.startsWith("[") && remainder.endsWith("]")) {
269                    remainder = remainder.substring(1, remainder.length() - 1);
270                }
271    
272                // validate syntax
273                boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder);
274                if (invalid) {
275                    throw new SimpleParserException("Valid syntax: ${header.name[key]} was: " + function, token.getIndex());
276                }
277    
278                if (OgnlHelper.isValidOgnlExpression(remainder)) {
279                    // ognl based header
280                    return ExpressionBuilder.headersOgnlExpression(remainder);
281                } else {
282                    // regular header
283                    return ExpressionBuilder.headerExpression(remainder);
284                }
285            }
286    
287            // out header function
288            remainder = ifStartsWithReturnRemainder("out.header.", function);
289            if (remainder == null) {
290                remainder = ifStartsWithReturnRemainder("out.headers.", function);
291            }
292            if (remainder != null) {
293                return ExpressionBuilder.outHeaderExpression(remainder);
294            }
295    
296            return null;
297        }
298    
299        private Expression createSimpleExpressionDirectly(String expression) {
300            if (ObjectHelper.isEqualToAny(expression, "body", "in.body")) {
301                return ExpressionBuilder.bodyExpression();
302            } else if (ObjectHelper.equal(expression, "out.body")) {
303                return ExpressionBuilder.outBodyExpression();
304            } else if (ObjectHelper.equal(expression, "id")) {
305                return ExpressionBuilder.messageIdExpression();
306            } else if (ObjectHelper.equal(expression, "exchangeId")) {
307                return ExpressionBuilder.exchangeIdExpression();
308            } else if (ObjectHelper.equal(expression, "exception")) {
309                return ExpressionBuilder.exchangeExceptionExpression();
310            } else if (ObjectHelper.equal(expression, "exception.message")) {
311                return ExpressionBuilder.exchangeExceptionMessageExpression();
312            } else if (ObjectHelper.equal(expression, "exception.stacktrace")) {
313                return ExpressionBuilder.exchangeExceptionStackTraceExpression();
314            } else if (ObjectHelper.equal(expression, "threadName")) {
315                return ExpressionBuilder.threadNameExpression();
316            } else if (ObjectHelper.equal(expression, "camelId")) {
317                return ExpressionBuilder.camelContextNameExpression();
318            } else if (ObjectHelper.equal(expression, "routeId")) {
319                return ExpressionBuilder.routeIdExpression();
320            } else if (ObjectHelper.equal(expression, "null")) {
321                return ExpressionBuilder.nullExpression();
322            }
323    
324            return null;
325        }
326    
327        private Expression createSimpleFileExpression(String remainder) {
328            if (ObjectHelper.equal(remainder, "name")) {
329                return ExpressionBuilder.fileNameExpression();
330            } else if (ObjectHelper.equal(remainder, "name.noext")) {
331                return ExpressionBuilder.fileNameNoExtensionExpression();
332            } else if (ObjectHelper.equal(remainder, "name.ext")) {
333                return ExpressionBuilder.fileExtensionExpression();
334            } else if (ObjectHelper.equal(remainder, "onlyname")) {
335                return ExpressionBuilder.fileOnlyNameExpression();
336            } else if (ObjectHelper.equal(remainder, "onlyname.noext")) {
337                return ExpressionBuilder.fileOnlyNameNoExtensionExpression();
338            } else if (ObjectHelper.equal(remainder, "ext")) {
339                return ExpressionBuilder.fileExtensionExpression();
340            } else if (ObjectHelper.equal(remainder, "parent")) {
341                return ExpressionBuilder.fileParentExpression();
342            } else if (ObjectHelper.equal(remainder, "path")) {
343                return ExpressionBuilder.filePathExpression();
344            } else if (ObjectHelper.equal(remainder, "absolute")) {
345                return ExpressionBuilder.fileAbsoluteExpression();
346            } else if (ObjectHelper.equal(remainder, "absolute.path")) {
347                return ExpressionBuilder.fileAbsolutePathExpression();
348            } else if (ObjectHelper.equal(remainder, "length") || ObjectHelper.equal(remainder, "size")) {
349                return ExpressionBuilder.fileSizeExpression();
350            } else if (ObjectHelper.equal(remainder, "modified")) {
351                return ExpressionBuilder.fileLastModifiedExpression();
352            }
353            throw new SimpleParserException("Unknown file language syntax: " + remainder, token.getIndex());
354        }
355    
356        private String ifStartsWithReturnRemainder(String prefix, String text) {
357            if (text.startsWith(prefix)) {
358                String remainder = text.substring(prefix.length());
359                if (remainder.length() > 0) {
360                    return remainder;
361                }
362            }
363            return null;
364        }
365    
366    }