|
|
|
@ -1,5 +1,5 @@ |
|
|
|
/* |
|
|
|
/* |
|
|
|
* Copyright 2002-2022 the original author or authors. |
|
|
|
* Copyright 2002-2023 the original author or authors. |
|
|
|
* |
|
|
|
* |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
@ -16,7 +16,6 @@ |
|
|
|
|
|
|
|
|
|
|
|
package org.springframework.expression.spel; |
|
|
|
package org.springframework.expression.spel; |
|
|
|
|
|
|
|
|
|
|
|
import java.util.HashMap; |
|
|
|
|
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
|
|
|
|
|
|
|
|
import org.junit.jupiter.api.Test; |
|
|
|
import org.junit.jupiter.api.Test; |
|
|
|
@ -34,27 +33,31 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
|
|
|
import static org.assertj.core.api.Assertions.assertThatIllegalStateException; |
|
|
|
import static org.assertj.core.api.Assertions.assertThatIllegalStateException; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Tests for the expression state object - some features are not yet exploited in the language (eg nested scopes) |
|
|
|
* Tests for {@link ExpressionState}. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* <p>Some features are not yet exploited in the language, such as nested scopes |
|
|
|
|
|
|
|
* or local variables scoped to the currently evaluated expression. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* <p>Local variables are in variable scopes which come and go during evaluation. |
|
|
|
|
|
|
|
* Normal/global variables are accessible through the {@link EvaluationContext}. |
|
|
|
* |
|
|
|
* |
|
|
|
* @author Andy Clement |
|
|
|
* @author Andy Clement |
|
|
|
* @author Juergen Hoeller |
|
|
|
* @author Juergen Hoeller |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public class ExpressionStateTests extends AbstractExpressionTests { |
|
|
|
class ExpressionStateTests extends AbstractExpressionTests { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ExpressionState state = new ExpressionState(TestScenarioCreator.getTestEvaluationContext()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testConstruction() { |
|
|
|
void construction() { |
|
|
|
EvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); |
|
|
|
EvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); |
|
|
|
ExpressionState state = new ExpressionState(context); |
|
|
|
ExpressionState state = new ExpressionState(context); |
|
|
|
assertThat(state.getEvaluationContext()).isEqualTo(context); |
|
|
|
assertThat(state.getEvaluationContext()).isEqualTo(context); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Local variables are in variable scopes which come and go during evaluation. Normal variables are
|
|
|
|
|
|
|
|
// accessible through the evaluation context
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testLocalVariables() { |
|
|
|
void localVariables() { |
|
|
|
ExpressionState state = getState(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Object value = state.lookupLocalVariable("foo"); |
|
|
|
Object value = state.lookupLocalVariable("foo"); |
|
|
|
assertThat(value).isNull(); |
|
|
|
assertThat(value).isNull(); |
|
|
|
|
|
|
|
|
|
|
|
@ -68,8 +71,7 @@ public class ExpressionStateTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testVariables() { |
|
|
|
void globalVariables() { |
|
|
|
ExpressionState state = getState(); |
|
|
|
|
|
|
|
TypedValue typedValue = state.lookupVariable("foo"); |
|
|
|
TypedValue typedValue = state.lookupVariable("foo"); |
|
|
|
assertThat(typedValue).isEqualTo(TypedValue.NULL); |
|
|
|
assertThat(typedValue).isEqualTo(TypedValue.NULL); |
|
|
|
|
|
|
|
|
|
|
|
@ -85,8 +87,7 @@ public class ExpressionStateTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testNoVariableInterference() { |
|
|
|
void noVariableInterference() { |
|
|
|
ExpressionState state = getState(); |
|
|
|
|
|
|
|
TypedValue typedValue = state.lookupVariable("foo"); |
|
|
|
TypedValue typedValue = state.lookupVariable("foo"); |
|
|
|
assertThat(typedValue).isEqualTo(TypedValue.NULL); |
|
|
|
assertThat(typedValue).isEqualTo(TypedValue.NULL); |
|
|
|
|
|
|
|
|
|
|
|
@ -99,8 +100,7 @@ public class ExpressionStateTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testLocalVariableNestedScopes() { |
|
|
|
void localVariableNestedScopes() { |
|
|
|
ExpressionState state = getState(); |
|
|
|
|
|
|
|
assertThat(state.lookupLocalVariable("foo")).isNull(); |
|
|
|
assertThat(state.lookupLocalVariable("foo")).isNull(); |
|
|
|
|
|
|
|
|
|
|
|
state.setLocalVariable("foo",12); |
|
|
|
state.setLocalVariable("foo",12); |
|
|
|
@ -120,30 +120,26 @@ public class ExpressionStateTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testRootContextObject() { |
|
|
|
void rootContextObject() { |
|
|
|
ExpressionState state = getState(); |
|
|
|
|
|
|
|
assertThat(state.getRootContextObject().getValue().getClass()).isEqualTo(Inventor.class); |
|
|
|
assertThat(state.getRootContextObject().getValue().getClass()).isEqualTo(Inventor.class); |
|
|
|
|
|
|
|
|
|
|
|
// although the root object is being set on the evaluation context, the value in the 'state' remains what it was when constructed
|
|
|
|
// Although the root object is being set on the evaluation context,
|
|
|
|
|
|
|
|
// the value in the 'state' remains what it was when constructed.
|
|
|
|
((StandardEvaluationContext) state.getEvaluationContext()).setRootObject(null); |
|
|
|
((StandardEvaluationContext) state.getEvaluationContext()).setRootObject(null); |
|
|
|
assertThat(state.getRootContextObject().getValue().getClass()).isEqualTo(Inventor.class); |
|
|
|
assertThat(state.getRootContextObject().getValue()).isInstanceOf(Inventor.class); |
|
|
|
// assertEquals(null, state.getRootContextObject().getValue());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
state = new ExpressionState(new StandardEvaluationContext()); |
|
|
|
state = new ExpressionState(new StandardEvaluationContext()); |
|
|
|
assertThat(state.getRootContextObject()).isEqualTo(TypedValue.NULL); |
|
|
|
assertThat(state.getRootContextObject()).isEqualTo(TypedValue.NULL); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
((StandardEvaluationContext) state.getEvaluationContext()).setRootObject(null); |
|
|
|
((StandardEvaluationContext) state.getEvaluationContext()).setRootObject(null); |
|
|
|
assertThat(state.getRootContextObject().getValue()).isNull(); |
|
|
|
assertThat(state.getRootContextObject().getValue()).isNull(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testActiveContextObject() { |
|
|
|
void activeContextObject() { |
|
|
|
ExpressionState state = getState(); |
|
|
|
|
|
|
|
assertThat(state.getActiveContextObject().getValue()).isEqualTo(state.getRootContextObject().getValue()); |
|
|
|
assertThat(state.getActiveContextObject().getValue()).isEqualTo(state.getRootContextObject().getValue()); |
|
|
|
|
|
|
|
|
|
|
|
assertThatIllegalStateException().isThrownBy( |
|
|
|
assertThatIllegalStateException().isThrownBy(state::popActiveContextObject); |
|
|
|
state::popActiveContextObject); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
state.pushActiveContextObject(new TypedValue(34)); |
|
|
|
state.pushActiveContextObject(new TypedValue(34)); |
|
|
|
assertThat(state.getActiveContextObject().getValue()).isEqualTo(34); |
|
|
|
assertThat(state.getActiveContextObject().getValue()).isEqualTo(34); |
|
|
|
@ -162,8 +158,7 @@ public class ExpressionStateTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testPopulatedNestedScopes() { |
|
|
|
void populatedNestedScopes() { |
|
|
|
ExpressionState state = getState(); |
|
|
|
|
|
|
|
assertThat(state.lookupLocalVariable("foo")).isNull(); |
|
|
|
assertThat(state.lookupLocalVariable("foo")).isNull(); |
|
|
|
|
|
|
|
|
|
|
|
state.enterScope("foo",34); |
|
|
|
state.enterScope("foo",34); |
|
|
|
@ -181,27 +176,22 @@ public class ExpressionStateTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testRootObjectConstructor() { |
|
|
|
void rootObjectConstructor() { |
|
|
|
EvaluationContext ctx = getContext(); |
|
|
|
EvaluationContext ctx = TestScenarioCreator.getTestEvaluationContext(); |
|
|
|
// TypedValue root = ctx.getRootObject();
|
|
|
|
// TypedValue root = ctx.getRootObject();
|
|
|
|
// supplied should override root on context
|
|
|
|
// supplied should override root on context
|
|
|
|
ExpressionState state = new ExpressionState(ctx,new TypedValue("i am a string")); |
|
|
|
ExpressionState state = new ExpressionState(ctx, new TypedValue("i am a string")); |
|
|
|
TypedValue stateRoot = state.getRootContextObject(); |
|
|
|
TypedValue stateRoot = state.getRootContextObject(); |
|
|
|
assertThat(stateRoot.getTypeDescriptor().getType()).isEqualTo(String.class); |
|
|
|
assertThat(stateRoot.getTypeDescriptor().getType()).isEqualTo(String.class); |
|
|
|
assertThat(stateRoot.getValue()).isEqualTo("i am a string"); |
|
|
|
assertThat(stateRoot.getValue()).isEqualTo("i am a string"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testPopulatedNestedScopesMap() { |
|
|
|
void populatedNestedScopesMap() { |
|
|
|
ExpressionState state = getState(); |
|
|
|
|
|
|
|
assertThat(state.lookupLocalVariable("foo")).isNull(); |
|
|
|
assertThat(state.lookupLocalVariable("foo")).isNull(); |
|
|
|
assertThat(state.lookupLocalVariable("goo")).isNull(); |
|
|
|
assertThat(state.lookupLocalVariable("goo")).isNull(); |
|
|
|
|
|
|
|
|
|
|
|
Map<String,Object> m = new HashMap<>(); |
|
|
|
state.enterScope(Map.of("foo", 34, "goo", "abc")); |
|
|
|
m.put("foo", 34); |
|
|
|
|
|
|
|
m.put("goo", "abc"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
state.enterScope(m); |
|
|
|
|
|
|
|
assertThat(state.lookupLocalVariable("foo")).isEqualTo(34); |
|
|
|
assertThat(state.lookupLocalVariable("foo")).isEqualTo(34); |
|
|
|
assertThat(state.lookupLocalVariable("goo")).isEqualTo("abc"); |
|
|
|
assertThat(state.lookupLocalVariable("goo")).isEqualTo("abc"); |
|
|
|
|
|
|
|
|
|
|
|
@ -217,61 +207,42 @@ public class ExpressionStateTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testOperators() { |
|
|
|
void operators() { |
|
|
|
ExpressionState state = getState(); |
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class) |
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> |
|
|
|
.isThrownBy(() -> state.operate(Operation.ADD,1,2)) |
|
|
|
state.operate(Operation.ADD,1,2)) |
|
|
|
|
|
|
|
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES)); |
|
|
|
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES)); |
|
|
|
|
|
|
|
|
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> |
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class) |
|
|
|
state.operate(Operation.ADD,null,null)) |
|
|
|
.isThrownBy(() -> state.operate(Operation.ADD,null,null)) |
|
|
|
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES)); |
|
|
|
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testComparator() { |
|
|
|
void comparator() { |
|
|
|
ExpressionState state = getState(); |
|
|
|
|
|
|
|
assertThat(state.getTypeComparator()).isEqualTo(state.getEvaluationContext().getTypeComparator()); |
|
|
|
assertThat(state.getTypeComparator()).isEqualTo(state.getEvaluationContext().getTypeComparator()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testTypeLocator() throws EvaluationException { |
|
|
|
void typeLocator() throws EvaluationException { |
|
|
|
ExpressionState state = getState(); |
|
|
|
|
|
|
|
assertThat(state.getEvaluationContext().getTypeLocator()).isNotNull(); |
|
|
|
assertThat(state.getEvaluationContext().getTypeLocator()).isNotNull(); |
|
|
|
assertThat(state.findType("java.lang.Integer")).isEqualTo(Integer.class); |
|
|
|
assertThat(state.findType("java.lang.Integer")).isEqualTo(Integer.class); |
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> |
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class) |
|
|
|
state.findType("someMadeUpName")) |
|
|
|
.isThrownBy(() -> state.findType("someMadeUpName")) |
|
|
|
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.TYPE_NOT_FOUND)); |
|
|
|
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.TYPE_NOT_FOUND)); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testTypeConversion() throws EvaluationException { |
|
|
|
void typeConversion() throws EvaluationException { |
|
|
|
ExpressionState state = getState(); |
|
|
|
|
|
|
|
String s = (String) state.convertValue(34, TypeDescriptor.valueOf(String.class)); |
|
|
|
String s = (String) state.convertValue(34, TypeDescriptor.valueOf(String.class)); |
|
|
|
assertThat(s).isEqualTo("34"); |
|
|
|
assertThat(s).isEqualTo("34"); |
|
|
|
|
|
|
|
|
|
|
|
s = (String)state.convertValue(new TypedValue(34), TypeDescriptor.valueOf(String.class)); |
|
|
|
s = (String) state.convertValue(new TypedValue(34), TypeDescriptor.valueOf(String.class)); |
|
|
|
assertThat(s).isEqualTo("34"); |
|
|
|
assertThat(s).isEqualTo("34"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testPropertyAccessors() { |
|
|
|
void propertyAccessors() { |
|
|
|
ExpressionState state = getState(); |
|
|
|
|
|
|
|
assertThat(state.getPropertyAccessors()).isEqualTo(state.getEvaluationContext().getPropertyAccessors()); |
|
|
|
assertThat(state.getPropertyAccessors()).isEqualTo(state.getEvaluationContext().getPropertyAccessors()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* @return a new ExpressionState |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private ExpressionState getState() { |
|
|
|
|
|
|
|
EvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); |
|
|
|
|
|
|
|
ExpressionState state = new ExpressionState(context); |
|
|
|
|
|
|
|
return state; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private EvaluationContext getContext() { |
|
|
|
|
|
|
|
return TestScenarioCreator.getTestEvaluationContext(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|