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