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;
+
+       }
+
+}

Reply via email to