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 85c1727c5c0..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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,14 +30,18 @@ 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 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 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 questions of the context. + * An 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 + * for component expressions in a compound expression to communicate state. This is in + * 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 + * questions of the context. * - *

It also acts as a place for to define common utility routines that the various Ast nodes might need. + *

It also acts as a place for to define common utility routines that the various AST + * nodes might need. * * @author Andy Clement * @since 3.0 @@ -46,35 +50,33 @@ public class ExpressionState { private final EvaluationContext relatedContext; + private final TypedValue rootObject; + + private final SpelParserConfiguration configuration; + private Stack variableScopes; private Stack contextObjects; - private final TypedValue rootObject; - - private SpelParserConfiguration configuration; - 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; } @@ -90,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(); } @@ -138,7 +139,8 @@ public class ExpressionState { } public Object convertValue(Object value, TypeDescriptor targetTypeDescriptor) throws EvaluationException { - return this.relatedContext.getTypeConverter().convertValue(value, TypeDescriptor.forObject(value), targetTypeDescriptor); + return this.relatedContext.getTypeConverter().convertValue(value, + TypeDescriptor.forObject(value), targetTypeDescriptor); } public TypeConverter getTypeConverter() { @@ -151,9 +153,8 @@ public class ExpressionState { } /* - * 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)); @@ -192,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); } } @@ -210,16 +211,20 @@ public class ExpressionState { return this.configuration; } + /** - * 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) { 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 ccadb93061b..8c26f883b28 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 99e79ff5048..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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,47 +67,20 @@ public class PropertyOrFieldReference extends SpelNodeImpl { } - static class AccessorLValue implements ValueRef { - private PropertyOrFieldReference ref; - private TypedValue contextObject; - private EvaluationContext eContext; - private boolean isAutoGrowNullReferences; - - public AccessorLValue( - PropertyOrFieldReference propertyOrFieldReference, - TypedValue activeContextObject, - EvaluationContext evaluationContext, boolean isAutoGrowNullReferences) { - this.ref = propertyOrFieldReference; - this.contextObject = activeContextObject; - this.eContext =evaluationContext; - this.isAutoGrowNullReferences = isAutoGrowNullReferences; - } - - public TypedValue getValue() { - return ref.getValueInternal(contextObject,eContext,isAutoGrowNullReferences); - } - - public void setValue(Object newValue) { - ref.writeProperty(contextObject,eContext, ref.name, newValue); - } - - 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); @@ -139,7 +112,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl { try { if (isWritableProperty(this.name,contextObject,eContext)) { Map newMap = HashMap.class.newInstance(); - writeProperty(contextObject, eContext, name, newMap); + writeProperty(contextObject, eContext, this.name, newMap); result = readProperty(contextObject, eContext, this.name); } } @@ -158,7 +131,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl { try { if (isWritableProperty(this.name,contextObject,eContext)) { Object newObject = result.getTypeDescriptor().getType().newInstance(); - writeProperty(contextObject, eContext, name, newObject); + writeProperty(contextObject, eContext, this.name, newObject); result = readProperty(contextObject, eContext, this.name); } } @@ -192,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; } @@ -249,8 +219,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl { } private void writeProperty(TypedValue contextObject, EvaluationContext eContext, String name, Object newValue) throws SpelEvaluationException { - - if (contextObject.getValue() == null && nullSafe) { + if (contextObject.getValue() == null && this.nullSafe) { return; } @@ -353,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; + } + } + }