diff --git a/org.springframework.context/src/main/java/org/springframework/ui/WebBindAndValidateLifecycle.java b/org.springframework.context/src/main/java/org/springframework/ui/WebBindAndValidateLifecycle.java new file mode 100644 index 00000000000..2e913ef1353 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/WebBindAndValidateLifecycle.java @@ -0,0 +1,59 @@ +/* + * 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; + +import java.util.Map; + +import org.springframework.ui.binding.BindingResults; +import org.springframework.ui.binding.UserValues; +import org.springframework.ui.binding.support.WebBinder; +import org.springframework.ui.message.MessageContext; +import org.springframework.ui.validation.Validator; + +public class WebBindAndValidateLifecycle { + + private WebBinder binder; + + @SuppressWarnings("unused") + private MessageContext messages; + + private ValidationDecider validationDecider = ValidationDecider.ALWAYS_VALIDATE; + + private Validator validator; + + public WebBindAndValidateLifecycle(Object model, MessageContext messages) { + this.binder = new WebBinder(model); + } + + public void execute(Map userMap) { + UserValues values = binder.createUserValues(userMap); + BindingResults results = binder.bind(values); + if (validationDecider.shouldValidateAfter(results)) { + validator.validate(binder.getModel(), results.successes().properties()); + } + } + + public interface ValidationDecider { + + boolean shouldValidateAfter(BindingResults results); + + public static ValidationDecider ALWAYS_VALIDATE = new ValidationDecider() { + public boolean shouldValidateAfter(BindingResults results) { + return true; + } + }; + } +} 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 158c915abf8..180f975f145 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 @@ -15,7 +15,6 @@ */ package org.springframework.ui.binding; -import java.util.List; import java.util.Map; import org.springframework.ui.format.AnnotationFormatterFactory; @@ -74,10 +73,10 @@ public interface Binder { /** * Bind values in the map to the properties of the model object. - * TODO consider returning BindingResults object that makes it easier to query/introspect results * @param values user-entered values to bind + * @return the results of the binding operation */ - List bind(UserValues values); + BindingResults bind(UserValues values); /** * Creates a {@link UserValue} list from a Map of user-submitted fields. diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingResults.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingResults.java new file mode 100644 index 00000000000..c4dbfa57c5b --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingResults.java @@ -0,0 +1,53 @@ +/* + * 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; + +import java.util.List; + +/** + * The results of a bind operation. + * @author Keith Donald + * @see Binder#bind(UserValues) + */ +public interface BindingResults extends Iterable { + + /** + * The subset of BindingResults that were successful. + */ + BindingResults successes(); + + /** + * The subset of BindingResults that failed. + */ + BindingResults failures(); + + /** + * The number of results. + */ + int size(); + + /** + * The BindingResult at the specified index. + * @throws IndexOutOfBoundsException if the index is out of bounds + */ + BindingResult get(int index); + + /** + * The list of properties for which binding was attempted. + */ + List properties(); + +} 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 8b46ed95712..c982325b03d 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 @@ -23,6 +23,7 @@ import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -48,6 +49,7 @@ import org.springframework.ui.binding.Binder; import org.springframework.ui.binding.Binding; import org.springframework.ui.binding.BindingConfiguration; import org.springframework.ui.binding.BindingResult; +import org.springframework.ui.binding.BindingResults; import org.springframework.ui.binding.FormatterRegistry; import org.springframework.ui.binding.UserValue; import org.springframework.ui.binding.UserValues; @@ -128,8 +130,8 @@ public class GenericBinder implements Binder { } } - public List bind(UserValues values) { - List results = new ArrayList(values.size()); + public BindingResults bind(UserValues values) { + ArrayListBindingResults results = new ArrayListBindingResults(values.size()); for (UserValue value : values) { BindingImpl binding = (BindingImpl) getBinding(value.getProperty()); results.add(binding.setValue(value.getValue())); @@ -147,6 +149,54 @@ public class GenericBinder implements Binder { // internal helpers + static class ArrayListBindingResults implements BindingResults { + + private List results; + + public ArrayListBindingResults(int size) { + results = new ArrayList(size); + } + + public void add(BindingResult result) { + results.add(result); + } + + // implementing Iterable + + public Iterator iterator() { + return results.iterator(); + } + + // implementing BindingResults + + public BindingResults successes() { + // TODO + return this; + } + + public BindingResults failures() { + // TODO + return this; + } + + public BindingResult get(int index) { + return results.get(index); + } + + public List properties() { + List properties = new ArrayList(results.size()); + for (BindingResult result : this) { + properties.add(result.getProperty()); + } + return properties; + } + + public int size() { + return results.size(); + } + + } + class BindingImpl implements Binding { private Expression property; 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 6a05cb0da13..e368e13f113 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 @@ -24,6 +24,7 @@ import org.springframework.ui.binding.Binder; import org.springframework.ui.binding.Binding; import org.springframework.ui.binding.BindingConfiguration; import org.springframework.ui.binding.BindingResult; +import org.springframework.ui.binding.BindingResults; import org.springframework.ui.binding.UserValues; import org.springframework.ui.format.AnnotationFormatterFactory; import org.springframework.ui.format.Formatter; @@ -55,7 +56,7 @@ public class GenericBinderTests { values.add("string", "test"); values.add("integer", "3"); values.add("foo", "BAR"); - List results = binder.bind(values); + BindingResults results = binder.bind(values); assertEquals(3, results.size()); assertEquals("string", results.get(0).getProperty()); @@ -85,7 +86,7 @@ public class GenericBinderTests { // bad value values.add("integer", "bogus"); values.add("foo", "BAR"); - List results = binder.bind(values); + BindingResults results = binder.bind(values); assertEquals(3, results.size()); assertTrue(results.get(1).isError()); assertEquals("typeConversionFailure", results.get(1).getErrorCode()); @@ -127,7 +128,7 @@ public class GenericBinderTests { @Test public void bindSingleValuePropertyNotFound() throws ParseException { - List results = binder.bind(UserValues.single("bogus", "2009-06-01")); + BindingResults results = binder.bind(UserValues.single("bogus", "2009-06-01")); assertEquals(1, results.size()); assertTrue(results.get(0).isError()); assertEquals("propertyNotFound", results.get(0).getErrorCode()); @@ -139,7 +140,7 @@ public class GenericBinderTests { userMap.put("string", "test"); userMap.put("integer", "3"); UserValues values = binder.createUserValues(userMap); - List results = binder.bind(values); + BindingResults results = binder.bind(values); assertEquals(2, results.size()); assertEquals("test", results.get(0).getUserValue()); assertEquals("3", results.get(1).getUserValue()); @@ -245,7 +246,7 @@ public class GenericBinderTests { values.add("addresses[5].state", "FL"); values.add("addresses[5].zip", "32901"); - List results = binder.bind(values); + BindingResults results = binder.bind(values); Assert.assertEquals(6, bean.addresses.size()); Assert.assertEquals("Palm Bay", bean.addresses.get(1).city); Assert.assertNotNull(bean.addresses.get(2)); 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/support/WebBinderTests.java index 4abb54cfebb..3a2dc7dc3c9 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/support/WebBinderTests.java @@ -16,7 +16,7 @@ import org.junit.Test; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.ui.binding.Binder; import org.springframework.ui.binding.BindingConfiguration; -import org.springframework.ui.binding.BindingResult; +import org.springframework.ui.binding.BindingResults; import org.springframework.ui.binding.UserValues; import org.springframework.ui.format.date.DateFormatter; import org.springframework.ui.format.number.CurrencyFormat; @@ -50,7 +50,7 @@ public class WebBinderTests { userMap.put("_currency", "doesn't matter"); userMap.put("_addresses", "doesn't matter"); UserValues values = binder.createUserValues(userMap); - List results = binder.bind(values); + BindingResults results = binder.bind(values); assertEquals(6, results.size()); assertEquals("test", results.get(0).getUserValue()); assertEquals(null, results.get(1).getUserValue()); diff --git a/org.springframework.context/src/test/java/org/springframework/ui/validation/Validator.java b/org.springframework.context/src/test/java/org/springframework/ui/validation/Validator.java new file mode 100644 index 00000000000..2bb132c7459 --- /dev/null +++ b/org.springframework.context/src/test/java/org/springframework/ui/validation/Validator.java @@ -0,0 +1,9 @@ +package org.springframework.ui.validation; + +import java.util.List; + +public interface Validator { + + void validate(Object model, List properties); + +}