Add BigDecimal support for SpEl numeric operations

Prior to this change, SpEL supported numeric operations for int, float,
ect. `new java.math.BigDecimal('0.1') > 0` evaluated to false
(BigDecimal is truncated to int)

This commit introduces support for BigDecimal operations for all
mathematical operators. `new java.math.BigDecimal('0.1') > 0` now
evaluates to true (the comparison is made with BigDecimals)

Issue: SPR-9164
This commit is contained in:
giovannidalloglio
2012-05-22 00:23:01 +02:00
committed by Phillip Webb
parent a41958435a
commit d0ab131a57
18 changed files with 569 additions and 160 deletions

View File

@@ -16,6 +16,8 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue;
@@ -29,6 +31,7 @@ import org.springframework.util.Assert;
* appropriate exceptions if the operand in question does not support decrement.
*
* @author Andy Clement
* @author Giovanni Dall'Oglio Risso
* @since 3.2
*/
public class OpDec extends Operator {
@@ -58,7 +61,11 @@ public class OpDec extends Operator {
if (operandValue instanceof Number) {
Number op1 = (Number) operandValue;
if (op1 instanceof Double) {
if (op1 instanceof BigDecimal) {
newValue = new TypedValue(((BigDecimal) op1).subtract(BigDecimal.ONE),
operandTypedValue.getTypeDescriptor());
}
else if (op1 instanceof Double) {
newValue = new TypedValue(op1.doubleValue() - 1.0d,
operandTypedValue.getTypeDescriptor());
}
@@ -79,7 +86,7 @@ public class OpDec extends Operator {
operandTypedValue.getTypeDescriptor());
}
}
if (newValue==null) {
if (newValue == null) {
try {
newValue = state.operate(Operation.SUBTRACT, returnValue.getValue(), 1);
}

View File

@@ -16,20 +16,26 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import java.math.RoundingMode;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.util.NumberUtils;
/**
* Implements division operator.
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OpDivide extends Operator {
public OpDivide(int pos, SpelNodeImpl... operands) {
super("/", pos, operands);
}
@@ -37,26 +43,35 @@ public class OpDivide extends Operator {
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValueInternal(state).getValue();
Object operandTwo = getRightOperand().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(op1.doubleValue() / op2.doubleValue());
Object leftOperand = getLeftOperand().getValueInternal(state).getValue();
Object rightOperand = getRightOperand().getValueInternal(state).getValue();
if (leftOperand instanceof Number && rightOperand instanceof Number) {
Number leftNumber = (Number) leftOperand;
Number rightNumber = (Number) rightOperand;
if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
int scale = Math.max(leftBigDecimal.scale(), rightBigDecimal.scale());
return new TypedValue(leftBigDecimal.divide(rightBigDecimal, scale, RoundingMode.HALF_EVEN));
}
else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() / op2.floatValue());
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return new TypedValue(leftNumber.doubleValue() / rightNumber.doubleValue());
}
else if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() / op2.longValue());
if (leftNumber instanceof Float || rightNumber instanceof Float) {
return new TypedValue(leftNumber.floatValue() / rightNumber.floatValue());
}
else {
// TODO what about non-int result of the division?
return new TypedValue(op1.intValue() / op2.intValue());
if (leftNumber instanceof Long || rightNumber instanceof Long) {
return new TypedValue(leftNumber.longValue() / rightNumber.longValue());
}
// TODO what about non-int result of the division?
return new TypedValue(leftNumber.intValue() / rightNumber.intValue());
}
return state.operate(Operation.DIVIDE, operandOne, operandTwo);
return state.operate(Operation.DIVIDE, leftOperand, rightOperand);
}
}

View File

@@ -15,18 +15,23 @@
*/
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.support.BooleanTypedValue;
import org.springframework.util.NumberUtils;
/**
* Implements greater-than-or-equal operator.
*
* @author Andy Clement
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OpGE extends Operator {
public OpGE(int pos, SpelNodeImpl... operands) {
super(">=", pos, operands);
}
@@ -39,18 +44,25 @@ public class OpGE extends Operator {
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
Number rightNumber = (Number) right;
if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return BooleanTypedValue.forValue(leftBigDecimal.compareTo(rightBigDecimal) >= 0);
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() >= rightNumber.doubleValue());
}
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() >= rightNumber.floatValue());
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() >= rightNumber.longValue());
}
else {
return BooleanTypedValue.forValue(leftNumber.intValue() >= rightNumber.intValue());
}
return BooleanTypedValue.forValue(leftNumber.intValue() >= rightNumber.intValue());
}
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) >= 0);
}

View File

@@ -16,14 +16,18 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.support.BooleanTypedValue;
import org.springframework.util.NumberUtils;
/**
* Implements greater-than operator.
*
* @author Andy Clement
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OpGT extends Operator {
@@ -38,21 +42,30 @@ public class OpGT extends Operator {
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
Number rightNumber = (Number) right;
if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return BooleanTypedValue.forValue(leftBigDecimal.compareTo(rightBigDecimal) > 0);
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() > rightNumber.doubleValue());
}
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() > rightNumber.floatValue());
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() > rightNumber.longValue());
}
else {
return BooleanTypedValue.forValue(leftNumber.intValue() > rightNumber.intValue());
}
return BooleanTypedValue.forValue(leftNumber.intValue() > rightNumber.intValue());
}
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) > 0);
}

View File

@@ -16,6 +16,8 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue;
@@ -29,6 +31,7 @@ import org.springframework.util.Assert;
* appropriate exceptions if the operand in question does not support increment.
*
* @author Andy Clement
* @author Giovanni Dall'Oglio Risso
* @since 3.2
*/
public class OpInc extends Operator {
@@ -47,34 +50,38 @@ public class OpInc extends Operator {
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl operand = getLeftOperand();
ValueRef lvalue = operand.getValueRef(state);
ValueRef valueRef = operand.getValueRef(state);
final TypedValue operandTypedValue = lvalue.getValue();
final Object operandValue = operandTypedValue.getValue();
TypedValue returnValue = operandTypedValue;
final TypedValue typedValue = valueRef.getValue();
final Object value = typedValue.getValue();
TypedValue returnValue = typedValue;
TypedValue newValue = null;
if (operandValue instanceof Number) {
Number op1 = (Number) operandValue;
if (op1 instanceof Double) {
if (value instanceof Number) {
Number op1 = (Number) value;
if (op1 instanceof BigDecimal) {
newValue = new TypedValue(((BigDecimal) op1).add(BigDecimal.ONE),
typedValue.getTypeDescriptor());
}
else if (op1 instanceof Double) {
newValue = new TypedValue(op1.doubleValue() + 1.0d,
operandTypedValue.getTypeDescriptor());
typedValue.getTypeDescriptor());
}
else if (op1 instanceof Float) {
newValue = new TypedValue(op1.floatValue() + 1.0f,
operandTypedValue.getTypeDescriptor());
typedValue.getTypeDescriptor());
}
else if (op1 instanceof Long) {
newValue = new TypedValue(op1.longValue() + 1L,
operandTypedValue.getTypeDescriptor());
typedValue.getTypeDescriptor());
}
else if (op1 instanceof Short) {
newValue = new TypedValue(op1.shortValue() + (short) 1,
operandTypedValue.getTypeDescriptor());
typedValue.getTypeDescriptor());
}
else {
newValue = new TypedValue(op1.intValue() + 1,
operandTypedValue.getTypeDescriptor());
typedValue.getTypeDescriptor());
}
}
if (newValue == null) {
@@ -93,7 +100,7 @@ public class OpInc extends Operator {
// set the name value
try {
lvalue.setValue(newValue.getValue());
valueRef.setValue(newValue.getValue());
}
catch (SpelEvaluationException see) {
// if unable to set the value the operand is not writable (e.g. 1++ )

View File

@@ -16,18 +16,23 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.support.BooleanTypedValue;
import org.springframework.util.NumberUtils;
/**
* Implements the less-than-or-equal operator.
*
* @author Andy Clement
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OpLE extends Operator {
public OpLE(int pos, SpelNodeImpl... operands) {
super("<=", pos, operands);
}
@@ -41,19 +46,28 @@ public class OpLE extends Operator {
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
Number rightNumber = (Number) right;
if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return BooleanTypedValue.forValue(leftBigDecimal.compareTo(rightBigDecimal) <= 0);
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() <= rightNumber.doubleValue());
}
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() <= rightNumber.floatValue());
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() <= rightNumber.longValue());
}
else {
return BooleanTypedValue.forValue(leftNumber.intValue() <= rightNumber.intValue());
}
return BooleanTypedValue.forValue(leftNumber.intValue() <= rightNumber.intValue());
}
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) <= 0);
}

View File

@@ -16,14 +16,18 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.support.BooleanTypedValue;
import org.springframework.util.NumberUtils;
/**
* Implements the less-than operator.
*
* @author Andy Clement
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OpLT extends Operator {
@@ -43,18 +47,26 @@ public class OpLT extends Operator {
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
Number rightNumber = (Number) right;
if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return BooleanTypedValue.forValue(leftBigDecimal.compareTo(rightBigDecimal) < 0);
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() < rightNumber.doubleValue());
}
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() < rightNumber.floatValue());
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() < rightNumber.longValue());
}
else {
return BooleanTypedValue.forValue(leftNumber.intValue() < rightNumber.intValue());
}
return BooleanTypedValue.forValue(leftNumber.intValue() < rightNumber.intValue());
}
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) < 0);
}

View File

@@ -16,33 +16,40 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.util.NumberUtils;
/**
* The minus operator supports:
* <ul>
* <li>subtraction of {@code BigDecimal}
* <li>subtraction of doubles (floats are represented as doubles)
* <li>subtraction of longs
* <li>subtraction of integers
* <li>subtraction of an int from a string of one character (effectively decreasing that
* character), so 'd'-3='a'
* </ul>
* It can be used as a unary operator for numbers (double/long/int). The standard
* promotions are performed when the operand types vary (double-int=double). For other
* options it defers to the registered overloader.
* It can be used as a unary operator for numbers ({@code BigDecimal}/double/long/int).
* The standard promotions are performed when the operand types vary (double-int=double).
* For other options it defers to the registered overloader.
*
* @author Andy Clement
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OpMinus extends Operator {
public OpMinus(int pos, SpelNodeImpl... operands) {
super("-", pos, operands);
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
@@ -53,6 +60,12 @@ public class OpMinus extends Operator {
Object operand = leftOp.getValueInternal(state).getValue();
if (operand instanceof Number) {
Number n = (Number) operand;
if (operand instanceof BigDecimal) {
BigDecimal bdn = (BigDecimal) n;
return new TypedValue(bdn.negate());
}
if (operand instanceof Double) {
return new TypedValue(0 - n.doubleValue());
}
@@ -64,6 +77,7 @@ public class OpMinus extends Operator {
if (operand instanceof Long) {
return new TypedValue(0 - n.longValue());
}
return new TypedValue(0 - n.intValue());
}
@@ -74,21 +88,28 @@ public class OpMinus extends Operator {
Object right = rightOp.getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return new TypedValue(op1.doubleValue() - op2.doubleValue());
Number leftNumber = (Number) left;
Number rightNumber = (Number) right;
if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return new TypedValue(leftBigDecimal.subtract(rightBigDecimal));
}
if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() - op2.floatValue());
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return new TypedValue(leftNumber.doubleValue() - rightNumber.doubleValue());
}
if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() - op2.longValue());
if (leftNumber instanceof Float || rightNumber instanceof Float) {
return new TypedValue(leftNumber.floatValue() - rightNumber.floatValue());
}
return new TypedValue(op1.intValue() - op2.intValue());
if (leftNumber instanceof Long || rightNumber instanceof Long) {
return new TypedValue(leftNumber.longValue() - rightNumber.longValue());
}
return new TypedValue(leftNumber.intValue() - rightNumber.intValue());
}
else if (left instanceof String && right instanceof Integer
&& ((String) left).length() == 1) {

View File

@@ -16,19 +16,24 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.util.NumberUtils;
/**
* Implements the modulus operator.
*
* @author Andy Clement
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OpModulus extends Operator {
public OpModulus(int pos, SpelNodeImpl... operands) {
super("%", pos, operands);
}
@@ -36,25 +41,34 @@ public class OpModulus extends Operator {
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValueInternal(state).getValue();
Object operandTwo = getRightOperand().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(op1.doubleValue() % op2.doubleValue());
Object leftOperand = getLeftOperand().getValueInternal(state).getValue();
Object rightOperand = getRightOperand().getValueInternal(state).getValue();
if (leftOperand instanceof Number && rightOperand instanceof Number) {
Number leftNumber = (Number) leftOperand;
Number rightNumber = (Number) rightOperand;
if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return new TypedValue(leftBigDecimal.remainder(rightBigDecimal));
}
else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() % op2.floatValue());
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return new TypedValue(leftNumber.doubleValue() % rightNumber.doubleValue());
}
else if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() % op2.longValue());
if (leftNumber instanceof Float || rightNumber instanceof Float) {
return new TypedValue(leftNumber.floatValue() % rightNumber.floatValue());
}
else {
return new TypedValue(op1.intValue() % op2.intValue());
if (leftNumber instanceof Long || rightNumber instanceof Long) {
return new TypedValue(leftNumber.longValue() % rightNumber.longValue());
}
return new TypedValue(leftNumber.intValue() % rightNumber.intValue());
}
return state.operate(Operation.MODULUS, operandOne, operandTwo);
return state.operate(Operation.MODULUS, leftOperand, rightOperand);
}
}

View File

@@ -16,20 +16,24 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.util.NumberUtils;
/**
* Implements the {@code multiply} operator.
*
* <p>Conversions and promotions are handled as defined in
* <a href="http://java.sun.com/docs/books/jls/third_edition/html/conversions.html">Section
* 5.6.2 of the Java Language Specification</a>:
* 5.6.2 of the Java Language Specification</a>, with the addiction of {@code BigDecimal} management:
*
* <p>If any of the operands is of a reference type, unboxing conversion (Section 5.1.8)
* is performed. Then:<br>
* If either operand is of type {@code BigDecimal}, the other is converted to {@code BigDecimal}.<br>
* If either operand is of type double, the other is converted to double.<br>
* Otherwise, if either operand is of type float, the other is converted to float.<br>
* Otherwise, if either operand is of type long, the other is converted to long.<br>
@@ -37,10 +41,12 @@ import org.springframework.expression.spel.ExpressionState;
*
* @author Andy Clement
* @author Sam Brannen
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OpMultiply extends Operator {
public OpMultiply(int pos, SpelNodeImpl... operands) {
super("*", pos, operands);
}
@@ -52,6 +58,7 @@ public class OpMultiply extends Operator {
* for types not supported here.
* <p>Supported operand types:
* <ul>
* <li>{@code BigDecimal}
* <li>doubles
* <li>longs
* <li>integers
@@ -61,15 +68,20 @@ public class OpMultiply extends Operator {
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValueInternal(state).getValue();
Object operandTwo = getRightOperand().getValueInternal(state).getValue();
Object leftOperand = getLeftOperand().getValueInternal(state).getValue();
Object rightOperand = getRightOperand().getValueInternal(state).getValue();
if (leftOperand instanceof Number && rightOperand instanceof Number) {
Number leftNumber = (Number) leftOperand;
Number rightNumber = (Number) rightOperand;
if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return new TypedValue(leftBigDecimal.multiply(rightBigDecimal));
}
if (operandOne instanceof Number && operandTwo instanceof Number) {
Number leftNumber = (Number) operandOne;
Number rightNumber = (Number) operandTwo;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return new TypedValue(leftNumber.doubleValue()
* rightNumber.doubleValue());
return new TypedValue(leftNumber.doubleValue() * rightNumber.doubleValue());
}
if (leftNumber instanceof Float || rightNumber instanceof Float) {
@@ -82,16 +94,16 @@ public class OpMultiply extends Operator {
return new TypedValue(leftNumber.intValue() * rightNumber.intValue());
}
else if (operandOne instanceof String && operandTwo instanceof Integer) {
int repeats = (Integer) operandTwo;
else if (leftOperand instanceof String && rightOperand instanceof Integer) {
int repeats = (Integer) rightOperand;
StringBuilder result = new StringBuilder();
for (int i = 0; i < repeats; i++) {
result.append(operandOne);
result.append(leftOperand);
}
return new TypedValue(result.toString());
}
return state.operate(Operation.MULTIPLY, operandOne, operandTwo);
return state.operate(Operation.MULTIPLY, leftOperand, rightOperand);
}
}

View File

@@ -16,6 +16,8 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
@@ -23,25 +25,29 @@ import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.util.Assert;
import org.springframework.util.NumberUtils;
/**
* The plus operator will:
* <ul>
* <li>add {@code BigDecimal}
* <li>add doubles (floats are represented as doubles)
* <li>add longs
* <li>add integers
* <li>concatenate strings
* </ul>
* It can be used as a unary operator for numbers (double/long/int). The standard
* promotions are performed when the operand types vary (double+int=double). For other
* options it defers to the registered overloader.
* It can be used as a unary operator for numbers ({@code BigDecimal}/double/long/int).
* The standard promotions are performed when the operand types vary (double+int=double).
* For other options it defers to the registered overloader.
*
* @author Andy Clement
* @author Ivo Smid
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OpPlus extends Operator {
public OpPlus(int pos, SpelNodeImpl... operands) {
super("+", pos, operands);
Assert.notEmpty(operands);
@@ -56,7 +62,7 @@ public class OpPlus extends Operator {
if (rightOp == null) { // If only one operand, then this is unary plus
Object operandOne = leftOp.getValueInternal(state).getValue();
if (operandOne instanceof Number) {
if (operandOne instanceof Double || operandOne instanceof Long) {
if (operandOne instanceof Double || operandOne instanceof Long || operandOne instanceof BigDecimal) {
return new TypedValue(operandOne);
}
if (operandOne instanceof Float) {
@@ -68,47 +74,57 @@ public class OpPlus extends Operator {
}
final TypedValue operandOneValue = leftOp.getValueInternal(state);
final Object operandOne = operandOneValue.getValue();
final Object leftOperand = operandOneValue.getValue();
final TypedValue operandTwoValue = rightOp.getValueInternal(state);
final Object operandTwo = operandTwoValue.getValue();
final Object rightOperand = operandTwoValue.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(op1.doubleValue() + op2.doubleValue());
if (leftOperand instanceof Number && rightOperand instanceof Number) {
Number leftNumber = (Number) leftOperand;
Number rightNumber = (Number) rightOperand;
if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return new TypedValue(leftBigDecimal.add(rightBigDecimal));
}
if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() + op2.floatValue());
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return new TypedValue(leftNumber.doubleValue() + rightNumber.doubleValue());
}
if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() + op2.longValue());
if (leftNumber instanceof Float || rightNumber instanceof Float) {
return new TypedValue(leftNumber.floatValue() + rightNumber.floatValue());
}
if (leftNumber instanceof Long || rightNumber instanceof Long) {
return new TypedValue(leftNumber.longValue() + rightNumber.longValue());
}
// TODO what about overflow?
return new TypedValue(op1.intValue() + op2.intValue());
return new TypedValue(leftNumber.intValue() + rightNumber.intValue());
}
if (operandOne instanceof String && operandTwo instanceof String) {
return new TypedValue(new StringBuilder((String) operandOne).append(
(String) operandTwo).toString());
if (leftOperand instanceof String && rightOperand instanceof String) {
return new TypedValue(new StringBuilder((String) leftOperand).append(
(String) rightOperand).toString());
}
if (operandOne instanceof String) {
StringBuilder result = new StringBuilder((String) operandOne);
result.append((operandTwo == null ? "null" : convertTypedValueToString(
if (leftOperand instanceof String) {
StringBuilder result = new StringBuilder((String) leftOperand);
result.append((rightOperand == null ? "null" : convertTypedValueToString(
operandTwoValue, state)));
return new TypedValue(result.toString());
}
if (operandTwo instanceof String) {
StringBuilder result = new StringBuilder((operandOne == null ? "null"
if (rightOperand instanceof String) {
StringBuilder result = new StringBuilder((leftOperand == null ? "null"
: convertTypedValueToString(operandOneValue, state)));
result.append((String) operandTwo);
result.append((String) rightOperand);
return new TypedValue(result.toString());
}
return state.operate(Operation.ADD, operandOne, operandTwo);
return state.operate(Operation.ADD, leftOperand, rightOperand);
}
@Override
@@ -116,7 +132,6 @@ public class OpPlus extends Operator {
if (this.children.length<2) { // unary plus
return new StringBuilder().append("+").append(getLeftOperand().toStringAST()).toString();
}
return super.toStringAST();
}
@@ -125,7 +140,6 @@ public class OpPlus extends Operator {
if (this.children.length < 2) {
return null;
}
return this.children[1];
}

View File

@@ -16,7 +16,11 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.util.NumberUtils;
import org.springframework.util.ObjectUtils;
/**
* Common supertype for operators that operate on either one or two operands. In the case
@@ -24,6 +28,7 @@ import org.springframework.expression.spel.ExpressionState;
* is only one.
*
* @author Andy Clement
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public abstract class Operator extends SpelNodeImpl {
@@ -67,29 +72,35 @@ public abstract class Operator extends SpelNodeImpl {
protected boolean equalityCheck(ExpressionState state, Object left, Object right) {
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
Number leftNumber = (Number) left;
Number rightNumber = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return (op1.doubleValue() == op2.doubleValue());
if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return (leftBigDecimal == null ? rightBigDecimal == null : leftBigDecimal.compareTo(rightBigDecimal) == 0);
}
if (op1 instanceof Float || op2 instanceof Float) {
return (op1.floatValue() == op2.floatValue());
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return (leftNumber.doubleValue() == rightNumber.doubleValue());
}
if (op1 instanceof Long || op2 instanceof Long) {
return (op1.longValue() == op2.longValue());
if (leftNumber instanceof Float || rightNumber instanceof Float) {
return (leftNumber.floatValue() == rightNumber.floatValue());
}
return (op1.intValue() == op2.intValue());
if (leftNumber instanceof Long || rightNumber instanceof Long) {
return (leftNumber.longValue() == rightNumber.longValue());
}
return (leftNumber.intValue() == rightNumber.intValue());
}
if (left != null && (left instanceof Comparable)) {
return (state.getTypeComparator().compare(left, right) == 0);
}
return (left == null ? right == null : left.equals(right));
return ObjectUtils.nullSafeEquals(left, right);
}
}

View File

@@ -16,15 +16,19 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.util.NumberUtils;
/**
* The power operator.
*
* @author Andy Clement
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OperatorPower extends Operator {
@@ -39,32 +43,41 @@ public class OperatorPower extends Operator {
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()));
Object leftOperand = leftOp.getValueInternal(state).getValue();
Object rightOperand = rightOp.getValueInternal(state).getValue();
if (leftOperand instanceof Number && rightOperand instanceof Number) {
Number leftNumber = (Number) leftOperand;
Number rightNumber = (Number) rightOperand;
if (leftNumber instanceof BigDecimal) {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
return new TypedValue(leftBigDecimal.pow(rightNumber.intValue()));
}
else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(Math.pow(op1.floatValue(), op2.floatValue()));
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return new TypedValue(Math.pow(leftNumber.doubleValue(), rightNumber.doubleValue()));
}
else if (op1 instanceof Long || op2 instanceof Long) {
double d = Math.pow(op1.longValue(), op2.longValue());
if (leftNumber instanceof Float || rightNumber instanceof Float) {
return new TypedValue(Math.pow(leftNumber.floatValue(), rightNumber.floatValue()));
}
if (leftNumber instanceof Long || rightNumber instanceof Long) {
double d = Math.pow(leftNumber.longValue(), rightNumber.longValue());
return new TypedValue((long) d);
}
double d = Math.pow(leftNumber.longValue(), rightNumber.longValue());
if (d > Integer.MAX_VALUE) {
return new TypedValue((long) d);
}
else {
double d = Math.pow(op1.longValue(), op2.longValue());
if (d > Integer.MAX_VALUE) {
return new TypedValue((long) d);
}
else {
return new TypedValue((int) d);
}
return new TypedValue((int) d);
}
}
return state.operate(Operation.POWER, operandOne, operandTwo);
return state.operate(Operation.POWER, leftOperand, rightOperand);
}
}

View File

@@ -16,9 +16,12 @@
package org.springframework.expression.spel.support;
import java.math.BigDecimal;
import org.springframework.expression.TypeComparator;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.util.NumberUtils;
/**
* A simple basic TypeComparator implementation. It supports comparison of numbers and
@@ -26,6 +29,7 @@ import org.springframework.expression.spel.SpelMessage;
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class StandardTypeComparator implements TypeComparator {
@@ -45,27 +49,26 @@ public class StandardTypeComparator implements TypeComparator {
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
Number rightNumber = (Number) right;
if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return leftBigDecimal.compareTo(rightBigDecimal);
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
double d1 = leftNumber.doubleValue();
double d2 = rightNumber.doubleValue();
return Double.compare(d1, d2);
return Double.compare(leftNumber.doubleValue(), rightNumber.doubleValue());
}
if (leftNumber instanceof Float || rightNumber instanceof Float) {
float f1 = leftNumber.floatValue();
float f2 = rightNumber.floatValue();
return Float.compare(f1, f2);
return Float.compare(leftNumber.floatValue(), rightNumber.floatValue());
}
if (leftNumber instanceof Long || rightNumber instanceof Long) {
Long l1 = leftNumber.longValue();
Long l2 = rightNumber.longValue();
return l1.compareTo(l2);
return Long.compare(leftNumber.longValue(), rightNumber.longValue());
}
Integer i1 = leftNumber.intValue();
Integer i2 = rightNumber.intValue();
return i1.compareTo(i2);
return Integer.compare(leftNumber.intValue(), rightNumber.intValue());
}
try {