Skip to content

Commit

Permalink
Use SimpleEvaluationContext in AbstractMvcView
Browse files Browse the repository at this point in the history
Issue: SWF-1722
  • Loading branch information
rstoyanchev committed Mar 27, 2018
1 parent 13d36e5 commit 962cb67
Show file tree
Hide file tree
Showing 8 changed files with 297 additions and 57 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ configure(subprojects.findAll {
}

subproject.ext {
springVersion = "5.0.3.RELEASE"
springSecurityVersion = "5.0.1.RELEASE"
springVersion = "5.0.5.BUILD-SNAPSHOT"
springSecurityVersion = "5.0.3.RELEASE"
servletVersion = "3.1.0"
hibernate5Version = "5.2.12.Final"
tiles3Version = "3.0.7"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2002-2018 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.binding.expression.spel;

import org.springframework.expression.EvaluationContext;

/**
* Factory to create the {@link EvaluationContext} for a given root Object.
*
* @author Rossen Stoyanchev
* @since 2.4.8
*/
public interface EvaluationContextFactory {

/**
* Create an {@link EvaluationContext} for the given root object.
* @param rootObject the root object
* @return the created context instance
*/
EvaluationContext createContext(Object rootObject);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2002-2018 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.binding.expression.spel;

import java.util.List;

import org.springframework.binding.convert.ConversionService;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.spel.support.DataBindingPropertyAccessor;
import org.springframework.expression.spel.support.SimpleEvaluationContext;

/**
* Creates {@link SimpleEvaluationContext}, for use with data binding.
*
* @author Rossen Stoyanchev
* @since 2.4.8
*/
public class SimpleEvaluationContextFactory implements EvaluationContextFactory {

private static final PropertyAccessor dataBindingPropertyAccessor =
DataBindingPropertyAccessor.forReadWriteAccess();


private final List<PropertyAccessor> propertyAccessors;

private final ConversionService conversionService;


public SimpleEvaluationContextFactory(List<PropertyAccessor> propertyAccessors,
ConversionService conversionService) {

this.propertyAccessors = propertyAccessors;
this.conversionService = conversionService;
}


@Override
public EvaluationContext createContext(Object rootObject) {
return SimpleEvaluationContext
.forPropertyAccessors(getAccessorsArray())
.withConversionService(conversionService.getDelegateConversionService())
.withRootObject(rootObject)
.build();
}

private PropertyAccessor[] getAccessorsArray() {
int length = propertyAccessors.size() + 1;
PropertyAccessor[] result = propertyAccessors.toArray(new PropertyAccessor[length]);
result[length - 1] = dataBindingPropertyAccessor;
return result;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2004-2014 the original author or authors.
* Copyright 2004-2018 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.
Expand All @@ -15,8 +15,6 @@
*/
package org.springframework.binding.expression.spel;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

Expand All @@ -25,11 +23,11 @@
import org.springframework.binding.expression.PropertyNotFoundException;
import org.springframework.binding.expression.ValueCoercionException;
import org.springframework.core.convert.ConversionService;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeConverter;
import org.springframework.util.Assert;

/**
Expand All @@ -45,17 +43,14 @@ public class SpringELExpression implements Expression {

private final Class<?> expectedType;

private final Map<String, Expression> expressionVariables;
private final EvaluationContextFactory contextFactory;

private final ConversionService conversionService;

private final List<PropertyAccessor> propertyAccessors;

/**
* Constructor for SpringELExpression.
*
* @param expression a parsed Spring EL expression instance. Must not be null.
* @param expressionVariables provides a mapping between variables names and
* @param expressionVars provides a mapping between variables names and
* parsed Spring EL expression instances.
* This parameter is optional (may be null).
* @param expectedType the target type expected from the evaluation of the expression or null.
Expand All @@ -64,24 +59,39 @@ public class SpringELExpression implements Expression {
* @param propertyAccessors propertyAccessors for Spring EL to use when evaluating expressions
*/
public SpringELExpression(org.springframework.expression.Expression expression,
Map<String, Expression> expressionVariables, Class<?> expectedType, ConversionService conversionService,
Map<String, Expression> expressionVars, Class<?> expectedType, ConversionService conversionService,
List<PropertyAccessor> propertyAccessors) {

this(expression, expectedType,
new StandardEvaluationContextFactory(propertyAccessors, conversionService, expressionVars));
}

/**
* Generalized constructor variant that accepts an {@link EvaluationContextFactory}.
* @since 2.4.8
*/
public SpringELExpression(org.springframework.expression.Expression expression, Class<?> expectedType,
EvaluationContextFactory contextFactory) {

Assert.notNull(expression, "The SpelExpression is required for evaluation");
Assert.notNull(contextFactory, "The EvaluationContextFactory is required");
this.expression = expression;
this.expressionVariables = expressionVariables;
this.expectedType = expectedType;
this.conversionService = conversionService;
this.propertyAccessors = propertyAccessors;
this.contextFactory = contextFactory;
}

public String getExpressionString() {
return expression.getExpressionString();
}

@SuppressWarnings("deprecation")
public Object getValue(Object rootObject) throws EvaluationException {
try {
return expression.getValue(createEvaluationContext(rootObject), expectedType);
EvaluationContext context = contextFactory.createContext(rootObject);
if (context instanceof StandardEvaluationContext) {
extendEvaluationContext((StandardEvaluationContext) context);
}
return expression.getValue(context, expectedType);
} catch (SpelEvaluationException e) {
if (e.getMessageCode().equals(SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE)) {
throw new PropertyNotFoundException(rootObject.getClass(), getExpressionString(), e);
Expand All @@ -95,9 +105,14 @@ public Object getValue(Object rootObject) throws EvaluationException {
}
}

@SuppressWarnings("deprecation")
public Class<?> getValueType(Object rootObject) throws EvaluationException {
try {
return expression.getValueType(createEvaluationContext(rootObject));
EvaluationContext context = contextFactory.createContext(rootObject);
if (context instanceof StandardEvaluationContext) {
extendEvaluationContext((StandardEvaluationContext) context);
}
return expression.getValueType(context);
} catch (SpelEvaluationException e) {
if (e.getMessageCode().equals(SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE)) {
throw new PropertyNotFoundException(rootObject.getClass(), getExpressionString(), e);
Expand All @@ -108,10 +123,14 @@ public Class<?> getValueType(Object rootObject) throws EvaluationException {
}
}

@SuppressWarnings("deprecation")
public void setValue(Object rootObject, Object value) throws EvaluationException {
try {
StandardEvaluationContext evaluationContext = createEvaluationContext(rootObject);
expression.setValue(evaluationContext, value);
EvaluationContext context = contextFactory.createContext(rootObject);
if (context instanceof StandardEvaluationContext) {
extendEvaluationContext((StandardEvaluationContext) context);
}
expression.setValue(context, value);
} catch (SpelEvaluationException e) {
if (e.getMessageCode().equals(SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE)) {
throw new PropertyNotFoundException(rootObject.getClass(), getExpressionString(), e);
Expand All @@ -125,43 +144,16 @@ public void setValue(Object rootObject, Object value) throws EvaluationException
}
}

/**
* Create a new Spring EL evaluation context for the given rootObject.
*/
private StandardEvaluationContext createEvaluationContext(Object rootObject) {
StandardEvaluationContext context = new StandardEvaluationContext(rootObject);
context.setVariables(getVariableValues(rootObject));
context.setTypeConverter(new StandardTypeConverter(conversionService));
context.getPropertyAccessors().addAll(propertyAccessors);
extendEvaluationContext(context);
return context;
}

/**
* Invoked every time an evaluation context is created allowing further
* initialization from sub-classes.
* @deprecated as of 2.4.8, to customize the context, please use the constructor
* that accepts an {@link EvaluationContextFactory}.
*/
@Deprecated
protected void extendEvaluationContext(StandardEvaluationContext context) {
}

/**
* Turn the map of variable-names-to-expressions into a map of variable-names-to-plain-objects
* by evaluating each object against the input rootObject.
*
* @param rootObject the Object to evaluate variable expressions against.
* @return a mapping between variables names and plain Object's.
*/
private Map<String, Object> getVariableValues(Object rootObject) {
if (expressionVariables == null) {
return Collections.emptyMap();
}
Map<String, Object> variableValues = new HashMap<>(expressionVariables.size());
for (Map.Entry<String, Expression> var : expressionVariables.entrySet()) {
variableValues.put(var.getKey(), var.getValue().getValue(rootObject));
}
return variableValues;
}

public String toString() {
return getExpressionString();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2004-2017 the original author or authors.
* Copyright 2004-2018 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.
Expand Down Expand Up @@ -28,6 +28,7 @@
import org.springframework.binding.expression.ParserContext;
import org.springframework.binding.expression.ParserException;
import org.springframework.binding.expression.support.NullParserContext;
import org.springframework.binding.expression.support.SimpleParserContext;
import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
Expand All @@ -49,6 +50,9 @@ public class SpringELExpressionParser implements ExpressionParser {

private final List<PropertyAccessor> propertyAccessors = new ArrayList<>();

private final SimpleEvaluationContextFactory simpleContextFactory;


public SpringELExpressionParser(SpelExpressionParser expressionParser) {
this(expressionParser, new DefaultConversionService());
}
Expand All @@ -57,6 +61,7 @@ public SpringELExpressionParser(SpelExpressionParser expressionParser, Conversio
this.expressionParser = expressionParser;
this.propertyAccessors.add(new MapAccessor());
this.conversionService = conversionService;
this.simpleContextFactory = new SimpleEvaluationContextFactory(this.propertyAccessors, conversionService);
}

public ConversionService getConversionService() {
Expand All @@ -78,11 +83,17 @@ public Expression parseExpression(String expression, ParserContext context) thro
Class<?> expectedResultType = context.getExpectedEvaluationResultType();
org.springframework.core.convert.ConversionService cs = conversionService.getDelegateConversionService();

return createSpringELExpression(expressionVars, spelExpression, expectedResultType, cs);
return context instanceof SimpleParserContext ?
new SpringELExpression(spelExpression, expectedResultType, simpleContextFactory) :
createSpringELExpression(expressionVars, spelExpression, expectedResultType, cs);
}

/**
* Create the {@link SpringELExpression}.
* <p><strong>Note:</strong> as of 2.4.8, this method is not invoked when a
* {@link SimpleParserContext} is passed in, which is mainly the case when using
* SpEL for data binding. In those scenarios, the configuration options are
* limited to the use of property accessors and a ConversionService.
*/
protected SpringELExpression createSpringELExpression(Map<String, Expression> expressionVars,
org.springframework.expression.Expression spelExpression, Class<?> expectedResultType,
Expand Down
Loading

0 comments on commit 962cb67

Please sign in to comment.