DataBinder allows for adding custom Formatters as alternative to PropertyEditors (including per-field formatters)
Includes a generic FormatterPropertyEditorAdapter plus Number conversion support in TypeConverterDelegate. Issue: SPR-7773 Issue: SPR-6069
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2002-2015 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.format.support;
|
||||
|
||||
import java.beans.PropertyEditor;
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.text.ParseException;
|
||||
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.format.Formatter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Adapter that bridges between {@link Formatter} and {@link PropertyEditor}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.2
|
||||
*/
|
||||
public class FormatterPropertyEditorAdapter extends PropertyEditorSupport {
|
||||
|
||||
private final Formatter<Object> formatter;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code FormatterPropertyEditorAdapter} for the given {@link Formatter}.
|
||||
* @param formatter the {@link Formatter} to wrap
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public FormatterPropertyEditorAdapter(Formatter<?> formatter) {
|
||||
Assert.notNull(formatter, "Formatter must not be null");
|
||||
this.formatter = (Formatter<Object>) formatter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine the {@link Formatter}-declared field type.
|
||||
* @return the field type declared in the wrapped {@link Formatter} implementation
|
||||
* (never {@code null})
|
||||
* @throws IllegalArgumentException if the {@link Formatter}-declared field type
|
||||
* cannot be inferred
|
||||
*/
|
||||
public Class<?> getFieldType() {
|
||||
return FormattingConversionService.getFieldType(this.formatter);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setAsText(String text) throws IllegalArgumentException {
|
||||
try {
|
||||
setValue(this.formatter.parse(text, LocaleContextHolder.getLocale()));
|
||||
}
|
||||
catch (ParseException ex) {
|
||||
throw new IllegalArgumentException("Parse attempt failed for value [" + text + "]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsText() {
|
||||
return this.formatter.print(getValue(), LocaleContextHolder.getLocale());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
@@ -67,12 +67,7 @@ public class FormattingConversionService extends GenericConversionService
|
||||
|
||||
@Override
|
||||
public void addFormatter(Formatter<?> formatter) {
|
||||
Class<?> fieldType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
|
||||
if (fieldType == null) {
|
||||
throw new IllegalArgumentException("Unable to extract parameterized field type argument from Formatter [" +
|
||||
formatter.getClass().getName() + "]; does the formatter parameterize the <T> generic type?");
|
||||
}
|
||||
addFormatterForFieldType(fieldType, formatter);
|
||||
addFormatterForFieldType(getFieldType(formatter), formatter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -88,14 +83,8 @@ public class FormattingConversionService extends GenericConversionService
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void addFormatterForFieldAnnotation(AnnotationFormatterFactory annotationFormatterFactory) {
|
||||
Class<? extends Annotation> annotationType = (Class<? extends Annotation>)
|
||||
GenericTypeResolver.resolveTypeArgument(annotationFormatterFactory.getClass(), AnnotationFormatterFactory.class);
|
||||
if (annotationType == null) {
|
||||
throw new IllegalArgumentException("Unable to extract parameterized Annotation type argument from AnnotationFormatterFactory [" +
|
||||
annotationFormatterFactory.getClass().getName() + "]; does the factory parameterize the <A extends Annotation> generic type?");
|
||||
}
|
||||
public void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation> annotationFormatterFactory) {
|
||||
Class<? extends Annotation> annotationType = getAnnotationType(annotationFormatterFactory);
|
||||
if (this.embeddedValueResolver != null && annotationFormatterFactory instanceof EmbeddedValueResolverAware) {
|
||||
((EmbeddedValueResolverAware) annotationFormatterFactory).setEmbeddedValueResolver(this.embeddedValueResolver);
|
||||
}
|
||||
@@ -107,6 +96,28 @@ public class FormattingConversionService extends GenericConversionService
|
||||
}
|
||||
|
||||
|
||||
static Class<?> getFieldType(Formatter<?> formatter) {
|
||||
Class<?> fieldType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
|
||||
if (fieldType == null) {
|
||||
throw new IllegalArgumentException("Unable to extract parameterized field type argument from Formatter [" +
|
||||
formatter.getClass().getName() + "]; does the formatter parameterize the <T> generic type?");
|
||||
}
|
||||
return fieldType;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Class<? extends Annotation> getAnnotationType(AnnotationFormatterFactory<? extends Annotation> factory) {
|
||||
Class<? extends Annotation> annotationType = (Class<? extends Annotation>)
|
||||
GenericTypeResolver.resolveTypeArgument(factory.getClass(), AnnotationFormatterFactory.class);
|
||||
if (annotationType == null) {
|
||||
throw new IllegalArgumentException("Unable to extract parameterized Annotation type argument from " +
|
||||
"AnnotationFormatterFactory [" + factory.getClass().getName() +
|
||||
"]; does the factory parameterize the <A extends Annotation> generic type?");
|
||||
}
|
||||
return annotationType;
|
||||
}
|
||||
|
||||
|
||||
private static class PrinterConverter implements GenericConverter {
|
||||
|
||||
private final Class<?> fieldType;
|
||||
@@ -148,7 +159,7 @@ public class FormattingConversionService extends GenericConversionService
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.fieldType.getName() + " -> " + String.class.getName() + " : " + this.printer;
|
||||
return (this.fieldType.getName() + " -> " + String.class.getName() + " : " + this.printer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +208,7 @@ public class FormattingConversionService extends GenericConversionService
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.class.getName() + " -> " + this.fieldType.getName() + ": " + this.parser;
|
||||
return (String.class.getName() + " -> " + this.fieldType.getName() + ": " + this.parser);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,8 +260,8 @@ public class FormattingConversionService extends GenericConversionService
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "@" + this.annotationType.getName() + " " + this.fieldType.getName() + " -> " +
|
||||
String.class.getName() + ": " + this.annotationFormatterFactory;
|
||||
return ("@" + this.annotationType.getName() + " " + this.fieldType.getName() + " -> " +
|
||||
String.class.getName() + ": " + this.annotationFormatterFactory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,8 +313,8 @@ public class FormattingConversionService extends GenericConversionService
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.class.getName() + " -> @" + this.annotationType.getName() + " " +
|
||||
this.fieldType.getName() + ": " + this.annotationFormatterFactory;
|
||||
return (String.class.getName() + " -> @" + this.annotationType.getName() + " " +
|
||||
this.fieldType.getName() + ": " + this.annotationFormatterFactory);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
@@ -42,6 +42,8 @@ import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.format.Formatter;
|
||||
import org.springframework.format.support.FormatterPropertyEditorAdapter;
|
||||
import org.springframework.lang.UsesJava8;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -553,6 +555,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
return Collections.unmodifiableList(this.validators);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of PropertyEditorRegistry/TypeConverter interface
|
||||
//---------------------------------------------------------------------
|
||||
@@ -576,6 +579,64 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
return this.conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom formatter, applying it to all fields matching the
|
||||
* {@link Formatter}-declared type.
|
||||
* <p>Registers a corresponding {@link PropertyEditor} adapter underneath the covers.
|
||||
* @param formatter the formatter to add, generically declared for a specific type
|
||||
* @since 4.2
|
||||
* @see #registerCustomEditor(Class, PropertyEditor)
|
||||
*/
|
||||
public void addCustomFormatter(Formatter<?> formatter) {
|
||||
FormatterPropertyEditorAdapter adapter = new FormatterPropertyEditorAdapter(formatter);
|
||||
getPropertyEditorRegistry().registerCustomEditor(adapter.getFieldType(), adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom formatter for the field type specified in {@link Formatter} class,
|
||||
* applying it to the specified fields only, if any, or otherwise to all fields.
|
||||
* <p>Registers a corresponding {@link PropertyEditor} adapter underneath the covers.
|
||||
* @param formatter the formatter to add, generically declared for a specific type
|
||||
* @param fields the fields to apply the formatter to, or none if to be applied to all
|
||||
* @since 4.2
|
||||
* @see #registerCustomEditor(Class, String, PropertyEditor)
|
||||
*/
|
||||
public void addCustomFormatter(Formatter<?> formatter, String... fields) {
|
||||
FormatterPropertyEditorAdapter adapter = new FormatterPropertyEditorAdapter(formatter);
|
||||
Class<?> fieldType = adapter.getFieldType();
|
||||
if (ObjectUtils.isEmpty(fields)) {
|
||||
getPropertyEditorRegistry().registerCustomEditor(fieldType, adapter);
|
||||
}
|
||||
else {
|
||||
for (String field : fields) {
|
||||
getPropertyEditorRegistry().registerCustomEditor(fieldType, field, adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom formatter, applying it to the specified field types only, if any,
|
||||
* or otherwise to all fields matching the {@link Formatter}-declared type.
|
||||
* <p>Registers a corresponding {@link PropertyEditor} adapter underneath the covers.
|
||||
* @param formatter the formatter to add (does not need to generically declare a
|
||||
* field type if field types are explicitly specified as parameters)
|
||||
* @param fieldTypes the field types to apply the formatter to, or none if to be
|
||||
* derived from the given {@link Formatter} implementation class
|
||||
* @since 4.2
|
||||
* @see #registerCustomEditor(Class, PropertyEditor)
|
||||
*/
|
||||
public void addCustomFormatter(Formatter<?> formatter, Class<?>... fieldTypes) {
|
||||
FormatterPropertyEditorAdapter adapter = new FormatterPropertyEditorAdapter(formatter);
|
||||
if (ObjectUtils.isEmpty(fieldTypes)) {
|
||||
getPropertyEditorRegistry().registerCustomEditor(adapter.getFieldType(), adapter);
|
||||
}
|
||||
else {
|
||||
for (Class<?> fieldType : fieldTypes) {
|
||||
getPropertyEditorRegistry().registerCustomEditor(fieldType, adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
|
||||
getPropertyEditorRegistry().registerCustomEditor(requiredType, propertyEditor);
|
||||
|
||||
Reference in New Issue
Block a user