removed user values in favor of simple map after code review

This commit is contained in:
Keith Donald
2009-06-25 03:17:04 +00:00
parent 8eb8602125
commit a0520501d3
11 changed files with 107 additions and 255 deletions

View File

@@ -24,7 +24,6 @@ import org.springframework.ui.format.Formatter;
* Binds user-entered values to properties of a model object.
* @author Keith Donald
* @since 3.0
* @param <M> The kind of model object this binder binds to
* @see #configureBinding(BindingConfiguration)
* @see #bind(UserValues)
*/
@@ -35,7 +34,7 @@ public interface Binder {
* @return the model object
*/
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.
@@ -64,6 +63,13 @@ public interface Binder {
*/
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
@@ -72,20 +78,10 @@ public interface Binder {
Binding getBinding(String property);
/**
* Bind values in the map to the properties of the model object.
* @param values user-entered values to bind
* Bind source values in the map to the properties of the model object.
* @param values the source values to bind
* @return the results of the binding operation
*/
BindingResults bind(UserValues values);
/**
* Creates a {@link UserValue} list from a Map of user-submitted fields.
* The Binder may apply transformations as part of the creation process.
* For example, a Binder might insert empty or default values for fields that are not present.
* As another example, a Binder might collapse multiple fields into a single {@link UserValue} object.
* @param userMap the map of user-submitted fields
* @return the UserValue list that can be passed to {@link #bind(UserValues)}.
*/
UserValues createUserValues(Map<String, ? extends Object> userMap);
BindingResults bind(Map<String, ? extends Object> sourceValues);
}

View File

@@ -56,7 +56,7 @@ public interface Binding {
/**
* The type of the underlying property associated with this binding.
*/
Class<?>getType();
Class<?> getType();
}

View File

@@ -48,7 +48,7 @@ public class BindingConfiguration {
}
/**
* THe Formatter to use to format bound property values.
* The Formatter to use to format bound property values.
*/
public Formatter<?> getFormatter() {
return formatter;

View File

@@ -32,11 +32,11 @@ public interface BindingResult {
String getProperty();
/**
* The raw user-entered value for which binding was attempted.
* The raw source value for which binding was attempted.
* If not a failure, this value was successfully bound to the model.
* @see #isFailure()
*/
Object getUserValue();
Object getSourceValue();
/**
* Indicates if the binding failed.

View File

@@ -1,54 +0,0 @@
/*
* 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;
/**
* Holds a user-entered value to bind to a model property.
* @author Keith Donald
* @since 3.0
* @see Binder#bind(List)
*/
public class UserValue {
private String property;
private Object value;
/**
* Create a new user value
* @param property the property associated with the value
* @param value the actual user-entered value
*/
public UserValue(String property, Object value) {
this.property = property;
this.value = value;
}
/**
* The property the user-entered value should bind to.
*/
public String getProperty() {
return property;
}
/**
* The actual user-entered value.
*/
public Object getValue() {
return value;
}
}

View File

@@ -1,90 +0,0 @@
/*
* 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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* A holder for a list of UserValues.
* TODO just use a map here
* @author Keith Donald
* @since 3.0
* @see Binder#bind(UserValues)
*/
public class UserValues implements Iterable<UserValue> {
private List<UserValue> values;
/**
* Creates a new user values list of the default size.
*/
public UserValues() {
values = new ArrayList<UserValue>();
}
/**
* Creates a new user values list of the size provided.
*/
public UserValues(int size) {
values = new ArrayList<UserValue>(size);
}
// implementing Iterable
public Iterator<UserValue> iterator() {
return values.iterator();
}
/**
* The user values list.
* The returned list is not modifiable.
*/
public List<UserValue> asList() {
return Collections.unmodifiableList(values);
}
/**
* Add a new user value.
* @param property the property the value should be bound to
* @param value the actual user-entered value
*/
public void add(String property, Object value) {
values.add(new UserValue(property, value));
}
/**
* The number of user values in the list.
*/
public int size() {
return values.size();
}
/**
* Creates a new UserValues list with a single element.
* @param property the property
* @param value the actual user-entered value
* @return the singleton user value list
*/
public static UserValues single(String property, Object value) {
UserValues values = new UserValues(1);
values.add(property, value);
return values;
}
}

View File

@@ -56,15 +56,13 @@ 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.binding.UserValue;
import org.springframework.ui.binding.UserValues;
import org.springframework.ui.format.AnnotationFormatterFactory;
import org.springframework.ui.format.Formatter;
import org.springframework.util.Assert;
/**
* A generic {@link Binder binder} suitable for use in most environments.
* TODO - localization of alert messages using MessageResolver/MesageSource
* TODO - localization of alert messages using MessageResolver/MessageSource
* @author Keith Donald
* @since 3.0
* @see #configureBinding(BindingConfiguration)
@@ -94,7 +92,7 @@ public class GenericBinder implements Binder {
* @param model the model object containing properties this binder will bind to
*/
public GenericBinder(Object model) {
Assert.notNull(model, "The model Object is reqyured");
Assert.notNull(model, "The model Object is required");
this.model = model;
bindings = new HashMap<String, Binding>();
int parserConfig = SpelExpressionParserConfiguration.CreateListsOnAttemptToIndexIntoNull
@@ -148,25 +146,31 @@ public class GenericBinder implements Binder {
}
}
public BindingResults bind(UserValues values) {
ArrayListBindingResults results = new ArrayListBindingResults(values.size());
for (UserValue value : values) {
BindingImpl binding = (BindingImpl) getBinding(value.getProperty());
public BindingResults bind(Map<String, ? extends Object> sourceValues) {
sourceValues = filter(sourceValues);
ArrayListBindingResults results = new ArrayListBindingResults(sourceValues.size());
for (Map.Entry<String, ? extends Object> sourceValue : sourceValues.entrySet()) {
String property = sourceValue.getKey();
Object value = sourceValue.getValue();
BindingImpl binding = (BindingImpl) getBinding(property);
if (binding != null) {
results.add(binding.setValue(value.getValue()));
results.add(binding.setValue(value));
} else {
results.add(new NoSuchBindingResult(value));
results.add(new NoSuchBindingResult(property, value));
}
}
}
return results;
}
public UserValues createUserValues(Map<String, ? extends Object> userMap) {
UserValues values = new UserValues(userMap.size());
for (Map.Entry<String, ? extends Object> entry : userMap.entrySet()) {
values.add(entry.getKey(), entry.getValue());
}
return values;
// subclassing hooks
/**
* Hook subclasses may use to filter the source values to bind.
* @param sourceValues the original source values map provided by the caller
* @return the filtered source values map that will be used to bind
*/
protected Map<String, ? extends Object> filter(Map<String, ? extends Object> sourceValues) {
return sourceValues;
}
// internal helpers
@@ -458,18 +462,21 @@ public class GenericBinder implements Binder {
}
static class NoSuchBindingResult implements BindingResult {
private UserValue userValue;
private String property;
public NoSuchBindingResult(UserValue userValue) {
this.userValue = userValue;
private Object sourceValue;
public NoSuchBindingResult(String property, Object sourceValue) {
this.property = property;
this.sourceValue = sourceValue;
}
public String getProperty() {
return userValue.getProperty();
return property;
}
public Object getUserValue() {
return userValue.getValue();
public Object getSourceValue() {
return sourceValue;
}
public boolean isFailure() {
@@ -492,7 +499,7 @@ public class GenericBinder implements Binder {
}
public String getMessage() {
return "Failed to bind to property '" + userValue.getProperty() + "'; no binding has been added for the property";
return "Failed to bind to property '" + property + "'; no binding has been added for the property";
}
};
}
@@ -513,7 +520,7 @@ public class GenericBinder implements Binder {
return property;
}
public Object getUserValue() {
public Object getSourceValue() {
return formatted;
}
@@ -563,7 +570,7 @@ public class GenericBinder implements Binder {
return property;
}
public Object getUserValue() {
public Object getSourceValue() {
return formatted;
}
@@ -651,7 +658,7 @@ public class GenericBinder implements Binder {
return property;
}
public Object getUserValue() {
public Object getSourceValue() {
return formatted;
}

View File

@@ -15,11 +15,9 @@
*/
package org.springframework.ui.binding.support;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.ui.binding.UserValue;
import org.springframework.ui.binding.UserValues;
/**
* A binder designed for use in HTTP (web) environments.
* Suited for binding user-provided HTTP query parameters to model properties.
@@ -61,27 +59,27 @@ public class WebBinder extends GenericBinder {
}
@Override
public UserValues createUserValues(Map<String, ? extends Object> userMap) {
UserValues values = new UserValues();
for (Map.Entry<String, ? extends Object> entry : userMap.entrySet()) {
protected Map<String, ? extends Object> filter(Map<String, ? extends Object> sourceValues) {
LinkedHashMap<String, Object> filteredValues = new LinkedHashMap<String, Object>();
for (Map.Entry<String, ? extends Object> entry : sourceValues.entrySet()) {
String field = entry.getKey();
Object value = entry.getValue();
if (field.startsWith(defaultPrefix)) {
field = field.substring(defaultPrefix.length());
if (!userMap.containsKey(field)) {
values.add(field, value);
if (!sourceValues.containsKey(field)) {
filteredValues.put(field, value);
}
} else if (field.startsWith(presentPrefix)) {
field = field.substring(presentPrefix.length());
if (!userMap.containsKey(field) && !userMap.containsKey(defaultPrefix + field)) {
if (!sourceValues.containsKey(field) && !sourceValues.containsKey(defaultPrefix + field)) {
value = getEmptyValue((BindingImpl) getBinding(field));
values.add(field, value);
filteredValues.put(field, value);
}
} else {
values.add(entry.getKey(), entry.getValue());
filteredValues.put(entry.getKey(), entry.getValue());
}
}
return values;
return filteredValues;
}
protected Object getEmptyValue(BindingImpl binding) {

View File

@@ -30,7 +30,6 @@ 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;
@@ -62,8 +61,7 @@ public class WebBindAndValidateLifecycle {
}
public void execute(Map<String, ? extends Object> userMap) {
UserValues values = binder.createUserValues(userMap);
BindingResults bindingResults = binder.bind(values);
BindingResults bindingResults = binder.bind(userMap);
if (validator != null && validationDecider.shouldValidateAfter(bindingResults)) {
// TODO get validation results
validator.validate(binder.getModel(), bindingResults.successes().properties());