added convention-based default valueOf formatter
This commit is contained in:
parent
a696a4fec9
commit
728a1415d9
|
|
@ -46,16 +46,14 @@ public interface FormatterRegistry {
|
||||||
* Adds a Formatter to this registry indexed by <T>.
|
* Adds a Formatter to this registry indexed by <T>.
|
||||||
* <o>Calling <code>getFormatter(<T>.class)</code> returns <code>formatter</code>.
|
* <o>Calling <code>getFormatter(<T>.class)</code> returns <code>formatter</code>.
|
||||||
* @param formatter the formatter
|
* @param formatter the formatter
|
||||||
* @param <T> the type of object the formatter formats
|
|
||||||
*/
|
*/
|
||||||
<T> void addFormatterByType(Formatter<T> formatter);
|
void addFormatterByType(Formatter<?> formatter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a Formatter to this registry indexed by the given annotation type.
|
* Adds a Formatter to this registry indexed by the given annotation type.
|
||||||
* <o>Calling <code>getFormatter(...)</code> on a field or accessor method
|
* <o>Calling <code>getFormatter(...)</code> on a field or accessor method
|
||||||
* with the given annotation returns <code>formatter</code>.
|
* with the given annotation returns <code>formatter</code>.
|
||||||
* @param formatter the formatter
|
* @param formatter the formatter
|
||||||
* @param <T> the type of object the formatter formats
|
|
||||||
*/
|
*/
|
||||||
void addFormatterByAnnotation(Class<? extends Annotation> annotationType, Formatter<?> formatter);
|
void addFormatterByAnnotation(Class<? extends Annotation> annotationType, Formatter<?> formatter);
|
||||||
|
|
||||||
|
|
@ -64,16 +62,8 @@ public interface FormatterRegistry {
|
||||||
* <o>Calling <code>getFormatter(...)</code> on a field or accessor method
|
* <o>Calling <code>getFormatter(...)</code> on a field or accessor method
|
||||||
* with the given annotation returns <code>formatter</code>.
|
* 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
|
|
||||||
*/
|
*/
|
||||||
<A extends Annotation, T> void addFormatterByAnnotation(AnnotationFormatterFactory<A, T> factory);
|
void addFormatterByAnnotation(AnnotationFormatterFactory<?, ?> 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.
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@
|
||||||
package org.springframework.ui.format.support;
|
package org.springframework.ui.format.support;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
@ -38,6 +40,7 @@ import org.springframework.ui.format.Formatted;
|
||||||
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.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A generic implementation of {@link org.springframework.ui.format.FormatterRegistry}
|
* A generic implementation of {@link org.springframework.ui.format.FormatterRegistry}
|
||||||
|
|
@ -46,25 +49,28 @@ import org.springframework.util.Assert;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
|
* @see #setFormatters(Set)
|
||||||
|
* @see #setFormatterMap(Map)
|
||||||
|
* @see #setAnnotationFormatterMap(Map)
|
||||||
|
* @see #setAnnotationFormatterFactories(Set)
|
||||||
* @see #setConversionService(ConversionService)
|
* @see #setConversionService(ConversionService)
|
||||||
* @see #add(org.springframework.ui.format.Formatter)
|
* @see #addFormatterByType(Formatter)
|
||||||
* @see #add(Class, org.springframework.ui.format.Formatter)
|
* @see #addFormatterByType(Class, Formatter)
|
||||||
* @see #add(org.springframework.ui.format.AnnotationFormatterFactory)
|
* @see #addFormatterByAnnotation(Class, Formatter)
|
||||||
|
* @see #addFormatterByAnnotation(AnnotationFormatterFactory)
|
||||||
*/
|
*/
|
||||||
public class GenericFormatterRegistry implements FormatterRegistry, ApplicationContextAware, Cloneable {
|
public class GenericFormatterRegistry implements FormatterRegistry, ApplicationContextAware, Cloneable {
|
||||||
|
|
||||||
private final Map<Class, Formatter> typeFormatters = new ConcurrentHashMap<Class, Formatter>();
|
private final Map<Class, FormatterHolder> typeFormatters = new ConcurrentHashMap<Class, FormatterHolder>();
|
||||||
|
|
||||||
private final Map<Class, AnnotationFormatterFactory> annotationFormatters =
|
private final Map<Class, AnnotationFormatterFactoryHolder> annotationFormatters = new ConcurrentHashMap<Class, AnnotationFormatterFactoryHolder>();
|
||||||
new ConcurrentHashMap<Class, AnnotationFormatterFactory>();
|
|
||||||
|
|
||||||
private ConversionService conversionService = new DefaultConversionService();
|
private ConversionService conversionService;
|
||||||
|
|
||||||
private ApplicationContext applicationContext;
|
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 #addFormatterByType(Formatter)}.
|
* JavaBean-friendly alternative to calling {@link #addFormatterByType(Formatter)}.
|
||||||
|
|
@ -130,15 +136,14 @@ public class GenericFormatterRegistry implements FormatterRegistry, ApplicationC
|
||||||
* Take the context's default ConversionService if none specified locally.
|
* Take the context's default ConversionService if none specified locally.
|
||||||
*/
|
*/
|
||||||
public void setApplicationContext(ApplicationContext context) {
|
public void setApplicationContext(ApplicationContext context) {
|
||||||
if (this.conversionService == null &&
|
if (this.conversionService == null
|
||||||
context.containsBean(ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME)) {
|
&& context.containsBean(ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME)) {
|
||||||
this.conversionService = context.getBean(
|
this.conversionService = context.getBean(ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME,
|
||||||
ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME, ConversionService.class);
|
ConversionService.class);
|
||||||
}
|
}
|
||||||
this.applicationContext = context;
|
this.applicationContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// cloning support
|
// cloning support
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -175,98 +180,120 @@ public class GenericFormatterRegistry implements FormatterRegistry, ApplicationC
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// implementing FormatterRegistry
|
// implementing FormatterRegistry
|
||||||
|
|
||||||
public void addFormatterByType(Class<?> type, Formatter<?> formatter) {
|
public void addFormatterByType(Class<?> type, Formatter<?> formatter) {
|
||||||
Class<?> formattedObjectType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
|
Class<?> formattedObjectType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
|
||||||
|
if (formattedObjectType != null && !type.isAssignableFrom(formattedObjectType)) {
|
||||||
|
if (this.conversionService == null) {
|
||||||
|
throw new IllegalStateException("Unable to index Formatter " + formatter + " under type ["
|
||||||
|
+ type.getName() + "]; unable to convert from [" + formattedObjectType.getName()
|
||||||
|
+ "] parsed by Formatter because this.conversionService is null");
|
||||||
|
}
|
||||||
if (!this.conversionService.canConvert(formattedObjectType, type)) {
|
if (!this.conversionService.canConvert(formattedObjectType, type)) {
|
||||||
throw new IllegalArgumentException("Unable to register Formatter " + formatter + " for type [" +
|
throw new IllegalArgumentException("Unable to index Formatter " + formatter + " under type ["
|
||||||
type.getName() + "]; not able to convert from [" + formattedObjectType.getName() + "] to parse");
|
+ 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 [" +
|
throw new IllegalArgumentException("Unable to index Formatter " + formatter + " under type ["
|
||||||
type.getName() + "]; not able to convert to [" + formattedObjectType.getName() + "] to format");
|
+ type.getName() + "]; not able to convert to [" + formattedObjectType.getName()
|
||||||
|
+ "] to format");
|
||||||
}
|
}
|
||||||
this.typeFormatters.put(type, formatter);
|
}
|
||||||
|
this.typeFormatters.put(type, new FormatterHolder(formattedObjectType, formatter));
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> void addFormatterByType(Formatter<T> formatter) {
|
public void addFormatterByType(Formatter<?> formatter) {
|
||||||
Class<?> formattedObjectType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
|
Class<?> formattedObjectType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
|
||||||
this.typeFormatters.put(formattedObjectType, formatter);
|
if (formattedObjectType == null) {
|
||||||
|
throw new IllegalArgumentException("Unable to register Formatter " + formatter
|
||||||
|
+ "; cannot determine parameterized object type <T>");
|
||||||
|
}
|
||||||
|
this.typeFormatters.put(formattedObjectType, new FormatterHolder(formattedObjectType, formatter));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addFormatterByAnnotation(Class<? extends Annotation> annotationType, Formatter<?> formatter) {
|
public void addFormatterByAnnotation(Class<? extends Annotation> annotationType, Formatter<?> formatter) {
|
||||||
this.annotationFormatters.put(annotationType, new SimpleAnnotationFormatterFactory(formatter));
|
Class<?> formattedObjectType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
|
||||||
|
SimpleAnnotationFormatterFactory factory = new SimpleAnnotationFormatterFactory(formatter);
|
||||||
|
this.annotationFormatters.put(annotationType,
|
||||||
|
new AnnotationFormatterFactoryHolder(formattedObjectType, factory));
|
||||||
}
|
}
|
||||||
|
|
||||||
public <A extends Annotation, T> void addFormatterByAnnotation(AnnotationFormatterFactory<A, T> factory) {
|
public void addFormatterByAnnotation(AnnotationFormatterFactory<?, ?> factory) {
|
||||||
Class[] typeArgs = GenericTypeResolver.resolveTypeArguments(factory.getClass(), AnnotationFormatterFactory.class);
|
Class[] typeArgs = GenericTypeResolver.resolveTypeArguments(factory.getClass(),
|
||||||
if (typeArgs == null) {
|
AnnotationFormatterFactory.class);
|
||||||
|
if (typeArgs == null || typeArgs.length != 2) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Unable to extract Annotation type A argument from AnnotationFormatterFactory [" +
|
"Unable to extract parameterized type arguments from AnnotationFormatterFactory ["
|
||||||
factory.getClass().getName() + "]; does the factory parameterize the <A> generic type?");
|
+ factory.getClass().getName()
|
||||||
|
+ "]; does the factory parameterize the <A> and <T> generic types?");
|
||||||
}
|
}
|
||||||
this.annotationFormatters.put(typeArgs[0], factory);
|
this.annotationFormatters.put(typeArgs[0], new AnnotationFormatterFactoryHolder(typeArgs[1], factory));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T> Formatter<T> getFormatter(Class<T> targetType) {
|
|
||||||
return (Formatter<T>) getFormatter(TypeDescriptor.valueOf(targetType));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public Formatter<Object> getFormatter(TypeDescriptor type) {
|
public Formatter<Object> getFormatter(TypeDescriptor type) {
|
||||||
Assert.notNull(type, "TypeDescriptor is required");
|
Assert.notNull(type, "TypeDescriptor is required");
|
||||||
Formatter<Object> formatter = getAnnotationFormatter(type);
|
FormatterHolder holder = findFormatterHolderForAnnotatedProperty(type.getAnnotations());
|
||||||
if (formatter == null) {
|
if (holder == null) {
|
||||||
formatter = getTypeFormatter(type.getType());
|
holder = findFormatterHolderForType(type.getType());
|
||||||
}
|
}
|
||||||
if (formatter != null) {
|
if (holder == null) {
|
||||||
Class<?> formattedObjectType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
|
holder = getDefaultFormatterHolder(type);
|
||||||
if (!type.getType().isAssignableFrom(formattedObjectType)) {
|
}
|
||||||
return new ConvertingFormatter(type.getType(), formattedObjectType, formatter);
|
if (holder == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Class formattedObjectType = holder.getFormattedObjectType();
|
||||||
|
if (formattedObjectType != null && !type.getType().isAssignableFrom(formattedObjectType)) {
|
||||||
|
if (this.conversionService != null) {
|
||||||
|
return new ConvertingFormatter(type, holder);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return holder.getFormatter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return formatter;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// internal helpers
|
// internal helpers
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
private FormatterHolder findFormatterHolderForAnnotatedProperty(Annotation[] annotations) {
|
||||||
private Formatter getAnnotationFormatter(TypeDescriptor type) {
|
for (Annotation annotation : annotations) {
|
||||||
Annotation[] annotations = type.getAnnotations();
|
FormatterHolder holder = findFormatterHolderForAnnotation(annotation);
|
||||||
for (Annotation ann : annotations) {
|
if (holder != null) {
|
||||||
AnnotationFormatterFactory factory = this.annotationFormatters.get(ann.annotationType());
|
return holder;
|
||||||
if (factory != null) {
|
|
||||||
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 FormatterHolder findFormatterHolderForAnnotation(Annotation annotation) {
|
||||||
Formatter formatter = findFormatter(type);
|
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||||
return (formatter != null ? formatter : getDefaultFormatter(type));
|
AnnotationFormatterFactoryHolder factory = this.annotationFormatters.get(annotationType);
|
||||||
|
if (factory != null) {
|
||||||
|
return factory.getFormatterHolder(annotation);
|
||||||
|
} else {
|
||||||
|
Formatted formattedAnnotation = annotationType.getAnnotation(Formatted.class);
|
||||||
|
if (formattedAnnotation != null) {
|
||||||
|
// annotation has @Formatted meta-annotation
|
||||||
|
Formatter formatter = createFormatter(formattedAnnotation.value());
|
||||||
|
addFormatterByAnnotation(annotationType, formatter);
|
||||||
|
return findFormatterHolderForAnnotation(annotation);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Formatter<?> findFormatter(Class<?> type) {
|
private FormatterHolder findFormatterHolderForType(Class type) {
|
||||||
LinkedList<Class> classQueue = new LinkedList<Class>();
|
LinkedList<Class> classQueue = new LinkedList<Class>();
|
||||||
classQueue.addFirst(type);
|
classQueue.addFirst(type);
|
||||||
while (!classQueue.isEmpty()) {
|
while (!classQueue.isEmpty()) {
|
||||||
Class currentClass = classQueue.removeLast();
|
Class currentClass = classQueue.removeLast();
|
||||||
Formatter<?> formatter = this.typeFormatters.get(currentClass);
|
FormatterHolder holder = this.typeFormatters.get(currentClass);
|
||||||
if (formatter != null) {
|
if (holder != null) {
|
||||||
return formatter;
|
return holder;
|
||||||
}
|
}
|
||||||
if (currentClass.getSuperclass() != null) {
|
if (currentClass.getSuperclass() != null) {
|
||||||
classQueue.addFirst(currentClass.getSuperclass());
|
classQueue.addFirst(currentClass.getSuperclass());
|
||||||
|
|
@ -279,52 +306,98 @@ public class GenericFormatterRegistry implements FormatterRegistry, ApplicationC
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Formatter<?> getDefaultFormatter(Class<?> type) {
|
private FormatterHolder getDefaultFormatterHolder(TypeDescriptor typeDescriptor) {
|
||||||
|
Class type = typeDescriptor.getType();
|
||||||
Formatted formatted = AnnotationUtils.findAnnotation(type, Formatted.class);
|
Formatted formatted = AnnotationUtils.findAnnotation(type, Formatted.class);
|
||||||
if (formatted != null) {
|
if (formatted != null) {
|
||||||
Formatter formatter = createFormatter(formatted.value());
|
Formatter formatter = createFormatter(formatted.value());
|
||||||
this.typeFormatters.put(type, formatter);
|
addFormatterByType(type, formatter);
|
||||||
return formatter;
|
return findFormatterHolderForType(type);
|
||||||
}
|
} else {
|
||||||
else {
|
Method valueOfMethod = getValueOfMethod(type);
|
||||||
|
if (valueOfMethod != null) {
|
||||||
|
Formatter formatter = createFormatter(valueOfMethod);
|
||||||
|
addFormatterByType(type, formatter);
|
||||||
|
return findFormatterHolderForType(type);
|
||||||
|
} 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 Formatter createFormatter(Class<? extends Formatter> formatterClass) {
|
||||||
|
return (this.applicationContext != null ? this.applicationContext.getAutowireCapableBeanFactory().createBean(
|
||||||
|
formatterClass) : BeanUtils.instantiate(formatterClass));
|
||||||
|
}
|
||||||
|
|
||||||
private class ConvertingFormatter implements Formatter {
|
private Method getValueOfMethod(Class type) {
|
||||||
|
Method[] methods = type.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
Method method = methods[i];
|
||||||
|
if ("valueOf".equals(method.getName()) && acceptsSingleStringParameterType(method)
|
||||||
|
&& Modifier.isPublic(method.getModifiers()) && Modifier.isStatic(method.getModifiers())) {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private final Class<?> type;
|
private boolean acceptsSingleStringParameterType(Method method) {
|
||||||
|
Class[] paramTypes = method.getParameterTypes();
|
||||||
|
if (paramTypes == null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return paramTypes.length == 1 && paramTypes[0] == String.class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final Class<?> formattedObjectType;
|
private Formatter createFormatter(Method valueOfMethod) {
|
||||||
|
return new ValueOfMethodFormatter(valueOfMethod);
|
||||||
|
}
|
||||||
|
|
||||||
private final Formatter targetFormatter;
|
private abstract static class AbstractFormatterHolder {
|
||||||
|
|
||||||
public ConvertingFormatter(Class<?> type, Class<?> formattedObjectType, Formatter targetFormatter) {
|
private Class formattedObjectType;
|
||||||
this.type = type;
|
|
||||||
|
public AbstractFormatterHolder(Class formattedObjectType) {
|
||||||
this.formattedObjectType = formattedObjectType;
|
this.formattedObjectType = formattedObjectType;
|
||||||
this.targetFormatter = targetFormatter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
public Class<?> getFormattedObjectType() {
|
||||||
public String format(Object object, Locale locale) {
|
return formattedObjectType;
|
||||||
object = conversionService.convert(object, this.formattedObjectType);
|
|
||||||
return this.targetFormatter.format(object, locale);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object parse(String formatted, Locale locale) throws ParseException {
|
|
||||||
Object parsed = this.targetFormatter.parse(formatted, locale);
|
|
||||||
parsed = conversionService.convert(parsed, this.type);
|
|
||||||
return parsed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class FormatterHolder extends AbstractFormatterHolder {
|
||||||
|
|
||||||
|
private Formatter formatter;
|
||||||
|
|
||||||
|
public FormatterHolder(Class formattedObjectType, Formatter formatter) {
|
||||||
|
super(formattedObjectType);
|
||||||
|
this.formatter = formatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Formatter getFormatter() {
|
||||||
|
return this.formatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AnnotationFormatterFactoryHolder extends AbstractFormatterHolder {
|
||||||
|
|
||||||
|
private AnnotationFormatterFactory factory;
|
||||||
|
|
||||||
|
public AnnotationFormatterFactoryHolder(Class formattedObjectType, AnnotationFormatterFactory factory) {
|
||||||
|
super(formattedObjectType);
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormatterHolder getFormatterHolder(Annotation annotation) {
|
||||||
|
return new FormatterHolder(getFormattedObjectType(), this.factory.getFormatter(annotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private static class SimpleAnnotationFormatterFactory implements AnnotationFormatterFactory {
|
private static class SimpleAnnotationFormatterFactory implements AnnotationFormatterFactory {
|
||||||
|
|
||||||
|
|
@ -339,4 +412,51 @@ public class GenericFormatterRegistry implements FormatterRegistry, ApplicationC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ValueOfMethodFormatter implements Formatter {
|
||||||
|
|
||||||
|
private Method valueOfMethod;
|
||||||
|
|
||||||
|
public ValueOfMethodFormatter(Method valueOfMethod) {
|
||||||
|
this.valueOfMethod = valueOfMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String format(Object object, Locale locale) {
|
||||||
|
if (object == null) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return object.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object parse(String formatted, Locale locale) throws ParseException {
|
||||||
|
return ReflectionUtils.invokeMethod(valueOfMethod, null, formatted);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ConvertingFormatter implements Formatter {
|
||||||
|
|
||||||
|
private final TypeDescriptor type;
|
||||||
|
|
||||||
|
private final FormatterHolder formatterHolder;
|
||||||
|
|
||||||
|
public ConvertingFormatter(TypeDescriptor type, FormatterHolder formatterHolder) {
|
||||||
|
this.type = type;
|
||||||
|
this.formatterHolder = formatterHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String format(Object object, Locale locale) {
|
||||||
|
object = GenericFormatterRegistry.this.conversionService.convert(object, this.formatterHolder
|
||||||
|
.getFormattedObjectType());
|
||||||
|
return this.formatterHolder.getFormatter().format(object, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object parse(String formatted, Locale locale) throws ParseException {
|
||||||
|
Object parsed = this.formatterHolder.getFormatter().parse(formatted, locale);
|
||||||
|
parsed = GenericFormatterRegistry.this.conversionService.convert(parsed, TypeDescriptor
|
||||||
|
.valueOf(this.formatterHolder.getFormattedObjectType()), this.type);
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package org.springframework.ui.format;
|
package org.springframework.ui.format;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
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;
|
||||||
|
|
@ -25,11 +27,10 @@ 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.convert.support.DefaultConversionService;
|
||||||
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;
|
||||||
|
|
@ -46,12 +47,13 @@ public class GenericFormatterRegistryTests {
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
registry = new GenericFormatterRegistry();
|
registry = new GenericFormatterRegistry();
|
||||||
|
registry.setConversionService(new DefaultConversionService());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAdd() throws ParseException {
|
public void testAdd() throws ParseException {
|
||||||
registry.addFormatterByType(new IntegerFormatter());
|
registry.addFormatterByType(new IntegerFormatter());
|
||||||
Formatter formatter = registry.getFormatter(Integer.class);
|
Formatter formatter = registry.getFormatter(TypeDescriptor.valueOf(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);
|
||||||
|
|
@ -61,7 +63,7 @@ public class GenericFormatterRegistryTests {
|
||||||
@Test
|
@Test
|
||||||
public void testAddByObjectType() {
|
public void testAddByObjectType() {
|
||||||
registry.addFormatterByType(BigInteger.class, new IntegerFormatter());
|
registry.addFormatterByType(BigInteger.class, new IntegerFormatter());
|
||||||
Formatter formatter = registry.getFormatter(BigInteger.class);
|
Formatter formatter = registry.getFormatter(TypeDescriptor.valueOf(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);
|
||||||
}
|
}
|
||||||
|
|
@ -91,7 +93,7 @@ public class GenericFormatterRegistryTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetDefaultFormatterForType() {
|
public void testGetDefaultFormatterForType() {
|
||||||
Formatter formatter = registry.getFormatter(Address.class);
|
Formatter formatter = registry.getFormatter(TypeDescriptor.valueOf(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";
|
||||||
|
|
@ -102,28 +104,29 @@ public class GenericFormatterRegistryTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetNoFormatterForType() {
|
public void testGetDefaultFormatterForTypeValueOfMethod() throws ParseException {
|
||||||
assertNull(registry.getFormatter(Integer.class));
|
Formatter formatter = registry.getFormatter(TypeDescriptor.valueOf(Integer.class));
|
||||||
|
assertEquals("3", formatter.format(new Integer(3), null));
|
||||||
|
assertEquals(new Integer(3), formatter.parse("3", null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected=IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testGetFormatterCannotConvert() {
|
public void testGetFormatterCannotConvert() {
|
||||||
registry.addFormatterByType(Integer.class, new AddressFormatter());
|
registry.addFormatterByType(Integer.class, new AddressFormatter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Currency
|
@Currency
|
||||||
public BigDecimal currencyField;
|
public BigDecimal currencyField;
|
||||||
|
|
||||||
@SmartCurrency
|
@SmartCurrency
|
||||||
public BigDecimal smartCurrencyField;
|
public BigDecimal smartCurrencyField;
|
||||||
|
|
||||||
@Target({ElementType.METHOD, ElementType.FIELD})
|
@Target( { ElementType.METHOD, ElementType.FIELD })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface Currency {
|
public @interface Currency {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Target({ElementType.METHOD, ElementType.FIELD})
|
@Target( { ElementType.METHOD, ElementType.FIELD })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Formatted(CurrencyFormatter.class)
|
@Formatted(CurrencyFormatter.class)
|
||||||
public @interface SmartCurrency {
|
public @interface SmartCurrency {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue