type formatters
This commit is contained in:
parent
b2c723a76e
commit
3cd3cddbe0
|
|
@ -1,6 +1,5 @@
|
||||||
package org.springframework.ui.binding.support;
|
package org.springframework.ui.binding.support;
|
||||||
|
|
||||||
import org.springframework.ui.binding.Binding;
|
|
||||||
import org.springframework.ui.binding.config.Condition;
|
import org.springframework.ui.binding.config.Condition;
|
||||||
import org.springframework.ui.format.Formatter;
|
import org.springframework.ui.format.Formatter;
|
||||||
|
|
||||||
|
|
@ -17,8 +16,5 @@ public interface BindingRule {
|
||||||
Condition getEnabledCondition();
|
Condition getEnabledCondition();
|
||||||
|
|
||||||
Condition getVisibleCondition();
|
Condition getVisibleCondition();
|
||||||
|
|
||||||
// TODO - does this belong here?
|
|
||||||
Binding getBinding(String property, Object model);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -37,4 +37,8 @@ class BindingStatusResult implements BindingResult {
|
||||||
return bindingStatusAlert;
|
return bindingStatusAlert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return getAlert().toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.ui.binding.support;
|
package org.springframework.ui.binding.support;
|
||||||
|
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
import java.beans.PropertyDescriptor;
|
||||||
|
|
||||||
import org.springframework.ui.format.AnnotationFormatterFactory;
|
import org.springframework.ui.format.AnnotationFormatterFactory;
|
||||||
import org.springframework.ui.format.Formatter;
|
import org.springframework.ui.format.Formatter;
|
||||||
|
|
||||||
|
|
@ -28,13 +29,14 @@ import org.springframework.ui.format.Formatter;
|
||||||
*/
|
*/
|
||||||
public interface FormatterRegistry {
|
public interface FormatterRegistry {
|
||||||
|
|
||||||
|
Formatter<?> getFormatter(PropertyDescriptor property);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Formatter for the property type.
|
* Get the Formatter for the type.
|
||||||
* @param propertyType the property type descriptor, which provides additional property metadata.
|
|
||||||
* @return the Formatter, or <code>null</code> if none is registered
|
* @return the Formatter, or <code>null</code> if none is registered
|
||||||
*/
|
*/
|
||||||
Formatter<?> getFormatter(TypeDescriptor<?> propertyType);
|
Formatter<?> getFormatter(Class<?> type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a Formatter that will format the values of properties of the provided type.
|
* Adds a Formatter that will format the values of properties of the provided type.
|
||||||
* The type should generally be a concrete class for a scalar value, such as BigDecimal, and not a collection value.
|
* The type should generally be a concrete class for a scalar value, such as BigDecimal, and not a collection value.
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,17 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.ui.binding.support;
|
package org.springframework.ui.binding.support;
|
||||||
|
|
||||||
|
import java.beans.BeanInfo;
|
||||||
|
import java.beans.IntrospectionException;
|
||||||
|
import java.beans.Introspector;
|
||||||
|
import java.beans.PropertyDescriptor;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.context.MessageSource;
|
import org.springframework.context.MessageSource;
|
||||||
|
import org.springframework.core.GenericCollectionTypeResolver;
|
||||||
import org.springframework.core.convert.TypeConverter;
|
import org.springframework.core.convert.TypeConverter;
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.core.convert.support.DefaultTypeConverter;
|
import org.springframework.core.convert.support.DefaultTypeConverter;
|
||||||
import org.springframework.ui.binding.Binder;
|
import org.springframework.ui.binding.Binder;
|
||||||
import org.springframework.ui.binding.Binding;
|
import org.springframework.ui.binding.Binding;
|
||||||
|
|
@ -45,6 +51,8 @@ public class GenericBinder implements Binder {
|
||||||
|
|
||||||
private Map<String, GenericBindingRule> bindingRules;
|
private Map<String, GenericBindingRule> bindingRules;
|
||||||
|
|
||||||
|
private FormatterRegistry formatterRegistry;
|
||||||
|
|
||||||
private TypeConverter typeConverter;
|
private TypeConverter typeConverter;
|
||||||
|
|
||||||
private MessageSource messageSource;
|
private MessageSource messageSource;
|
||||||
|
|
@ -57,9 +65,20 @@ public class GenericBinder implements Binder {
|
||||||
Assert.notNull(model, "The model to bind to is required");
|
Assert.notNull(model, "The model to bind to is required");
|
||||||
this.model = model;
|
this.model = model;
|
||||||
bindingRules = new HashMap<String, GenericBindingRule>();
|
bindingRules = new HashMap<String, GenericBindingRule>();
|
||||||
|
formatterRegistry = new GenericFormatterRegistry();
|
||||||
typeConverter = new DefaultTypeConverter();
|
typeConverter = new DefaultTypeConverter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the registry of Formatters to query when no explicit Formatter has been registered for a Binding.
|
||||||
|
* Allows Formatters to be applied by property type and by property annotation.
|
||||||
|
* @param registry the formatter registry
|
||||||
|
*/
|
||||||
|
public void setFormatterRegistry(FormatterRegistry formatterRegistry) {
|
||||||
|
Assert.notNull(formatterRegistry, "The FormatterRegistry is required");
|
||||||
|
this.formatterRegistry = formatterRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure the MessageSource that resolves localized {@link BindingResult} alert messages.
|
* Configure the MessageSource that resolves localized {@link BindingResult} alert messages.
|
||||||
* @param messageSource the message source
|
* @param messageSource the message source
|
||||||
|
|
@ -148,7 +167,7 @@ public class GenericBinder implements Binder {
|
||||||
private GenericBindingRule getBindingRule(String property) {
|
private GenericBindingRule getBindingRule(String property) {
|
||||||
GenericBindingRule rule = bindingRules.get(property);
|
GenericBindingRule rule = bindingRules.get(property);
|
||||||
if (rule == null) {
|
if (rule == null) {
|
||||||
rule = new GenericBindingRule(property);
|
rule = new GenericBindingRule(property, model.getClass());
|
||||||
bindingRules.put(property, rule);
|
bindingRules.put(property, rule);
|
||||||
}
|
}
|
||||||
return rule;
|
return rule;
|
||||||
|
|
@ -169,15 +188,17 @@ public class GenericBinder implements Binder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
class GenericBindingRule implements BindingRule, BindingRuleConfiguration {
|
class GenericBindingRule implements BindingRuleConfiguration, BindingContext {
|
||||||
|
|
||||||
private String property;
|
private Class<?> modelClass;
|
||||||
|
|
||||||
private Formatter formatter = DefaultFormatter.INSTANCE;
|
private PropertyDescriptor property;
|
||||||
|
|
||||||
private Formatter elementFormatter = DefaultFormatter.INSTANCE;
|
private Formatter formatter;
|
||||||
|
|
||||||
private Formatter keyFormatter = DefaultFormatter.INSTANCE;
|
private Formatter elementFormatter;
|
||||||
|
|
||||||
|
private Formatter keyFormatter;
|
||||||
|
|
||||||
private Condition editableCondition = Condition.ALWAYS_TRUE;
|
private Condition editableCondition = Condition.ALWAYS_TRUE;
|
||||||
|
|
||||||
|
|
@ -189,26 +210,39 @@ public class GenericBinder implements Binder {
|
||||||
|
|
||||||
private Binding binding;
|
private Binding binding;
|
||||||
|
|
||||||
public GenericBindingRule(String property) {
|
public GenericBindingRule(String property, Class modelClass) {
|
||||||
this.property = property;
|
this.modelClass = modelClass;
|
||||||
|
this.property = findPropertyDescriptor(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
// implementing BindingRule
|
// implementing BindingContext
|
||||||
|
|
||||||
public Binding getBinding(String property, Object model) {
|
|
||||||
return getBindingRule(property).getBinding(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public TypeConverter getTypeConverter() {
|
||||||
|
return typeConverter;
|
||||||
|
}
|
||||||
|
|
||||||
public Formatter<?> getFormatter() {
|
public Formatter<?> getFormatter() {
|
||||||
return formatter;
|
if (formatter != null) {
|
||||||
|
return formatter;
|
||||||
|
} else {
|
||||||
|
return formatterRegistry.getFormatter(property);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Formatter<?> getElementFormatter() {
|
public Formatter<?> getElementFormatter() {
|
||||||
return elementFormatter;
|
if (elementFormatter != null) {
|
||||||
|
return formatter;
|
||||||
|
} else {
|
||||||
|
return formatterRegistry.getFormatter(getElementType());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Formatter<?> getKeyFormatter() {
|
public Formatter<?> getKeyFormatter() {
|
||||||
return keyFormatter;
|
if (keyFormatter != null) {
|
||||||
|
return keyFormatter;
|
||||||
|
} else {
|
||||||
|
return formatterRegistry.getFormatter(getKeyType());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Condition getEnabledCondition() {
|
public Condition getEnabledCondition() {
|
||||||
|
|
@ -223,8 +257,12 @@ public class GenericBinder implements Binder {
|
||||||
return visibleCondition;
|
return visibleCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Binding getBinding(String property, Object model) {
|
||||||
|
return getBindingRule(property).getBinding(model);
|
||||||
|
}
|
||||||
|
|
||||||
// implementing BindingRuleConfiguration
|
// implementing BindingRuleConfiguration
|
||||||
|
|
||||||
public BindingRuleConfiguration formatWith(Formatter<?> formatter) {
|
public BindingRuleConfiguration formatWith(Formatter<?> formatter) {
|
||||||
this.formatter = formatter;
|
this.formatter = formatter;
|
||||||
return this;
|
return this;
|
||||||
|
|
@ -255,10 +293,20 @@ public class GenericBinder implements Binder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// internal helpers
|
||||||
|
|
||||||
|
private Class<?> getElementType() {
|
||||||
|
return GenericCollectionTypeResolver.getCollectionReturnType(property.getReadMethod());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> getKeyType() {
|
||||||
|
return GenericCollectionTypeResolver.getMapKeyReturnType(property.getReadMethod());
|
||||||
|
}
|
||||||
|
|
||||||
GenericBindingRule getBindingRule(String property) {
|
GenericBindingRule getBindingRule(String property) {
|
||||||
GenericBindingRule rule = nestedBindingRules.get(property);
|
GenericBindingRule rule = nestedBindingRules.get(property);
|
||||||
if (rule == null) {
|
if (rule == null) {
|
||||||
rule = new GenericBindingRule(property);
|
rule = new GenericBindingRule(property, this.property.getPropertyType());
|
||||||
nestedBindingRules.put(property, rule);
|
nestedBindingRules.put(property, rule);
|
||||||
}
|
}
|
||||||
return rule;
|
return rule;
|
||||||
|
|
@ -266,11 +314,52 @@ public class GenericBinder implements Binder {
|
||||||
|
|
||||||
Binding getBinding(Object model) {
|
Binding getBinding(Object model) {
|
||||||
if (binding == null) {
|
if (binding == null) {
|
||||||
binding = new PropertyBinding(property, model, typeConverter, this);
|
binding = new PropertyBinding(property, model, this);
|
||||||
}
|
}
|
||||||
return binding;
|
return binding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PropertyDescriptor findPropertyDescriptor(String property) {
|
||||||
|
PropertyDescriptor[] propDescs = getBeanInfo(modelClass).getPropertyDescriptors();
|
||||||
|
for (PropertyDescriptor propDesc : propDescs) {
|
||||||
|
if (propDesc.getName().equals(property)) {
|
||||||
|
return propDesc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("No property '" + property + "' found on model ["
|
||||||
|
+ modelClass.getName() + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
private BeanInfo getBeanInfo(Class<?> clazz) {
|
||||||
|
try {
|
||||||
|
return Introspector.getBeanInfo(clazz);
|
||||||
|
} catch (IntrospectionException e) {
|
||||||
|
throw new IllegalStateException("Unable to introspect model type " + clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface BindingContext {
|
||||||
|
|
||||||
|
TypeConverter getTypeConverter();
|
||||||
|
|
||||||
|
Condition getEditableCondition();
|
||||||
|
|
||||||
|
Condition getEnabledCondition();
|
||||||
|
|
||||||
|
Condition getVisibleCondition();
|
||||||
|
|
||||||
|
Binding getBinding(String property, Object model);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Formatter getFormatter();
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Formatter getElementFormatter();
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Formatter getKeyFormatter();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.ui.binding.support;
|
package org.springframework.ui.binding.support;
|
||||||
|
|
||||||
|
import java.beans.PropertyDescriptor;
|
||||||
import java.lang.annotation.Annotation;
|
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;
|
||||||
|
|
@ -24,6 +25,7 @@ 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.MethodParameter;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.ui.format.AnnotationFormatterFactory;
|
import org.springframework.ui.format.AnnotationFormatterFactory;
|
||||||
|
|
@ -46,7 +48,8 @@ public class GenericFormatterRegistry implements FormatterRegistry {
|
||||||
|
|
||||||
private Map<Class, AnnotationFormatterFactory> annotationFormatters = new HashMap<Class, AnnotationFormatterFactory>();
|
private Map<Class, AnnotationFormatterFactory> annotationFormatters = new HashMap<Class, AnnotationFormatterFactory>();
|
||||||
|
|
||||||
public Formatter<?> getFormatter(TypeDescriptor<?> propertyType) {
|
public Formatter<?> getFormatter(PropertyDescriptor property) {
|
||||||
|
TypeDescriptor<?> propertyType = new TypeDescriptor(new MethodParameter(property.getReadMethod(), -1));
|
||||||
Annotation[] annotations = propertyType.getAnnotations();
|
Annotation[] annotations = propertyType.getAnnotations();
|
||||||
for (Annotation a : annotations) {
|
for (Annotation a : annotations) {
|
||||||
AnnotationFormatterFactory factory = annotationFormatters.get(a.annotationType());
|
AnnotationFormatterFactory factory = annotationFormatters.get(a.annotationType());
|
||||||
|
|
@ -66,7 +69,11 @@ public class GenericFormatterRegistry implements FormatterRegistry {
|
||||||
} else {
|
} else {
|
||||||
type = propertyType.getType();
|
type = propertyType.getType();
|
||||||
}
|
}
|
||||||
formatter = typeFormatters.get(type);
|
return getFormatter(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Formatter<?> getFormatter(Class<?> type) {
|
||||||
|
Formatter formatter = typeFormatters.get(type);
|
||||||
if (formatter != null) {
|
if (formatter != null) {
|
||||||
return formatter;
|
return formatter;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -84,11 +91,12 @@ public class GenericFormatterRegistry implements FormatterRegistry {
|
||||||
typeFormatters.put(type, formatter);
|
typeFormatters.put(type, formatter);
|
||||||
return formatter;
|
return formatter;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return DefaultFormatter.INSTANCE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void add(Class<?> propertyType, Formatter<?> formatter) {
|
public void add(Class<?> propertyType, Formatter<?> formatter) {
|
||||||
if (propertyType.isAnnotation()) {
|
if (propertyType.isAnnotation()) {
|
||||||
annotationFormatters.put(propertyType, new SimpleAnnotationFormatterFactory(formatter));
|
annotationFormatters.put(propertyType, new SimpleAnnotationFormatterFactory(formatter));
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,6 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.ui.binding.support;
|
package org.springframework.ui.binding.support;
|
||||||
|
|
||||||
import java.beans.BeanInfo;
|
|
||||||
import java.beans.IntrospectionException;
|
|
||||||
import java.beans.Introspector;
|
|
||||||
import java.beans.PropertyDescriptor;
|
import java.beans.PropertyDescriptor;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
|
@ -26,36 +23,32 @@ import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.ui.alert.Alert;
|
import org.springframework.ui.alert.Alert;
|
||||||
import org.springframework.ui.alert.Severity;
|
import org.springframework.ui.alert.Severity;
|
||||||
import org.springframework.ui.binding.Binding;
|
import org.springframework.ui.binding.Binding;
|
||||||
|
import org.springframework.ui.binding.support.GenericBinder.BindingContext;
|
||||||
import org.springframework.ui.format.Formatter;
|
import org.springframework.ui.format.Formatter;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public class PropertyBinding implements Binding {
|
public class PropertyBinding implements Binding {
|
||||||
|
|
||||||
private String property;
|
private PropertyDescriptor property;
|
||||||
|
|
||||||
private Object model;
|
private Object object;
|
||||||
|
|
||||||
private TypeConverter typeConverter;
|
private BindingContext bindingContext;
|
||||||
|
|
||||||
private BindingRule bindingRule;
|
|
||||||
|
|
||||||
private PropertyDescriptor propertyDescriptor;
|
|
||||||
|
|
||||||
private Object sourceValue;
|
private Object sourceValue;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private ParseException sourceValueParseException;
|
private ParseException sourceValueParseException;
|
||||||
|
|
||||||
private ValueBuffer buffer;
|
private ValueBuffer buffer;
|
||||||
|
|
||||||
private BindingStatus status;
|
private BindingStatus status;
|
||||||
|
|
||||||
public PropertyBinding(String property, Object model, TypeConverter typeConverter, BindingRule bindingRule) {
|
public PropertyBinding(PropertyDescriptor property, Object object, BindingContext bindingContext) {
|
||||||
initProperty(property, model);
|
this.property = property;
|
||||||
this.model = model;
|
this.object = object;
|
||||||
this.typeConverter = typeConverter;
|
this.bindingContext = bindingContext;
|
||||||
this.bindingRule = bindingRule;
|
|
||||||
this.buffer = new ValueBuffer(getModel());
|
this.buffer = new ValueBuffer(getModel());
|
||||||
status = BindingStatus.CLEAN;
|
status = BindingStatus.CLEAN;
|
||||||
}
|
}
|
||||||
|
|
@ -71,23 +64,23 @@ public class PropertyBinding implements Binding {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEditable() {
|
public boolean isEditable() {
|
||||||
return isWriteableProperty() && bindingRule.getEditableCondition().isTrue();
|
return isWriteableProperty() && bindingContext.getEditableCondition().isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return bindingRule.getEnabledCondition().isTrue();
|
return bindingContext.getEnabledCondition().isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isVisible() {
|
public boolean isVisible() {
|
||||||
return bindingRule.getVisibleCondition().isTrue();
|
return bindingContext.getVisibleCondition().isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applySourceValue(Object sourceValue) {
|
public void applySourceValue(Object sourceValue) {
|
||||||
assertEditable();
|
assertEditable();
|
||||||
assertEnabled();
|
assertEnabled();
|
||||||
if (sourceValue instanceof String) {
|
if (sourceValue instanceof String) {
|
||||||
try {
|
try {
|
||||||
buffer.setValue(bindingRule.getFormatter().parse((String) sourceValue, getLocale()));
|
buffer.setValue(bindingContext.getFormatter().parse((String) sourceValue, getLocale()));
|
||||||
sourceValue = null;
|
sourceValue = null;
|
||||||
status = BindingStatus.DIRTY;
|
status = BindingStatus.DIRTY;
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
|
|
@ -97,7 +90,7 @@ public class PropertyBinding implements Binding {
|
||||||
}
|
}
|
||||||
} else if (sourceValue instanceof String[]) {
|
} else if (sourceValue instanceof String[]) {
|
||||||
String[] sourceValues = (String[]) sourceValue;
|
String[] sourceValues = (String[]) sourceValue;
|
||||||
Class<?> parsedType = getFormattedObjectType(bindingRule.getElementFormatter().getClass());
|
Class<?> parsedType = getFormattedObjectType(bindingContext.getElementFormatter().getClass());
|
||||||
if (parsedType == null) {
|
if (parsedType == null) {
|
||||||
parsedType = String.class;
|
parsedType = String.class;
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +98,8 @@ public class PropertyBinding implements Binding {
|
||||||
for (int i = 0; i < sourceValues.length; i++) {
|
for (int i = 0; i < sourceValues.length; i++) {
|
||||||
Object parsedValue;
|
Object parsedValue;
|
||||||
try {
|
try {
|
||||||
parsedValue = bindingRule.getElementFormatter().parse(sourceValues[i], LocaleContextHolder.getLocale());
|
parsedValue = bindingContext.getElementFormatter().parse(sourceValues[i],
|
||||||
|
LocaleContextHolder.getLocale());
|
||||||
Array.set(parsed, i, parsedValue);
|
Array.set(parsed, i, parsedValue);
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
this.sourceValue = sourceValue;
|
this.sourceValue = sourceValue;
|
||||||
|
|
@ -121,14 +115,14 @@ public class PropertyBinding implements Binding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public BindingStatus getStatus() {
|
public BindingStatus getStatus() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Alert getStatusAlert() {
|
public Alert getStatusAlert() {
|
||||||
if (status == BindingStatus.INVALID_SOURCE_VALUE) {
|
if (status == BindingStatus.INVALID_SOURCE_VALUE) {
|
||||||
return new Alert() {
|
return new AbstractAlert() {
|
||||||
public String getCode() {
|
public String getCode() {
|
||||||
return "typeMismatch";
|
return "typeMismatch";
|
||||||
}
|
}
|
||||||
|
|
@ -139,11 +133,11 @@ public class PropertyBinding implements Binding {
|
||||||
|
|
||||||
public Severity getSeverity() {
|
public Severity getSeverity() {
|
||||||
return Severity.ERROR;
|
return Severity.ERROR;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if (status == BindingStatus.COMMIT_FAILURE) {
|
} else if (status == BindingStatus.COMMIT_FAILURE) {
|
||||||
if (buffer.getFlushException() instanceof ConversionFailedException) {
|
if (buffer.getFlushException() instanceof ConversionFailedException) {
|
||||||
return new Alert() {
|
return new AbstractAlert() {
|
||||||
public String getCode() {
|
public String getCode() {
|
||||||
return "typeMismatch";
|
return "typeMismatch";
|
||||||
}
|
}
|
||||||
|
|
@ -154,25 +148,25 @@ public class PropertyBinding implements Binding {
|
||||||
|
|
||||||
public Severity getSeverity() {
|
public Severity getSeverity() {
|
||||||
return Severity.ERROR;
|
return Severity.ERROR;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return new Alert() {
|
return new AbstractAlert() {
|
||||||
public String getCode() {
|
public String getCode() {
|
||||||
return "internalError";
|
return "internalError";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return "Internal error occurred";
|
return "Internal error occurred " + buffer.getFlushException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Severity getSeverity() {
|
public Severity getSeverity() {
|
||||||
return Severity.FATAL;
|
return Severity.FATAL;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (status == BindingStatus.COMMITTED) {
|
} else if (status == BindingStatus.COMMITTED) {
|
||||||
return new Alert() {
|
return new AbstractAlert() {
|
||||||
public String getCode() {
|
public String getCode() {
|
||||||
return "bindSucces";
|
return "bindSucces";
|
||||||
}
|
}
|
||||||
|
|
@ -183,8 +177,8 @@ public class PropertyBinding implements Binding {
|
||||||
|
|
||||||
public Severity getSeverity() {
|
public Severity getSeverity() {
|
||||||
return Severity.INFO;
|
return Severity.INFO;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -204,7 +198,7 @@ public class PropertyBinding implements Binding {
|
||||||
throw new IllegalStateException("Binding is not dirty; nothing to commit");
|
throw new IllegalStateException("Binding is not dirty; nothing to commit");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void revert() {
|
public void revert() {
|
||||||
if (status == BindingStatus.INVALID_SOURCE_VALUE) {
|
if (status == BindingStatus.INVALID_SOURCE_VALUE) {
|
||||||
sourceValue = null;
|
sourceValue = null;
|
||||||
|
|
@ -212,28 +206,29 @@ public class PropertyBinding implements Binding {
|
||||||
status = BindingStatus.CLEAN;
|
status = BindingStatus.CLEAN;
|
||||||
} else if (status == BindingStatus.DIRTY || status == BindingStatus.COMMIT_FAILURE) {
|
} else if (status == BindingStatus.DIRTY || status == BindingStatus.COMMIT_FAILURE) {
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
status = BindingStatus.CLEAN;
|
status = BindingStatus.CLEAN;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Nothing to revert");
|
throw new IllegalStateException("Nothing to revert");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Model getModel() {
|
public Model getModel() {
|
||||||
return new Model() {
|
return new Model() {
|
||||||
public Object getValue() {
|
public Object getValue() {
|
||||||
return ReflectionUtils.invokeMethod(propertyDescriptor.getReadMethod(), model);
|
return ReflectionUtils.invokeMethod(property.getReadMethod(), object);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class<?> getValueType() {
|
public Class<?> getValueType() {
|
||||||
return propertyDescriptor.getPropertyType();
|
return property.getPropertyType();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(Object value) {
|
public void setValue(Object value) {
|
||||||
TypeDescriptor targetType = new TypeDescriptor(new MethodParameter(propertyDescriptor.getWriteMethod(), 0));
|
TypeDescriptor targetType = new TypeDescriptor(new MethodParameter(property.getWriteMethod(), 0));
|
||||||
if (value != null && typeConverter.canConvert(value.getClass(), targetType)) {
|
TypeConverter converter = bindingContext.getTypeConverter();
|
||||||
value = typeConverter.convert(value, targetType);
|
if (value != null && converter.canConvert(value.getClass(), targetType)) {
|
||||||
|
value = converter.convert(value, targetType);
|
||||||
}
|
}
|
||||||
ReflectionUtils.invokeMethod(propertyDescriptor.getWriteMethod(), model, value);
|
ReflectionUtils.invokeMethod(property.getWriteMethod(), object, value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -243,7 +238,7 @@ public class PropertyBinding implements Binding {
|
||||||
if (getValue() == null) {
|
if (getValue() == null) {
|
||||||
createValue();
|
createValue();
|
||||||
}
|
}
|
||||||
return bindingRule.getBinding(property, getValue());
|
return bindingContext.getBinding(property, getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isList() {
|
public boolean isList() {
|
||||||
|
|
@ -264,7 +259,7 @@ public class PropertyBinding implements Binding {
|
||||||
assertMapProperty();
|
assertMapProperty();
|
||||||
if (key instanceof String) {
|
if (key instanceof String) {
|
||||||
try {
|
try {
|
||||||
key = bindingRule.getKeyFormatter().parse((String) key, getLocale());
|
key = bindingContext.getKeyFormatter().parse((String) key, getLocale());
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
throw new IllegalArgumentException("Invald key", e);
|
throw new IllegalArgumentException("Invald key", e);
|
||||||
}
|
}
|
||||||
|
|
@ -276,41 +271,17 @@ public class PropertyBinding implements Binding {
|
||||||
public String formatValue(Object value) {
|
public String formatValue(Object value) {
|
||||||
Formatter formatter;
|
Formatter formatter;
|
||||||
if (isList() || isMap()) {
|
if (isList() || isMap()) {
|
||||||
formatter = bindingRule.getElementFormatter();
|
formatter = bindingContext.getElementFormatter();
|
||||||
} else {
|
} else {
|
||||||
formatter = bindingRule.getFormatter();
|
formatter = bindingContext.getFormatter();
|
||||||
}
|
}
|
||||||
Class<?> formattedType = getFormattedObjectType(formatter.getClass());
|
Class<?> formattedType = getFormattedObjectType(formatter.getClass());
|
||||||
value = typeConverter.convert(value, formattedType);
|
value = bindingContext.getTypeConverter().convert(value, formattedType);
|
||||||
return formatter.format(value, getLocale());
|
return formatter.format(value, getLocale());
|
||||||
}
|
}
|
||||||
|
|
||||||
// internal helpers
|
// internal helpers
|
||||||
|
|
||||||
private void initProperty(String property, Object model) {
|
|
||||||
this.propertyDescriptor = findPropertyDescriptor(property, model);
|
|
||||||
this.property = property;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PropertyDescriptor findPropertyDescriptor(String property, Object model) {
|
|
||||||
PropertyDescriptor[] propDescs = getBeanInfo(model.getClass()).getPropertyDescriptors();
|
|
||||||
for (PropertyDescriptor propDesc : propDescs) {
|
|
||||||
if (propDesc.getName().equals(property)) {
|
|
||||||
return propDesc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("No property '" + property + "' found on model ["
|
|
||||||
+ model.getClass().getName() + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
private BeanInfo getBeanInfo(Class<?> clazz) {
|
|
||||||
try {
|
|
||||||
return Introspector.getBeanInfo(clazz);
|
|
||||||
} catch (IntrospectionException e) {
|
|
||||||
throw new IllegalStateException("Unable to introspect model type " + clazz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Locale getLocale() {
|
private Locale getLocale() {
|
||||||
return LocaleContextHolder.getLocale();
|
return LocaleContextHolder.getLocale();
|
||||||
}
|
}
|
||||||
|
|
@ -320,11 +291,11 @@ public class PropertyBinding implements Binding {
|
||||||
Object value = getModel().getValueType().newInstance();
|
Object value = getModel().getValueType().newInstance();
|
||||||
getModel().setValue(value);
|
getModel().setValue(value);
|
||||||
} catch (InstantiationException e) {
|
} catch (InstantiationException e) {
|
||||||
throw new IllegalStateException("Could not lazily instantiate model of type [" + getModel().getValueType().getName()
|
throw new IllegalStateException("Could not lazily instantiate object of type ["
|
||||||
+ "] to access property" + property, e);
|
+ getModel().getValueType().getName() + "] to access property" + property, e);
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new IllegalStateException("Could not lazily instantiate model of type [" + getModel().getValueType().getName()
|
throw new IllegalStateException("Could not lazily instantiate object of type ["
|
||||||
+ "] to access property" + property, e);
|
+ getModel().getValueType().getName() + "] to access property" + property, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -358,8 +329,7 @@ public class PropertyBinding implements Binding {
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private CollectionTypeDescriptor getCollectionTypeDescriptor() {
|
private CollectionTypeDescriptor getCollectionTypeDescriptor() {
|
||||||
Class<?> elementType = GenericCollectionTypeResolver.getCollectionReturnType(propertyDescriptor
|
Class<?> elementType = GenericCollectionTypeResolver.getCollectionReturnType(property.getReadMethod());
|
||||||
.getReadMethod());
|
|
||||||
return new CollectionTypeDescriptor(getModel().getValueType(), elementType);
|
return new CollectionTypeDescriptor(getModel().getValueType(), elementType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -383,7 +353,7 @@ public class PropertyBinding implements Binding {
|
||||||
throw new IllegalStateException("Not a Map property binding");
|
throw new IllegalStateException("Not a Map property binding");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertEditable() {
|
private void assertEditable() {
|
||||||
if (!isEditable()) {
|
if (!isEditable()) {
|
||||||
throw new IllegalStateException("Binding is not editable");
|
throw new IllegalStateException("Binding is not editable");
|
||||||
|
|
@ -397,6 +367,13 @@ public class PropertyBinding implements Binding {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isWriteableProperty() {
|
private boolean isWriteableProperty() {
|
||||||
return propertyDescriptor.getWriteMethod() != null;
|
return property.getWriteMethod() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static abstract class AbstractAlert implements Alert {
|
||||||
|
public String toString() {
|
||||||
|
return getCode() + " - " + getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -97,32 +97,29 @@ public class GenericBinderTests {
|
||||||
assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), bean.getDate());
|
assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), bean.getDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
@Test
|
@Test
|
||||||
public void bindSingleValuePropertyFormatterParseException() {
|
public void bindSingleValuePropertyFormatterParseException() {
|
||||||
BindingRulesBuilder builder = new BindingRulesBuilder(TestBean.class);
|
GenericBinder binder = new GenericBinder(bean);
|
||||||
builder.bind("date").formatWith(new DateFormatter());
|
binder.bindingRule("date").formatWith(new DateFormatter());
|
||||||
GenericBinder binder = new GenericBinder(bean, builder.getBindingRules());
|
|
||||||
|
|
||||||
BindingResults results = binder.bind(Collections.singletonMap("date", "bogus"));
|
BindingResults results = binder.bind(Collections.singletonMap("date", "bogus"));
|
||||||
assertEquals(1, results.size());
|
assertEquals(1, results.size());
|
||||||
assertTrue(results.get(0).isFailure());
|
assertTrue(results.get(0).isFailure());
|
||||||
assertEquals("invalidFormat", results.get(0).getAlert().getCode());
|
assertEquals("typeMismatch", results.get(0).getAlert().getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void bindSingleValueWithFormatterRegistedByType() throws ParseException {
|
public void bindSingleValueWithFormatterRegistedByType() throws ParseException {
|
||||||
BindingRulesBuilder builder = new BindingRulesBuilder(TestBean.class);
|
GenericBinder binder = new GenericBinder(bean);
|
||||||
builder.bind("date").formatWith(new DateFormatter());
|
|
||||||
GenericBinder binder = new GenericBinder(bean, builder.getBindingRules());
|
|
||||||
|
|
||||||
GenericFormatterRegistry formatterRegistry = new GenericFormatterRegistry();
|
GenericFormatterRegistry formatterRegistry = new GenericFormatterRegistry();
|
||||||
formatterRegistry.add(Date.class, new DateFormatter());
|
formatterRegistry.add(Date.class, new DateFormatter());
|
||||||
|
binder.setFormatterRegistry(formatterRegistry);
|
||||||
|
|
||||||
binder.bind(Collections.singletonMap("date", "2009-06-01"));
|
binder.bind(Collections.singletonMap("date", "2009-06-01"));
|
||||||
assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), bean.getDate());
|
assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), bean.getDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
@Test
|
@Test
|
||||||
public void bindSingleValueWithAnnotationFormatterFactoryRegistered() throws ParseException {
|
public void bindSingleValueWithAnnotationFormatterFactoryRegistered() throws ParseException {
|
||||||
binder.addBinding("currency");
|
binder.addBinding("currency");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue