bean wrapper integration to support view->model binding with mvc semantics
This commit is contained in:
@@ -46,4 +46,6 @@ public interface ConversionService {
|
||||
*/
|
||||
public Class getClassForAlias(String alias);
|
||||
|
||||
public ConversionExecutor[] getConversionExecutors(Class sourceClass);
|
||||
|
||||
}
|
||||
@@ -16,11 +16,15 @@
|
||||
package org.springframework.binding.convert.service;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.binding.convert.ConversionExecutor;
|
||||
import org.springframework.binding.convert.ConversionExecutorNotFoundException;
|
||||
@@ -174,6 +178,22 @@ public class GenericConversionService implements ConversionService {
|
||||
|
||||
// subclassing support
|
||||
|
||||
public ConversionExecutor[] getConversionExecutors(Class sourceClass) {
|
||||
Map sourceMap = getSourceMap(sourceClass);
|
||||
if (sourceMap.isEmpty()) {
|
||||
return new ConversionExecutor[0];
|
||||
}
|
||||
Set entries = sourceMap.entrySet();
|
||||
List conversionExecutors = new ArrayList(entries.size());
|
||||
for (Iterator it = entries.iterator(); it.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
Class targetClass = (Class) entry.getKey();
|
||||
Converter converter = (Converter) entry.getValue();
|
||||
conversionExecutors.add(new StaticConversionExecutor(sourceClass, targetClass, converter));
|
||||
}
|
||||
return (ConversionExecutor[]) conversionExecutors.toArray(new ConversionExecutor[entries.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an indexed map of converters. Each entry key is a source class that can be converted from, and each entry
|
||||
* value is a map of target classes that can be convertered to, ultimately mapping to a specific converter that can
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package org.springframework.binding.expression.beanwrapper;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.InvalidPropertyException;
|
||||
import org.springframework.binding.convert.ConversionExecutor;
|
||||
import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.expression.EvaluationAttempt;
|
||||
import org.springframework.binding.expression.EvaluationException;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.PropertyNotFoundException;
|
||||
|
||||
public class BeanWrapperExpression implements Expression {
|
||||
|
||||
private String expression;
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
public BeanWrapperExpression(String expression, ConversionService conversionService) {
|
||||
this.expression = expression;
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof BeanWrapperExpression)) {
|
||||
return false;
|
||||
}
|
||||
BeanWrapperExpression other = (BeanWrapperExpression) o;
|
||||
return expression.equals(other.expression);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return expression.hashCode();
|
||||
}
|
||||
|
||||
public Object getValue(Object context) throws EvaluationException {
|
||||
try {
|
||||
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(context);
|
||||
return beanWrapper.getPropertyValue(expression);
|
||||
} catch (InvalidPropertyException e) {
|
||||
throw new PropertyNotFoundException(new EvaluationAttempt(this, context), e);
|
||||
} catch (BeansException e) {
|
||||
throw new EvaluationException(new EvaluationAttempt(this, context), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setValue(Object context, Object value) {
|
||||
try {
|
||||
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(context);
|
||||
ConversionExecutor[] converters = conversionService.getConversionExecutors(String.class);
|
||||
for (int i = 0; i < converters.length; i++) {
|
||||
ConversionExecutor converter = converters[i];
|
||||
beanWrapper.registerCustomEditor(converter.getTargetClass(), new PropertyEditorConverter(converter));
|
||||
}
|
||||
beanWrapper.setPropertyValue(expression, value);
|
||||
} catch (InvalidPropertyException e) {
|
||||
throw new PropertyNotFoundException(new EvaluationAttempt(this, context), e);
|
||||
} catch (BeansException e) {
|
||||
throw new EvaluationException(new EvaluationAttempt(this, context), e);
|
||||
}
|
||||
}
|
||||
|
||||
public Class getValueType(Object context) {
|
||||
try {
|
||||
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(context);
|
||||
return beanWrapper.getPropertyType(expression);
|
||||
} catch (InvalidPropertyException e) {
|
||||
throw new PropertyNotFoundException(new EvaluationAttempt(this, context), e);
|
||||
} catch (BeansException e) {
|
||||
throw new EvaluationException(new EvaluationAttempt(this, context), e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getExpressionString() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
public static class PropertyEditorConverter extends PropertyEditorSupport {
|
||||
|
||||
private ConversionExecutor converter;
|
||||
|
||||
public PropertyEditorConverter(ConversionExecutor converter) {
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
public void setAsText(String text) throws IllegalArgumentException {
|
||||
setValue(converter.execute(text));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2004-2008 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.beanwrapper;
|
||||
|
||||
import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.ParserContext;
|
||||
import org.springframework.binding.expression.ParserException;
|
||||
import org.springframework.binding.expression.support.AbstractExpressionParser;
|
||||
|
||||
/**
|
||||
* An expression parser that parses Ognl expressions.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class BeanWrapperExpressionParser extends AbstractExpressionParser {
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
public ConversionService getConversionService() {
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
protected Expression doParseExpression(String expressionString, ParserContext context) throws ParserException {
|
||||
return new BeanWrapperExpression(expressionString, conversionService);
|
||||
}
|
||||
}
|
||||
@@ -40,24 +40,12 @@ import org.springframework.binding.expression.SetValueAttempt;
|
||||
*/
|
||||
class OgnlExpression implements Expression {
|
||||
|
||||
/**
|
||||
* The expression.
|
||||
*/
|
||||
private Object expression;
|
||||
|
||||
/**
|
||||
* Expression variable initial values.
|
||||
*/
|
||||
private Map variableExpressions;
|
||||
|
||||
/**
|
||||
* The expected type of object returned from evaluating the expression.
|
||||
*/
|
||||
private Class expectedResultType;
|
||||
|
||||
/**
|
||||
* The original expression string.
|
||||
*/
|
||||
private String expressionString;
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,233 +15,21 @@
|
||||
*/
|
||||
package org.springframework.binding.expression.ognl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ognl.Ognl;
|
||||
import ognl.OgnlException;
|
||||
import ognl.OgnlRuntime;
|
||||
import ognl.PropertyAccessor;
|
||||
|
||||
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.CompositeStringExpression;
|
||||
import org.springframework.binding.expression.support.LiteralExpression;
|
||||
import org.springframework.binding.expression.support.NullParserContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.binding.expression.support.AbstractExpressionParser;
|
||||
|
||||
/**
|
||||
* An expression parser that parses Ognl expressions.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class OgnlExpressionParser implements ExpressionParser {
|
||||
|
||||
/**
|
||||
* The expression prefix.
|
||||
*/
|
||||
private static final String DEFAULT_EXPRESSION_PREFIX = "${";
|
||||
|
||||
/**
|
||||
* The expression suffix.
|
||||
*/
|
||||
private static final String DEFAULT_EXPRESSION_SUFFIX = "}";
|
||||
|
||||
/**
|
||||
* The marked expression delimter prefix.
|
||||
*/
|
||||
private String expressionPrefix = DEFAULT_EXPRESSION_PREFIX;
|
||||
|
||||
/**
|
||||
* The marked expression delimiter suffix.
|
||||
*/
|
||||
private String expressionSuffix = DEFAULT_EXPRESSION_SUFFIX;
|
||||
|
||||
/**
|
||||
* Should we allow delimited OGNL eval expressions like "#{foo.bar}"? If not, evalutable OGNL expressions must not
|
||||
* be enclosed in delimiters like ${foo.bar} else an exception is thrown. Only here for compatability reasons, as
|
||||
* Web Flow 1.0 allows delimited OGNL eval expressions while 2.x does not.
|
||||
*/
|
||||
private boolean allowDelimitedEvalExpressions;
|
||||
|
||||
/**
|
||||
* Returns the configured expression delimiter prefix. Defaults to "${".
|
||||
*/
|
||||
public String getExpressionPrefix() {
|
||||
return expressionPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the expression delimiter prefix.
|
||||
*/
|
||||
public void setExpressionPrefix(String expressionPrefix) {
|
||||
this.expressionPrefix = expressionPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expression delimiter suffix. Defaults to "}".
|
||||
*/
|
||||
public String getExpressionSuffix() {
|
||||
return expressionSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the expression delimiter suffix.
|
||||
*/
|
||||
public void setExpressionSuffix(String expressionSuffix) {
|
||||
this.expressionSuffix = expressionSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if this parser allows delimited OGNL eval expressions like <code>${foo.bar}</code>.
|
||||
*/
|
||||
public boolean getAllowDelimitedEvalExpressions() {
|
||||
return allowDelimitedEvalExpressions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if this parser allows OGNL eval expressions like ${foo.bar}.
|
||||
*/
|
||||
public void setAllowDelimitedEvalExpressions(boolean allowDelmitedEvalExpressions) {
|
||||
this.allowDelimitedEvalExpressions = allowDelmitedEvalExpressions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a property access strategy for the given class.
|
||||
* @param clazz the class that contains properties needing access
|
||||
* @param propertyAccessor the property access strategy
|
||||
*/
|
||||
public void addPropertyAccessor(Class clazz, PropertyAccessor propertyAccessor) {
|
||||
OgnlRuntime.setPropertyAccessor(clazz, propertyAccessor);
|
||||
}
|
||||
|
||||
// expression parser
|
||||
|
||||
public Expression parseExpression(String expressionString, ParserContext context) throws ParserException {
|
||||
Assert.notNull(expressionString, "The expression string to parse is required");
|
||||
if (context == null) {
|
||||
context = NullParserContext.INSTANCE;
|
||||
}
|
||||
if (context.isTemplate()) {
|
||||
return parseTemplate(expressionString, context);
|
||||
} else {
|
||||
if (expressionString.startsWith(getExpressionPrefix()) && expressionString.endsWith(getExpressionSuffix())) {
|
||||
if (!allowDelimitedEvalExpressions) {
|
||||
throw new ParserException(
|
||||
expressionString,
|
||||
"The expression '"
|
||||
+ expressionString
|
||||
+ "' being parsed is expected be a standard OGNL expression. Do not attempt to enclose such expression strings in ${} delimiters--this is redundant. If you need to parse a template that mixes literal text with evaluatable blocks, set the 'template' parser context attribute to true.",
|
||||
null);
|
||||
} else {
|
||||
int lastIndex = expressionString.length() - getExpressionSuffix().length();
|
||||
String ognlExpression = expressionString.substring(getExpressionPrefix().length(), lastIndex);
|
||||
return doParseExpression(ognlExpression, context);
|
||||
}
|
||||
} else {
|
||||
return doParseExpression(expressionString, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Expression parseTemplate(String expressionString, ParserContext context) throws ParserException {
|
||||
Assert.notNull(expressionString, "The expression string to parse is required");
|
||||
if (expressionString.length() == 0) {
|
||||
return parseEmptyExpressionString(context);
|
||||
}
|
||||
Expression[] expressions = parseExpressions(expressionString, context);
|
||||
if (expressions.length == 1) {
|
||||
return expressions[0];
|
||||
} else {
|
||||
return new CompositeStringExpression(expressions);
|
||||
}
|
||||
}
|
||||
|
||||
// helper methods
|
||||
|
||||
/**
|
||||
* Helper that handles a empty expression string.
|
||||
*/
|
||||
private Expression parseEmptyExpressionString(ParserContext context) {
|
||||
if (allowDelimitedEvalExpressions) {
|
||||
// let the parser handle it
|
||||
return doParseExpression("", context);
|
||||
} else {
|
||||
// return a literal expression containing the empty string
|
||||
return new LiteralExpression("");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper that parses given expression string using the configured parser. The expression string can contain any
|
||||
* number of expressions all contained in "${...}" markers. For instance: "foo${expr0}bar${expr1}". The static
|
||||
* pieces of text will also be returned as Expressions that just return that static piece of text. As a result,
|
||||
* evaluating all returned expressions and concatenating the results produces the complete evaluated string.
|
||||
* @param expressionString the expression string
|
||||
* @return the parsed expressions
|
||||
* @throws ParserException when the expressions cannot be parsed
|
||||
*/
|
||||
private Expression[] parseExpressions(String expressionString, ParserContext context) throws ParserException {
|
||||
List expressions = new LinkedList();
|
||||
int startIdx = 0;
|
||||
while (startIdx < expressionString.length()) {
|
||||
int prefixIndex = expressionString.indexOf(getExpressionPrefix(), startIdx);
|
||||
if (prefixIndex >= startIdx) {
|
||||
// a inner expression was found - this is a composite
|
||||
if (prefixIndex > startIdx) {
|
||||
expressions.add(new LiteralExpression(expressionString.substring(startIdx, prefixIndex)));
|
||||
startIdx = prefixIndex;
|
||||
}
|
||||
int nextPrefixIndex = expressionString.indexOf(getExpressionPrefix(), prefixIndex
|
||||
+ getExpressionPrefix().length());
|
||||
int suffixIndex;
|
||||
if (nextPrefixIndex == -1) {
|
||||
// this is the last expression in the expression string
|
||||
suffixIndex = expressionString.lastIndexOf(getExpressionSuffix());
|
||||
} else {
|
||||
// another expression exists after this one in the expression string
|
||||
suffixIndex = expressionString.lastIndexOf(getExpressionSuffix(), nextPrefixIndex);
|
||||
}
|
||||
if (suffixIndex < (prefixIndex + getExpressionPrefix().length())) {
|
||||
throw new ParserException(expressionString, "No ending suffix '" + getExpressionSuffix()
|
||||
+ "' for expression starting at character " + prefixIndex + ": "
|
||||
+ expressionString.substring(prefixIndex), null);
|
||||
} else if (suffixIndex == prefixIndex + getExpressionPrefix().length()) {
|
||||
throw new ParserException(expressionString, "No expression defined within delimiter '"
|
||||
+ getExpressionPrefix() + getExpressionSuffix() + "' at character " + prefixIndex, null);
|
||||
} else {
|
||||
String expr = expressionString.substring(prefixIndex + getExpressionPrefix().length(), suffixIndex);
|
||||
expressions.add(doParseExpression(expr, context));
|
||||
startIdx = suffixIndex + 1;
|
||||
}
|
||||
} else {
|
||||
if (startIdx == 0) {
|
||||
// treat the entire string as one expression
|
||||
if (allowDelimitedEvalExpressions) {
|
||||
expressions.add(doParseExpression(expressionString, context));
|
||||
} else {
|
||||
// treat entire string as a literal
|
||||
expressions.add(new LiteralExpression(expressionString));
|
||||
}
|
||||
} else {
|
||||
// no more ${expressions} found in string, add rest as static text
|
||||
expressions.add(new LiteralExpression(expressionString.substring(startIdx)));
|
||||
}
|
||||
startIdx = expressionString.length();
|
||||
}
|
||||
}
|
||||
return (Expression[]) expressions.toArray(new Expression[expressions.size()]);
|
||||
}
|
||||
|
||||
private Expression doParseExpression(String expressionString, ParserContext context) throws ParserException {
|
||||
if (context == null) {
|
||||
context = NullParserContext.INSTANCE;
|
||||
}
|
||||
public class OgnlExpressionParser extends AbstractExpressionParser {
|
||||
protected Expression doParseExpression(String expressionString, ParserContext context) throws ParserException {
|
||||
try {
|
||||
return new OgnlExpression(Ognl.parseExpression(expressionString), parseVariableExpressions(context
|
||||
.getExpressionVariables()), context.getExpectedEvaluationResultType(), expressionString);
|
||||
@@ -249,16 +37,4 @@ public class OgnlExpressionParser implements ExpressionParser {
|
||||
throw new ParserException(expressionString, e);
|
||||
}
|
||||
}
|
||||
|
||||
private Map parseVariableExpressions(ExpressionVariable[] variables) throws OgnlException {
|
||||
if (variables == null || variables.length == 0) {
|
||||
return null;
|
||||
}
|
||||
Map variableExpressions = new HashMap(variables.length, 1);
|
||||
for (int i = 0; i < variables.length; i++) {
|
||||
ExpressionVariable var = variables[i];
|
||||
variableExpressions.put(var.getName(), parseExpression(var.getValueExpression(), var.getParserContext()));
|
||||
}
|
||||
return variableExpressions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright 2004-2008 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.support;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
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.util.Assert;
|
||||
|
||||
/**
|
||||
* An expression parser that parses Ognl expressions.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public abstract class AbstractExpressionParser implements ExpressionParser {
|
||||
|
||||
/**
|
||||
* The expression prefix.
|
||||
*/
|
||||
private static final String DEFAULT_EXPRESSION_PREFIX = "${";
|
||||
|
||||
/**
|
||||
* The expression suffix.
|
||||
*/
|
||||
private static final String DEFAULT_EXPRESSION_SUFFIX = "}";
|
||||
|
||||
/**
|
||||
* The marked expression delimiter prefix.
|
||||
*/
|
||||
private String expressionPrefix = DEFAULT_EXPRESSION_PREFIX;
|
||||
|
||||
/**
|
||||
* The marked expression delimiter suffix.
|
||||
*/
|
||||
private String expressionSuffix = DEFAULT_EXPRESSION_SUFFIX;
|
||||
|
||||
/**
|
||||
* Should we allow delimited eval expressions like "${foo.bar}"? If not, evalutable expressions must not be enclosed
|
||||
* in delimiters like ${foo.bar} else an exception is thrown. Only here for compatability reasons, as Web Flow 1.0
|
||||
* allows delimited eval expressions while 2.x does not.
|
||||
*/
|
||||
private boolean allowDelimitedEvalExpressions;
|
||||
|
||||
/**
|
||||
* Returns the configured expression delimiter prefix. Defaults to "${".
|
||||
*/
|
||||
public String getExpressionPrefix() {
|
||||
return expressionPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the expression delimiter prefix.
|
||||
*/
|
||||
public void setExpressionPrefix(String expressionPrefix) {
|
||||
this.expressionPrefix = expressionPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expression delimiter suffix. Defaults to "}".
|
||||
*/
|
||||
public String getExpressionSuffix() {
|
||||
return expressionSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the expression delimiter suffix.
|
||||
*/
|
||||
public void setExpressionSuffix(String expressionSuffix) {
|
||||
this.expressionSuffix = expressionSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if this parser allows delimited eval expressions like <code>${foo.bar}</code>.
|
||||
*/
|
||||
public boolean getAllowDelimitedEvalExpressions() {
|
||||
return allowDelimitedEvalExpressions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if this parser allows eval expressions like ${foo.bar}.
|
||||
*/
|
||||
public void setAllowDelimitedEvalExpressions(boolean allowDelmitedEvalExpressions) {
|
||||
this.allowDelimitedEvalExpressions = allowDelmitedEvalExpressions;
|
||||
}
|
||||
|
||||
// expression parser
|
||||
|
||||
public Expression parseExpression(String expressionString, ParserContext context) throws ParserException {
|
||||
Assert.notNull(expressionString, "The expression string to parse is required");
|
||||
if (context == null) {
|
||||
context = NullParserContext.INSTANCE;
|
||||
}
|
||||
if (context.isTemplate()) {
|
||||
return parseTemplate(expressionString, context);
|
||||
} else {
|
||||
if (expressionString.startsWith(getExpressionPrefix()) && expressionString.endsWith(getExpressionSuffix())) {
|
||||
if (!allowDelimitedEvalExpressions) {
|
||||
throw new ParserException(
|
||||
expressionString,
|
||||
"The expression '"
|
||||
+ expressionString
|
||||
+ "' being parsed is expected be a standard OGNL expression. Do not attempt to enclose such expression strings in ${} delimiters--this is redundant. If you need to parse a template that mixes literal text with evaluatable blocks, set the 'template' parser context attribute to true.",
|
||||
null);
|
||||
} else {
|
||||
int lastIndex = expressionString.length() - getExpressionSuffix().length();
|
||||
String ognlExpression = expressionString.substring(getExpressionPrefix().length(), lastIndex);
|
||||
return doParseExpression(ognlExpression, context);
|
||||
}
|
||||
} else {
|
||||
return doParseExpression(expressionString, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Expression parseTemplate(String expressionString, ParserContext context) throws ParserException {
|
||||
Assert.notNull(expressionString, "The expression string to parse is required");
|
||||
if (expressionString.length() == 0) {
|
||||
return parseEmptyExpressionString(context);
|
||||
}
|
||||
Expression[] expressions = parseExpressions(expressionString, context);
|
||||
if (expressions.length == 1) {
|
||||
return expressions[0];
|
||||
} else {
|
||||
return new CompositeStringExpression(expressions);
|
||||
}
|
||||
}
|
||||
|
||||
// helper methods
|
||||
|
||||
/**
|
||||
* Helper that handles a empty expression string.
|
||||
*/
|
||||
private Expression parseEmptyExpressionString(ParserContext context) {
|
||||
if (allowDelimitedEvalExpressions) {
|
||||
// let the parser handle it
|
||||
return doParseExpression("", context);
|
||||
} else {
|
||||
// return a literal expression containing the empty string
|
||||
return new LiteralExpression("");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper that parses given expression string using the configured parser. The expression string can contain any
|
||||
* number of expressions all contained in "${...}" markers. For instance: "foo${expr0}bar${expr1}". The static
|
||||
* pieces of text will also be returned as Expressions that just return that static piece of text. As a result,
|
||||
* evaluating all returned expressions and concatenating the results produces the complete evaluated string.
|
||||
* @param expressionString the expression string
|
||||
* @return the parsed expressions
|
||||
* @throws ParserException when the expressions cannot be parsed
|
||||
*/
|
||||
private Expression[] parseExpressions(String expressionString, ParserContext context) throws ParserException {
|
||||
List expressions = new LinkedList();
|
||||
int startIdx = 0;
|
||||
while (startIdx < expressionString.length()) {
|
||||
int prefixIndex = expressionString.indexOf(getExpressionPrefix(), startIdx);
|
||||
if (prefixIndex >= startIdx) {
|
||||
// a inner expression was found - this is a composite
|
||||
if (prefixIndex > startIdx) {
|
||||
expressions.add(new LiteralExpression(expressionString.substring(startIdx, prefixIndex)));
|
||||
startIdx = prefixIndex;
|
||||
}
|
||||
int nextPrefixIndex = expressionString.indexOf(getExpressionPrefix(), prefixIndex
|
||||
+ getExpressionPrefix().length());
|
||||
int suffixIndex;
|
||||
if (nextPrefixIndex == -1) {
|
||||
// this is the last expression in the expression string
|
||||
suffixIndex = expressionString.lastIndexOf(getExpressionSuffix());
|
||||
} else {
|
||||
// another expression exists after this one in the expression string
|
||||
suffixIndex = expressionString.lastIndexOf(getExpressionSuffix(), nextPrefixIndex);
|
||||
}
|
||||
if (suffixIndex < (prefixIndex + getExpressionPrefix().length())) {
|
||||
throw new ParserException(expressionString, "No ending suffix '" + getExpressionSuffix()
|
||||
+ "' for expression starting at character " + prefixIndex + ": "
|
||||
+ expressionString.substring(prefixIndex), null);
|
||||
} else if (suffixIndex == prefixIndex + getExpressionPrefix().length()) {
|
||||
throw new ParserException(expressionString, "No expression defined within delimiter '"
|
||||
+ getExpressionPrefix() + getExpressionSuffix() + "' at character " + prefixIndex, null);
|
||||
} else {
|
||||
String expr = expressionString.substring(prefixIndex + getExpressionPrefix().length(), suffixIndex);
|
||||
expressions.add(doParseExpression(expr, context));
|
||||
startIdx = suffixIndex + 1;
|
||||
}
|
||||
} else {
|
||||
if (startIdx == 0) {
|
||||
// treat the entire string as one expression
|
||||
if (allowDelimitedEvalExpressions) {
|
||||
expressions.add(doParseExpression(expressionString, context));
|
||||
} else {
|
||||
// treat entire string as a literal
|
||||
expressions.add(new LiteralExpression(expressionString));
|
||||
}
|
||||
} else {
|
||||
// no more ${expressions} found in string, add rest as static text
|
||||
expressions.add(new LiteralExpression(expressionString.substring(startIdx)));
|
||||
}
|
||||
startIdx = expressionString.length();
|
||||
}
|
||||
}
|
||||
return (Expression[]) expressions.toArray(new Expression[expressions.size()]);
|
||||
}
|
||||
|
||||
protected Map parseVariableExpressions(ExpressionVariable[] variables) throws ParserException {
|
||||
if (variables == null || variables.length == 0) {
|
||||
return null;
|
||||
}
|
||||
Map variableExpressions = new HashMap(variables.length, 1);
|
||||
for (int i = 0; i < variables.length; i++) {
|
||||
ExpressionVariable var = variables[i];
|
||||
variableExpressions.put(var.getName(), parseExpression(var.getValueExpression(), var.getParserContext()));
|
||||
}
|
||||
return variableExpressions;
|
||||
}
|
||||
|
||||
protected abstract Expression doParseExpression(String expressionString, ParserContext context)
|
||||
throws ParserException;
|
||||
|
||||
}
|
||||
@@ -17,7 +17,6 @@ package org.springframework.binding.mapping.impl;
|
||||
|
||||
import org.springframework.binding.convert.ConversionExecutionException;
|
||||
import org.springframework.binding.convert.ConversionExecutor;
|
||||
import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.expression.EvaluationException;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.mapping.Mapping;
|
||||
@@ -106,7 +105,7 @@ public class DefaultMapping implements Mapping {
|
||||
* Execute this mapping.
|
||||
* @param context the mapping context
|
||||
*/
|
||||
void map(DefaultMappingContext context) {
|
||||
public void map(DefaultMappingContext context) {
|
||||
context.setCurrentMapping(this);
|
||||
Object sourceValue;
|
||||
try {
|
||||
@@ -128,27 +127,6 @@ public class DefaultMapping implements Mapping {
|
||||
context.setTypeConversionErrorResult(e);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ConversionService conversionService = context.getConversionService();
|
||||
if (conversionService != null) {
|
||||
Class targetType;
|
||||
try {
|
||||
targetType = targetExpression.getValueType(context.getTarget());
|
||||
} catch (EvaluationException e) {
|
||||
context.setTargetAccessError(sourceValue, e);
|
||||
return;
|
||||
}
|
||||
if (targetType != null && !targetType.isInstance(targetValue)) {
|
||||
ConversionExecutor typeConverter = conversionService.getConversionExecutor(sourceValue
|
||||
.getClass(), targetType);
|
||||
try {
|
||||
targetValue = typeConverter.execute(sourceValue);
|
||||
} catch (ConversionExecutionException e) {
|
||||
context.setTypeConversionErrorResult(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.binding.mapping.results;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import org.springframework.binding.expression.EvaluationException;
|
||||
import org.springframework.binding.expression.PropertyNotFoundException;
|
||||
import org.springframework.binding.mapping.Result;
|
||||
@@ -65,7 +68,11 @@ public class SourceAccessError extends Result {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("errorCode", getErrorCode()).append("details", error.getMessage())
|
||||
.toString();
|
||||
ToStringCreator creator = new ToStringCreator(this).append("errorCode", getErrorCode());
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
PrintWriter writer = new PrintWriter(stringWriter);
|
||||
error.printStackTrace(writer);
|
||||
creator.append("exceptionStackTrace", stringWriter.toString());
|
||||
return creator.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.binding.mapping.results;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import org.springframework.binding.expression.EvaluationException;
|
||||
import org.springframework.binding.expression.PropertyNotFoundException;
|
||||
import org.springframework.binding.mapping.Result;
|
||||
@@ -69,7 +72,11 @@ public class TargetAccessError extends Result {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("errorCode", getErrorCode()).append("details", error.getMessage())
|
||||
.toString();
|
||||
ToStringCreator creator = new ToStringCreator(this).append("errorCode", getErrorCode());
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
PrintWriter writer = new PrintWriter(stringWriter);
|
||||
error.printStackTrace(writer);
|
||||
creator.append("stackTrace", stringWriter.toString());
|
||||
return creator.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.binding.mapping.results;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import org.springframework.binding.convert.ConversionExecutionException;
|
||||
import org.springframework.binding.mapping.Result;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
@@ -73,7 +76,12 @@ public class TypeConversionError extends Result {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("originalValue", originalValue).append("targetType", targetType)
|
||||
.append("details", exception.getMessage()).toString();
|
||||
ToStringCreator creator = new ToStringCreator(this).append("originalValue", originalValue).append("targetType",
|
||||
targetType);
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
PrintWriter writer = new PrintWriter(stringWriter);
|
||||
exception.printStackTrace(writer);
|
||||
creator.append("exceptionStackTrace", stringWriter.toString());
|
||||
return creator.toString();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user