diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDec.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDec.java index 0a5a1236d7ea..e7cf3129a97c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDec.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDec.java @@ -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; @@ -58,7 +60,12 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep if (operandValue instanceof Number) { Number op1 = (Number) operandValue; - if (op1 instanceof Double) { + if (op1 instanceof BigDecimal) { + BigDecimal bd = (BigDecimal) op1; + newValue = new TypedValue(bd.subtract(BigDecimal.ONE), + operandTypedValue.getTypeDescriptor()); + } + else if (op1 instanceof Double) { newValue = new TypedValue(op1.doubleValue() - 1.0d, operandTypedValue.getTypeDescriptor()); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java index 844e2aa1d81e..1027f2b14209 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java @@ -16,10 +16,14 @@ 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. @@ -42,19 +46,24 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep if (operandOne instanceof Number && operandTwo instanceof Number) { Number op1 = (Number) operandOne; Number op2 = (Number) operandTwo; - if (op1 instanceof Double || op2 instanceof Double) { + if (op1 instanceof BigDecimal || op2 instanceof BigDecimal) { + BigDecimal op1BD = NumberUtils.convertNumberToTargetClass(op1, BigDecimal.class); + BigDecimal op2BD = NumberUtils.convertNumberToTargetClass(op2, BigDecimal.class); + int scale = Math.max(op1BD.scale(), op2BD.scale()); + return new TypedValue(op1BD.divide(op2BD, scale, RoundingMode.HALF_EVEN)); + } + if (op1 instanceof Double || op2 instanceof Double) { return new TypedValue(op1.doubleValue() / op2.doubleValue()); } - else if (op1 instanceof Float || op2 instanceof Float) { + if (op1 instanceof Float || op2 instanceof Float) { return new TypedValue(op1.floatValue() / op2.floatValue()); } - else if (op1 instanceof Long || op2 instanceof Long) { + if (op1 instanceof Long || op2 instanceof Long) { return new TypedValue(op1.longValue() / op2.longValue()); } - else { - // TODO what about non-int result of the division? - return new TypedValue(op1.intValue() / op2.intValue()); - } + + // TODO what about non-int result of the division? + return new TypedValue(op1.intValue() / op2.intValue()); } return state.operate(Operation.DIVIDE, operandOne, operandTwo); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java index 1121cc840a44..fe7ce7977aa8 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java @@ -16,9 +16,14 @@ 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.SpelEvaluationException; +import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.support.BooleanTypedValue; +import org.springframework.util.NumberUtils; /** * Implements equality operator. @@ -41,18 +46,21 @@ public BooleanTypedValue getValueInternal(ExpressionState state) if (left instanceof Number && right instanceof Number) { Number op1 = (Number) left; Number op2 = (Number) right; + if (op1 instanceof BigDecimal || op2 instanceof BigDecimal) { + BigDecimal bd1 = NumberUtils.convertNumberToTargetClass(op1, BigDecimal.class); + BigDecimal bd2 = NumberUtils.convertNumberToTargetClass(op2, BigDecimal.class); + return BooleanTypedValue.forValue(bd1.compareTo(bd2) == 0); + } if (op1 instanceof Double || op2 instanceof Double) { return BooleanTypedValue.forValue(op1.doubleValue() == op2.doubleValue()); } - else if (op1 instanceof Float || op2 instanceof Float) { + if (op1 instanceof Float || op2 instanceof Float) { return BooleanTypedValue.forValue(op1.floatValue() == op2.floatValue()); } - else if (op1 instanceof Long || op2 instanceof Long) { + if (op1 instanceof Long || op2 instanceof Long) { return BooleanTypedValue.forValue(op1.longValue() == op2.longValue()); } - else { - return BooleanTypedValue.forValue(op1.intValue() == op2.intValue()); - } + return BooleanTypedValue.forValue(op1.intValue() == op2.intValue()); } if (left != null && (left instanceof Comparable)) { return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGE.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGE.java index 6e1294e461ce..3e79ac74435c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGE.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGE.java @@ -15,9 +15,14 @@ */ 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.SpelEvaluationException; +import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.support.BooleanTypedValue; +import org.springframework.util.NumberUtils; /** * Implements greater-than-or-equal operator. @@ -39,18 +44,21 @@ public BooleanTypedValue getValueInternal(ExpressionState state) throws Evaluati if (left instanceof Number && right instanceof Number) { Number leftNumber = (Number) left; Number rightNumber = (Number) right; + if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) { + BigDecimal bdLeft = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class); + BigDecimal bdRight = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); + return BooleanTypedValue.forValue(bdLeft.compareTo(bdRight) >= 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); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGT.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGT.java index 3d751c976d48..7587a12b5c26 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGT.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGT.java @@ -16,9 +16,14 @@ 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.SpelEvaluationException; +import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.support.BooleanTypedValue; +import org.springframework.util.NumberUtils; /** * Implements greater-than operator. @@ -40,15 +45,18 @@ public BooleanTypedValue getValueInternal(ExpressionState state) throws Evaluati if (left instanceof Number && right instanceof Number) { Number leftNumber = (Number) left; Number rightNumber = (Number) right; + if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) { + BigDecimal bdLeft = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class); + BigDecimal bdRight = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); + return BooleanTypedValue.forValue(bdLeft.compareTo(bdRight) > 0); + } if (leftNumber instanceof Double || rightNumber instanceof Double) { return BooleanTypedValue.forValue(leftNumber.doubleValue() > rightNumber.doubleValue()); } - 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); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpInc.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpInc.java index 43937ca95362..4ca7fd020762 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpInc.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpInc.java @@ -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; @@ -56,7 +58,12 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep if (operandValue instanceof Number) { Number op1 = (Number) operandValue; - if (op1 instanceof Double) { + if (op1 instanceof BigDecimal) { + BigDecimal bd = (BigDecimal) op1; + newValue = new TypedValue(bd.add(BigDecimal.ONE), + operandTypedValue.getTypeDescriptor()); + } + else if (op1 instanceof Double) { newValue = new TypedValue(op1.doubleValue() + 1.0d, operandTypedValue.getTypeDescriptor()); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLE.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLE.java index 47424db2044e..60cef4b7a86c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLE.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLE.java @@ -16,9 +16,14 @@ 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.SpelEvaluationException; +import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.support.BooleanTypedValue; +import org.springframework.util.NumberUtils; /** * Implements the less-than-or-equal operator. @@ -41,18 +46,21 @@ public BooleanTypedValue getValueInternal(ExpressionState state) if (left instanceof Number && right instanceof Number) { Number leftNumber = (Number) left; Number rightNumber = (Number) right; + if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) { + BigDecimal bdLeft = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class); + BigDecimal bdRight = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); + return BooleanTypedValue.forValue(bdLeft.compareTo(bdRight) <= 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); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLT.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLT.java index dc8916dda8f2..df29f1831e27 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLT.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLT.java @@ -16,9 +16,14 @@ 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.SpelEvaluationException; +import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.support.BooleanTypedValue; +import org.springframework.util.NumberUtils; /** * Implements the less-than operator. @@ -42,18 +47,21 @@ public BooleanTypedValue getValueInternal(ExpressionState state) if (left instanceof Number && right instanceof Number) { Number leftNumber = (Number) left; Number rightNumber = (Number) right; + if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) { + BigDecimal bdLeft = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class); + BigDecimal bdRight = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); + return BooleanTypedValue.forValue(bdLeft.compareTo(bdRight) < 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); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMinus.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMinus.java index 143dfd9014ab..62d64303ff9c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMinus.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMinus.java @@ -16,23 +16,27 @@ 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: * - * 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 * @since 3.0 @@ -53,6 +57,11 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep 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()); } @@ -76,18 +85,20 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep if (left instanceof Number && right instanceof Number) { Number op1 = (Number) left; Number op2 = (Number) right; + if (op1 instanceof BigDecimal || op2 instanceof BigDecimal) { + BigDecimal bd1 = NumberUtils.convertNumberToTargetClass(op1, BigDecimal.class); + BigDecimal bd2 = NumberUtils.convertNumberToTargetClass(op2, BigDecimal.class); + return new TypedValue(bd1.subtract(bd2)); + } if (op1 instanceof Double || op2 instanceof Double) { return new TypedValue(op1.doubleValue() - op2.doubleValue()); } - if (op1 instanceof Float || op2 instanceof Float) { return new TypedValue(op1.floatValue() - op2.floatValue()); } - if (op1 instanceof Long || op2 instanceof Long) { return new TypedValue(op1.longValue() - op2.longValue()); } - return new TypedValue(op1.intValue() - op2.intValue()); } else if (left instanceof String && right instanceof Integer diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java index 48b511229d74..6ccb74a57b4c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java @@ -16,10 +16,13 @@ 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. @@ -41,18 +44,22 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep if (operandOne instanceof Number && operandTwo instanceof Number) { Number op1 = (Number) operandOne; Number op2 = (Number) operandTwo; + + if (op1 instanceof BigDecimal || op2 instanceof BigDecimal) { + BigDecimal bd1 = NumberUtils.convertNumberToTargetClass(op1, BigDecimal.class); + BigDecimal bd2 = NumberUtils.convertNumberToTargetClass(op2, BigDecimal.class); + return new TypedValue(bd1.remainder(bd2)); + } if (op1 instanceof Double || op2 instanceof Double) { return new TypedValue(op1.doubleValue() % op2.doubleValue()); } - else if (op1 instanceof Float || op2 instanceof Float) { + if (op1 instanceof Float || op2 instanceof Float) { return new TypedValue(op1.floatValue() % op2.floatValue()); } - else if (op1 instanceof Long || op2 instanceof Long) { + if (op1 instanceof Long || op2 instanceof Long) { return new TypedValue(op1.longValue() % op2.longValue()); } - else { - return new TypedValue(op1.intValue() % op2.intValue()); - } + return new TypedValue(op1.intValue() % op2.intValue()); } return state.operate(Operation.MODULUS, operandOne, operandTwo); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java index e795dff8b8cb..940b1f879631 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java @@ -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. * *

Conversions and promotions are handled as defined in * Section - * 5.6.2 of the Java Language Specification: + * 5.6.2 of the Java Language Specification, with the addiction of {@code BigDecimal} management: * *

If any of the operands is of a reference type, unboxing conversion (Section 5.1.8) * is performed. Then:
+ * If either operand is of type {@code BigDecimal}, the other is converted to {@code BigDecimal}.
* If either operand is of type double, the other is converted to double.
* Otherwise, if either operand is of type float, the other is converted to float.
* Otherwise, if either operand is of type long, the other is converted to long.
@@ -52,6 +56,7 @@ public OpMultiply(int pos, SpelNodeImpl... operands) { * for types not supported here. *

Supported operand types: *