@Model and @Bound annotations for configuring Binder instance from annotation model beans
This commit is contained in:
@@ -34,7 +34,7 @@ import org.springframework.util.CachingMapDecorator;
|
||||
public class DefaultAlertContext implements AlertContext {
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private Map<String, List<Alert>> alertsByElement = new CachingMapDecorator<String, List<Alert>>(new LinkedHashMap<String, List<Alert>>()) {
|
||||
private Map<String, List<Alert>> alerts = new CachingMapDecorator<String, List<Alert>>(new LinkedHashMap<String, List<Alert>>()) {
|
||||
protected List<Alert> create(String element) {
|
||||
return new ArrayList<Alert>();
|
||||
}
|
||||
@@ -43,11 +43,11 @@ public class DefaultAlertContext implements AlertContext {
|
||||
// implementing AlertContext
|
||||
|
||||
public Map<String, List<Alert>> getAlerts() {
|
||||
return Collections.unmodifiableMap(alertsByElement);
|
||||
return Collections.unmodifiableMap(alerts);
|
||||
}
|
||||
|
||||
public List<Alert> getAlerts(String element) {
|
||||
List<Alert> messages = alertsByElement.get(element);
|
||||
List<Alert> messages = alerts.get(element);
|
||||
if (messages.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -55,12 +55,12 @@ public class DefaultAlertContext implements AlertContext {
|
||||
}
|
||||
|
||||
public void add(Alert alert) {
|
||||
List<Alert> alerts = alertsByElement.get(alert.getElement());
|
||||
List<Alert> alerts = this.alerts.get(alert.getElement());
|
||||
alerts.add(alert);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("alertsByElement", alertsByElement).toString();
|
||||
return new ToStringCreator(this).append("alerts", alerts).toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ElementType.METHOD})
|
||||
@Target({ElementType.METHOD, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Bound {
|
||||
|
||||
@@ -20,6 +20,6 @@ public @interface Model {
|
||||
* Configures strict model binding.
|
||||
* @see Binder#setStrict(boolean)
|
||||
*/
|
||||
boolean strict() default false;
|
||||
boolean strictBinding() default false;
|
||||
|
||||
}
|
||||
|
||||
@@ -107,6 +107,10 @@ public class GenericBinder implements Binder {
|
||||
return model;
|
||||
}
|
||||
|
||||
public boolean isStrict() {
|
||||
return strict;
|
||||
}
|
||||
|
||||
public void setStrict(boolean strict) {
|
||||
this.strict = strict;
|
||||
}
|
||||
@@ -148,7 +152,11 @@ public class GenericBinder implements Binder {
|
||||
ArrayListBindingResults results = new ArrayListBindingResults(values.size());
|
||||
for (UserValue value : values) {
|
||||
BindingImpl binding = (BindingImpl) getBinding(value.getProperty());
|
||||
results.add(binding.setValue(value.getValue()));
|
||||
if (binding != null) {
|
||||
results.add(binding.setValue(value.getValue()));
|
||||
} else {
|
||||
results.add(new NoSuchBindingResult(value));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
@@ -449,6 +457,47 @@ public class GenericBinder implements Binder {
|
||||
}
|
||||
}
|
||||
|
||||
static class NoSuchBindingResult implements BindingResult {
|
||||
private UserValue userValue;
|
||||
|
||||
public NoSuchBindingResult(UserValue userValue) {
|
||||
this.userValue = userValue;
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
return userValue.getProperty();
|
||||
}
|
||||
|
||||
public Object getUserValue() {
|
||||
return userValue.getValue();
|
||||
}
|
||||
|
||||
public boolean isFailure() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Alert getAlert() {
|
||||
return new AbstractAlert() {
|
||||
public String getElement() {
|
||||
// TODO append model first? e.g. model.property
|
||||
return getProperty();
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return "noSuchBinding";
|
||||
}
|
||||
|
||||
public Severity getSeverity() {
|
||||
return Severity.WARNING;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return "Failed to bind to property '" + userValue.getProperty() + "'; no binding has been added for the property";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static class InvalidFormatResult implements BindingResult {
|
||||
|
||||
private String property;
|
||||
@@ -473,7 +522,7 @@ public class GenericBinder implements Binder {
|
||||
}
|
||||
|
||||
public Alert getAlert() {
|
||||
return new Alert() {
|
||||
return new AbstractAlert() {
|
||||
public String getElement() {
|
||||
// TODO append model first? e.g. model.property
|
||||
return getProperty();
|
||||
@@ -523,7 +572,7 @@ public class GenericBinder implements Binder {
|
||||
}
|
||||
|
||||
public Alert getAlert() {
|
||||
return new Alert() {
|
||||
return new AbstractAlert() {
|
||||
public String getElement() {
|
||||
// TODO append model first? e.g. model.property
|
||||
return getProperty();
|
||||
@@ -611,7 +660,7 @@ public class GenericBinder implements Binder {
|
||||
}
|
||||
|
||||
public Alert getAlert() {
|
||||
return new Alert() {
|
||||
return new AbstractAlert() {
|
||||
public String getElement() {
|
||||
// TODO append model first? e.g. model.property
|
||||
return getProperty();
|
||||
@@ -632,4 +681,10 @@ public class GenericBinder implements Binder {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static abstract class AbstractAlert implements Alert {
|
||||
public String toString() {
|
||||
return getElement() + ":" + getCode() + " - " + getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,25 @@
|
||||
*/
|
||||
package org.springframework.ui.lifecycle;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.ui.alert.AlertContext;
|
||||
import org.springframework.ui.binding.BindingConfiguration;
|
||||
import org.springframework.ui.binding.BindingResult;
|
||||
import org.springframework.ui.binding.BindingResults;
|
||||
import org.springframework.ui.binding.Bound;
|
||||
import org.springframework.ui.binding.FormatterRegistry;
|
||||
import org.springframework.ui.binding.Model;
|
||||
import org.springframework.ui.binding.UserValues;
|
||||
import org.springframework.ui.binding.support.WebBinder;
|
||||
import org.springframework.ui.validation.Validator;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Implementation of the bind and validate lifecycle for web (HTTP) environments.
|
||||
@@ -41,13 +51,12 @@ public class WebBindAndValidateLifecycle {
|
||||
private Validator validator;
|
||||
|
||||
public WebBindAndValidateLifecycle(Object model, AlertContext alertContext) {
|
||||
// TODO allow binder to be configured with bindings from @Model metadata
|
||||
// TODO support @Bound property annotation?
|
||||
// TODO support @StrictBinding class-level annotation?
|
||||
this.binder = new WebBinder(model);
|
||||
// TODO this doesn't belong in here
|
||||
configure(binder, model);
|
||||
this.alertContext = alertContext;
|
||||
}
|
||||
|
||||
|
||||
public void setFormatterRegistry(FormatterRegistry registry) {
|
||||
binder.setFormatterRegistry(registry);
|
||||
}
|
||||
@@ -76,4 +85,35 @@ public class WebBindAndValidateLifecycle {
|
||||
};
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private void configure(WebBinder binder, Object model) {
|
||||
Model m = AnnotationUtils.findAnnotation(model.getClass(), Model.class);
|
||||
if (m != null) {
|
||||
if (StringUtils.hasText(m.value())) {
|
||||
// TODO model name setting
|
||||
//binder.setModelName(m.value());
|
||||
}
|
||||
binder.setStrict(m.strictBinding());
|
||||
}
|
||||
if (binder.isStrict()) {
|
||||
BeanInfo beanInfo;
|
||||
try {
|
||||
beanInfo = Introspector.getBeanInfo(model.getClass());
|
||||
} catch (IntrospectionException e) {
|
||||
throw new IllegalStateException("Unable to introspect model " + model, e);
|
||||
}
|
||||
// TODO do we have to still flush introspector cache here?
|
||||
for (PropertyDescriptor prop : beanInfo.getPropertyDescriptors()) {
|
||||
Method getter = prop.getReadMethod();
|
||||
Bound b = AnnotationUtils.getAnnotation(getter, Bound.class);
|
||||
if (b != null) {
|
||||
// TODO should we wire formatter here if using a format annotation - an optimization?
|
||||
binder.add(new BindingConfiguration(prop.getName(), null));
|
||||
}
|
||||
}
|
||||
// TODO @Bound fields
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user