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