Merge pull request #85 from obecker/master
* pull85: Call ConversionService for null SpEL values Polish whitespace
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.
|
||||
@@ -60,7 +60,7 @@ public abstract class ExpressionUtils {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T convertTypedValue(EvaluationContext context, TypedValue typedValue, Class<T> targetType) {
|
||||
Object value = typedValue.getValue();
|
||||
if (targetType == null || ClassUtils.isAssignableValue(targetType, value)) {
|
||||
if ((targetType == null) || (value != null && ClassUtils.isAssignableValue(targetType, value))) {
|
||||
return (T) value;
|
||||
}
|
||||
if (context != null) {
|
||||
|
||||
@@ -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,7 +16,6 @@
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
@@ -29,6 +28,7 @@ import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Mark Fisher
|
||||
* @author Oliver Becker
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpAnd extends Operator {
|
||||
@@ -39,38 +39,27 @@ public class OpAnd extends Operator {
|
||||
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
boolean leftValue;
|
||||
boolean rightValue;
|
||||
|
||||
try {
|
||||
TypedValue typedValue = getLeftOperand().getValueInternal(state);
|
||||
this.assertTypedValueNotNull(typedValue);
|
||||
leftValue = (Boolean)state.convertValue(typedValue, TypeDescriptor.valueOf(Boolean.class));
|
||||
if (getBooleanValue(state, getLeftOperand()) == false) {
|
||||
// no need to evaluate right operand
|
||||
return BooleanTypedValue.FALSE;
|
||||
}
|
||||
catch (SpelEvaluationException ee) {
|
||||
ee.setPosition(getLeftOperand().getStartPosition());
|
||||
throw ee;
|
||||
}
|
||||
|
||||
if (leftValue == false) {
|
||||
return BooleanTypedValue.forValue(false); // no need to evaluate right operand
|
||||
}
|
||||
|
||||
try {
|
||||
TypedValue typedValue = getRightOperand().getValueInternal(state);
|
||||
this.assertTypedValueNotNull(typedValue);
|
||||
rightValue = (Boolean)state.convertValue(typedValue, TypeDescriptor.valueOf(Boolean.class));
|
||||
}
|
||||
catch (SpelEvaluationException ee) {
|
||||
ee.setPosition(getRightOperand().getStartPosition());
|
||||
throw ee;
|
||||
}
|
||||
|
||||
return /* leftValue && */BooleanTypedValue.forValue(rightValue);
|
||||
return BooleanTypedValue.forValue(getBooleanValue(state, getRightOperand()));
|
||||
}
|
||||
|
||||
private void assertTypedValueNotNull(TypedValue typedValue) {
|
||||
if (TypedValue.NULL.equals(typedValue)) {
|
||||
private boolean getBooleanValue(ExpressionState state, SpelNodeImpl operand) {
|
||||
try {
|
||||
Boolean value = operand.getValue(state, Boolean.class);
|
||||
assertValueNotNull(value);
|
||||
return value;
|
||||
}
|
||||
catch (SpelEvaluationException ee) {
|
||||
ee.setPosition(operand.getStartPosition());
|
||||
throw ee;
|
||||
}
|
||||
}
|
||||
|
||||
private void assertValueNotNull(Boolean value) {
|
||||
if (value == null) {
|
||||
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,9 +16,7 @@
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
@@ -29,6 +27,7 @@ import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Mark Fisher
|
||||
* @author Oliver Becker
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpOr extends Operator {
|
||||
@@ -39,37 +38,27 @@ public class OpOr extends Operator {
|
||||
|
||||
@Override
|
||||
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
boolean leftValue;
|
||||
boolean rightValue;
|
||||
try {
|
||||
TypedValue typedValue = getLeftOperand().getValueInternal(state);
|
||||
this.assertTypedValueNotNull(typedValue);
|
||||
leftValue = (Boolean)state.convertValue(typedValue, TypeDescriptor.valueOf(Boolean.class));
|
||||
if (getBooleanValue(state, getLeftOperand())) {
|
||||
// no need to evaluate right operand
|
||||
return BooleanTypedValue.TRUE;
|
||||
}
|
||||
catch (SpelEvaluationException see) {
|
||||
see.setPosition(getLeftOperand().getStartPosition());
|
||||
throw see;
|
||||
}
|
||||
|
||||
if (leftValue == true) {
|
||||
return BooleanTypedValue.TRUE; // no need to evaluate right operand
|
||||
}
|
||||
|
||||
try {
|
||||
TypedValue typedValue = getRightOperand().getValueInternal(state);
|
||||
this.assertTypedValueNotNull(typedValue);
|
||||
rightValue = (Boolean)state.convertValue(typedValue, TypeDescriptor.valueOf(Boolean.class));
|
||||
}
|
||||
catch (SpelEvaluationException see) {
|
||||
see.setPosition(getRightOperand().getStartPosition()); // TODO end positions here and in similar situations
|
||||
throw see;
|
||||
}
|
||||
|
||||
return BooleanTypedValue.forValue(leftValue || rightValue);
|
||||
return BooleanTypedValue.forValue(getBooleanValue(state, getRightOperand()));
|
||||
}
|
||||
|
||||
private void assertTypedValueNotNull(TypedValue typedValue) {
|
||||
if (TypedValue.NULL.equals(typedValue)) {
|
||||
private boolean getBooleanValue(ExpressionState state, SpelNodeImpl operand) {
|
||||
try {
|
||||
Boolean value = operand.getValue(state, Boolean.class);
|
||||
assertValueNotNull(value);
|
||||
return value;
|
||||
}
|
||||
catch (SpelEvaluationException ee) {
|
||||
ee.setPosition(operand.getStartPosition());
|
||||
throw ee;
|
||||
}
|
||||
}
|
||||
|
||||
private void assertValueNotNull(Boolean value) {
|
||||
if (value == null) {
|
||||
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,9 +16,7 @@
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
@@ -29,6 +27,7 @@ import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Mark Fisher
|
||||
* @author Oliver Becker
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do not extend BinaryOperator
|
||||
@@ -36,15 +35,14 @@ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do
|
||||
public OperatorNot(int pos, SpelNodeImpl operand) {
|
||||
super(pos, operand);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
try {
|
||||
TypedValue typedValue = children[0].getValueInternal(state);
|
||||
if (TypedValue.NULL.equals(typedValue)) {
|
||||
Boolean value = children[0].getValue(state, Boolean.class);
|
||||
if (value == null) {
|
||||
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
|
||||
}
|
||||
boolean value = (Boolean) state.convertValue(typedValue, TypeDescriptor.valueOf(Boolean.class));
|
||||
return BooleanTypedValue.forValue(!value);
|
||||
}
|
||||
catch (SpelEvaluationException see) {
|
||||
|
||||
@@ -35,11 +35,11 @@ import org.springframework.util.Assert;
|
||||
public abstract class SpelNodeImpl implements SpelNode {
|
||||
|
||||
private static SpelNodeImpl[] NO_CHILDREN = new SpelNodeImpl[0];
|
||||
|
||||
|
||||
protected int pos; // start = top 16bits, end = bottom 16bits
|
||||
protected SpelNodeImpl[] children = SpelNodeImpl.NO_CHILDREN;
|
||||
private SpelNodeImpl parent;
|
||||
|
||||
|
||||
public SpelNodeImpl(int pos, SpelNodeImpl... operands) {
|
||||
this.pos = pos;
|
||||
// pos combines start and end so can never be zero because tokens cannot be zero length
|
||||
@@ -51,7 +51,7 @@ public abstract class SpelNodeImpl implements SpelNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected SpelNodeImpl getPreviousChild() {
|
||||
SpelNodeImpl result = null;
|
||||
if (parent != null) {
|
||||
@@ -62,7 +62,7 @@ public abstract class SpelNodeImpl implements SpelNode {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return true if the next child is one of the specified classes
|
||||
*/
|
||||
@@ -96,7 +96,7 @@ public abstract class SpelNodeImpl implements SpelNode {
|
||||
return getValue(new ExpressionState(new StandardEvaluationContext()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final TypedValue getTypedValue(ExpressionState expressionState) throws EvaluationException {
|
||||
if (expressionState != null) {
|
||||
return getValueInternal(expressionState);
|
||||
@@ -118,7 +118,7 @@ public abstract class SpelNodeImpl implements SpelNode {
|
||||
public SpelNode getChild(int index) {
|
||||
return children[index];
|
||||
}
|
||||
|
||||
|
||||
public int getChildCount() {
|
||||
return children.length;
|
||||
}
|
||||
@@ -130,18 +130,8 @@ public abstract class SpelNodeImpl implements SpelNode {
|
||||
return (obj instanceof Class ? ((Class<?>) obj) : obj.getClass());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected final <T> T getValue(ExpressionState state, Class<T> desiredReturnType) throws EvaluationException {
|
||||
Object result = getValueInternal(state).getValue();
|
||||
if (result != null && desiredReturnType != null) {
|
||||
Class<?> resultType = result.getClass();
|
||||
if (desiredReturnType.isAssignableFrom(resultType)) {
|
||||
return (T) result;
|
||||
}
|
||||
// Attempt conversion to the requested type, may throw an exception
|
||||
return ExpressionUtils.convert(state.getEvaluationContext(), result, desiredReturnType);
|
||||
}
|
||||
return (T) result;
|
||||
return ExpressionUtils.convertTypedValue(state.getEvaluationContext(), getValueInternal(state), desiredReturnType);
|
||||
}
|
||||
|
||||
public abstract TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException;
|
||||
|
||||
@@ -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,15 @@
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.expression.spel.support.StandardTypeConverter;
|
||||
|
||||
/**
|
||||
* Tests the evaluation of real boolean expressions, these use AND, OR, NOT, TRUE, FALSE
|
||||
*
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Oliver Becker
|
||||
*/
|
||||
public class BooleanExpressionTests extends ExpressionTestCase {
|
||||
|
||||
@@ -83,4 +87,27 @@ public class BooleanExpressionTests extends ExpressionTestCase {
|
||||
evaluateAndCheckError("!35.2", SpelMessage.TYPE_CONVERSION_ERROR, 1);
|
||||
evaluateAndCheckError("! 'foob'", SpelMessage.TYPE_CONVERSION_ERROR, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertAndHandleNull() { // SPR-9445
|
||||
// without null conversion
|
||||
evaluateAndCheckError("null or true", SpelMessage.TYPE_CONVERSION_ERROR, 0, "null", "boolean");
|
||||
evaluateAndCheckError("null and true", SpelMessage.TYPE_CONVERSION_ERROR, 0, "null", "boolean");
|
||||
evaluateAndCheckError("!null", SpelMessage.TYPE_CONVERSION_ERROR, 1, "null", "boolean");
|
||||
evaluateAndCheckError("null ? 'foo' : 'bar'", SpelMessage.TYPE_CONVERSION_ERROR, 0, "null", "boolean");
|
||||
|
||||
// with null conversion (null -> false)
|
||||
GenericConversionService conversionService = new GenericConversionService() {
|
||||
@Override
|
||||
protected Object convertNullSource(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return targetType.getType() == Boolean.class ? false : null;
|
||||
}
|
||||
};
|
||||
eContext.setTypeConverter(new StandardTypeConverter(conversionService));
|
||||
|
||||
evaluate("null or true", Boolean.TRUE, Boolean.class, false);
|
||||
evaluate("null and true", Boolean.FALSE, Boolean.class, false);
|
||||
evaluate("!null", Boolean.TRUE, Boolean.class, false);
|
||||
evaluate("null ? 'foo' : 'bar'", "bar", String.class, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,8 +40,8 @@ public abstract class ExpressionTestCase {
|
||||
protected final static boolean SHOULD_BE_WRITABLE = true;
|
||||
protected final static boolean SHOULD_NOT_BE_WRITABLE = false;
|
||||
|
||||
protected final static ExpressionParser parser = new SpelExpressionParser();
|
||||
protected final static StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
|
||||
protected final ExpressionParser parser = new SpelExpressionParser();
|
||||
protected final StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
|
||||
|
||||
/**
|
||||
* Evaluate an expression and check that the actual result matches the expectedValue and the class of the result
|
||||
|
||||
Reference in New Issue
Block a user