WICKET-5418 properly handle groups in NonNull constraint
Project: http://git-wip-us.apache.org/repos/asf/wicket/repo Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/9eb670b5 Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/9eb670b5 Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/9eb670b5 Branch: refs/heads/wicket-6.x Commit: 9eb670b5ad0d04e0a942db660b89befc0935f6ea Parents: 77065e7 Author: Igor Vaynberg <[email protected]> Authored: Fri Nov 15 15:41:43 2013 -0800 Committer: Igor Vaynberg <[email protected]> Committed: Fri Nov 15 15:41:43 2013 -0800 ---------------------------------------------------------------------- .../wicket-bean-validation/pom.xml | 6 + .../bean/validation/PropertyValidator.java | 72 ++++++-- .../validation/SessionLocaleInterpolator.java | 8 +- .../wicket/bean/validation/SizeTagModifier.java | 4 +- .../PropertyValidatorRequiredTest.java | 185 +++++++++++++++++++ 5 files changed, 255 insertions(+), 20 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/wicket/blob/9eb670b5/wicket-experimental/wicket-bean-validation/pom.xml ---------------------------------------------------------------------- diff --git a/wicket-experimental/wicket-bean-validation/pom.xml b/wicket-experimental/wicket-bean-validation/pom.xml index 9f3895c..460e7fc 100644 --- a/wicket-experimental/wicket-bean-validation/pom.xml +++ b/wicket-experimental/wicket-bean-validation/pom.xml @@ -25,5 +25,11 @@ <groupId>org.apache.wicket</groupId> <artifactId>wicket-core</artifactId> </dependency> + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-validator</artifactId> + <version>4.3.1.Final</version> + <scope>test</scope> + </dependency> </dependencies> </project> http://git-wip-us.apache.org/repos/asf/wicket/blob/9eb670b5/wicket-experimental/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/PropertyValidator.java ---------------------------------------------------------------------- diff --git a/wicket-experimental/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/PropertyValidator.java b/wicket-experimental/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/PropertyValidator.java index 032a818..e8e6376 100644 --- a/wicket-experimental/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/PropertyValidator.java +++ b/wicket-experimental/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/PropertyValidator.java @@ -1,6 +1,10 @@ package org.apache.wicket.bean.validation; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Set; import javax.validation.ConstraintViolation; @@ -62,8 +66,8 @@ public class PropertyValidator<T> extends Behavior implements IValidator<T> private final IModel<Class<?>[]> groups_; /** - * A flag that indicates whether {@linkplain #setComponentRequiredFlag()} - * has been called for this behavior. + * A flag that indicates whether {@linkplain #setComponentRequiredFlag()} has been called for + * this behavior. */ private boolean requiredFlagSet; @@ -132,8 +136,10 @@ public class PropertyValidator<T> extends Behavior implements IValidator<T> " can only be added to FormComponents"); } - // TODO add a validation key that appends the type so we can have different messages for - // @Size on String vs Collection - done but need to add a key for each superclass/interface + // TODO add a validation key that appends the type so we can have + // different messages for + // @Size on String vs Collection - done but need to add a key for each + // superclass/interface this.component = (FormComponent<T>)component; } @@ -144,10 +150,15 @@ public class PropertyValidator<T> extends Behavior implements IValidator<T> super.onConfigure(component); if (requiredFlagSet == false) { - // "Required" flag is calculated upon component's model property, so we must ensure, - // that model object is accessible (i.e. component is already added in a page). + // "Required" flag is calculated upon component's model property, so + // we must ensure, + // that model object is accessible (i.e. component is already added + // in a page). requiredFlagSet = true; - setComponentRequiredFlag(); + if (isRequired()) + { + this.component.setRequired(true); + } } } @@ -161,27 +172,57 @@ public class PropertyValidator<T> extends Behavior implements IValidator<T> } } - /** - * Marks the form component required if necessary - */ - private void setComponentRequiredFlag() + private List<NotNull> findNotNullConstraints() { BeanValidationContext config = BeanValidationConfiguration.get(); Validator validator = config.getValidator(); Property property = getProperty(); - // if the property has a NotNull constraint mark the form component required + List<NotNull> constraints = new ArrayList<NotNull>(); Iterator<ConstraintDescriptor<?>> it = new ConstraintIterator(validator, property); + while (it.hasNext()) { ConstraintDescriptor<?> desc = it.next(); if (desc.getAnnotation().annotationType().equals(NotNull.class)) { - component.setRequired(true); - break; + constraints.add((NotNull)desc.getAnnotation()); + } + } + + return constraints; + } + + boolean isRequired() + { + List<NotNull> constraints = findNotNullConstraints(); + + if (constraints.isEmpty()) + { + return false; + } + + HashSet<Class<?>> validatorGroups = new HashSet<Class<?>>(); + validatorGroups.addAll(Arrays.asList(getGroups())); + + for (NotNull constraint : constraints) + { + if (constraint.groups().length == 0 && validatorGroups.isEmpty()) + { + return true; + } + + for (Class<?> constraintGroup : constraint.groups()) + { + if (validatorGroups.contains(constraintGroup)) + { + return true; + } } } + + return false; } @Override @@ -194,7 +235,8 @@ public class PropertyValidator<T> extends Behavior implements IValidator<T> Validator validator = config.getValidator(); Property property = getProperty(); - // find any tag modifiers that apply to the constraints of the property being validated + // find any tag modifiers that apply to the constraints of the property + // being validated // and allow them to modify the component tag Iterator<ConstraintDescriptor<?>> it = new ConstraintIterator(validator, property, http://git-wip-us.apache.org/repos/asf/wicket/blob/9eb670b5/wicket-experimental/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/SessionLocaleInterpolator.java ---------------------------------------------------------------------- diff --git a/wicket-experimental/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/SessionLocaleInterpolator.java b/wicket-experimental/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/SessionLocaleInterpolator.java index 00aa506..f8927e2 100644 --- a/wicket-experimental/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/SessionLocaleInterpolator.java +++ b/wicket-experimental/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/SessionLocaleInterpolator.java @@ -20,7 +20,7 @@ public class SessionLocaleInterpolator implements MessageInterpolator * Constructor * * @param delegate - * the MessageInterpolator to delegate to + * the MessageInterpolator to delegate to */ public SessionLocaleInterpolator(MessageInterpolator delegate) { @@ -28,7 +28,8 @@ public class SessionLocaleInterpolator implements MessageInterpolator this.delegate = delegate; } - public String interpolate(final String messageTemplate, final MessageInterpolator.Context context) + public String interpolate(final String messageTemplate, + final MessageInterpolator.Context context) { final Locale locale = getLocale(); if (locale != null) @@ -41,7 +42,8 @@ public class SessionLocaleInterpolator implements MessageInterpolator } } - public String interpolate(final String message, final MessageInterpolator.Context context, final Locale locale) + public String interpolate(final String message, final MessageInterpolator.Context context, + final Locale locale) { return delegate.interpolate(message, context, locale); } http://git-wip-us.apache.org/repos/asf/wicket/blob/9eb670b5/wicket-experimental/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/SizeTagModifier.java ---------------------------------------------------------------------- diff --git a/wicket-experimental/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/SizeTagModifier.java b/wicket-experimental/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/SizeTagModifier.java index 4124974..1795dc4 100644 --- a/wicket-experimental/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/SizeTagModifier.java +++ b/wicket-experimental/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/SizeTagModifier.java @@ -6,8 +6,8 @@ import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.html.form.FormComponent; /** - * A tag modifier that adds the {@code maxlength} attribute to the {@code input} tag with the max value - * from the {@link Size} constraint annotation. + * A tag modifier that adds the {@code maxlength} attribute to the {@code input} tag with the max + * value from the {@link Size} constraint annotation. * * @author igor * http://git-wip-us.apache.org/repos/asf/wicket/blob/9eb670b5/wicket-experimental/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/PropertyValidatorRequiredTest.java ---------------------------------------------------------------------- diff --git a/wicket-experimental/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/PropertyValidatorRequiredTest.java b/wicket-experimental/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/PropertyValidatorRequiredTest.java new file mode 100644 index 0000000..fcdb007 --- /dev/null +++ b/wicket-experimental/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/PropertyValidatorRequiredTest.java @@ -0,0 +1,185 @@ +package org.apache.wicket.bean.validation; + +import static org.junit.Assert.*; + +import javax.validation.constraints.NotNull; + +import org.apache.wicket.MarkupContainer; +import org.apache.wicket.markup.IMarkupResourceStreamProvider; +import org.apache.wicket.markup.html.WebPage; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.form.FormComponent; +import org.apache.wicket.markup.html.form.TextField; +import org.apache.wicket.mock.MockApplication; +import org.apache.wicket.model.PropertyModel; +import org.apache.wicket.util.resource.IResourceStream; +import org.apache.wicket.util.resource.StringResourceStream; +import org.apache.wicket.util.tester.WicketTester; +import org.apache.wicket.util.tester.WicketTesterScope; +import org.junit.Rule; +import org.junit.Test; + +public class PropertyValidatorRequiredTest +{ + @Rule + public WicketTesterScope scope = new WicketTesterScope() + { + protected WicketTester create() + { + return new WicketTester(new TestApplication()); + }; + }; + + @Test + public void test() + { + TestPage page = scope.getTester().startPage(TestPage.class); + + // no group + assertTrue(page.input1.isRequired()); + assertFalse(page.input2.isRequired()); + assertFalse(page.input3.isRequired()); + assertFalse(page.input4.isRequired()); + + // group1 + assertFalse(page.input5.isRequired()); + assertTrue(page.input6.isRequired()); + assertFalse(page.input7.isRequired()); + assertTrue(page.input8.isRequired()); + + // group2 + assertFalse(page.input9.isRequired()); + assertFalse(page.input10.isRequired()); + assertTrue(page.input11.isRequired()); + assertTrue(page.input12.isRequired()); + + // group1+group2 + assertFalse(page.input13.isRequired()); + assertTrue(page.input14.isRequired()); + assertTrue(page.input15.isRequired()); + assertTrue(page.input16.isRequired()); + + // group3 + assertFalse(page.input17.isRequired()); + assertFalse(page.input18.isRequired()); + assertFalse(page.input19.isRequired()); + assertFalse(page.input20.isRequired()); + + } + + public static class TestApplication extends MockApplication + { + @Override + protected void init() + { + super.init(); + new BeanValidationConfiguration().configure(this); + } + } + + public static class TestPage extends WebPage implements IMarkupResourceStreamProvider + { + + private TestBean bean = new TestBean(); + private FormComponent<String> input1, input2, input3, input4, input5, input6, input7, + input8, input9, input10, input11, input12, input13, input14, input15, input16, input17, + input18, input19, input20; + + public TestPage() + { + Form<?> form = new Form<Void>("form"); + add(form); + + input1 = new TextField<String>("input1", new PropertyModel<String>(this, + "bean.property")).add(new PropertyValidator<String>()); + input2 = new TextField<String>("input2", new PropertyModel<String>(this, + "bean.propertyOne")).add(new PropertyValidator<String>()); + input3 = new TextField<String>("input3", new PropertyModel<String>(this, + "bean.propertyTwo")).add(new PropertyValidator<String>()); + input4 = new TextField<String>("input4", new PropertyModel<String>(this, + "bean.propertyOneTwo")).add(new PropertyValidator<String>()); + + input5 = new TextField<String>("input5", new PropertyModel<String>(this, + "bean.property")).add(new PropertyValidator<String>(GroupOne.class)); + input6 = new TextField<String>("input6", new PropertyModel<String>(this, + "bean.propertyOne")).add(new PropertyValidator<String>(GroupOne.class)); + input7 = new TextField<String>("input7", new PropertyModel<String>(this, + "bean.propertyTwo")).add(new PropertyValidator<String>(GroupOne.class)); + input8 = new TextField<String>("input8", new PropertyModel<String>(this, + "bean.propertyOneTwo")).add(new PropertyValidator<String>(GroupOne.class)); + + input9 = new TextField<String>("input9", new PropertyModel<String>(this, + "bean.property")).add(new PropertyValidator<String>(GroupTwo.class)); + input10 = new TextField<String>("input10", new PropertyModel<String>(this, + "bean.propertyOne")).add(new PropertyValidator<String>(GroupTwo.class)); + input11 = new TextField<String>("input11", new PropertyModel<String>(this, + "bean.propertyTwo")).add(new PropertyValidator<String>(GroupTwo.class)); + input12 = new TextField<String>("input12", new PropertyModel<String>(this, + "bean.propertyOneTwo")).add(new PropertyValidator<String>(GroupTwo.class)); + + input13 = new TextField<String>("input13", new PropertyModel<String>(this, + "bean.property")).add(new PropertyValidator<String>(GroupOne.class, GroupTwo.class)); + input14 = new TextField<String>("input14", new PropertyModel<String>(this, + "bean.propertyOne")).add(new PropertyValidator<String>(GroupOne.class, + GroupTwo.class)); + input15 = new TextField<String>("input15", new PropertyModel<String>(this, + "bean.propertyTwo")).add(new PropertyValidator<String>(GroupOne.class, + GroupTwo.class)); + input16 = new TextField<String>("input16", new PropertyModel<String>(this, + "bean.propertyOneTwo")).add(new PropertyValidator<String>(GroupOne.class, + GroupTwo.class)); + + input17 = new TextField<String>("input17", new PropertyModel<String>(this, + "bean.property")).add(new PropertyValidator<String>(GroupThree.class)); + input18 = new TextField<String>("input18", new PropertyModel<String>(this, + "bean.propertyOne")).add(new PropertyValidator<String>(GroupThree.class)); + input19 = new TextField<String>("input19", new PropertyModel<String>(this, + "bean.propertyTwo")).add(new PropertyValidator<String>(GroupThree.class)); + input20 = new TextField<String>("input20", new PropertyModel<String>(this, + "bean.propertyOneTwo")).add(new PropertyValidator<String>(GroupThree.class)); + + form.add(input1, input2, input3, input4, input5, input6, input7, input8, input9, + input10, input11, input12, input13, input14, input15, input16, input17, input18, + input19, input20); + + } + + @Override + public IResourceStream getMarkupResourceStream(MarkupContainer container, + Class<?> containerClass) + { + return new StringResourceStream( + "<form wicket:id='form'><input wicket:id='input1'/><input wicket:id='input2'/><input wicket:id='input3'/><input wicket:id='input4'/><input wicket:id='input5'/><input wicket:id='input6'/><input wicket:id='input7'/><input wicket:id='input8'/><input wicket:id='input9'/><input wicket:id='input10'/><input wicket:id='input11'/><input wicket:id='input12'/><input wicket:id='input13'/><input wicket:id='input14'/><input wicket:id='input15'/><input wicket:id='input16'/><input wicket:id='input17'/><input wicket:id='input18'/><input wicket:id='input19'/><input wicket:id='input20'/></form>"); + } + + } + + public static interface GroupOne + { + } + + public static interface GroupTwo + { + } + + public static interface GroupThree + { + } + + public static class TestBean + { + @NotNull + String property; + + @NotNull(groups = { GroupOne.class }) + String propertyOne; + + @NotNull(groups = { GroupTwo.class }) + String propertyTwo; + + @NotNull(groups = { GroupOne.class, GroupTwo.class }) + String propertyOneTwo; + + } + +}
