Refactored class naming to capture ui.binding system as a general PresentationModel framework; PresentationModel and FieldModel are the key API elements now
This commit is contained in:
parent
c7b019cd5c
commit
5cb5169237
|
|
@ -16,9 +16,10 @@
|
|||
package org.springframework.ui.binding;
|
||||
|
||||
/**
|
||||
* Binding states.
|
||||
* FieldModel binding states.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
* @see FieldModel#getBindingStatus()
|
||||
*/
|
||||
public enum BindingStatus {
|
||||
|
||||
|
|
@ -28,9 +29,9 @@ public enum BindingStatus {
|
|||
CLEAN,
|
||||
|
||||
/**
|
||||
* An invalid source value is applied.
|
||||
* An invalid submitted value is applied.
|
||||
*/
|
||||
INVALID_SOURCE_VALUE,
|
||||
INVALID_SUBMITTED_VALUE,
|
||||
|
||||
/**
|
||||
* The binding buffer contains a valid value that has not been committed.
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ import org.springframework.ui.alert.Alert;
|
|||
import org.springframework.ui.alert.Severity;
|
||||
|
||||
/**
|
||||
* A binding between one or more UI components and a model property.
|
||||
* A model for a single data field containing dynamic information to display in the view.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface Binding {
|
||||
public interface FieldModel {
|
||||
|
||||
/**
|
||||
* The model value formatted for display in a single field in the UI.
|
||||
|
|
@ -33,47 +33,47 @@ public interface Binding {
|
|||
String getRenderValue();
|
||||
|
||||
/**
|
||||
* The bound model value.
|
||||
* The field model value.
|
||||
*/
|
||||
Object getValue();
|
||||
|
||||
/**
|
||||
* The bound model value type.
|
||||
* The field model value type.
|
||||
*/
|
||||
Class<?> getValueType();
|
||||
|
||||
/**
|
||||
* If this Binding is editable.
|
||||
* If editable.
|
||||
* Used to determine if the user can edit the field value.
|
||||
* A Binding that is not editable cannot have source values applied and cannot be committed.
|
||||
* A Binding that is not editable cannot have submitted values applied and cannot be committed.
|
||||
*/
|
||||
boolean isEditable();
|
||||
|
||||
/**
|
||||
* If this Binding is enabled.
|
||||
* If enabled.
|
||||
* Used to determine if the user can interact with the field at all.
|
||||
* A Binding that is not enabled cannot have source values applied and cannot be committed.
|
||||
* A Binding that is not enabled cannot have submitted values applied and cannot be committed.
|
||||
*/
|
||||
boolean isEnabled();
|
||||
|
||||
/**
|
||||
* If this Binding is visible.
|
||||
* If visible.
|
||||
* Used to determine if the user can see the field.
|
||||
*/
|
||||
boolean isVisible();
|
||||
|
||||
/**
|
||||
* The current binding status.
|
||||
* The current field binding status.
|
||||
* Initially {@link BindingStatus#CLEAN clean}.
|
||||
* Is {@link BindingStatus#DIRTY} after applying a source value to the value buffer.
|
||||
* Is {@link BindingStatus#DIRTY} after applying a submitted value to the value buffer.
|
||||
* Is {@link BindingStatus#COMMITTED} after successfully committing the buffered value.
|
||||
* Is {@link BindingStatus#INVALID_SOURCE_VALUE} if a source value could not be applied.
|
||||
* Is {@link BindingStatus#INVALID_SUBMITTED_VALUE} if a submitted value could not be applied.
|
||||
* Is {@link BindingStatus#COMMIT_FAILURE} if a buffered value could not be committed.
|
||||
*/
|
||||
BindingStatus getBindingStatus();
|
||||
|
||||
/**
|
||||
* The current validation status.
|
||||
* The current field validation status.
|
||||
* Initially {@link ValidationStatus#NOT_VALIDATED}.
|
||||
* Is {@link ValidationStatus#VALID} after value is successfully validated.
|
||||
* Is {@link ValidationStatus#INVALID} after value fails validation.
|
||||
|
|
@ -82,31 +82,31 @@ public interface Binding {
|
|||
ValidationStatus getValidationStatus();
|
||||
|
||||
/**
|
||||
* An alert that communicates current status to the user.
|
||||
* An alert that communicates current FieldModel status to the user.
|
||||
* Returns <code>null</code> if {@link BindingStatus#CLEAN} and {@link ValidationStatus#NOT_VALIDATED}.
|
||||
* Returns a {@link Severity#INFO} Alert with code <code>bindSuccess</code> when {@link BindingStatus#COMMITTED}.
|
||||
* Returns a {@link Severity#ERROR} Alert with code <code>typeMismatch</code> when {@link BindingStatus#INVALID_SOURCE_VALUE} or {@link BindingStatus#COMMIT_FAILURE} due to a value parse / type conversion error.
|
||||
* Returns a {@link Severity#ERROR} Alert with code <code>typeMismatch</code> when {@link BindingStatus#INVALID_SUBMITTED_VALUE} or {@link BindingStatus#COMMIT_FAILURE} due to a value parse / type conversion error.
|
||||
* Returns a {@link Severity#FATAL} Alert with code <code>internalError</code> when {@link BindingStatus#COMMIT_FAILURE} due to a unexpected runtime exception.
|
||||
* Returns a {@link Severity#INFO} Alert describing results of validation if {@link ValidationStatus#VALID} or {@link ValidationStatus#INVALID}.
|
||||
*/
|
||||
Alert getStatusAlert();
|
||||
|
||||
/**
|
||||
* Apply the source value to this binding.
|
||||
* The source value is parsed and stored in the binding's value buffer.
|
||||
* Apply a submitted value to this FieldModel.
|
||||
* The submitted value is parsed and stored in the value buffer.
|
||||
* Sets to {@link BindingStatus#DIRTY} if succeeds.
|
||||
* Sets to {@link BindingStatus#INVALID_SOURCE_VALUE} if fails.
|
||||
* @param sourceValue
|
||||
* Sets to {@link BindingStatus#INVALID_SUBMITTED_VALUE} if fails.
|
||||
* @param submittedValue
|
||||
* @throws IllegalStateException if not editable or not enabled
|
||||
*/
|
||||
void applySourceValue(Object sourceValue);
|
||||
void applySubmittedValue(Object submittedValue);
|
||||
|
||||
/**
|
||||
* If {@link BindingStatus#INVALID_SOURCE_VALUE}, returns the invalid source value.
|
||||
* If {@link BindingStatus#INVALID_SUBMITTED_VALUE}, returns the invalid submitted value.
|
||||
* Returns null otherwise.
|
||||
* @return the invalid source value
|
||||
* @return the invalid submitted value
|
||||
*/
|
||||
Object getInvalidSourceValue();
|
||||
Object getInvalidSubmittedValue();
|
||||
|
||||
/**
|
||||
* Validate the model value.
|
||||
|
|
@ -130,45 +130,46 @@ public interface Binding {
|
|||
void revert();
|
||||
|
||||
/**
|
||||
* Get a Binding to a nested property value.
|
||||
* @param property the nested property name, such as "foo"; should not be a property path like "foo.bar"
|
||||
* @return the binding to the nested property
|
||||
* @throws IllegalStateException if not a bean
|
||||
* Get a model for a nested field.
|
||||
* @param fieldName the nested field name, such as "foo"; should not be a property path like "foo.bar"
|
||||
* @return the nested field model
|
||||
* @throws IllegalStateException if {@link #isList()}
|
||||
* @throws FieldNotFoundException if no such nested field exists
|
||||
*/
|
||||
Binding getNestedBinding(String property);
|
||||
FieldModel getNested(String fieldName);
|
||||
|
||||
/**
|
||||
* If bound to an indexable collection, either a {@link java.util.List} or an array.
|
||||
* If an indexable {@link java.util.List} or array.
|
||||
*/
|
||||
boolean isList();
|
||||
|
||||
/**
|
||||
* If a list, get a Binding to a element in the list..
|
||||
* If {@link #isList()}, get a FieldModel for a element in the list..
|
||||
* @param index the element index
|
||||
* @return the indexed binding
|
||||
* @throws IllegalStateException if not a list
|
||||
*/
|
||||
Binding getListElementBinding(int index);
|
||||
FieldModel getListElement(int index);
|
||||
|
||||
/**
|
||||
* If bound to a Map.
|
||||
* If a Map.
|
||||
*/
|
||||
boolean isMap();
|
||||
|
||||
/**
|
||||
* If a Map, get a Binding to a value in the Map.
|
||||
* If {@link #isMap()}, get FieldModel for a value in the Map.
|
||||
* @param key the map key
|
||||
* @return the keyed binding
|
||||
* @throws IllegalStateException if not a map
|
||||
*/
|
||||
Binding getMapValueBinding(Object key);
|
||||
FieldModel getMapValue(Object key);
|
||||
|
||||
/**
|
||||
* Format a potential model value for display.
|
||||
* If a list binding, expects the model value to be a potential list element & uses the configured element formatter.
|
||||
* If a map binding, expects the model value to be a potential map value & uses the configured map value formatter.
|
||||
* If {@link #isList()}, expects the value to be a potential list element & uses the configured element formatter.
|
||||
* If {@link #isMap()}, expects the value to be a potential map value & uses the configured map value formatter.
|
||||
* @param potentialValue the potential value
|
||||
* @return the formatted string
|
||||
*/
|
||||
String formatValue(Object potentialModelValue);
|
||||
String formatValue(Object potentialValue);
|
||||
}
|
||||
|
|
@ -16,22 +16,27 @@
|
|||
package org.springframework.ui.binding;
|
||||
|
||||
/**
|
||||
* A factory for model property bindings.
|
||||
* Thrown when a PresentationModel field cannot be found.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
* @see PresentationModel#getFieldModel(String)
|
||||
* @see FieldModel#getNested(String)
|
||||
*/
|
||||
public interface BindingFactory {
|
||||
@SuppressWarnings("serial")
|
||||
public class FieldNotFoundException extends RuntimeException {
|
||||
|
||||
private String field;
|
||||
|
||||
/**
|
||||
* The model object upon which bindings may be accessed.
|
||||
* Creates a new FieldNotFoundException.
|
||||
* @param fieldName the field not found exception
|
||||
*/
|
||||
Object getModel();
|
||||
public FieldNotFoundException(String fieldName) {
|
||||
super("No field '" + fieldName + "' found");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a binding to a model property.
|
||||
* @param property the property path
|
||||
* @throws NoSuchBindingException if no binding to the property exists
|
||||
*/
|
||||
Binding getBinding(String property);
|
||||
public String getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Represents the state and behavior of a presentation independently of the GUI controls used in the interface.
|
||||
* Pulls the state and behavior of a view out into a model class that is part of the presentation.
|
||||
* Coordinates with the domain layer and provides an interface to the view that minimizes decision making in the view.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface PresentationModel {
|
||||
|
||||
/**
|
||||
* Get the model for the field.
|
||||
* @param fieldName the field name.
|
||||
* @throws FieldNotFoundException if no such field exists
|
||||
*/
|
||||
FieldModel getFieldModel(String fieldName);
|
||||
|
||||
}
|
||||
|
|
@ -16,19 +16,19 @@
|
|||
package org.springframework.ui.binding;
|
||||
|
||||
/**
|
||||
* A locator for BindingFactories indexed by their models.
|
||||
* Makes it easy for clients to lookup BindingFactories for models the need to bind to.
|
||||
* A factory for domain object PresentationModels.
|
||||
* Makes it easy for clients to lookup PresentationModels for domain objects they need to bind to.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface BindingFactoryLocator {
|
||||
public interface PresentationModelFactory {
|
||||
|
||||
/**
|
||||
* Get the BindingFactory for the model object.
|
||||
* If no such BindingFactory exists, one is created and cached.
|
||||
* Get the PresentationModel for the domain object.
|
||||
* If none exists, one is created and cached.
|
||||
* Never returns <code>null</code>.
|
||||
* @param model the model object
|
||||
* @return the binding Factory
|
||||
* @param domainObject the model object
|
||||
* @return the presentation model
|
||||
*/
|
||||
public BindingFactory getBindingFactory(Object model);
|
||||
public PresentationModel getPresentationModel(Object domainObject);
|
||||
}
|
||||
|
|
@ -16,9 +16,10 @@
|
|||
package org.springframework.ui.binding;
|
||||
|
||||
/**
|
||||
* Validation states.
|
||||
* FieldModel Validation states.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
* @see FieldModel#getValidationStatus()
|
||||
*/
|
||||
public enum ValidationStatus {
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ class ArrayListBindingResults implements BindingResults {
|
|||
public List<String> properties() {
|
||||
List<String> properties = new ArrayList<String>(results.size());
|
||||
for (BindingResult result : this) {
|
||||
properties.add(result.getProperty());
|
||||
properties.add(result.getFieldName());
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ package org.springframework.ui.binding.binder;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ui.binding.Binding;
|
||||
import org.springframework.ui.binding.FieldModel;
|
||||
|
||||
/**
|
||||
* Binds user-entered values to properties of a model object.
|
||||
|
|
@ -28,18 +28,13 @@ import org.springframework.ui.binding.Binding;
|
|||
*/
|
||||
public interface Binder {
|
||||
|
||||
/**
|
||||
* The model this binder binds to.
|
||||
*/
|
||||
public Object getModel();
|
||||
|
||||
/**
|
||||
* Bind the source values to the properties of the model.
|
||||
* A result is returned for each registered {@link Binding}.
|
||||
* @param sourceValues the source values to bind
|
||||
* A result is returned for each registered {@link FieldModel}.
|
||||
* @param fieldValues the field values to bind
|
||||
* @return the results of the binding operation
|
||||
* @throws MissingSourceValuesException when the sourceValues Map is missing entries for required bindings
|
||||
* @throws MissingFieldException when the fieldValues Map is missing entries for required bindings
|
||||
*/
|
||||
BindingResults bind(Map<String, ? extends Object> sourceValues);
|
||||
BindingResults bind(Map<String, ? extends Object> fieldValues);
|
||||
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package org.springframework.ui.binding.binder;
|
||||
|
||||
import org.springframework.ui.binding.config.BindingRuleConfiguration;
|
||||
import org.springframework.ui.binding.config.FieldModelConfiguration;
|
||||
|
||||
/**
|
||||
* A SPI interface that lets you configure a model binder, then execute it.
|
||||
|
|
@ -37,7 +37,7 @@ public interface BinderExecutor<M> {
|
|||
* @param property the model property
|
||||
* @return a builder API for configuring the rule
|
||||
*/
|
||||
BindingRuleConfiguration bindingRule(String property);
|
||||
FieldModelConfiguration bindingRule(String property);
|
||||
|
||||
// TODO allow injection of pre-created BindingRules
|
||||
|
||||
|
|
|
|||
|
|
@ -26,17 +26,17 @@ import org.springframework.ui.alert.Alert;
|
|||
public interface BindingResult {
|
||||
|
||||
/**
|
||||
* The model property this binding result is for.
|
||||
* @see Binder#getNestedBinding(String)
|
||||
* The name of the field this binding result is for.
|
||||
* @see Binder#getNested(String)
|
||||
*/
|
||||
String getProperty();
|
||||
String getFieldName();
|
||||
|
||||
/**
|
||||
* The raw source value for which binding was attempted.
|
||||
* The raw submitted value for which binding was attempted.
|
||||
* If not a failure, this value was successfully bound to the model.
|
||||
* @see #isFailure()
|
||||
*/
|
||||
Object getSourceValue();
|
||||
Object getSubmittedValue();
|
||||
|
||||
/**
|
||||
* Indicates if the binding failed.
|
||||
|
|
|
|||
|
|
@ -20,23 +20,23 @@ import org.springframework.ui.alert.Severity;
|
|||
|
||||
class BindingStatusResult implements BindingResult {
|
||||
|
||||
private String property;
|
||||
private String fieldName;
|
||||
|
||||
private Object sourceValue;
|
||||
|
||||
private Alert bindingStatusAlert;
|
||||
|
||||
public BindingStatusResult(String property, Object sourceValue, Alert alert) {
|
||||
this.property = property;
|
||||
public BindingStatusResult(String fieldName, Object sourceValue, Alert alert) {
|
||||
this.fieldName = fieldName;
|
||||
this.sourceValue = sourceValue;
|
||||
this.bindingStatusAlert = alert;
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
return property;
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
public Object getSourceValue() {
|
||||
public Object getSubmittedValue() {
|
||||
return sourceValue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import org.springframework.ui.alert.Severity;
|
|||
import org.springframework.ui.message.MessageBuilder;
|
||||
import org.springframework.ui.message.ResolvableArgument;
|
||||
|
||||
class PropertyNotEditableResult implements BindingResult {
|
||||
class FieldNotEditableResult implements BindingResult {
|
||||
|
||||
private String property;
|
||||
|
||||
|
|
@ -30,17 +30,17 @@ class PropertyNotEditableResult implements BindingResult {
|
|||
|
||||
private MessageSource messageSource;
|
||||
|
||||
public PropertyNotEditableResult(String property, Object sourceValue, MessageSource messageSource) {
|
||||
public FieldNotEditableResult(String property, Object sourceValue, MessageSource messageSource) {
|
||||
this.property = property;
|
||||
this.sourceValue = sourceValue;
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
public String getFieldName() {
|
||||
return property;
|
||||
}
|
||||
|
||||
public Object getSourceValue() {
|
||||
public Object getSubmittedValue() {
|
||||
return sourceValue;
|
||||
}
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ import org.springframework.ui.alert.Severity;
|
|||
import org.springframework.ui.message.MessageBuilder;
|
||||
import org.springframework.ui.message.ResolvableArgument;
|
||||
|
||||
class PropertyNotFoundResult implements BindingResult {
|
||||
class FieldNotFoundResult implements BindingResult {
|
||||
|
||||
private String property;
|
||||
|
||||
|
|
@ -30,17 +30,17 @@ class PropertyNotFoundResult implements BindingResult {
|
|||
|
||||
private MessageSource messageSource;
|
||||
|
||||
public PropertyNotFoundResult(String property, Object sourceValue, MessageSource messageSource) {
|
||||
public FieldNotFoundResult(String property, Object sourceValue, MessageSource messageSource) {
|
||||
this.property = property;
|
||||
this.sourceValue = sourceValue;
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
public String getFieldName() {
|
||||
return property;
|
||||
}
|
||||
|
||||
public Object getSourceValue() {
|
||||
public Object getSubmittedValue() {
|
||||
return sourceValue;
|
||||
}
|
||||
|
||||
|
|
@ -21,10 +21,10 @@ import java.util.Map;
|
|||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.core.convert.TypeConverter;
|
||||
import org.springframework.ui.binding.Binding;
|
||||
import org.springframework.ui.binding.BindingFactory;
|
||||
import org.springframework.ui.binding.FieldModel;
|
||||
import org.springframework.ui.binding.FieldNotFoundException;
|
||||
import org.springframework.ui.binding.PresentationModel;
|
||||
import org.springframework.ui.binding.BindingStatus;
|
||||
import org.springframework.ui.binding.support.PropertyNotFoundException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
|
@ -37,15 +37,15 @@ import org.springframework.util.Assert;
|
|||
*/
|
||||
public class GenericBinder implements Binder {
|
||||
|
||||
private BindingFactory bindingFactory;
|
||||
private PresentationModel presentationModel;
|
||||
|
||||
private String[] requiredProperties;
|
||||
private String[] requiredFields;
|
||||
|
||||
private MessageSource messageSource;
|
||||
|
||||
public GenericBinder(BindingFactory bindingFactory) {
|
||||
Assert.notNull(bindingFactory, "The BindingFactory is required");
|
||||
this.bindingFactory = bindingFactory;
|
||||
public GenericBinder(PresentationModel presentationModel) {
|
||||
Assert.notNull(presentationModel, "The PresentationModel is required");
|
||||
this.presentationModel = presentationModel;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -58,34 +58,38 @@ public class GenericBinder implements Binder {
|
|||
}
|
||||
|
||||
/**
|
||||
* Configure the properties for which source values must be present in each bind attempt.
|
||||
* @param propertyPaths the property path expressions
|
||||
* @see MissingSourceValuesException
|
||||
* Configure the fields for which values must be present in each bind attempt.
|
||||
* @param fieldNames the field names
|
||||
* @see MissingFieldException
|
||||
*/
|
||||
public void setRequired(String[] propertyPaths) {
|
||||
this.requiredProperties = propertyPaths;
|
||||
public void setRequiredFields(String[] fieldNames) {
|
||||
this.requiredFields = fieldNames;
|
||||
}
|
||||
|
||||
public Object getModel() {
|
||||
return bindingFactory.getModel();
|
||||
}
|
||||
// subclassing hooks
|
||||
|
||||
protected Binding getBinding(String property) {
|
||||
return bindingFactory.getBinding(property);
|
||||
/**
|
||||
* Get the model for the field.
|
||||
* @param fieldName
|
||||
* @return the field model
|
||||
* @throws NoSuchFieldException if no such field exists
|
||||
*/
|
||||
protected FieldModel getFieldModel(String fieldName) {
|
||||
return presentationModel.getFieldModel(fieldName);
|
||||
}
|
||||
|
||||
// implementing Binder
|
||||
|
||||
public BindingResults bind(Map<String, ? extends Object> sourceValues) {
|
||||
sourceValues = filter(sourceValues);
|
||||
checkRequired(sourceValues);
|
||||
ArrayListBindingResults results = new ArrayListBindingResults(sourceValues.size());
|
||||
for (Map.Entry<String, ? extends Object> sourceValue : sourceValues.entrySet()) {
|
||||
public BindingResults bind(Map<String, ? extends Object> fieldValues) {
|
||||
fieldValues = filter(fieldValues);
|
||||
checkRequired(fieldValues);
|
||||
ArrayListBindingResults results = new ArrayListBindingResults(fieldValues.size());
|
||||
for (Map.Entry<String, ? extends Object> fieldValue : fieldValues.entrySet()) {
|
||||
try {
|
||||
Binding binding = getBinding(sourceValue.getKey());
|
||||
results.add(bind(sourceValue, binding));
|
||||
} catch (PropertyNotFoundException e) {
|
||||
results.add(new PropertyNotFoundResult(sourceValue.getKey(), sourceValue.getValue(), messageSource));
|
||||
FieldModel field = getFieldModel(fieldValue.getKey());
|
||||
results.add(bind(fieldValue, field));
|
||||
} catch (FieldNotFoundException e) {
|
||||
results.add(new FieldNotFoundResult(fieldValue.getKey(), fieldValue.getValue(), messageSource));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
|
|
@ -95,26 +99,26 @@ public class GenericBinder implements Binder {
|
|||
|
||||
/**
|
||||
* Hook subclasses may use to filter the source values to bind.
|
||||
* This hook allows the binder to pre-process the source values before binding occurs.
|
||||
* This hook allows the binder to pre-process the field values before binding occurs.
|
||||
* For example, a Binder might insert empty or default values for fields that are not present.
|
||||
* As another example, a Binder might collapse multiple source values into a single source value.
|
||||
* @param sourceValues the original source values map provided by the caller
|
||||
* @return the filtered source values map that will be used to bind
|
||||
* @param fieldValues the original fieldValues map provided by the caller
|
||||
* @return the filtered fieldValues map that will be used to bind
|
||||
*/
|
||||
protected Map<String, ? extends Object> filter(Map<String, ? extends Object> sourceValues) {
|
||||
return sourceValues;
|
||||
protected Map<String, ? extends Object> filter(Map<String, ? extends Object> fieldValues) {
|
||||
return fieldValues;
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private void checkRequired(Map<String, ? extends Object> sourceValues) {
|
||||
if (requiredProperties == null) {
|
||||
private void checkRequired(Map<String, ? extends Object> fieldValues) {
|
||||
if (requiredFields == null) {
|
||||
return;
|
||||
}
|
||||
List<String> missingRequired = new ArrayList<String>();
|
||||
for (String required : requiredProperties) {
|
||||
for (String required : requiredFields) {
|
||||
boolean found = false;
|
||||
for (String property : sourceValues.keySet()) {
|
||||
for (String property : fieldValues.keySet()) {
|
||||
if (property.equals(required)) {
|
||||
found = true;
|
||||
}
|
||||
|
|
@ -124,21 +128,21 @@ public class GenericBinder implements Binder {
|
|||
}
|
||||
}
|
||||
if (!missingRequired.isEmpty()) {
|
||||
throw new MissingSourceValuesException(missingRequired, sourceValues);
|
||||
throw new MissingFieldException(missingRequired, fieldValues);
|
||||
}
|
||||
}
|
||||
|
||||
private BindingResult bind(Map.Entry<String, ? extends Object> sourceValue, Binding binding) {
|
||||
String property = sourceValue.getKey();
|
||||
Object value = sourceValue.getValue();
|
||||
if (!binding.isEditable()) {
|
||||
return new PropertyNotEditableResult(property, value, messageSource);
|
||||
private BindingResult bind(Map.Entry<String, ? extends Object> fieldValue, FieldModel field) {
|
||||
String fieldName = fieldValue.getKey();
|
||||
Object value = fieldValue.getValue();
|
||||
if (!field.isEditable()) {
|
||||
return new FieldNotEditableResult(fieldName, value, messageSource);
|
||||
} else {
|
||||
binding.applySourceValue(value);
|
||||
if (binding.getBindingStatus() == BindingStatus.DIRTY) {
|
||||
binding.commit();
|
||||
field.applySubmittedValue(value);
|
||||
if (field.getBindingStatus() == BindingStatus.DIRTY) {
|
||||
field.commit();
|
||||
}
|
||||
return new BindingStatusResult(property, value, binding.getStatusAlert());
|
||||
return new BindingStatusResult(fieldName, value, field.getStatusAlert());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import java.util.Map;
|
|||
* @see Binder#bind(java.util.Map)
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class MissingSourceValuesException extends RuntimeException {
|
||||
public class MissingFieldException extends RuntimeException {
|
||||
|
||||
private List<String> missing;
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ public class MissingSourceValuesException extends RuntimeException {
|
|||
* @param missing
|
||||
* @param sourceValues
|
||||
*/
|
||||
public MissingSourceValuesException(List<String> missing, Map<String, ? extends Object> sourceValues) {
|
||||
public MissingFieldException(List<String> missing, Map<String, ? extends Object> sourceValues) {
|
||||
super(getMessage(missing, sourceValues));
|
||||
this.missing = missing;
|
||||
}
|
||||
|
|
@ -49,9 +49,9 @@ public class MissingSourceValuesException extends RuntimeException {
|
|||
|
||||
private static String getMessage(List<String> missingRequired, Map<String, ? extends Object> sourceValues) {
|
||||
if (missingRequired.size() == 1) {
|
||||
return "Missing a source value for required propertyPath [" + missingRequired.get(0) + "]; sourceValues map contained " + sourceValues.keySet();
|
||||
return "Missing a field [" + missingRequired.get(0) + "]; fieldValues map contained " + sourceValues.keySet();
|
||||
} else {
|
||||
return "Missing source values to required propertyPaths " + missingRequired + "; sourceValues map contained " + sourceValues.keySet();
|
||||
return "Missing fields " + missingRequired + "; fieldValues map contained " + sourceValues.keySet();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -18,8 +18,8 @@ package org.springframework.ui.binding.binder;
|
|||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ui.binding.Binding;
|
||||
import org.springframework.ui.binding.BindingFactory;
|
||||
import org.springframework.ui.binding.FieldModel;
|
||||
import org.springframework.ui.binding.PresentationModel;
|
||||
|
||||
/**
|
||||
* A binder designed for use in HTTP (web) environments.
|
||||
|
|
@ -39,13 +39,12 @@ public class WebBinder extends GenericBinder {
|
|||
* Creates a new web binder for the model object.
|
||||
* @param model the model object containing properties this binder will bind to
|
||||
*/
|
||||
public WebBinder(BindingFactory bindingFactory) {
|
||||
public WebBinder(PresentationModel bindingFactory) {
|
||||
super(bindingFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the prefix to detect the default user value for a property when no value was submitted.
|
||||
* This is used to configure a <i>default</i> {@link UserValue} for binding when no value is submitted by the client.
|
||||
* Configure the prefix used to detect the default value for a field when no value is submitted.
|
||||
* Default is '!'.
|
||||
*/
|
||||
public void setDefaultPrefix(String defaultPrefix) {
|
||||
|
|
@ -53,8 +52,8 @@ public class WebBinder extends GenericBinder {
|
|||
}
|
||||
|
||||
/**
|
||||
* Configure the prefix to detect the presence of a property on the web UI when no user value for the property was actually submitted.
|
||||
* This is used to configure a <i>empty</i> {@link UserValue} for binding when no other {@link #setDefaultPrefix(String) default value} is specified by the client.
|
||||
* Configure the prefix used to detect the presence of a field on the web UI when no value was actually submitted.
|
||||
* This is used to configure a <i>empty</i> field when no other {@link #setDefaultPrefix(String) default value} is specified by the client.
|
||||
* Default is '_'.
|
||||
*/
|
||||
public void setPresentPrefix(String presentPrefix) {
|
||||
|
|
@ -62,20 +61,20 @@ public class WebBinder extends GenericBinder {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, ? extends Object> filter(Map<String, ? extends Object> sourceValues) {
|
||||
protected Map<String, ? extends Object> filter(Map<String, ? extends Object> fieldValues) {
|
||||
LinkedHashMap<String, Object> filteredValues = new LinkedHashMap<String, Object>();
|
||||
for (Map.Entry<String, ? extends Object> entry : sourceValues.entrySet()) {
|
||||
for (Map.Entry<String, ? extends Object> entry : fieldValues.entrySet()) {
|
||||
String field = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (field.startsWith(defaultPrefix)) {
|
||||
field = field.substring(defaultPrefix.length());
|
||||
if (!sourceValues.containsKey(field)) {
|
||||
if (!fieldValues.containsKey(field)) {
|
||||
filteredValues.put(field, value);
|
||||
}
|
||||
} else if (field.startsWith(presentPrefix)) {
|
||||
field = field.substring(presentPrefix.length());
|
||||
if (!sourceValues.containsKey(field) && !sourceValues.containsKey(defaultPrefix + field)) {
|
||||
value = getEmptyValue(getBinding(field));
|
||||
if (!fieldValues.containsKey(field) && !fieldValues.containsKey(defaultPrefix + field)) {
|
||||
value = getEmptyValue(getFieldModel(field));
|
||||
filteredValues.put(field, value);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -85,7 +84,7 @@ public class WebBinder extends GenericBinder {
|
|||
return filteredValues;
|
||||
}
|
||||
|
||||
protected Object getEmptyValue(Binding binding) {
|
||||
protected Object getEmptyValue(FieldModel binding) {
|
||||
Class<?> type = binding.getValueType();
|
||||
if (boolean.class.equals(type) || Boolean.class.equals(type)) {
|
||||
return Boolean.FALSE;
|
||||
|
|
|
|||
|
|
@ -21,35 +21,35 @@ import org.springframework.ui.format.Formatter;
|
|||
* A fluent interface for configuring a newly added binding rule.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public interface BindingRuleConfiguration {
|
||||
public interface FieldModelConfiguration {
|
||||
|
||||
/**
|
||||
* Set the Formatter to use to format bound property values.
|
||||
*/
|
||||
BindingRuleConfiguration formatWith(Formatter<?> formatter);
|
||||
FieldModelConfiguration formatWith(Formatter<?> formatter);
|
||||
|
||||
/**
|
||||
* If a map property, set the Formatter to use to format map keys.
|
||||
*/
|
||||
BindingRuleConfiguration formatKeysWith(Formatter<?> formatter);
|
||||
FieldModelConfiguration formatKeysWith(Formatter<?> formatter);
|
||||
|
||||
/**
|
||||
* If an list or map property, set the Formatter to use to format indexed elements.
|
||||
*/
|
||||
BindingRuleConfiguration formatElementsWith(Formatter<?> formatter);
|
||||
FieldModelConfiguration formatElementsWith(Formatter<?> formatter);
|
||||
|
||||
/**
|
||||
* Set when the binding is editable.
|
||||
*/
|
||||
BindingRuleConfiguration editableWhen(Condition condition);
|
||||
FieldModelConfiguration editableWhen(Condition condition);
|
||||
|
||||
/**
|
||||
* Set when the binding is enabled.
|
||||
*/
|
||||
BindingRuleConfiguration enabledWhen(Condition condition);
|
||||
FieldModelConfiguration enabledWhen(Condition condition);
|
||||
|
||||
/**
|
||||
* Set when the binding is visible.
|
||||
*/
|
||||
BindingRuleConfiguration visibleWhen(Condition condition);
|
||||
FieldModelConfiguration visibleWhen(Condition condition);
|
||||
}
|
||||
|
|
@ -15,29 +15,26 @@
|
|||
*/
|
||||
package org.springframework.ui.binding.support;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
public class CollectionTypeDescriptor {
|
||||
|
||||
private Class<?> collectionType;
|
||||
private Class<?> type;
|
||||
|
||||
private Class<?> elementType;
|
||||
|
||||
/**
|
||||
* Creates a new generic collection property type
|
||||
* @param collectionType the collection type
|
||||
* @param elementType the element type
|
||||
*/
|
||||
public CollectionTypeDescriptor(Class<?> collectionType, Class<?> elementType) {
|
||||
this.collectionType = collectionType;
|
||||
public CollectionTypeDescriptor(Class<?> type, Class<?> elementType) {
|
||||
Assert.notNull(type, "The collection type is required");
|
||||
this.type = type;
|
||||
this.elementType = elementType;
|
||||
}
|
||||
|
||||
/**
|
||||
* The collection type.
|
||||
*/
|
||||
public Class<?> getCollectionType() {
|
||||
return collectionType;
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -52,11 +49,11 @@ public class CollectionTypeDescriptor {
|
|||
return false;
|
||||
}
|
||||
CollectionTypeDescriptor type = (CollectionTypeDescriptor) o;
|
||||
return collectionType.equals(type.collectionType)
|
||||
return type.equals(type.type)
|
||||
&& ObjectUtils.nullSafeEquals(elementType, type.elementType);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return collectionType.hashCode() + elementType.hashCode();
|
||||
return type.hashCode() + elementType.hashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -34,38 +34,38 @@ import org.springframework.core.convert.TypeDescriptor;
|
|||
import org.springframework.core.style.StylerUtils;
|
||||
import org.springframework.ui.alert.Alert;
|
||||
import org.springframework.ui.alert.Severity;
|
||||
import org.springframework.ui.binding.Binding;
|
||||
import org.springframework.ui.binding.BindingStatus;
|
||||
import org.springframework.ui.binding.FieldModel;
|
||||
import org.springframework.ui.binding.ValidationStatus;
|
||||
import org.springframework.ui.format.Formatter;
|
||||
import org.springframework.ui.message.MessageBuilder;
|
||||
import org.springframework.ui.message.ResolvableArgument;
|
||||
|
||||
public class GenericBinding implements Binding {
|
||||
public class DefaultFieldModel implements FieldModel {
|
||||
|
||||
private ValueModel valueModel;
|
||||
|
||||
private BindingContext bindingContext;
|
||||
private FieldModelContext context;
|
||||
|
||||
private ValueBuffer buffer;
|
||||
|
||||
private BindingStatus bindingStatus;
|
||||
|
||||
private Object sourceValue;
|
||||
private Object submittedValue;
|
||||
|
||||
private Exception invalidSourceValueCause;
|
||||
private Exception invalidSubmittedValueCause;
|
||||
|
||||
public GenericBinding(ValueModel valueModel, BindingContext bindingContext) {
|
||||
public DefaultFieldModel(ValueModel valueModel, FieldModelContext context) {
|
||||
this.valueModel = valueModel;
|
||||
this.bindingContext = bindingContext;
|
||||
this.context = context;
|
||||
buffer = new ValueBuffer(valueModel);
|
||||
bindingStatus = BindingStatus.CLEAN;
|
||||
}
|
||||
|
||||
// implementing Binding
|
||||
// implementing FieldModel
|
||||
|
||||
public String getRenderValue() {
|
||||
return format(getValue(), bindingContext.getFormatter());
|
||||
return format(getValue(), context.getFormatter());
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
|
|
@ -81,42 +81,42 @@ public class GenericBinding implements Binding {
|
|||
}
|
||||
|
||||
public boolean isEditable() {
|
||||
return valueModel.isWriteable() && bindingContext.getEditableCondition().isTrue();
|
||||
return valueModel.isWriteable() && context.getEditableCondition().isTrue();
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return bindingContext.getEnabledCondition().isTrue();
|
||||
return context.getEnabledCondition().isTrue();
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
return bindingContext.getVisibleCondition().isTrue();
|
||||
return context.getVisibleCondition().isTrue();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void applySourceValue(Object sourceValue) {
|
||||
public void applySubmittedValue(Object submittedValue) {
|
||||
assertEditable();
|
||||
assertEnabled();
|
||||
if (sourceValue instanceof String) {
|
||||
if (submittedValue instanceof String) {
|
||||
try {
|
||||
Object parsed = bindingContext.getFormatter().parse((String) sourceValue, getLocale());
|
||||
Object parsed = context.getFormatter().parse((String) submittedValue, getLocale());
|
||||
buffer.setValue(coerseToValueType(parsed));
|
||||
sourceValue = null;
|
||||
submittedValue = null;
|
||||
bindingStatus = BindingStatus.DIRTY;
|
||||
} catch (ParseException e) {
|
||||
this.sourceValue = sourceValue;
|
||||
invalidSourceValueCause = e;
|
||||
bindingStatus = BindingStatus.INVALID_SOURCE_VALUE;
|
||||
this.submittedValue = submittedValue;
|
||||
invalidSubmittedValueCause = e;
|
||||
bindingStatus = BindingStatus.INVALID_SUBMITTED_VALUE;
|
||||
} catch (ConversionFailedException e) {
|
||||
this.sourceValue = sourceValue;
|
||||
invalidSourceValueCause = e;
|
||||
bindingStatus = BindingStatus.INVALID_SOURCE_VALUE;
|
||||
this.submittedValue = submittedValue;
|
||||
invalidSubmittedValueCause = e;
|
||||
bindingStatus = BindingStatus.INVALID_SUBMITTED_VALUE;
|
||||
}
|
||||
} else if (sourceValue instanceof String[]) {
|
||||
} else if (submittedValue instanceof String[]) {
|
||||
Object parsed;
|
||||
if (isMap()) {
|
||||
String[] sourceValues = (String[]) sourceValue;
|
||||
Formatter keyFormatter = bindingContext.getKeyFormatter();
|
||||
Formatter valueFormatter = bindingContext.getElementFormatter();
|
||||
String[] sourceValues = (String[]) submittedValue;
|
||||
Formatter keyFormatter = context.getKeyFormatter();
|
||||
Formatter valueFormatter = context.getElementFormatter();
|
||||
Map map = new LinkedHashMap(sourceValues.length);
|
||||
for (int i = 0; i < sourceValues.length; i++) {
|
||||
String entryString = sourceValues[i];
|
||||
|
|
@ -126,59 +126,59 @@ public class GenericBinding implements Binding {
|
|||
Object parsedMapValue = valueFormatter.parse(keyValue[1], getLocale());
|
||||
map.put(parsedMapKey, parsedMapValue);
|
||||
} catch (ParseException e) {
|
||||
this.sourceValue = sourceValue;
|
||||
invalidSourceValueCause = e;
|
||||
bindingStatus = BindingStatus.INVALID_SOURCE_VALUE;
|
||||
this.submittedValue = submittedValue;
|
||||
invalidSubmittedValueCause = e;
|
||||
bindingStatus = BindingStatus.INVALID_SUBMITTED_VALUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
parsed = map;
|
||||
} else {
|
||||
String[] sourceValues = (String[]) sourceValue;
|
||||
String[] sourceValues = (String[]) submittedValue;
|
||||
List list = new ArrayList(sourceValues.length);
|
||||
for (int i = 0; i < sourceValues.length; i++) {
|
||||
Object parsedValue;
|
||||
try {
|
||||
parsedValue = bindingContext.getElementFormatter().parse(sourceValues[i], getLocale());
|
||||
parsedValue = context.getElementFormatter().parse(sourceValues[i], getLocale());
|
||||
list.add(parsedValue);
|
||||
} catch (ParseException e) {
|
||||
this.sourceValue = sourceValue;
|
||||
invalidSourceValueCause = e;
|
||||
bindingStatus = BindingStatus.INVALID_SOURCE_VALUE;
|
||||
this.submittedValue = submittedValue;
|
||||
invalidSubmittedValueCause = e;
|
||||
bindingStatus = BindingStatus.INVALID_SUBMITTED_VALUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
parsed = list;
|
||||
}
|
||||
if (bindingStatus != BindingStatus.INVALID_SOURCE_VALUE) {
|
||||
if (bindingStatus != BindingStatus.INVALID_SUBMITTED_VALUE) {
|
||||
try {
|
||||
buffer.setValue(coerseToValueType(parsed));
|
||||
sourceValue = null;
|
||||
submittedValue = null;
|
||||
bindingStatus = BindingStatus.DIRTY;
|
||||
} catch (ConversionFailedException e) {
|
||||
this.sourceValue = sourceValue;
|
||||
invalidSourceValueCause = e;
|
||||
bindingStatus = BindingStatus.INVALID_SOURCE_VALUE;
|
||||
this.submittedValue = submittedValue;
|
||||
invalidSubmittedValueCause = e;
|
||||
bindingStatus = BindingStatus.INVALID_SUBMITTED_VALUE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
buffer.setValue(coerseToValueType(sourceValue));
|
||||
sourceValue = null;
|
||||
buffer.setValue(coerseToValueType(submittedValue));
|
||||
submittedValue = null;
|
||||
bindingStatus = BindingStatus.DIRTY;
|
||||
} catch (ConversionFailedException e) {
|
||||
this.sourceValue = sourceValue;
|
||||
invalidSourceValueCause = e;
|
||||
bindingStatus = BindingStatus.INVALID_SOURCE_VALUE;
|
||||
this.submittedValue = submittedValue;
|
||||
invalidSubmittedValueCause = e;
|
||||
bindingStatus = BindingStatus.INVALID_SUBMITTED_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Object getInvalidSourceValue() {
|
||||
if (bindingStatus != BindingStatus.INVALID_SOURCE_VALUE) {
|
||||
throw new IllegalStateException("No invalid source value");
|
||||
public Object getInvalidSubmittedValue() {
|
||||
if (bindingStatus != BindingStatus.INVALID_SUBMITTED_VALUE) {
|
||||
throw new IllegalStateException("No invalid submitted value applied to this field");
|
||||
}
|
||||
return sourceValue;
|
||||
return submittedValue;
|
||||
}
|
||||
|
||||
public BindingStatus getBindingStatus() {
|
||||
|
|
@ -186,32 +186,33 @@ public class GenericBinding implements Binding {
|
|||
}
|
||||
|
||||
public ValidationStatus getValidationStatus() {
|
||||
// TODO implementation
|
||||
return ValidationStatus.NOT_VALIDATED;
|
||||
}
|
||||
|
||||
public Alert getStatusAlert() {
|
||||
if (bindingStatus == BindingStatus.INVALID_SOURCE_VALUE) {
|
||||
if (bindingStatus == BindingStatus.INVALID_SUBMITTED_VALUE) {
|
||||
return new AbstractAlert() {
|
||||
public String getCode() {
|
||||
return "typeMismatch";
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
MessageBuilder builder = new MessageBuilder(bindingContext.getMessageSource());
|
||||
MessageBuilder builder = new MessageBuilder(context.getMessageSource());
|
||||
builder.code(getCode());
|
||||
if (invalidSourceValueCause instanceof ParseException) {
|
||||
ParseException e = (ParseException) invalidSourceValueCause;
|
||||
builder.arg("label", bindingContext.getLabel());
|
||||
builder.arg("value", sourceValue);
|
||||
if (invalidSubmittedValueCause instanceof ParseException) {
|
||||
ParseException e = (ParseException) invalidSubmittedValueCause;
|
||||
builder.arg("label", context.getLabel());
|
||||
builder.arg("value", submittedValue);
|
||||
builder.arg("errorOffset", e.getErrorOffset());
|
||||
builder.defaultMessage("Failed to bind '" + bindingContext.getLabel() + "'; the source value "
|
||||
+ StylerUtils.style(sourceValue) + " has an invalid format and could no be parsed");
|
||||
builder.defaultMessage("Failed to bind '" + context.getLabel() + "'; the submitted value "
|
||||
+ StylerUtils.style(submittedValue) + " has an invalid format and could no be parsed");
|
||||
} else {
|
||||
ConversionFailedException e = (ConversionFailedException) invalidSourceValueCause;
|
||||
builder.arg("label", new ResolvableArgument(bindingContext.getLabel()));
|
||||
builder.arg("value", sourceValue);
|
||||
builder.defaultMessage("Failed to bind '" + bindingContext.getLabel() + "'; the source value "
|
||||
+ StylerUtils.style(sourceValue) + " has could not be converted to "
|
||||
ConversionFailedException e = (ConversionFailedException) invalidSubmittedValueCause;
|
||||
builder.arg("label", new ResolvableArgument(context.getLabel()));
|
||||
builder.arg("value", submittedValue);
|
||||
builder.defaultMessage("Failed to bind '" + context.getLabel() + "'; the submitted value "
|
||||
+ StylerUtils.style(submittedValue) + " has could not be converted to "
|
||||
+ e.getTargetType().getName());
|
||||
|
||||
}
|
||||
|
|
@ -257,6 +258,7 @@ public class GenericBinding implements Binding {
|
|||
}
|
||||
|
||||
public void validate() {
|
||||
// TODO implementation
|
||||
}
|
||||
|
||||
public void commit() {
|
||||
|
|
@ -270,57 +272,57 @@ public class GenericBinding implements Binding {
|
|||
bindingStatus = BindingStatus.COMMITTED;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Binding is not dirty; nothing to commit");
|
||||
throw new IllegalStateException("Field is not dirty; nothing to commit");
|
||||
}
|
||||
}
|
||||
|
||||
public void revert() {
|
||||
if (bindingStatus == BindingStatus.INVALID_SOURCE_VALUE) {
|
||||
sourceValue = null;
|
||||
invalidSourceValueCause = null;
|
||||
if (bindingStatus == BindingStatus.INVALID_SUBMITTED_VALUE) {
|
||||
submittedValue = null;
|
||||
invalidSubmittedValueCause = null;
|
||||
bindingStatus = BindingStatus.CLEAN;
|
||||
} else if (bindingStatus == BindingStatus.DIRTY || bindingStatus == BindingStatus.COMMIT_FAILURE) {
|
||||
buffer.clear();
|
||||
bindingStatus = BindingStatus.CLEAN;
|
||||
} else {
|
||||
throw new IllegalStateException("Nothing to revert");
|
||||
throw new IllegalStateException("Field is clean or committed; nothing to revert");
|
||||
}
|
||||
}
|
||||
|
||||
public Binding getNestedBinding(String property) {
|
||||
return bindingContext.getNestedBinding(property);
|
||||
public FieldModel getNested(String fieldName) {
|
||||
return context.getNested(fieldName);
|
||||
}
|
||||
|
||||
public boolean isList() {
|
||||
return List.class.isAssignableFrom(getValueType());
|
||||
return getValueType().isArray() || List.class.isAssignableFrom(getValueType());
|
||||
}
|
||||
|
||||
public Binding getListElementBinding(int index) {
|
||||
return bindingContext.getListElementBinding(index);
|
||||
public FieldModel getListElement(int index) {
|
||||
return context.getListElement(index);
|
||||
}
|
||||
|
||||
public boolean isMap() {
|
||||
return Map.class.isAssignableFrom(getValueType());
|
||||
}
|
||||
|
||||
public Binding getMapValueBinding(Object key) {
|
||||
public FieldModel getMapValue(Object key) {
|
||||
if (key instanceof String) {
|
||||
try {
|
||||
key = bindingContext.getKeyFormatter().parse((String) key, getLocale());
|
||||
key = context.getKeyFormatter().parse((String) key, getLocale());
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalArgumentException("Unable to parse map key '" + key + "'", e);
|
||||
}
|
||||
}
|
||||
return bindingContext.getMapValueBinding(key);
|
||||
return context.getMapValue(key);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public String formatValue(Object value) {
|
||||
Formatter formatter;
|
||||
if (Collection.class.isAssignableFrom(getValueType()) || getValueType().isArray() || isMap()) {
|
||||
formatter = bindingContext.getElementFormatter();
|
||||
formatter = context.getElementFormatter();
|
||||
} else {
|
||||
formatter = bindingContext.getFormatter();
|
||||
formatter = context.getFormatter();
|
||||
}
|
||||
return format(value, formatter);
|
||||
}
|
||||
|
|
@ -330,7 +332,7 @@ public class GenericBinding implements Binding {
|
|||
@SuppressWarnings("unchecked")
|
||||
private String format(Object value, Formatter formatter) {
|
||||
Class<?> formattedType = getFormattedObjectType(formatter.getClass());
|
||||
value = bindingContext.getTypeConverter().convert(value, formattedType);
|
||||
value = context.getTypeConverter().convert(value, formattedType);
|
||||
return formatter.format(value, getLocale());
|
||||
}
|
||||
|
||||
|
|
@ -370,7 +372,7 @@ public class GenericBinding implements Binding {
|
|||
@SuppressWarnings("unchecked")
|
||||
private Object coerseToValueType(Object parsed) {
|
||||
TypeDescriptor targetType = valueModel.getValueTypeDescriptor();
|
||||
TypeConverter converter = bindingContext.getTypeConverter();
|
||||
TypeConverter converter = context.getTypeConverter();
|
||||
if (parsed != null && converter.canConvert(parsed.getClass(), targetType)) {
|
||||
return converter.convert(parsed, targetType);
|
||||
} else {
|
||||
|
|
@ -380,13 +382,13 @@ public class GenericBinding implements Binding {
|
|||
|
||||
private void assertEditable() {
|
||||
if (!isEditable()) {
|
||||
throw new IllegalStateException("Binding is not editable");
|
||||
throw new IllegalStateException("Field is not editable");
|
||||
}
|
||||
}
|
||||
|
||||
private void assertEnabled() {
|
||||
if (!isEditable()) {
|
||||
throw new IllegalStateException("Binding is not enabled");
|
||||
throw new IllegalStateException("Field is not enabled");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -29,11 +29,11 @@ import org.springframework.context.MessageSource;
|
|||
import org.springframework.core.GenericCollectionTypeResolver;
|
||||
import org.springframework.core.convert.TypeConverter;
|
||||
import org.springframework.core.convert.support.DefaultTypeConverter;
|
||||
import org.springframework.ui.binding.Binding;
|
||||
import org.springframework.ui.binding.BindingFactory;
|
||||
import org.springframework.ui.binding.FieldModel;
|
||||
import org.springframework.ui.binding.FieldNotFoundException;
|
||||
import org.springframework.ui.binding.PresentationModel;
|
||||
import org.springframework.ui.binding.binder.Binder;
|
||||
import org.springframework.ui.binding.binder.BindingResult;
|
||||
import org.springframework.ui.binding.config.BindingRuleConfiguration;
|
||||
import org.springframework.ui.binding.config.FieldModelConfiguration;
|
||||
import org.springframework.ui.binding.config.Condition;
|
||||
import org.springframework.ui.format.Formatter;
|
||||
import org.springframework.util.Assert;
|
||||
|
|
@ -42,15 +42,16 @@ import org.springframework.util.Assert;
|
|||
* A generic {@link Binder binder} suitable for use in most environments.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
* @see #setFormatterRegistry(FormatterRegistry)
|
||||
* @see #setMessageSource(MessageSource)
|
||||
* @see #setTypeConverter(TypeConverter)
|
||||
* @see #bind(Map)
|
||||
*/
|
||||
public class GenericBindingFactory implements BindingFactory {
|
||||
public class DefaultPresentationModel implements PresentationModel {
|
||||
|
||||
private Object model;
|
||||
private Object domainModel;
|
||||
|
||||
private Map<String, GenericBindingRule> bindingRules;
|
||||
private Map<String, PropertyFieldModelRule> fieldModelRules;
|
||||
|
||||
private FormatterRegistry formatterRegistry;
|
||||
|
||||
|
|
@ -59,19 +60,19 @@ public class GenericBindingFactory implements BindingFactory {
|
|||
private MessageSource messageSource;
|
||||
|
||||
/**
|
||||
* Creates a new binder for the model object.
|
||||
* @param model the model object containing properties this binder will bind to
|
||||
* Creates a new presentation model for the domain model.
|
||||
* @param domainModel the domain model object
|
||||
*/
|
||||
public GenericBindingFactory(Object model) {
|
||||
Assert.notNull(model, "The model to bind to is required");
|
||||
this.model = model;
|
||||
bindingRules = new HashMap<String, GenericBindingRule>();
|
||||
public DefaultPresentationModel(Object domainModel) {
|
||||
Assert.notNull(domainModel, "The domain model to bind to is required");
|
||||
this.domainModel = domainModel;
|
||||
fieldModelRules = new HashMap<String, PropertyFieldModelRule>();
|
||||
formatterRegistry = new GenericFormatterRegistry();
|
||||
typeConverter = new DefaultTypeConverter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the registry of Formatters to query when no explicit Formatter has been registered for a Binding.
|
||||
* Configures the registry of Formatters to query when no explicit Formatter has been registered for a field.
|
||||
* Allows Formatters to be applied by property type and by property annotation.
|
||||
* @param registry the formatter registry
|
||||
*/
|
||||
|
|
@ -81,7 +82,7 @@ public class GenericBindingFactory implements BindingFactory {
|
|||
}
|
||||
|
||||
/**
|
||||
* Configure the MessageSource that resolves localized {@link BindingResult} alert messages.
|
||||
* Configure the MessageSource that resolves localized UI alert messages.
|
||||
* @param messageSource the message source
|
||||
*/
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
|
|
@ -90,10 +91,11 @@ public class GenericBindingFactory implements BindingFactory {
|
|||
}
|
||||
|
||||
/**
|
||||
* Configure the TypeConverter that converts values as required by Binding setValue and getValue attempts.
|
||||
* For a setValue attempt, the TypeConverter will be asked to perform a conversion if the value parsed by the Binding's Formatter is not assignable to the target property type.
|
||||
* For a getValue attempt, the TypeConverter will be asked to perform a conversion if the property type does not match the type T required by the Binding's Formatter.
|
||||
* @param typeConverter the type converter used by the binding system, which is based on Spring EL
|
||||
* Configure the TypeConverter that converts values as required by the binding system.
|
||||
* For a {@link FieldModel#applySubmittedValue(Object) applySubmittedValue call}, this TypeConverter will be asked to perform a conversion if the value parsed by the field's Formatter is not assignable to the target value type.
|
||||
* For a {@link FieldModel#getRenderValue() getRenderValue call}, this TypeConverter will be asked to perform a conversion if the value type does not match the type T required by the field's Formatter.
|
||||
* For a {@link FieldModel#getMapValue(Object) getMapValue call} this TypeConverter will be asked to convert the Map key to the type required if there is no keyFormatter registered for the field.
|
||||
* @param typeConverter the type converter used by the binding system
|
||||
*/
|
||||
public void setTypeConverter(TypeConverter typeConverter) {
|
||||
Assert.notNull(typeConverter, "The TypeConverter is required");
|
||||
|
|
@ -101,57 +103,57 @@ public class GenericBindingFactory implements BindingFactory {
|
|||
}
|
||||
|
||||
/**
|
||||
* Add a new binding rule for the property at the path specified.
|
||||
* Add a a new FieldModelRule for the property at the path specified.
|
||||
* @param propertyPath binding rule property path in format prop.nestedProp
|
||||
* @return a builder for the binding rule
|
||||
*/
|
||||
public BindingRuleConfiguration bindingRule(String propertyPath) {
|
||||
PropertyPath path = new PropertyPath(propertyPath);
|
||||
GenericBindingRule rule = getBindingRule(path.getFirstElement().getValue());
|
||||
for (PropertyPathElement element : path.getNestedElements()) {
|
||||
rule = rule.getBindingRule(element.getValue());
|
||||
public FieldModelConfiguration field(String propertyPath) {
|
||||
FieldPath path = new FieldPath(propertyPath);
|
||||
PropertyFieldModelRule rule = getRule(path.getFirstElement().getValue());
|
||||
for (FieldPathElement element : path.getNestedElements()) {
|
||||
rule = rule.getNestedRule(element.getValue());
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
// implementing Binder
|
||||
|
||||
public Object getModel() {
|
||||
return model;
|
||||
public Object getDomainModel() {
|
||||
return domainModel;
|
||||
}
|
||||
|
||||
public Binding getBinding(String property) {
|
||||
PropertyPath path = new PropertyPath(property);
|
||||
Binding binding = getBindingRule(path.getFirstElement().getValue()).getBinding(model);
|
||||
for (PropertyPathElement element : path.getNestedElements()) {
|
||||
// implementing PresentationModel
|
||||
|
||||
public FieldModel getFieldModel(String fieldName) {
|
||||
FieldPath path = new FieldPath(fieldName);
|
||||
FieldModel field = getRule(path.getFirstElement().getValue()).getFieldModel(domainModel);
|
||||
for (FieldPathElement element : path.getNestedElements()) {
|
||||
if (element.isIndex()) {
|
||||
if (binding.isMap()) {
|
||||
binding = binding.getMapValueBinding(element.getValue());
|
||||
} else if (binding.isList()) {
|
||||
binding = binding.getListElementBinding(element.getIntValue());
|
||||
if (field.isMap()) {
|
||||
field = field.getMapValue(element.getValue());
|
||||
} else if (field.isList()) {
|
||||
field = field.getListElement(element.getIntValue());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Attempted to index a property that is not a List or Map");
|
||||
throw new IllegalArgumentException("Attempted to index a field that is not a List, Array, or a Map");
|
||||
}
|
||||
} else {
|
||||
binding = binding.getNestedBinding(element.getValue());
|
||||
field = field.getNested(element.getValue());
|
||||
}
|
||||
}
|
||||
return binding;
|
||||
return field;
|
||||
}
|
||||
|
||||
private GenericBindingRule getBindingRule(String property) {
|
||||
GenericBindingRule rule = bindingRules.get(property);
|
||||
private PropertyFieldModelRule getRule(String fieldName) {
|
||||
PropertyFieldModelRule rule = fieldModelRules.get(fieldName);
|
||||
if (rule == null) {
|
||||
rule = new GenericBindingRule(property, model.getClass());
|
||||
bindingRules.put(property, rule);
|
||||
rule = new PropertyFieldModelRule(fieldName, domainModel.getClass());
|
||||
fieldModelRules.put(fieldName, rule);
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
class GenericBindingRule implements BindingRuleConfiguration, BindingContext {
|
||||
class PropertyFieldModelRule implements FieldModelConfiguration, FieldModelContext {
|
||||
|
||||
private Class<?> modelClass;
|
||||
private Class<?> domainModelClass;
|
||||
|
||||
private PropertyDescriptor property;
|
||||
|
||||
|
|
@ -167,20 +169,20 @@ public class GenericBindingFactory implements BindingFactory {
|
|||
|
||||
private Condition visibleCondition = Condition.ALWAYS_TRUE;
|
||||
|
||||
private Map<String, GenericBindingRule> nestedBindingRules;
|
||||
private Map<String, PropertyFieldModelRule> nestedFieldModelRules;
|
||||
|
||||
private Binding binding;
|
||||
private FieldModel fieldModel;
|
||||
|
||||
private Map<Integer, Binding> listElementBindings;
|
||||
private Map<Integer, FieldModel> listElements;
|
||||
|
||||
private Map<Object, Binding> mapValueBindings;
|
||||
private Map<Object, FieldModel> mapValues;
|
||||
|
||||
public GenericBindingRule(String property, Class modelClass) {
|
||||
this.modelClass = modelClass;
|
||||
public PropertyFieldModelRule(String property, Class domainModelClass) {
|
||||
this.domainModelClass = domainModelClass;
|
||||
this.property = findPropertyDescriptor(property);
|
||||
}
|
||||
|
||||
// implementing BindingContext
|
||||
// implementing FieldModelContext
|
||||
|
||||
public MessageSource getMessageSource() {
|
||||
return messageSource;
|
||||
|
|
@ -226,84 +228,103 @@ public class GenericBindingFactory implements BindingFactory {
|
|||
return visibleCondition;
|
||||
}
|
||||
|
||||
public Binding getNestedBinding(String property) {
|
||||
createValueIfNecessary();
|
||||
return getBindingRule(property, binding.getValueType()).getBinding(binding.getValue());
|
||||
}
|
||||
|
||||
public Binding getListElementBinding(int index) {
|
||||
if (listElementBindings == null) {
|
||||
listElementBindings = new HashMap<Integer, Binding>();
|
||||
}
|
||||
growListIfNecessary(index);
|
||||
Binding binding = listElementBindings.get(index);
|
||||
if (binding == null) {
|
||||
BindingContext bindingContext = new ListElementBindingContext(index, this);
|
||||
ValueModel valueModel = new ListElementValueModel(index, getElementType(), (List) this.binding.getValue());
|
||||
binding = new GenericBinding(valueModel, bindingContext);
|
||||
listElementBindings.put(index, binding);
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
||||
public Binding getMapValueBinding(Object key) {
|
||||
if (mapValueBindings == null) {
|
||||
mapValueBindings = new HashMap<Object, Binding>();
|
||||
}
|
||||
createMapValueIfNecessary();
|
||||
Binding binding = mapValueBindings.get(key);
|
||||
if (binding == null) {
|
||||
BindingContext bindingContext = new MapValueBindingContext(key, this);
|
||||
ValueModel valueModel = new MapValueValueModel(key, getElementType(), (Map) this.binding.getValue(), bindingContext);
|
||||
binding = new GenericBinding(valueModel, bindingContext);
|
||||
mapValueBindings.put(key, binding);
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return property.getName();
|
||||
}
|
||||
|
||||
// implementing BindingRuleConfiguration
|
||||
public FieldModel getNested(String fieldName) {
|
||||
createValueIfNecessary();
|
||||
return getNestedRule(fieldName, fieldModel.getValueType()).getFieldModel(fieldModel.getValue());
|
||||
}
|
||||
|
||||
public BindingRuleConfiguration formatWith(Formatter<?> formatter) {
|
||||
public FieldModel getListElement(int index) {
|
||||
// TODO array support
|
||||
if (listElements == null) {
|
||||
listElements = new HashMap<Integer, FieldModel>();
|
||||
}
|
||||
growListIfNecessary(index);
|
||||
FieldModel field = listElements.get(index);
|
||||
if (field == null) {
|
||||
FieldModelContext context = new ListElementContext(index, this);
|
||||
ValueModel valueModel = new ListElementValueModel(index, getElementType(), (List) fieldModel.getValue());
|
||||
field = new DefaultFieldModel(valueModel, context);
|
||||
listElements.put(index, field);
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
public FieldModel getMapValue(Object key) {
|
||||
if (mapValues == null) {
|
||||
mapValues = new HashMap<Object, FieldModel>();
|
||||
}
|
||||
createMapValueIfNecessary();
|
||||
FieldModel field = mapValues.get(key);
|
||||
if (field == null) {
|
||||
FieldModelContext context = new MapValueContext(key, this);
|
||||
ValueModel valueModel = new MapValueValueModel(key, getElementType(), (Map) fieldModel.getValue(), context);
|
||||
field = new DefaultFieldModel(valueModel, context);
|
||||
mapValues.put(key, field);
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
// implementing FieldModelConfiguration
|
||||
|
||||
public FieldModelConfiguration formatWith(Formatter<?> formatter) {
|
||||
this.formatter = formatter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BindingRuleConfiguration formatElementsWith(Formatter<?> formatter) {
|
||||
if (!List.class.isAssignableFrom(modelClass) || modelClass.isArray()) {
|
||||
public FieldModelConfiguration formatElementsWith(Formatter<?> formatter) {
|
||||
if (!List.class.isAssignableFrom(domainModelClass) || domainModelClass.isArray()) {
|
||||
throw new IllegalStateException(
|
||||
"Bound property is not a List or an array; cannot set a element formatter");
|
||||
"Field is not a List or an Array; cannot set a element formatter");
|
||||
}
|
||||
elementFormatter = formatter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BindingRuleConfiguration formatKeysWith(Formatter<?> formatter) {
|
||||
if (!Map.class.isAssignableFrom(modelClass)) {
|
||||
throw new IllegalStateException("Bound property is not a Map; cannot set a key formatter");
|
||||
public FieldModelConfiguration formatKeysWith(Formatter<?> formatter) {
|
||||
if (!Map.class.isAssignableFrom(domainModelClass)) {
|
||||
throw new IllegalStateException("Field is not a Map; cannot set a key formatter");
|
||||
}
|
||||
keyFormatter = formatter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BindingRuleConfiguration editableWhen(Condition condition) {
|
||||
public FieldModelConfiguration editableWhen(Condition condition) {
|
||||
editableCondition = condition;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BindingRuleConfiguration enabledWhen(Condition condition) {
|
||||
public FieldModelConfiguration enabledWhen(Condition condition) {
|
||||
enabledCondition = condition;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BindingRuleConfiguration visibleWhen(Condition condition) {
|
||||
public FieldModelConfiguration visibleWhen(Condition condition) {
|
||||
visibleCondition = condition;
|
||||
return this;
|
||||
}
|
||||
|
||||
// package private helpers
|
||||
|
||||
PropertyFieldModelRule getNestedRule(String propertyName) {
|
||||
return getNestedRule(propertyName, this.property.getPropertyType());
|
||||
}
|
||||
|
||||
PropertyFieldModelRule getNestedRule(String propertyName, Class<?> domainModelClass) {
|
||||
if (nestedFieldModelRules == null) {
|
||||
nestedFieldModelRules = new HashMap<String, PropertyFieldModelRule>();
|
||||
}
|
||||
PropertyFieldModelRule rule = nestedFieldModelRules.get(propertyName);
|
||||
if (rule == null) {
|
||||
rule = new PropertyFieldModelRule(propertyName, domainModelClass);
|
||||
nestedFieldModelRules.put(propertyName, rule);
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private Class<?> getElementType() {
|
||||
|
|
@ -318,40 +339,22 @@ public class GenericBindingFactory implements BindingFactory {
|
|||
return GenericCollectionTypeResolver.getMapKeyReturnType(property.getReadMethod());
|
||||
}
|
||||
|
||||
GenericBindingRule getBindingRule(String property) {
|
||||
return getBindingRule(property, this.property.getPropertyType());
|
||||
}
|
||||
|
||||
GenericBindingRule getBindingRule(String property, Class<?> modelClass) {
|
||||
if (nestedBindingRules == null) {
|
||||
nestedBindingRules = new HashMap<String, GenericBindingRule>();
|
||||
FieldModel getFieldModel(Object domainObject) {
|
||||
if (fieldModel == null) {
|
||||
PropertyValueModel valueModel = new PropertyValueModel(property, domainObject);
|
||||
fieldModel = new DefaultFieldModel(valueModel, this);
|
||||
}
|
||||
GenericBindingRule rule = nestedBindingRules.get(property);
|
||||
if (rule == null) {
|
||||
rule = new GenericBindingRule(property, modelClass);
|
||||
nestedBindingRules.put(property, rule);
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
Binding getBinding(Object model) {
|
||||
if (binding == null) {
|
||||
PropertyValueModel valueModel = new PropertyValueModel(property, model);
|
||||
binding = new GenericBinding(valueModel, this);
|
||||
}
|
||||
return binding;
|
||||
return fieldModel;
|
||||
}
|
||||
|
||||
private PropertyDescriptor findPropertyDescriptor(String property) {
|
||||
PropertyDescriptor[] propDescs = getBeanInfo(modelClass).getPropertyDescriptors();
|
||||
PropertyDescriptor[] propDescs = getBeanInfo(domainModelClass).getPropertyDescriptors();
|
||||
for (PropertyDescriptor propDesc : propDescs) {
|
||||
if (propDesc.getName().equals(property)) {
|
||||
return propDesc;
|
||||
}
|
||||
}
|
||||
throw new PropertyNotFoundException(property, modelClass);
|
||||
throw new FieldNotFoundException(property);
|
||||
}
|
||||
|
||||
private BeanInfo getBeanInfo(Class<?> clazz) {
|
||||
|
|
@ -363,30 +366,30 @@ public class GenericBindingFactory implements BindingFactory {
|
|||
}
|
||||
|
||||
private void createValueIfNecessary() {
|
||||
Object value = binding.getValue();
|
||||
Object value = fieldModel.getValue();
|
||||
if (value == null) {
|
||||
value = newValue(binding.getValueType());
|
||||
binding.applySourceValue(value);
|
||||
binding.commit();
|
||||
value = newValue(fieldModel.getValueType());
|
||||
fieldModel.applySubmittedValue(value);
|
||||
fieldModel.commit();
|
||||
}
|
||||
}
|
||||
|
||||
private void createMapValueIfNecessary() {
|
||||
Object value = binding.getValue();
|
||||
Object value = fieldModel.getValue();
|
||||
if (value == null) {
|
||||
value = newMapValue(binding.getValueType());
|
||||
binding.applySourceValue(value);
|
||||
binding.commit();
|
||||
value = newMapValue(fieldModel.getValueType());
|
||||
fieldModel.applySubmittedValue(value);
|
||||
fieldModel.commit();
|
||||
}
|
||||
}
|
||||
|
||||
private void growListIfNecessary(int index) {
|
||||
List list = (List) binding.getValue();
|
||||
List list = (List) fieldModel.getValue();
|
||||
if (list == null) {
|
||||
list = newListValue(binding.getValueType());
|
||||
binding.applySourceValue(list);
|
||||
binding.commit();
|
||||
list = (List) binding.getValue();
|
||||
list = newListValue(fieldModel.getValueType());
|
||||
fieldModel.applySubmittedValue(list);
|
||||
fieldModel.commit();
|
||||
list = (List) fieldModel.getValue();
|
||||
}
|
||||
if (index >= list.size()) {
|
||||
for (int i = list.size(); i <= index; i++) {
|
||||
|
|
@ -423,15 +426,15 @@ public class GenericBindingFactory implements BindingFactory {
|
|||
|
||||
}
|
||||
|
||||
private static class ListElementBindingContext implements BindingContext {
|
||||
private static class ListElementContext implements FieldModelContext {
|
||||
|
||||
private int index;
|
||||
|
||||
private GenericBindingRule listBindingContext;
|
||||
private PropertyFieldModelRule listBindingContext;
|
||||
|
||||
final Map<String, Binding> nestedBindings = new HashMap<String, Binding>();
|
||||
final Map<String, FieldModel> nestedBindings = new HashMap<String, FieldModel>();
|
||||
|
||||
public ListElementBindingContext(int index, GenericBindingRule listBindingContext) {
|
||||
public ListElementContext(int index, PropertyFieldModelRule listBindingContext) {
|
||||
this.index = index;
|
||||
this.listBindingContext = listBindingContext;
|
||||
}
|
||||
|
|
@ -444,22 +447,6 @@ public class GenericBindingFactory implements BindingFactory {
|
|||
return listBindingContext.getTypeConverter();
|
||||
}
|
||||
|
||||
public Binding getNestedBinding(String property) {
|
||||
Object model = ((List<?>) listBindingContext.binding.getValue()).get(index);
|
||||
Class<?> elementType = listBindingContext.getElementType();
|
||||
if (elementType == null) {
|
||||
elementType = model.getClass();
|
||||
}
|
||||
GenericBindingRule rule = listBindingContext.getBindingRule(property, elementType);
|
||||
Binding binding = nestedBindings.get(property);
|
||||
if (binding == null) {
|
||||
PropertyValueModel valueModel = new PropertyValueModel(rule.property, model);
|
||||
binding = new GenericBinding(valueModel, rule);
|
||||
nestedBindings.put(property, binding);
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Formatter getFormatter() {
|
||||
return listBindingContext.getElementFormatter();
|
||||
|
|
@ -467,11 +454,13 @@ public class GenericBindingFactory implements BindingFactory {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Formatter getElementFormatter() {
|
||||
// TODO multi-dimensional support
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Formatter getKeyFormatter() {
|
||||
// TODO multi-dimensional support
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -487,95 +476,116 @@ public class GenericBindingFactory implements BindingFactory {
|
|||
return listBindingContext.getVisibleCondition();
|
||||
}
|
||||
|
||||
public Binding getListElementBinding(int index) {
|
||||
throw new IllegalArgumentException("Not yet supported");
|
||||
}
|
||||
|
||||
public Binding getMapValueBinding(Object key) {
|
||||
throw new IllegalArgumentException("Not yet supported");
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return listBindingContext.getLabel() + "[" + index + "]";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private static class MapValueBindingContext implements BindingContext {
|
||||
|
||||
private Object key;
|
||||
|
||||
private GenericBindingRule mapBindingContext;
|
||||
|
||||
final Map<String, Binding> nestedBindings = new HashMap<String, Binding>();
|
||||
|
||||
public MapValueBindingContext(Object key, GenericBindingRule mapBindingContext) {
|
||||
this.key = key;
|
||||
this.mapBindingContext = mapBindingContext;
|
||||
}
|
||||
|
||||
public MessageSource getMessageSource() {
|
||||
return mapBindingContext.getMessageSource();
|
||||
}
|
||||
|
||||
public TypeConverter getTypeConverter() {
|
||||
return mapBindingContext.getTypeConverter();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Binding getNestedBinding(String property) {
|
||||
Object model = ((Map) mapBindingContext.binding.getValue()).get(key);
|
||||
Class<?> elementType = mapBindingContext.getElementType();
|
||||
public FieldModel getNested(String property) {
|
||||
Object model = ((List<?>) listBindingContext.fieldModel.getValue()).get(index);
|
||||
Class<?> elementType = listBindingContext.getElementType();
|
||||
if (elementType == null) {
|
||||
elementType = model.getClass();
|
||||
}
|
||||
GenericBindingRule rule = mapBindingContext.getBindingRule(property, elementType);
|
||||
Binding binding = nestedBindings.get(property);
|
||||
PropertyFieldModelRule rule = listBindingContext.getNestedRule(property, elementType);
|
||||
FieldModel binding = nestedBindings.get(property);
|
||||
if (binding == null) {
|
||||
PropertyValueModel valueModel = new PropertyValueModel(rule.property, model);
|
||||
binding = new GenericBinding(valueModel, rule);
|
||||
binding = new DefaultFieldModel(valueModel, rule);
|
||||
nestedBindings.put(property, binding);
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
||||
public FieldModel getListElement(int index) {
|
||||
// TODO multi-dimensional support
|
||||
throw new IllegalArgumentException("Not yet supported");
|
||||
}
|
||||
|
||||
public FieldModel getMapValue(Object key) {
|
||||
// TODO multi-dimensional support
|
||||
throw new IllegalArgumentException("Not yet supported");
|
||||
}
|
||||
};
|
||||
|
||||
private static class MapValueContext implements FieldModelContext {
|
||||
|
||||
private Object key;
|
||||
|
||||
private PropertyFieldModelRule mapContext;
|
||||
|
||||
final Map<String, FieldModel> nestedBindings = new HashMap<String, FieldModel>();
|
||||
|
||||
public MapValueContext(Object key, PropertyFieldModelRule mapContext) {
|
||||
this.key = key;
|
||||
this.mapContext = mapContext;
|
||||
}
|
||||
|
||||
public MessageSource getMessageSource() {
|
||||
return mapContext.getMessageSource();
|
||||
}
|
||||
|
||||
public TypeConverter getTypeConverter() {
|
||||
return mapContext.getTypeConverter();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Formatter getFormatter() {
|
||||
return mapBindingContext.getElementFormatter();
|
||||
return mapContext.getElementFormatter();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Formatter getElementFormatter() {
|
||||
// TODO multi-dimensional support
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Formatter getKeyFormatter() {
|
||||
// TODO multi-dimensional support
|
||||
return null;
|
||||
}
|
||||
|
||||
public Condition getEditableCondition() {
|
||||
return mapBindingContext.getEditableCondition();
|
||||
return mapContext.getEditableCondition();
|
||||
}
|
||||
|
||||
public Condition getEnabledCondition() {
|
||||
return mapBindingContext.getEnabledCondition();
|
||||
return mapContext.getEnabledCondition();
|
||||
}
|
||||
|
||||
public Condition getVisibleCondition() {
|
||||
return mapBindingContext.getVisibleCondition();
|
||||
return mapContext.getVisibleCondition();
|
||||
}
|
||||
|
||||
public Binding getListElementBinding(int index) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public FieldModel getNested(String property) {
|
||||
Object model = ((Map) mapContext.fieldModel.getValue()).get(key);
|
||||
Class<?> elementType = mapContext.getElementType();
|
||||
if (elementType == null) {
|
||||
elementType = model.getClass();
|
||||
}
|
||||
PropertyFieldModelRule rule = mapContext.getNestedRule(property, elementType);
|
||||
FieldModel binding = nestedBindings.get(property);
|
||||
if (binding == null) {
|
||||
PropertyValueModel valueModel = new PropertyValueModel(rule.property, model);
|
||||
binding = new DefaultFieldModel(valueModel, rule);
|
||||
nestedBindings.put(property, binding);
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
||||
public FieldModel getListElement(int index) {
|
||||
// TODO multi-dimensional support
|
||||
throw new IllegalArgumentException("Not yet supported");
|
||||
}
|
||||
|
||||
public Binding getMapValueBinding(Object key) {
|
||||
public FieldModel getMapValue(Object key) {
|
||||
// TODO multi-dimensional support
|
||||
throw new IllegalArgumentException("Not yet supported");
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return mapBindingContext.getLabel() + "[" + key + "]";
|
||||
return mapContext.getLabel() + "[" + key + "]";
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -18,26 +18,26 @@ package org.springframework.ui.binding.support;
|
|||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ui.binding.BindingFactory;
|
||||
import org.springframework.ui.binding.BindingFactoryLocator;
|
||||
import org.springframework.ui.binding.PresentationModel;
|
||||
import org.springframework.ui.binding.PresentationModelFactory;
|
||||
|
||||
/**
|
||||
* BindingFactoryLocator implementation that uses a {@link IdentityHashMap} to map models to BindingFactories.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class GenericBindingFactoryLocator implements BindingFactoryLocator {
|
||||
public class DefaultPresentationModelFactory implements PresentationModelFactory {
|
||||
|
||||
private Map<Object, BindingFactory> bindingFactories = new IdentityHashMap<Object, BindingFactory>();
|
||||
private Map<Object, PresentationModel> presentationModels = new IdentityHashMap<Object, PresentationModel>();
|
||||
|
||||
public void put(BindingFactory bindingFactory) {
|
||||
bindingFactories.put(bindingFactory.getModel(), bindingFactory);
|
||||
public void put(Object domainObject, PresentationModel presentationModel) {
|
||||
presentationModels.put(domainObject, presentationModel);
|
||||
}
|
||||
|
||||
public BindingFactory getBindingFactory(Object model) {
|
||||
BindingFactory factory = bindingFactories.get(model);
|
||||
public PresentationModel getPresentationModel(Object domainObject) {
|
||||
PresentationModel factory = presentationModels.get(domainObject);
|
||||
if (factory == null) {
|
||||
factory = new GenericBindingFactory(model);
|
||||
bindingFactories.put(model, factory);
|
||||
factory = new DefaultPresentationModel(domainObject);
|
||||
presentationModels.put(domainObject, factory);
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
|
|
@ -17,16 +17,16 @@ package org.springframework.ui.binding.support;
|
|||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.core.convert.TypeConverter;
|
||||
import org.springframework.ui.binding.Binding;
|
||||
import org.springframework.ui.binding.FieldModel;
|
||||
import org.springframework.ui.binding.config.Condition;
|
||||
import org.springframework.ui.format.Formatter;
|
||||
|
||||
/**
|
||||
* A context that allows a Binding to query its BindingRule.
|
||||
* A context that allows a FieldModel to access its external configuration.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface BindingContext {
|
||||
public interface FieldModelContext {
|
||||
|
||||
MessageSource getMessageSource();
|
||||
|
||||
|
|
@ -38,8 +38,6 @@ public interface BindingContext {
|
|||
|
||||
Condition getVisibleCondition();
|
||||
|
||||
Binding getNestedBinding(String property);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Formatter getFormatter();
|
||||
|
||||
|
|
@ -49,10 +47,12 @@ public interface BindingContext {
|
|||
@SuppressWarnings("unchecked")
|
||||
Formatter getKeyFormatter();
|
||||
|
||||
Binding getListElementBinding(int index);
|
||||
|
||||
Binding getMapValueBinding(Object key);
|
||||
|
||||
String getLabel();
|
||||
|
||||
FieldModel getNested(String fieldName);
|
||||
|
||||
FieldModel getListElement(int index);
|
||||
|
||||
FieldModel getMapValue(Object key);
|
||||
|
||||
}
|
||||
|
|
@ -20,11 +20,11 @@ import java.util.Collections;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class PropertyPath implements Iterable<PropertyPathElement> {
|
||||
class FieldPath implements Iterable<FieldPathElement> {
|
||||
|
||||
private List<PropertyPathElement> elements = new ArrayList<PropertyPathElement>();
|
||||
private List<FieldPathElement> elements = new ArrayList<FieldPathElement>();
|
||||
|
||||
public PropertyPath(String propertyPath) {
|
||||
public FieldPath(String propertyPath) {
|
||||
// a.b.c[i].d[key].e
|
||||
String[] props = propertyPath.split("\\.");
|
||||
if (props.length == 0) {
|
||||
|
|
@ -35,19 +35,19 @@ public class PropertyPath implements Iterable<PropertyPathElement> {
|
|||
int start = prop.indexOf('[');
|
||||
int end = prop.indexOf(']', start);
|
||||
String index = prop.substring(start + 1, end);
|
||||
elements.add(new PropertyPathElement(prop.substring(0, start), false));
|
||||
elements.add(new PropertyPathElement(index, true));
|
||||
elements.add(new FieldPathElement(prop.substring(0, start), false));
|
||||
elements.add(new FieldPathElement(index, true));
|
||||
} else {
|
||||
elements.add(new PropertyPathElement(prop, false));
|
||||
elements.add(new FieldPathElement(prop, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PropertyPathElement getFirstElement() {
|
||||
public FieldPathElement getFirstElement() {
|
||||
return elements.get(0);
|
||||
}
|
||||
|
||||
public List<PropertyPathElement> getNestedElements() {
|
||||
public List<FieldPathElement> getNestedElements() {
|
||||
if (elements.size() > 1) {
|
||||
return elements.subList(1, elements.size());
|
||||
} else {
|
||||
|
|
@ -55,12 +55,11 @@ public class PropertyPath implements Iterable<PropertyPathElement> {
|
|||
}
|
||||
}
|
||||
|
||||
public Iterator<PropertyPathElement> iterator() {
|
||||
public Iterator<FieldPathElement> iterator() {
|
||||
return elements.iterator();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return elements.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -15,13 +15,13 @@
|
|||
*/
|
||||
package org.springframework.ui.binding.support;
|
||||
|
||||
public class PropertyPathElement {
|
||||
class FieldPathElement {
|
||||
|
||||
private String value;
|
||||
|
||||
private boolean index;
|
||||
|
||||
public PropertyPathElement(String value, boolean index) {
|
||||
public FieldPathElement(String value, boolean index) {
|
||||
this.value = value;
|
||||
this.index = index;
|
||||
}
|
||||
|
|
@ -28,6 +28,10 @@ import org.springframework.ui.format.Formatter;
|
|||
*/
|
||||
public interface FormatterRegistry {
|
||||
|
||||
/**
|
||||
* Get the Formatter for the property.
|
||||
* @return the Formatter, or <code>null</code> if none is registered
|
||||
*/
|
||||
Formatter<?> getFormatter(PropertyDescriptor property);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ public class GenericFormatterRegistry implements FormatterRegistry {
|
|||
|
||||
public Object parse(String formatted, Locale locale) throws ParseException {
|
||||
String[] fields = StringUtils.commaDelimitedListToStringArray(formatted);
|
||||
if (collectionType.getCollectionType().isArray()) {
|
||||
if (collectionType.getType().isArray()) {
|
||||
Object array = Array.newInstance(getElementType(), fields.length);
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
Array.set(array, i, elementFormatter.parse(fields[i], locale));
|
||||
|
|
@ -289,7 +289,7 @@ public class GenericFormatterRegistry implements FormatterRegistry {
|
|||
private Collection newCollection() {
|
||||
try {
|
||||
Class<? extends Collection> implType = ConversionUtils
|
||||
.getCollectionImpl((Class<? extends Collection>) collectionType.getCollectionType());
|
||||
.getCollectionImpl((Class<? extends Collection>) collectionType.getType());
|
||||
return (Collection) implType.newInstance();
|
||||
} catch (InstantiationException e) {
|
||||
throw new IllegalStateException("Should not happen", e);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,12 @@ import java.util.List;
|
|||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
|
||||
class ListElementValueModel implements ValueModel {
|
||||
/**
|
||||
* A ValueModel for a element in a List.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ListElementValueModel implements ValueModel {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List list;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@ import java.util.Map;
|
|||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
|
||||
/**
|
||||
* A ValueModel for a element in a Map.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MapValueValueModel implements ValueModel {
|
||||
|
||||
private Object key;
|
||||
|
|
@ -29,7 +34,7 @@ public class MapValueValueModel implements ValueModel {
|
|||
private Map map;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public MapValueValueModel(Object key, Class<?> elementType, Map map, BindingContext bindingContext) {
|
||||
public MapValueValueModel(Object key, Class<?> elementType, Map map, FieldModelContext bindingContext) {
|
||||
this.key = key;
|
||||
this.elementType = elementType;
|
||||
this.map = map;
|
||||
|
|
|
|||
|
|
@ -1,39 +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.support;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class PropertyNotFoundException extends RuntimeException {
|
||||
|
||||
private String property;
|
||||
|
||||
private Class<?> modelClass;
|
||||
|
||||
public PropertyNotFoundException(String property, Class<?> modelClass) {
|
||||
super("No property '" + property + "' found on model [" + modelClass.getName() + "]");
|
||||
this.property = property;
|
||||
this.modelClass = modelClass;
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
public Class<?> getModelClass() {
|
||||
return modelClass;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -21,6 +21,11 @@ import org.springframework.core.MethodParameter;
|
|||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* A ValueModel for a bean property.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
public class PropertyValueModel implements ValueModel {
|
||||
|
||||
private PropertyDescriptor property;
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@ package org.springframework.ui.binding.support;
|
|||
import org.springframework.core.convert.TypeDescriptor;
|
||||
|
||||
/**
|
||||
* For accessing the raw bound model object.
|
||||
* A interface for reading and writing a value.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface ValueModel {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package org.springframework.ui.binding.support;
|
||||
package org.springframework.ui.binding.binder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
|
@ -22,11 +22,14 @@ import org.junit.Before;
|
|||
import org.junit.Test;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.ui.binding.Binding;
|
||||
import org.springframework.ui.binding.FieldModel;
|
||||
import org.springframework.ui.binding.BindingStatus;
|
||||
import org.springframework.ui.binding.binder.BindingResults;
|
||||
import org.springframework.ui.binding.binder.GenericBinder;
|
||||
import org.springframework.ui.binding.binder.MissingSourceValuesException;
|
||||
import org.springframework.ui.binding.binder.MissingFieldException;
|
||||
import org.springframework.ui.binding.support.CollectionTypeDescriptor;
|
||||
import org.springframework.ui.binding.support.DefaultPresentationModel;
|
||||
import org.springframework.ui.binding.support.GenericFormatterRegistry;
|
||||
import org.springframework.ui.format.AnnotationFormatterFactory;
|
||||
import org.springframework.ui.format.Formatted;
|
||||
import org.springframework.ui.format.Formatter;
|
||||
|
|
@ -40,15 +43,15 @@ public class GenericBinderTests {
|
|||
|
||||
private GenericBinder binder;
|
||||
|
||||
private GenericBindingFactory bindingFactory;
|
||||
private DefaultPresentationModel presentationModel;
|
||||
|
||||
private TestBean bean;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
bean = new TestBean();
|
||||
bindingFactory = new GenericBindingFactory(bean);
|
||||
binder = new GenericBinder(bindingFactory);
|
||||
presentationModel = new DefaultPresentationModel(bean);
|
||||
binder = new GenericBinder(presentationModel);
|
||||
LocaleContextHolder.setLocale(Locale.US);
|
||||
}
|
||||
|
||||
|
|
@ -66,17 +69,17 @@ public class GenericBinderTests {
|
|||
BindingResults results = binder.bind(values);
|
||||
assertEquals(3, results.size());
|
||||
|
||||
assertEquals("string", results.get(0).getProperty());
|
||||
assertEquals("string", results.get(0).getFieldName());
|
||||
assertFalse(results.get(0).isFailure());
|
||||
assertEquals("test", results.get(0).getSourceValue());
|
||||
assertEquals("test", results.get(0).getSubmittedValue());
|
||||
|
||||
assertEquals("integer", results.get(1).getProperty());
|
||||
assertEquals("integer", results.get(1).getFieldName());
|
||||
assertFalse(results.get(1).isFailure());
|
||||
assertEquals("3", results.get(1).getSourceValue());
|
||||
assertEquals("3", results.get(1).getSubmittedValue());
|
||||
|
||||
assertEquals("foo", results.get(2).getProperty());
|
||||
assertEquals("foo", results.get(2).getFieldName());
|
||||
assertFalse(results.get(2).isFailure());
|
||||
assertEquals("BAR", results.get(2).getSourceValue());
|
||||
assertEquals("BAR", results.get(2).getSubmittedValue());
|
||||
|
||||
assertEquals("test", bean.getString());
|
||||
assertEquals(3, bean.getInteger());
|
||||
|
|
@ -98,14 +101,14 @@ public class GenericBinderTests {
|
|||
|
||||
@Test
|
||||
public void bindSingleValuePropertyFormatter() throws ParseException {
|
||||
bindingFactory.bindingRule("date").formatWith(new DateFormatter());
|
||||
presentationModel.field("date").formatWith(new DateFormatter());
|
||||
binder.bind(Collections.singletonMap("date", "2009-06-01"));
|
||||
assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), bean.getDate());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindSingleValuePropertyFormatterParseException() {
|
||||
bindingFactory.bindingRule("date").formatWith(new DateFormatter());
|
||||
presentationModel.field("date").formatWith(new DateFormatter());
|
||||
BindingResults results = binder.bind(Collections.singletonMap("date", "bogus"));
|
||||
assertEquals(1, results.size());
|
||||
assertTrue(results.get(0).isFailure());
|
||||
|
|
@ -116,7 +119,7 @@ public class GenericBinderTests {
|
|||
public void bindSingleValueWithFormatterRegistedByType() throws ParseException {
|
||||
GenericFormatterRegistry formatterRegistry = new GenericFormatterRegistry();
|
||||
formatterRegistry.add(Date.class, new DateFormatter());
|
||||
bindingFactory.setFormatterRegistry(formatterRegistry);
|
||||
presentationModel.setFormatterRegistry(formatterRegistry);
|
||||
|
||||
binder.bind(Collections.singletonMap("date", "2009-06-01"));
|
||||
assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), bean.getDate());
|
||||
|
|
@ -126,7 +129,7 @@ public class GenericBinderTests {
|
|||
public void bindSingleValueWithAnnotationFormatterFactoryRegistered() throws ParseException {
|
||||
GenericFormatterRegistry formatterRegistry = new GenericFormatterRegistry();
|
||||
formatterRegistry.add(new CurrencyAnnotationFormatterFactory());
|
||||
bindingFactory.setFormatterRegistry(formatterRegistry);
|
||||
presentationModel.setFormatterRegistry(formatterRegistry);
|
||||
|
||||
binder.bind(Collections.singletonMap("currency", "$23.56"));
|
||||
assertEquals(new BigDecimal("23.56"), bean.getCurrency());
|
||||
|
|
@ -135,27 +138,27 @@ public class GenericBinderTests {
|
|||
@Test
|
||||
public void bindSingleValuePropertyNotFound() throws ParseException {
|
||||
BindingResults results = binder.bind(Collections.singletonMap("bogus", "2009-06-01"));
|
||||
assertEquals("bogus", results.get(0).getProperty());
|
||||
assertEquals("bogus", results.get(0).getFieldName());
|
||||
assertTrue(results.get(0).isFailure());
|
||||
assertEquals("propertyNotFound", results.get(0).getAlert().getCode());
|
||||
}
|
||||
|
||||
@Test(expected = MissingSourceValuesException.class)
|
||||
@Test(expected = MissingFieldException.class)
|
||||
public void bindMissingRequiredSourceValue() {
|
||||
binder.setRequired(new String[] { "integer" });
|
||||
binder.setRequiredFields(new String[] { "integer" });
|
||||
// missing "integer" - violated bind contract
|
||||
binder.bind(Collections.singletonMap("string", "test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBindingCustomFormatter() {
|
||||
bindingFactory.bindingRule("currency").formatWith(new CurrencyFormatter());
|
||||
Binding b = bindingFactory.getBinding("currency");
|
||||
presentationModel.field("currency").formatWith(new CurrencyFormatter());
|
||||
FieldModel b = presentationModel.getFieldModel("currency");
|
||||
assertFalse(b.isList());
|
||||
assertFalse(b.isMap());
|
||||
assertEquals(null, b.getValue());
|
||||
assertEquals("", b.getRenderValue());
|
||||
b.applySourceValue("$23.56");
|
||||
b.applySubmittedValue("$23.56");
|
||||
assertEquals(BindingStatus.DIRTY, b.getBindingStatus());
|
||||
assertEquals(new BigDecimal("23.56"), b.getValue());
|
||||
assertEquals("$23.56", b.getRenderValue());
|
||||
|
|
@ -168,9 +171,9 @@ public class GenericBinderTests {
|
|||
@Test
|
||||
public void getBindingCustomFormatterRequiringTypeCoersion() {
|
||||
// IntegerFormatter formats Longs, so conversion from Integer -> Long is performed
|
||||
bindingFactory.bindingRule("integer").formatWith(new IntegerFormatter());
|
||||
Binding b = bindingFactory.getBinding("integer");
|
||||
b.applySourceValue("2,300");
|
||||
presentationModel.field("integer").formatWith(new IntegerFormatter());
|
||||
FieldModel b = presentationModel.getFieldModel("integer");
|
||||
b.applySubmittedValue("2,300");
|
||||
assertEquals("2,300", b.getRenderValue());
|
||||
b.commit();
|
||||
assertEquals(BindingStatus.COMMITTED, b.getBindingStatus());
|
||||
|
|
@ -182,10 +185,10 @@ public class GenericBinderTests {
|
|||
MockMessageSource messages = new MockMessageSource();
|
||||
messages.addMessage("typeMismatch", Locale.US,
|
||||
"Please enter an integer in format ### for the #{label} field; you entered #{value}");
|
||||
bindingFactory.setMessageSource(messages);
|
||||
bindingFactory.bindingRule("integer").formatWith(new IntegerFormatter());
|
||||
Binding b = bindingFactory.getBinding("integer");
|
||||
b.applySourceValue("bogus");
|
||||
presentationModel.setMessageSource(messages);
|
||||
presentationModel.field("integer").formatWith(new IntegerFormatter());
|
||||
FieldModel b = presentationModel.getFieldModel("integer");
|
||||
b.applySubmittedValue("bogus");
|
||||
assertEquals("Please enter an integer in format ### for the integer field; you entered bogus", b
|
||||
.getStatusAlert().getMessage());
|
||||
}
|
||||
|
|
@ -193,11 +196,11 @@ public class GenericBinderTests {
|
|||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void getBindingMultiValued() {
|
||||
Binding b = bindingFactory.getBinding("foos");
|
||||
FieldModel b = presentationModel.getFieldModel("foos");
|
||||
assertTrue(b.isList());
|
||||
assertEquals(null, b.getValue());
|
||||
assertEquals("", b.getRenderValue());
|
||||
b.applySourceValue(new String[] { "BAR", "BAZ", "BOOP" });
|
||||
b.applySubmittedValue(new String[] { "BAR", "BAZ", "BOOP" });
|
||||
b.commit();
|
||||
assertEquals(FooEnum.BAR, bean.getFoos().get(0));
|
||||
assertEquals(FooEnum.BAZ, bean.getFoos().get(1));
|
||||
|
|
@ -213,22 +216,22 @@ public class GenericBinderTests {
|
|||
@Test
|
||||
public void getBindingMultiValuedIndexAccess() {
|
||||
bean.setFoos(Arrays.asList(new FooEnum[] { FooEnum.BAR }));
|
||||
Binding b = bindingFactory.getBinding("foos[0]");
|
||||
FieldModel b = presentationModel.getFieldModel("foos[0]");
|
||||
assertFalse(b.isList());
|
||||
assertEquals(FooEnum.BAR, b.getValue());
|
||||
assertEquals("BAR", b.getRenderValue());
|
||||
b.applySourceValue("BAZ");
|
||||
b.applySubmittedValue("BAZ");
|
||||
assertEquals("BAZ", b.getRenderValue());
|
||||
assertEquals(FooEnum.BAZ, b.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBindingMultiValuedTypeConversionFailure() {
|
||||
Binding b = bindingFactory.getBinding("foos");
|
||||
FieldModel b = presentationModel.getFieldModel("foos");
|
||||
assertTrue(b.isList());
|
||||
assertEquals(null, b.getValue());
|
||||
b.applySourceValue(new String[] { "BAR", "BOGUS", "BOOP" });
|
||||
assertEquals(BindingStatus.INVALID_SOURCE_VALUE, b.getBindingStatus());
|
||||
b.applySubmittedValue(new String[] { "BAR", "BOGUS", "BOOP" });
|
||||
assertEquals(BindingStatus.INVALID_SUBMITTED_VALUE, b.getBindingStatus());
|
||||
assertEquals("typeMismatch", b.getStatusAlert().getCode());
|
||||
}
|
||||
|
||||
|
|
@ -263,7 +266,7 @@ public class GenericBinderTests {
|
|||
public void bindToListSingleString() {
|
||||
GenericFormatterRegistry formatterRegistry = new GenericFormatterRegistry();
|
||||
formatterRegistry.add(new CollectionTypeDescriptor(List.class, Address.class), new AddressListFormatter());
|
||||
bindingFactory.setFormatterRegistry(formatterRegistry);
|
||||
presentationModel.setFormatterRegistry(formatterRegistry);
|
||||
Map<String, String> values = new LinkedHashMap<String, String>();
|
||||
values
|
||||
.put("addresses",
|
||||
|
|
@ -310,7 +313,7 @@ public class GenericBinderTests {
|
|||
public void getListAsSingleString() {
|
||||
GenericFormatterRegistry formatterRegistry = new GenericFormatterRegistry();
|
||||
formatterRegistry.add(new CollectionTypeDescriptor(List.class, Address.class), new AddressListFormatter());
|
||||
bindingFactory.setFormatterRegistry(formatterRegistry);
|
||||
presentationModel.setFormatterRegistry(formatterRegistry);
|
||||
Address address1 = new Address();
|
||||
address1.setStreet("s1");
|
||||
address1.setCity("c1");
|
||||
|
|
@ -325,8 +328,8 @@ public class GenericBinderTests {
|
|||
addresses.add(address1);
|
||||
addresses.add(address2);
|
||||
bean.addresses = addresses;
|
||||
String value = bindingFactory.getBinding("addresses").getRenderValue();
|
||||
assertEquals("s1:c1:st1:z1,s2:c2:st2:z2,", value);
|
||||
String value = presentationModel.getFieldModel("addresses").getRenderValue();
|
||||
assertEquals("s1:c1:st1:z1,s2:c2:st2:z2", value);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -345,7 +348,7 @@ public class GenericBinderTests {
|
|||
addresses.add(address1);
|
||||
addresses.add(address2);
|
||||
bean.addresses = addresses;
|
||||
String value = bindingFactory.getBinding("addresses").getRenderValue();
|
||||
String value = presentationModel.getFieldModel("addresses").getRenderValue();
|
||||
assertEquals("s1:c1:st1:z1,s2:c2:st2:z2", value);
|
||||
}
|
||||
|
||||
|
|
@ -424,7 +427,7 @@ public class GenericBinderTests {
|
|||
foods.put(FoodGroup.FRUIT, "Peaches");
|
||||
foods.put(FoodGroup.MEAT, "Ham");
|
||||
bean.favoriteFoodsByGroup = foods;
|
||||
String value = bindingFactory.getBinding("favoriteFoodsByGroup").getRenderValue();
|
||||
String value = presentationModel.getFieldModel("favoriteFoodsByGroup").getRenderValue();
|
||||
// TODO this is inconsistent with previous test case
|
||||
assertEquals("{DAIRY=Milk, FRUIT=Peaches, MEAT=Ham}", value);
|
||||
}
|
||||
|
|
@ -439,15 +442,15 @@ public class GenericBinderTests {
|
|||
|
||||
@Test
|
||||
public void formatPossibleValue() {
|
||||
bindingFactory.bindingRule("currency").formatWith(new CurrencyFormatter());
|
||||
Binding b = bindingFactory.getBinding("currency");
|
||||
presentationModel.field("currency").formatWith(new CurrencyFormatter());
|
||||
FieldModel b = presentationModel.getFieldModel("currency");
|
||||
assertEquals("$5.00", b.formatValue(new BigDecimal("5")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formatPossibleValueDefault() {
|
||||
bindingFactory.bindingRule("currency");
|
||||
Binding b = bindingFactory.getBinding("currency");
|
||||
presentationModel.field("currency");
|
||||
FieldModel b = presentationModel.getFieldModel("currency");
|
||||
assertEquals("5", b.formatValue(new BigDecimal("5")));
|
||||
}
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package org.springframework.ui.binding.support;
|
||||
package org.springframework.ui.binding.binder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
|
|
@ -12,11 +12,12 @@ import java.util.Map;
|
|||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.ui.binding.binder.BindingResults;
|
||||
import org.springframework.ui.binding.binder.WebBinder;
|
||||
import org.springframework.ui.binding.support.DefaultPresentationModel;
|
||||
import org.springframework.ui.binding.support.GenericFormatterRegistry;
|
||||
import org.springframework.ui.format.date.DateFormatter;
|
||||
import org.springframework.ui.format.number.CurrencyFormat;
|
||||
import org.springframework.ui.format.number.CurrencyFormatter;
|
||||
|
|
@ -25,11 +26,15 @@ public class WebBinderTests {
|
|||
|
||||
TestBean bean = new TestBean();
|
||||
|
||||
WebBinder binder = new WebBinder(new GenericBindingFactory(bean));
|
||||
DefaultPresentationModel presentationModel;
|
||||
|
||||
WebBinder binder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
LocaleContextHolder.setLocale(Locale.US);
|
||||
presentationModel = new DefaultPresentationModel(bean);
|
||||
binder = new WebBinder(presentationModel);
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
@ -38,11 +43,11 @@ public class WebBinderTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void bindUserValuesCreatedFromUserMap() throws ParseException {
|
||||
GenericFormatterRegistry registry = new GenericFormatterRegistry();
|
||||
registry.add(Date.class, new DateFormatter());
|
||||
registry.add(CurrencyFormat.class, new CurrencyFormatter());
|
||||
//binder.setFormatterRegistry(registry);
|
||||
presentationModel.setFormatterRegistry(registry);
|
||||
Map<String, String> userMap = new LinkedHashMap<String, String>();
|
||||
userMap.put("string", "test");
|
||||
userMap.put("_integer", "doesn't matter");
|
||||
|
|
@ -53,12 +58,12 @@ public class WebBinderTests {
|
|||
userMap.put("_addresses", "doesn't matter");
|
||||
BindingResults results = binder.bind(userMap);
|
||||
assertEquals(6, results.size());
|
||||
assertEquals("test", results.get(0).getSourceValue());
|
||||
assertEquals(null, results.get(1).getSourceValue());
|
||||
assertEquals(Boolean.FALSE, results.get(2).getSourceValue());
|
||||
assertEquals("2009-06-10", results.get(3).getSourceValue());
|
||||
assertEquals("$5.00", results.get(4).getSourceValue());
|
||||
assertEquals(null, results.get(5).getSourceValue());
|
||||
assertEquals("test", results.get(0).getSubmittedValue());
|
||||
assertEquals(null, results.get(1).getSubmittedValue());
|
||||
assertEquals(Boolean.FALSE, results.get(2).getSubmittedValue());
|
||||
assertEquals("2009-06-10", results.get(3).getSubmittedValue());
|
||||
assertEquals("$5.00", results.get(4).getSubmittedValue());
|
||||
assertEquals(null, results.get(5).getSubmittedValue());
|
||||
|
||||
assertEquals("test", bean.getString());
|
||||
assertEquals(0, bean.getInteger());
|
||||
|
|
@ -73,13 +78,21 @@ public class WebBinderTests {
|
|||
}
|
||||
|
||||
public static class TestBean {
|
||||
|
||||
private String string;
|
||||
|
||||
private int integer;
|
||||
|
||||
private boolean bool;
|
||||
|
||||
private Date date;
|
||||
|
||||
private FooEnum foo;
|
||||
|
||||
private BigDecimal currency;
|
||||
|
||||
private List<FooEnum> foos;
|
||||
|
||||
private List<Address> addresses;
|
||||
|
||||
public String getString() {
|
||||
Loading…
Reference in New Issue