SWF-1136 First cut of Spring Binding adapters for Spring EL. Results verified with same test cases as what is there for the Unified EL adapters.

This commit is contained in:
Rossen Stoyanchev
2010-04-23 10:55:30 +00:00
parent 89783b5b9e
commit 4aff050e81
8 changed files with 542 additions and 56 deletions

View File

@@ -0,0 +1,136 @@
/*
* Copyright 2004-2010 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.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.springframework.binding.expression.EvaluationException;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ValueCoercionException;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
/**
* <p>
* A wrapper for a Spring EL {@link org.springframework.expression.Expression} allowing it to be used under the Spring
* Binding {@link Expression} abstraction.
* </p>
*
* @author Rossen Stoyanchev
* @since 2.1.0
*/
public class SpringELExpression implements Expression {
private org.springframework.expression.Expression expression;
private StandardEvaluationContext evaluationContext;
private Class expectedType;
private Map expressionVariables;
/**
* Constructor for SpringELExpression.
*
* @param expression a parsed Spring EL expression instance. Must not be null.
* @param expressionVariables 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. This parameter is
* optional (may be null).
* @param evaluationContext a Spring EL evaluation context.
*/
public SpringELExpression(org.springframework.expression.Expression expression, Map expressionVariables,
Class expectedType, StandardEvaluationContext evaluationContext) {
Assert.notNull(expression, "The SpelExpression is required for evaluation");
Assert.notNull(evaluationContext, "The StandardEvaluationContext is required for evaluation");
this.expression = expression;
this.expressionVariables = expressionVariables;
this.expectedType = expectedType;
this.evaluationContext = evaluationContext;
}
public String getExpressionString() {
return expression.getExpressionString();
}
public Object getValue(Object rootObject) throws EvaluationException {
try {
updateEvaluationContext(rootObject);
return expression.getValue(evaluationContext, expectedType);
} catch (SpelEvaluationException e) {
if (e.getMessageCode().equals(SpelMessage.TYPE_CONVERSION_ERROR)) {
throw new ValueCoercionException(rootObject.getClass(), getExpressionString(), null, expectedType, e);
}
throw new EvaluationException(rootObject.getClass(), expression.getExpressionString(),
"An ELException occurred getting the value for expression '" + getExpressionString()
+ "' on context [" + rootObject.getClass() + "]", e);
}
}
public Class getValueType(Object rootObject) throws EvaluationException {
evaluationContext.setRootObject(rootObject);
return expression.getValueType(evaluationContext);
}
public void setValue(Object rootObject, Object value) throws EvaluationException {
try {
updateEvaluationContext(rootObject);
expression.setValue(evaluationContext, value);
} catch (SpelEvaluationException e) {
if (e.getMessageCode().equals(SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE)) {
throw new ValueCoercionException(rootObject.getClass(), getExpressionString(), value, expectedType, e);
}
throw new EvaluationException(rootObject.getClass(), getExpressionString(),
"An ELException occurred setting the value of expression '" + getExpressionString()
+ "' on context [" + rootObject.getClass() + "] to [" + value + "]", e);
}
}
/**
* Updates the Spring EL evaluation context to reflect the given rootObject.
*
* @param rootObject the object for the evaluation.
*/
private void updateEvaluationContext(Object rootObject) {
evaluationContext.setRootObject(rootObject);
evaluationContext.setVariables(getVariableValues(rootObject));
}
/**
* Turns 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 getVariableValues(Object rootObject) {
if (expressionVariables == null) {
return Collections.EMPTY_MAP;
}
Map variableValues = new HashMap(expressionVariables.size());
for (Iterator iterator = expressionVariables.entrySet().iterator(); iterator.hasNext();) {
Map.Entry var = (Map.Entry) iterator.next();
variableValues.put(var.getKey(), ((Expression) var.getValue()).getValue(rootObject));
}
return variableValues;
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright 2004-2010 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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.expression.ExpressionVariable;
import org.springframework.binding.expression.ParserContext;
import org.springframework.binding.expression.ParserException;
import org.springframework.binding.expression.support.NullParserContext;
import org.springframework.core.convert.ConversionService;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeConverter;
import org.springframework.util.Assert;
/**
* <p>
* Adapts the Spring EL {@link SpelExpressionParser} to the Spring Binding {@link ExpressionParser} interface.
* </p>
*
* @author Rossen Stoyanchev
* @since 2.1.0
*/
public class SpringELExpressionParser implements ExpressionParser {
private SpelExpressionParser expressionParser;
private TypeConverter typeConverter = new StandardTypeConverter();
private List propertyAccessors = new ArrayList();
public SpringELExpressionParser(SpelExpressionParser expressionParser) {
this.expressionParser = expressionParser;
}
public void setConversionService(ConversionService conversionService) {
typeConverter = new StandardTypeConverter(conversionService);
}
public void addPropertyAccessor(PropertyAccessor propertyAccessor) {
propertyAccessors.add(propertyAccessor);
}
public Expression parseExpression(String expressionString, ParserContext parserContext) throws ParserException {
Assert.hasText(expressionString, "The expression string to parse is required and must not be empty");
parserContext = (parserContext == null) ? NullParserContext.INSTANCE : parserContext;
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
evaluationContext.setTypeConverter(typeConverter);
evaluationContext.getPropertyAccessors().addAll(propertyAccessors);
Map spelExpressionVariables = parseSpelExpressionVariables(parserContext.getExpressionVariables());
return new SpringELExpression(parseSpelExpression(expressionString, parserContext), spelExpressionVariables,
parserContext.getExpectedEvaluationResultType(), evaluationContext);
}
private org.springframework.expression.Expression parseSpelExpression(String expression, ParserContext parserContext) {
return expressionParser.parseExpression(expression, getSpelParserContext(parserContext));
}
private org.springframework.expression.ParserContext getSpelParserContext(ParserContext parserContext) {
return parserContext.isTemplate() ? org.springframework.expression.ParserContext.TEMPLATE_EXPRESSION : null;
}
/**
* Turns {@link ExpressionVariable}'s (pairs of variable names and string expressions) into a map of variable names
* and parsed Spring EL expressions. The map will be saved in a Spring EL {@link EvaluationContext} for later use at
* evaluation time.
*
* @param expressionVariables an array of ExpressionVariable instances.
* @return a Map or null if the input array is empty.
*/
private Map parseSpelExpressionVariables(ExpressionVariable[] expressionVariables) {
if (expressionVariables == null || expressionVariables.length == 0) {
return null;
}
Map spelExpressionVariables = new HashMap(expressionVariables.length);
for (int i = 0; i < expressionVariables.length; i++) {
ExpressionVariable var = expressionVariables[i];
spelExpressionVariables.put(var.getName(),
parseExpression(var.getValueExpression(), var.getParserContext()));
}
return spelExpressionVariables;
}
}