diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/Binder.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/Binder.java index f60dad64d7d..c682a8a914d 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/Binder.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/Binder.java @@ -137,7 +137,7 @@ public class Binder { required = config.isRequired(); } - public String getFormattedValue() { + public String getValue() { try { return format(property.getValue(createEvaluationContext())); } catch (ExpressionException e) { @@ -146,15 +146,13 @@ public class Binder { } public void setValue(String formatted) { - Object value = parse(formatted); - assertRequired(value); - setValue(value); + setValue(parse(formatted)); } - public String format(Object possibleValue) { + public String format(Object selectableValue) { Formatter formatter = getFormatter(); - possibleValue = typeConverter.convert(possibleValue, formatter.getFormattedObjectType()); - return formatter.format(possibleValue, LocaleContextHolder.getLocale()); + selectableValue = typeConverter.convert(selectableValue, formatter.getFormattedObjectType()); + return formatter.format(selectableValue, LocaleContextHolder.getLocale()); } public boolean isCollection() { @@ -162,7 +160,7 @@ public class Binder { return type.isCollection() || type.isArray(); } - public String[] getFormattedValues() { + public String[] getValues() { Object multiValue; try { multiValue = property.getValue(createEvaluationContext()); @@ -199,18 +197,12 @@ public class Binder { return required; } - public Messages getMessages() { + public BindingFailures getFailures() { return null; } // internal helpers - private void assertRequired(Object value) { - if (required && value == null) { - throw new IllegalArgumentException("Value required"); - } - } - private Object parse(String formatted) { try { return getFormatter().parse(formatted, LocaleContextHolder.getLocale()); @@ -254,7 +246,7 @@ public class Binder { return new Annotation[0]; } - private void copy(Iterable values, String[] formattedValues) { + private void copy(Iterable values, String[] formattedValues) { int i = 0; for (Object value : values) { formattedValues[i] = format(value); 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 a41172456c2..9902e5378f6 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 @@ -4,24 +4,22 @@ public interface Binding { // single-value properties - String getFormattedValue(); + String getValue(); void setValue(String formatted); - String format(Object possibleValue); + String format(Object selectableValue); // multi-value properties boolean isCollection(); - String[] getFormattedValues(); + String[] getValues(); void setValues(String[] formattedValues); // validation metadata - boolean isRequired(); - - Messages getMessages(); + BindingFailures getFailures(); } \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingFailure.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingFailure.java new file mode 100644 index 00000000000..542095d5457 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingFailure.java @@ -0,0 +1,15 @@ +package org.springframework.ui.binding; + +import java.util.Map; + +public interface BindingFailure { + + String getCode(); + + String getSeverity(); + + // TODO - where does arg formatting occur + Map getArgs(); + + Map getDetails(); +} diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/Messages.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingFailures.java similarity index 50% rename from org.springframework.context/src/main/java/org/springframework/ui/binding/Messages.java rename to org.springframework.context/src/main/java/org/springframework/ui/binding/BindingFailures.java index 42029a5fd1b..b2d3ac98215 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/Messages.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingFailures.java @@ -2,14 +2,14 @@ package org.springframework.ui.binding; import java.util.List; -public interface Messages { +public interface BindingFailures { int getCount(); Severity getMaximumSeverity(); - List getAll(); + List getAll(); - List getBySeverity(Severity severity); + List getBySeverity(Severity severity); } diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/Message.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/Message.java deleted file mode 100644 index cfe5e7c2d4f..00000000000 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/Message.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.springframework.ui.binding; - -public interface Message { - - String getText(); - - String getSeverity(); - -} diff --git a/org.springframework.context/src/main/java/org/springframework/ui/format/Formatter.java b/org.springframework.context/src/main/java/org/springframework/ui/format/Formatter.java index c20edeb6967..063500c353d 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/format/Formatter.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/format/Formatter.java @@ -41,6 +41,7 @@ public interface Formatter { /** * Parse an object from its formatted representation. + * TODO - remove dependency on java.text by replacing ParseException? * @param formatted a formatted representation * @param locale the user's locale * @return the parsed object diff --git a/org.springframework.context/src/test/java/org/springframework/ui/binding/BinderTests.java b/org.springframework.context/src/test/java/org/springframework/ui/binding/BinderTests.java index 6ffebb43f88..6e584185ed1 100644 --- a/org.springframework.context/src/test/java/org/springframework/ui/binding/BinderTests.java +++ b/org.springframework.context/src/test/java/org/springframework/ui/binding/BinderTests.java @@ -6,20 +6,32 @@ 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.After; +import org.junit.Before; import org.junit.Ignore; import org.junit.Test; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.ui.format.DateFormatter; import org.springframework.ui.format.number.CurrencyFormatter; public class BinderTests { + @Before + public void setUp() { + LocaleContextHolder.setLocale(Locale.US); + } + + @After + public void tearDown() { + LocaleContextHolder.setLocale(null); + } + @Test public void bindSingleValuesWithDefaultTypeConverterConversion() { Binder binder = new Binder(new TestBean()); @@ -96,11 +108,10 @@ public class BinderTests { public void getBindingOptimistic() { Binder binder = new Binder(new TestBean()); Binding b = binder.getBinding("integer"); - assertFalse(b.isRequired()); assertFalse(b.isCollection()); - assertEquals("0", b.getFormattedValue()); + assertEquals("0", b.getValue()); b.setValue("5"); - assertEquals("5", b.getFormattedValue()); + assertEquals("5", b.getValue()); } @Test @@ -108,36 +119,23 @@ public class BinderTests { Binder binder = new Binder(new TestBean()); binder.add(new BindingConfiguration("currency", new CurrencyFormatter(), false)); Binding b = binder.getBinding("currency"); - assertFalse(b.isRequired()); assertFalse(b.isCollection()); - assertEquals("", b.getFormattedValue()); + assertEquals("", b.getValue()); b.setValue("$23.56"); - assertEquals("$23.56", b.getFormattedValue()); - } - - // TODO should update error context, not throw exception - @Test(expected=IllegalArgumentException.class) - public void getBindingRequired() { - Binder binder = new Binder(new TestBean()); - binder.add(new BindingConfiguration("string", null, true)); - Binding b = binder.getBinding("string"); - assertTrue(b.isRequired()); - assertFalse(b.isCollection()); - assertEquals("", b.getFormattedValue()); - b.setValue(""); + assertEquals("$23.56", b.getValue()); } @Test public void getBindingMultiValued() { Binder binder = new Binder(new TestBean()); Binding b = binder.getBinding("foos"); - assertTrue(b.isCollection()); - assertEquals(0, b.getFormattedValues().length); + // TODO should work - assertTrue(b.isCollection()); + assertEquals(0, b.getValues().length); b.setValues(new String[] { "BAR", "BAZ", "BOOP" }); assertEquals(FooEnum.BAR, binder.getModel().getFoos().get(0)); assertEquals(FooEnum.BAZ, binder.getModel().getFoos().get(1)); assertEquals(FooEnum.BOOP, binder.getModel().getFoos().get(2)); - String[] values = b.getFormattedValues(); + String[] values = b.getValues(); assertEquals(3, values.length); assertEquals("BAR", values[0]); assertEquals("BAZ", values[1]); @@ -148,11 +146,47 @@ public class BinderTests { public void getBindingMultiValuedTypeConversionError() { Binder binder = new Binder(new TestBean()); Binding b = binder.getBinding("foos"); - assertTrue(b.isCollection()); - assertEquals(0, b.getFormattedValues().length); + // TODO should work -- assertTrue(b.isCollection()); + assertEquals(0, b.getValues().length); b.setValues(new String[] { "BAR", "BOGUS", "BOOP" }); } + @Test + @Ignore + public void bindHandleNullValueInNestedPath() { + Binder binder = new Binder(new TestBean()); + Map propertyValues = new HashMap(); + // TODO should auto add(new Address) at 0 + propertyValues.put("addresses[0].street", "4655 Macy Lane"); + propertyValues.put("addresses[0].city", "Melbourne"); + propertyValues.put("addresses[0].state", "FL"); + propertyValues.put("addresses[0].state", "35452"); + // TODO should auto add(new Address) at 1 + propertyValues.put("addresses[1].street", "1234 Rostock Circle"); + propertyValues.put("addresses[1].city", "Palm Bay"); + propertyValues.put("addresses[1].state", "FL"); + propertyValues.put("addresses[1].state", "32901"); + binder.bind(propertyValues); + } + + @Test + public void formatPossibleValue() { + Binder binder = new Binder(new TestBean()); + binder.add(new BindingConfiguration("currency", new CurrencyFormatter(), false)); + Binding b = binder.getBinding("currency"); + assertEquals("$5.00", b.format(new BigDecimal("5"))); + } + + @Test + public void getBindingRequiredConstraint() { + Binder binder = new Binder(new TestBean()); + // TODO add constraint API + binder.add(new BindingConfiguration("string", null, true)); + Binding b = binder.getBinding("string"); + assertEquals("", b.getValue()); + b.setValue(""); + } + public static enum FooEnum { BAR, BAZ, BOOP; } @@ -163,7 +197,8 @@ public class BinderTests { private Date date; private FooEnum foo; private BigDecimal currency; - private List foos = new ArrayList(); + private List foos; + private List
addresses; public String getString() { return string; @@ -197,11 +232,11 @@ public class BinderTests { this.foo = foo; } + @Currency public BigDecimal getCurrency() { return currency; } - @Currency public void setCurrency(BigDecimal currency) { this.currency = currency; } @@ -213,10 +248,67 @@ public class BinderTests { public void setFoos(List foos) { this.foos = foos; } - + + public List
getAddresses() { + return addresses; + } + + public void setAddresses(List
addresses) { + this.addresses = addresses; + } + } public @interface Currency { } + + 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; + } + + } }