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.CamelExchangeException;
020    import org.apache.camel.Exchange;
021    import org.apache.camel.Expression;
022    import org.apache.camel.NoTypeConversionAvailableException;
023    import org.apache.camel.language.simple.types.SimpleParserException;
024    import org.apache.camel.language.simple.types.SimpleToken;
025    import org.apache.camel.language.simple.types.UnaryOperatorType;
026    import org.apache.camel.util.ObjectHelper;
027    
028    /**
029     * Represents an unary expression in the AST
030     */
031    public 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    }