Browse Source

Disable variable assignment in SimpleEvaluationContext

This commit introduces infrastructure to differentiate between
programmatic setting of a variable in an EvaluationContext versus the
assignment of a variable within a SpEL expression using the assignment
operator (=). In addition, this commit disables variable assignment
within expressions when using the SimpleEvaluationContext.

Closes gh-30328
pull/30400/head
Sam Brannen 3 years ago committed by Brian Clozel
parent
commit
e246b47f4d
  1. 45
      spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java
  2. 33
      spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java
  3. 6
      spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java
  4. 7
      spring-expression/src/main/java/org/springframework/expression/spel/ast/Assign.java
  5. 13
      spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java
  6. 14
      spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java
  7. 12
      spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java
  8. 26
      spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java
  9. 15
      spring-expression/src/main/java/org/springframework/expression/spel/ast/VariableReference.java
  10. 15
      spring-expression/src/main/java/org/springframework/expression/spel/support/SimpleEvaluationContext.java
  11. 22
      spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java

45
spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 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.
@ -17,6 +17,7 @@
package org.springframework.expression; package org.springframework.expression;
import java.util.List; import java.util.List;
import java.util.function.Supplier;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -24,12 +25,21 @@ import org.springframework.lang.Nullable;
* Expressions are executed in an evaluation context. It is in this context that * Expressions are executed in an evaluation context. It is in this context that
* references are resolved when encountered during expression evaluation. * references are resolved when encountered during expression evaluation.
* *
* <p>There is a default implementation of this EvaluationContext interface: * <p>There are two default implementations of this interface.
* {@link org.springframework.expression.spel.support.StandardEvaluationContext} * <ul>
* which can be extended, rather than having to implement everything manually. * <li>{@link org.springframework.expression.spel.support.SimpleEvaluationContext
* SimpleEvaluationContext}: a simpler builder-style {@code EvaluationContext}
* variant for data-binding purposes, which allows for opting into several SpEL
* features as needed.</li>
* <li>{@link org.springframework.expression.spel.support.StandardEvaluationContext
* StandardEvaluationContext}: a powerful and highly configurable {@code EvaluationContext}
* implementation, which can be extended, rather than having to implement everything
* manually.</li>
* </ul>
* *
* @author Andy Clement * @author Andy Clement
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0 * @since 3.0
*/ */
public interface EvaluationContext { public interface EvaluationContext {
@ -85,7 +95,30 @@ public interface EvaluationContext {
OperatorOverloader getOperatorOverloader(); OperatorOverloader getOperatorOverloader();
/** /**
* Set a named variable within this evaluation context to a specified value. * Assign the value created by the specified {@link Supplier} to a named variable
* within this evaluation context.
* <p>In contrast to {@link #setVariable(String, Object)}, this method should only
* be invoked to support the assignment operator ({@code =}) within an expression.
* <p>By default, this method delegates to {@code setVariable(String, Object)},
* providing the value created by the {@code valueSupplier}. Concrete implementations
* may override this <em>default</em> method to provide different semantics.
* @param name the name of the variable to assign
* @param valueSupplier the supplier of the value to be assigned to the variable
* @return a {@link TypedValue} wrapping the assigned value
* @since 5.2.24
*/
default TypedValue assignVariable(String name, Supplier<TypedValue> valueSupplier) {
TypedValue typedValue = valueSupplier.get();
setVariable(name, typedValue.getValue());
return typedValue;
}
/**
* Set a named variable in this evaluation context to a specified value.
* <p>In contrast to {@link #assignVariable(String, Supplier)}, this method
* should only be invoked programmatically when interacting directly with the
* {@code EvaluationContext} &mdash; for example, to provide initial
* configuration for the context.
* @param name the name of the variable to set * @param name the name of the variable to set
* @param value the value to be placed in the variable * @param value the value to be placed in the variable
*/ */
@ -93,7 +126,7 @@ public interface EvaluationContext {
/** /**
* Look up a named variable within this evaluation context. * Look up a named variable within this evaluation context.
* @param name variable to lookup * @param name the name of the variable to look up
* @return the value of the variable, or {@code null} if not found * @return the value of the variable, or {@code null} if not found
*/ */
@Nullable @Nullable

33
spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 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.
@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.function.Supplier;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
@ -38,18 +39,19 @@ import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
/** /**
* An ExpressionState is for maintaining per-expression-evaluation state, any changes to * ExpressionState is for maintaining per-expression-evaluation state: any changes to
* it are not seen by other expressions but it gives a place to hold local variables and * it are not seen by other expressions, but it gives a place to hold local variables and
* for component expressions in a compound expression to communicate state. This is in * for component expressions in a compound expression to communicate state. This is in
* contrast to the EvaluationContext, which is shared amongst expression evaluations, and * contrast to the EvaluationContext, which is shared amongst expression evaluations, and
* any changes to it will be seen by other expressions or any code that chooses to ask * any changes to it will be seen by other expressions or any code that chooses to ask
* questions of the context. * questions of the context.
* *
* <p>It also acts as a place for to define common utility routines that the various AST * <p>It also acts as a place to define common utility routines that the various AST
* nodes might need. * nodes might need.
* *
* @author Andy Clement * @author Andy Clement
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0 * @since 3.0
*/ */
public class ExpressionState { public class ExpressionState {
@ -138,6 +140,29 @@ public class ExpressionState {
return this.scopeRootObjects.element(); return this.scopeRootObjects.element();
} }
/**
* Assign the value created by the specified {@link Supplier} to a named variable
* within the evaluation context.
* <p>In contrast to {@link #setVariable(String, Object)}, this method should
* only be invoked to support assignment within an expression.
* @param name the name of the variable to assign
* @param valueSupplier the supplier of the value to be assigned to the variable
* @return a {@link TypedValue} wrapping the assigned value
* @since 5.2.24
* @see EvaluationContext#assignVariable(String, Supplier)
*/
public TypedValue assignVariable(String name, Supplier<TypedValue> valueSupplier) {
return this.relatedContext.assignVariable(name, valueSupplier);
}
/**
* Set a named variable in the evaluation context to a specified value.
* <p>In contrast to {@link #assignVariable(String, Supplier)}, this method
* should only be invoked programmatically.
* @param name the name of the variable to set
* @param value the value to be placed in the variable
* @see EvaluationContext#setVariable(String, Object)
*/
public void setVariable(String name, @Nullable Object value) { public void setVariable(String name, @Nullable Object value) {
this.relatedContext.setVariable(name, value); this.relatedContext.setVariable(name, value);
} }

6
spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java

@ -276,7 +276,11 @@ public enum SpelMessage {
/** @since 5.2.24 */ /** @since 5.2.24 */
MAX_EXPRESSION_LENGTH_EXCEEDED(Kind.ERROR, 1079, MAX_EXPRESSION_LENGTH_EXCEEDED(Kind.ERROR, 1079,
"SpEL expression is too long, exceeding the threshold of ''{0}'' characters"); "SpEL expression is too long, exceeding the threshold of ''{0}'' characters"),
/** @since 5.2.24 */
VARIABLE_ASSIGNMENT_NOT_SUPPORTED(Kind.ERROR, 1080,
"Assignment to variable ''{0}'' is not supported");
private final Kind kind; private final Kind kind;

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 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.
@ -27,6 +27,7 @@ import org.springframework.expression.spel.ExpressionState;
* <p>Example: 'someNumberProperty=42' * <p>Example: 'someNumberProperty=42'
* *
* @author Andy Clement * @author Andy Clement
* @author Sam Brannen
* @since 3.0 * @since 3.0
*/ */
public class Assign extends SpelNodeImpl { public class Assign extends SpelNodeImpl {
@ -38,9 +39,7 @@ public class Assign extends SpelNodeImpl {
@Override @Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
TypedValue newValue = this.children[1].getValueInternal(state); return this.children[0].setValueInternal(state, () -> this.children[1].getValueInternal(state));
getChild(0).setValue(state, newValue.getValue());
return newValue;
} }
@Override @Override

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 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.
@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast; package org.springframework.expression.spel.ast;
import java.util.StringJoiner; import java.util.StringJoiner;
import java.util.function.Supplier;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
@ -24,13 +25,13 @@ import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow; import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.lang.Nullable;
/** /**
* Represents a DOT separated expression sequence, such as * Represents a DOT separated expression sequence, such as
* {@code 'property1.property2.methodOne()'}. * {@code 'property1.property2.methodOne()'}.
* *
* @author Andy Clement * @author Andy Clement
* @author Sam Brannen
* @since 3.0 * @since 3.0
*/ */
public class CompoundExpression extends SpelNodeImpl { public class CompoundExpression extends SpelNodeImpl {
@ -95,8 +96,12 @@ public class CompoundExpression extends SpelNodeImpl {
} }
@Override @Override
public void setValue(ExpressionState state, @Nullable Object value) throws EvaluationException { public TypedValue setValueInternal(ExpressionState state, Supplier<TypedValue> valueSupplier)
getValueRef(state).setValue(value); throws EvaluationException {
TypedValue typedValue = valueSupplier.get();
getValueRef(state).setValue(typedValue.getValue());
return typedValue;
} }
@Override @Override

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 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.
@ -25,6 +25,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.StringJoiner; import java.util.StringJoiner;
import java.util.function.Supplier;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
@ -45,11 +46,12 @@ import org.springframework.util.ReflectionUtils;
/** /**
* An Indexer can index into some proceeding structure to access a particular piece of it. * An Indexer can index into some proceeding structure to access a particular piece of it.
* Supported structures are: strings / collections (lists/sets) / arrays. * <p>Supported structures are: strings / collections (lists/sets) / arrays.
* *
* @author Andy Clement * @author Andy Clement
* @author Phillip Webb * @author Phillip Webb
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Sam Brannen
* @since 3.0 * @since 3.0
*/ */
// TODO support multidimensional arrays // TODO support multidimensional arrays
@ -102,8 +104,12 @@ public class Indexer extends SpelNodeImpl {
} }
@Override @Override
public void setValue(ExpressionState state, @Nullable Object newValue) throws EvaluationException { public TypedValue setValueInternal(ExpressionState state, Supplier<TypedValue> valueSupplier)
getValueRef(state).setValue(newValue); throws EvaluationException {
TypedValue typedValue = valueSupplier.get();
getValueRef(state).setValue(typedValue.getValue());
return typedValue;
} }
@Override @Override

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 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.
@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier;
import org.springframework.asm.Label; import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
@ -46,6 +47,7 @@ import org.springframework.util.ReflectionUtils;
* @author Andy Clement * @author Andy Clement
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Clark Duplichien * @author Clark Duplichien
* @author Sam Brannen
* @since 3.0 * @since 3.0
*/ */
public class PropertyOrFieldReference extends SpelNodeImpl { public class PropertyOrFieldReference extends SpelNodeImpl {
@ -147,8 +149,12 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
} }
@Override @Override
public void setValue(ExpressionState state, @Nullable Object newValue) throws EvaluationException { public TypedValue setValueInternal(ExpressionState state, Supplier<TypedValue> valueSupplier)
writeProperty(state.getActiveContextObject(), state.getEvaluationContext(), this.name, newValue); throws EvaluationException {
TypedValue typedValue = valueSupplier.get();
writeProperty(state.getActiveContextObject(), state.getEvaluationContext(), this.name, typedValue.getValue());
return typedValue;
} }
@Override @Override

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 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.
@ -19,6 +19,7 @@ package org.springframework.expression.spel.ast;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Member; import java.lang.reflect.Member;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.function.Supplier;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes; import org.springframework.asm.Opcodes;
@ -40,6 +41,7 @@ import org.springframework.util.ObjectUtils;
* *
* @author Andy Clement * @author Andy Clement
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0 * @since 3.0
*/ */
public abstract class SpelNodeImpl implements SpelNode, Opcodes { public abstract class SpelNodeImpl implements SpelNode, Opcodes {
@ -125,6 +127,28 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
@Override @Override
public void setValue(ExpressionState expressionState, @Nullable Object newValue) throws EvaluationException { public void setValue(ExpressionState expressionState, @Nullable Object newValue) throws EvaluationException {
setValueInternal(expressionState, () -> new TypedValue(newValue));
}
/**
* Evaluate the expression to a node and then set the new value created by the
* specified {@link Supplier} on that node.
* <p>For example, if the expression evaluates to a property reference, then the
* property will be set to the new value.
* <p>Favor this method over {@link #setValue(ExpressionState, Object)} when
* the value should be lazily computed.
* <p>By default, this method throws a {@link SpelEvaluationException},
* effectively disabling this feature. Subclasses may override this method to
* provide an actual implementation.
* @param expressionState the current expression state (includes the context)
* @param valueSupplier a supplier of the new value
* @throws EvaluationException if any problem occurs evaluating the expression or
* setting the new value
* @since 5.2.24
*/
public TypedValue setValueInternal(ExpressionState expressionState, Supplier<TypedValue> valueSupplier)
throws EvaluationException {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.SETVALUE_NOT_SUPPORTED, getClass()); throw new SpelEvaluationException(getStartPosition(), SpelMessage.SETVALUE_NOT_SUPPORTED, getClass());
} }

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 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.
@ -17,9 +17,11 @@
package org.springframework.expression.spel.ast; package org.springframework.expression.spel.ast;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.function.Supplier;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow; import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
@ -27,10 +29,11 @@ import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
/** /**
* Represents a variable reference, eg. #someVar. Note this is different to a *local* * Represents a variable reference &mdash; for example, {@code #someVar}. Note
* variable like $someVar * that this is different than a <em>local</em> variable like {@code $someVar}.
* *
* @author Andy Clement * @author Andy Clement
* @author Sam Brannen
* @since 3.0 * @since 3.0
*/ */
public class VariableReference extends SpelNodeImpl { public class VariableReference extends SpelNodeImpl {
@ -90,8 +93,10 @@ public class VariableReference extends SpelNodeImpl {
} }
@Override @Override
public void setValue(ExpressionState state, @Nullable Object value) throws SpelEvaluationException { public TypedValue setValueInternal(ExpressionState state, Supplier<TypedValue> valueSupplier)
state.setVariable(this.name, value); throws EvaluationException {
return state.assignVariable(this.name, valueSupplier);
} }
@Override @Override

15
spring-expression/src/main/java/org/springframework/expression/spel/support/SimpleEvaluationContext.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 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.
@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
@ -78,6 +79,7 @@ import org.springframework.lang.Nullable;
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sam Brannen
* @since 4.3.15 * @since 4.3.15
* @see #forPropertyAccessors * @see #forPropertyAccessors
* @see #forReadOnlyDataBinding() * @see #forReadOnlyDataBinding()
@ -200,6 +202,17 @@ public final class SimpleEvaluationContext implements EvaluationContext {
return this.operatorOverloader; return this.operatorOverloader;
} }
/**
* {@code SimpleEvaluationContext} does not support variable assignment within
* expressions.
* @throws SpelEvaluationException with {@link SpelMessage#VARIABLE_ASSIGNMENT_NOT_SUPPORTED}
* @since 5.2.24
*/
@Override
public TypedValue assignVariable(String name, Supplier<TypedValue> valueSupplier) {
throw new SpelEvaluationException(SpelMessage.VARIABLE_ASSIGNMENT_NOT_SUPPORTED, "#" + name);
}
@Override @Override
public void setVariable(String name, @Nullable Object value) { public void setVariable(String name, @Nullable Object value) {
this.variables.put(name, value); this.variables.put(name, value);

22
spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java

@ -24,6 +24,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException; import org.springframework.expression.AccessException;
@ -38,6 +40,7 @@ import org.springframework.expression.MethodResolver;
import org.springframework.expression.ParseException; import org.springframework.expression.ParseException;
import org.springframework.expression.spel.standard.SpelExpression; import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeLocator; import org.springframework.expression.spel.support.StandardTypeLocator;
import org.springframework.expression.spel.testresources.TestPerson; import org.springframework.expression.spel.testresources.TestPerson;
@ -367,11 +370,28 @@ public class EvaluationTests extends AbstractExpressionTests {
} }
// assignment // assignment
@Test @Test
public void testAssignmentToVariables01() { void assignmentToVariableWithStandardEvaluationContext() {
evaluate("#var1 = 'value1'", "value1", String.class); evaluate("#var1 = 'value1'", "value1", String.class);
} }
@ParameterizedTest
@CsvSource(delimiterString = "->", value = {
"'#var1 = \"value1\"' -> #var1",
"'true ? #myVar = 4 : 0' -> #myVar"
})
void assignmentToVariableWithSimpleEvaluationContext(String expression, String varName) {
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
Expression expr = parser.parseExpression(expression);
assertThatExceptionOfType(SpelEvaluationException.class)
.isThrownBy(() -> expr.getValue(context))
.satisfies(ex -> {
assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.VARIABLE_ASSIGNMENT_NOT_SUPPORTED);
assertThat(ex.getInserts()).as("inserts").containsExactly(varName);
});
}
@Test @Test
public void testTernaryOperator01() { public void testTernaryOperator01() {
evaluate("2>4?1:2", 2, Integer.class); evaluate("2>4?1:2", 2, Integer.class);

Loading…
Cancel
Save