Skip to content

Commit db056ae

Browse files
committed
ExpressionState.getConfiguration() should never return null
Issue: SPR-11031 (cherry picked from commit 4aab315)
1 parent 35d53af commit db056ae

File tree

3 files changed

+84
-76
lines changed

3 files changed

+84
-76
lines changed

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

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 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.
@@ -30,14 +30,18 @@
3030
import org.springframework.expression.TypeComparator;
3131
import org.springframework.expression.TypeConverter;
3232
import org.springframework.expression.TypedValue;
33+
import org.springframework.util.Assert;
3334

3435
/**
35-
* An ExpressionState is for maintaining per-expression-evaluation state, any changes to it are not seen by other
36-
* expressions but it gives a place to hold local variables and for component expressions in a compound expression to
37-
* communicate state. This is in contrast to the EvaluationContext, which is shared amongst expression evaluations, and
38-
* any changes to it will be seen by other expressions or any code that chooses to ask questions of the context.
36+
* An ExpressionState is for maintaining per-expression-evaluation state, any changes to
37+
* it are not seen by other expressions but it gives a place to hold local variables and
38+
* for component expressions in a compound expression to communicate state. This is in
39+
* contrast to the EvaluationContext, which is shared amongst expression evaluations, and
40+
* any changes to it will be seen by other expressions or any code that chooses to ask
41+
* questions of the context.
3942
*
40-
* <p>It also acts as a place for to define common utility routines that the various Ast nodes might need.
43+
* <p>It also acts as a place for to define common utility routines that the various AST
44+
* nodes might need.
4145
*
4246
* @author Andy Clement
4347
* @since 3.0
@@ -46,35 +50,33 @@ public class ExpressionState {
4650

4751
private final EvaluationContext relatedContext;
4852

49-
private Stack<VariableScope> variableScopes;
53+
private final TypedValue rootObject;
5054

51-
private Stack<TypedValue> contextObjects;
55+
private final SpelParserConfiguration configuration;
5256

53-
private final TypedValue rootObject;
57+
private Stack<VariableScope> variableScopes;
5458

55-
private SpelParserConfiguration configuration;
59+
private Stack<TypedValue> contextObjects;
5660

5761

5862
public ExpressionState(EvaluationContext context) {
59-
this.relatedContext = context;
60-
this.rootObject = context.getRootObject();
63+
this(context, context.getRootObject(), new SpelParserConfiguration(false, false));
6164
}
6265

6366
public ExpressionState(EvaluationContext context, SpelParserConfiguration configuration) {
64-
this.relatedContext = context;
65-
this.configuration = configuration;
66-
this.rootObject = context.getRootObject();
67+
this(context, context.getRootObject(), configuration);
6768
}
6869

6970
public ExpressionState(EvaluationContext context, TypedValue rootObject) {
70-
this.relatedContext = context;
71-
this.rootObject = rootObject;
71+
this(context, rootObject, new SpelParserConfiguration(false, false));
7272
}
7373

7474
public ExpressionState(EvaluationContext context, TypedValue rootObject, SpelParserConfiguration configuration) {
75+
Assert.notNull(context, "EvaluationContext must not be null");
76+
Assert.notNull(configuration, "SpelParserConfiguration must not be null");
7577
this.relatedContext = context;
76-
this.configuration = configuration;
7778
this.rootObject = rootObject;
79+
this.configuration = configuration;
7880
}
7981

8082

@@ -90,23 +92,22 @@ private void ensureVariableScopesInitialized() {
9092
* The active context object is what unqualified references to properties/etc are resolved against.
9193
*/
9294
public TypedValue getActiveContextObject() {
93-
if (this.contextObjects==null || this.contextObjects.isEmpty()) {
95+
if (this.contextObjects == null || this.contextObjects.isEmpty()) {
9496
return this.rootObject;
9597
}
96-
9798
return this.contextObjects.peek();
9899
}
99100

100101
public void pushActiveContextObject(TypedValue obj) {
101-
if (this.contextObjects==null) {
102-
this.contextObjects = new Stack<TypedValue>();
102+
if (this.contextObjects == null) {
103+
this.contextObjects = new Stack<TypedValue>();
103104
}
104105
this.contextObjects.push(obj);
105106
}
106107

107108
public void popActiveContextObject() {
108-
if (this.contextObjects==null) {
109-
this.contextObjects = new Stack<TypedValue>();
109+
if (this.contextObjects == null) {
110+
this.contextObjects = new Stack<TypedValue>();
110111
}
111112
this.contextObjects.pop();
112113
}
@@ -138,7 +139,8 @@ public Class<?> findType(String type) throws EvaluationException {
138139
}
139140

140141
public Object convertValue(Object value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
141-
return this.relatedContext.getTypeConverter().convertValue(value, TypeDescriptor.forObject(value), targetTypeDescriptor);
142+
return this.relatedContext.getTypeConverter().convertValue(value,
143+
TypeDescriptor.forObject(value), targetTypeDescriptor);
142144
}
143145

144146
public TypeConverter getTypeConverter() {
@@ -151,9 +153,8 @@ public Object convertValue(TypedValue value, TypeDescriptor targetTypeDescriptor
151153
}
152154

153155
/*
154-
* A new scope is entered when a function is invoked
156+
* A new scope is entered when a function is invoked.
155157
*/
156-
157158
public void enterScope(Map<String, Object> argMap) {
158159
ensureVariableScopesInitialized();
159160
this.variableScopes.push(new VariableScope(argMap));
@@ -192,8 +193,8 @@ public TypedValue operate(Operation op, Object left, Object right) throws Evalua
192193
return new TypedValue(returnValue);
193194
}
194195
else {
195-
String leftType = (left==null?"null":left.getClass().getName());
196-
String rightType = (right==null?"null":right.getClass().getName());
196+
String leftType = (left == null ? "null" : left.getClass().getName());
197+
String rightType = (right == null? "null" : right.getClass().getName());
197198
throw new SpelEvaluationException(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES, op, leftType, rightType);
198199
}
199200
}
@@ -210,16 +211,20 @@ public SpelParserConfiguration getConfiguration() {
210211
return this.configuration;
211212
}
212213

214+
213215
/**
214-
* A new scope is entered when a function is called and it is used to hold the parameters to the function call. If the names
215-
* of the parameters clash with those in a higher level scope, those in the higher level scope will not be accessible whilst
216-
* the function is executing. When the function returns the scope is exited.
216+
* A new scope is entered when a function is called and it is used to hold the
217+
* parameters to the function call. If the names of the parameters clash with
218+
* those in a higher level scope, those in the higher level scope will not be
219+
* accessible whilst the function is executing. When the function returns,
220+
* the scope is exited.
217221
*/
218222
private static class VariableScope {
219223

220224
private final Map<String, Object> vars = new HashMap<String, Object>();
221225

222-
public VariableScope() { }
226+
public VariableScope() {
227+
}
223228

224229
public VariableScope(Map<String, Object> arguments) {
225230
if (arguments != null) {

spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowC
4949
* @param autoGrowCollections if collections should automatically grow
5050
* @param maximumAutoGrowSize the maximum size that the collection can auto grow
5151
*/
52-
public SpelParserConfiguration(boolean autoGrowNullReferences,
53-
boolean autoGrowCollections, int maximumAutoGrowSize) {
52+
public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) {
5453
this.autoGrowNullReferences = autoGrowNullReferences;
5554
this.autoGrowCollections = autoGrowCollections;
5655
this.maximumAutoGrowSize = maximumAutoGrowSize;

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

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 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.
@@ -67,47 +67,20 @@ public String getName() {
6767
}
6868

6969

70-
static class AccessorLValue implements ValueRef {
71-
private PropertyOrFieldReference ref;
72-
private TypedValue contextObject;
73-
private EvaluationContext eContext;
74-
private boolean isAutoGrowNullReferences;
75-
76-
public AccessorLValue(
77-
PropertyOrFieldReference propertyOrFieldReference,
78-
TypedValue activeContextObject,
79-
EvaluationContext evaluationContext, boolean isAutoGrowNullReferences) {
80-
this.ref = propertyOrFieldReference;
81-
this.contextObject = activeContextObject;
82-
this.eContext =evaluationContext;
83-
this.isAutoGrowNullReferences = isAutoGrowNullReferences;
84-
}
85-
86-
public TypedValue getValue() {
87-
return ref.getValueInternal(contextObject,eContext,isAutoGrowNullReferences);
88-
}
89-
90-
public void setValue(Object newValue) {
91-
ref.writeProperty(contextObject,eContext, ref.name, newValue);
92-
}
93-
94-
public boolean isWritable() {
95-
return true;
96-
}
97-
98-
}
99-
10070
@Override
10171
public ValueRef getValueRef(ExpressionState state) throws EvaluationException {
102-
return new AccessorLValue(this,state.getActiveContextObject(),state.getEvaluationContext(),state.getConfiguration().isAutoGrowNullReferences());
72+
return new AccessorLValue(this, state.getActiveContextObject(), state.getEvaluationContext(),
73+
state.getConfiguration().isAutoGrowNullReferences());
10374
}
10475

10576
@Override
10677
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
107-
return getValueInternal(state.getActiveContextObject(), state.getEvaluationContext(), state.getConfiguration().isAutoGrowNullReferences());
78+
return getValueInternal(state.getActiveContextObject(), state.getEvaluationContext(),
79+
state.getConfiguration().isAutoGrowNullReferences());
10880
}
10981

110-
private TypedValue getValueInternal(TypedValue contextObject, EvaluationContext eContext, boolean isAutoGrowNullReferences) throws EvaluationException {
82+
private TypedValue getValueInternal(TypedValue contextObject, EvaluationContext eContext,
83+
boolean isAutoGrowNullReferences) throws EvaluationException {
11184

11285
TypedValue result = readProperty(contextObject, eContext, this.name);
11386

@@ -139,7 +112,7 @@ private TypedValue getValueInternal(TypedValue contextObject, EvaluationContext
139112
try {
140113
if (isWritableProperty(this.name,contextObject,eContext)) {
141114
Map<?,?> newMap = HashMap.class.newInstance();
142-
writeProperty(contextObject, eContext, name, newMap);
115+
writeProperty(contextObject, eContext, this.name, newMap);
143116
result = readProperty(contextObject, eContext, this.name);
144117
}
145118
}
@@ -158,7 +131,7 @@ private TypedValue getValueInternal(TypedValue contextObject, EvaluationContext
158131
try {
159132
if (isWritableProperty(this.name,contextObject,eContext)) {
160133
Object newObject = result.getTypeDescriptor().getType().newInstance();
161-
writeProperty(contextObject, eContext, name, newObject);
134+
writeProperty(contextObject, eContext, this.name, newObject);
162135
result = readProperty(contextObject, eContext, this.name);
163136
}
164137
}
@@ -192,14 +165,11 @@ public String toStringAST() {
192165

193166
/**
194167
* Attempt to read the named property from the current context object.
195-
* @param state the evaluation state
196-
* @param name the name of the property
197168
* @return the value of the property
198169
* @throws SpelEvaluationException if any problem accessing the property or it cannot be found
199170
*/
200171
private TypedValue readProperty(TypedValue contextObject, EvaluationContext eContext, String name) throws EvaluationException {
201172
Object targetObject = contextObject.getValue();
202-
203173
if (targetObject == null && this.nullSafe) {
204174
return TypedValue.NULL;
205175
}
@@ -249,8 +219,7 @@ private TypedValue readProperty(TypedValue contextObject, EvaluationContext eCon
249219
}
250220

251221
private void writeProperty(TypedValue contextObject, EvaluationContext eContext, String name, Object newValue) throws SpelEvaluationException {
252-
253-
if (contextObject.getValue() == null && nullSafe) {
222+
if (contextObject.getValue() == null && this.nullSafe) {
254223
return;
255224
}
256225

@@ -353,4 +322,39 @@ else if (clazz.isAssignableFrom(targetType)) {
353322
return resolvers;
354323
}
355324

325+
326+
private static class AccessorLValue implements ValueRef {
327+
328+
private final PropertyOrFieldReference ref;
329+
330+
private final TypedValue contextObject;
331+
332+
private final EvaluationContext eContext;
333+
334+
private final boolean autoGrowNullReferences;
335+
336+
public AccessorLValue(PropertyOrFieldReference propertyOrFieldReference, TypedValue activeContextObject,
337+
EvaluationContext evaluationContext, boolean autoGrowNullReferences) {
338+
this.ref = propertyOrFieldReference;
339+
this.contextObject = activeContextObject;
340+
this.eContext = evaluationContext;
341+
this.autoGrowNullReferences = autoGrowNullReferences;
342+
}
343+
344+
@Override
345+
public TypedValue getValue() {
346+
return this.ref.getValueInternal(this.contextObject, this.eContext, this.autoGrowNullReferences);
347+
}
348+
349+
@Override
350+
public void setValue(Object newValue) {
351+
this.ref.writeProperty(this.contextObject, this.eContext, this.ref.name, newValue);
352+
}
353+
354+
@Override
355+
public boolean isWritable() {
356+
return true;
357+
}
358+
}
359+
356360
}

0 commit comments

Comments
 (0)