fixes for known 2.0.0 bugs reported since release - see changelog
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() + "'";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user