@ -2,6 +2,9 @@ package org.springframework.ui.binding;
@@ -2,6 +2,9 @@ package org.springframework.ui.binding;
import java.lang.annotation.Annotation ;
import java.lang.reflect.Array ;
import java.lang.reflect.ParameterizedType ;
import java.lang.reflect.Type ;
import java.lang.reflect.TypeVariable ;
import java.text.ParseException ;
import java.util.Collection ;
import java.util.HashMap ;
@ -10,8 +13,11 @@ import java.util.Map;
@@ -10,8 +13,11 @@ import java.util.Map;
import org.springframework.context.expression.MapAccessor ;
import org.springframework.context.i18n.LocaleContextHolder ;
import org.springframework.core.GenericTypeResolver ;
import org.springframework.core.convert.TypeConverter ;
import org.springframework.core.convert.TypeDescriptor ;
import org.springframework.core.convert.converter.Converter ;
import org.springframework.core.convert.converter.ConverterFactory ;
import org.springframework.core.convert.support.DefaultTypeConverter ;
import org.springframework.expression.EvaluationContext ;
import org.springframework.expression.EvaluationException ;
@ -22,6 +28,7 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -22,6 +28,7 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParserConfiguration ;
import org.springframework.expression.spel.support.StandardEvaluationContext ;
import org.springframework.expression.spel.support.StandardTypeConverter ;
import org.springframework.ui.format.AnnotationFormatterFactory ;
import org.springframework.ui.format.Formatter ;
public class Binder < T > {
@ -32,9 +39,9 @@ public class Binder<T> {
@@ -32,9 +39,9 @@ public class Binder<T> {
private Map < String , Binding > bindings ;
private Map < Class < ? > , Formatter < ? > > typeFormatters = new HashMap < Class < ? > , Formatter < ? > > ( ) ;
private Map < Class , Formatter > typeFormatters = new HashMap < Class , Formatter > ( ) ;
private Map < Class < ? > , Formatter < ? > > annotationFormatters = new HashMap < Class < ? > , Formatter < ? > > ( ) ;
private Map < Class , AnnotationFormatterFactory > annotationFormatters = new HashMap < Class , AnnotationFormatterFactory > ( ) ;
private ExpressionParser expressionParser ;
@ -43,11 +50,7 @@ public class Binder<T> {
@@ -43,11 +50,7 @@ public class Binder<T> {
private boolean strict = false ;
private static Formatter defaultFormatter = new Formatter ( ) {
public Class < ? > getFormattedObjectType ( ) {
return String . class ;
}
public String format ( Object object , Locale locale ) {
if ( object = = null ) {
return "" ;
@ -92,15 +95,17 @@ public class Binder<T> {
@@ -92,15 +95,17 @@ public class Binder<T> {
}
public void add ( Formatter < ? > formatter , Class < ? > propertyType ) {
if ( propertyType = = null ) {
propertyType = formatter . getFormattedObjectType ( ) ;
}
if ( propertyType . isAnnotation ( ) ) {
annotationFormatters . put ( propertyType , formatter ) ;
annotationFormatters . put ( propertyType , new SimpleAnnotationFormatterFactory ( formatter ) ) ;
} else {
typeFormatters . put ( propertyType , formatter ) ;
}
}
// TODO determine Annotation type from factory using reflection
public void add ( AnnotationFormatterFactory < ? , ? > factory ) {
annotationFormatters . put ( getAnnotationType ( factory ) , factory ) ;
}
public T getModel ( ) {
return model ;
@ -152,12 +157,13 @@ public class Binder<T> {
@@ -152,12 +157,13 @@ public class Binder<T> {
}
public void setValue ( String formatted ) {
setValue ( parse ( formatted ) ) ;
setValue ( parse ( formatted , getFormatter ( ) ) ) ;
}
public String format ( Object selectableValue ) {
Formatter formatter = getFormatter ( ) ;
selectableValue = typeConverter . convert ( selectableValue , formatter . getFormattedObjectType ( ) ) ;
Class < ? > formattedType = getFormattedObjectType ( formatter ) ;
selectableValue = typeConverter . convert ( selectableValue , formattedType ) ;
return formatter . format ( selectableValue , LocaleContextHolder . getLocale ( ) ) ;
}
@ -192,9 +198,14 @@ public class Binder<T> {
@@ -192,9 +198,14 @@ public class Binder<T> {
}
public void setValues ( String [ ] formattedValues ) {
Object values = Array . newInstance ( getFormatter ( ) . getFormattedObjectType ( ) , formattedValues . length ) ;
Formatter formatter = getFormatter ( ) ;
Class parsedType = getFormattedObjectType ( formatter ) ;
if ( parsedType = = null ) {
parsedType = String . class ;
}
Object values = Array . newInstance ( parsedType , formattedValues . length ) ;
for ( int i = 0 ; i < formattedValues . length ; i + + ) {
Array . set ( values , i , parse ( formattedValues [ i ] ) ) ;
Array . set ( values , i , parse ( formattedValues [ i ] , formatter ) ) ;
}
setValue ( values ) ;
}
@ -205,14 +216,15 @@ public class Binder<T> {
@@ -205,14 +216,15 @@ public class Binder<T> {
// internal helpers
private Object parse ( String formatted ) {
private Object parse ( String formatted , Formatter formatter ) {
try {
return getFormatter ( ) . parse ( formatted , LocaleContextHolder . getLocale ( ) ) ;
return formatter . parse ( formatted , LocaleContextHolder . getLocale ( ) ) ;
} catch ( ParseException e ) {
throw new IllegalArgumentException ( "Invalid format " + formatted , e ) ;
}
}
@SuppressWarnings ( "unchecked" )
private Formatter getFormatter ( ) {
if ( formatter ! = null ) {
return formatter ;
@ -224,9 +236,9 @@ public class Binder<T> {
@@ -224,9 +236,9 @@ public class Binder<T> {
} else {
Annotation [ ] annotations = getAnnotations ( ) ;
for ( Annotation a : annotations ) {
formatter = annotationFormatters . get ( a . annotationType ( ) ) ;
if ( formatter ! = null ) {
return formatter ;
AnnotationFormatterFactory factory = annotationFormatters . get ( a . annotationType ( ) ) ;
if ( factory ! = null ) {
return factory . getF ormatter ( a ) ;
}
}
return defaultFormatter ;
@ -276,4 +288,66 @@ public class Binder<T> {
@@ -276,4 +288,66 @@ public class Binder<T> {
context . setTypeConverter ( new StandardTypeConverter ( typeConverter ) ) ;
return context ;
}
private Class getAnnotationType ( AnnotationFormatterFactory factory ) {
Class classToIntrospect = factory . getClass ( ) ;
while ( classToIntrospect ! = null ) {
Type [ ] genericInterfaces = classToIntrospect . getGenericInterfaces ( ) ;
for ( Type genericInterface : genericInterfaces ) {
if ( genericInterface instanceof ParameterizedType ) {
ParameterizedType pInterface = ( ParameterizedType ) genericInterface ;
if ( AnnotationFormatterFactory . class . isAssignableFrom ( ( Class ) pInterface . getRawType ( ) ) ) {
return getParameterClass ( pInterface . getActualTypeArguments ( ) [ 0 ] , factory . getClass ( ) ) ;
}
}
}
classToIntrospect = classToIntrospect . getSuperclass ( ) ;
}
throw new IllegalArgumentException ( "Unable to extract Annotation type A argument from AnnotationFormatterFactory ["
+ factory . getClass ( ) . getName ( ) + "]; does the factory parameterize the <A> generic type?" ) ;
}
private Class getFormattedObjectType ( Formatter formatter ) {
// TODO consider caching this info
Class classToIntrospect = formatter . getClass ( ) ;
while ( classToIntrospect ! = null ) {
Type [ ] genericInterfaces = classToIntrospect . getGenericInterfaces ( ) ;
for ( Type genericInterface : genericInterfaces ) {
if ( genericInterface instanceof ParameterizedType ) {
ParameterizedType pInterface = ( ParameterizedType ) genericInterface ;
if ( Formatter . class . isAssignableFrom ( ( Class ) pInterface . getRawType ( ) ) ) {
return getParameterClass ( pInterface . getActualTypeArguments ( ) [ 0 ] , formatter . getClass ( ) ) ;
}
}
}
classToIntrospect = classToIntrospect . getSuperclass ( ) ;
}
return null ;
}
private Class getParameterClass ( Type parameterType , Class converterClass ) {
if ( parameterType instanceof TypeVariable ) {
parameterType = GenericTypeResolver . resolveTypeVariable ( ( TypeVariable ) parameterType , converterClass ) ;
}
if ( parameterType instanceof Class ) {
return ( Class ) parameterType ;
}
throw new IllegalArgumentException ( "Unable to obtain the java.lang.Class for parameterType [" + parameterType
+ "] on Formatter [" + converterClass . getName ( ) + "]" ) ;
}
@SuppressWarnings ( "unchecked" )
static class SimpleAnnotationFormatterFactory implements AnnotationFormatterFactory {
private Formatter formatter ;
public SimpleAnnotationFormatterFactory ( Formatter formatter ) {
this . formatter = formatter ;
}
public Formatter getFormatter ( Annotation annotation ) {
return formatter ;
}
}
}