diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/Operation.java b/org.springframework.expression/src/main/java/org/springframework/expression/Operation.java index b0ff0dfe015..38636a2b382 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/Operation.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/Operation.java @@ -24,6 +24,6 @@ package org.springframework.expression; */ public enum Operation { - ADD, SUBTRACT, DIVIDE, MULTIPLY, MODULUS + ADD, SUBTRACT, DIVIDE, MULTIPLY, MODULUS, POWER } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelExpression.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelExpression.java index f908edc7ebe..97f9d195397 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelExpression.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelExpression.java @@ -19,6 +19,7 @@ import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationException; import org.springframework.expression.Expression; import org.springframework.expression.common.ExpressionUtils; +import org.springframework.expression.spel.support.StandardEvaluationContext; /** * A SpelExpressions represents a parsed (valid) expression that is ready to be evaluated in a specified context. An @@ -54,7 +55,8 @@ public class SpelExpression implements Expression { * {@inheritDoc} */ public Object getValue() throws EvaluationException { - return this.ast.getValue(null); + ExpressionState expressionState = new ExpressionState(new StandardEvaluationContext()); + return this.ast.getValue(expressionState); } /** @@ -134,11 +136,10 @@ public class SpelExpression implements Expression { /** * {@inheritDoc} */ - @SuppressWarnings("unchecked") public T getValue(Class expectedResultType) throws EvaluationException { - Object result = getValue(); - // TODO propagate generic-ness into convert - return (T) ExpressionUtils.convert(null, result, expectedResultType); + ExpressionState expressionState = new ExpressionState(new StandardEvaluationContext()); + Object result = this.ast.getValue(expressionState); + return ExpressionUtils.convert(expressionState.getEvaluationContext(), result, expectedResultType); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorPower.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorPower.java new file mode 100644 index 00000000000..2e84e169fe2 --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorPower.java @@ -0,0 +1,69 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.expression.spel.ast; + +import org.antlr.runtime.Token; +import org.springframework.expression.EvaluationException; +import org.springframework.expression.Operation; +import org.springframework.expression.TypedValue; +import org.springframework.expression.spel.ExpressionState; + +/** + * The power operator. + * + * @author Andy Clement + * @since 3.0 + */ +public class OperatorPower extends Operator { + + public OperatorPower(Token payload) { + super(payload); + } + + @Override + public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { + SpelNodeImpl leftOp = getLeftOperand(); + SpelNodeImpl rightOp = getRightOperand(); + + Object operandOne = leftOp.getValueInternal(state).getValue(); + Object operandTwo = rightOp.getValueInternal(state).getValue(); + if (operandOne instanceof Number && operandTwo instanceof Number) { + Number op1 = (Number) operandOne; + Number op2 = (Number) operandTwo; + if (op1 instanceof Double || op2 instanceof Double) { + return new TypedValue(Math.pow(op1.doubleValue(),op2.doubleValue()),DOUBLE_TYPE_DESCRIPTOR); + } else if (op1 instanceof Long || op2 instanceof Long) { + double d= Math.pow(op1.longValue(), op2.longValue()); + return new TypedValue((long)d, LONG_TYPE_DESCRIPTOR); + } else { + double d= Math.pow(op1.longValue(), op2.longValue()); + if (d > Integer.MAX_VALUE) { + return new TypedValue((long)d,LONG_TYPE_DESCRIPTOR); + } else { + return new TypedValue((int)d,INTEGER_TYPE_DESCRIPTOR); + } + } + } + return state.operate(Operation.POWER, operandOne, operandTwo); + } + + @Override + public String getOperatorName() { + return "^"; + } + +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelTreeAdaptor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelTreeAdaptor.java index 623779990d1..cfcd3f3a5a1 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelTreeAdaptor.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelTreeAdaptor.java @@ -72,6 +72,9 @@ public class SpelTreeAdaptor extends CommonTreeAdaptor { return new OperatorDivide(payload); case SpringExpressionsLexer.MOD: return new OperatorModulus(payload); + case SpringExpressionsLexer.POWER: + return new OperatorPower(payload); + case SpringExpressionsLexer.STRING_LITERAL: case SpringExpressionsLexer.DQ_STRING_LITERAL: diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java index c96a49e88af..b02029cbf04 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java @@ -68,6 +68,11 @@ public class StandardEvaluationContext implements EvaluationContext { this.constructorResolvers.add(new ReflectiveConstructorResolver()); this.propertyAccessors.add(new ReflectivePropertyResolver()); } + + public StandardEvaluationContext(Object rootObject) { + this(); + setRootObject(rootObject); + } public void setRootObject(Object rootObject) { this.rootObject = new TypedValue(rootObject,TypeDescriptor.forObject(rootObject)); @@ -84,6 +89,10 @@ public class StandardEvaluationContext implements EvaluationContext { public void setVariable(String name, Object value) { this.variables.put(name, value); } + + public void setVariables(Map variables) { + this.variables.putAll(variables); + } public void registerFunction(String name, Method method) { this.variables.put(name, method); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeComparator.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeComparator.java index 71496a8afd3..1a93beab75d 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeComparator.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeComparator.java @@ -18,7 +18,6 @@ package org.springframework.expression.spel.support; import org.springframework.expression.TypeComparator; import org.springframework.expression.spel.SpelException; -import org.springframework.expression.spel.SpelMessages; /** * A simple basic TypeComparator implementation. It supports comparison of numbers and types implementing Comparable. @@ -64,12 +63,15 @@ public class StandardTypeComparator implements TypeComparator { } } - if (left.getClass() == right.getClass() && left instanceof Comparable) { - return ((Comparable) left).compareTo(right); - } - else { - throw new SpelException(SpelMessages.NOT_COMPARABLE, left.getClass(), right.getClass()); + boolean sameType = left.getClass() == right.getClass(); + + if (sameType) { + if (left instanceof Comparable) { + return ((Comparable) left).compareTo(right); + } } + // TODO coerce one to be like the other? + return left==right?0:1; // identity comparison } public boolean canCompare(Object left, Object right) { diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java index b48dfcc23d1..ec42b5c85d5 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java @@ -40,8 +40,8 @@ public class StandardTypeConverter implements TypeConverter { @SuppressWarnings("unchecked") public T convertValue(Object value, Class targetType) throws EvaluationException { - // For activation when conversion service available - this replaces the rest of the method (probably...) - // return (T)convertValue(value,TypeDescriptor.valueOf(targetType)); +// For activation when conversion service available - this replaces the rest of the method (probably...) +// return (T)convertValue(value,TypeDescriptor.valueOf(targetType)); if (ClassUtils.isAssignableValue(targetType, value)) { return (T) value; } @@ -89,7 +89,7 @@ public class StandardTypeConverter implements TypeConverter { public Object convertValue(Object value, TypeDescriptor typeDescriptor) throws EvaluationException { // For activation when conversion service available - this replaces the rest of the method (probably...) // try { -// return (T)conversionService.executeConversion(value, typeDescriptor); +// return conversionService.executeConversion(value, typeDescriptor); // } catch (ConversionExecutorNotFoundException cenfe) { // throw new SpelException(cenfe, SpelMessages.TYPE_CONVERSION_ERROR, value.getClass(), typeDescriptor.asString()); // } catch (ConversionException ce) { @@ -100,7 +100,7 @@ public class StandardTypeConverter implements TypeConverter { public boolean canConvert(Class sourceType, Class targetType) { // For activation when conversion service available - this replaces the rest of the method (probably...) - // return canConvert(sourceType,TypeDescriptor.valueOf(targetType)); +// return canConvert(sourceType,TypeDescriptor.valueOf(targetType)); if (ClassUtils.isAssignable(targetType, sourceType) || String.class.equals(targetType)) { return true; } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/DefaultComparatorUnitTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/DefaultComparatorUnitTests.java index 98a951cc090..d93a5ba1879 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/DefaultComparatorUnitTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/DefaultComparatorUnitTests.java @@ -68,21 +68,6 @@ public class DefaultComparatorUnitTests extends TestCase { assertTrue(comparator.compare("a","a")==0); assertTrue(comparator.compare("a","b")<0); assertTrue(comparator.compare("b","a")>0); - - try { - comparator.compare("a",3); - fail("Should have failed"); - } catch (EvaluationException ee) { - SpelException sEx = (SpelException)ee; - assertEquals(SpelMessages.NOT_COMPARABLE,sEx.getMessageUnformatted()); - } - try { - comparator.compare(2,"b"); - fail("Should have failed"); - } catch (EvaluationException ee) { - SpelException sEx = (SpelException)ee; - assertEquals(SpelMessages.NOT_COMPARABLE,sEx.getMessageUnformatted()); - } } public void testCanCompare() throws EvaluationException { diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java index bd081f22536..e41973d79d0 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java @@ -16,6 +16,7 @@ package org.springframework.expression.spel; +import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.support.StandardEvaluationContext; @@ -237,5 +238,25 @@ public class EvaluationTests extends ExpressionTestCase { evaluateAndAskForReturnType("3*4+5", (short) 17, Short.class); evaluateAndAskForReturnType("3*4+5", "17", String.class); } + + + public void testAdvancedNumerics() throws Exception { + int twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Integer.class); + assertEquals(24,twentyFour); + double one = parser.parseExpression("8.0 / 5e0 % 2").getValue(Double.class); + assertEquals(1.6d,one); + int o = parser.parseExpression("8.0 / 5e0 % 2").getValue(Integer.class); + assertEquals(1,o); + int sixteen = parser.parseExpression("-2 ^ 4").getValue(Integer.class); + assertEquals(16,sixteen); + int minusFortyFive = parser.parseExpression("1+2-3*8^2/2/2").getValue(Integer.class); + assertEquals(-45,minusFortyFive); + } + + public void testComparison() throws Exception { + EvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); + boolean trueValue = parser.parseExpression("T(java.util.Date) == Birthdate.Class").getValue(context, Boolean.class); + assertTrue(trueValue); + } } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/OperatorTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/OperatorTests.java index 8cb48259f69..0ee30bf34f0 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/OperatorTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/OperatorTests.java @@ -236,10 +236,21 @@ public class OperatorTests extends ExpressionTestCase { node = getOperatorNode((SpelExpression)parser.parseExpression("3 between 4")); assertEquals("between",node.getOperatorName()); + + node = getOperatorNode((SpelExpression)parser.parseExpression("3 ^ 4")); + assertEquals("^",node.getOperatorName()); } public void testOperatorOverloading() { evaluateAndCheckError("'a' * '2'", SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); + evaluateAndCheckError("'a' ^ '2'", SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); + } + + public void testPower() { + evaluate("3^2",9,Integer.class); + evaluate("3.0d^2.0d",9.0d,Double.class); + evaluate("3L^2L",9L,Long.class); + evaluate("(2^32)^2",9223372036854775807L,Long.class); } public void testMixedOperands_FloatsAndDoubles() {