From 9fb9157f285ec5c5541814882574bbae770b27a8 Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Mon, 20 Jul 2009 06:22:11 +0000 Subject: [PATCH] binding rule impl git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@1559 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../springframework/ui/binding/Binding.java | 14 +- .../ui/binding/config/BindingRule.java | 19 -- .../config/BindingRuleConfiguration.java | 28 +- .../ui/binding/config/BindingRules.java | 9 - .../binding/config/BindingRulesBuilder.java | 120 ------- .../ui/binding/config/Condition.java | 19 ++ .../config/ConfigurableBindingRule.java | 68 ---- .../ui/binding/config/package.html | 7 + .../ui/binding/support/BindingRule.java | 24 ++ .../ui/binding/support/GenericBinder.java | 141 +++++++- .../ui/binding/support/PropertyBinding.java | 49 +-- ...lt.java => PropertyNotEditableResult.java} | 4 +- .../ui/binding/support/PropertyPath.java | 8 +- .../config/BindingRuleBuilderTests.java | 314 ------------------ .../binding/support/GenericBinderTests.java | 1 + 15 files changed, 237 insertions(+), 588 deletions(-) delete mode 100644 org.springframework.context/src/main/java/org/springframework/ui/binding/config/BindingRule.java delete mode 100644 org.springframework.context/src/main/java/org/springframework/ui/binding/config/BindingRules.java delete mode 100644 org.springframework.context/src/main/java/org/springframework/ui/binding/config/BindingRulesBuilder.java create mode 100644 org.springframework.context/src/main/java/org/springframework/ui/binding/config/Condition.java delete mode 100644 org.springframework.context/src/main/java/org/springframework/ui/binding/config/ConfigurableBindingRule.java create mode 100644 org.springframework.context/src/main/java/org/springframework/ui/binding/config/package.html create mode 100644 org.springframework.context/src/main/java/org/springframework/ui/binding/support/BindingRule.java rename org.springframework.context/src/main/java/org/springframework/ui/binding/support/{PropertyNotWriteableResult.java => PropertyNotEditableResult.java} (90%) delete mode 100644 org.springframework.context/src/test/java/org/springframework/ui/binding/config/BindingRuleBuilderTests.java 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/Binding.java index a8f477d2dc0..63f76d2e2e1 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/Binding.java @@ -42,7 +42,7 @@ public interface Binding { /** * If this Binding is enabled. - * Used to determine if the user can interact with the field. + * 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. */ boolean isEnabled(); @@ -59,7 +59,7 @@ public interface Binding { * Sets to {@link BindingStatus#DIRTY} if succeeds. * Sets to {@link BindingStatus#INVALID_SOURCE_VALUE} if fails. * @param sourceValue - * @throws IllegalStateException if {@link #isEditable()} + * @throws IllegalStateException if not editable or not enabled */ void applySourceValue(Object sourceValue); @@ -87,7 +87,7 @@ public interface Binding { * Commit the buffered value to the model. * Sets to {@link BindingStatus#COMMITTED} if succeeds. * Sets to {@link BindingStatus#COMMIT_FAILURE} if fails. - * @throws IllegalStateException if not {@link BindingStatus#DIRTY} or {@link #isEditable()} + * @throws IllegalStateException if not editable, not enabled, or not dirty */ void commit(); @@ -104,10 +104,11 @@ public interface Binding { /** * Get a Binding to a nested property value. - * @param nestedProperty the nested property name, such as "foo"; should not be a property path like "foo.bar" + * @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 */ - Binding getBinding(String nestedProperty); + Binding getBinding(String property); /** * If bound to an indexable Collection, either a {@link java.util.List} or an array. @@ -118,6 +119,7 @@ public interface Binding { * If a List, get a Binding to a element in the List. * @param index the element index * @return the indexed binding + * @throws IllegalStateException if not a list */ Binding getListElementBinding(int index); @@ -130,6 +132,7 @@ public interface Binding { * If a Map, get a Binding to a value in the Map. * @param key the map key * @return the keyed binding + * @throws IllegalStateException if not a map */ Binding getMapValueBinding(Object key); @@ -160,7 +163,6 @@ public interface Binding { /** * Set the model value. - * @throws IllegalStateException if this binding is read only */ void setValue(Object value); } diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/config/BindingRule.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/config/BindingRule.java deleted file mode 100644 index c020c09cb76..00000000000 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/config/BindingRule.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.springframework.ui.binding.config; - -import org.springframework.ui.format.Formatter; - -public interface BindingRule { - - String getPropertyPath(); - - Formatter getFormatter(); - - boolean isRequired(); - - boolean isCollectionBinding(); - - Formatter getKeyFormatter(); - - Formatter getValueFormatter(); - -} \ No newline at end of file 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/BindingRuleConfiguration.java index 7360d2d332c..a2cfe6b948b 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/BindingRuleConfiguration.java @@ -25,27 +25,31 @@ public interface BindingRuleConfiguration { /** * Set the Formatter to use to format bound property values. - * If a collection property, this formatter is used to format the Collection as a String. - * Default is null. */ BindingRuleConfiguration formatWith(Formatter formatter); /** - * If a indexable map property, set the Formatter to use to format map key indexes. - * Default is null. + * If a map property, set the Formatter to use to format map keys. */ BindingRuleConfiguration formatKeysWith(Formatter formatter); /** - * If an indexable list or map property, set the Formatter to use to format indexed elements. - * Default is null. + * If an list or map property, set the Formatter to use to format indexed elements. */ BindingRuleConfiguration formatElementsWith(Formatter formatter); - - /** - * Mark the binding as read only. - * A read-only binding cannot have source values applied and cannot be committed. - */ - BindingRuleConfiguration readOnly(); + /** + * Set when the binding is editable. + */ + BindingRuleConfiguration editableWhen(Condition condition); + + /** + * Set when the binding is enabled. + */ + BindingRuleConfiguration enabledWhen(Condition condition); + + /** + * Set when the binding is visible. + */ + BindingRuleConfiguration visibleWhen(Condition condition); } \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/config/BindingRules.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/config/BindingRules.java deleted file mode 100644 index 02278280123..00000000000 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/config/BindingRules.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.springframework.ui.binding.config; - -import java.util.List; - -public interface BindingRules extends List { - - Class getModelType(); - -} diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/config/BindingRulesBuilder.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/config/BindingRulesBuilder.java deleted file mode 100644 index 3025bb37cf5..00000000000 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/config/BindingRulesBuilder.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.springframework.ui.binding.config; - -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; - -import org.springframework.core.GenericCollectionTypeResolver; -import org.springframework.util.Assert; - -/** - * Builder for constructing the rules for binding to a model. - * @author Keith Donald - * @param the model type - */ -public class BindingRulesBuilder { - - private BindingRules bindingRules; - - /** - * Creates a new BindingRuleBuilder. - * @param modelType the type of model to build binding rules against - */ - public BindingRulesBuilder(Class modelType) { - Assert.notNull(modelType, "The model type is required"); - bindingRules = new ArrayListBindingRules(modelType); - } - - /** - * Creates a rule for binding to the model property. - * @param propertyPath the model property path - * @return allows additional binding configuration options to be specified fluently - * @throws IllegalArgumentException if the property path is invalid given the modelType - */ - public BindingRuleConfiguration bind(String propertyPath) { - boolean collectionBinding = validate(propertyPath); - ConfigurableBindingRule rule = new ConfigurableBindingRule(propertyPath); - if (collectionBinding) { - rule.markCollectionBinding(); - } - bindingRules.add(rule); - return rule; - } - - /** - * The built list of binding rules. - * Call after recording {@link #bind(String)} instructions. - */ - public BindingRules getBindingRules() { - return bindingRules; - } - - private boolean validate(String propertyPath) { - boolean collectionBinding = false; - String[] props = propertyPath.split("\\."); - if (props.length == 0) { - props = new String[] { propertyPath }; - } - Class modelType = bindingRules.getModelType(); - for (int i = 0; i < props.length; i ++) { - String prop = props[i]; - PropertyDescriptor[] propDescs = getBeanInfo(modelType).getPropertyDescriptors(); - boolean found = false; - for (PropertyDescriptor propDesc : propDescs) { - if (prop.equals(propDesc.getName())) { - found = true; - Class propertyType = propDesc.getPropertyType(); - if (Collection.class.isAssignableFrom(propertyType)) { - modelType = GenericCollectionTypeResolver.getCollectionReturnType(propDesc.getReadMethod()); - if (i == (props.length - 1)) { - collectionBinding = true; - } - } else if (Map.class.isAssignableFrom(propertyType)) { - modelType = GenericCollectionTypeResolver.getMapValueReturnType(propDesc.getReadMethod()); - if (i == (props.length - 1)) { - collectionBinding = true; - } - } else { - modelType = propertyType; - } - break; - } - } - if (!found) { - if (props.length > 1) { - throw new IllegalArgumentException("No property named '" + prop + "' found on model class [" + modelType.getName() + "] as part of property path '" + propertyPath + "'"); - } else { - throw new IllegalArgumentException("No property named '" + prop + "' found on model class [" + modelType.getName() + "]"); - } - } - } - return collectionBinding; - } - - private BeanInfo getBeanInfo(Class clazz) { - try { - return Introspector.getBeanInfo(clazz); - } catch (IntrospectionException e) { - throw new IllegalStateException("Unable to introspect model type " + clazz); - } - } - - @SuppressWarnings("serial") - static class ArrayListBindingRules extends ArrayList implements BindingRules { - - private Class modelType; - - public ArrayListBindingRules(Class modelType) { - this.modelType = modelType; - } - - public Class getModelType() { - return modelType; - } - - } -} diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/config/Condition.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/config/Condition.java new file mode 100644 index 00000000000..7efde4bb2df --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/config/Condition.java @@ -0,0 +1,19 @@ +package org.springframework.ui.binding.config; + +public interface Condition { + + boolean isTrue(); + + static final Condition ALWAYS_TRUE = new Condition() { + public boolean isTrue() { + return true; + } + }; + + static final Condition ALWAYS_FALSE = new Condition() { + public boolean isTrue() { + return false; + } + }; + +} diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/config/ConfigurableBindingRule.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/config/ConfigurableBindingRule.java deleted file mode 100644 index d87fa34e87b..00000000000 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/config/ConfigurableBindingRule.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.springframework.ui.binding.config; - -import org.springframework.ui.format.Formatter; - -public class ConfigurableBindingRule implements BindingRuleConfiguration, BindingRule { - - private String propertyPath; - - private Formatter formatter; - - private boolean required; - - private boolean collectionBinding; - - private Formatter elementFormatter; - - public ConfigurableBindingRule(String propertyPath) { - this.propertyPath = propertyPath; - } - - // implementing BindingRuleConfiguration - - public BindingRuleConfiguration formatWith(Formatter formatter) { - this.formatter = formatter; - return this; - } - - public BindingRuleConfiguration required() { - this.required = true; - return this; - } - - public BindingRuleConfiguration formatElementsWith(Formatter formatter) { - this.elementFormatter = formatter; - return this; - } - - // implementing BindingRule - - public String getPropertyPath() { - return propertyPath; - } - - public Formatter getFormatter() { - return formatter; - } - - public boolean isRequired() { - return required; - } - - public boolean isCollectionBinding() { - return collectionBinding; - } - - public void markCollectionBinding() { - this.collectionBinding = true; - } - - public Formatter getValueFormatter() { - return elementFormatter; - } - - public Formatter getKeyFormatter() { - return null; - } - -} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/config/package.html b/org.springframework.context/src/main/java/org/springframework/ui/binding/config/package.html new file mode 100644 index 00000000000..6dff74abb93 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/config/package.html @@ -0,0 +1,7 @@ + + +

+Binding configuration SPI. +

+ + \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/BindingRule.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/BindingRule.java new file mode 100644 index 00000000000..3bfe4ec2b41 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/BindingRule.java @@ -0,0 +1,24 @@ +package org.springframework.ui.binding.support; + +import org.springframework.ui.binding.Binding; +import org.springframework.ui.binding.config.Condition; +import org.springframework.ui.format.Formatter; + +public interface BindingRule { + + Formatter getFormatter(); + + Formatter getKeyFormatter(); + + Formatter getElementFormatter(); + + Condition getEditableCondition(); + + Condition getEnabledCondition(); + + Condition getVisibleCondition(); + + // TODO - does this belong here? + Binding getBinding(String property, Object model); + +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericBinder.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericBinder.java index a8e6c7da0c4..dba6d604d93 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericBinder.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericBinder.java @@ -27,6 +27,8 @@ import org.springframework.ui.binding.BindingResult; import org.springframework.ui.binding.BindingResults; import org.springframework.ui.binding.Binding.BindingStatus; import org.springframework.ui.binding.config.BindingRuleConfiguration; +import org.springframework.ui.binding.config.Condition; +import org.springframework.ui.format.Formatter; import org.springframework.util.Assert; /** @@ -41,8 +43,8 @@ public class GenericBinder implements Binder { private Object model; - private Map bindings; - + private Map bindingRules; + private TypeConverter typeConverter; private MessageSource messageSource; @@ -54,7 +56,7 @@ public class GenericBinder implements Binder { public GenericBinder(Object model) { Assert.notNull(model, "The model to bind to is required"); this.model = model; - bindings = new HashMap(); + bindingRules = new HashMap(); typeConverter = new DefaultTypeConverter(); } @@ -80,11 +82,16 @@ public class GenericBinder implements Binder { /** * - * @param propertyPath binding rule property path in format prop.nestedListProp[].nestedMapProp[].nestedProp + * @param propertyPath binding rule property path in format prop.nestedProp * @return */ public BindingRuleConfiguration bindingRule(String propertyPath) { - return null; + PropertyPath path = new PropertyPath(propertyPath); + GenericBindingRule rule = getBindingRule(path.getFirstElement().getValue()); + for (PropertyPathElement element : path.getNestedElements()) { + rule = rule.getBindingRule(element.getValue()); + } + return rule; } // implementing Binder @@ -95,11 +102,7 @@ public class GenericBinder implements Binder { public Binding getBinding(String property) { PropertyPath path = new PropertyPath(property); - Binding binding = bindings.get(path.getFirstElement().getValue()); - if (binding == null) { - binding = new PropertyBinding(path.getFirstElement().getValue(), model, typeConverter); - bindings.put(path.getFirstElement().getValue(), binding); - } + Binding binding = getBindingRule(path.getFirstElement().getValue()).getBinding(model); for (PropertyPathElement element : path.getNestedElements()) { if (element.isIndex()) { if (binding.isMap()) { @@ -142,11 +145,20 @@ public class GenericBinder implements Binder { // internal helpers + private GenericBindingRule getBindingRule(String property) { + GenericBindingRule rule = bindingRules.get(property); + if (rule == null) { + rule = new GenericBindingRule(property); + bindingRules.put(property, rule); + } + return rule; + } + private BindingResult bind(Map.Entry sourceValue, Binding binding) { String property = sourceValue.getKey(); Object value = sourceValue.getValue(); - if (binding.isEditable()) { - return new PropertyNotWriteableResult(property, value, messageSource); + if (!binding.isEditable()) { + return new PropertyNotEditableResult(property, value, messageSource); } else { binding.applySourceValue(value); if (binding.getStatus() == BindingStatus.DIRTY) { @@ -156,4 +168,109 @@ public class GenericBinder implements Binder { } } + @SuppressWarnings("unchecked") + class GenericBindingRule implements BindingRule, BindingRuleConfiguration { + + private String property; + + private Formatter formatter = DefaultFormatter.INSTANCE; + + private Formatter elementFormatter = DefaultFormatter.INSTANCE; + + private Formatter keyFormatter = DefaultFormatter.INSTANCE; + + private Condition editableCondition = Condition.ALWAYS_TRUE; + + private Condition enabledCondition = Condition.ALWAYS_TRUE; + + private Condition visibleCondition = Condition.ALWAYS_TRUE; + + private Map nestedBindingRules; + + private Binding binding; + + public GenericBindingRule(String property) { + this.property = property; + } + + // implementing BindingRule + + public Binding getBinding(String property, Object model) { + return getBindingRule(property).getBinding(model); + } + + public Formatter getFormatter() { + return formatter; + } + + public Formatter getElementFormatter() { + return elementFormatter; + } + + public Formatter getKeyFormatter() { + return keyFormatter; + } + + public Condition getEnabledCondition() { + return enabledCondition; + } + + public Condition getEditableCondition() { + return editableCondition; + } + + public Condition getVisibleCondition() { + return visibleCondition; + } + + // implementing BindingRuleConfiguration + + public BindingRuleConfiguration formatWith(Formatter formatter) { + this.formatter = formatter; + return this; + } + + public BindingRuleConfiguration formatElementsWith(Formatter formatter) { + elementFormatter = formatter; + return this; + } + + public BindingRuleConfiguration formatKeysWith(Formatter formatter) { + keyFormatter = formatter; + return this; + } + + public BindingRuleConfiguration editableWhen(Condition condition) { + editableCondition = condition; + return this; + } + + public BindingRuleConfiguration enabledWhen(Condition condition) { + enabledCondition = condition; + return this; + } + + public BindingRuleConfiguration visibleWhen(Condition condition) { + visibleCondition = condition; + return this; + } + + GenericBindingRule getBindingRule(String property) { + GenericBindingRule rule = nestedBindingRules.get(property); + if (rule == null) { + rule = new GenericBindingRule(property); + nestedBindingRules.put(property, rule); + } + return rule; + } + + Binding getBinding(Object model) { + if (binding == null) { + binding = new PropertyBinding(property, model, typeConverter, this); + } + return binding; + } + + } + } diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyBinding.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyBinding.java index 40bee219729..a7da51bd822 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyBinding.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyBinding.java @@ -36,16 +36,12 @@ public class PropertyBinding implements Binding { private Object model; - private Formatter valueFormatter = DefaultFormatter.INSTANCE; - - private Formatter mapKeyFormatter = DefaultFormatter.INSTANCE; - - private Formatter indexedValueFormatter = DefaultFormatter.INSTANCE; - - private PropertyDescriptor propertyDescriptor; - private TypeConverter typeConverter; + private BindingRule bindingRule; + + private PropertyDescriptor propertyDescriptor; + private Object sourceValue; @SuppressWarnings("unused") @@ -55,11 +51,11 @@ public class PropertyBinding implements Binding { private BindingStatus status; - public PropertyBinding(String property, Object model, TypeConverter typeConverter) { - this.propertyDescriptor = findPropertyDescriptor(property, model); - this.property = property; + public PropertyBinding(String property, Object model, TypeConverter typeConverter, BindingRule bindingRule) { + initProperty(property, model); this.model = model; this.typeConverter = typeConverter; + this.bindingRule = bindingRule; this.buffer = new ValueBuffer(getModel()); status = BindingStatus.CLEAN; } @@ -75,15 +71,15 @@ public class PropertyBinding implements Binding { } public boolean isEditable() { - return propertyDescriptor.getWriteMethod() != null || !markedNotEditable(); + return isWriteableProperty() && bindingRule.getEditableCondition().isTrue(); } public boolean isEnabled() { - return true; + return bindingRule.getEnabledCondition().isTrue(); } public boolean isVisible() { - return true; + return bindingRule.getVisibleCondition().isTrue(); } public void applySourceValue(Object sourceValue) { @@ -91,7 +87,7 @@ public class PropertyBinding implements Binding { assertEnabled(); if (sourceValue instanceof String) { try { - buffer.setValue(valueFormatter.parse((String) sourceValue, getLocale())); + buffer.setValue(bindingRule.getFormatter().parse((String) sourceValue, getLocale())); sourceValue = null; status = BindingStatus.DIRTY; } catch (ParseException e) { @@ -101,7 +97,7 @@ public class PropertyBinding implements Binding { } } else if (sourceValue instanceof String[]) { String[] sourceValues = (String[]) sourceValue; - Class parsedType = getFormattedObjectType(indexedValueFormatter.getClass()); + Class parsedType = getFormattedObjectType(bindingRule.getElementFormatter().getClass()); if (parsedType == null) { parsedType = String.class; } @@ -109,7 +105,7 @@ public class PropertyBinding implements Binding { for (int i = 0; i < sourceValues.length; i++) { Object parsedValue; try { - parsedValue = indexedValueFormatter.parse(sourceValues[i], LocaleContextHolder.getLocale()); + parsedValue = bindingRule.getElementFormatter().parse(sourceValues[i], LocaleContextHolder.getLocale()); Array.set(parsed, i, parsedValue); } catch (ParseException e) { this.sourceValue = sourceValue; @@ -242,12 +238,12 @@ public class PropertyBinding implements Binding { }; } - public Binding getBinding(String nestedProperty) { + public Binding getBinding(String property) { assertScalarProperty(); if (getValue() == null) { createValue(); } - return new PropertyBinding(nestedProperty, getValue(), typeConverter); + return bindingRule.getBinding(property, getValue()); } public boolean isList() { @@ -268,7 +264,7 @@ public class PropertyBinding implements Binding { assertMapProperty(); if (key instanceof String) { try { - key = mapKeyFormatter.parse((String) key, getLocale()); + key = bindingRule.getKeyFormatter().parse((String) key, getLocale()); } catch (ParseException e) { throw new IllegalArgumentException("Invald key", e); } @@ -280,9 +276,9 @@ public class PropertyBinding implements Binding { public String formatValue(Object value) { Formatter formatter; if (isList() || isMap()) { - formatter = indexedValueFormatter; + formatter = bindingRule.getElementFormatter(); } else { - formatter = valueFormatter; + formatter = bindingRule.getFormatter(); } Class formattedType = getFormattedObjectType(formatter.getClass()); value = typeConverter.convert(value, formattedType); @@ -291,6 +287,11 @@ public class PropertyBinding implements Binding { // internal helpers + private void initProperty(String property, Object model) { + this.propertyDescriptor = findPropertyDescriptor(property, model); + this.property = property; + } + private PropertyDescriptor findPropertyDescriptor(String property, Object model) { PropertyDescriptor[] propDescs = getBeanInfo(model.getClass()).getPropertyDescriptors(); for (PropertyDescriptor propDesc : propDescs) { @@ -395,7 +396,7 @@ public class PropertyBinding implements Binding { } } - private boolean markedNotEditable() { - return false; + private boolean isWriteableProperty() { + return propertyDescriptor.getWriteMethod() != null; } } \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotWriteableResult.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotEditableResult.java similarity index 90% rename from org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotWriteableResult.java rename to org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotEditableResult.java index dfc010fba56..a5db886ea1f 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotWriteableResult.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotEditableResult.java @@ -11,7 +11,7 @@ import org.springframework.ui.binding.BindingResult; import org.springframework.ui.message.MessageBuilder; import org.springframework.ui.message.ResolvableArgument; -class PropertyNotWriteableResult implements BindingResult { +class PropertyNotEditableResult implements BindingResult { private String property; @@ -19,7 +19,7 @@ class PropertyNotWriteableResult implements BindingResult { private MessageSource messageSource; - public PropertyNotWriteableResult(String property, Object sourceValue, MessageSource messageSource) { + public PropertyNotEditableResult(String property, Object sourceValue, MessageSource messageSource) { this.property = property; this.sourceValue = sourceValue; this.messageSource = messageSource; 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/PropertyPath.java index 3df1f37a6e7..48af4193ce6 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/PropertyPath.java @@ -5,10 +5,10 @@ package org.springframework.ui.binding.support; import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; - -public class PropertyPath { +public class PropertyPath implements Iterable { private List elements = new ArrayList(); @@ -41,4 +41,8 @@ public class PropertyPath { } } + public Iterator iterator() { + return elements.iterator(); + } + } \ No newline at end of file diff --git a/org.springframework.context/src/test/java/org/springframework/ui/binding/config/BindingRuleBuilderTests.java b/org.springframework.context/src/test/java/org/springframework/ui/binding/config/BindingRuleBuilderTests.java deleted file mode 100644 index e16408c67ee..00000000000 --- a/org.springframework.context/src/test/java/org/springframework/ui/binding/config/BindingRuleBuilderTests.java +++ /dev/null @@ -1,314 +0,0 @@ -package org.springframework.ui.binding.config; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.math.BigDecimal; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import org.junit.Ignore; -import org.junit.Test; -import org.springframework.ui.format.Formatter; -import org.springframework.ui.format.number.CurrencyFormatter; - -public class BindingRuleBuilderTests { - - @Test - @Ignore - public void createBindingRules() { - BindingRulesBuilder builder = new BindingRulesBuilder(TestBean.class); - // TODO ability to add nested rules? - // TODO ability to format map keys and values? - builder.bind("string"); - builder.bind("integer").required(); - builder.bind("currency").formatWith(new CurrencyFormatter()).required(); - builder.bind("addresses").formatWith(new AddressListFormatter()).formatElementsWith(new AddressFormatter()).required(); - builder.bind("addresses.street"); - builder.bind("addresses.city"); - builder.bind("addresses.state"); - builder.bind("addresses.zip"); - builder.bind("favoriteFoodsByGroup").formatWith(new FavoriteFoodGroupMapFormatter()).formatElementsWith(new FoodEntryFormatter()); - builder.bind("favoriteFoodsByGroup.name"); - List rules = builder.getBindingRules(); - assertEquals(10, rules.size()); - assertEquals("string", rules.get(0).getPropertyPath()); - assertNull(rules.get(0).getFormatter()); - assertFalse(rules.get(0).isRequired()); - assertFalse(rules.get(0).isCollectionBinding()); - assertNull(rules.get(0).getValueFormatter()); - assertEquals("integer", rules.get(1).getPropertyPath()); - assertNull(rules.get(1).getFormatter()); - assertTrue(rules.get(1).isRequired()); - assertFalse(rules.get(1).isCollectionBinding()); - assertNull(rules.get(1).getValueFormatter()); - assertEquals("currency", rules.get(2).getPropertyPath()); - assertTrue(rules.get(2).getFormatter() instanceof CurrencyFormatter); - assertFalse(rules.get(2).isRequired()); - assertFalse(rules.get(2).isCollectionBinding()); - assertNull(rules.get(2).getValueFormatter()); - assertEquals("addresses", rules.get(3).getPropertyPath()); - assertTrue(rules.get(3).getFormatter() instanceof AddressListFormatter); - assertFalse(rules.get(3).isRequired()); - assertTrue(rules.get(3).isCollectionBinding()); - assertTrue(rules.get(3).getValueFormatter() instanceof AddressFormatter); - assertTrue(rules.get(8).getFormatter() instanceof FavoriteFoodGroupMapFormatter); - assertFalse(rules.get(8).isRequired()); - assertTrue(rules.get(8).isCollectionBinding()); - assertTrue(rules.get(8).getValueFormatter() instanceof FoodEntryFormatter); - } - - @Test(expected=IllegalArgumentException.class) - public void createBindingRulesInvalidProperty() { - BindingRulesBuilder builder = new BindingRulesBuilder(TestBean.class); - builder.bind("bogus"); - } - - @Test(expected=IllegalArgumentException.class) - public void createBindingRulesInvalidNestedCollectionProperty() { - BindingRulesBuilder builder = new BindingRulesBuilder(TestBean.class); - builder.bind("addresses.bogus"); - } - - @Test(expected=IllegalArgumentException.class) - public void createBindingRulesInvalidNestedMapProperty() { - BindingRulesBuilder builder = new BindingRulesBuilder(TestBean.class); - builder.bind("favoriteFoodsByGroup.bogus"); - } - - public static enum FooEnum { - BAR, BAZ, BOOP; - } - - public static enum FoodGroup { - DAIRY, VEG, FRUIT, BREAD, MEAT - } - - public static class TestBean { - private String string; - private int integer; - private Date date; - private FooEnum foo; - private BigDecimal currency; - private List foos; - private List
addresses; - private Map favoriteFoodsByGroup; - private Address primaryAddress; - - public TestBean() { - } - - public String getString() { - return string; - } - - public void setString(String string) { - this.string = string; - } - - public int getInteger() { - return integer; - } - - public void setInteger(int integer) { - this.integer = integer; - } - - public Date getDate() { - return date; - } - - public void setDate(Date date) { - this.date = date; - } - - public FooEnum getFoo() { - return foo; - } - - public void setFoo(FooEnum foo) { - this.foo = foo; - } - - public BigDecimal getCurrency() { - return currency; - } - - public void setCurrency(BigDecimal currency) { - this.currency = currency; - } - - public List getFoos() { - return foos; - } - - public void setFoos(List foos) { - this.foos = foos; - } - - public List
getAddresses() { - return addresses; - } - - public void setAddresses(List
addresses) { - this.addresses = addresses; - } - - public Map getFavoriteFoodsByGroup() { - return favoriteFoodsByGroup; - } - - public void setFavoriteFoodsByGroup(Map favoriteFoodsByGroup) { - this.favoriteFoodsByGroup = favoriteFoodsByGroup; - } - - public Address getPrimaryAddress() { - return primaryAddress; - } - - public void setPrimaryAddress(Address primaryAddress) { - this.primaryAddress = primaryAddress; - } - - } - - public static class Food { - private String name; - - public Food(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - - } - - public static class Address { - private String street; - private String city; - private String state; - private String zip; - private String country; - - public String getStreet() { - return street; - } - - public void setStreet(String street) { - this.street = street; - } - - public String getCity() { - return city; - } - - public void setCity(String city) { - this.city = city; - } - - public String getState() { - return state; - } - - public void setState(String state) { - this.state = state; - } - - public String getZip() { - return zip; - } - - public void setZip(String zip) { - this.zip = zip; - } - - public String getCountry() { - return country; - } - - public void setCountry(String country) { - this.country = country; - } - - } - - public static class AddressFormatter implements Formatter
{ - - public String format(Address address, Locale locale) { - return address.getStreet() + ":" + address.getCity() + ":" + address.getState() + ":" + address.getZip(); - } - - public Address parse(String formatted, Locale locale) throws ParseException { - Address address = new Address(); - String[] fields = formatted.split(":"); - address.setStreet(fields[0]); - address.setCity(fields[1]); - address.setState(fields[2]); - address.setZip(fields[3]); - return address; - } - - } - - public static class AddressListFormatter implements Formatter> { - - public String format(List
addresses, Locale locale) { - StringBuilder builder = new StringBuilder(); - for (Address address : addresses) { - builder.append(new AddressFormatter().format(address, locale)); - builder.append(","); - } - return builder.toString(); - } - - public List
parse(String formatted, Locale locale) throws ParseException { - String[] fields = formatted.split(","); - List
addresses = new ArrayList
(fields.length); - for (String field : fields) { - addresses.add(new AddressFormatter().parse(field, locale)); - } - return addresses; - } - - } - - public static class FavoriteFoodGroupMapFormatter implements Formatter> { - - public String format(Map map, Locale locale) { - StringBuilder builder = new StringBuilder(); - return builder.toString(); - } - - public Map parse(String formatted, Locale locale) throws ParseException { - Map map = new HashMap(); - return map; - } - - } - - public static class FoodEntryFormatter implements Formatter> { - - public String format(Map.Entry food, Locale locale) { - return null; - } - - public Map.Entry parse(String formatted, Locale locale) throws ParseException { - return null; - } - - } -} 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/support/GenericBinderTests.java index 3f0a166c7e9..461bc9b59c9 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/support/GenericBinderTests.java @@ -55,6 +55,7 @@ public class GenericBinderTests { values.put("integer", "3"); values.put("foo", "BAR"); BindingResults results = binder.bind(values); + System.out.println(results); assertEquals(3, results.size()); assertEquals("string", results.get(0).getProperty());