From 4aab31531b66b6dd3c7e4f1e859d0ee95d4d39e3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 26 Oct 2013 00:01:31 +0200 Subject: [PATCH] ExpressionState.getConfiguration() should never return null Issue: SPR-11031 --- .../expression/spel/ExpressionState.java | 57 +++++++------ .../spel/SpelParserConfiguration.java | 3 +- .../spel/ast/PropertyOrFieldReference.java | 81 ++++++++++--------- 3 files changed, 69 insertions(+), 72 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java b/spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java index 3d6d2171101..bf4866660a4 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java @@ -30,6 +30,7 @@ import org.springframework.expression.PropertyAccessor; import org.springframework.expression.TypeComparator; import org.springframework.expression.TypeConverter; import org.springframework.expression.TypedValue; +import org.springframework.util.Assert; /** * An ExpressionState is for maintaining per-expression-evaluation state, any changes to @@ -49,35 +50,33 @@ public class ExpressionState { private final EvaluationContext relatedContext; - private Stack variableScopes; + private final TypedValue rootObject; - private Stack contextObjects; + private final SpelParserConfiguration configuration; - private final TypedValue rootObject; + private Stack variableScopes; - private SpelParserConfiguration configuration; + private Stack contextObjects; public ExpressionState(EvaluationContext context) { - this.relatedContext = context; - this.rootObject = context.getRootObject(); + this(context, context.getRootObject(), new SpelParserConfiguration(false, false)); } public ExpressionState(EvaluationContext context, SpelParserConfiguration configuration) { - this.relatedContext = context; - this.configuration = configuration; - this.rootObject = context.getRootObject(); + this(context, context.getRootObject(), configuration); } public ExpressionState(EvaluationContext context, TypedValue rootObject) { - this.relatedContext = context; - this.rootObject = rootObject; + this(context, rootObject, new SpelParserConfiguration(false, false)); } public ExpressionState(EvaluationContext context, TypedValue rootObject, SpelParserConfiguration configuration) { + Assert.notNull(context, "EvaluationContext must not be null"); + Assert.notNull(configuration, "SpelParserConfiguration must not be null"); this.relatedContext = context; - this.configuration = configuration; this.rootObject = rootObject; + this.configuration = configuration; } @@ -93,23 +92,22 @@ public class ExpressionState { * The active context object is what unqualified references to properties/etc are resolved against. */ public TypedValue getActiveContextObject() { - if (this.contextObjects==null || this.contextObjects.isEmpty()) { + if (this.contextObjects == null || this.contextObjects.isEmpty()) { return this.rootObject; } - return this.contextObjects.peek(); } public void pushActiveContextObject(TypedValue obj) { - if (this.contextObjects==null) { - this.contextObjects = new Stack(); + if (this.contextObjects == null) { + this.contextObjects = new Stack(); } this.contextObjects.push(obj); } public void popActiveContextObject() { - if (this.contextObjects==null) { - this.contextObjects = new Stack(); + if (this.contextObjects == null) { + this.contextObjects = new Stack(); } this.contextObjects.pop(); } @@ -151,14 +149,12 @@ public class ExpressionState { public Object convertValue(TypedValue value, TypeDescriptor targetTypeDescriptor) throws EvaluationException { Object val = value.getValue(); - return this.relatedContext.getTypeConverter().convertValue(val, - TypeDescriptor.forObject(val), targetTypeDescriptor); + return this.relatedContext.getTypeConverter().convertValue(val, TypeDescriptor.forObject(val), targetTypeDescriptor); } /* - * A new scope is entered when a function is invoked + * A new scope is entered when a function is invoked. */ - public void enterScope(Map argMap) { ensureVariableScopesInitialized(); this.variableScopes.push(new VariableScope(argMap)); @@ -197,8 +193,8 @@ public class ExpressionState { return new TypedValue(returnValue); } else { - String leftType = (left==null?"null":left.getClass().getName()); - String rightType = (right==null?"null":right.getClass().getName()); + String leftType = (left == null ? "null" : left.getClass().getName()); + String rightType = (right == null? "null" : right.getClass().getName()); throw new SpelEvaluationException(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES, op, leftType, rightType); } } @@ -217,16 +213,18 @@ public class ExpressionState { /** - * 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 - * of the parameters clash with those in a higher level scope, those in the higher level scope will not be accessible whilst - * the function is executing. When the function returns the scope is exited. + * 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 of the parameters clash with + * those in a higher level scope, those in the higher level scope will not be + * accessible whilst the function is executing. When the function returns, + * the scope is exited. */ private static class VariableScope { private final Map vars = new HashMap(); - public VariableScope() { } - + public VariableScope() { + } public VariableScope(Map arguments) { if (arguments != null) { @@ -238,7 +236,6 @@ public class ExpressionState { this.vars.put(name,value); } - public Object lookupVariable(String name) { return this.vars.get(name); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java index 20932fa9b2b..a4620901a88 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java @@ -49,8 +49,7 @@ public class SpelParserConfiguration { * @param autoGrowCollections if collections should automatically grow * @param maximumAutoGrowSize the maximum size that the collection can auto grow */ - public SpelParserConfiguration(boolean autoGrowNullReferences, - boolean autoGrowCollections, int maximumAutoGrowSize) { + public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) { this.autoGrowNullReferences = autoGrowNullReferences; this.autoGrowCollections = autoGrowCollections; this.maximumAutoGrowSize = maximumAutoGrowSize; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java index 93c16bf3e4c..e1b8c34a3fe 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java @@ -67,50 +67,20 @@ public class PropertyOrFieldReference extends SpelNodeImpl { } - static class AccessorLValue implements ValueRef { - private final PropertyOrFieldReference ref; - private final TypedValue contextObject; - private final EvaluationContext eContext; - private final boolean isAutoGrowNullReferences; - - public AccessorLValue( - PropertyOrFieldReference propertyOrFieldReference, - TypedValue activeContextObject, - EvaluationContext evaluationContext, boolean isAutoGrowNullReferences) { - this.ref = propertyOrFieldReference; - this.contextObject = activeContextObject; - this.eContext =evaluationContext; - this.isAutoGrowNullReferences = isAutoGrowNullReferences; - } - - @Override - public TypedValue getValue() { - return this.ref.getValueInternal(this.contextObject,this.eContext,this.isAutoGrowNullReferences); - } - - @Override - public void setValue(Object newValue) { - this.ref.writeProperty(this.contextObject,this.eContext, this.ref.name, newValue); - } - - @Override - public boolean isWritable() { - return true; - } - - } - @Override public ValueRef getValueRef(ExpressionState state) throws EvaluationException { - return new AccessorLValue(this,state.getActiveContextObject(),state.getEvaluationContext(),state.getConfiguration().isAutoGrowNullReferences()); + return new AccessorLValue(this, state.getActiveContextObject(), state.getEvaluationContext(), + state.getConfiguration().isAutoGrowNullReferences()); } @Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { - return getValueInternal(state.getActiveContextObject(), state.getEvaluationContext(), state.getConfiguration().isAutoGrowNullReferences()); + return getValueInternal(state.getActiveContextObject(), state.getEvaluationContext(), + state.getConfiguration().isAutoGrowNullReferences()); } - private TypedValue getValueInternal(TypedValue contextObject, EvaluationContext eContext, boolean isAutoGrowNullReferences) throws EvaluationException { + private TypedValue getValueInternal(TypedValue contextObject, EvaluationContext eContext, + boolean isAutoGrowNullReferences) throws EvaluationException { TypedValue result = readProperty(contextObject, eContext, this.name); @@ -195,14 +165,11 @@ public class PropertyOrFieldReference extends SpelNodeImpl { /** * Attempt to read the named property from the current context object. - * @param state the evaluation state - * @param name the name of the property * @return the value of the property * @throws SpelEvaluationException if any problem accessing the property or it cannot be found */ private TypedValue readProperty(TypedValue contextObject, EvaluationContext eContext, String name) throws EvaluationException { Object targetObject = contextObject.getValue(); - if (targetObject == null && this.nullSafe) { return TypedValue.NULL; } @@ -252,7 +219,6 @@ public class PropertyOrFieldReference extends SpelNodeImpl { } private void writeProperty(TypedValue contextObject, EvaluationContext eContext, String name, Object newValue) throws SpelEvaluationException { - if (contextObject.getValue() == null && this.nullSafe) { return; } @@ -356,4 +322,39 @@ public class PropertyOrFieldReference extends SpelNodeImpl { return resolvers; } + + private static class AccessorLValue implements ValueRef { + + private final PropertyOrFieldReference ref; + + private final TypedValue contextObject; + + private final EvaluationContext eContext; + + private final boolean autoGrowNullReferences; + + public AccessorLValue(PropertyOrFieldReference propertyOrFieldReference, TypedValue activeContextObject, + EvaluationContext evaluationContext, boolean autoGrowNullReferences) { + this.ref = propertyOrFieldReference; + this.contextObject = activeContextObject; + this.eContext = evaluationContext; + this.autoGrowNullReferences = autoGrowNullReferences; + } + + @Override + public TypedValue getValue() { + return this.ref.getValueInternal(this.contextObject, this.eContext, this.autoGrowNullReferences); + } + + @Override + public void setValue(Object newValue) { + this.ref.writeProperty(this.contextObject, this.eContext, this.ref.name, newValue); + } + + @Override + public boolean isWritable() { + return true; + } + } + }