Added binder factory; simplified public binder api

This commit is contained in:
Keith Donald
2009-06-25 20:26:22 +00:00
parent 9b7e9942db
commit 5828010bae
14 changed files with 138 additions and 169 deletions

View File

@@ -17,15 +17,11 @@ package org.springframework.ui.binding;
import java.util.Map;
import org.springframework.ui.format.AnnotationFormatterFactory;
import org.springframework.ui.format.Formatter;
/**
* Binds user-entered values to properties of a model object.
* @author Keith Donald
* @since 3.0
* @see #configureBinding(BindingConfiguration)
* @see #bind(UserValues)
* @see #bind(Map)
*/
public interface Binder {
@@ -35,41 +31,6 @@ public interface Binder {
*/
Object getModel();
/**
* Configures if this binder is <i>strict</i>; a strict binder requires all bindings to be registered explicitly using {@link #configureBinding(BindingConfiguration)}.
* An <i>optimistic</i> binder will implicitly create bindings as required to support {@link #bind(UserValues)} operations.
* Default is optimistic.
* @param strict strict binder status
*/
void setStrict(boolean strict);
/**
* Adds a new binding.
* @param configuration the binding configuration
* @return the new binding created from the configuration provided
*/
Binding configureBinding(BindingConfiguration configuration);
/**
* Adds a Formatter that will format property values of type <code>propertyType</coe>.
* @param propertyType the property type
* @param formatter the formatter
*/
void registerFormatter(Class<?> propertyType, Formatter<?> formatter);
/**
* Adds a AnnotationFormatterFactory that will format values of properties annotated with a specific annotation.
* @param factory the annotation formatter factory
*/
void registerFormatterFactory(AnnotationFormatterFactory<?, ?> factory);
/**
* 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
*/
void setFormatterRegistry(FormatterRegistry registry);
/**
* Returns the binding for the property.
* @param property the property path

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2004-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ui.binding;
/**
* A factory for model Binders.
* @author Keith Donald
* @since 3.0
*/
public interface BinderFactory {
/**
* Get the Binder for the model
* @param model the model
* @return the binder
*/
Binder getBinder(Object model);
}

View File

@@ -21,7 +21,7 @@ import org.springframework.ui.alert.Alert;
* The result of a bind operation.
* @author Keith Donald
* @since 3.0
* @see Binder#bind(UserValues)
* @see Binder#bind(Map)
* @see Binding#setValue(Object)
*/
public interface BindingResult {

View File

@@ -0,0 +1,40 @@
package org.springframework.ui.binding.support;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.ui.binding.Bound;
import org.springframework.ui.binding.Model;
class AnnotatedModelBinderConfigurer {
public void configure(GenericBinder binder) {
Class<?> modelClass = binder.getModel().getClass();
Model m = AnnotationUtils.findAnnotation(modelClass, Model.class);
if (m != null) {
binder.setStrict(m.strictBinding());
}
if (binder.isStrict()) {
BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(modelClass);
} catch (IntrospectionException e) {
throw new IllegalStateException("Unable to introspect model " + binder.getModel(), 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.configureBinding(new BindingConfiguration(prop.getName(), null));
}
}
}
}
}

View File

@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ui.binding;
package org.springframework.ui.binding.support;
import org.springframework.ui.binding.support.GenericBinder;
import org.springframework.ui.binding.Binding;
import org.springframework.ui.format.Formatter;
/**

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ui.binding;
package org.springframework.ui.binding.support;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.ui.format.AnnotationFormatterFactory;

View File

@@ -53,11 +53,8 @@ import org.springframework.ui.alert.Alert;
import org.springframework.ui.alert.Severity;
import org.springframework.ui.binding.Binder;
import org.springframework.ui.binding.Binding;
import org.springframework.ui.binding.BindingConfiguration;
import org.springframework.ui.binding.BindingResult;
import org.springframework.ui.binding.BindingResults;
import org.springframework.ui.binding.FormatterRegistry;
import org.springframework.ui.format.AnnotationFormatterFactory;
import org.springframework.ui.format.Formatter;
import org.springframework.ui.message.MessageBuilder;
import org.springframework.ui.message.ResolvableArgument;
@@ -109,14 +106,30 @@ public class GenericBinder implements Binder {
return model;
}
/**
* Is this binder strict?
* A strict binder requires all bindings to be registered explicitly using {@link #configureBinding(BindingConfiguration)}.
*/
public boolean isStrict() {
return strict;
}
/**
* Configures if this binder is <i>strict</i>.
* A strict binder requires all bindings to be registered explicitly using {@link #configureBinding(BindingConfiguration)}.
* An <i>optimistic</i> binder will implicitly create bindings as required to support {@link #bind(UserValues)} operations.
* Default is optimistic.
* @param strict strict binder status
*/
public void setStrict(boolean strict) {
this.strict = strict;
}
/**
* 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;
@@ -127,6 +140,7 @@ public class GenericBinder implements Binder {
* @param messageSource the message source
*/
public void setMessageSource(MessageSource messageSource) {
Assert.notNull(messageSource, "The MessageSource is required");
this.messageSource = messageSource;
}
@@ -138,9 +152,15 @@ public class GenericBinder implements Binder {
* @see EvaluationContext#getTypeConverter()
*/
public void setTypeConverter(TypeConverter typeConverter) {
Assert.notNull(messageSource, "The TypeConverter is required");
this.typeConverter = typeConverter;
}
/**
* Configures a new binding on this binder.
* @param configuration the binding configuration
* @return the new binding created from the configuration provided
*/
public Binding configureBinding(BindingConfiguration configuration) {
Binding binding;
try {
@@ -152,14 +172,6 @@ public class GenericBinder implements Binder {
return binding;
}
public void registerFormatter(Class<?> propertyType, Formatter<?> formatter) {
formatterRegistry.add(propertyType, formatter);
}
public void registerFormatterFactory(AnnotationFormatterFactory<?, ?> factory) {
formatterRegistry.add(factory);
}
public Binding getBinding(String property) {
Binding binding = bindings.get(property);
if (binding == null && !strict) {

View File

@@ -24,7 +24,6 @@ import java.util.Map;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.ui.binding.FormatterRegistry;
import org.springframework.ui.format.AnnotationFormatterFactory;
import org.springframework.ui.format.Formatter;

View File

@@ -26,7 +26,7 @@ import java.util.Map;
* @see #setDefaultPrefix(String)
* @see #setPresentPrefix(String)
*/
public class WebBinder extends GenericBinder {
class WebBinder extends GenericBinder {
private String defaultPrefix = "!";

View File

@@ -0,0 +1,16 @@
package org.springframework.ui.binding.support;
import org.springframework.ui.binding.Binder;
import org.springframework.ui.binding.BinderFactory;
public class WebBinderFactory implements BinderFactory {
public Binder getBinder(Object model) {
WebBinder binder = new WebBinder(model);
new AnnotatedModelBinderConfigurer().configure(binder);
return binder;
}
// internal helpers
}

View File

@@ -15,24 +15,13 @@
*/
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.Binder;
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.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,7 +30,7 @@ import org.springframework.util.StringUtils;
*/
public class WebBindAndValidateLifecycle {
private final WebBinder binder;
private final Binder binder;
private final AlertContext alertContext;
@@ -49,19 +38,13 @@ public class WebBindAndValidateLifecycle {
private Validator validator;
public WebBindAndValidateLifecycle(Object model, AlertContext alertContext) {
this.binder = new WebBinder(model);
// TODO this doesn't belong in here
configure(binder, model);
public WebBindAndValidateLifecycle(Binder binder, AlertContext alertContext) {
this.binder = binder;
this.alertContext = alertContext;
}
public void setFormatterRegistry(FormatterRegistry registry) {
binder.setFormatterRegistry(registry);
}
public void execute(Map<String, ? extends Object> userMap) {
BindingResults bindingResults = binder.bind(userMap);
public void execute(Map<String, ? extends Object> sourceValues) {
BindingResults bindingResults = binder.bind(sourceValues);
if (validator != null && validationDecider.shouldValidateAfter(bindingResults)) {
// TODO get validation results
validator.validate(binder.getModel(), bindingResults.successes().properties());
@@ -83,35 +66,4 @@ 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.configureBinding(new BindingConfiguration(prop.getName(), null));
}
}
// TODO @Bound fields
}
}
}