More tests, corrections in standard comparator, more helper methods in StandardEvaluationContext.

This commit is contained in:
Andy Clement 2009-04-10 00:03:53 +00:00
parent ba613f2e35
commit 46c5340f57
10 changed files with 132 additions and 31 deletions

View File

@ -24,6 +24,6 @@ package org.springframework.expression;
*/
public enum Operation {
ADD, SUBTRACT, DIVIDE, MULTIPLY, MODULUS
ADD, SUBTRACT, DIVIDE, MULTIPLY, MODULUS, POWER
}

View File

@ -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> T getValue(Class<T> 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);
}
}

View File

@ -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 "^";
}
}

View File

@ -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:

View File

@ -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<String,Object> variables) {
this.variables.putAll(variables);
}
public void registerFunction(String name, Method method) {
this.variables.put(name, method);

View File

@ -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) {

View File

@ -40,8 +40,8 @@ public class StandardTypeConverter implements TypeConverter {
@SuppressWarnings("unchecked")
public <T> T convertValue(Object value, Class<T> 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;
}

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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() {