bind action
attribute mapper enhancements
This commit is contained in:
@@ -117,6 +117,7 @@ public class GenericConversionService implements ConversionService {
|
||||
* Add an alias for given target type.
|
||||
*/
|
||||
public void addAlias(String alias, Class targetType) {
|
||||
Assert.isTrue(!targetType.isPrimitive(), "Primitive types cannot be registered");
|
||||
aliasMap.put(alias, targetType);
|
||||
}
|
||||
|
||||
@@ -137,6 +138,8 @@ public class GenericConversionService implements ConversionService {
|
||||
if (targetClass.isAssignableFrom(sourceClass)) {
|
||||
return new ConversionExecutorImpl(sourceClass, targetClass, new NoOpConverter(sourceClass, targetClass));
|
||||
}
|
||||
sourceClass = convertToWrapperClassIfNecessary(sourceClass);
|
||||
targetClass = convertToWrapperClassIfNecessary(targetClass);
|
||||
Map sourceTargetConverters = findConvertersForSource(sourceClass);
|
||||
Converter converter = findTargetConverter(sourceTargetConverters, targetClass);
|
||||
if (converter != null) {
|
||||
@@ -220,6 +223,25 @@ public class GenericConversionService implements ConversionService {
|
||||
}
|
||||
}
|
||||
|
||||
// subclassing support
|
||||
|
||||
/**
|
||||
* 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
|
||||
* perform the source->target conversion.
|
||||
*/
|
||||
protected Map getSourceClassConverters() {
|
||||
return sourceClassConverters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of known aliases. Each entry key is a String alias and the associated value is either a target
|
||||
* class or a converter.
|
||||
*/
|
||||
protected Map getAliasMap() {
|
||||
return aliasMap;
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private Map findConvertersForSource(Class sourceClass) {
|
||||
@@ -264,22 +286,29 @@ public class GenericConversionService implements ConversionService {
|
||||
return null;
|
||||
}
|
||||
|
||||
// subclassing support
|
||||
|
||||
/**
|
||||
* 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
|
||||
* perform the source->target conversion.
|
||||
*/
|
||||
protected Map getSourceClassConverters() {
|
||||
return sourceClassConverters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of known aliases. Each entry key is a String alias and the associated value is either a target
|
||||
* class or a converter.
|
||||
*/
|
||||
protected Map getAliasMap() {
|
||||
return aliasMap;
|
||||
private Class convertToWrapperClassIfNecessary(Class targetType) {
|
||||
if (targetType.isPrimitive()) {
|
||||
if (targetType.equals(int.class)) {
|
||||
return Integer.class;
|
||||
} else if (targetType.equals(short.class)) {
|
||||
return Short.class;
|
||||
} else if (targetType.equals(long.class)) {
|
||||
return Long.class;
|
||||
} else if (targetType.equals(float.class)) {
|
||||
return Float.class;
|
||||
} else if (targetType.equals(double.class)) {
|
||||
return Double.class;
|
||||
} else if (targetType.equals(byte.class)) {
|
||||
return Byte.class;
|
||||
} else if (targetType.equals(boolean.class)) {
|
||||
return Boolean.class;
|
||||
} else if (targetType.equals(char.class)) {
|
||||
return Character.class;
|
||||
} else {
|
||||
throw new IllegalStateException("Should never happen - primitive type is not a primitive?");
|
||||
}
|
||||
} else {
|
||||
return targetType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public class TextToNumber extends AbstractFormattingConverter {
|
||||
}
|
||||
|
||||
public Class[] getTargetClasses() {
|
||||
return new Class[] { Integer.class, Short.class, Byte.class, Long.class, Float.class, Double.class,
|
||||
return new Class[] { Integer.class, Short.class, Long.class, Float.class, Double.class, Byte.class,
|
||||
BigInteger.class, BigDecimal.class };
|
||||
}
|
||||
|
||||
|
||||
@@ -33,9 +33,24 @@ public interface Expression {
|
||||
|
||||
/**
|
||||
* Set this expression in the provided context to the value provided.
|
||||
* @param context the context to apply this value to
|
||||
* @param context the context to set this value to
|
||||
* @param value the new value to be set
|
||||
* @throws EvaluationException an exception occurred during evaluation
|
||||
*/
|
||||
public void setValue(Object context, Object value) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Returns the most general type that can be passed to the {@link #setValue(Object, Object)} method for the given
|
||||
* context.
|
||||
* @param context the context of expression evaluation
|
||||
* @return the most general type of value that can be set
|
||||
*/
|
||||
public Class getValueType(Object context);
|
||||
|
||||
/**
|
||||
* Returns the original string used to create this expression, unmodified.
|
||||
* @return the original expression string
|
||||
*/
|
||||
public String getExpressionString();
|
||||
|
||||
}
|
||||
@@ -41,7 +41,11 @@ public class DefaultELResolver extends CompositeELResolver {
|
||||
}
|
||||
|
||||
public Class getType(ELContext context, Object base, Object property) {
|
||||
return super.getType(context, base, property);
|
||||
if (base == null) {
|
||||
return super.getType(context, target, property);
|
||||
} else {
|
||||
return super.getType(context, base, property);
|
||||
}
|
||||
}
|
||||
|
||||
public Object getValue(ELContext context, Object base, Object property) {
|
||||
|
||||
@@ -21,16 +21,21 @@ public class ELExpression implements Expression {
|
||||
|
||||
private ValueExpression valueExpression;
|
||||
|
||||
private boolean template;
|
||||
|
||||
/**
|
||||
* Creates a new el expression
|
||||
* @param factory the el context factory for creating the EL context that will be used during expression evaluation
|
||||
* @param valueExpression the value expression to evaluate
|
||||
* @param template whether or not this expression is a template expression; if not it was parsed as an implict eval
|
||||
* expression (without delimiters)
|
||||
*/
|
||||
public ELExpression(ELContextFactory factory, ValueExpression valueExpression) {
|
||||
public ELExpression(ELContextFactory factory, ValueExpression valueExpression, boolean template) {
|
||||
Assert.notNull(factory, "The ELContextFactory is required to evaluate EL expressions");
|
||||
Assert.notNull(valueExpression, "The EL value expression is required for evaluation");
|
||||
this.elContextFactory = factory;
|
||||
this.valueExpression = valueExpression;
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
public Object getValue(Object context) throws EvaluationException {
|
||||
@@ -58,6 +63,24 @@ public class ELExpression implements Expression {
|
||||
}
|
||||
}
|
||||
|
||||
public Class getValueType(Object context) {
|
||||
ELContext ctx = elContextFactory.getELContext(context);
|
||||
try {
|
||||
return valueExpression.getType(ctx);
|
||||
} catch (ELException ex) {
|
||||
throw new EvaluationException(new EvaluationAttempt(this, context), ex);
|
||||
}
|
||||
}
|
||||
|
||||
public String getExpressionString() {
|
||||
if (template) {
|
||||
return valueExpression.getExpressionString();
|
||||
} else {
|
||||
String rawExpressionString = valueExpression.getExpressionString();
|
||||
return rawExpressionString.substring("#{".length(), rawExpressionString.length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return valueExpression.hashCode();
|
||||
}
|
||||
|
||||
@@ -56,20 +56,21 @@ public class ELExpressionParser implements ExpressionParser {
|
||||
context = NullParserContext.INSTANCE;
|
||||
}
|
||||
if (context.isTemplate()) {
|
||||
return parseTemplate(expressionString, context);
|
||||
return parseExpressionInternal(expressionString, context, true);
|
||||
} else {
|
||||
assertNotDelimited(expressionString);
|
||||
assertHasText(expressionString);
|
||||
return parseTemplate("#{" + expressionString + "}", context);
|
||||
return parseExpressionInternal("#{" + expressionString + "}", context, false);
|
||||
}
|
||||
}
|
||||
|
||||
private Expression parseTemplate(String expressionString, ParserContext context) throws ParserException {
|
||||
private Expression parseExpressionInternal(String expressionString, ParserContext context, boolean template)
|
||||
throws ParserException {
|
||||
Assert.notNull(expressionString, "The expression string to parse is required");
|
||||
try {
|
||||
ValueExpression expression = parseValueExpression(expressionString, context);
|
||||
ELContextFactory contextFactory = getContextFactory(context.getEvaluationContextType(), expressionString);
|
||||
return new ELExpression(contextFactory, expression);
|
||||
return new ELExpression(contextFactory, expression, template);
|
||||
} catch (ELException e) {
|
||||
throw new ParserException(expressionString, e);
|
||||
}
|
||||
|
||||
@@ -52,13 +52,18 @@ class OgnlExpression implements Expression {
|
||||
private Class expectedResultType;
|
||||
|
||||
/**
|
||||
* Creates a new OGNL expression.
|
||||
* @param expression the parsed expression
|
||||
* The original expression string.
|
||||
*/
|
||||
public OgnlExpression(Object expression, Map variableExpressions, Class expectedResultType) {
|
||||
private String expressionString;
|
||||
|
||||
/**
|
||||
* Creates a new OGNL expression.
|
||||
*/
|
||||
public OgnlExpression(Object expression, Map variableExpressions, Class expectedResultType, String expressionString) {
|
||||
this.expression = expression;
|
||||
this.variableExpressions = variableExpressions;
|
||||
this.expectedResultType = expectedResultType;
|
||||
this.expressionString = expressionString;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
@@ -98,6 +103,23 @@ class OgnlExpression implements Expression {
|
||||
}
|
||||
}
|
||||
|
||||
public Class getValueType(Object context) {
|
||||
try {
|
||||
if (Ognl.isSimpleProperty(expression)) {
|
||||
// TODO
|
||||
throw new UnsupportedOperationException("Not yet implemented - in progress");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (OgnlException e) {
|
||||
throw new EvaluationException(new EvaluationAttempt(this, context), e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getExpressionString() {
|
||||
return expressionString;
|
||||
}
|
||||
|
||||
private Map getVariables(Object context) {
|
||||
if (variableExpressions == null) {
|
||||
return Collections.EMPTY_MAP;
|
||||
|
||||
@@ -244,7 +244,7 @@ public class OgnlExpressionParser implements ExpressionParser {
|
||||
}
|
||||
try {
|
||||
return new OgnlExpression(Ognl.parseExpression(expressionString), parseVariableExpressions(context
|
||||
.getExpressionVariables()), context.getExpectedEvaluationResultType());
|
||||
.getExpressionVariables()), context.getExpectedEvaluationResultType(), expressionString);
|
||||
} catch (OgnlException e) {
|
||||
throw new ParserException(expressionString, e);
|
||||
}
|
||||
|
||||
@@ -19,4 +19,12 @@ public abstract class AbstractGetValueExpression implements Expression {
|
||||
throw new UnsupportedOperationException("Setting this expression's value is not supported");
|
||||
}
|
||||
|
||||
public Class getValueType(Object context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getExpressionString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,6 +60,14 @@ public class CollectionAddingExpression implements Expression {
|
||||
}
|
||||
}
|
||||
|
||||
public Class getValueType(Object context) {
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
public String getExpressionString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("collectionExpression", collectionExpression).toString();
|
||||
}
|
||||
|
||||
@@ -54,6 +54,14 @@ public class CompositeStringExpression implements Expression {
|
||||
"Cannot set a composite string expression value"));
|
||||
}
|
||||
|
||||
public Class getValueType(Object context) {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
public String getExpressionString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("expressions", expressions).toString();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,14 @@ public class LiteralExpression implements Expression {
|
||||
+ "If so, should the expression string be enclosed in eval delimiters?"));
|
||||
}
|
||||
|
||||
public Class getValueType(Object context) {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
public String getExpressionString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "literal('" + literal + "')";
|
||||
}
|
||||
|
||||
@@ -55,14 +55,22 @@ public final class StaticExpression implements Expression {
|
||||
return ObjectUtils.nullSafeEquals(value, other.value);
|
||||
}
|
||||
|
||||
public Object getValue(Object target) throws EvaluationException {
|
||||
public Object getValue(Object context) throws EvaluationException {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Object target, Object value) throws EvaluationException {
|
||||
public void setValue(Object context, Object value) throws EvaluationException {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Class getValueType(Object context) {
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
public String getExpressionString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.text.ParseException;
|
||||
|
||||
import org.springframework.binding.format.Formatter;
|
||||
import org.springframework.binding.format.InvalidFormatException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -29,38 +28,29 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public abstract class AbstractFormatter implements Formatter {
|
||||
|
||||
/**
|
||||
* Does this formatter allow empty values?
|
||||
*/
|
||||
private boolean allowEmpty = true;
|
||||
|
||||
/**
|
||||
* Constructs a formatter.
|
||||
*/
|
||||
protected AbstractFormatter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a formatter.
|
||||
* @param allowEmpty allow formatting of empty (null or blank) values?
|
||||
*/
|
||||
protected AbstractFormatter(boolean allowEmpty) {
|
||||
this.allowEmpty = allowEmpty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow formatting of empty (null or blank) values?
|
||||
*/
|
||||
public boolean isAllowEmpty() {
|
||||
return allowEmpty;
|
||||
}
|
||||
|
||||
public final String formatValue(Object value) {
|
||||
if (allowEmpty && isEmpty(value)) {
|
||||
if (isEmpty(value)) {
|
||||
return getEmptyFormattedValue();
|
||||
} else {
|
||||
return doFormatValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public final Object parseValue(String formattedString, Class targetClass) throws InvalidFormatException {
|
||||
try {
|
||||
if (isEmpty(formattedString)) {
|
||||
return getEmptyValue();
|
||||
}
|
||||
return doParseValue(formattedString, targetClass);
|
||||
} catch (ParseException ex) {
|
||||
throw new InvalidFormatException(formattedString, getExpectedFormat(targetClass), ex);
|
||||
}
|
||||
Assert.isTrue(!isEmpty(value), "Object to format cannot be empty");
|
||||
return doFormatValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,22 +67,12 @@ public abstract class AbstractFormatter implements Formatter {
|
||||
return "";
|
||||
}
|
||||
|
||||
public final Object parseValue(String formattedString, Class targetClass) throws InvalidFormatException {
|
||||
try {
|
||||
if (allowEmpty && isEmpty(formattedString)) {
|
||||
return getEmptyValue();
|
||||
}
|
||||
return doParseValue(formattedString, targetClass);
|
||||
} catch (ParseException ex) {
|
||||
throw new InvalidFormatException(formattedString, getExpectedFormat(targetClass), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method subclasses should override to encapsulate parsing logic.
|
||||
* @param formattedString the formatted string to parse
|
||||
* @param targetClass the target class to convert the formatted value to
|
||||
* @return the parsed value
|
||||
* @throws InvalidFormatException an exception occured parsing
|
||||
* @throws InvalidFormatException an exception occurred parsing
|
||||
* @throws ParseException when parse exceptions occur
|
||||
*/
|
||||
protected abstract Object doParseValue(String formattedString, Class targetClass) throws InvalidFormatException,
|
||||
|
||||
@@ -38,16 +38,6 @@ public class DateFormatter extends AbstractFormatter {
|
||||
this.dateFormat = dateFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a date formatter that will delegate to the specified date format.
|
||||
* @param dateFormat the date format to use
|
||||
* @param allowEmpty should this formatter allow empty input arguments?
|
||||
*/
|
||||
public DateFormatter(DateFormat dateFormat, boolean allowEmpty) {
|
||||
super(allowEmpty);
|
||||
this.dateFormat = dateFormat;
|
||||
}
|
||||
|
||||
// convert from date to string
|
||||
protected String doFormatValue(Object date) {
|
||||
return dateFormat.format((Date) date);
|
||||
|
||||
@@ -36,14 +36,6 @@ public class LabeledEnumFormatter extends AbstractFormatter {
|
||||
public LabeledEnumFormatter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new LabeledEnum formatter.
|
||||
* @param allowEmpty should this formatter allow empty input arguments?
|
||||
*/
|
||||
public LabeledEnumFormatter(boolean allowEmpty) {
|
||||
super(allowEmpty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the LabeledEnumResolver used. Defaults to {@link StaticLabeledEnumResolver}.
|
||||
*/
|
||||
@@ -58,13 +50,7 @@ public class LabeledEnumFormatter extends AbstractFormatter {
|
||||
}
|
||||
|
||||
protected Object doParseValue(String formattedString, Class targetClass) throws IllegalArgumentException {
|
||||
LabeledEnum labeledEnum = labeledEnumResolver.getLabeledEnumByLabel(targetClass, formattedString);
|
||||
if (!isAllowEmpty()) {
|
||||
Assert.notNull(labeledEnum, "The label '" + formattedString
|
||||
+ "' did not map to a valid enum instance for type " + targetClass);
|
||||
Assert.isInstanceOf(targetClass, labeledEnum);
|
||||
}
|
||||
return labeledEnum;
|
||||
return labeledEnumResolver.getLabeledEnumByLabel(targetClass, formattedString);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,13 +31,6 @@ public class NumberFormatter extends AbstractFormatter {
|
||||
|
||||
private NumberFormat numberFormat;
|
||||
|
||||
/**
|
||||
* Default constructor. The formatter will use "toString" when formatting a value and "valueOf" when parsing a
|
||||
* value.
|
||||
*/
|
||||
public NumberFormatter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new number formatter.
|
||||
* @param numberFormat the number format to use
|
||||
@@ -46,16 +39,6 @@ public class NumberFormatter extends AbstractFormatter {
|
||||
this.numberFormat = numberFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new number formatter.
|
||||
* @param numberFormat the number format to use
|
||||
* @param allowEmpty should this formatter allow empty input arguments?
|
||||
*/
|
||||
public NumberFormatter(NumberFormat numberFormat, boolean allowEmpty) {
|
||||
super(allowEmpty);
|
||||
this.numberFormat = numberFormat;
|
||||
}
|
||||
|
||||
protected String doFormatValue(Object number) {
|
||||
if (this.numberFormat != null) {
|
||||
// use NumberFormat for rendering value
|
||||
@@ -67,12 +50,11 @@ public class NumberFormatter extends AbstractFormatter {
|
||||
}
|
||||
|
||||
protected Object doParseValue(String text, Class targetClass) throws IllegalArgumentException {
|
||||
// use given NumberFormat for parsing text
|
||||
if (this.numberFormat != null) {
|
||||
// use given NumberFormat for parsing text
|
||||
return NumberUtils.parseNumber(text, targetClass, this.numberFormat);
|
||||
}
|
||||
// use default valueOf methods for parsing text
|
||||
else {
|
||||
} else {
|
||||
// use default valueOf methods for parsing text
|
||||
return NumberUtils.parseNumber(text, targetClass);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,11 @@ public class SimpleFormatterFactory extends AbstractFormatterFactory {
|
||||
}
|
||||
|
||||
public Formatter getNumberFormatter(Class numberClass) {
|
||||
return new NumberFormatter(NumberFormat.getNumberInstance(getLocale()));
|
||||
if (numberClass.equals(Integer.class) || numberClass.equals(int.class)) {
|
||||
return new NumberFormatter(NumberFormat.getIntegerInstance(getLocale()));
|
||||
} else {
|
||||
return new NumberFormatter(NumberFormat.getNumberInstance(getLocale()));
|
||||
}
|
||||
}
|
||||
|
||||
public Formatter getCurrencyFormatter() {
|
||||
|
||||
@@ -30,6 +30,7 @@ public interface AttributeMapper {
|
||||
* @param source the source
|
||||
* @param target the target
|
||||
* @param context the mapping context
|
||||
* @throws AttributeMappingException if errors occurred during the mapping process
|
||||
*/
|
||||
public void map(Object source, Object target, MappingContext context);
|
||||
public void map(Object source, Object target, MappingContext context) throws AttributeMappingException;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.binding.mapping;
|
||||
|
||||
public class AttributeMappingException extends RuntimeException {
|
||||
|
||||
}
|
||||
@@ -38,10 +38,10 @@ public class DefaultAttributeMapper implements AttributeMapper {
|
||||
|
||||
/**
|
||||
* Add a mapping to this mapper.
|
||||
* @param mapping the mapping to add (as an AttributeMapper)
|
||||
* @param mapping the mapping to add
|
||||
* @return this, to support convenient call chaining
|
||||
*/
|
||||
public DefaultAttributeMapper addMapping(AttributeMapper mapping) {
|
||||
public DefaultAttributeMapper addMapping(Mapping mapping) {
|
||||
mappings.add(mapping);
|
||||
return this;
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public class DefaultAttributeMapper implements AttributeMapper {
|
||||
* Add a set of mappings.
|
||||
* @param mappings the mappings
|
||||
*/
|
||||
public void addMappings(AttributeMapper[] mappings) {
|
||||
public void addMappings(Mapping[] mappings) {
|
||||
if (mappings == null) {
|
||||
return;
|
||||
}
|
||||
@@ -61,18 +61,25 @@ public class DefaultAttributeMapper implements AttributeMapper {
|
||||
* Returns this mapper's list of mappings.
|
||||
* @return the list of mappings
|
||||
*/
|
||||
public AttributeMapper[] getMappings() {
|
||||
return (AttributeMapper[]) mappings.toArray(new AttributeMapper[mappings.size()]);
|
||||
public Mapping[] getMappings() {
|
||||
return (Mapping[]) mappings.toArray(new Mapping[mappings.size()]);
|
||||
}
|
||||
|
||||
public void map(Object source, Object target, MappingContext context) {
|
||||
public void map(Object source, Object target, MappingContext context) throws AttributeMappingException {
|
||||
boolean mappingFailure = false;
|
||||
if (mappings != null) {
|
||||
Iterator it = mappings.iterator();
|
||||
while (it.hasNext()) {
|
||||
AttributeMapper mapping = (AttributeMapper) it.next();
|
||||
mapping.map(source, target, context);
|
||||
Mapping mapping = (Mapping) it.next();
|
||||
boolean result = mapping.map(source, target, context);
|
||||
if (!result && !mappingFailure) {
|
||||
mappingFailure = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mappingFailure) {
|
||||
throw new AttributeMappingException();
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
@@ -17,10 +17,14 @@ package org.springframework.binding.mapping;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.binding.convert.ConversionException;
|
||||
import org.springframework.binding.convert.ConversionExecutor;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.message.MessageBuilder;
|
||||
import org.springframework.binding.message.MessageResolver;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* A single mapping definition, encapsulating the information necessary to map the result of evaluating an expression on
|
||||
@@ -28,7 +32,7 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class Mapping implements AttributeMapper {
|
||||
public class Mapping {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(Mapping.class);
|
||||
|
||||
@@ -85,29 +89,54 @@ public class Mapping implements AttributeMapper {
|
||||
* @param source The source data structure
|
||||
* @param target The target data structure
|
||||
*/
|
||||
public void map(Object source, Object target, MappingContext context) {
|
||||
// get source value
|
||||
public boolean map(Object source, Object target, MappingContext context) {
|
||||
Assert.notNull(source, "The source to map from is required");
|
||||
Assert.notNull(target, "The target to map to is required");
|
||||
Assert.notNull(context, "The mapping context is required");
|
||||
Object sourceValue = sourceExpression.getValue(source);
|
||||
if (sourceValue == null) {
|
||||
if (required) {
|
||||
throw new RequiredMappingException("This mapping is required; evaluation of expression '"
|
||||
+ sourceExpression + "' against source of type [" + source.getClass()
|
||||
+ "] must return a non-null value");
|
||||
} else {
|
||||
// source expression returned no value, simply abort mapping
|
||||
return;
|
||||
if (required && sourceValue == null || isEmptyString(sourceValue)) {
|
||||
String defaultText = "'" + targetExpression.getExpressionString() + "' is required";
|
||||
MessageResolver message = new MessageBuilder().error().source(targetExpression.getExpressionString())
|
||||
.codes(createMessageCodes("required", target, targetExpression)).defaultText(defaultText).build();
|
||||
context.getMessageContext().addMessage(message);
|
||||
return false;
|
||||
}
|
||||
Object targetValue;
|
||||
if (sourceValue != null && typeConverter != null) {
|
||||
try {
|
||||
targetValue = typeConverter.execute(sourceValue);
|
||||
} catch (ConversionException e) {
|
||||
String defaultText = "The '" + targetExpression.getExpressionString() + "' value is the wrong type";
|
||||
MessageResolver message = new MessageBuilder().error().source(targetExpression.getExpressionString())
|
||||
.codes(createMessageCodes("typeMismatch", target, targetExpression)).defaultText(defaultText)
|
||||
.build();
|
||||
context.getMessageContext().addMessage(message);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
targetValue = sourceValue;
|
||||
}
|
||||
Object targetValue = sourceValue;
|
||||
if (typeConverter != null) {
|
||||
targetValue = typeConverter.execute(sourceValue);
|
||||
}
|
||||
// set target value
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Mapping '" + sourceExpression + "' value [" + sourceValue + "] to target property '"
|
||||
+ targetExpression + "'; setting property value to [" + targetValue + "]");
|
||||
logger.debug("Mapping '" + sourceExpression + "' value [" + sourceValue + "] to target '"
|
||||
+ targetExpression + "'; setting target value to [" + targetValue + "]");
|
||||
}
|
||||
targetExpression.setValue(target, targetValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isEmptyString(Object sourceValue) {
|
||||
if (sourceValue instanceof CharSequence) {
|
||||
return ((CharSequence) sourceValue).length() == 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private String[] createMessageCodes(String errorCode, Object target, Expression targetExpression) {
|
||||
String[] codes = new String[2];
|
||||
codes[0] = ClassUtils.getShortName(target.getClass()) + "." + targetExpression.getExpressionString();
|
||||
codes[1] = errorCode;
|
||||
return codes;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.binding.mapping;
|
||||
|
||||
import org.springframework.binding.message.MessageContext;
|
||||
|
||||
/**
|
||||
* A context object with two main responsibities:
|
||||
* <ol>
|
||||
@@ -27,4 +29,10 @@ package org.springframework.binding.mapping;
|
||||
*/
|
||||
public interface MappingContext {
|
||||
|
||||
/**
|
||||
* Returns the message context to use to record errors during the mapping process.
|
||||
* @return the message context
|
||||
*/
|
||||
public MessageContext getMessageContext();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.springframework.binding.mapping;
|
||||
|
||||
import org.springframework.binding.message.MessageContext;
|
||||
|
||||
public class MappingContextImpl implements MappingContext {
|
||||
|
||||
private MessageContext messageContext;
|
||||
|
||||
public MappingContextImpl(MessageContext messageContext) {
|
||||
this.messageContext = messageContext;
|
||||
}
|
||||
|
||||
public MessageContext getMessageContext() {
|
||||
return messageContext;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user