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 */
017package org.apache.camel.language.simple.ast;
018
019import org.apache.camel.Exchange;
020import org.apache.camel.Expression;
021import org.apache.camel.language.simple.types.SimpleIllegalSyntaxException;
022import org.apache.camel.language.simple.types.SimpleParserException;
023import org.apache.camel.language.simple.types.SimpleToken;
024import org.apache.camel.util.StringHelper;
025
026/**
027 * Starts a function
028 */
029public class SimpleFunctionStart extends BaseSimpleNode implements BlockStart {
030
031    private CompositeNodes block;
032
033    public SimpleFunctionStart(SimpleToken token) {
034        super(token);
035        this.block = new CompositeNodes(token);
036    }
037
038    public boolean lazyEval(SimpleNode child) {
039        String text = child.toString();
040        // don't lazy evaluate nested type references as they are static
041        return !text.startsWith("${type:");
042    }
043
044    @Override
045    public String toString() {
046        // output a nice toString so it makes debugging easier as we can see the entire block
047        return "${" + block + "}";
048    }
049
050    @Override
051    public Expression createExpression(String expression) {
052        // a function can either be a simple literal function, or contain nested functions
053        if (block.getChildren().size() == 1 && block.getChildren().get(0) instanceof LiteralNode) {
054            return doCreateLiteralExpression(expression);
055        } else {
056            return doCreateCompositeExpression(expression);
057        }
058    }
059
060    private Expression doCreateLiteralExpression(final String expression) {
061        SimpleFunctionExpression function = new SimpleFunctionExpression(this.getToken());
062        LiteralNode literal = (LiteralNode) block.getChildren().get(0);
063        function.addText(literal.getText());
064        return function.createExpression(expression);
065    }
066
067    private Expression doCreateCompositeExpression(final String expression) {
068        final SimpleToken token = getToken();
069        return new Expression() {
070            @Override
071            public <T> T evaluate(Exchange exchange, Class<T> type) {
072                StringBuilder sb = new StringBuilder();
073                boolean quoteEmbeddedFunctions = false;
074
075                // we need to concat the block so we have the expression
076                for (SimpleNode child : block.getChildren()) {
077                    // whether a nested function should be lazy evaluated or not
078                    boolean lazy = true;
079                    if (child instanceof SimpleFunctionStart) {
080                        lazy = ((SimpleFunctionStart) child).lazyEval(child);
081                    }
082                    if (child instanceof LiteralNode) {
083                        String text = ((LiteralNode) child).getText();
084                        sb.append(text);
085                        quoteEmbeddedFunctions |= ((LiteralNode) child).quoteEmbeddedNodes();
086                    // if its quoted literal then embed that as text
087                    } else if (!lazy || child instanceof SingleQuoteStart || child instanceof DoubleQuoteStart) {
088                        try {
089                            // pass in null when we evaluate the nested expressions
090                            Expression nested = child.createExpression(null);
091                            String text = nested.evaluate(exchange, String.class);
092                            if (text != null) {
093                                if (quoteEmbeddedFunctions && !StringHelper.isQuoted(text)) {
094                                    sb.append("'").append(text).append("'");
095                                } else {
096                                    sb.append(text);
097                                }
098                            }
099                        } catch (SimpleParserException e) {
100                            // must rethrow parser exception as illegal syntax with details about the location
101                            throw new SimpleIllegalSyntaxException(expression, e.getIndex(), e.getMessage(), e);
102                        }
103                    // if its an inlined function then embed that function as text so it can be evaluated lazy
104                    } else if (child instanceof SimpleFunctionStart) {
105                        sb.append(child);
106                    }
107                }
108
109                // we have now concat the block as a String which contains the function expression
110                // which we then need to evaluate as a function
111                String exp = sb.toString();
112                SimpleFunctionExpression function = new SimpleFunctionExpression(token);
113                function.addText(exp);
114                try {
115                    return function.createExpression(exp).evaluate(exchange, type);
116                } catch (SimpleParserException e) {
117                    // must rethrow parser exception as illegal syntax with details about the location
118                    throw new SimpleIllegalSyntaxException(expression, e.getIndex(), e.getMessage(), e);
119                }
120            }
121
122            @Override
123            public String toString() {
124                return expression;
125            }
126        };
127    }
128
129    @Override
130    public boolean acceptAndAddNode(SimpleNode node) {
131        // only accept literals, quotes or embedded functions
132        if (node instanceof LiteralNode || node instanceof SimpleFunctionStart
133                || node instanceof SingleQuoteStart || node instanceof DoubleQuoteStart) {
134            block.addChild(node);
135            return true;
136        } else {
137            return false;
138        }
139    }
140
141}