Add SpEL support for float literals

This change ensures that SpEL expressions involving floats are
supported natively as opposed to the previous behavior which required
conversion to double, leading to potential downstream conversion
ambiguities.

Issue: SPR-9486
Backport-Commit: be8f23d756
This commit is contained in:
Satyapal Reddy
2012-08-06 11:55:36 -07:00
committed by Chris Beams
parent ca386c13fc
commit a94677e7b6
16 changed files with 600 additions and 80 deletions

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2002-2012 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.springframework.expression.TypedValue;
/**
* Expression language AST node that represents a float literal.
*
* @author Satyapal Reddy
* @since 3.2
*/
public class FloatLiteral extends Literal {
private final TypedValue value;
FloatLiteral(String payload, int pos, float value) {
super(payload, pos);
this.value = new TypedValue(value);
}
@Override
public TypedValue getLiteralValue() {
return this.value;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
@@ -17,11 +17,7 @@
package org.springframework.expression.spel.ast;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.SpelParseException;
import org.springframework.expression.spel.InternalParseException;
import org.springframework.expression.spel.*;
/**
* Common superclass for nodes representing literals (boolean, string, number, etc).
@@ -80,12 +76,12 @@ public abstract class Literal extends SpelNodeImpl {
}
}
// TODO should allow for 'f' for float, not just double
public static Literal getRealLiteral(String numberToken, int pos, boolean isFloat) {
try {
if (isFloat) {
float value = Float.parseFloat(numberToken);
return new RealLiteral(numberToken, pos, value);
return new FloatLiteral(numberToken, pos, value);
} else {
double value = Double.parseDouble(numberToken);
return new RealLiteral(numberToken, pos, value);

View File

@@ -43,8 +43,9 @@ public class OpDivide extends Operator {
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
return new TypedValue(op1.doubleValue() / op2.doubleValue());
}
else if (op1 instanceof Long || op2 instanceof Long) {
} else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() / op2.floatValue());
} else if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() / op2.longValue());
}
else {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
@@ -41,6 +41,8 @@ public class OpEQ extends Operator {
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return BooleanTypedValue.forValue(op1.doubleValue() == op2.doubleValue());
} else if (op1 instanceof Float || op2 instanceof Float) {
return BooleanTypedValue.forValue(op1.floatValue() == op2.floatValue());
} else if (op1 instanceof Long || op2 instanceof Long) {
return BooleanTypedValue.forValue(op1.longValue() == op2.longValue());
} else {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
@@ -40,6 +40,8 @@ public class OpGE extends Operator {
Number rightNumber = (Number) right;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() >= rightNumber.doubleValue());
} else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() >= rightNumber.floatValue());
} else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue( leftNumber.longValue() >= rightNumber.longValue());
} else {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
@@ -41,6 +41,8 @@ public class OpLE extends Operator {
Number rightNumber = (Number) right;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() <= rightNumber.doubleValue());
} else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() <= rightNumber.floatValue());
} else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() <= rightNumber.longValue());
} else {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
@@ -42,6 +42,8 @@ public class OpLT extends Operator {
Number rightNumber = (Number) right;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() < rightNumber.doubleValue());
} else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() < rightNumber.floatValue());
} else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() < rightNumber.longValue());
} else {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
@@ -52,6 +52,8 @@ public class OpMinus extends Operator {
Number n = (Number) operand;
if (operand instanceof Double) {
return new TypedValue(0 - n.doubleValue());
} else if (operand instanceof Float) {
return new TypedValue(0 - n.floatValue());
} else if (operand instanceof Long) {
return new TypedValue(0 - n.longValue());
} else {
@@ -67,6 +69,8 @@ public class OpMinus extends Operator {
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return new TypedValue(op1.doubleValue() - op2.doubleValue());
} else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() - op2.floatValue());
} else if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() - op2.longValue());
} else {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
@@ -42,6 +42,8 @@ public class OpModulus extends Operator {
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
return new TypedValue(op1.doubleValue() % op2.doubleValue());
} else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() % op2.floatValue());
} else if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() % op2.longValue());
} else {

View File

@@ -66,6 +66,9 @@ public class OpMultiply extends Operator {
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return new TypedValue(leftNumber.doubleValue() * rightNumber.doubleValue());
}
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return new TypedValue(leftNumber.floatValue() * rightNumber.floatValue());
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return new TypedValue(leftNumber.longValue() * rightNumber.longValue());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
@@ -41,6 +41,8 @@ public class OpNE extends Operator {
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return BooleanTypedValue.forValue(op1.doubleValue() != op2.doubleValue());
} else if (op1 instanceof Float || op2 instanceof Float) {
return BooleanTypedValue.forValue(op1.floatValue() != op2.floatValue());
} else if (op1 instanceof Long || op2 instanceof Long) {
return BooleanTypedValue.forValue(op1.longValue() != op2.longValue());
} else {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
@@ -16,10 +16,13 @@
package org.springframework.expression.spel.ast;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.util.Assert;
/**
* The plus operator will:
@@ -31,14 +34,16 @@ import org.springframework.expression.spel.ExpressionState;
* </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.
*
*
* @author Andy Clement
* @author Ivo Smid
* @since 3.0
*/
public class OpPlus extends Operator {
public OpPlus(int pos, SpelNodeImpl... operands) {
super("+", pos, operands);
Assert.notEmpty(operands);
}
@Override
@@ -48,24 +53,30 @@ 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) {
return new TypedValue(((Double) operandOne).doubleValue());
} else if (operandOne instanceof Long) {
return new TypedValue(((Long) operandOne).longValue());
if (operandOne instanceof Double || operandOne instanceof Long) {
return new TypedValue(operandOne);
} else if (operandOne instanceof Float) {
return new TypedValue(((Number) operandOne).floatValue());
} else {
return new TypedValue(((Integer) operandOne).intValue());
return new TypedValue(((Number) operandOne).intValue());
}
}
return state.operate(Operation.ADD, operandOne, null);
}
else {
Object operandOne = leftOp.getValueInternal(state).getValue();
Object operandTwo = rightOp.getValueInternal(state).getValue();
final TypedValue operandOneValue = leftOp.getValueInternal(state);
final Object operandOne = operandOneValue.getValue();
final TypedValue operandTwoValue = rightOp.getValueInternal(state);
final Object operandTwo = 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());
} else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() + op2.floatValue());
} else if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() + op2.longValue());
} else { // TODO what about overflow?
@@ -74,13 +85,14 @@ public class OpPlus extends Operator {
} else if (operandOne instanceof String && operandTwo instanceof String) {
return new TypedValue(new StringBuilder((String) operandOne).append((String) operandTwo).toString());
} else if (operandOne instanceof String) {
StringBuilder result = new StringBuilder((String)operandOne);
result.append((operandTwo==null?"null":operandTwo.toString()));
return new TypedValue(result.toString());
StringBuilder result = new StringBuilder((String) operandOne);
result.append((operandTwo == null ? "null" : convertTypedValueToString(operandTwoValue, state)));
return new TypedValue(result.toString());
} else if (operandTwo instanceof String) {
StringBuilder result = new StringBuilder((operandOne==null?"null":operandOne.toString()));
result.append((String)operandTwo);
return new TypedValue(result.toString());
StringBuilder result = new StringBuilder((operandOne == null ? "null" : convertTypedValueToString(
operandOneValue, state)));
result.append((String) operandTwo);
return new TypedValue(result.toString());
}
return state.operate(Operation.ADD, operandOne, operandTwo);
}
@@ -94,10 +106,32 @@ public class OpPlus extends Operator {
return super.toStringAST();
}
@Override
public SpelNodeImpl getRightOperand() {
if (children.length<2) {return null;}
if (children.length < 2) {
return null;
}
return children[1];
}
/**
* Convert operand value to string using registered converter or using
* {@code toString} method.
*
* @param value typed value to be converted
* @param state expression state
* @return {@code TypedValue} instance converted to {@code String}
*/
private static String convertTypedValueToString(TypedValue value, ExpressionState state) {
final TypeConverter typeConverter = state.getEvaluationContext().getTypeConverter();
final TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(String.class);
if (typeConverter.canConvert(value.getTypeDescriptor(), typeDescriptor)) {
final Object obj = typeConverter.convertValue(value.getValue(), value.getTypeDescriptor(), typeDescriptor);
return String.valueOf(obj);
} else {
return String.valueOf(value.getValue());
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
@@ -45,6 +45,8 @@ public class OperatorPower extends Operator {
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
return new TypedValue(Math.pow(op1.doubleValue(),op2.doubleValue()));
} else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(Math.pow(op1.floatValue(), op2.floatValue()));
} else if (op1 instanceof Long || op2 instanceof Long) {
double d= Math.pow(op1.longValue(), op2.longValue());
return new TypedValue((long)d);