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.CamelExchangeException;
020import org.apache.camel.Exchange;
021import org.apache.camel.Expression;
022import org.apache.camel.NoTypeConversionAvailableException;
023import org.apache.camel.language.simple.types.SimpleParserException;
024import org.apache.camel.language.simple.types.SimpleToken;
025import org.apache.camel.language.simple.types.UnaryOperatorType;
026import org.apache.camel.util.ObjectHelper;
027
028/**
029 * Represents an unary expression in the AST
030 */
031public class UnaryExpression extends BaseSimpleNode {
032
033    private UnaryOperatorType operator;
034    private SimpleNode left;
035
036    public UnaryExpression(SimpleToken token) {
037        super(token);
038        operator = UnaryOperatorType.asOperator(token.getText());
039    }
040
041    @Override
042    public String toString() {
043        if (left != null) {
044            return left + token.getText();
045        } else {
046            return token.getText();
047        }
048    }
049
050    /**
051     * Accepts the left node to this operator
052     *
053     * @param left  the left node to accept
054     */
055    public void acceptLeft(SimpleNode left) {
056        this.left = left;
057    }
058
059    public UnaryOperatorType getOperator() {
060        return operator;
061    }
062
063    @Override
064    public Expression createExpression(String expression) {
065        ObjectHelper.notNull(left, "left node", this);
066
067        final Expression leftExp = left.createExpression(expression);
068
069        if (operator == UnaryOperatorType.INC) {
070            return createIncExpression(leftExp);
071        } else if (operator == UnaryOperatorType.DEC) {
072            return createDecExpression(leftExp);
073        }
074
075        throw new SimpleParserException("Unknown unary operator " + operator, token.getIndex());
076    }
077
078    private Expression createIncExpression(final Expression leftExp) {
079        return new Expression() {
080            @Override
081            public <T> T evaluate(Exchange exchange, Class<T> type) {
082                Number num = leftExp.evaluate(exchange, Number.class);
083                if (num != null) {
084                    long val = num.longValue();
085                    val++;
086
087                    // convert value back to same type as input as we want to preserve type
088                    Object left = leftExp.evaluate(exchange, Object.class);
089                    try {
090                        left = exchange.getContext().getTypeConverter().mandatoryConvertTo(left.getClass(), exchange, val);
091                    } catch (NoTypeConversionAvailableException e) {
092                        throw ObjectHelper.wrapRuntimeCamelException(e);
093                    }
094
095                    // and return the result
096                    return exchange.getContext().getTypeConverter().convertTo(type, left);
097                }
098                // cannot convert the expression as a number
099                Exception cause = new CamelExchangeException("Cannot evaluate " + leftExp + " as a number", exchange);
100                throw ObjectHelper.wrapRuntimeCamelException(cause);
101            }
102
103            @Override
104            public String toString() {
105                return left + operator.toString();
106            }
107        };
108    }
109
110    private Expression createDecExpression(final Expression leftExp) {
111        return new Expression() {
112            @Override
113            public <T> T evaluate(Exchange exchange, Class<T> type) {
114                Number num = leftExp.evaluate(exchange, Number.class);
115                if (num != null) {
116                    long val = num.longValue();
117                    val--;
118
119                    // convert value back to same type as input as we want to preserve type
120                    Object left = leftExp.evaluate(exchange, Object.class);
121                    try {
122                        left = exchange.getContext().getTypeConverter().mandatoryConvertTo(left.getClass(), exchange, val);
123                    } catch (NoTypeConversionAvailableException e) {
124                        throw ObjectHelper.wrapRuntimeCamelException(e);
125                    }
126
127                    // and return the result
128                    return exchange.getContext().getTypeConverter().convertTo(type, left);
129                }
130                // cannot convert the expression as a number
131                Exception cause = new CamelExchangeException("Cannot evaluate " + leftExp + " as a number", exchange);
132                throw ObjectHelper.wrapRuntimeCamelException(cause);
133            }
134
135            @Override
136            public String toString() {
137                return left + operator.toString();
138            }
139        };
140    }
141
142}