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 a function or quoted literal, then embed that as text
076                    } else if (child instanceof SimpleFunctionStart || 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                    }
093                }
094
095                // we have now concat the block as a String which contains the function expression
096                // which we then need to evaluate as a function
097                String exp = sb.toString();
098                SimpleFunctionExpression function = new SimpleFunctionExpression(token);
099                function.addText(exp);
100                try {
101                    return function.createExpression(exp).evaluate(exchange, type);
102                } catch (SimpleParserException e) {
103                    // must rethrow parser exception as illegal syntax with details about the location
104                    throw new SimpleIllegalSyntaxException(expression, e.getIndex(), e.getMessage(), e);
105                }
106            }
107
108            @Override
109            public String toString() {
110                return expression;
111            }
112        };
113    }
114
115    @Override
116    public boolean acceptAndAddNode(SimpleNode node) {
117        // only accept literals, quotes or embedded functions
118        if (node instanceof LiteralNode || node instanceof SimpleFunctionStart
119                || node instanceof SingleQuoteStart || node instanceof DoubleQuoteStart) {
120            block.addChild(node);
121            return true;
122        } else {
123            return false;
124        }
125    }
126
127}