initial JSR-303 Bean Validation support; revised ConversionService and FormatterRegistry
This commit is contained in:
parent
f9f9b431a6
commit
a86a698e5b
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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.beans;
|
||||||
|
|
||||||
|
import java.beans.PropertyDescriptor;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link TypeDescriptor} extension that exposes additional annotations
|
||||||
|
* as conversion metadata: namely, annotations on other accessor methods
|
||||||
|
* (getter/setter) and on the underlying field, if found.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
class BeanTypeDescriptor extends TypeDescriptor {
|
||||||
|
|
||||||
|
private final PropertyDescriptor propertyDescriptor;
|
||||||
|
|
||||||
|
private Annotation[] cachedAnnotations;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new BeanTypeDescriptor for the given bean property.
|
||||||
|
* @param methodParameter the target method parameter
|
||||||
|
* @param propertyDescriptor the corresponding JavaBean PropertyDescriptor
|
||||||
|
*/
|
||||||
|
public BeanTypeDescriptor(MethodParameter methodParameter, PropertyDescriptor propertyDescriptor) {
|
||||||
|
super(methodParameter);
|
||||||
|
this.propertyDescriptor = propertyDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Annotation[] getAnnotations() {
|
||||||
|
Annotation[] anns = this.cachedAnnotations;
|
||||||
|
if (anns == null) {
|
||||||
|
Field underlyingField = ReflectionUtils.findField(
|
||||||
|
getMethodParameter().getMethod().getDeclaringClass(), this.propertyDescriptor.getName());
|
||||||
|
Map<Class, Annotation> annMap = new LinkedHashMap<Class, Annotation>();
|
||||||
|
if (underlyingField != null) {
|
||||||
|
for (Annotation ann : underlyingField.getAnnotations()) {
|
||||||
|
annMap.put(ann.annotationType(), ann);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Method targetMethod = getMethodParameter().getMethod();
|
||||||
|
Method writeMethod = this.propertyDescriptor.getWriteMethod();
|
||||||
|
Method readMethod = this.propertyDescriptor.getReadMethod();
|
||||||
|
if (writeMethod != null && writeMethod != targetMethod) {
|
||||||
|
for (Annotation ann : writeMethod.getAnnotations()) {
|
||||||
|
annMap.put(ann.annotationType(), ann);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (readMethod != null && readMethod != targetMethod) {
|
||||||
|
for (Annotation ann : readMethod.getAnnotations()) {
|
||||||
|
annMap.put(ann.annotationType(), ann);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Annotation ann : targetMethod.getAnnotations()) {
|
||||||
|
annMap.put(ann.annotationType(), ann);
|
||||||
|
}
|
||||||
|
anns = annMap.values().toArray(new Annotation[annMap.size()]);
|
||||||
|
this.cachedAnnotations = anns;
|
||||||
|
}
|
||||||
|
return anns;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -333,10 +333,10 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
|
||||||
PropertyDescriptor pd = getPropertyDescriptorInternal(propertyName);
|
PropertyDescriptor pd = getPropertyDescriptorInternal(propertyName);
|
||||||
if (pd != null) {
|
if (pd != null) {
|
||||||
if (pd.getReadMethod() != null) {
|
if (pd.getReadMethod() != null) {
|
||||||
return new TypeDescriptor(new MethodParameter(pd.getReadMethod(), -1));
|
return new BeanTypeDescriptor(new MethodParameter(pd.getReadMethod(), -1), pd);
|
||||||
}
|
}
|
||||||
else if (pd.getWriteMethod() != null) {
|
else if (pd.getWriteMethod() != null) {
|
||||||
return new TypeDescriptor(new MethodParameter(pd.getWriteMethod(), 0));
|
return new BeanTypeDescriptor(BeanUtils.getWriteMethodParameter(pd), pd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -947,7 +947,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
|
||||||
writeMethod.invoke(object, value);
|
writeMethod.invoke(object, value);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},acc);
|
}, acc);
|
||||||
} catch (PrivilegedActionException ex) {
|
} catch (PrivilegedActionException ex) {
|
||||||
throw ex.getException();
|
throw ex.getException();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,8 @@ public interface PropertyAccessor {
|
||||||
Class getPropertyType(String propertyName) throws BeansException;
|
Class getPropertyType(String propertyName) throws BeansException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a type descriptor for the specified property.
|
* Return a type descriptor for the specified property:
|
||||||
|
* preferably from the read method, falling back to the write method.
|
||||||
* @param propertyName the property to check
|
* @param propertyName the property to check
|
||||||
* (may be a nested path and/or an indexed/mapped property)
|
* (may be a nested path and/or an indexed/mapped property)
|
||||||
* @return the property type for the particular property,
|
* @return the property type for the particular property,
|
||||||
|
|
|
||||||
|
|
@ -165,13 +165,17 @@ class TypeConverterDelegate {
|
||||||
|
|
||||||
// No custom editor but custom ConversionService specified?
|
// No custom editor but custom ConversionService specified?
|
||||||
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
|
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
|
||||||
if (editor == null && conversionService != null && convertedValue != null &&
|
if (editor == null && conversionService != null && convertedValue != null) {
|
||||||
conversionService.canConvert(convertedValue.getClass(), requiredType)) {
|
TypeDescriptor typeDesc;
|
||||||
if (methodParam != null) {
|
if (methodParam != null) {
|
||||||
return (T) conversionService.convert(convertedValue, new TypeDescriptor(methodParam));
|
typeDesc = (descriptor != null ?
|
||||||
|
new BeanTypeDescriptor(methodParam, descriptor) : new TypeDescriptor(methodParam));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return conversionService.convert(convertedValue, requiredType);
|
typeDesc = TypeDescriptor.valueOf(requiredType);
|
||||||
|
}
|
||||||
|
if (conversionService.canConvert(convertedValue.getClass(), typeDesc)) {
|
||||||
|
return (T) conversionService.convert(convertedValue, typeDesc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -353,21 +357,26 @@ class TypeConverterDelegate {
|
||||||
convertedValue = StringUtils.arrayToCommaDelimitedString((String[]) convertedValue);
|
convertedValue = StringUtils.arrayToCommaDelimitedString((String[]) convertedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editor != null && convertedValue instanceof String) {
|
if (convertedValue instanceof String) {
|
||||||
// Use PropertyEditor's setAsText in case of a String value.
|
if (editor != null) {
|
||||||
if (logger.isTraceEnabled()) {
|
// Use PropertyEditor's setAsText in case of a String value.
|
||||||
logger.trace("Converting String to [" + requiredType + "] using property editor [" + editor + "]");
|
if (logger.isTraceEnabled()) {
|
||||||
}
|
logger.trace("Converting String to [" + requiredType + "] using property editor [" + editor + "]");
|
||||||
String newTextValue = (String) convertedValue;
|
}
|
||||||
if (sharedEditor) {
|
String newTextValue = (String) convertedValue;
|
||||||
// Synchronized access to shared editor instance.
|
if (sharedEditor) {
|
||||||
synchronized (editor) {
|
// Synchronized access to shared editor instance.
|
||||||
|
synchronized (editor) {
|
||||||
|
return doConvertTextValue(oldValue, newTextValue, editor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Unsynchronized access to non-shared editor instance.
|
||||||
return doConvertTextValue(oldValue, newTextValue, editor);
|
return doConvertTextValue(oldValue, newTextValue, editor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else if (String.class.equals(requiredType)) {
|
||||||
// Unsynchronized access to non-shared editor instance.
|
returnValue = convertedValue;
|
||||||
return doConvertTextValue(oldValue, newTextValue, editor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -832,7 +832,7 @@ public final class DefaultListableBeanFactoryTests {
|
||||||
public void testCustomConverter() {
|
public void testCustomConverter() {
|
||||||
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
|
||||||
DefaultConversionService conversionService = new DefaultConversionService();
|
DefaultConversionService conversionService = new DefaultConversionService();
|
||||||
conversionService.add(new Converter<String, Float>() {
|
conversionService.addConverter(new Converter<String, Float>() {
|
||||||
public Float convert(String source) throws Exception {
|
public Float convert(String source) throws Exception {
|
||||||
NumberFormat nf = NumberFormat.getInstance(Locale.GERMAN);
|
NumberFormat nf = NumberFormat.getInstance(Locale.GERMAN);
|
||||||
return nf.parse(source).floatValue();
|
return nf.parse(source).floatValue();
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ import java.lang.annotation.Annotation;
|
||||||
*
|
*
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
* @param <A> The type of Annotation this factory uses to create Formatter instances
|
* @param <A> the type of Annotation this factory uses to create Formatter instances
|
||||||
* @param <T> The type of Object Formatters created by this factory format
|
* @param <T> the type of object that the factory's Formatters are dealing with
|
||||||
*/
|
*/
|
||||||
public interface AnnotationFormatterFactory<A extends Annotation, T> {
|
public interface AnnotationFormatterFactory<A extends Annotation, T> {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,6 @@ public @interface Formatted {
|
||||||
/**
|
/**
|
||||||
* The Formatter that handles the formatting for the annotated element.
|
* The Formatter that handles the formatting for the annotated element.
|
||||||
*/
|
*/
|
||||||
Class<?> value();
|
Class<? extends Formatter> value();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import java.util.Locale;
|
||||||
* @param <T> the type of object this formatter can format
|
* @param <T> the type of object this formatter can format
|
||||||
*/
|
*/
|
||||||
public interface Formatter<T> {
|
public interface Formatter<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format the object of type T for display.
|
* Format the object of type T for display.
|
||||||
* @param object the object to format
|
* @param object the object to format
|
||||||
|
|
@ -35,13 +35,15 @@ public interface Formatter<T> {
|
||||||
* @return the formatted display string
|
* @return the formatted display string
|
||||||
*/
|
*/
|
||||||
String format(T object, Locale locale);
|
String format(T object, Locale locale);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse an object from its formatted representation.
|
* Parse an object from its formatted representation.
|
||||||
* @param formatted a formatted representation
|
* @param formatted a formatted representation
|
||||||
* @param locale the user's locale
|
* @param locale the user's locale
|
||||||
* @return the parsed object
|
* @return the parsed object
|
||||||
* @throws ParseException when a parse exception occurs
|
* @throws ParseException when a parse exception occurs
|
||||||
|
* @throws RuntimeException when thrown by coercion methods that are
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
T parse(String formatted, Locale locale) throws ParseException;
|
T parse(String formatted, Locale locale) throws ParseException;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,30 +28,52 @@ import org.springframework.core.convert.TypeDescriptor;
|
||||||
*/
|
*/
|
||||||
public interface FormatterRegistry {
|
public interface FormatterRegistry {
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a Formatter to this registry indexed by <T>.
|
|
||||||
* Calling getFormatter(<T>.class) returns <code>formatter</code>.
|
|
||||||
* @param formatter the formatter
|
|
||||||
* @param <T> the type of object the formatter formats
|
|
||||||
*/
|
|
||||||
<T> void add(Formatter<T> formatter);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a Formatter to this registry indexed by type.
|
* Adds a Formatter to this registry indexed by type.
|
||||||
* Use this add method when type differs from <T>.
|
* <p>Use this add method when type differs from <T>.
|
||||||
* Calling getFormatter(type) returns a decorator that wraps the targetFormatter.
|
* Calling <code>getFormatter(type)</code> returns a decorator that wraps
|
||||||
* On format, the decorator first coerses the instance of type to <T>, then delegates to <code>targetFormatter</code> to format the value.
|
* the <code>targetFormatter</code> instance.
|
||||||
* On parse, the decorator first delegates to the formatter to parse a <T>, then coerses the parsed value to type.
|
* <p>On format, the decorator first coerses the instance of type to <T>,
|
||||||
|
* then delegates to <code>targetFormatter</code> to format the value.
|
||||||
|
* <p>On parse, the decorator first delegates to the formatter to parse a <T>,
|
||||||
|
* then coerces the parsed value to type.
|
||||||
* @param type the object type
|
* @param type the object type
|
||||||
* @param targetFormatter the target formatter
|
* @param targetFormatter the target formatter
|
||||||
*/
|
*/
|
||||||
void add(Class<?> type, Formatter<?> targetFormatter);
|
void addFormatterByType(Class<?> type, Formatter<?> targetFormatter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a Formatter to this registry indexed by <T>.
|
||||||
|
* <o>Calling <code>getFormatter(<T>.class)</code> returns <code>formatter</code>.
|
||||||
|
* @param formatter the formatter
|
||||||
|
* @param <T> the type of object the formatter formats
|
||||||
|
*/
|
||||||
|
<T> void addFormatterByType(Formatter<T> formatter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a Formatter to this registry indexed by the given annotation type.
|
||||||
|
* <o>Calling <code>getFormatter(...)</code> on a field or accessor method
|
||||||
|
* with the given annotation returns <code>formatter</code>.
|
||||||
|
* @param formatter the formatter
|
||||||
|
* @param <T> the type of object the formatter formats
|
||||||
|
*/
|
||||||
|
void addFormatterByAnnotation(Class<? extends Annotation> annotationType, Formatter<?> formatter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a AnnotationFormatterFactory that returns the Formatter for properties annotated with a specific annotation.
|
* Adds a AnnotationFormatterFactory that returns the Formatter for properties annotated with a specific annotation.
|
||||||
|
* <o>Calling <code>getFormatter(...)</code> on a field or accessor method
|
||||||
|
* with the given annotation returns <code>formatter</code>.
|
||||||
* @param factory the annotation formatter factory
|
* @param factory the annotation formatter factory
|
||||||
|
* @param <A> the type of Annotation this factory uses to create Formatter instances
|
||||||
|
* @param <T> the type of object that the factory's Formatters are dealing with
|
||||||
*/
|
*/
|
||||||
void add(AnnotationFormatterFactory<?, ?> factory);
|
<A extends Annotation, T> void addFormatterByAnnotation(AnnotationFormatterFactory<A, T> factory);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Formatter for the specified type.
|
||||||
|
* @return the Formatter, or <code>null</code> if no suitable one is registered
|
||||||
|
*/
|
||||||
|
<T> Formatter<T> getFormatter(Class<T> targetType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Formatter for the type descriptor.
|
* Get the Formatter for the type descriptor.
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import org.springframework.ui.format.Formatter;
|
import org.springframework.ui.format.Formatter;
|
||||||
|
|
||||||
|
|
@ -33,12 +34,16 @@ import org.springframework.ui.format.Formatter;
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
* @see SimpleDateFormat
|
* @see SimpleDateFormat
|
||||||
*/
|
*/
|
||||||
public final class DateFormatter implements Formatter<Date> {
|
public class DateFormatter implements Formatter<Date> {
|
||||||
|
|
||||||
private String pattern;
|
private String pattern;
|
||||||
|
|
||||||
private int style = DateFormat.DEFAULT;
|
private int style = DateFormat.DEFAULT;
|
||||||
|
|
||||||
|
private TimeZone timeZone;
|
||||||
|
|
||||||
|
private boolean lenient = false;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new default DateFormatter.
|
* Create a new default DateFormatter.
|
||||||
|
|
@ -75,6 +80,22 @@ public final class DateFormatter implements Formatter<Date> {
|
||||||
this.style = style;
|
this.style = style;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the TimeZone to normalize the date values into, if any.
|
||||||
|
*/
|
||||||
|
public void setTimeZone(TimeZone timeZone) {
|
||||||
|
this.timeZone = timeZone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify whether or not parsing is to be lenient. Default is false.
|
||||||
|
* <p>With lenient parsing, the parser may allow inputs that do not precisely match the format.
|
||||||
|
* With strict parsing, inputs must match the format exactly.
|
||||||
|
*/
|
||||||
|
public void setLenient(boolean lenient) {
|
||||||
|
this.lenient = lenient;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public String format(Date date, Locale locale) {
|
public String format(Date date, Locale locale) {
|
||||||
if (date == null) {
|
if (date == null) {
|
||||||
|
|
@ -99,7 +120,10 @@ public final class DateFormatter implements Formatter<Date> {
|
||||||
else {
|
else {
|
||||||
dateFormat = DateFormat.getDateInstance(this.style, locale);
|
dateFormat = DateFormat.getDateInstance(this.style, locale);
|
||||||
}
|
}
|
||||||
dateFormat.setLenient(false);
|
if (this.timeZone != null) {
|
||||||
|
dateFormat.setTimeZone(this.timeZone);
|
||||||
|
}
|
||||||
|
dateFormat.setLenient(this.lenient);
|
||||||
return dateFormat;
|
return dateFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
/**
|
/**
|
||||||
* Formatters for <code>java.util.Date</code> fields.
|
* Formatters for <code>java.util.Date</code> properties.
|
||||||
*/
|
*/
|
||||||
package org.springframework.ui.format.date;
|
package org.springframework.ui.format.date;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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.ui.format.number;
|
||||||
|
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.ParsePosition;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.springframework.ui.format.Formatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract formatter for Numbers,
|
||||||
|
* providing a {@link #getNumberFormat(java.util.Locale)} template method.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author Keith Donald
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public abstract class AbstractNumberFormatter implements Formatter<Number> {
|
||||||
|
|
||||||
|
private boolean lenient = false;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify whether or not parsing is to be lenient. Default is false.
|
||||||
|
* <p>With lenient parsing, the parser may allow inputs that do not precisely match the format.
|
||||||
|
* With strict parsing, inputs must match the format exactly.
|
||||||
|
*/
|
||||||
|
public void setLenient(boolean lenient) {
|
||||||
|
this.lenient = lenient;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String format(Number integer, Locale locale) {
|
||||||
|
if (integer == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
NumberFormat format = getNumberFormat(locale);
|
||||||
|
return format.format(integer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Number parse(String formatted, Locale locale) throws ParseException {
|
||||||
|
if (formatted.length() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
NumberFormat format = getNumberFormat(locale);
|
||||||
|
ParsePosition position = new ParsePosition(0);
|
||||||
|
Number number = format.parse(formatted, position);
|
||||||
|
if (position.getErrorIndex() != -1) {
|
||||||
|
throw new ParseException(formatted, position.getIndex());
|
||||||
|
}
|
||||||
|
if (!this.lenient) {
|
||||||
|
if (formatted.length() != position.getIndex()) {
|
||||||
|
// indicates a part of the string that was not parsed
|
||||||
|
throw new ParseException(formatted, position.getIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain a concrete NumberFormat for the specified locale.
|
||||||
|
* @param locale the current locale
|
||||||
|
* @return the NumberFormat instance (never <code>null</code>)
|
||||||
|
*/
|
||||||
|
protected abstract NumberFormat getNumberFormat(Locale locale);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,82 +1,103 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2004-2009 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.ui.format.number;
|
package org.springframework.ui.format.number;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.ParsePosition;
|
import java.util.Currency;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.springframework.ui.format.Formatter;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A BigDecimal formatter for currency values.
|
* A BigDecimal formatter for currency values.
|
||||||
* Delegates to {@link NumberFormat#getCurrencyInstance(Locale)}.
|
*
|
||||||
|
* <p>Delegates to {@link NumberFormat#getCurrencyInstance(Locale)}.
|
||||||
* Configures BigDecimal parsing so there is no loss of precision.
|
* Configures BigDecimal parsing so there is no loss of precision.
|
||||||
* Sets the scale of parsed BigDecimal values to {@link NumberFormat#getMaximumFractionDigits()}.
|
* Can apply a specified {@link RoundingMode} to parsed values.
|
||||||
* Applies {@link RoundingMode#DOWN} to parsed values.
|
*
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
* @see #setLenient(boolean)
|
* @see #setLenient
|
||||||
|
* @see #setRoundingMode
|
||||||
*/
|
*/
|
||||||
public final class CurrencyFormatter implements Formatter<BigDecimal> {
|
public final class CurrencyFormatter extends AbstractNumberFormatter {
|
||||||
|
|
||||||
private CurrencyNumberFormatFactory currencyFormatFactory = new CurrencyNumberFormatFactory();
|
private static final boolean roundingModeOnDecimalFormat =
|
||||||
|
ClassUtils.hasMethod(DecimalFormat.class, "setRoundingMode", RoundingMode.class);
|
||||||
|
|
||||||
|
private int fractionDigits = 2;
|
||||||
|
|
||||||
|
private RoundingMode roundingMode;
|
||||||
|
|
||||||
|
private Currency currency;
|
||||||
|
|
||||||
private boolean lenient;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify whether or not parsing is to be lenient.
|
* Specify the desired number of fraction digits.
|
||||||
* With lenient parsing, the parser may allow inputs that do not precisely match the format.
|
* Default is 2.
|
||||||
* With strict parsing, inputs must match the format exactly.
|
|
||||||
* Default is false.
|
|
||||||
*/
|
*/
|
||||||
public void setLenient(boolean lenient) {
|
public void setFractionDigits(int fractionDigits) {
|
||||||
this.lenient = lenient;
|
this.fractionDigits = fractionDigits;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String format(BigDecimal decimal, Locale locale) {
|
/**
|
||||||
if (decimal == null) {
|
* Specify the rounding mode to use for decimal parsing.
|
||||||
return "";
|
* Default is {@link RoundingMode#UNNECESSARY}.
|
||||||
}
|
*/
|
||||||
NumberFormat format = currencyFormatFactory.getNumberFormat(locale);
|
public void setRoundingMode(RoundingMode roundingMode) {
|
||||||
return format.format(decimal);
|
this.roundingMode = roundingMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal parse(String formatted, Locale locale)
|
/**
|
||||||
throws ParseException {
|
* Specify the currency, if known.
|
||||||
if (formatted.length() == 0) {
|
*/
|
||||||
return null;
|
public void setCurrency(Currency currency) {
|
||||||
}
|
this.currency = currency;
|
||||||
NumberFormat format = currencyFormatFactory.getNumberFormat(locale);
|
}
|
||||||
ParsePosition position = new ParsePosition(0);
|
|
||||||
BigDecimal decimal = (BigDecimal) format.parse(formatted, position);
|
|
||||||
if (position.getErrorIndex() != -1) {
|
public BigDecimal parse(String formatted, Locale locale) throws ParseException {
|
||||||
throw new ParseException(formatted, position.getIndex());
|
BigDecimal decimal = (BigDecimal) super.parse(formatted, locale);
|
||||||
|
if (this.roundingMode != null) {
|
||||||
|
decimal = decimal.setScale(this.fractionDigits, this.roundingMode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
decimal = decimal.setScale(this.fractionDigits);
|
||||||
}
|
}
|
||||||
if (!lenient) {
|
|
||||||
if (formatted.length() != position.getIndex()) {
|
|
||||||
// indicates a part of the string that was not parsed
|
|
||||||
throw new ParseException(formatted, position.getIndex());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
decimal = decimal.setScale(format.getMaximumFractionDigits(), format.getRoundingMode());
|
|
||||||
return decimal;
|
return decimal;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
protected NumberFormat getNumberFormat(Locale locale) {
|
||||||
|
DecimalFormat format = (DecimalFormat) NumberFormat.getCurrencyInstance(locale);
|
||||||
|
format.setParseBigDecimal(true);
|
||||||
|
format.setMaximumFractionDigits(this.fractionDigits);
|
||||||
|
format.setMinimumFractionDigits(this.fractionDigits);
|
||||||
|
if (this.roundingMode != null && roundingModeOnDecimalFormat) {
|
||||||
|
format.setRoundingMode(this.roundingMode);
|
||||||
|
}
|
||||||
|
if (this.currency != null) {
|
||||||
|
format.setCurrency(this.currency);
|
||||||
|
}
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2004-2009 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.ui.format.number;
|
|
||||||
|
|
||||||
import java.math.RoundingMode;
|
|
||||||
import java.text.DecimalFormat;
|
|
||||||
import java.text.NumberFormat;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produces NumberFormat instances that format currency values.
|
|
||||||
* @author Keith Donald
|
|
||||||
* @since 3.0
|
|
||||||
* @see NumberFormat
|
|
||||||
*/
|
|
||||||
final class CurrencyNumberFormatFactory extends NumberFormatFactory {
|
|
||||||
|
|
||||||
private RoundingMode roundingMode = RoundingMode.DOWN;
|
|
||||||
|
|
||||||
public NumberFormat getNumberFormat(Locale locale) {
|
|
||||||
DecimalFormat format = (DecimalFormat) NumberFormat.getCurrencyInstance(locale);
|
|
||||||
format.setParseBigDecimal(true);
|
|
||||||
format.setRoundingMode(roundingMode);
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +1,43 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2004-2009 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.ui.format.number;
|
package org.springframework.ui.format.number;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.ParsePosition;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.springframework.ui.format.Formatter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Number formatter for decimal values.
|
* A Number formatter for decimal values.
|
||||||
* Delegates to {@link NumberFormat#getInstance(Locale)}.
|
*
|
||||||
|
* <p>Delegates to {@link NumberFormat#getInstance(Locale)}.
|
||||||
* Configures BigDecimal parsing so there is no loss in precision.
|
* Configures BigDecimal parsing so there is no loss in precision.
|
||||||
* Allows configuration over the decimal number pattern.
|
* Allows configuration over the decimal number pattern.
|
||||||
* The {@link #parse(String, Locale)} routine always returns a BigDecimal.
|
* The {@link #parse(String, Locale)} routine always returns a BigDecimal.
|
||||||
|
*
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
* @see #setPattern(String)
|
* @see #setPattern
|
||||||
* @see #setLenient(boolean)
|
* @see #setLenient
|
||||||
*/
|
*/
|
||||||
public final class DecimalFormatter implements Formatter<Number> {
|
public final class DecimalFormatter extends AbstractNumberFormatter {
|
||||||
|
|
||||||
private DefaultNumberFormatFactory formatFactory = new DefaultNumberFormatFactory();
|
private String pattern;
|
||||||
|
|
||||||
private boolean lenient;
|
|
||||||
|
|
||||||
public DecimalFormatter() {
|
|
||||||
initDefaults();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the pattern to use to format number values.
|
* Sets the pattern to use to format number values.
|
||||||
|
|
@ -52,50 +46,24 @@ public final class DecimalFormatter implements Formatter<Number> {
|
||||||
* @see DecimalFormat#applyPattern(String)
|
* @see DecimalFormat#applyPattern(String)
|
||||||
*/
|
*/
|
||||||
public void setPattern(String pattern) {
|
public void setPattern(String pattern) {
|
||||||
formatFactory.setPattern(pattern);
|
this.pattern = pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify whether or not parsing is to be lenient.
|
|
||||||
* With lenient parsing, the parser may allow inputs that do not precisely match the format.
|
|
||||||
* With strict parsing, inputs must match the format exactly.
|
|
||||||
* Default is false.
|
|
||||||
*/
|
|
||||||
public void setLenient(boolean lenient) {
|
|
||||||
this.lenient = lenient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String format(Number decimal, Locale locale) {
|
public NumberFormat getNumberFormat(Locale locale) {
|
||||||
if (decimal == null) {
|
NumberFormat format = NumberFormat.getInstance(locale);
|
||||||
return "";
|
if (!(format instanceof DecimalFormat)) {
|
||||||
}
|
if (this.pattern != null) {
|
||||||
NumberFormat format = formatFactory.getNumberFormat(locale);
|
throw new IllegalStateException("Cannot support pattern for non-DecimalFormat: " + format);
|
||||||
return format.format(decimal);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Number parse(String formatted, Locale locale) throws ParseException {
|
|
||||||
if (formatted.length() == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
NumberFormat format = formatFactory.getNumberFormat(locale);
|
|
||||||
ParsePosition position = new ParsePosition(0);
|
|
||||||
BigDecimal decimal = (BigDecimal) format.parse(formatted, position);
|
|
||||||
if (position.getErrorIndex() != -1) {
|
|
||||||
throw new ParseException(formatted, position.getIndex());
|
|
||||||
}
|
|
||||||
if (!lenient) {
|
|
||||||
if (formatted.length() != position.getIndex()) {
|
|
||||||
// indicates a part of the string that was not parsed
|
|
||||||
throw new ParseException(formatted, position.getIndex());
|
|
||||||
}
|
}
|
||||||
|
return format;
|
||||||
}
|
}
|
||||||
return decimal;
|
DecimalFormat decimalFormat = (DecimalFormat) format;
|
||||||
|
decimalFormat.setParseBigDecimal(true);
|
||||||
|
if (this.pattern != null) {
|
||||||
|
decimalFormat.applyPattern(this.pattern);
|
||||||
|
}
|
||||||
|
return decimalFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
// internal helpers
|
}
|
||||||
|
|
||||||
private void initDefaults() {
|
|
||||||
formatFactory.setParseBigDecimal(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2004-2009 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.ui.format.number;
|
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
|
||||||
import java.text.NumberFormat;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Works with a general purpose {@link DecimalFormat} instance returned by calling
|
|
||||||
* {@link NumberFormat#getInstance(Locale)} by default.
|
|
||||||
* @author Keith Donald
|
|
||||||
* @see NumberFormat
|
|
||||||
* @see DecimalFormat
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
class DefaultNumberFormatFactory extends NumberFormatFactory {
|
|
||||||
|
|
||||||
private static Log logger = LogFactory.getLog(DefaultNumberFormatFactory.class);
|
|
||||||
|
|
||||||
private String pattern;
|
|
||||||
|
|
||||||
private Boolean parseBigDecimal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the pattern to use to format number values.
|
|
||||||
* If not specified, the default DecimalFormat pattern is used.
|
|
||||||
* @param pattern the format pattern
|
|
||||||
* @see DecimalFormat#applyPattern(String)
|
|
||||||
*/
|
|
||||||
public void setPattern(String pattern) {
|
|
||||||
this.pattern = pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether the format should always parse a big decimal.
|
|
||||||
* @param parseBigDecimal the big decimal parse status
|
|
||||||
* @see DecimalFormat#setParseBigDecimal(boolean)
|
|
||||||
*/
|
|
||||||
public void setParseBigDecimal(boolean parseBigDecimal) {
|
|
||||||
this.parseBigDecimal = parseBigDecimal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NumberFormat getNumberFormat(Locale locale) {
|
|
||||||
NumberFormat format = NumberFormat.getInstance(locale);
|
|
||||||
if (pattern != null) {
|
|
||||||
if (format instanceof DecimalFormat) {
|
|
||||||
((DecimalFormat) format).applyPattern(pattern);
|
|
||||||
} else {
|
|
||||||
logger.warn("Unable to apply format pattern '" + pattern
|
|
||||||
+ "'; Returned NumberFormat is not a DecimalFormat");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (parseBigDecimal != null) {
|
|
||||||
if (format instanceof DecimalFormat) {
|
|
||||||
((DecimalFormat) format).setParseBigDecimal(parseBigDecimal);
|
|
||||||
} else {
|
|
||||||
logger.warn("Unable to call setParseBigDecimal; not a DecimalFormat");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,77 +1,39 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2004-2009 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.ui.format.number;
|
package org.springframework.ui.format.number;
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.ParsePosition;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.springframework.ui.format.Formatter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Number formatter for whole integer values.
|
* A Number formatter for whole integer values.
|
||||||
* Delegates to {@link NumberFormat#getIntegerInstance(Locale)}.
|
*
|
||||||
|
* <p>Delegates to {@link NumberFormat#getIntegerInstance(Locale)}.
|
||||||
* The {@link #parse(String, Locale)} routine always returns a Long.
|
* The {@link #parse(String, Locale)} routine always returns a Long.
|
||||||
|
*
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
* @see #setLenient(boolean)
|
* @see #setLenient
|
||||||
*/
|
*/
|
||||||
public final class IntegerFormatter implements Formatter<Number> {
|
public final class IntegerFormatter extends AbstractNumberFormatter {
|
||||||
|
|
||||||
private IntegerNumberFormatFactory formatFactory = new IntegerNumberFormatFactory();
|
protected NumberFormat getNumberFormat(Locale locale) {
|
||||||
|
return NumberFormat.getIntegerInstance(locale);
|
||||||
private boolean lenient;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify whether or not parsing is to be lenient.
|
|
||||||
* With lenient parsing, the parser may allow inputs that do not precisely match the format.
|
|
||||||
* With strict parsing, inputs must match the format exactly.
|
|
||||||
* Default is false.
|
|
||||||
*/
|
|
||||||
public void setLenient(boolean lenient) {
|
|
||||||
this.lenient = lenient;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String format(Number integer, Locale locale) {
|
}
|
||||||
if (integer == null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
NumberFormat format = formatFactory.getNumberFormat(locale);
|
|
||||||
return format.format(integer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Number parse(String formatted, Locale locale)
|
|
||||||
throws ParseException {
|
|
||||||
if (formatted.length() == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
NumberFormat format = formatFactory.getNumberFormat(locale);
|
|
||||||
ParsePosition position = new ParsePosition(0);
|
|
||||||
Long integer = (Long) format.parse(formatted, position);
|
|
||||||
if (position.getErrorIndex() != -1) {
|
|
||||||
throw new ParseException(formatted, position.getIndex());
|
|
||||||
}
|
|
||||||
if (!lenient) {
|
|
||||||
if (formatted.length() != position.getIndex()) {
|
|
||||||
// indicates a part of the string that was not parsed
|
|
||||||
throw new ParseException(formatted, position.getIndex());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return integer;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2004-2009 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.ui.format.number;
|
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produces NumberFormat instances that format integer values.
|
|
||||||
* @author Keith Donald
|
|
||||||
* @see NumberFormat
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
final class IntegerNumberFormatFactory extends NumberFormatFactory {
|
|
||||||
public NumberFormat getNumberFormat(Locale locale) {
|
|
||||||
return NumberFormat.getIntegerInstance(locale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2004-2009 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.ui.format.number;
|
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A factory for {@link NumberFormat} objects.
|
|
||||||
* Conceals the complexity associated with configuring, constructing, and/or caching number format instances.
|
|
||||||
* @author Keith Donald
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
abstract class NumberFormatFactory {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory method that returns a fully-configured {@link NumberFormat} instance to use to format an object for
|
|
||||||
* display.
|
|
||||||
* @return the number format
|
|
||||||
*/
|
|
||||||
public abstract NumberFormat getNumberFormat(Locale locale);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,79 +1,45 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2004-2009 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.ui.format.number;
|
package org.springframework.ui.format.number;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.text.DecimalFormat;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.ParsePosition;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.springframework.ui.format.Formatter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Number formatter for percent values.
|
* A Number formatter for percent values.
|
||||||
* Delegates to {@link NumberFormat#getPercentInstance(Locale)}.
|
*
|
||||||
|
* <p>Delegates to {@link NumberFormat#getPercentInstance(Locale)}.
|
||||||
* Configures BigDecimal parsing so there is no loss in precision.
|
* Configures BigDecimal parsing so there is no loss in precision.
|
||||||
* The {@link #parse(String, Locale)} routine always returns a BigDecimal.
|
* The {@link #parse(String, Locale)} routine always returns a BigDecimal.
|
||||||
|
*
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
* @see #setLenient(boolean)
|
* @see #setLenient
|
||||||
*/
|
*/
|
||||||
public final class PercentFormatter implements Formatter<Number> {
|
public final class PercentFormatter extends AbstractNumberFormatter {
|
||||||
|
|
||||||
private PercentNumberFormatFactory percentFormatFactory = new PercentNumberFormatFactory();
|
protected NumberFormat getNumberFormat(Locale locale) {
|
||||||
|
NumberFormat format = NumberFormat.getPercentInstance(locale);
|
||||||
private boolean lenient;
|
if (format instanceof DecimalFormat) {
|
||||||
|
((DecimalFormat) format).setParseBigDecimal(true);
|
||||||
/**
|
}
|
||||||
* Specify whether or not parsing is to be lenient.
|
return format;
|
||||||
* With lenient parsing, the parser may allow inputs that do not precisely match the format.
|
|
||||||
* With strict parsing, inputs must match the format exactly.
|
|
||||||
* Default is false.
|
|
||||||
*/
|
|
||||||
public void setLenient(boolean lenient) {
|
|
||||||
this.lenient = lenient;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String format(Number number, Locale locale) {
|
}
|
||||||
if (number == null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
NumberFormat format = percentFormatFactory.getNumberFormat(locale);
|
|
||||||
return format.format(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Number parse(String formatted, Locale locale)
|
|
||||||
throws ParseException {
|
|
||||||
if (formatted.length() == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
NumberFormat format = percentFormatFactory.getNumberFormat(locale);
|
|
||||||
ParsePosition position = new ParsePosition(0);
|
|
||||||
BigDecimal decimal = (BigDecimal) format.parse(formatted, position);
|
|
||||||
if (position.getErrorIndex() != -1) {
|
|
||||||
throw new ParseException(formatted, position.getIndex());
|
|
||||||
}
|
|
||||||
if (!lenient) {
|
|
||||||
if (formatted.length() != position.getIndex()) {
|
|
||||||
// indicates a part of the string that was not parsed
|
|
||||||
throw new ParseException(formatted, position.getIndex());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return decimal;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2004-2009 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.ui.format.number;
|
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
|
||||||
import java.text.NumberFormat;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produces NumberFormat instances that format percent values.
|
|
||||||
* @see NumberFormat
|
|
||||||
* @author Keith Donald
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
final class PercentNumberFormatFactory extends NumberFormatFactory {
|
|
||||||
public NumberFormat getNumberFormat(Locale locale) {
|
|
||||||
DecimalFormat format = (DecimalFormat) NumberFormat.getPercentInstance(locale);
|
|
||||||
format.setParseBigDecimal(true);
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,4 +2,3 @@
|
||||||
* Formatters for <code>java.lang.Number</code> properties.
|
* Formatters for <code>java.lang.Number</code> properties.
|
||||||
*/
|
*/
|
||||||
package org.springframework.ui.format.number;
|
package org.springframework.ui.format.number;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,16 +16,13 @@
|
||||||
|
|
||||||
package org.springframework.ui.format.support;
|
package org.springframework.ui.format.support;
|
||||||
|
|
||||||
import java.text.ParseException;
|
|
||||||
|
|
||||||
import org.springframework.context.i18n.LocaleContextHolder;
|
import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
import org.springframework.core.convert.ConversionFailedException;
|
|
||||||
import org.springframework.core.convert.ConversionService;
|
import org.springframework.core.convert.ConversionService;
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
import org.springframework.core.convert.support.DefaultConversionService;
|
import org.springframework.core.convert.support.DefaultConversionService;
|
||||||
|
import org.springframework.core.convert.support.GenericConversionService;
|
||||||
import org.springframework.ui.format.Formatter;
|
import org.springframework.ui.format.Formatter;
|
||||||
import org.springframework.ui.format.FormatterRegistry;
|
import org.springframework.ui.format.FormatterRegistry;
|
||||||
import org.springframework.ui.format.support.GenericFormatterRegistry;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -36,12 +33,10 @@ import org.springframework.util.Assert;
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class FormattingConversionServiceAdapter implements ConversionService {
|
public class FormattingConversionServiceAdapter extends GenericConversionService {
|
||||||
|
|
||||||
private final FormatterRegistry formatterRegistry;
|
private final FormatterRegistry formatterRegistry;
|
||||||
|
|
||||||
private final ConversionService targetConversionService;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new FormattingConversionServiceAdapter for the given FormatterRegistry.
|
* Create a new FormattingConversionServiceAdapter for the given FormatterRegistry.
|
||||||
|
|
@ -51,41 +46,37 @@ public class FormattingConversionServiceAdapter implements ConversionService {
|
||||||
Assert.notNull(formatterRegistry, "FormatterRegistry must not be null");
|
Assert.notNull(formatterRegistry, "FormatterRegistry must not be null");
|
||||||
this.formatterRegistry = formatterRegistry;
|
this.formatterRegistry = formatterRegistry;
|
||||||
if (formatterRegistry instanceof GenericFormatterRegistry) {
|
if (formatterRegistry instanceof GenericFormatterRegistry) {
|
||||||
this.targetConversionService = ((GenericFormatterRegistry) formatterRegistry).getConversionService();
|
setParent(((GenericFormatterRegistry) formatterRegistry).getConversionService());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.targetConversionService = new DefaultConversionService();
|
setParent(new DefaultConversionService());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
|
@Override
|
||||||
return canConvert(sourceType, TypeDescriptor.valueOf(targetType));
|
protected <T> Converter findRegisteredConverter(Class<?> sourceType, Class<T> targetType) {
|
||||||
}
|
if (String.class.equals(sourceType)) {
|
||||||
|
Formatter<T> formatter = this.formatterRegistry.getFormatter(targetType);
|
||||||
public boolean canConvert(Class<?> sourceType, TypeDescriptor targetType) {
|
|
||||||
return (this.formatterRegistry.getFormatter(targetType) != null ||
|
|
||||||
this.targetConversionService.canConvert(sourceType, targetType));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T> T convert(Object source, Class<T> targetType) {
|
|
||||||
return (T) convert(source, TypeDescriptor.valueOf(targetType));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object convert(Object source, TypeDescriptor targetType) {
|
|
||||||
if (source instanceof String) {
|
|
||||||
Formatter formatter = this.formatterRegistry.getFormatter(targetType);
|
|
||||||
if (formatter != null) {
|
if (formatter != null) {
|
||||||
try {
|
return new FormattingConverter<T>(formatter);
|
||||||
return formatter.parse((String) source, LocaleContextHolder.getLocale());
|
|
||||||
}
|
|
||||||
catch (ParseException ex) {
|
|
||||||
throw new ConversionFailedException(source, String.class, targetType.getType(), ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.targetConversionService.convert(source, targetType);
|
return super.findRegisteredConverter(sourceType, targetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class FormattingConverter<T> implements Converter<String, T> {
|
||||||
|
|
||||||
|
private final Formatter<T> formatter;
|
||||||
|
|
||||||
|
public FormattingConverter(Formatter<T> formatter) {
|
||||||
|
this.formatter = formatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T convert(String source) throws Exception {
|
||||||
|
return this.formatter.parse(source, LocaleContextHolder.getLocale());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,22 +27,24 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.BeanFactoryAware;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.core.GenericTypeResolver;
|
import org.springframework.core.GenericTypeResolver;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.core.convert.ConversionService;
|
import org.springframework.core.convert.ConversionService;
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.core.convert.support.DefaultConversionService;
|
import org.springframework.core.convert.support.DefaultConversionService;
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.ui.format.FormatterRegistry;
|
|
||||||
import org.springframework.ui.format.Formatter;
|
|
||||||
import org.springframework.ui.format.AnnotationFormatterFactory;
|
import org.springframework.ui.format.AnnotationFormatterFactory;
|
||||||
import org.springframework.ui.format.Formatted;
|
import org.springframework.ui.format.Formatted;
|
||||||
|
import org.springframework.ui.format.Formatter;
|
||||||
|
import org.springframework.ui.format.FormatterRegistry;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A generic implementation of {@link org.springframework.ui.format.FormatterRegistry} suitable for use in most environments.
|
* A generic implementation of {@link org.springframework.ui.format.FormatterRegistry}
|
||||||
|
* suitable for use in most environments.
|
||||||
*
|
*
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
|
@ -52,7 +54,7 @@ import org.springframework.ui.format.Formatted;
|
||||||
* @see #add(Class, org.springframework.ui.format.Formatter)
|
* @see #add(Class, org.springframework.ui.format.Formatter)
|
||||||
* @see #add(org.springframework.ui.format.AnnotationFormatterFactory)
|
* @see #add(org.springframework.ui.format.AnnotationFormatterFactory)
|
||||||
*/
|
*/
|
||||||
public class GenericFormatterRegistry implements FormatterRegistry, BeanFactoryAware, Cloneable {
|
public class GenericFormatterRegistry implements FormatterRegistry, ApplicationContextAware, Cloneable {
|
||||||
|
|
||||||
private final Map<Class, Formatter> typeFormatters = new ConcurrentHashMap<Class, Formatter>();
|
private final Map<Class, Formatter> typeFormatters = new ConcurrentHashMap<Class, Formatter>();
|
||||||
|
|
||||||
|
|
@ -61,46 +63,59 @@ public class GenericFormatterRegistry implements FormatterRegistry, BeanFactoryA
|
||||||
|
|
||||||
private ConversionService conversionService = new DefaultConversionService();
|
private ConversionService conversionService = new DefaultConversionService();
|
||||||
|
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
private boolean shared = true;
|
private boolean shared = true;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the formatters in the set provided.
|
* Registers the formatters in the set provided.
|
||||||
* JavaBean-friendly alternative to calling {@link #add(Formatter)}.
|
* JavaBean-friendly alternative to calling {@link #addFormatterByType(Formatter)}.
|
||||||
* @see #add(Formatter)
|
* @see #add(Formatter)
|
||||||
*/
|
*/
|
||||||
public void setFormatters(Set<Formatter<?>> formatters) {
|
public void setFormatters(Set<Formatter<?>> formatters) {
|
||||||
for (Formatter<?> formatter : formatters) {
|
for (Formatter<?> formatter : formatters) {
|
||||||
add(formatter);
|
addFormatterByType(formatter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the formatters in the map provided by type.
|
* Registers the formatters in the map provided by type.
|
||||||
* JavaBean-friendly alternative to calling {@link #add(Class, Formatter)}.
|
* JavaBean-friendly alternative to calling {@link #addFormatterByType(Class, Formatter)}.
|
||||||
* @see #add(Class, Formatter)
|
* @see #add(Class, Formatter)
|
||||||
*/
|
*/
|
||||||
public void setFormatterMap(Map<Class<?>, Formatter<?>> formatters) {
|
public void setFormatterMap(Map<Class<?>, Formatter<?>> formatters) {
|
||||||
for (Map.Entry<Class<?>, Formatter<?>> entry : formatters.entrySet()) {
|
for (Map.Entry<Class<?>, Formatter<?>> entry : formatters.entrySet()) {
|
||||||
add(entry.getKey(), entry.getValue());
|
addFormatterByType(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the formatters in the map provided by annotation type.
|
||||||
|
* JavaBean-friendly alternative to calling {@link #addFormatterByAnnotation(Class, Formatter)}.
|
||||||
|
* @see #add(Class, Formatter)
|
||||||
|
*/
|
||||||
|
public void setAnnotationFormatterMap(Map<Class<? extends Annotation>, Formatter<?>> formatters) {
|
||||||
|
for (Map.Entry<Class<? extends Annotation>, Formatter<?>> entry : formatters.entrySet()) {
|
||||||
|
addFormatterByAnnotation(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the annotation formatter factories in the set provided.
|
* Registers the annotation formatter factories in the set provided.
|
||||||
* JavaBean-friendly alternative to calling {@link #add(AnnotationFormatterFactory)}.
|
* JavaBean-friendly alternative to calling {@link #addFormatterByAnnotation(AnnotationFormatterFactory)}.
|
||||||
* @see #add(AnnotationFormatterFactory)
|
* @see #add(AnnotationFormatterFactory)
|
||||||
*/
|
*/
|
||||||
public void setAnnotationFormatterFactories(Set<AnnotationFormatterFactory> factories) {
|
public void setAnnotationFormatterFactories(Set<AnnotationFormatterFactory<?, ?>> factories) {
|
||||||
for (AnnotationFormatterFactory factory : factories) {
|
for (AnnotationFormatterFactory<?, ?> factory : factories) {
|
||||||
add(factory);
|
addFormatterByAnnotation(factory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify the type conversion service that will be used to coerce objects to the
|
* Specify the type conversion service that will be used to coerce objects to the
|
||||||
* types required for formatting. Defaults to a {@link DefaultConversionService}.
|
* types required for formatting. Defaults to a {@link DefaultConversionService}.
|
||||||
* @see #add(Class, Formatter)
|
* @see #addFormatterByType(Class, Formatter)
|
||||||
*/
|
*/
|
||||||
public void setConversionService(ConversionService conversionService) {
|
public void setConversionService(ConversionService conversionService) {
|
||||||
Assert.notNull(conversionService, "ConversionService must not be null");
|
Assert.notNull(conversionService, "ConversionService must not be null");
|
||||||
|
|
@ -117,12 +132,13 @@ public class GenericFormatterRegistry implements FormatterRegistry, BeanFactoryA
|
||||||
/**
|
/**
|
||||||
* Take the context's default ConversionService if none specified locally.
|
* Take the context's default ConversionService if none specified locally.
|
||||||
*/
|
*/
|
||||||
public void setBeanFactory(BeanFactory beanFactory) {
|
public void setApplicationContext(ApplicationContext context) {
|
||||||
if (this.conversionService == null &&
|
if (this.conversionService == null &&
|
||||||
beanFactory.containsBean(ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME)) {
|
context.containsBean(ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME)) {
|
||||||
this.conversionService = beanFactory.getBean(
|
this.conversionService = context.getBean(
|
||||||
ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME, ConversionService.class);
|
ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME, ConversionService.class);
|
||||||
}
|
}
|
||||||
|
this.applicationContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -157,6 +173,7 @@ public class GenericFormatterRegistry implements FormatterRegistry, BeanFactoryA
|
||||||
clone.typeFormatters.putAll(this.typeFormatters);
|
clone.typeFormatters.putAll(this.typeFormatters);
|
||||||
clone.annotationFormatters.putAll(this.annotationFormatters);
|
clone.annotationFormatters.putAll(this.annotationFormatters);
|
||||||
clone.conversionService = this.conversionService;
|
clone.conversionService = this.conversionService;
|
||||||
|
clone.applicationContext = applicationContext;
|
||||||
clone.shared = false;
|
clone.shared = false;
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
@ -164,32 +181,48 @@ public class GenericFormatterRegistry implements FormatterRegistry, BeanFactoryA
|
||||||
|
|
||||||
// implementing FormatterRegistry
|
// implementing FormatterRegistry
|
||||||
|
|
||||||
public <T> void add(Formatter<T> formatter) {
|
public void addFormatterByType(Class<?> type, Formatter<?> formatter) {
|
||||||
this.typeFormatters.put(getFormattedObjectType(formatter.getClass()), formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(Class<?> type, Formatter<?> formatter) {
|
|
||||||
Class<?> formattedObjectType = getFormattedObjectType(formatter.getClass());
|
Class<?> formattedObjectType = getFormattedObjectType(formatter.getClass());
|
||||||
if (!this.conversionService.canConvert(formattedObjectType, type)) {
|
if (!this.conversionService.canConvert(formattedObjectType, type)) {
|
||||||
throw new IllegalArgumentException("Unable to register formatter " + formatter + " for type [" + type.getName() + "]; not able to convert from [" + formattedObjectType.getName() + "] to parse");
|
throw new IllegalArgumentException("Unable to register Formatter " + formatter + " for type [" + type.getName() + "]; not able to convert from [" + formattedObjectType.getName() + "] to parse");
|
||||||
}
|
}
|
||||||
if (!this.conversionService.canConvert(type, formattedObjectType)) {
|
if (!this.conversionService.canConvert(type, formattedObjectType)) {
|
||||||
throw new IllegalArgumentException("Unable to register formatter " + formatter + " for type [" + type.getName() + "]; not able to convert to [" + formattedObjectType.getName() + "] to format");
|
throw new IllegalArgumentException("Unable to register Formatter " + formatter + " for type [" + type.getName() + "]; not able to convert to [" + formattedObjectType.getName() + "] to format");
|
||||||
}
|
}
|
||||||
this.typeFormatters.put(type, formatter);
|
this.typeFormatters.put(type, formatter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(AnnotationFormatterFactory<?, ?> factory) {
|
public <T> void addFormatterByType(Formatter<T> formatter) {
|
||||||
|
Class formattedObjectType = getFormattedObjectType(formatter.getClass());
|
||||||
|
this.typeFormatters.put(formattedObjectType, formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFormatterByAnnotation(Class<? extends Annotation> annotationType, Formatter<?> formatter) {
|
||||||
|
this.annotationFormatters.put(annotationType, new SimpleAnnotationFormatterFactory(formatter));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <A extends Annotation, T> void addFormatterByAnnotation(AnnotationFormatterFactory<A, T> factory) {
|
||||||
this.annotationFormatters.put(getAnnotationType(factory.getClass()), factory);
|
this.annotationFormatters.put(getAnnotationType(factory.getClass()), factory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> Formatter<T> getFormatter(Class<T> targetType) {
|
||||||
|
return (Formatter<T>) getFormatter(TypeDescriptor.valueOf(targetType));
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Formatter<Object> getFormatter(TypeDescriptor type) {
|
public Formatter<Object> getFormatter(TypeDescriptor type) {
|
||||||
Assert.notNull(type, "The TypeDescriptor is required");
|
Assert.notNull(type, "TypeDescriptor is required");
|
||||||
Formatter<Object> formatter = getAnnotationFormatter(type);
|
Formatter<Object> formatter = getAnnotationFormatter(type);
|
||||||
if (formatter == null) {
|
if (formatter == null) {
|
||||||
formatter = getTypeFormatter(type.getType());
|
formatter = getTypeFormatter(type.getType());
|
||||||
}
|
}
|
||||||
|
if (formatter != null) {
|
||||||
|
Class<?> formattedObjectType = getFormattedObjectType(formatter.getClass());
|
||||||
|
if (!type.getType().isAssignableFrom(formattedObjectType)) {
|
||||||
|
return new ConvertingFormatter(type.getType(), formattedObjectType, formatter);
|
||||||
|
}
|
||||||
|
}
|
||||||
return formatter;
|
return formatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -250,37 +283,33 @@ public class GenericFormatterRegistry implements FormatterRegistry, BeanFactoryA
|
||||||
classToIntrospect = classToIntrospect.getSuperclass();
|
classToIntrospect = classToIntrospect.getSuperclass();
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Unable to extract Annotation type A argument from AnnotationFormatterFactory ["
|
"Unable to extract Annotation type A argument from AnnotationFormatterFactory [" +
|
||||||
+ factoryClass.getName() + "]; does the factory parameterize the <A> generic type?");
|
factoryClass.getName() + "]; does the factory parameterize the <A> generic type?");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Formatter getAnnotationFormatter(TypeDescriptor type) {
|
private Formatter getAnnotationFormatter(TypeDescriptor type) {
|
||||||
Annotation[] annotations = type.getAnnotations();
|
Annotation[] annotations = type.getAnnotations();
|
||||||
for (Annotation a : annotations) {
|
for (Annotation ann : annotations) {
|
||||||
AnnotationFormatterFactory factory = annotationFormatters.get(a.annotationType());
|
AnnotationFormatterFactory factory = this.annotationFormatters.get(ann.annotationType());
|
||||||
if (factory != null) {
|
if (factory != null) {
|
||||||
return factory.getFormatter(a);
|
return factory.getFormatter(ann);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Formatted formattedAnnotation = ann.annotationType().getAnnotation(Formatted.class);
|
||||||
|
if (formattedAnnotation != null) {
|
||||||
|
Formatter formatter = createFormatter(formattedAnnotation.value());
|
||||||
|
this.annotationFormatters.put(ann.annotationType(), new SimpleAnnotationFormatterFactory(formatter));
|
||||||
|
return formatter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Formatter getTypeFormatter(Class<?> type) {
|
private Formatter getTypeFormatter(Class<?> type) {
|
||||||
Assert.notNull(type, "The Class of the object to format is required");
|
|
||||||
Formatter formatter = findFormatter(type);
|
Formatter formatter = findFormatter(type);
|
||||||
if (formatter != null) {
|
return (formatter != null ? formatter : getDefaultFormatter(type));
|
||||||
Class<?> formattedObjectType = getFormattedObjectType(formatter.getClass());
|
|
||||||
if (type.isAssignableFrom(formattedObjectType)) {
|
|
||||||
return formatter;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return new ConvertingFormatter(type, formattedObjectType, formatter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return getDefaultFormatter(type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Formatter<?> findFormatter(Class<?> type) {
|
private Formatter<?> findFormatter(Class<?> type) {
|
||||||
|
|
@ -288,7 +317,7 @@ public class GenericFormatterRegistry implements FormatterRegistry, BeanFactoryA
|
||||||
classQueue.addFirst(type);
|
classQueue.addFirst(type);
|
||||||
while (!classQueue.isEmpty()) {
|
while (!classQueue.isEmpty()) {
|
||||||
Class currentClass = classQueue.removeLast();
|
Class currentClass = classQueue.removeLast();
|
||||||
Formatter<?> formatter = typeFormatters.get(currentClass);
|
Formatter<?> formatter = this.typeFormatters.get(currentClass);
|
||||||
if (formatter != null) {
|
if (formatter != null) {
|
||||||
return formatter;
|
return formatter;
|
||||||
}
|
}
|
||||||
|
|
@ -306,32 +335,29 @@ public class GenericFormatterRegistry implements FormatterRegistry, BeanFactoryA
|
||||||
private Formatter<?> getDefaultFormatter(Class<?> type) {
|
private Formatter<?> getDefaultFormatter(Class<?> type) {
|
||||||
Formatted formatted = AnnotationUtils.findAnnotation(type, Formatted.class);
|
Formatted formatted = AnnotationUtils.findAnnotation(type, Formatted.class);
|
||||||
if (formatted != null) {
|
if (formatted != null) {
|
||||||
Class formatterClass = formatted.value();
|
Formatter formatter = createFormatter(formatted.value());
|
||||||
try {
|
this.typeFormatters.put(type, formatter);
|
||||||
Formatter formatter = (Formatter) formatterClass.newInstance();
|
return formatter;
|
||||||
typeFormatters.put(type, formatter);
|
|
||||||
return formatter;
|
|
||||||
} catch (InstantiationException e) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Formatter referenced by @Formatted annotation does not have default constructor", e);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Formatter referenced by @Formatted annotation does not have public constructor", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Formatter<?> createFormatter(Class<? extends Formatter> formatterClass) {
|
||||||
|
return (this.applicationContext != null ?
|
||||||
|
this.applicationContext.getAutowireCapableBeanFactory().createBean(formatterClass) :
|
||||||
|
BeanUtils.instantiate(formatterClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private class ConvertingFormatter implements Formatter {
|
private class ConvertingFormatter implements Formatter {
|
||||||
|
|
||||||
private Class<?> type;
|
private final Class<?> type;
|
||||||
|
|
||||||
private Class<?> formattedObjectType;
|
private final Class<?> formattedObjectType;
|
||||||
|
|
||||||
private Formatter targetFormatter;
|
private final Formatter targetFormatter;
|
||||||
|
|
||||||
public ConvertingFormatter(Class<?> type, Class<?> formattedObjectType, Formatter targetFormatter) {
|
public ConvertingFormatter(Class<?> type, Class<?> formattedObjectType, Formatter targetFormatter) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
|
@ -341,16 +367,29 @@ public class GenericFormatterRegistry implements FormatterRegistry, BeanFactoryA
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public String format(Object object, Locale locale) {
|
public String format(Object object, Locale locale) {
|
||||||
object = conversionService.convert(object, formattedObjectType);
|
object = conversionService.convert(object, this.formattedObjectType);
|
||||||
return targetFormatter.format(object, locale);
|
return this.targetFormatter.format(object, locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object parse(String formatted, Locale locale) throws ParseException {
|
public Object parse(String formatted, Locale locale) throws ParseException {
|
||||||
Object parsed = targetFormatter.parse(formatted, locale);
|
Object parsed = this.targetFormatter.parse(formatted, locale);
|
||||||
parsed = conversionService.convert(parsed, type);
|
parsed = conversionService.convert(parsed, this.type);
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class SimpleAnnotationFormatterFactory implements AnnotationFormatterFactory {
|
||||||
|
|
||||||
|
private final Formatter instance;
|
||||||
|
|
||||||
|
public SimpleAnnotationFormatterFactory(Formatter instance) {
|
||||||
|
this.instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Formatter getFormatter(Annotation annotation) {
|
||||||
|
return this.instance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Generic support for UI layer concepts.
|
* Generic support for UI layer concepts.
|
||||||
* Provides a generic ModelMap for model holding.
|
* Provides a generic ModelMap for model holding.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
package org.springframework.ui;
|
package org.springframework.ui;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,11 +106,7 @@ public abstract class AbstractPropertyBindingResult extends AbstractBindingResul
|
||||||
@Override
|
@Override
|
||||||
protected Object formatFieldValue(String field, Object value) {
|
protected Object formatFieldValue(String field, Object value) {
|
||||||
String fixedField = fixedField(field);
|
String fixedField = fixedField(field);
|
||||||
TypeDescriptor td = getPropertyAccessor().getPropertyTypeDescriptor(fixedField);
|
// Try custom editor...
|
||||||
Formatter<Object> formatter = (this.formatterRegistry != null ? this.formatterRegistry.getFormatter(td) : null);
|
|
||||||
if (formatter != null) {
|
|
||||||
return formatter.format(value, LocaleContextHolder.getLocale());
|
|
||||||
}
|
|
||||||
PropertyEditor customEditor = getCustomEditor(fixedField);
|
PropertyEditor customEditor = getCustomEditor(fixedField);
|
||||||
if (customEditor != null) {
|
if (customEditor != null) {
|
||||||
customEditor.setValue(value);
|
customEditor.setValue(value);
|
||||||
|
|
@ -121,6 +117,13 @@ public abstract class AbstractPropertyBindingResult extends AbstractBindingResul
|
||||||
return textValue;
|
return textValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Try custom formatter...
|
||||||
|
TypeDescriptor td = getPropertyAccessor().getPropertyTypeDescriptor(fixedField);
|
||||||
|
Formatter<Object> formatter = (this.formatterRegistry != null ? this.formatterRegistry.getFormatter(td) : null);
|
||||||
|
if (formatter != null) {
|
||||||
|
return formatter.format(value, LocaleContextHolder.getLocale());
|
||||||
|
}
|
||||||
|
// Nothing found: return value as-is.
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,14 +147,18 @@ public abstract class AbstractPropertyBindingResult extends AbstractBindingResul
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public PropertyEditor findEditor(String field, Class valueType) {
|
public PropertyEditor findEditor(String field, Class valueType) {
|
||||||
TypeDescriptor td = (valueType != null ? TypeDescriptor.valueOf(valueType) :
|
PropertyEditor editor = super.findEditor(field, valueType);
|
||||||
getPropertyAccessor().getPropertyTypeDescriptor(fixedField(field)));
|
if (editor == null) {
|
||||||
final Formatter<Object> formatter =
|
TypeDescriptor td = (field != null ?
|
||||||
(this.formatterRegistry != null ? this.formatterRegistry.getFormatter(td) : null);
|
getPropertyAccessor().getPropertyTypeDescriptor(fixedField(field)) :
|
||||||
if (formatter != null) {
|
TypeDescriptor.valueOf(valueType));
|
||||||
return new FormattingPropertyEditorAdapter(formatter);
|
Formatter<Object> formatter =
|
||||||
|
(this.formatterRegistry != null ? this.formatterRegistry.getFormatter(td) : null);
|
||||||
|
if (formatter != null) {
|
||||||
|
editor = new FormattingPropertyEditorAdapter(formatter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return super.findEditor(field, valueType);
|
return editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,8 @@ import org.springframework.beans.TypeConverter;
|
||||||
import org.springframework.beans.TypeMismatchException;
|
import org.springframework.beans.TypeMismatchException;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.ui.format.FormatterRegistry;
|
import org.springframework.ui.format.FormatterRegistry;
|
||||||
import org.springframework.ui.format.support.GenericFormatterRegistry;
|
|
||||||
import org.springframework.ui.format.support.FormattingConversionServiceAdapter;
|
import org.springframework.ui.format.support.FormattingConversionServiceAdapter;
|
||||||
|
import org.springframework.ui.format.support.GenericFormatterRegistry;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.PatternMatchUtils;
|
import org.springframework.util.PatternMatchUtils;
|
||||||
|
|
@ -135,6 +135,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||||
|
|
||||||
private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor();
|
private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor();
|
||||||
|
|
||||||
|
private Validator validator;
|
||||||
|
|
||||||
private FormatterRegistry formatterRegistry;
|
private FormatterRegistry formatterRegistry;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -443,6 +445,23 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||||
return this.bindingErrorProcessor;
|
return this.bindingErrorProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Validator to apply after each binding step.
|
||||||
|
*/
|
||||||
|
public void setValidator(Validator validator) {
|
||||||
|
if (validator != null && (getTarget() != null && !validator.supports(getTarget().getClass()))) {
|
||||||
|
throw new IllegalStateException("Invalid target for Validator [" + validator + "]: " + getTarget());
|
||||||
|
}
|
||||||
|
this.validator = validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the Validator to apply after each binding step, if any.
|
||||||
|
*/
|
||||||
|
public Validator getValidator() {
|
||||||
|
return this.validator;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the FormatterRegistry to use for obtaining Formatters in preference
|
* Set the FormatterRegistry to use for obtaining Formatters in preference
|
||||||
* to JavaBeans PropertyEditors.
|
* to JavaBeans PropertyEditors.
|
||||||
|
|
@ -633,6 +652,18 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoke the specified Validator, if any.
|
||||||
|
* @see #setValidator(Validator)
|
||||||
|
* @see #getBindingResult()
|
||||||
|
*/
|
||||||
|
public void validate() {
|
||||||
|
Validator validator = getValidator();
|
||||||
|
if (validator != null) {
|
||||||
|
validator.validate(getTarget(), getBindingResult());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close this DataBinder, which may result in throwing
|
* Close this DataBinder, which may result in throwing
|
||||||
* a BindException if it encountered any errors.
|
* a BindException if it encountered any errors.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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.validation.beanvalidation;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.validation.ConstraintViolation;
|
||||||
|
import javax.validation.Validation;
|
||||||
|
import javax.validation.Validator;
|
||||||
|
import javax.validation.ValidatorFactory;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.BeanInitializationException;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public class BeanValidationPostProcessor implements BeanPostProcessor, InitializingBean {
|
||||||
|
|
||||||
|
private javax.validation.Validator validator;
|
||||||
|
|
||||||
|
|
||||||
|
public void setValidator(Validator validator) {
|
||||||
|
this.validator = validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValidatorFactory(ValidatorFactory validatorFactory) {
|
||||||
|
this.validator = validatorFactory.getValidator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void afterPropertiesSet() {
|
||||||
|
if (this.validator == null) {
|
||||||
|
Validation.buildDefaultValidatorFactory().getValidator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||||
|
Set<ConstraintViolation<Object>> result = this.validator.validate(bean);
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
StringBuilder sb = new StringBuilder("Bean state is invalid: ");
|
||||||
|
for (Iterator<ConstraintViolation<Object>> it = result.iterator(); it.hasNext();) {
|
||||||
|
ConstraintViolation<Object> violation = it.next();
|
||||||
|
sb.append(violation.getPropertyPath()).append(" - ").append(violation.getMessage());
|
||||||
|
if (it.hasNext()) {
|
||||||
|
sb.append("; ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new BeanInitializationException(sb.toString());
|
||||||
|
}
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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.validation.beanvalidation;
|
||||||
|
|
||||||
|
import javax.validation.MessageInterpolator;
|
||||||
|
import javax.validation.TraversableResolver;
|
||||||
|
import javax.validation.Validation;
|
||||||
|
import javax.validation.Validator;
|
||||||
|
import javax.validation.ValidatorContext;
|
||||||
|
import javax.validation.ValidatorFactory;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configurable bean class that exposes a specific JSR-303 Validator
|
||||||
|
* through its original interface as well as through the Spring
|
||||||
|
* {@link org.springframework.validation.Validator} interface.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public class CustomValidatorBean extends SpringValidatorAdapter implements Validator, InitializingBean {
|
||||||
|
|
||||||
|
private ValidatorFactory validatorFactory;
|
||||||
|
|
||||||
|
private MessageInterpolator messageInterpolator;
|
||||||
|
|
||||||
|
private TraversableResolver traversableResolver;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the ValidatorFactory to obtain the target Validator from.
|
||||||
|
* <p>Default is {@link javax.validation.Validation#buildDefaultValidatorFactory()}.
|
||||||
|
*/
|
||||||
|
public void setValidatorFactory(ValidatorFactory validatorFactory) {
|
||||||
|
this.validatorFactory = validatorFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a custom MessageInterpolator to use for this Validator.
|
||||||
|
*/
|
||||||
|
public void setMessageInterpolator(MessageInterpolator messageInterpolator) {
|
||||||
|
this.messageInterpolator = messageInterpolator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a custom TraversableResolver to use for this Validator.
|
||||||
|
*/
|
||||||
|
public void setTraversableResolver(TraversableResolver traversableResolver) {
|
||||||
|
this.traversableResolver = traversableResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void afterPropertiesSet() {
|
||||||
|
if (this.validatorFactory == null) {
|
||||||
|
this.validatorFactory = Validation.buildDefaultValidatorFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidatorContext validatorContext = this.validatorFactory.usingContext();
|
||||||
|
validatorContext.messageInterpolator(new LocaleContextMessageInterpolator(
|
||||||
|
this.messageInterpolator, this.validatorFactory.getMessageInterpolator()));
|
||||||
|
if (this.traversableResolver != null) {
|
||||||
|
validatorContext.traversableResolver(this.traversableResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTargetValidator(validatorContext.getValidator());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,209 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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.validation.beanvalidation;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import javax.validation.Configuration;
|
||||||
|
import javax.validation.ConstraintValidatorFactory;
|
||||||
|
import javax.validation.MessageInterpolator;
|
||||||
|
import javax.validation.TraversableResolver;
|
||||||
|
import javax.validation.Validation;
|
||||||
|
import javax.validation.Validator;
|
||||||
|
import javax.validation.ValidatorContext;
|
||||||
|
import javax.validation.ValidatorFactory;
|
||||||
|
import javax.validation.spi.ValidationProvider;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the central class for <code>javax.validation</code> (JSR-303) setup
|
||||||
|
* in a Spring application context: It bootstraps a <code>javax.validation.ValidationFactory</code>
|
||||||
|
* and exposes it through the Spring {@link org.springframework.validation.Validator} interface
|
||||||
|
* as well as through the JSR-303 {@link javax.validation.Validator} interface and the
|
||||||
|
* {@link javax.validation.ValidatorFactory} interface itself.
|
||||||
|
*
|
||||||
|
* <p>When talking to an instance of this bean through the Spring or JSR-303 Validator interfaces,
|
||||||
|
* you'll be talking to the default Validator of the underlying ValidatorFactory. This is very
|
||||||
|
* convenient in that you don't have to perform yet another call on the factory, assuming that
|
||||||
|
* you will almost always use the default Validator anyway. This can also be injected directly
|
||||||
|
* into any target dependency of type {@link org.springframework.validation.Validator}!
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 3.0
|
||||||
|
* @see javax.validation.ValidatorFactory
|
||||||
|
* @see javax.validation.Validator
|
||||||
|
* @see javax.validation.Validation#buildDefaultValidatorFactory()
|
||||||
|
*/
|
||||||
|
public class LocalValidatorFactoryBean extends SpringValidatorAdapter
|
||||||
|
implements ValidatorFactory, Validator, ApplicationContextAware, InitializingBean {
|
||||||
|
|
||||||
|
private Class<? extends ValidationProvider> providerClass;
|
||||||
|
|
||||||
|
private MessageInterpolator messageInterpolator;
|
||||||
|
|
||||||
|
private TraversableResolver traversableResolver;
|
||||||
|
|
||||||
|
private ConstraintValidatorFactory constraintValidatorFactory;
|
||||||
|
|
||||||
|
private Resource[] mappingLocations;
|
||||||
|
|
||||||
|
private final Map<String, String> validationPropertyMap = new HashMap<String, String>();
|
||||||
|
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
private ValidatorFactory validatorFactory;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the desired provider class, if any.
|
||||||
|
* <p>If not specified, JSR-303's default search mechanism will be used.
|
||||||
|
* @see javax.validation.Validation#byProvider(Class)
|
||||||
|
* @see javax.validation.Validation#byDefaultProvider()
|
||||||
|
*/
|
||||||
|
public void setProviderClass(Class<? extends ValidationProvider> providerClass) {
|
||||||
|
this.providerClass = providerClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a custom MessageInterpolator to use for this ValidatorFactory
|
||||||
|
* and its exposed default Validator.
|
||||||
|
*/
|
||||||
|
public void setMessageInterpolator(MessageInterpolator messageInterpolator) {
|
||||||
|
this.messageInterpolator = messageInterpolator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a custom TraversableResolver to use for this ValidatorFactory
|
||||||
|
* and its exposed default Validator.
|
||||||
|
*/
|
||||||
|
public void setTraversableResolver(TraversableResolver traversableResolver) {
|
||||||
|
this.traversableResolver = traversableResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a custom ConstraintValidatorFactory to use for this ValidatorFactory.
|
||||||
|
* <p>Default is a {@link SpringConstraintValidatorFactory}, delegating to the
|
||||||
|
* containing ApplicationContext for creating autowired ConstraintValidator instances.
|
||||||
|
*/
|
||||||
|
public void setConstraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) {
|
||||||
|
this.constraintValidatorFactory = constraintValidatorFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify resource locations to load XML constraint mapping files from, if any.
|
||||||
|
*/
|
||||||
|
public void setMappingLocations(Resource[] mappingLocations) {
|
||||||
|
this.mappingLocations = mappingLocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify bean validation properties to be passed to the validation provider.
|
||||||
|
* <p>Can be populated with a String
|
||||||
|
* "value" (parsed via PropertiesEditor) or a "props" element in XML bean definitions.
|
||||||
|
* @see javax.validation.Configuration#addProperty(String, String)
|
||||||
|
*/
|
||||||
|
public void setValidationProperties(Properties jpaProperties) {
|
||||||
|
CollectionUtils.mergePropertiesIntoMap(jpaProperties, this.validationPropertyMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify bean validation properties to be passed to the validation provider as a Map.
|
||||||
|
* <p>Can be populated with a
|
||||||
|
* "map" or "props" element in XML bean definitions.
|
||||||
|
* @see javax.validation.Configuration#addProperty(String, String)
|
||||||
|
*/
|
||||||
|
public void setValidationPropertyMap(Map<String, String> validationProperties) {
|
||||||
|
if (validationProperties != null) {
|
||||||
|
this.validationPropertyMap.putAll(validationProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow Map access to the bean validation properties to be passed to the validation provider,
|
||||||
|
* with the option to add or override specific entries.
|
||||||
|
* <p>Useful for specifying entries directly, for example via "validationPropertyMap[myKey]".
|
||||||
|
*/
|
||||||
|
public Map<String, String> getValidationPropertyMap() {
|
||||||
|
return this.validationPropertyMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void afterPropertiesSet() {
|
||||||
|
Configuration configuration = (this.providerClass != null ?
|
||||||
|
Validation.byProvider(this.providerClass).configure() :
|
||||||
|
Validation.byDefaultProvider().configure());
|
||||||
|
|
||||||
|
configuration.messageInterpolator(new LocaleContextMessageInterpolator(
|
||||||
|
this.messageInterpolator, configuration.getDefaultMessageInterpolator()));
|
||||||
|
|
||||||
|
if (this.traversableResolver != null) {
|
||||||
|
configuration.traversableResolver(this.traversableResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstraintValidatorFactory targetConstraintValidatorFactory = this.constraintValidatorFactory;
|
||||||
|
if (targetConstraintValidatorFactory == null && this.applicationContext != null) {
|
||||||
|
targetConstraintValidatorFactory =
|
||||||
|
new SpringConstraintValidatorFactory(this.applicationContext.getAutowireCapableBeanFactory());
|
||||||
|
}
|
||||||
|
if (targetConstraintValidatorFactory != null) {
|
||||||
|
configuration.constraintValidatorFactory(targetConstraintValidatorFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mappingLocations != null) {
|
||||||
|
for (Resource location : this.mappingLocations) {
|
||||||
|
try {
|
||||||
|
configuration.addMapping(location.getInputStream());
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new IllegalStateException("Cannot read mapping resource: " + location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> entry : this.validationPropertyMap.entrySet()) {
|
||||||
|
configuration.addProperty(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.validatorFactory = configuration.buildValidatorFactory();
|
||||||
|
setTargetValidator(this.validatorFactory.getValidator());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Validator getValidator() {
|
||||||
|
return this.validatorFactory.getValidator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidatorContext usingContext() {
|
||||||
|
return this.validatorFactory.usingContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageInterpolator getMessageInterpolator() {
|
||||||
|
return this.validatorFactory.getMessageInterpolator();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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.validation.beanvalidation;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import javax.validation.MessageInterpolator;
|
||||||
|
|
||||||
|
import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegates to a target {@link MessageInterpolator} implementation but enforces Spring's
|
||||||
|
* managed Locale. Typically used to wrap the validation provider's default interpolator.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 3.0
|
||||||
|
* @see org.springframework.context.i18n.LocaleContextHolder#getLocale()
|
||||||
|
*/
|
||||||
|
public class LocaleContextMessageInterpolator implements MessageInterpolator {
|
||||||
|
|
||||||
|
private final MessageInterpolator targetInterpolator;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new LocaleContextMessageInterpolator, wrapping the given target interpolator.
|
||||||
|
* @param targetInterpolator the target MessageInterpolator to wrap
|
||||||
|
*/
|
||||||
|
public LocaleContextMessageInterpolator(MessageInterpolator targetInterpolator) {
|
||||||
|
Assert.notNull(targetInterpolator, "Target MessageInterpolator must not be null");
|
||||||
|
this.targetInterpolator = targetInterpolator;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocaleContextMessageInterpolator(MessageInterpolator customInterpolator, MessageInterpolator defaultInterpolator) {
|
||||||
|
this.targetInterpolator = (customInterpolator != null ? customInterpolator : defaultInterpolator);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String interpolate(String message, Context context) {
|
||||||
|
return this.targetInterpolator.interpolate(message, context, LocaleContextHolder.getLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String interpolate(String message, Context context, Locale locale) {
|
||||||
|
return this.targetInterpolator.interpolate(message, context, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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.validation.beanvalidation;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintValidator;
|
||||||
|
import javax.validation.ConstraintValidatorFactory;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSR-303 {@link ConstraintValidatorFactory} implementation that delegates to a
|
||||||
|
* Spring BeanFactory for creating autowired {@link ConstraintValidator} instances.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 3.0
|
||||||
|
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean(Class)
|
||||||
|
* @see org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory()
|
||||||
|
*/
|
||||||
|
public class SpringConstraintValidatorFactory implements ConstraintValidatorFactory {
|
||||||
|
|
||||||
|
private final AutowireCapableBeanFactory beanFactory;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new SpringConstraintValidatorFactory for the given BeanFactory.
|
||||||
|
* @param beanFactory the target BeanFactory
|
||||||
|
*/
|
||||||
|
public SpringConstraintValidatorFactory(AutowireCapableBeanFactory beanFactory) {
|
||||||
|
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||||
|
this.beanFactory = beanFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
|
||||||
|
return this.beanFactory.createBean(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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.validation.beanvalidation;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.validation.ConstraintViolation;
|
||||||
|
import javax.validation.metadata.BeanDescriptor;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.validation.Errors;
|
||||||
|
import org.springframework.validation.Validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter that takes a JSR-303 <code>javax.validator.Validator</code>
|
||||||
|
* and exposes it as a Spring {@link org.springframework.validation.Validator}
|
||||||
|
* while also exposing the original JSR-303 Validator interface itself.
|
||||||
|
*
|
||||||
|
* <p>Can be used as a programmatic wrapper. Also serves as base class for
|
||||||
|
* {@link CustomValidatorBean} and {@link LocalValidatorFactoryBean}.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public class SpringValidatorAdapter implements Validator, javax.validation.Validator {
|
||||||
|
|
||||||
|
private javax.validation.Validator targetValidator;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new SpringValidatorAdapter for the given JSR-303 Validator.
|
||||||
|
* @param targetValidator the JSR-303 Validator to wrap
|
||||||
|
*/
|
||||||
|
public SpringValidatorAdapter(javax.validation.Validator targetValidator) {
|
||||||
|
Assert.notNull(targetValidator, "Target Validator must not be null");
|
||||||
|
this.targetValidator = targetValidator;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpringValidatorAdapter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTargetValidator(javax.validation.Validator targetValidator) {
|
||||||
|
this.targetValidator = targetValidator;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// Implementation of Spring Validator interface
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
public boolean supports(Class<?> clazz) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validate(Object target, Errors errors) {
|
||||||
|
Set<ConstraintViolation<Object>> result = this.targetValidator.validate(target);
|
||||||
|
for (ConstraintViolation<Object> violation : result) {
|
||||||
|
errors.rejectValue(violation.getPropertyPath().toString(),
|
||||||
|
violation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName(),
|
||||||
|
violation.getConstraintDescriptor().getAttributes().values().toArray(),
|
||||||
|
violation.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// Implementation of JSR-303 Validator interface
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
|
||||||
|
return this.targetValidator.validate(object, groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups) {
|
||||||
|
return this.targetValidator.validateProperty(object, propertyName, groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Set<ConstraintViolation<T>> validateValue(
|
||||||
|
Class<T> beanType, String propertyName, Object value, Class<?>... groups) {
|
||||||
|
|
||||||
|
return this.targetValidator.validateValue(beanType, propertyName, groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeanDescriptor getConstraintsForClass(Class<?> clazz) {
|
||||||
|
return this.targetValidator.getConstraintsForClass(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T unwrap(Class<T> type) {
|
||||||
|
return this.targetValidator.unwrap(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
/**
|
||||||
|
* Support classes for integrating a JSR-303 Bean Validation provider
|
||||||
|
* (such as Hibernate Validator 4.0) into a Spring ApplicationContext
|
||||||
|
* and in particular with Spring's data binding and validation APIs.
|
||||||
|
*
|
||||||
|
* <p>The central class is {@link LocalValidatorFactoryBean} which
|
||||||
|
* defines a shared ValidatorFactory/Validator setup for availability
|
||||||
|
* to other Spring components.
|
||||||
|
*/
|
||||||
|
package org.springframework.validation.beanvalidation;
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Provides data binding and validation functionality,
|
* Provides data binding and validation functionality,
|
||||||
* for usage in business and/or UI layers.
|
* for usage in business and/or UI layers.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
package org.springframework.validation;
|
package org.springframework.validation;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,4 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Support classes for handling validation results.
|
* Support classes for handling validation results.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
package org.springframework.validation.support;
|
package org.springframework.validation.support;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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.ui.format;
|
package org.springframework.ui.format;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
|
|
||||||
import java.lang.annotation.Documented;
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
@ -13,14 +25,20 @@ import java.math.BigInteger;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.core.style.ToStringCreator;
|
import org.springframework.core.style.ToStringCreator;
|
||||||
import org.springframework.ui.format.number.CurrencyFormatter;
|
import org.springframework.ui.format.number.CurrencyFormatter;
|
||||||
import org.springframework.ui.format.number.IntegerFormatter;
|
import org.springframework.ui.format.number.IntegerFormatter;
|
||||||
import org.springframework.ui.format.support.GenericFormatterRegistry;
|
import org.springframework.ui.format.support.GenericFormatterRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Keith Donald
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
*/
|
||||||
public class GenericFormatterRegistryTests {
|
public class GenericFormatterRegistryTests {
|
||||||
|
|
||||||
private GenericFormatterRegistry registry;
|
private GenericFormatterRegistry registry;
|
||||||
|
|
@ -32,8 +50,8 @@ public class GenericFormatterRegistryTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAdd() throws ParseException {
|
public void testAdd() throws ParseException {
|
||||||
registry.add(new IntegerFormatter());
|
registry.addFormatterByType(new IntegerFormatter());
|
||||||
Formatter formatter = registry.getFormatter(typeDescriptor(Integer.class));
|
Formatter formatter = registry.getFormatter(Integer.class);
|
||||||
String formatted = formatter.format(new Integer(3), Locale.US);
|
String formatted = formatter.format(new Integer(3), Locale.US);
|
||||||
assertEquals("3", formatted);
|
assertEquals("3", formatted);
|
||||||
Integer i = (Integer) formatter.parse("3", Locale.US);
|
Integer i = (Integer) formatter.parse("3", Locale.US);
|
||||||
|
|
@ -42,23 +60,38 @@ public class GenericFormatterRegistryTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddByObjectType() {
|
public void testAddByObjectType() {
|
||||||
registry.add(BigInteger.class, new IntegerFormatter());
|
registry.addFormatterByType(BigInteger.class, new IntegerFormatter());
|
||||||
Formatter formatter = registry.getFormatter(typeDescriptor(BigInteger.class));
|
Formatter formatter = registry.getFormatter(BigInteger.class);
|
||||||
String formatted = formatter.format(new BigInteger("3"), Locale.US);
|
String formatted = formatter.format(new BigInteger("3"), Locale.US);
|
||||||
assertEquals("3", formatted);
|
assertEquals("3", formatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddAnnotationFormatterFactory() throws Exception {
|
public void testAddByAnnotation() throws Exception {
|
||||||
registry.add(new CurrencyAnnotationFormatterFactory());
|
registry.addFormatterByAnnotation(Currency.class, new CurrencyFormatter());
|
||||||
Formatter formatter = registry.getFormatter(new TypeDescriptor(getClass().getField("currencyField")));
|
Formatter formatter = registry.getFormatter(new TypeDescriptor(getClass().getField("currencyField")));
|
||||||
String formatted = formatter.format(new BigDecimal("5.00"), Locale.US);
|
String formatted = formatter.format(new BigDecimal("5.00"), Locale.US);
|
||||||
assertEquals("$5.00", formatted);
|
assertEquals("$5.00", formatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddAnnotationFormatterFactory() throws Exception {
|
||||||
|
registry.addFormatterByAnnotation(new CurrencyAnnotationFormatterFactory());
|
||||||
|
Formatter formatter = registry.getFormatter(new TypeDescriptor(getClass().getField("currencyField")));
|
||||||
|
String formatted = formatter.format(new BigDecimal("5.00"), Locale.US);
|
||||||
|
assertEquals("$5.00", formatted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetDefaultFormatterFromMetaAnnotation() throws Exception {
|
||||||
|
Formatter formatter = registry.getFormatter(new TypeDescriptor(getClass().getField("smartCurrencyField")));
|
||||||
|
String formatted = formatter.format(new BigDecimal("5.00"), Locale.US);
|
||||||
|
assertEquals("$5.00", formatted);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetDefaultFormatterForType() {
|
public void testGetDefaultFormatterForType() {
|
||||||
Formatter formatter = registry.getFormatter(typeDescriptor(Address.class));
|
Formatter formatter = registry.getFormatter(Address.class);
|
||||||
Address address = new Address();
|
Address address = new Address();
|
||||||
address.street = "12345 Bel Aire Estates";
|
address.street = "12345 Bel Aire Estates";
|
||||||
address.city = "Palm Bay";
|
address.city = "Palm Bay";
|
||||||
|
|
@ -70,33 +103,44 @@ public class GenericFormatterRegistryTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetNoFormatterForType() {
|
public void testGetNoFormatterForType() {
|
||||||
assertNull(registry.getFormatter(typeDescriptor(Integer.class)));
|
assertNull(registry.getFormatter(Integer.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected=IllegalArgumentException.class)
|
@Test(expected=IllegalArgumentException.class)
|
||||||
public void testGetFormatterCannotConvert() {
|
public void testGetFormatterCannotConvert() {
|
||||||
registry.add(Integer.class, new AddressFormatter());
|
registry.addFormatterByType(Integer.class, new AddressFormatter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Currency
|
@Currency
|
||||||
public BigDecimal currencyField;
|
public BigDecimal currencyField;
|
||||||
|
|
||||||
private static TypeDescriptor typeDescriptor(Class<?> clazz) {
|
@SmartCurrency
|
||||||
return TypeDescriptor.valueOf(clazz);
|
public BigDecimal smartCurrencyField;
|
||||||
|
|
||||||
|
@Target({ElementType.METHOD, ElementType.FIELD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Currency {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class CurrencyAnnotationFormatterFactory implements
|
@Target({ElementType.METHOD, ElementType.FIELD})
|
||||||
AnnotationFormatterFactory<Currency, BigDecimal> {
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Formatted(CurrencyFormatter.class)
|
||||||
|
public @interface SmartCurrency {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CurrencyAnnotationFormatterFactory implements AnnotationFormatterFactory<Currency, Number> {
|
||||||
|
|
||||||
private CurrencyFormatter currencyFormatter = new CurrencyFormatter();
|
private final CurrencyFormatter currencyFormatter = new CurrencyFormatter();
|
||||||
|
|
||||||
public Formatter<BigDecimal> getFormatter(Currency annotation) {
|
public Formatter<Number> getFormatter(Currency annotation) {
|
||||||
return currencyFormatter;
|
return this.currencyFormatter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Formatted(AddressFormatter.class)
|
@Formatted(AddressFormatter.class)
|
||||||
public static class Address {
|
public static class Address {
|
||||||
|
|
||||||
private String street;
|
private String street;
|
||||||
private String city;
|
private String city;
|
||||||
private String state;
|
private String state;
|
||||||
|
|
@ -148,7 +192,7 @@ public class GenericFormatterRegistryTests {
|
||||||
.append("zip", zip).toString();
|
.append("zip", zip).toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class AddressFormatter implements Formatter<Address> {
|
public static class AddressFormatter implements Formatter<Address> {
|
||||||
|
|
||||||
public String format(Address address, Locale locale) {
|
public String format(Address address, Locale locale) {
|
||||||
|
|
@ -164,14 +208,6 @@ public class GenericFormatterRegistryTests {
|
||||||
address.setZip(fields[3]);
|
address.setZip(fields[3]);
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Target( { ElementType.METHOD, ElementType.FIELD })
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Documented
|
|
||||||
public @interface Currency {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,11 @@ import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
|
@ -44,6 +49,8 @@ import org.springframework.context.support.ResourceBundleMessageSource;
|
||||||
import org.springframework.context.support.StaticMessageSource;
|
import org.springframework.context.support.StaticMessageSource;
|
||||||
import org.springframework.context.i18n.LocaleContextHolder;
|
import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
import org.springframework.ui.format.number.DecimalFormatter;
|
import org.springframework.ui.format.number.DecimalFormatter;
|
||||||
|
import org.springframework.ui.format.Formatted;
|
||||||
|
import org.springframework.ui.format.support.GenericFormatterRegistry;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -297,7 +304,7 @@ public class DataBinderTests extends TestCase {
|
||||||
public void testBindingWithFormatter() {
|
public void testBindingWithFormatter() {
|
||||||
TestBean tb = new TestBean();
|
TestBean tb = new TestBean();
|
||||||
DataBinder binder = new DataBinder(tb);
|
DataBinder binder = new DataBinder(tb);
|
||||||
binder.getFormatterRegistry().add(Float.class, new DecimalFormatter());
|
binder.getFormatterRegistry().addFormatterByType(Float.class, new DecimalFormatter());
|
||||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||||
pvs.addPropertyValue("myFloat", "1,2");
|
pvs.addPropertyValue("myFloat", "1,2");
|
||||||
|
|
||||||
|
|
@ -322,6 +329,45 @@ public class DataBinderTests extends TestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testBindingWithDefaultFormatterFromField() {
|
||||||
|
doTestBindingWithDefaultFormatter(new FormattedFieldTestBean());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBindingWithDefaultFormatterFromGetter() {
|
||||||
|
doTestBindingWithDefaultFormatter(new FormattedGetterTestBean());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBindingWithDefaultFormatterFromSetter() {
|
||||||
|
doTestBindingWithDefaultFormatter(new FormattedSetterTestBean());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doTestBindingWithDefaultFormatter(Object tb) {
|
||||||
|
DataBinder binder = new DataBinder(tb);
|
||||||
|
binder.setFormatterRegistry(new GenericFormatterRegistry());
|
||||||
|
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||||
|
pvs.addPropertyValue("number", "1,2");
|
||||||
|
|
||||||
|
LocaleContextHolder.setLocale(Locale.GERMAN);
|
||||||
|
try {
|
||||||
|
binder.bind(pvs);
|
||||||
|
assertEquals(new Float("1.2"), binder.getBindingResult().getRawFieldValue("number"));
|
||||||
|
assertEquals("1,2", binder.getBindingResult().getFieldValue("number"));
|
||||||
|
|
||||||
|
PropertyEditor editor = binder.getBindingResult().findEditor("number", Float.class);
|
||||||
|
assertNotNull(editor);
|
||||||
|
editor.setValue(new Float("1.4"));
|
||||||
|
assertEquals("1,4", editor.getAsText());
|
||||||
|
|
||||||
|
editor = binder.getBindingResult().findEditor("number", null);
|
||||||
|
assertNotNull(editor);
|
||||||
|
editor.setAsText("1,6");
|
||||||
|
assertEquals(new Float("1.6"), editor.getValue());
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
LocaleContextHolder.resetLocaleContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void testBindingWithAllowedFields() throws Exception {
|
public void testBindingWithAllowedFields() throws Exception {
|
||||||
TestBean rod = new TestBean();
|
TestBean rod = new TestBean();
|
||||||
DataBinder binder = new DataBinder(rod);
|
DataBinder binder = new DataBinder(rod);
|
||||||
|
|
@ -1376,4 +1422,56 @@ public class DataBinderTests extends TestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Target({ElementType.METHOD, ElementType.FIELD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Formatted(DecimalFormatter.class)
|
||||||
|
public @interface Decimal {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class FormattedFieldTestBean {
|
||||||
|
|
||||||
|
@Decimal
|
||||||
|
private Float number;
|
||||||
|
|
||||||
|
public Float getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumber(Float number) {
|
||||||
|
this.number = number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class FormattedGetterTestBean {
|
||||||
|
|
||||||
|
private Float number;
|
||||||
|
|
||||||
|
@Decimal
|
||||||
|
public Float getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumber(Float number) {
|
||||||
|
this.number = number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class FormattedSetterTestBean {
|
||||||
|
|
||||||
|
private Float number;
|
||||||
|
|
||||||
|
public Float getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Decimal
|
||||||
|
public void setNumber(Float number) {
|
||||||
|
this.number = number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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.validation.beanvalidation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.validation.Constraint;
|
||||||
|
import javax.validation.ConstraintPayload;
|
||||||
|
import javax.validation.ConstraintValidator;
|
||||||
|
import javax.validation.ConstraintValidatorContext;
|
||||||
|
import javax.validation.ConstraintViolation;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import org.hibernate.validation.HibernateValidationProvider;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.validation.BeanPropertyBindingResult;
|
||||||
|
import org.springframework.validation.FieldError;
|
||||||
|
import org.springframework.validation.ObjectError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public class ValidatorFactoryTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimpleValidation() throws Exception {
|
||||||
|
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
|
||||||
|
validator.afterPropertiesSet();
|
||||||
|
ValidPerson person = new ValidPerson();
|
||||||
|
Set<ConstraintViolation<ValidPerson>> result = validator.validate(person);
|
||||||
|
assertEquals(2, result.size());
|
||||||
|
for (ConstraintViolation<ValidPerson> cv : result) {
|
||||||
|
String path = cv.getPropertyPath().toString();
|
||||||
|
if ("name".equals(path) || "address.street".equals(path)) {
|
||||||
|
assertTrue(cv.getConstraintDescriptor().getAnnotation() instanceof NotNull);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fail("Invalid constraint violation with path '" + path + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimpleValidationWithCustomProvider() throws Exception {
|
||||||
|
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
|
||||||
|
validator.setProviderClass(HibernateValidationProvider.class);
|
||||||
|
validator.afterPropertiesSet();
|
||||||
|
ValidPerson person = new ValidPerson();
|
||||||
|
Set<ConstraintViolation<ValidPerson>> result = validator.validate(person);
|
||||||
|
assertEquals(2, result.size());
|
||||||
|
for (ConstraintViolation<ValidPerson> cv : result) {
|
||||||
|
String path = cv.getPropertyPath().toString();
|
||||||
|
if ("name".equals(path) || "address.street".equals(path)) {
|
||||||
|
assertTrue(cv.getConstraintDescriptor().getAnnotation() instanceof NotNull);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fail("Invalid constraint violation with path '" + path + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimpleValidationWithClassLevel() throws Exception {
|
||||||
|
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
|
||||||
|
validator.afterPropertiesSet();
|
||||||
|
ValidPerson person = new ValidPerson();
|
||||||
|
person.setName("Juergen");
|
||||||
|
person.getAddress().setStreet("Juergen's Street");
|
||||||
|
Set<ConstraintViolation<ValidPerson>> result = validator.validate(person);
|
||||||
|
assertEquals(1, result.size());
|
||||||
|
Iterator<ConstraintViolation<ValidPerson>> iterator = result.iterator();
|
||||||
|
ConstraintViolation cv = iterator.next();
|
||||||
|
assertEquals("", cv.getPropertyPath().toString());
|
||||||
|
assertTrue(cv.getConstraintDescriptor().getAnnotation() instanceof NameAddressValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSpringValidation() throws Exception {
|
||||||
|
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
|
||||||
|
validator.afterPropertiesSet();
|
||||||
|
ValidPerson person = new ValidPerson();
|
||||||
|
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person");
|
||||||
|
validator.validate(person, result);
|
||||||
|
assertEquals(2, result.getErrorCount());
|
||||||
|
FieldError fieldError = result.getFieldError("name");
|
||||||
|
assertEquals("name", fieldError.getField());
|
||||||
|
System.out.println(Arrays.asList(fieldError.getCodes()));
|
||||||
|
System.out.println(fieldError.getDefaultMessage());
|
||||||
|
fieldError = result.getFieldError("address.street");
|
||||||
|
assertEquals("address.street", fieldError.getField());
|
||||||
|
System.out.println(Arrays.asList(fieldError.getCodes()));
|
||||||
|
System.out.println(fieldError.getDefaultMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSpringValidationWithClassLevel() throws Exception {
|
||||||
|
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
|
||||||
|
validator.afterPropertiesSet();
|
||||||
|
ValidPerson person = new ValidPerson();
|
||||||
|
person.setName("Juergen");
|
||||||
|
person.getAddress().setStreet("Juergen's Street");
|
||||||
|
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person");
|
||||||
|
validator.validate(person, result);
|
||||||
|
assertEquals(1, result.getErrorCount());
|
||||||
|
ObjectError fieldError = result.getGlobalError();
|
||||||
|
System.out.println(Arrays.asList(fieldError.getCodes()));
|
||||||
|
System.out.println(fieldError.getDefaultMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NameAddressValid
|
||||||
|
private static class ValidPerson {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Valid
|
||||||
|
private ValidAddress address = new ValidAddress();
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidAddress getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddress(ValidAddress address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class ValidAddress {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String street;
|
||||||
|
|
||||||
|
public String getStreet() {
|
||||||
|
return street;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStreet(String street) {
|
||||||
|
this.street = street;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Constraint(validatedBy = NameAddressValidator.class)
|
||||||
|
public @interface NameAddressValid {
|
||||||
|
|
||||||
|
String message() default "Street must not contain name";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends ConstraintPayload>[] payload() default {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class NameAddressValidator implements ConstraintValidator<NameAddressValid, ValidPerson> {
|
||||||
|
|
||||||
|
public void initialize(NameAddressValid constraintAnnotation) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValid(ValidPerson value, ConstraintValidatorContext constraintValidatorContext) {
|
||||||
|
return (value.name == null || !value.address.street.contains(value.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -37,7 +37,10 @@ import org.springframework.util.ClassUtils;
|
||||||
*/
|
*/
|
||||||
public class TypeDescriptor {
|
public class TypeDescriptor {
|
||||||
|
|
||||||
public final static TypeDescriptor NULL = new TypeDescriptor((Class<?>) null);
|
/**
|
||||||
|
* Constant defining an 'empty' TypeDescriptor.
|
||||||
|
*/
|
||||||
|
public static final TypeDescriptor NULL = new TypeDescriptor();
|
||||||
|
|
||||||
|
|
||||||
private Class<?> type;
|
private Class<?> type;
|
||||||
|
|
@ -50,17 +53,20 @@ public class TypeDescriptor {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new descriptor for the given type.
|
* Create a new descriptor for the given type.
|
||||||
* Use this constructor when a conversion point comes from a source such as a Map or collection, where no additional context is available.
|
* <p>Use this constructor when a conversion point comes from a source such as a Map
|
||||||
* @param type the actual type
|
* or Collection, where no additional context is available.
|
||||||
|
* @param type the actual type to wrap
|
||||||
*/
|
*/
|
||||||
public TypeDescriptor(Class<?> type) {
|
public TypeDescriptor(Class<?> type) {
|
||||||
|
Assert.notNull(type, "Type must not be null");
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new type descriptor from a method or constructor parameter.
|
* Create a new type descriptor from a method or constructor parameter.
|
||||||
* Use this constructor when a target conversion point originates from a method parameter, such as a setter method argument.
|
* <p>Use this constructor when a target conversion point originates from a method parameter,
|
||||||
|
* such as a setter method argument.
|
||||||
* @param methodParameter the MethodParameter to wrap
|
* @param methodParameter the MethodParameter to wrap
|
||||||
*/
|
*/
|
||||||
public TypeDescriptor(MethodParameter methodParameter) {
|
public TypeDescriptor(MethodParameter methodParameter) {
|
||||||
|
|
@ -78,6 +84,12 @@ public class TypeDescriptor {
|
||||||
this.field = field;
|
this.field = field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal constructor for a NULL descriptor.
|
||||||
|
*/
|
||||||
|
private TypeDescriptor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the wrapped MethodParameter, if any.
|
* Return the wrapped MethodParameter, if any.
|
||||||
|
|
@ -212,7 +224,7 @@ public class TypeDescriptor {
|
||||||
public Annotation[] getAnnotations() {
|
public Annotation[] getAnnotations() {
|
||||||
if (this.field != null) {
|
if (this.field != null) {
|
||||||
if (this.cachedFieldAnnotations == null) {
|
if (this.cachedFieldAnnotations == null) {
|
||||||
this.cachedFieldAnnotations = field.getAnnotations();
|
this.cachedFieldAnnotations = this.field.getAnnotations();
|
||||||
}
|
}
|
||||||
return this.cachedFieldAnnotations;
|
return this.cachedFieldAnnotations;
|
||||||
}
|
}
|
||||||
|
|
@ -317,7 +329,7 @@ public class TypeDescriptor {
|
||||||
*/
|
*/
|
||||||
public static TypeDescriptor valueOf(Class type) {
|
public static TypeDescriptor valueOf(Class type) {
|
||||||
// TODO needs a cache for common type descriptors
|
// TODO needs a cache for common type descriptors
|
||||||
return new TypeDescriptor(type);
|
return (type != null ? new TypeDescriptor(type) : NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.core.convert.converter;
|
package org.springframework.core.convert.converter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -25,7 +26,7 @@ package org.springframework.core.convert.converter;
|
||||||
* for example {@link Number} for a set of number subtypes.
|
* for example {@link Number} for a set of number subtypes.
|
||||||
*/
|
*/
|
||||||
public interface ConverterFactory<S, R> {
|
public interface ConverterFactory<S, R> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the converter to convert from S to target type T, where T is also an instance of R.
|
* Get the converter to convert from S to target type T, where T is also an instance of R.
|
||||||
* @param <T> the target type
|
* @param <T> the target type
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,14 @@ package org.springframework.core.convert.converter;
|
||||||
public interface ConverterRegistry {
|
public interface ConverterRegistry {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a converter to this registry.
|
* Add a plain converter to this registry.
|
||||||
*/
|
*/
|
||||||
void add(Converter<?, ?> converter);
|
void addConverter(Converter<?, ?> converter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a converter factory to this registry.
|
* Add a factory-created converter to this registry.
|
||||||
*/
|
*/
|
||||||
void add(ConverterFactory<?, ?> converterFactory);
|
void addConverter(ConverterFactory<?, ?> converterFactory);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the conversion logic from the sourceType to the targetType.
|
* Remove the conversion logic from the sourceType to the targetType.
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ import org.springframework.util.NumberUtils;
|
||||||
* @see java.math.BigDecimal
|
* @see java.math.BigDecimal
|
||||||
* @see NumberUtils
|
* @see NumberUtils
|
||||||
*/
|
*/
|
||||||
public class CharacterToNumberFactory implements ConverterFactory<Character, Number> {
|
class CharacterToNumberFactory implements ConverterFactory<Character, Number> {
|
||||||
|
|
||||||
public <T extends Number> Converter<Character, T> getConverter(Class<T> targetType) {
|
public <T extends Number> Converter<Character, T> getConverter(Class<T> targetType) {
|
||||||
return new CharacterToNumber<T>(targetType);
|
return new CharacterToNumber<T>(targetType);
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ package org.springframework.core.convert.support;
|
||||||
* converters for a number of standard Java types like Class, Number, Boolean and so on.
|
* converters for a number of standard Java types like Class, Number, Boolean and so on.
|
||||||
*
|
*
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class DefaultConversionService extends GenericConversionService {
|
public class DefaultConversionService extends GenericConversionService {
|
||||||
|
|
@ -29,22 +30,22 @@ public class DefaultConversionService extends GenericConversionService {
|
||||||
* Create a new default conversion service, installing the default converters.
|
* Create a new default conversion service, installing the default converters.
|
||||||
*/
|
*/
|
||||||
public DefaultConversionService() {
|
public DefaultConversionService() {
|
||||||
add(new StringToByte());
|
addConverter(new StringToByte());
|
||||||
add(new StringToBoolean());
|
addConverter(new StringToBoolean());
|
||||||
add(new StringToCharacter());
|
addConverter(new StringToCharacter());
|
||||||
add(new StringToShort());
|
addConverter(new StringToShort());
|
||||||
add(new StringToInteger());
|
addConverter(new StringToInteger());
|
||||||
add(new StringToLong());
|
addConverter(new StringToLong());
|
||||||
add(new StringToFloat());
|
addConverter(new StringToFloat());
|
||||||
add(new StringToDouble());
|
addConverter(new StringToDouble());
|
||||||
add(new StringToBigInteger());
|
addConverter(new StringToBigInteger());
|
||||||
add(new StringToBigDecimal());
|
addConverter(new StringToBigDecimal());
|
||||||
add(new StringToLocale());
|
addConverter(new StringToLocale());
|
||||||
add(new NumberToCharacter());
|
addConverter(new NumberToCharacter());
|
||||||
add(new ObjectToString());
|
addConverter(new ObjectToString());
|
||||||
add(new StringToEnumFactory());
|
addConverter(new StringToEnumFactory());
|
||||||
add(new NumberToNumberFactory());
|
addConverter(new NumberToNumberFactory());
|
||||||
add(new CharacterToNumberFactory());
|
addConverter(new CharacterToNumberFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ import org.springframework.util.ClassUtils;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
* @see #add(Converter)
|
* @see #addConverter(Converter)
|
||||||
* @see #add(ConverterFactory)
|
* @see #addConverter(ConverterFactory)
|
||||||
*/
|
*/
|
||||||
public class GenericConversionService implements ConversionService, ConverterRegistry {
|
public class GenericConversionService implements ConversionService, ConverterRegistry {
|
||||||
|
|
||||||
|
|
@ -61,23 +61,23 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the converters in the set provided.
|
* Registers the converters in the set provided.
|
||||||
* JavaBean-friendly alternative to calling {@link #add(Converter)}.
|
* JavaBean-friendly alternative to calling {@link #addConverter(Converter)}.
|
||||||
* @see #add(Converter)
|
* @see #addConverter(Converter)
|
||||||
*/
|
*/
|
||||||
public void setConverters(Set<Converter> converters) {
|
public void setConverters(Set<Converter> converters) {
|
||||||
for (Converter converter : converters) {
|
for (Converter converter : converters) {
|
||||||
add(converter);
|
addConverter(converter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the converters in the set provided.
|
* Registers the converters in the set provided.
|
||||||
* JavaBean-friendly alternative to calling {@link #add(ConverterFactory)}.
|
* JavaBean-friendly alternative to calling {@link #addConverter(ConverterFactory)}.
|
||||||
* @see #add(ConverterFactory)
|
* @see #addConverter(ConverterFactory)
|
||||||
*/
|
*/
|
||||||
public void setConverterFactories(Set<ConverterFactory> converters) {
|
public void setConverterFactories(Set<ConverterFactory> converters) {
|
||||||
for (ConverterFactory converterFactory : converters) {
|
for (ConverterFactory converterFactory : converters) {
|
||||||
add(converterFactory);
|
addConverter(converterFactory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,7 +98,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
|
|
||||||
// implementing ConverterRegistry
|
// implementing ConverterRegistry
|
||||||
|
|
||||||
public void add(Converter converter) {
|
public void addConverter(Converter converter) {
|
||||||
List<Class> typeInfo = getRequiredTypeInfo(converter);
|
List<Class> typeInfo = getRequiredTypeInfo(converter);
|
||||||
if (typeInfo == null) {
|
if (typeInfo == null) {
|
||||||
throw new IllegalArgumentException("Unable to the determine sourceType <S> and targetType <T> your Converter<S, T> converts between");
|
throw new IllegalArgumentException("Unable to the determine sourceType <S> and targetType <T> your Converter<S, T> converts between");
|
||||||
|
|
@ -108,7 +108,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
getSourceMap(sourceType).put(targetType, converter);
|
getSourceMap(sourceType).put(targetType, converter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(ConverterFactory<?, ?> converterFactory) {
|
public void addConverter(ConverterFactory<?, ?> converterFactory) {
|
||||||
List<Class> typeInfo = getRequiredTypeInfo(converterFactory);
|
List<Class> typeInfo = getRequiredTypeInfo(converterFactory);
|
||||||
if (typeInfo == null) {
|
if (typeInfo == null) {
|
||||||
throw new IllegalArgumentException("Unable to the determine sourceType <S> and targetType <T> your ConverterFactory<S, T> creates Converters to convert between");
|
throw new IllegalArgumentException("Unable to the determine sourceType <S> and targetType <T> your ConverterFactory<S, T> creates Converters to convert between");
|
||||||
|
|
@ -153,12 +153,12 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new ConverterNotFoundException(source.getClass(), targetType.getType(),
|
throw new ConverterNotFoundException(source.getClass(), targetType.getType(),
|
||||||
"No converter found that can convert from sourceType [" + source.getClass().getName()
|
"No converter found that can convert from source type [" + source.getClass().getName() +
|
||||||
+ "] to targetType [" + targetType.getName() + "]");
|
"] to target type [" + targetType.getName() + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConversionExecutor getConversionExecutor(Class<?> sourceClass, TypeDescriptor targetType)
|
ConversionExecutor getConversionExecutor(final Class<?> sourceClass, final TypeDescriptor targetType)
|
||||||
throws ConverterNotFoundException {
|
throws ConverterNotFoundException {
|
||||||
|
|
||||||
Assert.notNull(sourceClass, "The sourceType to convert from is required");
|
Assert.notNull(sourceClass, "The sourceType to convert from is required");
|
||||||
|
|
@ -175,8 +175,8 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
else if (targetType.isCollection()) {
|
else if (targetType.isCollection()) {
|
||||||
if (targetType.isAbstractClass()) {
|
if (targetType.isAbstractClass()) {
|
||||||
throw new IllegalArgumentException("Conversion target class [" + targetType.getName()
|
throw new IllegalArgumentException("Conversion target class [" + targetType.getName()
|
||||||
+ "] is invalid; cannot convert to abstract collection types--"
|
+ "] is invalid; cannot convert to abstract collection types. "
|
||||||
+ "request an interface or concrete implementation instead");
|
+ "Request an interface or a concrete implementation instead!");
|
||||||
}
|
}
|
||||||
return new ArrayToCollection(sourceType, targetType, this);
|
return new ArrayToCollection(sourceType, targetType, this);
|
||||||
}
|
}
|
||||||
|
|
@ -190,9 +190,8 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (targetType.getType().equals(String.class)) {
|
if (sourceType.getElementType().equals(String.class)) {
|
||||||
// array to string
|
return new StringArrayToObject(targetType, this);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// array to object
|
// array to object
|
||||||
|
|
@ -210,14 +209,15 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
else if (targetType.isMap()) {
|
else if (targetType.isMap()) {
|
||||||
if (sourceType.getElementType().equals(String.class)) {
|
if (sourceType.getElementType().equals(String.class)) {
|
||||||
return new StringCollectionToMap(sourceType, targetType, this);
|
return new StringCollectionToMap(sourceType, targetType, this);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// object collection to map
|
// object collection to map
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (targetType.getType().equals(String.class)) {
|
if (targetType.getType().equals(String.class)) {
|
||||||
// collection to string;
|
// collection to string
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -255,7 +255,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
}
|
}
|
||||||
if (targetType.isArray()) {
|
if (targetType.isArray()) {
|
||||||
if (sourceType.getType().equals(String.class)) {
|
if (sourceType.getType().equals(String.class)) {
|
||||||
return new StringToArray(sourceType, targetType, this);
|
return new StringToArray(targetType, this);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new ObjectToArray(sourceType, targetType, this);
|
return new ObjectToArray(sourceType, targetType, this);
|
||||||
|
|
@ -281,10 +281,22 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
if (sourceType.isAssignableTo(targetType)) {
|
if (sourceType.isAssignableTo(targetType)) {
|
||||||
return NoOpConversionExecutor.INSTANCE;
|
return NoOpConversionExecutor.INSTANCE;
|
||||||
}
|
}
|
||||||
Converter converter = findRegisteredConverter(ClassUtils.resolvePrimitiveIfNecessary(sourceType.getType()), ClassUtils.resolvePrimitiveIfNecessary(targetType.getType()));
|
Converter converter = findRegisteredConverter(
|
||||||
|
ClassUtils.resolvePrimitiveIfNecessary(sourceClass),
|
||||||
|
ClassUtils.resolvePrimitiveIfNecessary(targetType.getType()));
|
||||||
if (converter != null) {
|
if (converter != null) {
|
||||||
return new StaticConversionExecutor(sourceType, targetType, converter);
|
return new StaticConversionExecutor(sourceType, targetType, converter);
|
||||||
}
|
}
|
||||||
|
else if (this.parent instanceof GenericConversionService) {
|
||||||
|
return ((GenericConversionService) this.parent).getConversionExecutor(sourceClass, targetType);
|
||||||
|
}
|
||||||
|
else if (this.parent != null && this.parent.canConvert(sourceClass, targetType)){
|
||||||
|
return new ConversionExecutor() {
|
||||||
|
public Object execute(Object source) {
|
||||||
|
return parent.convert(source, targetType);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -356,7 +368,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
return sourceMap;
|
return sourceMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Converter findRegisteredConverter(Class<?> sourceType, Class<?> targetType) {
|
protected <T> Converter findRegisteredConverter(Class<?> sourceType, Class<T> targetType) {
|
||||||
if (sourceType.isInterface()) {
|
if (sourceType.isInterface()) {
|
||||||
LinkedList<Class> classQueue = new LinkedList<Class>();
|
LinkedList<Class> classQueue = new LinkedList<Class>();
|
||||||
classQueue.addFirst(sourceType);
|
classQueue.addFirst(sourceType);
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ import org.springframework.util.NumberUtils;
|
||||||
* @see java.math.BigDecimal
|
* @see java.math.BigDecimal
|
||||||
* @see NumberUtils
|
* @see NumberUtils
|
||||||
*/
|
*/
|
||||||
public class NumberToCharacter implements Converter<Number, Character> {
|
class NumberToCharacter implements Converter<Number, Character> {
|
||||||
|
|
||||||
public Character convert(Number source) {
|
public Character convert(Number source) {
|
||||||
return (char) source.shortValue();
|
return (char) source.shortValue();
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ import org.springframework.util.NumberUtils;
|
||||||
* @see java.math.BigDecimal
|
* @see java.math.BigDecimal
|
||||||
* @see NumberUtils
|
* @see NumberUtils
|
||||||
*/
|
*/
|
||||||
public class NumberToNumberFactory implements ConverterFactory<Number, Number> {
|
class NumberToNumberFactory implements ConverterFactory<Number, Number> {
|
||||||
|
|
||||||
public <T extends Number> Converter<Number, T> getConverter(Class<T> targetType) {
|
public <T extends Number> Converter<Number, T> getConverter(Class<T> targetType) {
|
||||||
return new NumberToNumber<T>(targetType);
|
return new NumberToNumber<T>(targetType);
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import org.springframework.core.convert.TypeDescriptor;
|
||||||
*/
|
*/
|
||||||
class ObjectToArray implements ConversionExecutor {
|
class ObjectToArray implements ConversionExecutor {
|
||||||
|
|
||||||
private final TypeDescriptor targetArrayType;
|
private final Class elementType;
|
||||||
|
|
||||||
private final ConversionExecutor elementConverter;
|
private final ConversionExecutor elementConverter;
|
||||||
|
|
||||||
|
|
@ -38,15 +38,15 @@ class ObjectToArray implements ConversionExecutor {
|
||||||
public ObjectToArray(TypeDescriptor sourceObjectType, TypeDescriptor targetArrayType,
|
public ObjectToArray(TypeDescriptor sourceObjectType, TypeDescriptor targetArrayType,
|
||||||
GenericConversionService conversionService) {
|
GenericConversionService conversionService) {
|
||||||
|
|
||||||
this.targetArrayType = targetArrayType;
|
this.elementType = targetArrayType.getElementType();
|
||||||
this.elementConverter = conversionService.getConversionExecutor(
|
this.elementConverter = conversionService.getConversionExecutor(
|
||||||
sourceObjectType.getType(), TypeDescriptor.valueOf(targetArrayType.getElementType()));
|
sourceObjectType.getType(), TypeDescriptor.valueOf(this.elementType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Object execute(Object source) throws ConversionFailedException {
|
public Object execute(Object source) throws ConversionFailedException {
|
||||||
Object array = Array.newInstance(targetArrayType.getElementType(), 1);
|
Object array = Array.newInstance(this.elementType, 1);
|
||||||
Object element = elementConverter.execute(source);
|
Object element = this.elementConverter.execute(source);
|
||||||
Array.set(array, 0, element);
|
Array.set(array, 0, element);
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import org.springframework.core.convert.converter.Converter;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class ObjectToString implements Converter<Object, String> {
|
class ObjectToString implements Converter<Object, String> {
|
||||||
|
|
||||||
public String convert(Object source) {
|
public String convert(Object source) {
|
||||||
return source.toString();
|
return source.toString();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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.core.convert.support;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.ConversionFailedException;
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a String array to a single element.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
class StringArrayToObject implements ConversionExecutor {
|
||||||
|
|
||||||
|
private final ConversionExecutor elementConverter;
|
||||||
|
|
||||||
|
|
||||||
|
public StringArrayToObject(TypeDescriptor targetType, GenericConversionService conversionService) {
|
||||||
|
this.elementConverter = conversionService.getConversionExecutor(String.class, targetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Object execute(Object source) throws ConversionFailedException {
|
||||||
|
String str = StringUtils.arrayToCommaDelimitedString(ObjectUtils.toObjectArray(source));
|
||||||
|
return this.elementConverter.execute(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.core.convert.support;
|
package org.springframework.core.convert.support;
|
||||||
|
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a comma-delimited string to an array.
|
* Converts a comma-delimited string to an array.
|
||||||
|
|
@ -27,15 +28,18 @@ import org.springframework.core.convert.TypeDescriptor;
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
class StringToArray implements ConversionExecutor {
|
class StringToArray implements ConversionExecutor {
|
||||||
|
|
||||||
private ArrayToArray converter;
|
private final ArrayToArray converter;
|
||||||
|
|
||||||
public StringToArray(TypeDescriptor sourceType, TypeDescriptor targetType, GenericConversionService conversionService) {
|
|
||||||
converter = new ArrayToArray(TypeDescriptor.valueOf(String[].class), targetType, conversionService);
|
public StringToArray(TypeDescriptor targetType, GenericConversionService conversionService) {
|
||||||
|
this.converter = new ArrayToArray(TypeDescriptor.valueOf(String[].class), targetType, conversionService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Object execute(Object source) {
|
public Object execute(Object source) {
|
||||||
String string = (String) source;
|
String str = (String) source;
|
||||||
String[] fields = string.split(",");
|
String[] fields = StringUtils.commaDelimitedListToStringArray(str);
|
||||||
return converter.execute(fields);
|
return this.converter.execute(fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.core.convert.support;
|
package org.springframework.core.convert.support;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
@ -26,10 +27,10 @@ import org.springframework.util.NumberUtils;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class StringToBigDecimal implements Converter<String, BigDecimal> {
|
class StringToBigDecimal implements Converter<String, BigDecimal> {
|
||||||
|
|
||||||
public BigDecimal convert(String source) {
|
public BigDecimal convert(String source) {
|
||||||
return NumberUtils.parseNumber(source, BigDecimal.class);
|
return NumberUtils.parseNumber(source, BigDecimal.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.core.convert.support;
|
package org.springframework.core.convert.support;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
|
@ -26,10 +27,10 @@ import org.springframework.util.NumberUtils;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class StringToBigInteger implements Converter<String, BigInteger> {
|
class StringToBigInteger implements Converter<String, BigInteger> {
|
||||||
|
|
||||||
public BigInteger convert(String source) {
|
public BigInteger convert(String source) {
|
||||||
return NumberUtils.parseNumber(source, BigInteger.class);
|
return NumberUtils.parseNumber(source, BigInteger.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,18 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.core.convert.support;
|
package org.springframework.core.convert.support;
|
||||||
|
|
||||||
import org.springframework.core.convert.converter.Converter;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts String to a Boolean..
|
* Converts String to a Boolean.
|
||||||
*
|
*
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class StringToBoolean implements Converter<String, Boolean> {
|
class StringToBoolean implements Converter<String, Boolean> {
|
||||||
|
|
||||||
public Boolean convert(String source) {
|
public Boolean convert(String source) {
|
||||||
if (source.equals("true")) {
|
if (source.equals("true")) {
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,10 @@ import org.springframework.util.NumberUtils;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class StringToByte implements Converter<String, Byte> {
|
class StringToByte implements Converter<String, Byte> {
|
||||||
|
|
||||||
public Byte convert(String source) {
|
public Byte convert(String source) {
|
||||||
return NumberUtils.parseNumber(source, Byte.class);
|
return NumberUtils.parseNumber(source, Byte.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import org.springframework.core.convert.converter.Converter;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class StringToCharacter implements Converter<String, Character> {
|
class StringToCharacter implements Converter<String, Character> {
|
||||||
|
|
||||||
public Character convert(String source) {
|
public Character convert(String source) {
|
||||||
if (source.length() != 1) {
|
if (source.length() != 1) {
|
||||||
|
|
|
||||||
|
|
@ -21,22 +21,25 @@ import org.springframework.core.convert.TypeDescriptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a comma-delimited string to a collection.
|
* Converts a comma-delimited string to a collection.
|
||||||
|
*
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
class StringToCollection implements ConversionExecutor {
|
class StringToCollection implements ConversionExecutor {
|
||||||
|
|
||||||
private ArrayToCollection converter;
|
private final ArrayToCollection converter;
|
||||||
|
|
||||||
|
|
||||||
public StringToCollection(TypeDescriptor sourceType, TypeDescriptor targetType, GenericConversionService conversionService) {
|
public StringToCollection(TypeDescriptor sourceType, TypeDescriptor targetType, GenericConversionService conversionService) {
|
||||||
converter = new ArrayToCollection(sourceType, targetType, conversionService);
|
this.converter = new ArrayToCollection(sourceType, targetType, conversionService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Object execute(Object source) throws ConversionFailedException {
|
public Object execute(Object source) throws ConversionFailedException {
|
||||||
String string = (String) source;
|
String string = (String) source;
|
||||||
String[] fields = string.split(",");
|
String[] fields = string.split(",");
|
||||||
return converter.execute(fields);
|
return this.converter.execute(fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import org.springframework.util.NumberUtils;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class StringToDouble implements Converter<String, Double> {
|
class StringToDouble implements Converter<String, Double> {
|
||||||
|
|
||||||
public Double convert(String source) {
|
public Double convert(String source) {
|
||||||
return NumberUtils.parseNumber(source, Double.class);
|
return NumberUtils.parseNumber(source, Double.class);
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import org.springframework.core.convert.converter.ConverterInfo;
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public class StringToEnumFactory implements ConverterFactory<String, Enum> {
|
class StringToEnumFactory implements ConverterFactory<String, Enum> {
|
||||||
|
|
||||||
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
|
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
|
||||||
return new StringToEnum(targetType);
|
return new StringToEnum(targetType);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import org.springframework.util.NumberUtils;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class StringToFloat implements Converter<String, Float> {
|
class StringToFloat implements Converter<String, Float> {
|
||||||
|
|
||||||
public Float convert(String source) {
|
public Float convert(String source) {
|
||||||
return NumberUtils.parseNumber(source, Float.class);
|
return NumberUtils.parseNumber(source, Float.class);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import org.springframework.util.NumberUtils;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class StringToInteger implements Converter<String, Integer> {
|
class StringToInteger implements Converter<String, Integer> {
|
||||||
|
|
||||||
public Integer convert(String source) {
|
public Integer convert(String source) {
|
||||||
return NumberUtils.parseNumber(source, Integer.class);
|
return NumberUtils.parseNumber(source, Integer.class);
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import org.springframework.util.StringUtils;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class StringToLocale implements Converter<String, Locale> {
|
class StringToLocale implements Converter<String, Locale> {
|
||||||
|
|
||||||
public Locale convert(String source) {
|
public Locale convert(String source) {
|
||||||
return StringUtils.parseLocaleString(source);
|
return StringUtils.parseLocaleString(source);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import org.springframework.util.NumberUtils;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class StringToLong implements Converter<String, Long> {
|
class StringToLong implements Converter<String, Long> {
|
||||||
|
|
||||||
public Long convert(String source) {
|
public Long convert(String source) {
|
||||||
return NumberUtils.parseNumber(source, Long.class);
|
return NumberUtils.parseNumber(source, Long.class);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import org.springframework.util.NumberUtils;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class StringToShort implements Converter<String, Short> {
|
class StringToShort implements Converter<String, Short> {
|
||||||
|
|
||||||
public Short convert(String source) {
|
public Short convert(String source) {
|
||||||
return NumberUtils.parseNumber(source, Short.class);
|
return NumberUtils.parseNumber(source, Short.class);
|
||||||
|
|
|
||||||
|
|
@ -776,7 +776,7 @@ public abstract class ClassUtils {
|
||||||
* @param clazz the class to check
|
* @param clazz the class to check
|
||||||
* @return the original class, or a primitive wrapper for the original primitive type
|
* @return the original class, or a primitive wrapper for the original primitive type
|
||||||
*/
|
*/
|
||||||
public static Class resolvePrimitiveIfNecessary(Class clazz) {
|
public static Class<?> resolvePrimitiveIfNecessary(Class clazz) {
|
||||||
Assert.notNull(clazz, "Class must not be null");
|
Assert.notNull(clazz, "Class must not be null");
|
||||||
return (clazz.isPrimitive() ? primitiveTypeToWrapperMap.get(clazz) : clazz);
|
return (clazz.isPrimitive() ? primitiveTypeToWrapperMap.get(clazz) : clazz);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -356,7 +356,7 @@ public abstract class StringUtils {
|
||||||
}
|
}
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
int idx = 0;
|
int idx;
|
||||||
while ((idx = str.indexOf(sub, pos)) != -1) {
|
while ((idx = str.indexOf(sub, pos)) != -1) {
|
||||||
++count;
|
++count;
|
||||||
pos = idx + sub.length();
|
pos = idx + sub.length();
|
||||||
|
|
@ -997,7 +997,7 @@ public abstract class StringUtils {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
int delPos = 0;
|
int delPos;
|
||||||
while ((delPos = str.indexOf(delimiter, pos)) != -1) {
|
while ((delPos = str.indexOf(delimiter, pos)) != -1) {
|
||||||
result.add(deleteAny(str.substring(pos, delPos), charsToDelete));
|
result.add(deleteAny(str.substring(pos, delPos), charsToDelete));
|
||||||
pos = delPos + delimiter.length();
|
pos = delPos + delimiter.length();
|
||||||
|
|
@ -1090,6 +1090,9 @@ public abstract class StringUtils {
|
||||||
if (ObjectUtils.isEmpty(arr)) {
|
if (ObjectUtils.isEmpty(arr)) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
if (arr.length == 1) {
|
||||||
|
return ObjectUtils.nullSafeToString(arr[0]);
|
||||||
|
}
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (int i = 0; i < arr.length; i++) {
|
for (int i = 0; i < arr.length; i++) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ public class GenericConversionServiceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void executeConversion() {
|
public void executeConversion() {
|
||||||
converter.add(new StringToInteger());
|
converter.addConverter(new StringToInteger());
|
||||||
assertEquals(new Integer(3), converter.convert("3", Integer.class));
|
assertEquals(new Integer(3), converter.convert("3", Integer.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@ public class GenericConversionServiceTests {
|
||||||
@Test
|
@Test
|
||||||
public void addConverterNoSourceTargetClassInfoAvailable() {
|
public void addConverterNoSourceTargetClassInfoAvailable() {
|
||||||
try {
|
try {
|
||||||
converter.add(new Converter() {
|
converter.addConverter(new Converter() {
|
||||||
public Object convert(Object source) throws Exception {
|
public Object convert(Object source) throws Exception {
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +97,7 @@ public class GenericConversionServiceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWrongTypeArgument() {
|
public void convertWrongTypeArgument() {
|
||||||
converter.add(new StringToInteger());
|
converter.addConverter(new StringToInteger());
|
||||||
try {
|
try {
|
||||||
converter.convert("BOGUS", Integer.class);
|
converter.convert("BOGUS", Integer.class);
|
||||||
fail("Should have failed");
|
fail("Should have failed");
|
||||||
|
|
@ -108,7 +108,7 @@ public class GenericConversionServiceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertSuperSourceType() {
|
public void convertSuperSourceType() {
|
||||||
converter.add(new Converter<CharSequence, Integer>() {
|
converter.addConverter(new Converter<CharSequence, Integer>() {
|
||||||
public Integer convert(CharSequence source) throws Exception {
|
public Integer convert(CharSequence source) throws Exception {
|
||||||
return Integer.valueOf(source.toString());
|
return Integer.valueOf(source.toString());
|
||||||
}
|
}
|
||||||
|
|
@ -119,14 +119,14 @@ public class GenericConversionServiceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertObjectToPrimitive() {
|
public void convertObjectToPrimitive() {
|
||||||
converter.add(new StringToInteger());
|
converter.addConverter(new StringToInteger());
|
||||||
Integer three = converter.convert("3", int.class);
|
Integer three = converter.convert("3", int.class);
|
||||||
assertEquals(3, three.intValue());
|
assertEquals(3, three.intValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertArrayToArray() {
|
public void convertArrayToArray() {
|
||||||
converter.add(new StringToInteger());
|
converter.addConverter(new StringToInteger());
|
||||||
Integer[] result = converter.convert(new String[] { "1", "2", "3" }, Integer[].class);
|
Integer[] result = converter.convert(new String[] { "1", "2", "3" }, Integer[].class);
|
||||||
assertEquals(new Integer(1), result[0]);
|
assertEquals(new Integer(1), result[0]);
|
||||||
assertEquals(new Integer(2), result[1]);
|
assertEquals(new Integer(2), result[1]);
|
||||||
|
|
@ -135,7 +135,7 @@ public class GenericConversionServiceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertArrayToPrimitiveArray() {
|
public void convertArrayToPrimitiveArray() {
|
||||||
converter.add(new StringToInteger());
|
converter.addConverter(new StringToInteger());
|
||||||
int[] result = converter.convert(new String[] { "1", "2", "3" }, int[].class);
|
int[] result = converter.convert(new String[] { "1", "2", "3" }, int[].class);
|
||||||
assertEquals(1, result[0]);
|
assertEquals(1, result[0]);
|
||||||
assertEquals(2, result[1]);
|
assertEquals(2, result[1]);
|
||||||
|
|
@ -154,7 +154,7 @@ public class GenericConversionServiceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertArrayToListGenericTypeConversion() throws Exception {
|
public void convertArrayToListGenericTypeConversion() throws Exception {
|
||||||
converter.add(new StringToInteger());
|
converter.addConverter(new StringToInteger());
|
||||||
List<Integer> result = (List<Integer>) converter.convert(new String[] { "1", "2", "3" }, new TypeDescriptor(getClass().getDeclaredField("genericList")));
|
List<Integer> result = (List<Integer>) converter.convert(new String[] { "1", "2", "3" }, new TypeDescriptor(getClass().getDeclaredField("genericList")));
|
||||||
assertEquals(new Integer("1"), result.get(0));
|
assertEquals(new Integer("1"), result.get(0));
|
||||||
assertEquals(new Integer("2"), result.get(1));
|
assertEquals(new Integer("2"), result.get(1));
|
||||||
|
|
@ -192,7 +192,7 @@ public class GenericConversionServiceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertListToArrayWithComponentConversion() {
|
public void convertListToArrayWithComponentConversion() {
|
||||||
converter.add(new StringToInteger());
|
converter.addConverter(new StringToInteger());
|
||||||
List<String> list = new ArrayList<String>();
|
List<String> list = new ArrayList<String>();
|
||||||
list.add("1");
|
list.add("1");
|
||||||
list.add("2");
|
list.add("2");
|
||||||
|
|
@ -210,8 +210,8 @@ public class GenericConversionServiceTests {
|
||||||
Map<String, String> foo = new HashMap<String, String>();
|
Map<String, String> foo = new HashMap<String, String>();
|
||||||
foo.put("1", "BAR");
|
foo.put("1", "BAR");
|
||||||
foo.put("2", "BAZ");
|
foo.put("2", "BAZ");
|
||||||
converter.add(new StringToInteger());
|
converter.addConverter(new StringToInteger());
|
||||||
converter.add(new StringToEnumFactory().getConverter(FooEnum.class));
|
converter.addConverter(new StringToEnumFactory().getConverter(FooEnum.class));
|
||||||
Map<String, FooEnum> map = (Map<String, FooEnum>) converter.convert(foo, new TypeDescriptor(getClass().getField("genericMap")));
|
Map<String, FooEnum> map = (Map<String, FooEnum>) converter.convert(foo, new TypeDescriptor(getClass().getField("genericMap")));
|
||||||
assertEquals(map.get(1), FooEnum.BAR);
|
assertEquals(map.get(1), FooEnum.BAR);
|
||||||
assertEquals(map.get(2), FooEnum.BAZ);
|
assertEquals(map.get(2), FooEnum.BAZ);
|
||||||
|
|
@ -228,7 +228,7 @@ public class GenericConversionServiceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertStringToArrayWithElementConversion() {
|
public void convertStringToArrayWithElementConversion() {
|
||||||
converter.add(new StringToInteger());
|
converter.addConverter(new StringToInteger());
|
||||||
Integer[] result = converter.convert("1,2,3", Integer[].class);
|
Integer[] result = converter.convert("1,2,3", Integer[].class);
|
||||||
assertEquals(3, result.length);
|
assertEquals(3, result.length);
|
||||||
assertEquals(new Integer(1), result[0]);
|
assertEquals(new Integer(1), result[0]);
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,6 @@ import javax.servlet.http.Cookie;
|
||||||
|
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||||
import org.springframework.core.MethodParameter;
|
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.core.style.StylerUtils;
|
import org.springframework.core.style.StylerUtils;
|
||||||
|
|
@ -66,7 +65,6 @@ import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.validation.support.BindingAwareModelMap;
|
import org.springframework.validation.support.BindingAwareModelMap;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
|
||||||
import org.springframework.web.bind.annotation.InitBinder;
|
import org.springframework.web.bind.annotation.InitBinder;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
@ -83,7 +81,6 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||||
import org.springframework.web.portlet.HandlerAdapter;
|
import org.springframework.web.portlet.HandlerAdapter;
|
||||||
import org.springframework.web.portlet.ModelAndView;
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
import org.springframework.web.portlet.bind.MissingPortletRequestParameterException;
|
import org.springframework.web.portlet.bind.MissingPortletRequestParameterException;
|
||||||
import org.springframework.web.portlet.bind.PortletRequestDataBinder;
|
|
||||||
import org.springframework.web.portlet.bind.annotation.ActionMapping;
|
import org.springframework.web.portlet.bind.annotation.ActionMapping;
|
||||||
import org.springframework.web.portlet.bind.annotation.EventMapping;
|
import org.springframework.web.portlet.bind.annotation.EventMapping;
|
||||||
import org.springframework.web.portlet.bind.annotation.RenderMapping;
|
import org.springframework.web.portlet.bind.annotation.RenderMapping;
|
||||||
|
|
@ -335,26 +332,6 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl
|
||||||
return mav;
|
return mav;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Template method for creating a new PortletRequestDataBinder instance.
|
|
||||||
* <p>The default implementation creates a standard PortletRequestDataBinder.
|
|
||||||
* This can be overridden for custom PortletRequestDataBinder subclasses.
|
|
||||||
* @param request current portlet request
|
|
||||||
* @param target the target object to bind onto (or <code>null</code>
|
|
||||||
* if the binder is just used to convert a plain parameter value)
|
|
||||||
* @param objectName the objectName of the target object
|
|
||||||
* @return the PortletRequestDataBinder instance to use
|
|
||||||
* @throws Exception in case of invalid state or arguments
|
|
||||||
* @see PortletRequestDataBinder#bind(javax.portlet.PortletRequest)
|
|
||||||
* @see PortletRequestDataBinder#convertIfNecessary(Object, Class, MethodParameter)
|
|
||||||
*/
|
|
||||||
protected PortletRequestDataBinder createBinder(
|
|
||||||
PortletRequest request, Object target, String objectName) throws Exception {
|
|
||||||
|
|
||||||
return new PortletRequestDataBinder(target, objectName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a HandlerMethodResolver for the given handler type.
|
* Build a HandlerMethodResolver for the given handler type.
|
||||||
*/
|
*/
|
||||||
|
|
@ -579,25 +556,6 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl
|
||||||
throw new PortletSessionRequiredException(message);
|
throw new PortletSessionRequiredException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
|
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
return AnnotationMethodHandlerAdapter.this.createBinder(
|
|
||||||
(PortletRequest) webRequest.getNativeRequest(), target, objectName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doBind(NativeWebRequest webRequest, WebDataBinder binder, boolean failOnErrors)
|
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
PortletRequestDataBinder servletBinder = (PortletRequestDataBinder) binder;
|
|
||||||
servletBinder.bind((PortletRequest) webRequest.getNativeRequest());
|
|
||||||
if (failOnErrors) {
|
|
||||||
servletBinder.closeNoCatch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest)
|
protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,6 @@ import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||||
import org.springframework.core.MethodParameter;
|
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.http.HttpInputMessage;
|
import org.springframework.http.HttpInputMessage;
|
||||||
|
|
@ -74,8 +73,6 @@ import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||||
import org.springframework.web.HttpSessionRequiredException;
|
import org.springframework.web.HttpSessionRequiredException;
|
||||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||||
import org.springframework.web.bind.ServletRequestDataBinder;
|
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
|
||||||
import org.springframework.web.bind.annotation.InitBinder;
|
import org.springframework.web.bind.annotation.InitBinder;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
@ -373,25 +370,6 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Template method for creating a new ServletRequestDataBinder instance.
|
|
||||||
* <p>The default implementation creates a standard ServletRequestDataBinder.
|
|
||||||
* This can be overridden for custom ServletRequestDataBinder subclasses.
|
|
||||||
* @param request current HTTP request
|
|
||||||
* @param target the target object to bind onto (or <code>null</code>
|
|
||||||
* if the binder is just used to convert a plain parameter value)
|
|
||||||
* @param objectName the objectName of the target object
|
|
||||||
* @return the ServletRequestDataBinder instance to use
|
|
||||||
* @throws Exception in case of invalid state or arguments
|
|
||||||
* @see ServletRequestDataBinder#bind(javax.servlet.ServletRequest)
|
|
||||||
* @see ServletRequestDataBinder#convertIfNecessary(Object, Class, MethodParameter)
|
|
||||||
*/
|
|
||||||
protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object target, String objectName)
|
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
return new ServletRequestDataBinder(target, objectName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a HandlerMethodResolver for the given handler type.
|
* Build a HandlerMethodResolver for the given handler type.
|
||||||
*/
|
*/
|
||||||
|
|
@ -617,25 +595,6 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
throw new HttpSessionRequiredException(message);
|
throw new HttpSessionRequiredException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
|
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
return AnnotationMethodHandlerAdapter.this
|
|
||||||
.createBinder((HttpServletRequest) webRequest.getNativeRequest(), target, objectName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doBind(NativeWebRequest webRequest, WebDataBinder binder, boolean failOnErrors)
|
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
|
|
||||||
servletBinder.bind((ServletRequest) webRequest.getNativeRequest());
|
|
||||||
if (failOnErrors) {
|
|
||||||
servletBinder.closeNoCatch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception {
|
protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception {
|
||||||
HttpServletRequest servletRequest = (HttpServletRequest) webRequest.getNativeRequest();
|
HttpServletRequest servletRequest = (HttpServletRequest) webRequest.getNativeRequest();
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@ import javax.servlet.http.Cookie;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.servlet.http.HttpSession;
|
import javax.servlet.http.HttpSession;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
@ -73,12 +75,13 @@ import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.ExtendedModelMap;
|
import org.springframework.ui.ExtendedModelMap;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.ui.ModelMap;
|
import org.springframework.ui.ModelMap;
|
||||||
import org.springframework.ui.format.support.GenericFormatterRegistry;
|
|
||||||
import org.springframework.ui.format.date.DateFormatter;
|
import org.springframework.ui.format.date.DateFormatter;
|
||||||
|
import org.springframework.ui.format.support.GenericFormatterRegistry;
|
||||||
import org.springframework.util.SerializationTestUtils;
|
import org.springframework.util.SerializationTestUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.validation.Errors;
|
import org.springframework.validation.Errors;
|
||||||
|
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.CookieValue;
|
import org.springframework.web.bind.annotation.CookieValue;
|
||||||
import org.springframework.web.bind.annotation.InitBinder;
|
import org.springframework.web.bind.annotation.InitBinder;
|
||||||
|
|
@ -433,13 +436,13 @@ public class ServletAnnotationControllerTests {
|
||||||
@Override
|
@Override
|
||||||
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
|
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
|
||||||
GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
||||||
wac.registerBeanDefinition("controller",
|
wac.registerBeanDefinition("controller", new RootBeanDefinition(MyCommandProvidingFormController.class));
|
||||||
new RootBeanDefinition(MyCommandProvidingFormController.class));
|
|
||||||
wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class));
|
wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class));
|
||||||
RootBeanDefinition registryDef = new RootBeanDefinition(GenericFormatterRegistry.class);
|
RootBeanDefinition registryDef = new RootBeanDefinition(GenericFormatterRegistry.class);
|
||||||
registryDef.getPropertyValues().addPropertyValue("formatters", new DateFormatter("yyyy-MM-dd"));
|
registryDef.getPropertyValues().addPropertyValue("formatters", new DateFormatter("yyyy-MM-dd"));
|
||||||
RootBeanDefinition initializerDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
|
RootBeanDefinition initializerDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
|
||||||
initializerDef.getPropertyValues().addPropertyValue("formatterRegistry", registryDef);
|
initializerDef.getPropertyValues().addPropertyValue("formatterRegistry", registryDef);
|
||||||
|
initializerDef.getPropertyValues().addPropertyValue("validator", new RootBeanDefinition(LocalValidatorFactoryBean.class));
|
||||||
RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
|
RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
|
||||||
adapterDef.getPropertyValues().addPropertyValue("webBindingInitializer", initializerDef);
|
adapterDef.getPropertyValues().addPropertyValue("webBindingInitializer", initializerDef);
|
||||||
wac.registerBeanDefinition("handlerAdapter", adapterDef);
|
wac.registerBeanDefinition("handlerAdapter", adapterDef);
|
||||||
|
|
@ -469,8 +472,7 @@ public class ServletAnnotationControllerTests {
|
||||||
wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class));
|
wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class));
|
||||||
RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
|
RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
|
||||||
adapterDef.getPropertyValues().addPropertyValue("webBindingInitializer", new MyWebBindingInitializer());
|
adapterDef.getPropertyValues().addPropertyValue("webBindingInitializer", new MyWebBindingInitializer());
|
||||||
adapterDef.getPropertyValues()
|
adapterDef.getPropertyValues().addPropertyValue("customArgumentResolver", new MySpecialArgumentResolver());
|
||||||
.addPropertyValue("customArgumentResolver", new MySpecialArgumentResolver());
|
|
||||||
wac.registerBeanDefinition("handlerAdapter", adapterDef);
|
wac.registerBeanDefinition("handlerAdapter", adapterDef);
|
||||||
wac.refresh();
|
wac.refresh();
|
||||||
return wac;
|
return wac;
|
||||||
|
|
@ -1227,6 +1229,20 @@ public class ServletAnnotationControllerTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ValidTestBean extends TestBean {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String validCountry;
|
||||||
|
|
||||||
|
public void setValidCountry(String validCountry) {
|
||||||
|
this.validCountry = validCountry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValidCountry() {
|
||||||
|
return this.validCountry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
public static class MyModelFormController {
|
public static class MyModelFormController {
|
||||||
|
|
||||||
|
|
@ -1253,16 +1269,20 @@ public class ServletAnnotationControllerTests {
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@ModelAttribute("myCommand")
|
@ModelAttribute("myCommand")
|
||||||
private TestBean createTestBean(@RequestParam T defaultName,
|
private ValidTestBean createTestBean(@RequestParam T defaultName,
|
||||||
Map<String, Object> model,
|
Map<String, Object> model, @RequestParam Date date) {
|
||||||
@RequestParam Date date) {
|
|
||||||
model.put("myKey", "myOriginalValue");
|
model.put("myKey", "myOriginalValue");
|
||||||
return new TestBean(defaultName.getClass().getSimpleName() + ":" + defaultName.toString());
|
ValidTestBean tb = new ValidTestBean();
|
||||||
|
tb.setName(defaultName.getClass().getSimpleName() + ":" + defaultName.toString());
|
||||||
|
return tb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@RequestMapping("/myPath.do")
|
@RequestMapping("/myPath.do")
|
||||||
public String myHandle(@ModelAttribute("myCommand") TestBean tb, BindingResult errors, ModelMap model) {
|
public String myHandle(@ModelAttribute("myCommand") @Valid TestBean tb, BindingResult errors, ModelMap model) {
|
||||||
|
if (!errors.hasFieldErrors("validCountry")) {
|
||||||
|
throw new IllegalStateException("Declarative validation not applied");
|
||||||
|
}
|
||||||
return super.myHandle(tb, errors, model);
|
return super.myHandle(tb, errors, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1306,6 +1326,9 @@ public class ServletAnnotationControllerTests {
|
||||||
@InitBinder
|
@InitBinder
|
||||||
private void initBinder(WebDataBinder binder) {
|
private void initBinder(WebDataBinder binder) {
|
||||||
binder.initBeanPropertyAccess();
|
binder.initBeanPropertyAccess();
|
||||||
|
LocalValidatorFactoryBean vf = new LocalValidatorFactoryBean();
|
||||||
|
vf.afterPropertiesSet();
|
||||||
|
binder.setValidator(vf);
|
||||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||||
dateFormat.setLenient(false);
|
dateFormat.setLenient(false);
|
||||||
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
|
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
|
||||||
|
|
@ -1319,6 +1342,9 @@ public class ServletAnnotationControllerTests {
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@InitBinder({"myCommand", "date"})
|
@InitBinder({"myCommand", "date"})
|
||||||
private void initBinder(WebDataBinder binder, String date, @RequestParam("date") String[] date2) {
|
private void initBinder(WebDataBinder binder, String date, @RequestParam("date") String[] date2) {
|
||||||
|
LocalValidatorFactoryBean vf = new LocalValidatorFactoryBean();
|
||||||
|
vf.afterPropertiesSet();
|
||||||
|
binder.setValidator(vf);
|
||||||
assertEquals("2007-10-02", date);
|
assertEquals("2007-10-02", date);
|
||||||
assertEquals(1, date2.length);
|
assertEquals(1, date2.length);
|
||||||
assertEquals("2007-10-02", date2[0]);
|
assertEquals("2007-10-02", date2[0]);
|
||||||
|
|
@ -1331,6 +1357,9 @@ public class ServletAnnotationControllerTests {
|
||||||
private static class MyWebBindingInitializer implements WebBindingInitializer {
|
private static class MyWebBindingInitializer implements WebBindingInitializer {
|
||||||
|
|
||||||
public void initBinder(WebDataBinder binder, WebRequest request) {
|
public void initBinder(WebDataBinder binder, WebRequest request) {
|
||||||
|
LocalValidatorFactoryBean vf = new LocalValidatorFactoryBean();
|
||||||
|
vf.afterPropertiesSet();
|
||||||
|
binder.setValidator(vf);
|
||||||
assertNotNull(request.getLocale());
|
assertNotNull(request.getLocale());
|
||||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||||
dateFormat.setLenient(false);
|
dateFormat.setLenient(false);
|
||||||
|
|
@ -1496,8 +1525,8 @@ public class ServletAnnotationControllerTests {
|
||||||
if (tb == null) {
|
if (tb == null) {
|
||||||
tb = (TestBean) model.get("myCommand");
|
tb = (TestBean) model.get("myCommand");
|
||||||
}
|
}
|
||||||
if (tb.getName().endsWith("myDefaultName")) {
|
if (tb.getName() != null && tb.getName().endsWith("myDefaultName")) {
|
||||||
assertTrue(tb.getDate().getYear() == 107);
|
assertEquals(107, tb.getDate().getYear());
|
||||||
}
|
}
|
||||||
Errors errors = (Errors) model.get(BindingResult.MODEL_KEY_PREFIX + "testBean");
|
Errors errors = (Errors) model.get(BindingResult.MODEL_KEY_PREFIX + "testBean");
|
||||||
if (errors == null) {
|
if (errors == null) {
|
||||||
|
|
@ -1701,11 +1730,8 @@ public class ServletAnnotationControllerTests {
|
||||||
|
|
||||||
public static class MyModelAndViewResolver implements ModelAndViewResolver {
|
public static class MyModelAndViewResolver implements ModelAndViewResolver {
|
||||||
|
|
||||||
public ModelAndView resolveModelAndView(Method handlerMethod,
|
public ModelAndView resolveModelAndView(Method handlerMethod, Class handlerType,
|
||||||
Class handlerType,
|
Object returnValue, ExtendedModelMap implicitModel, NativeWebRequest webRequest) {
|
||||||
Object returnValue,
|
|
||||||
ExtendedModelMap implicitModel,
|
|
||||||
NativeWebRequest webRequest) {
|
|
||||||
if (returnValue instanceof MySpecialArg) {
|
if (returnValue instanceof MySpecialArg) {
|
||||||
return new ModelAndView(new View() {
|
return new ModelAndView(new View() {
|
||||||
public String getContentType() {
|
public String getContentType() {
|
||||||
|
|
@ -1736,7 +1762,6 @@ public class ServletAnnotationControllerTests {
|
||||||
public void param(@RequestParam("myParam") int myParam, Writer writer) throws IOException {
|
public void param(@RequestParam("myParam") int myParam, Writer writer) throws IOException {
|
||||||
writer.write("myParam-" + myParam);
|
writer.write("myParam-" + myParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
|
|
@ -1757,7 +1782,6 @@ public class ServletAnnotationControllerTests {
|
||||||
assertEquals("Invalid path variable value", new Date(108, 10, 18), date);
|
assertEquals("Invalid path variable value", new Date(108, 10, 18), date);
|
||||||
writer.write("test-" + date.getYear());
|
writer.write("test-" + date.getYear());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.core.BridgeMethodResolver;
|
import org.springframework.core.BridgeMethodResolver;
|
||||||
import org.springframework.core.Conventions;
|
import org.springframework.core.Conventions;
|
||||||
|
|
@ -172,6 +173,7 @@ public class HandlerMethodInvoker {
|
||||||
String attrName = null;
|
String attrName = null;
|
||||||
boolean required = false;
|
boolean required = false;
|
||||||
String defaultValue = null;
|
String defaultValue = null;
|
||||||
|
boolean validate = false;
|
||||||
int found = 0;
|
int found = 0;
|
||||||
Annotation[] paramAnns = methodParam.getParameterAnnotations();
|
Annotation[] paramAnns = methodParam.getParameterAnnotations();
|
||||||
|
|
||||||
|
|
@ -211,6 +213,9 @@ public class HandlerMethodInvoker {
|
||||||
attrName = attr.value();
|
attrName = attr.value();
|
||||||
found++;
|
found++;
|
||||||
}
|
}
|
||||||
|
else if ("Valid".equals(paramAnn.annotationType().getSimpleName())) {
|
||||||
|
validate = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found > 1) {
|
if (found > 1) {
|
||||||
|
|
@ -260,10 +265,10 @@ public class HandlerMethodInvoker {
|
||||||
args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
|
args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
|
||||||
}
|
}
|
||||||
else if (attrName != null) {
|
else if (attrName != null) {
|
||||||
WebDataBinder binder = resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
|
WebRequestDataBinder binder = resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
|
||||||
boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
||||||
if (binder.getTarget() != null) {
|
if (binder.getTarget() != null) {
|
||||||
doBind(webRequest, binder, !assignBindingResult);
|
doBind(binder, webRequest, validate, !assignBindingResult);
|
||||||
}
|
}
|
||||||
args[i] = binder.getTarget();
|
args[i] = binder.getTarget();
|
||||||
if (assignBindingResult) {
|
if (assignBindingResult) {
|
||||||
|
|
@ -401,7 +406,7 @@ public class HandlerMethodInvoker {
|
||||||
}
|
}
|
||||||
paramValue = checkValue(paramName, paramValue, paramType);
|
paramValue = checkValue(paramName, paramValue, paramType);
|
||||||
}
|
}
|
||||||
WebDataBinder binder = createBinder(webRequest, null, paramName);
|
WebRequestDataBinder binder = new WebRequestDataBinder(null, paramName);
|
||||||
initBinder(handlerForInitBinderCall, paramName, binder, webRequest);
|
initBinder(handlerForInitBinderCall, paramName, binder, webRequest);
|
||||||
return binder.convertIfNecessary(paramValue, paramType, methodParam);
|
return binder.convertIfNecessary(paramValue, paramType, methodParam);
|
||||||
}
|
}
|
||||||
|
|
@ -428,7 +433,7 @@ public class HandlerMethodInvoker {
|
||||||
}
|
}
|
||||||
headerValue = checkValue(headerName, headerValue, paramType);
|
headerValue = checkValue(headerName, headerValue, paramType);
|
||||||
}
|
}
|
||||||
WebDataBinder binder = createBinder(webRequest, null, headerName);
|
WebRequestDataBinder binder = new WebRequestDataBinder(null, headerName);
|
||||||
initBinder(handlerForInitBinderCall, headerName, binder, webRequest);
|
initBinder(handlerForInitBinderCall, headerName, binder, webRequest);
|
||||||
return binder.convertIfNecessary(headerValue, paramType, methodParam);
|
return binder.convertIfNecessary(headerValue, paramType, methodParam);
|
||||||
}
|
}
|
||||||
|
|
@ -495,7 +500,7 @@ public class HandlerMethodInvoker {
|
||||||
}
|
}
|
||||||
cookieValue = checkValue(cookieName, cookieValue, paramType);
|
cookieValue = checkValue(cookieName, cookieValue, paramType);
|
||||||
}
|
}
|
||||||
WebDataBinder binder = createBinder(webRequest, null, cookieName);
|
WebRequestDataBinder binder = new WebRequestDataBinder(null, cookieName);
|
||||||
initBinder(handlerForInitBinderCall, cookieName, binder, webRequest);
|
initBinder(handlerForInitBinderCall, cookieName, binder, webRequest);
|
||||||
return binder.convertIfNecessary(cookieValue, paramType, methodParam);
|
return binder.convertIfNecessary(cookieValue, paramType, methodParam);
|
||||||
}
|
}
|
||||||
|
|
@ -518,7 +523,7 @@ public class HandlerMethodInvoker {
|
||||||
pathVarName = getRequiredParameterName(methodParam);
|
pathVarName = getRequiredParameterName(methodParam);
|
||||||
}
|
}
|
||||||
String pathVarValue = resolvePathVariable(pathVarName, paramType, webRequest);
|
String pathVarValue = resolvePathVariable(pathVarName, paramType, webRequest);
|
||||||
WebDataBinder binder = createBinder(webRequest, null, pathVarName);
|
WebRequestDataBinder binder = new WebRequestDataBinder(null, pathVarName);
|
||||||
initBinder(handlerForInitBinderCall, pathVarName, binder, webRequest);
|
initBinder(handlerForInitBinderCall, pathVarName, binder, webRequest);
|
||||||
return binder.convertIfNecessary(pathVarValue, paramType, methodParam);
|
return binder.convertIfNecessary(pathVarValue, paramType, methodParam);
|
||||||
}
|
}
|
||||||
|
|
@ -557,7 +562,7 @@ public class HandlerMethodInvoker {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
|
private WebRequestDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
|
||||||
ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler)
|
ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
|
|
@ -580,7 +585,7 @@ public class HandlerMethodInvoker {
|
||||||
else {
|
else {
|
||||||
bindObject = BeanUtils.instantiateClass(paramType);
|
bindObject = BeanUtils.instantiateClass(paramType);
|
||||||
}
|
}
|
||||||
WebDataBinder binder = createBinder(webRequest, bindObject, name);
|
WebRequestDataBinder binder = new WebRequestDataBinder(bindObject, name);
|
||||||
initBinder(handler, name, binder, webRequest);
|
initBinder(handler, name, binder, webRequest);
|
||||||
return binder;
|
return binder;
|
||||||
}
|
}
|
||||||
|
|
@ -609,7 +614,7 @@ public class HandlerMethodInvoker {
|
||||||
(isSessionAttr || isBindingCandidate(attrValue))) {
|
(isSessionAttr || isBindingCandidate(attrValue))) {
|
||||||
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName;
|
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName;
|
||||||
if (mavModel != null && !model.containsKey(bindingResultKey)) {
|
if (mavModel != null && !model.containsKey(bindingResultKey)) {
|
||||||
WebDataBinder binder = createBinder(webRequest, attrValue, attrName);
|
WebRequestDataBinder binder = new WebRequestDataBinder(attrValue, attrName);
|
||||||
initBinder(handler, attrName, binder, webRequest);
|
initBinder(handler, attrName, binder, webRequest);
|
||||||
mavModel.put(bindingResultKey, binder.getBindingResult());
|
mavModel.put(bindingResultKey, binder.getBindingResult());
|
||||||
}
|
}
|
||||||
|
|
@ -653,15 +658,15 @@ public class HandlerMethodInvoker {
|
||||||
throw new IllegalStateException(message);
|
throw new IllegalStateException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) throws Exception {
|
protected void doBind(WebRequestDataBinder binder, NativeWebRequest webRequest, boolean validate, boolean failOnErrors)
|
||||||
return new WebRequestDataBinder(target, objectName);
|
throws Exception {
|
||||||
}
|
|
||||||
|
|
||||||
protected void doBind(NativeWebRequest webRequest, WebDataBinder binder, boolean failOnErrors) throws Exception {
|
binder.bind(webRequest);
|
||||||
WebRequestDataBinder requestBinder = (WebRequestDataBinder) binder;
|
if (validate) {
|
||||||
requestBinder.bind(webRequest);
|
binder.validate();
|
||||||
|
}
|
||||||
if (failOnErrors) {
|
if (failOnErrors) {
|
||||||
requestBinder.closeNoCatch();
|
binder.closeNoCatch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import org.springframework.beans.PropertyEditorRegistrar;
|
||||||
import org.springframework.ui.format.FormatterRegistry;
|
import org.springframework.ui.format.FormatterRegistry;
|
||||||
import org.springframework.validation.BindingErrorProcessor;
|
import org.springframework.validation.BindingErrorProcessor;
|
||||||
import org.springframework.validation.MessageCodesResolver;
|
import org.springframework.validation.MessageCodesResolver;
|
||||||
|
import org.springframework.validation.Validator;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.context.request.WebRequest;
|
import org.springframework.web.context.request.WebRequest;
|
||||||
|
|
||||||
|
|
@ -43,6 +44,8 @@ public class ConfigurableWebBindingInitializer implements WebBindingInitializer
|
||||||
|
|
||||||
private BindingErrorProcessor bindingErrorProcessor;
|
private BindingErrorProcessor bindingErrorProcessor;
|
||||||
|
|
||||||
|
private Validator validator;
|
||||||
|
|
||||||
private FormatterRegistry formatterRegistry;
|
private FormatterRegistry formatterRegistry;
|
||||||
|
|
||||||
private PropertyEditorRegistrar[] propertyEditorRegistrars;
|
private PropertyEditorRegistrar[] propertyEditorRegistrars;
|
||||||
|
|
@ -93,6 +96,20 @@ public class ConfigurableWebBindingInitializer implements WebBindingInitializer
|
||||||
return this.bindingErrorProcessor;
|
return this.bindingErrorProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Validator to apply after each binding step.
|
||||||
|
*/
|
||||||
|
public final void setValidator(Validator validator) {
|
||||||
|
this.validator = validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the Validator to apply after each binding step, if any.
|
||||||
|
*/
|
||||||
|
public final Validator getValidator() {
|
||||||
|
return this.validator;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify a FormatterRegistry which will apply to every DataBinder.
|
* Specify a FormatterRegistry which will apply to every DataBinder.
|
||||||
*/
|
*/
|
||||||
|
|
@ -139,6 +156,10 @@ public class ConfigurableWebBindingInitializer implements WebBindingInitializer
|
||||||
if (this.bindingErrorProcessor != null) {
|
if (this.bindingErrorProcessor != null) {
|
||||||
binder.setBindingErrorProcessor(this.bindingErrorProcessor);
|
binder.setBindingErrorProcessor(this.bindingErrorProcessor);
|
||||||
}
|
}
|
||||||
|
if (this.validator != null && binder.getTarget() != null &&
|
||||||
|
this.validator.supports(binder.getTarget().getClass())) {
|
||||||
|
binder.setValidator(this.validator);
|
||||||
|
}
|
||||||
if (this.formatterRegistry != null) {
|
if (this.formatterRegistry != null) {
|
||||||
binder.setFormatterRegistry(this.formatterRegistry);
|
binder.setFormatterRegistry(this.formatterRegistry);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue