|
|
|
@ -19,19 +19,24 @@ import java.lang.annotation.Annotation; |
|
|
|
import java.lang.reflect.ParameterizedType; |
|
|
|
import java.lang.reflect.ParameterizedType; |
|
|
|
import java.lang.reflect.Type; |
|
|
|
import java.lang.reflect.Type; |
|
|
|
import java.lang.reflect.TypeVariable; |
|
|
|
import java.lang.reflect.TypeVariable; |
|
|
|
|
|
|
|
import java.text.ParseException; |
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.HashMap; |
|
|
|
|
|
|
|
import java.util.Locale; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
|
|
|
|
|
|
|
|
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.TypeDescriptor; |
|
|
|
import org.springframework.core.convert.TypeDescriptor; |
|
|
|
|
|
|
|
import org.springframework.core.convert.support.DefaultConversionService; |
|
|
|
import org.springframework.util.Assert; |
|
|
|
import org.springframework.util.Assert; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* A generic implementation of {@link FormatterRegistry} suitable for use in most binding environments. |
|
|
|
* A generic implementation of {@link FormatterRegistry} suitable for use in most binding environments. |
|
|
|
* @author Keith Donald |
|
|
|
* @author Keith Donald |
|
|
|
* @since 3.0 |
|
|
|
* @since 3.0 |
|
|
|
|
|
|
|
* @see #add(Formatter) |
|
|
|
* @see #add(Class, Formatter) |
|
|
|
* @see #add(Class, Formatter) |
|
|
|
* @see #add(AnnotationFormatterFactory) |
|
|
|
* @see #add(AnnotationFormatterFactory) |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@ -42,10 +47,21 @@ public class GenericFormatterRegistry implements FormatterRegistry { |
|
|
|
|
|
|
|
|
|
|
|
private Map<Class, AnnotationFormatterFactory> annotationFormatters = new HashMap<Class, AnnotationFormatterFactory>(); |
|
|
|
private Map<Class, AnnotationFormatterFactory> annotationFormatters = new HashMap<Class, AnnotationFormatterFactory>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ConversionService conversionService = new DefaultConversionService(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Sets the type conversion service used to coerse objects to the types required for Formatting purposes. |
|
|
|
|
|
|
|
* @param conversionService the conversion service |
|
|
|
|
|
|
|
* @see #add(Class, Formatter) |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public void setConversionService(ConversionService conversionService) { |
|
|
|
|
|
|
|
this.conversionService = conversionService; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// implementing FormatterRegistry
|
|
|
|
// implementing FormatterRegistry
|
|
|
|
|
|
|
|
|
|
|
|
public <T> void add(Formatter<T> formatter) { |
|
|
|
public <T> void add(Formatter<T> formatter) { |
|
|
|
// TODO
|
|
|
|
typeFormatters.put(getFormattedObjectType(formatter.getClass()), formatter); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public <T> void add(Class<?> objectType, Formatter<T> formatter) { |
|
|
|
public <T> void add(Class<?> objectType, Formatter<T> formatter) { |
|
|
|
@ -57,77 +73,122 @@ public class GenericFormatterRegistry implements FormatterRegistry { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public <A extends Annotation, T> void add(AnnotationFormatterFactory<A, T> factory) { |
|
|
|
public <A extends Annotation, T> void add(AnnotationFormatterFactory<A, T> factory) { |
|
|
|
annotationFormatters.put(getAnnotationType(factory), factory); |
|
|
|
annotationFormatters.put(getAnnotationType(factory.getClass()), factory); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public Formatter<?> getFormatter(TypeDescriptor type) { |
|
|
|
public Formatter<?> getFormatter(TypeDescriptor type) { |
|
|
|
Assert.notNull(type, "The TypeDescriptor is required"); |
|
|
|
Assert.notNull(type, "The TypeDescriptor is required"); |
|
|
|
Annotation[] annotations = type.getAnnotations(); |
|
|
|
Formatter formatter = getAnnotationFormatter(type); |
|
|
|
for (Annotation a : annotations) { |
|
|
|
if (formatter == null) { |
|
|
|
AnnotationFormatterFactory factory = annotationFormatters.get(a.annotationType()); |
|
|
|
formatter = getTypeFormatter(type.getType()); |
|
|
|
if (factory != null) { |
|
|
|
|
|
|
|
return factory.getFormatter(a); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
return getFormatter(type.getType()); |
|
|
|
return formatter; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// internal helpers
|
|
|
|
// internal helpers
|
|
|
|
|
|
|
|
|
|
|
|
private Formatter<?> getFormatter(Class<?> type) { |
|
|
|
private Class getFormattedObjectType(Class formatterClass) { |
|
|
|
Assert.notNull(type, "The Class of the object to format is required"); |
|
|
|
Class classToIntrospect = formatterClass; |
|
|
|
Formatter formatter = typeFormatters.get(type); |
|
|
|
while (classToIntrospect != null) { |
|
|
|
if (formatter != null) { |
|
|
|
Type[] ifcs = classToIntrospect.getGenericInterfaces(); |
|
|
|
return formatter; |
|
|
|
for (Type ifc : ifcs) { |
|
|
|
} else { |
|
|
|
if (ifc instanceof ParameterizedType) { |
|
|
|
Formatted formatted = AnnotationUtils.findAnnotation(type, Formatted.class); |
|
|
|
ParameterizedType paramIfc = (ParameterizedType) ifc; |
|
|
|
if (formatted != null) { |
|
|
|
Type rawType = paramIfc.getRawType(); |
|
|
|
Class formatterClass = formatted.value(); |
|
|
|
if (Formatter.class.equals(rawType)) { |
|
|
|
try { |
|
|
|
Type arg = paramIfc.getActualTypeArguments()[0]; |
|
|
|
formatter = (Formatter) formatterClass.newInstance(); |
|
|
|
if (arg instanceof TypeVariable) { |
|
|
|
} catch (InstantiationException e) { |
|
|
|
arg = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg, formatterClass); |
|
|
|
throw new IllegalStateException( |
|
|
|
} |
|
|
|
"Formatter referenced by @Formatted annotation does not have default constructor", e); |
|
|
|
if (arg instanceof Class) { |
|
|
|
} catch (IllegalAccessException e) { |
|
|
|
return (Class) arg; |
|
|
|
throw new IllegalStateException( |
|
|
|
} |
|
|
|
"Formatter referenced by @Formatted annotation does not have public constructor", e); |
|
|
|
} else if (Formatter.class.isAssignableFrom((Class) rawType)) { |
|
|
|
|
|
|
|
return getFormattedObjectType((Class) rawType); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else if (Formatter.class.isAssignableFrom((Class) ifc)) { |
|
|
|
|
|
|
|
return getFormattedObjectType((Class) ifc); |
|
|
|
} |
|
|
|
} |
|
|
|
typeFormatters.put(type, formatter); |
|
|
|
|
|
|
|
return formatter; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
classToIntrospect = classToIntrospect.getSuperclass(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Class getAnnotationType(AnnotationFormatterFactory factory) { |
|
|
|
private Class getAnnotationType(Class factoryClass) { |
|
|
|
Class classToIntrospect = factory.getClass(); |
|
|
|
Class classToIntrospect = factoryClass; |
|
|
|
while (classToIntrospect != null) { |
|
|
|
while (classToIntrospect != null) { |
|
|
|
Type[] genericInterfaces = classToIntrospect.getGenericInterfaces(); |
|
|
|
Type[] ifcs = classToIntrospect.getGenericInterfaces(); |
|
|
|
for (Type genericInterface : genericInterfaces) { |
|
|
|
for (Type ifc : ifcs) { |
|
|
|
if (genericInterface instanceof ParameterizedType) { |
|
|
|
if (ifc instanceof ParameterizedType) { |
|
|
|
ParameterizedType pInterface = (ParameterizedType) genericInterface; |
|
|
|
ParameterizedType paramIfc = (ParameterizedType) ifc; |
|
|
|
if (AnnotationFormatterFactory.class.isAssignableFrom((Class) pInterface.getRawType())) { |
|
|
|
Type rawType = paramIfc.getRawType(); |
|
|
|
return getParameterClass(pInterface.getActualTypeArguments()[0], factory.getClass()); |
|
|
|
if (AnnotationFormatterFactory.class.equals(rawType)) { |
|
|
|
|
|
|
|
Type arg = paramIfc.getActualTypeArguments()[0]; |
|
|
|
|
|
|
|
if (arg instanceof TypeVariable) { |
|
|
|
|
|
|
|
arg = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg, factoryClass); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (arg instanceof Class) { |
|
|
|
|
|
|
|
return (Class) arg; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else if (AnnotationFormatterFactory.class.isAssignableFrom((Class) rawType)) { |
|
|
|
|
|
|
|
return getAnnotationType((Class) rawType); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} else if (AnnotationFormatterFactory.class.isAssignableFrom((Class) ifc)) { |
|
|
|
|
|
|
|
return getAnnotationType((Class) ifc); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
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 [" |
|
|
|
+ factory.getClass().getName() + "]; does the factory parameterize the <A> generic type?"); |
|
|
|
+ factoryClass.getName() + "]; does the factory parameterize the <A> generic type?"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Class getParameterClass(Type parameterType, Class converterClass) { |
|
|
|
private Formatter<?> getAnnotationFormatter(TypeDescriptor type) { |
|
|
|
if (parameterType instanceof TypeVariable) { |
|
|
|
Annotation[] annotations = type.getAnnotations(); |
|
|
|
parameterType = GenericTypeResolver.resolveTypeVariable((TypeVariable) parameterType, converterClass); |
|
|
|
for (Annotation a : annotations) { |
|
|
|
|
|
|
|
AnnotationFormatterFactory factory = annotationFormatters.get(a.annotationType()); |
|
|
|
|
|
|
|
if (factory != null) { |
|
|
|
|
|
|
|
return factory.getFormatter(a); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (parameterType instanceof Class) { |
|
|
|
return null; |
|
|
|
return (Class) parameterType; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Formatter<?> getTypeFormatter(Class<?> type) { |
|
|
|
|
|
|
|
Assert.notNull(type, "The Class of the object to format is required"); |
|
|
|
|
|
|
|
Formatter formatter = typeFormatters.get(type); |
|
|
|
|
|
|
|
if (formatter != null) { |
|
|
|
|
|
|
|
Class<?> formattedObjectType = getFormattedObjectType(formatter.getClass()); |
|
|
|
|
|
|
|
if (type.isAssignableFrom(formattedObjectType)) { |
|
|
|
|
|
|
|
return formatter; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
return new ConvertingFormatter(type, formattedObjectType, formatter); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
return getDefaultFormatter(type); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Formatter<?> getDefaultFormatter(Class<?> type) { |
|
|
|
|
|
|
|
Formatted formatted = AnnotationUtils.findAnnotation(type, Formatted.class); |
|
|
|
|
|
|
|
if (formatted != null) { |
|
|
|
|
|
|
|
Class formatterClass = formatted.value(); |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
Formatter formatter = (Formatter) formatterClass.newInstance(); |
|
|
|
|
|
|
|
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 { |
|
|
|
|
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
throw new IllegalArgumentException("Unable to obtain the java.lang.Class for parameterType [" + parameterType |
|
|
|
|
|
|
|
+ "] on Formatter [" + converterClass.getName() + "]"); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static class SimpleAnnotationFormatterFactory implements AnnotationFormatterFactory { |
|
|
|
private static class SimpleAnnotationFormatterFactory implements AnnotationFormatterFactory { |
|
|
|
@ -144,4 +205,31 @@ public class GenericFormatterRegistry implements FormatterRegistry { |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private class ConvertingFormatter implements Formatter { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Class<?> type; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Class<?> formattedObjectType; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Formatter targetFormatter; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ConvertingFormatter(Class<?> type, Class<?> formattedObjectType, Formatter targetFormatter) { |
|
|
|
|
|
|
|
this.type = type; |
|
|
|
|
|
|
|
this.formattedObjectType = formattedObjectType; |
|
|
|
|
|
|
|
this.targetFormatter = targetFormatter; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public String format(Object object, Locale locale) { |
|
|
|
|
|
|
|
object = conversionService.convert(object, formattedObjectType); |
|
|
|
|
|
|
|
return targetFormatter.format(object, locale); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Object parse(String formatted, Locale locale) throws ParseException { |
|
|
|
|
|
|
|
Object parsed = targetFormatter.parse(formatted, locale); |
|
|
|
|
|
|
|
parsed = conversionService.convert(parsed, type); |
|
|
|
|
|
|
|
return parsed; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |