Skip to content

Commit 4181397

Browse files
committed
Merge pull request #85 from obecker/master
* pull85: Call ConversionService for null SpEL values Polish whitespace
2 parents c5f3915 + 759c9b3 commit 4181397

File tree

7 files changed

+76
-83
lines changed

7 files changed

+76
-83
lines changed

spring-expression/src/main/java/org/springframework/expression/common/ExpressionUtils.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2010 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -60,7 +60,7 @@ public static <T> T convert(EvaluationContext context, Object value, Class<T> ta
6060
@SuppressWarnings("unchecked")
6161
public static <T> T convertTypedValue(EvaluationContext context, TypedValue typedValue, Class<T> targetType) {
6262
Object value = typedValue.getValue();
63-
if (targetType == null || ClassUtils.isAssignableValue(targetType, value)) {
63+
if ((targetType == null) || (value != null && ClassUtils.isAssignableValue(targetType, value))) {
6464
return (T) value;
6565
}
6666
if (context != null) {

spring-expression/src/main/java/org/springframework/expression/spel/ast/OpAnd.java

+14-25
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2009 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
1616

1717
package org.springframework.expression.spel.ast;
1818

19-
import org.springframework.core.convert.TypeDescriptor;
2019
import org.springframework.expression.EvaluationException;
2120
import org.springframework.expression.TypedValue;
2221
import org.springframework.expression.spel.ExpressionState;
@@ -29,6 +28,7 @@
2928
*
3029
* @author Andy Clement
3130
* @author Mark Fisher
31+
* @author Oliver Becker
3232
* @since 3.0
3333
*/
3434
public class OpAnd extends Operator {
@@ -39,38 +39,27 @@ public OpAnd(int pos, SpelNodeImpl... operands) {
3939

4040
@Override
4141
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
42-
boolean leftValue;
43-
boolean rightValue;
44-
45-
try {
46-
TypedValue typedValue = getLeftOperand().getValueInternal(state);
47-
this.assertTypedValueNotNull(typedValue);
48-
leftValue = (Boolean)state.convertValue(typedValue, TypeDescriptor.valueOf(Boolean.class));
49-
}
50-
catch (SpelEvaluationException ee) {
51-
ee.setPosition(getLeftOperand().getStartPosition());
52-
throw ee;
53-
}
54-
55-
if (leftValue == false) {
56-
return BooleanTypedValue.forValue(false); // no need to evaluate right operand
42+
if (getBooleanValue(state, getLeftOperand()) == false) {
43+
// no need to evaluate right operand
44+
return BooleanTypedValue.FALSE;
5745
}
46+
return BooleanTypedValue.forValue(getBooleanValue(state, getRightOperand()));
47+
}
5848

49+
private boolean getBooleanValue(ExpressionState state, SpelNodeImpl operand) {
5950
try {
60-
TypedValue typedValue = getRightOperand().getValueInternal(state);
61-
this.assertTypedValueNotNull(typedValue);
62-
rightValue = (Boolean)state.convertValue(typedValue, TypeDescriptor.valueOf(Boolean.class));
51+
Boolean value = operand.getValue(state, Boolean.class);
52+
assertValueNotNull(value);
53+
return value;
6354
}
6455
catch (SpelEvaluationException ee) {
65-
ee.setPosition(getRightOperand().getStartPosition());
56+
ee.setPosition(operand.getStartPosition());
6657
throw ee;
6758
}
68-
69-
return /* leftValue && */BooleanTypedValue.forValue(rightValue);
7059
}
7160

72-
private void assertTypedValueNotNull(TypedValue typedValue) {
73-
if (TypedValue.NULL.equals(typedValue)) {
61+
private void assertValueNotNull(Boolean value) {
62+
if (value == null) {
7463
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
7564
}
7665
}

spring-expression/src/main/java/org/springframework/expression/spel/ast/OpOr.java

+16-27
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2009 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,9 +16,7 @@
1616

1717
package org.springframework.expression.spel.ast;
1818

19-
import org.springframework.core.convert.TypeDescriptor;
2019
import org.springframework.expression.EvaluationException;
21-
import org.springframework.expression.TypedValue;
2220
import org.springframework.expression.spel.ExpressionState;
2321
import org.springframework.expression.spel.SpelEvaluationException;
2422
import org.springframework.expression.spel.SpelMessage;
@@ -29,6 +27,7 @@
2927
*
3028
* @author Andy Clement
3129
* @author Mark Fisher
30+
* @author Oliver Becker
3231
* @since 3.0
3332
*/
3433
public class OpOr extends Operator {
@@ -39,37 +38,27 @@ public OpOr(int pos, SpelNodeImpl... operands) {
3938

4039
@Override
4140
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
42-
boolean leftValue;
43-
boolean rightValue;
44-
try {
45-
TypedValue typedValue = getLeftOperand().getValueInternal(state);
46-
this.assertTypedValueNotNull(typedValue);
47-
leftValue = (Boolean)state.convertValue(typedValue, TypeDescriptor.valueOf(Boolean.class));
48-
}
49-
catch (SpelEvaluationException see) {
50-
see.setPosition(getLeftOperand().getStartPosition());
51-
throw see;
52-
}
53-
54-
if (leftValue == true) {
55-
return BooleanTypedValue.TRUE; // no need to evaluate right operand
41+
if (getBooleanValue(state, getLeftOperand())) {
42+
// no need to evaluate right operand
43+
return BooleanTypedValue.TRUE;
5644
}
45+
return BooleanTypedValue.forValue(getBooleanValue(state, getRightOperand()));
46+
}
5747

48+
private boolean getBooleanValue(ExpressionState state, SpelNodeImpl operand) {
5849
try {
59-
TypedValue typedValue = getRightOperand().getValueInternal(state);
60-
this.assertTypedValueNotNull(typedValue);
61-
rightValue = (Boolean)state.convertValue(typedValue, TypeDescriptor.valueOf(Boolean.class));
50+
Boolean value = operand.getValue(state, Boolean.class);
51+
assertValueNotNull(value);
52+
return value;
6253
}
63-
catch (SpelEvaluationException see) {
64-
see.setPosition(getRightOperand().getStartPosition()); // TODO end positions here and in similar situations
65-
throw see;
54+
catch (SpelEvaluationException ee) {
55+
ee.setPosition(operand.getStartPosition());
56+
throw ee;
6657
}
67-
68-
return BooleanTypedValue.forValue(leftValue || rightValue);
6958
}
7059

71-
private void assertTypedValueNotNull(TypedValue typedValue) {
72-
if (TypedValue.NULL.equals(typedValue)) {
60+
private void assertValueNotNull(Boolean value) {
61+
if (value == null) {
7362
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
7463
}
7564
}

spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2009 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,9 +16,7 @@
1616

1717
package org.springframework.expression.spel.ast;
1818

19-
import org.springframework.core.convert.TypeDescriptor;
2019
import org.springframework.expression.EvaluationException;
21-
import org.springframework.expression.TypedValue;
2220
import org.springframework.expression.spel.ExpressionState;
2321
import org.springframework.expression.spel.SpelEvaluationException;
2422
import org.springframework.expression.spel.SpelMessage;
@@ -29,22 +27,22 @@
2927
*
3028
* @author Andy Clement
3129
* @author Mark Fisher
30+
* @author Oliver Becker
3231
* @since 3.0
3332
*/
3433
public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do not extend BinaryOperator
3534

3635
public OperatorNot(int pos, SpelNodeImpl operand) {
3736
super(pos, operand);
3837
}
39-
38+
4039
@Override
4140
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
4241
try {
43-
TypedValue typedValue = children[0].getValueInternal(state);
44-
if (TypedValue.NULL.equals(typedValue)) {
42+
Boolean value = children[0].getValue(state, Boolean.class);
43+
if (value == null) {
4544
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
4645
}
47-
boolean value = (Boolean) state.convertValue(typedValue, TypeDescriptor.valueOf(Boolean.class));
4846
return BooleanTypedValue.forValue(!value);
4947
}
5048
catch (SpelEvaluationException see) {

spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java

+7-17
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@
3535
public abstract class SpelNodeImpl implements SpelNode {
3636

3737
private static SpelNodeImpl[] NO_CHILDREN = new SpelNodeImpl[0];
38-
38+
3939
protected int pos; // start = top 16bits, end = bottom 16bits
4040
protected SpelNodeImpl[] children = SpelNodeImpl.NO_CHILDREN;
4141
private SpelNodeImpl parent;
42-
42+
4343
public SpelNodeImpl(int pos, SpelNodeImpl... operands) {
4444
this.pos = pos;
4545
// pos combines start and end so can never be zero because tokens cannot be zero length
@@ -51,7 +51,7 @@ public SpelNodeImpl(int pos, SpelNodeImpl... operands) {
5151
}
5252
}
5353
}
54-
54+
5555
protected SpelNodeImpl getPreviousChild() {
5656
SpelNodeImpl result = null;
5757
if (parent != null) {
@@ -62,7 +62,7 @@ protected SpelNodeImpl getPreviousChild() {
6262
}
6363
return result;
6464
}
65-
65+
6666
/**
6767
* @return true if the next child is one of the specified classes
6868
*/
@@ -96,7 +96,7 @@ public final Object getValue(ExpressionState expressionState) throws EvaluationE
9696
return getValue(new ExpressionState(new StandardEvaluationContext()));
9797
}
9898
}
99-
99+
100100
public final TypedValue getTypedValue(ExpressionState expressionState) throws EvaluationException {
101101
if (expressionState != null) {
102102
return getValueInternal(expressionState);
@@ -118,7 +118,7 @@ public void setValue(ExpressionState expressionState, Object newValue) throws Ev
118118
public SpelNode getChild(int index) {
119119
return children[index];
120120
}
121-
121+
122122
public int getChildCount() {
123123
return children.length;
124124
}
@@ -130,18 +130,8 @@ public Class<?> getObjectClass(Object obj) {
130130
return (obj instanceof Class ? ((Class<?>) obj) : obj.getClass());
131131
}
132132

133-
@SuppressWarnings("unchecked")
134133
protected final <T> T getValue(ExpressionState state, Class<T> desiredReturnType) throws EvaluationException {
135-
Object result = getValueInternal(state).getValue();
136-
if (result != null && desiredReturnType != null) {
137-
Class<?> resultType = result.getClass();
138-
if (desiredReturnType.isAssignableFrom(resultType)) {
139-
return (T) result;
140-
}
141-
// Attempt conversion to the requested type, may throw an exception
142-
return ExpressionUtils.convert(state.getEvaluationContext(), result, desiredReturnType);
143-
}
144-
return (T) result;
134+
return ExpressionUtils.convertTypedValue(state.getEvaluationContext(), getValueInternal(state), desiredReturnType);
145135
}
146136

147137
public abstract TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException;

spring-expression/src/test/java/org/springframework/expression/spel/BooleanExpressionTests.java

+29-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2009 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,11 +17,15 @@
1717
package org.springframework.expression.spel;
1818

1919
import org.junit.Test;
20+
import org.springframework.core.convert.TypeDescriptor;
21+
import org.springframework.core.convert.support.GenericConversionService;
22+
import org.springframework.expression.spel.support.StandardTypeConverter;
2023

2124
/**
2225
* Tests the evaluation of real boolean expressions, these use AND, OR, NOT, TRUE, FALSE
23-
*
26+
*
2427
* @author Andy Clement
28+
* @author Oliver Becker
2529
*/
2630
public class BooleanExpressionTests extends ExpressionTestCase {
2731

@@ -83,4 +87,27 @@ public void testBooleanErrors01() {
8387
evaluateAndCheckError("!35.2", SpelMessage.TYPE_CONVERSION_ERROR, 1);
8488
evaluateAndCheckError("! 'foob'", SpelMessage.TYPE_CONVERSION_ERROR, 2);
8589
}
90+
91+
@Test
92+
public void testConvertAndHandleNull() { // SPR-9445
93+
// without null conversion
94+
evaluateAndCheckError("null or true", SpelMessage.TYPE_CONVERSION_ERROR, 0, "null", "boolean");
95+
evaluateAndCheckError("null and true", SpelMessage.TYPE_CONVERSION_ERROR, 0, "null", "boolean");
96+
evaluateAndCheckError("!null", SpelMessage.TYPE_CONVERSION_ERROR, 1, "null", "boolean");
97+
evaluateAndCheckError("null ? 'foo' : 'bar'", SpelMessage.TYPE_CONVERSION_ERROR, 0, "null", "boolean");
98+
99+
// with null conversion (null -> false)
100+
GenericConversionService conversionService = new GenericConversionService() {
101+
@Override
102+
protected Object convertNullSource(TypeDescriptor sourceType, TypeDescriptor targetType) {
103+
return targetType.getType() == Boolean.class ? false : null;
104+
}
105+
};
106+
eContext.setTypeConverter(new StandardTypeConverter(conversionService));
107+
108+
evaluate("null or true", Boolean.TRUE, Boolean.class, false);
109+
evaluate("null and true", Boolean.FALSE, Boolean.class, false);
110+
evaluate("!null", Boolean.TRUE, Boolean.class, false);
111+
evaluate("null ? 'foo' : 'bar'", "bar", String.class, false);
112+
}
86113
}

spring-expression/src/test/java/org/springframework/expression/spel/ExpressionTestCase.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2009 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -40,8 +40,8 @@ public abstract class ExpressionTestCase {
4040
protected final static boolean SHOULD_BE_WRITABLE = true;
4141
protected final static boolean SHOULD_NOT_BE_WRITABLE = false;
4242

43-
protected final static ExpressionParser parser = new SpelExpressionParser();
44-
protected final static StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
43+
protected final ExpressionParser parser = new SpelExpressionParser();
44+
protected final StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
4545

4646
/**
4747
* Evaluate an expression and check that the actual result matches the expectedValue and the class of the result

0 commit comments

Comments
 (0)