fixes for known 2.0.0 bugs reported since release - see changelog

This commit is contained in:
Keith Donald
2008-05-06 07:05:18 +00:00
parent c9179d9d21
commit 797e203f8f
54 changed files with 840 additions and 397 deletions

View File

@@ -16,85 +16,26 @@
package org.springframework.binding.convert;
/**
* Base class for exceptions thrown by the type conversion system.
* Base class for exceptions thrown by the convert system.
*
* @author Keith Donald
*/
public class ConversionException extends RuntimeException {
/**
* The source type we tried to convert from
*/
private Class sourceClass;
/**
* The target type we tried to convert to.
*/
private Class targetClass;
/**
* The value we tried to convert. Transient because we cannot guarantee that the value is Serializable.
*/
private transient Object value;
public abstract class ConversionException extends RuntimeException {
/**
* Creates a new conversion exception.
* @param value the value we tried to convert
* @param targetClass the target type
* @param cause underlying cause of this exception
* @param message the exception message
* @param cause the cause
*/
public ConversionException(Object value, Class targetClass, Throwable cause) {
super("Unable to convert value '" + value + "' of type '" + (value != null ? value.getClass().getName() : null)
+ "' to class '" + targetClass.getName() + "'", cause);
this.value = value;
this.targetClass = targetClass;
}
/**
* Creates a new conversion exception.
* @param sourceClass the source type
* @param value the value we tried to convert
* @param targetClass the target type
* @param message a descriptive message
*/
public ConversionException(Class sourceClass, Object value, Class targetClass, String message, Throwable cause) {
public ConversionException(String message, Throwable cause) {
super(message, cause);
this.sourceClass = sourceClass;
this.value = value;
this.targetClass = targetClass;
}
/**
* Creates a new conversion exception.
* @param sourceClass the source type
* @param targetClass the target type
* @param message a descriptive message
* @param message the exception message
*/
public ConversionException(Class sourceClass, Class targetClass, String message) {
public ConversionException(String message) {
super(message);
this.sourceClass = sourceClass;
this.targetClass = targetClass;
}
/**
* Returns the source type.
*/
public Class getSourceClass() {
return sourceClass;
}
/**
* Returns the target type.
*/
public Class getTargetClass() {
return targetClass;
}
/**
* Returns the value we tried to convert.
*/
public Object getValue() {
return value;
}
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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.convert;
/**
* Thrown when an attempt to execute a type conversion fails.
*
* @author Keith Donald
*/
public class ConversionExecutionException extends ConversionException {
/**
* The value we tried to convert. Transient because we cannot guarantee that the value is Serializable.
*/
private transient Object value;
/**
* The source type we tried to convert the value from.
*/
private Class sourceClass;
/**
* The target type we tried to convert the value to.
*/
private Class targetClass;
/**
* Creates a new conversion exception.
* @param value the value we tried to convert
* @param sourceClass the value's original type
* @param targetClass the value's target type
* @param cause the cause of the conversion failure
*/
public ConversionExecutionException(Object value, Class sourceClass, Class targetClass, Throwable cause) {
super(defaultMessage(value, sourceClass, targetClass, cause), cause);
this.value = value;
this.sourceClass = sourceClass;
this.targetClass = targetClass;
}
/**
* Creates a new conversion exception.
* @param value the value we tried to convert
* @param sourceClass the value's original type
* @param targetClass the value's target type
* @param message a descriptive message of what went wrong.
*/
public ConversionExecutionException(Object value, Class sourceClass, Class targetClass, String message) {
super(message);
this.value = value;
this.sourceClass = sourceClass;
this.targetClass = targetClass;
}
/**
* Returns the actual value we tried to convert, an instance of {@link #getSourceClass()}.
*/
public Object getValue() {
return value;
}
/**
* Returns the source type we tried to convert the value from.
*/
public Class getSourceClass() {
return sourceClass;
}
/**
* Returns the target type we tried to convert the value to.
*/
public Class getTargetClass() {
return targetClass;
}
private static String defaultMessage(Object value, Class sourceClass, Class targetClass, Throwable cause) {
return "Unable to convert value " + value + " from type '" + sourceClass.getName() + "' to type '"
+ targetClass.getName() + "; reason = '" + cause.getMessage() + "'";
}
}

View File

@@ -40,13 +40,13 @@ public interface ConversionExecutor {
* Execute the conversion for the provided source object.
* @param source the source object to convert
*/
public Object execute(Object source) throws ConversionException;
public Object execute(Object source) throws ConversionExecutionException;
/**
* Execute the conversion for the provided source object.
* @param source the source object to convert
* @param context the conversion context, useful for influencing the behavior of the converter
*/
public Object execute(Object source, Object context) throws ConversionException;
public Object execute(Object source, Object context) throws ConversionExecutionException;
}

View File

@@ -0,0 +1,55 @@
/*
* 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.convert;
/**
* Thrown when a conversion executor could not be found in a conversion service.
*
* @see ConversionService#getConversionExecutor(Class, Class)
* @author Keith Donald
*/
public class ConversionExecutorNotFoundException extends ConversionException {
private Class sourceClass;
private Class targetClass;
/**
* Creates a new conversion executor not found exception.
* @param sourceClass the source type requested to convert from
* @param targetClass the target type requested to convert to
* @param message a descriptive message
*/
public ConversionExecutorNotFoundException(Class sourceClass, Class targetClass, String message) {
super(message);
this.sourceClass = sourceClass;
this.targetClass = targetClass;
}
/**
* Returns the source type requested to convert from.
*/
public Class getSourceClass() {
return sourceClass;
}
/**
* Returns the target type requested to convert to.
*/
public Class getTargetClass() {
return targetClass;
}
}

View File

@@ -33,9 +33,10 @@ public interface ConversionService {
* The returned ConversionExecutor is thread-safe and may safely be cached for use in client code.
* @param sourceClass the source class to convert from
* @param targetClass the target class to convert to
* @return the executor that can execute instance conversion, never null
* @throws ConversionException an exception occurred retrieving a converter for the source-to-target pair
* @return the executor that can execute instance type conversion, never null
* @throws ConversionExecutorNotFoundException when no suitable conversion executor could be found
*/
public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass) throws ConversionException;
public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass)
throws ConversionExecutorNotFoundException;
}

View File

@@ -50,8 +50,8 @@ public interface Converter {
* <code>targetClasses</code>
* @param context an optional conversion context that may be used to influence the conversion process
* @return the converted object, an instance of the target type
* @throws ConversionException an exception occurred during the type conversion
* @throws Exception an exception occurred performing the conversion
*/
public Object convert(Object source, Class targetClass, Object context) throws ConversionException;
public Object convert(Object source, Class targetClass, Object context) throws Exception;
}

View File

@@ -1,88 +0,0 @@
/*
* 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.convert.converters;
import org.springframework.binding.convert.ConversionException;
import org.springframework.binding.convert.Converter;
/**
* Base class for converters provided as a convenience to implementors.
*
* @author Keith Donald
*/
public abstract class AbstractConverter implements Converter {
/**
* Convenience convert method that converts the provided source to the first target object supported by this
* converter. Useful when a converter only supports conversion to a single target.
* @param source the source to convert
* @return the converted object
* @throws ConversionException an exception occured converting the source value
*/
public Object convert(Object source) throws ConversionException {
return convert(source, getTargetClasses()[0], null);
}
/**
* Convenience convert method that converts the provided source to the target class specified with an empty
* conversion context.
* @param source the source to convert
* @param targetClass the target class to convert the source to, must be one of the supported
* <code>targetClasses</code>
* @return the converted object
* @throws ConversionException an exception occured converting the source value
*/
public Object convert(Object source, Class targetClass) throws ConversionException {
return convert(source, targetClass, null);
}
/**
* Convenience convert method that converts the provided source to the first target object supported by this
* converter. Useful when a converter only supports conversion to a single target.
* @param source the source to convert
* @param context the conversion context, useful for influencing the behavior of the converter
* @return the converted object
* @throws ConversionException an exception occured converting the source value
*/
public Object convert(Object source, Object context) throws ConversionException {
return convert(source, getTargetClasses()[0], context);
}
public Object convert(Object source, Class targetClass, Object context) throws ConversionException {
try {
return doConvert(source, targetClass, context);
} catch (ConversionException e) {
throw e;
} catch (Throwable e) {
// wrap in a ConversionException
if (targetClass == null) {
targetClass = getTargetClasses()[0];
}
throw new ConversionException(source, targetClass, e);
}
}
/**
* Template method subclasses should override to actually perform the type conversion.
* @param source the source to convert from
* @param targetClass the target type to convert to
* @param context an optional conversion context that may be used to influence the conversion process, could be null
* @return the converted source value
* @throws Exception an exception occurred, will be wrapped in a conversion exception if necessary
*/
protected abstract Object doConvert(Object source, Class targetClass, Object context) throws Exception;
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.binding.convert.converters;
import org.springframework.binding.convert.Converter;
import org.springframework.util.StringUtils;
/**
@@ -22,7 +23,7 @@ import org.springframework.util.StringUtils;
*
* @author Keith Donald
*/
public class TextToBoolean extends AbstractConverter {
public class TextToBoolean implements Converter {
private static final String VALUE_TRUE = "true";
@@ -70,7 +71,7 @@ public class TextToBoolean extends AbstractConverter {
return new Class[] { Boolean.class };
}
protected Object doConvert(Object source, Class targetClass, Object context) throws Exception {
public Object convert(Object source, Class targetClass, Object context) throws Exception {
String text = (String) source;
if (!StringUtils.hasText(text)) {
return null;

View File

@@ -20,6 +20,7 @@ import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import org.springframework.binding.convert.Converter;
import org.springframework.core.enums.LabeledEnum;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@@ -29,7 +30,7 @@ import org.springframework.util.StringUtils;
*
* @author Keith Donald
*/
public class TextToClass extends AbstractConverter {
public class TextToClass implements Converter {
private Map aliasMap = new HashMap();
@@ -52,7 +53,7 @@ public class TextToClass extends AbstractConverter {
return new Class[] { Class.class };
}
protected Object doConvert(Object source, Class targetClass, Object context) throws Exception {
public Object convert(Object source, Class targetClass, Object context) throws Exception {
String text = (String) source;
if (StringUtils.hasText(text)) {
text = text.trim();

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.binding.convert.converters;
import org.springframework.binding.convert.Converter;
import org.springframework.core.enums.LabeledEnum;
import org.springframework.core.enums.LabeledEnumResolver;
import org.springframework.core.enums.StaticLabeledEnumResolver;
@@ -24,7 +25,7 @@ import org.springframework.core.enums.StaticLabeledEnumResolver;
*
* @author Keith Donald
*/
public class TextToLabeledEnum extends AbstractConverter {
public class TextToLabeledEnum implements Converter {
private LabeledEnumResolver labeledEnumResolver = StaticLabeledEnumResolver.instance();
@@ -36,7 +37,7 @@ public class TextToLabeledEnum extends AbstractConverter {
return new Class[] { LabeledEnum.class };
}
protected Object doConvert(Object source, Class targetClass, Object context) throws Exception {
public Object convert(Object source, Class targetClass, Object context) throws Exception {
String label = (String) source;
return labeledEnumResolver.getLabeledEnumByLabel(targetClass, label);
}

View File

@@ -18,6 +18,7 @@ package org.springframework.binding.convert.converters;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.springframework.binding.convert.Converter;
import org.springframework.util.NumberUtils;
/**
@@ -26,7 +27,7 @@ import org.springframework.util.NumberUtils;
*
* @author Keith Donald
*/
public class TextToNumber extends AbstractConverter {
public class TextToNumber implements Converter {
public Class[] getSourceClasses() {
return new Class[] { String.class };
@@ -37,7 +38,7 @@ public class TextToNumber extends AbstractConverter {
BigInteger.class, BigDecimal.class };
}
protected Object doConvert(Object source, Class targetClass, Object context) throws Exception {
public Object convert(Object source, Class targetClass, Object context) throws Exception {
return NumberUtils.parseNumber((String) source, targetClass);
}

View File

@@ -20,8 +20,8 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import org.springframework.binding.convert.ConversionException;
import org.springframework.binding.convert.ConversionExecutor;
import org.springframework.binding.convert.ConversionExecutorNotFoundException;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.convert.Converter;
import org.springframework.util.Assert;
@@ -80,10 +80,11 @@ public class GenericConversionService implements ConversionService {
}
}
public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass) throws ConversionException {
public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass)
throws ConversionExecutorNotFoundException {
Assert.notNull(sourceClass, "The source class to convert from is required");
Assert.notNull(targetClass, "The target class to convert to is required");
if (this.sourceClassConverters == null || this.sourceClassConverters.isEmpty()) {
if (sourceClassConverters == null || sourceClassConverters.isEmpty()) {
throw new IllegalStateException("No converters have been added to this service's registry");
}
sourceClass = convertToWrapperClassIfNecessary(sourceClass);
@@ -101,9 +102,9 @@ public class GenericConversionService implements ConversionService {
// try the parent
return parent.getConversionExecutor(sourceClass, targetClass);
} else {
throw new ConversionException(sourceClass, targetClass,
"No converter registered to convert from sourceClass '" + sourceClass + "' to target class '"
+ targetClass + "'");
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
"No ConversionExecutor found for converting from sourceClass '" + sourceClass.getName()
+ "' to target class '" + targetClass.getName() + "'");
}
}
}

View File

@@ -15,15 +15,14 @@
*/
package org.springframework.binding.convert.service;
import org.springframework.binding.convert.converters.AbstractConverter;
import org.springframework.binding.convert.Converter;
/**
* Package private converter that is a "no op".
*
* @author Keith Donald
*/
class NoOpConverter extends AbstractConverter {
class NoOpConverter implements Converter {
private Class sourceClass;
@@ -37,7 +36,7 @@ class NoOpConverter extends AbstractConverter {
this.targetClass = targetClass;
}
protected Object doConvert(Object source, Class targetClass, Object context) throws Exception {
public Object convert(Object source, Class targetClass, Object context) throws Exception {
return source;
}

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.binding.convert.service;
import org.springframework.binding.convert.ConversionException;
import org.springframework.binding.convert.ConversionExecutionException;
import org.springframework.binding.convert.ConversionExecutor;
import org.springframework.binding.convert.ConversionService;
import org.springframework.util.Assert;
@@ -62,11 +62,11 @@ public class RuntimeBindingConversionExecutor implements ConversionExecutor {
return targetClass.hashCode();
}
public Object execute(Object source) throws ConversionException {
public Object execute(Object source) throws ConversionExecutionException {
return execute(source, null);
}
public Object execute(Object source, Object context) throws ConversionException {
public Object execute(Object source, Object context) throws ConversionExecutionException {
return conversionService.getConversionExecutor(source.getClass(), targetClass).execute(source);
}

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.binding.convert.service;
import org.springframework.binding.convert.ConversionException;
import org.springframework.binding.convert.ConversionExecutionException;
import org.springframework.binding.convert.ConversionExecutor;
import org.springframework.binding.convert.Converter;
import org.springframework.core.style.ToStringCreator;
@@ -30,7 +30,7 @@ import org.springframework.util.Assert;
*
* @author Keith Donald
*/
class StaticConversionExecutor implements ConversionExecutor {
public class StaticConversionExecutor implements ConversionExecutor {
/**
* The source value type this executor will attempt to convert from.
@@ -86,20 +86,24 @@ class StaticConversionExecutor implements ConversionExecutor {
return converter;
}
public Object execute(Object source) throws ConversionException {
public Object execute(Object source) throws ConversionExecutionException {
return execute(source, null);
}
public Object execute(Object source, Object context) throws ConversionException {
public Object execute(Object source, Object context) throws ConversionExecutionException {
if (targetClass.isInstance(source)) {
// source is already assignment compatible with target class
return source;
}
if (source != null && !sourceClass.isInstance(source)) {
throw new ConversionException(getSourceClass(), source, getTargetClass(), "Source object '" + source
+ "' is expected to be an instance of " + getSourceClass(), null);
throw new ConversionExecutionException(source, getSourceClass(), getTargetClass(), "Source object "
+ source + " is expected to be an instance of " + getSourceClass());
}
try {
return converter.convert(source, targetClass, context);
} catch (Exception e) {
throw new ConversionExecutionException(source, getSourceClass(), getTargetClass(), e);
}
return converter.convert(source, targetClass, context);
}
public boolean equals(Object o) {

View File

@@ -33,8 +33,7 @@ public class EvaluationException extends RuntimeException {
* @param cause the underlying cause of this exception
*/
public EvaluationException(EvaluationAttempt evaluationAttempt, Throwable cause) {
this(evaluationAttempt, evaluationAttempt
+ " failed - make sure the expression is evaluatable in the context provided", cause);
this(evaluationAttempt, defaultMessage(evaluationAttempt, cause), cause);
}
/**
@@ -53,4 +52,13 @@ public class EvaluationException extends RuntimeException {
public EvaluationAttempt getEvaluationAttempt() {
return evaluationAttempt;
}
private static String defaultMessage(EvaluationAttempt evaluationAttempt, Throwable cause) {
if (cause != null) {
return evaluationAttempt + " failed - " + cause.getMessage();
} else {
return evaluationAttempt + " failed - make sure the expression is evaluatable in the context provided";
}
}
}

View File

@@ -19,6 +19,11 @@ import org.springframework.binding.format.Formatter;
import org.springframework.binding.format.InvalidFormatException;
import org.springframework.util.StringUtils;
/**
* A formatter for boolean values. Formats {@link Boolean#TRUE} as "true" and {@link Boolean#FALSE} as "false".
*
* @author Keith Donald
*/
public class BooleanFormatter implements Formatter {
public String format(Object value) throws IllegalArgumentException {
@@ -30,12 +35,11 @@ public class BooleanFormatter implements Formatter {
} else if (Boolean.FALSE.equals(value)) {
return "false";
} else {
throw new IllegalArgumentException("Must be a Boolean " + value);
throw new IllegalArgumentException("Not a Boolean: " + value);
}
}
public Object parse(String formattedString) throws InvalidFormatException {
formattedString = (formattedString != null ? formattedString.trim() : null);
if (!StringUtils.hasText(formattedString)) {
return null;
}

View File

@@ -25,26 +25,50 @@ import org.springframework.binding.format.Formatter;
import org.springframework.binding.format.InvalidFormatException;
import org.springframework.util.StringUtils;
/**
* A formatter for {@link Date} types. Allows the configuration of an explicit date pattern and locale.
* @see SimpleDateFormat
* @author Keith Donald
*/
public class DateFormatter implements Formatter {
public static final String DEFAULT_PATTERN = "yyyy-MM-dd";
/**
* The default date pattern.
*/
private static final String DEFAULT_PATTERN = "yyyy-MM-dd";
private String pattern;
private Locale locale;
/**
* The pattern to use to format date values.
* @return the date formatting pattern
*/
public String getPattern() {
return pattern;
}
/**
* Sets the pattern to use to format date values.
* @param pattern the date formatting pattern
*/
public void setPattern(String pattern) {
this.pattern = pattern;
}
/**
* The locale to use in formatting date values. If null, the default locale is used.
* @return the locale
*/
public Locale getLocale() {
return locale;
}
/**
* Sets the locale to use in formatting date values.
* @param locale the locale
*/
public void setLocale(Locale locale) {
this.locale = locale;
}
@@ -64,20 +88,26 @@ public class DateFormatter implements Formatter {
try {
return dateFormat.parse(formattedString);
} catch (ParseException e) {
throw new InvalidFormatException(formattedString, dateFormat.toString());
throw new InvalidFormatException(formattedString, determinePattern(pattern), e);
}
}
// subclassing hookings
protected DateFormat getDateFormat() {
String pattern = this.pattern;
if (pattern == null) {
pattern = DEFAULT_PATTERN;
}
Locale locale = this.locale;
if (locale == null) {
locale = Locale.getDefault();
}
String pattern = determinePattern(this.pattern);
Locale locale = determineLocale(this.locale);
return new SimpleDateFormat(pattern, locale);
}
// internal helpers
private String determinePattern(String pattern) {
return pattern != null ? pattern : DEFAULT_PATTERN;
}
private Locale determineLocale(Locale locale) {
return locale != null ? locale : Locale.getDefault();
}
}

View File

@@ -25,8 +25,9 @@ import org.springframework.util.NumberUtils;
import org.springframework.util.StringUtils;
/**
* A formatter for the common number types such as integers, and big decimals.
*
* A formatter for common number types such as integers and big decimals. Supports basic decoding of number values from
* text, as well as applying custom number format patterns.
* @see DecimalFormat
* @author Keith Donald
*/
public class NumberFormatter implements Formatter {
@@ -78,7 +79,7 @@ public class NumberFormatter implements Formatter {
try {
return NumberUtils.parseNumber(formattedString, numberClass);
} catch (NumberFormatException e) {
throw new InvalidFormatException(formattedString, "A " + numberClass.getName(), e);
throw new InvalidFormatException(formattedString, "A valid " + numberClass.getName() + " string", e);
}
}
}

View File

@@ -21,7 +21,8 @@ import org.springframework.binding.format.Formatter;
import org.springframework.util.Assert;
/**
* Adapts a property editor to the formatter interface.
* Adapts a {@link PropertyEditor} to the formatter interface.
*
* @author Keith Donald
*/
public class PropertyEditorFormatter implements Formatter {
@@ -32,7 +33,7 @@ public class PropertyEditorFormatter implements Formatter {
* Wrap the given property editor in a formatter.
*/
public PropertyEditorFormatter(PropertyEditor propertyEditor) {
Assert.notNull(propertyEditor, "Property editor is required");
Assert.notNull(propertyEditor, "The PropertyEditor is required");
this.propertyEditor = propertyEditor;
}

View File

@@ -15,8 +15,8 @@
*/
package org.springframework.binding.mapping.impl;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.logging.Log;
@@ -29,13 +29,16 @@ import org.springframework.core.style.ToStringCreator;
/**
* Generic mapper implementation that allows mappings to be configured programatically.
*
* @see #addMapping(DefaultMapping)
* @see #setConversionService(ConversionService)
* @author Keith Donald
*/
public class DefaultMapper implements Mapper {
private static final Log logger = LogFactory.getLog(DefaultMapper.class);
private List mappings = new LinkedList();
private List mappings = new ArrayList();
private ConversionService conversionService;
@@ -56,7 +59,7 @@ public class DefaultMapper implements Mapper {
/**
* Add a mapping to this mapper.
* @param mapping the mapping to add
* @param mapping the mapping to add (required)
* @return this, to support convenient call chaining
*/
public DefaultMapper addMapping(DefaultMapping mapping) {
@@ -73,15 +76,21 @@ public class DefaultMapper implements Mapper {
}
public MappingResults map(Object source, Object target) {
if (logger.isDebugEnabled()) {
logger.debug("Beginning mapping between source [" + source.getClass().getName() + "] and target ["
+ target.getClass().getName() + "]");
}
DefaultMappingContext context = new DefaultMappingContext(source, target, conversionService);
Iterator it = mappings.iterator();
while (it.hasNext()) {
DefaultMapping mapping = (DefaultMapping) it.next();
mapping.map(context);
}
MappingResults results = context.toResult();
MappingResults results = context.getMappingResults();
if (logger.isDebugEnabled()) {
logger.debug("Mapper completed; results = " + results);
logger.debug("Completing mapping between source [" + source.getClass().getName() + "] and target ["
+ target.getClass().getName() + "]; total mappings = " + results.getAllResults().size()
+ "; total errors = " + results.getErrorResults().size() + "; " + results);
}
return results;
}

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.binding.mapping.impl;
import org.springframework.binding.convert.ConversionException;
import org.springframework.binding.convert.ConversionExecutionException;
import org.springframework.binding.convert.ConversionExecutor;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.expression.EvaluationException;
@@ -123,9 +123,9 @@ public class DefaultMapping implements Mapping {
if (sourceValue != null) {
if (typeConverter != null) {
try {
targetValue = typeConverter.execute(targetValue, context);
} catch (ConversionException e) {
context.setTypeConversionErrorResult(sourceValue, e.getTargetClass());
targetValue = typeConverter.execute(sourceValue, context);
} catch (ConversionExecutionException e) {
context.setTypeConversionErrorResult(e);
return;
}
} else {
@@ -139,12 +139,12 @@ public class DefaultMapping implements Mapping {
return;
}
if (targetType != null && !targetType.isInstance(targetValue)) {
ConversionExecutor typeConverter = conversionService.getConversionExecutor(sourceValue
.getClass(), targetType);
try {
ConversionExecutor typeConverter = conversionService.getConversionExecutor(sourceValue
.getClass(), targetType);
targetValue = typeConverter.execute(sourceValue, context);
} catch (ConversionException e) {
context.setTypeConversionErrorResult(sourceValue, e.getTargetClass());
} catch (ConversionExecutionException e) {
context.setTypeConversionErrorResult(e);
return;
}
}

View File

@@ -20,6 +20,7 @@ import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.binding.convert.ConversionExecutionException;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.expression.EvaluationException;
import org.springframework.binding.mapping.Mapping;
@@ -92,10 +93,10 @@ public class DefaultMappingContext {
* @param mapping the mapping to make the current mapping
*/
public void setCurrentMapping(Mapping mapping) {
if (this.currentMapping != null) {
if (currentMapping != null) {
throw new IllegalStateException("The current mapping has not finished yet");
}
this.currentMapping = mapping;
currentMapping = mapping;
}
/**
@@ -120,11 +121,12 @@ public class DefaultMappingContext {
/**
* Indicates the current mapping ended with a 'type conversion' error. This means the value obtained from the source
* could not be converted to a type that could be assigned to the target expression.
* @param originalValue the original source value that could not be converted during the mapping attempt
* @param targetType the desired target type to which conversion could not be performed
* @param exception the conversion exception that occurred, containing the original source value that could not be
* converted during the mapping attempt, as well as the desired target type to which conversion could not be
* performed
*/
public void setTypeConversionErrorResult(Object originalValue, Class targetType) {
add(new MappingResult(currentMapping, new TypeConversionError(originalValue, targetType)));
public void setTypeConversionErrorResult(ConversionExecutionException exception) {
add(new MappingResult(currentMapping, new TypeConversionError(exception)));
}
/**
@@ -143,7 +145,11 @@ public class DefaultMappingContext {
add(new MappingResult(currentMapping, new TargetAccessError(originalValue, error)));
}
public MappingResults toResult() {
/**
* Returns the mapping results recorded in this context.
* @return the mapping results
*/
public MappingResults getMappingResults() {
return new DefaultMappingResults(source, target, mappingResults);
}
@@ -153,8 +159,8 @@ public class DefaultMappingContext {
if (logger.isDebugEnabled()) {
logger.debug("Adding " + result);
}
this.mappingResults.add(result);
this.currentMapping = null;
mappingResults.add(result);
currentMapping = null;
}
}

View File

@@ -23,9 +23,6 @@ import java.util.List;
import org.springframework.binding.mapping.MappingResult;
import org.springframework.binding.mapping.MappingResults;
import org.springframework.binding.mapping.MappingResultsCriteria;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* Default mapping results implementation.
@@ -99,11 +96,6 @@ public class DefaultMappingResults implements MappingResults {
}
public String toString() {
String sourceString = ClassUtils.getShortName(source.getClass()) + "@"
+ ObjectUtils.getIdentityHexString(source);
String targetString = ClassUtils.getShortName(target.getClass()) + "@"
+ ObjectUtils.getIdentityHexString(target);
return new ToStringCreator(this).append("source", sourceString).append("target", targetString).append(
"results", mappingResults).toString();
return "Mapping Results = " + mappingResults.toString();
}
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.binding.mapping.results;
import org.springframework.binding.convert.ConversionExecutionException;
import org.springframework.binding.mapping.Result;
import org.springframework.core.style.ToStringCreator;
@@ -29,18 +30,18 @@ public class TypeConversionError extends Result {
private Class targetType;
private ConversionExecutionException exception;
/**
* Creates a new type conversion error.
* @param originalValue the value that could not be converted
* @param targetType the target type of the conversion
* @param exception the underlying type conversion exception
*/
public TypeConversionError(Object originalValue, Class targetType) {
this.originalValue = originalValue;
this.targetType = targetType;
public TypeConversionError(ConversionExecutionException exception) {
this.exception = exception;
}
public Object getOriginalValue() {
return originalValue;
return exception.getValue();
}
public Object getMappedValue() {
@@ -60,12 +61,19 @@ public class TypeConversionError extends Result {
/**
* Returns the target type of the conversion attempt.
*/
public Class getTargetType() {
return targetType;
public Class getTargetClass() {
return exception.getTargetClass();
}
/**
* Returns the backing type conversion exception that occurred.
*/
public ConversionExecutionException getException() {
return exception;
}
public String toString() {
return new ToStringCreator(this).append("originalValue", originalValue).append("targetType", targetType)
.toString();
.append("details", exception.getMessage()).toString();
}
}