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    @Override
039    public String toString() {
040        // output a nice toString so it makes debugging easier as we can see the entire block
041        return "${" + block + "}";
042    }
043
044    @Override
045    public Expression createExpression(String expression) {
046        // a function can either be a simple literal function, or contain nested functions
047        if (block.getChildren().size() == 1 && block.getChildren().get(0) instanceof LiteralNode) {
048            return doCreateLiteralExpression(expression);
049        } else {
050            return doCreateCompositeExpression(expression);
051        }
052    }
053
054    private Expression doCreateLiteralExpression(final String expression) {
055        SimpleFunctionExpression function = new SimpleFunctionExpression(this.getToken());
056        LiteralNode literal = (LiteralNode) block.getChildren().get(0);
057        function.addText(literal.getText());
058        return function.createExpression(expression);
059    }
060
061    private Expression doCreateCompositeExpression(final String expression) {
062        final SimpleToken token = getToken();
063        return new Expression() {
064            @Override
065            public <T> T evaluate(Exchange exchange, Class<T> type) {
066                StringBuilder sb = new StringBuilder();
067                boolean quoteEmbeddedFunctions = false;
068
069                // we need to concat the block so we have the expression
070                for (SimpleNode child : block.getChildren()) {
071                    if (child instanceof LiteralNode) {
072                        String text = ((LiteralNode) child).getText();
073                        sb.append(text);
074                        quoteEmbeddedFunctions |= ((LiteralNode) child).quoteEmbeddedNodes();
075                    // if its quoted literal then embed that as text
076                    } else if (child instanceof SingleQuoteStart || child instanceof DoubleQuoteStart) {
077                        try {
078                            // pass in null when we evaluate the nested expressions
079                            Expression nested = child.createExpression(null);
080                            String text = nested.evaluate(exchange, String.class);
081                            if (text != null) {
082                                if (quoteEmbeddedFunctions && !StringHelper.isQuoted(text)) {
083                                    sb.append("'").append(text).append("'");
084                                } else {
085                                    sb.append(text);
086                                }
087                            }
088                        } catch (SimpleParserException e) {
089                            // must rethrow parser exception as illegal syntax with details about the location
090                            throw new SimpleIllegalSyntaxException(expression, e.getIndex(), e.getMessage(), e);
091                        }
092                    // if its an inlined function then embed that function as text so it can be evaluated lazy
093                    } else if (child instanceof SimpleFunctionStart) {
094                        sb.append(child);
095                    }
096                }
097
098                // we have now concat the block as a String which contains the function expression
099                // which we then need to evaluate as a function
100                String exp = sb.toString();
101                SimpleFunctionExpression function = new SimpleFunctionExpression(token);
102                function.addText(exp);
103                try {
104                    return function.createExpression(exp).evaluate(exchange, type);
105                } catch (SimpleParserException e) {
106                    // must rethrow parser exception as illegal syntax with details about the location
107                    throw new SimpleIllegalSyntaxException(expression, e.getIndex(), e.getMessage(), e);
108                }
109            }
110
111            @Override
112            public String toString() {
113                return expression;
114            }
115        };
116    }
117
118    @Override
119    public boolean acceptAndAddNode(SimpleNode node) {
120        // only accept literals, quotes or embedded functions
121        if (node instanceof LiteralNode || node instanceof SimpleFunctionStart
122                || node instanceof SingleQuoteStart || node instanceof DoubleQuoteStart) {
123            block.addChild(node);
124            return true;
125        } else {
126            return false;
127        }
128    }
129
130}