TAP5-2075 - @AssertTrue and @AssertFalse client side Bean validation for checkboxes
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java
index 880242e..ff57390 100644
--- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java
@@ -53,7 +53,7 @@
     }
 
     public static void contributeServiceOverride(
-            MappedConfiguration<Class, Object> configuration,
+            MappedConfiguration<Class<?>, Object> configuration,
             @Local FieldValidatorDefaultSource source)
     {
         configuration.add(FieldValidatorDefaultSource.class, source);
@@ -71,7 +71,7 @@
     }
 
     public static void contributeBeanValidatorGroupSource(
-            final Configuration<Class> configuration)
+            final Configuration<Class<?>> configuration)
     {
         configuration.add(Default.class);
     }
@@ -186,5 +186,33 @@
                 }
             }
         });
+
+        configuration.add(new BaseCCD(AssertTrue.class)
+        {
+            @Override
+            public void applyClientValidation(MarkupWriter writer, String message, Map<String, Object> attributes)
+            {
+                javaScriptSupport.require("t5/core/validation");
+
+                writer.attributes(
+                        DataConstants.VALIDATION_ATTRIBUTE, true,
+                        "data-expected-status", "checked",
+                        "data-checked-message", message);
+            }
+        });
+
+        configuration.add(new BaseCCD(AssertFalse.class)
+        {
+            @Override
+            public void applyClientValidation(MarkupWriter writer, String message, Map<String, Object> attributes)
+            {
+                javaScriptSupport.require("t5/core/validation");
+
+                writer.attributes(
+                        DataConstants.VALIDATION_ATTRIBUTE, true,
+                        "data-expected-status", "unchecked",
+                        "data-checked-message", message);
+            }
+        });
     }
 }
diff --git a/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java b/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java
index 539b823..fa28e01 100644
--- a/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java
+++ b/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java
@@ -40,6 +40,8 @@
         assertTextPresent("Favorite Colors may not be null");
         assertTextPresent("More Colors size must be between 3 and 4");
         assertTextPresent("Birth Day may not be null");
+        assertTextPresent("Accepted Terms And Conditions must be true");
+        assertTextPresent("Subscribed must be false");
 
 
         type("secretPassword", "igor");
@@ -60,6 +62,8 @@
         assertFalse(isTextPresent("Favorite Colors may not be null"));
         assertTextPresent("More Colors size must be between 3 and 4");
         assertTextPresent("Birth Day must be in the past");
+        assertTextPresent("Accepted Terms And Conditions must be true");
+        assertTextPresent("Subscribed must be false");
 
         //Test Tapestry validator
 
@@ -75,12 +79,29 @@
         assertTextPresent("You must provide at least 5 characters for Login Name.");
         assertFalse(isTextPresent("Birth Day must be in the past"));
         assertFalse(isTextPresent("More Colors size must be between 3 and 4"));
+        assertTextPresent("Accepted Terms And Conditions must be true");
+        assertTextPresent("Subscribed must be false");
 
         type("loginName", "igor123");
 
         clickAndWait(SUBMIT);
 
         assertFalse(isTextPresent("You must provide at least 5 characters for Login Name."));
+        assertTextPresent("Accepted Terms And Conditions must be true");
+        assertTextPresent("Subscribed must be false");
+
+        click("acceptedTermsAndConditions");
+
+        clickAndWait(SUBMIT);
+
+        assertFalse(isTextPresent("Accepted Terms And Conditions must be true"));
+        assertTextPresent("Subscribed must be false");
+
+        click("subscribed");
+
+        clickAndWait(SUBMIT);
+
+        assertFalse(isTextPresent("Subscribed must be false"));
     }
 
     @Test
@@ -202,6 +223,27 @@
 
         waitForCssSelectorToAppear("p[data-error-block-for='nullValue']");
         assertTextPresent("Null Value must be null");
+
+        waitForCssSelectorToAppear("p[data-error-block-for='mustBeTrue']");
+        assertTextPresent("Must Be True must be true");
+        waitForCssSelectorToAppear("p[data-error-block-for='mustBeFalse']");
+        assertTextPresent("Must Be False must be false");
+
+        click("mustBeTrue");
+
+        click(SUBMIT);
+
+        waitForInvisible("p[data-error-block-for='mustBeTrue']");
+        // assertFalse(isTextPresent("Must Be True must be true"));
+        assertTextPresent("Must Be False must be false");
+
+        click("mustBeFalse");
+
+        click(SUBMIT);
+
+        waitForInvisible("p[data-error-block-for='mustBeFalse']");
+        // assertFalse(isTextPresent("Must Be False must be false"));
+
     }
 
 
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/TestEntity.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/TestEntity.java
index 04d9045..b296b95 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/TestEntity.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/TestEntity.java
@@ -13,10 +13,11 @@
 // limitations under the License.
 package org.example.testapp.entities;
 
-import javax.validation.constraints.*;
 import java.util.ArrayList;
 import java.util.Collection;
 
+import javax.validation.constraints.*;
+
 public class TestEntity
 {
     @NotNull
@@ -36,12 +37,18 @@
 
     @Size(min = 3)
     private String stringMinLength;
-    
+
     @Size(max = 6)
     private String stringMaxLength;
 
     @Size(min = 2, max = 3)
-    private Collection<String> collectionSizeValue = new ArrayList<String>();
+    private Collection<String> collectionSizeValue = new ArrayList<>();
+
+    @AssertTrue
+    private boolean mustBeTrue;
+
+    @AssertFalse
+    private boolean mustBeFalse = true;
 
     public String getNotNullValue()
     {
@@ -119,5 +126,25 @@
     public void setStringMaxLength(String stringMaxLength) {
         this.stringMaxLength = stringMaxLength;
     }
-    
+
+    public boolean isMustBeTrue()
+    {
+        return mustBeTrue;
+    }
+
+    public void setMustBeTrue(boolean mustBeTrue)
+    {
+        this.mustBeTrue = mustBeTrue;
+    }
+
+    public boolean isMustBeFalse()
+    {
+        return mustBeFalse;
+    }
+
+    public void setMustBeFalse(boolean mustBeFalse)
+    {
+        this.mustBeFalse = mustBeFalse;
+    }
+
 }
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormValidationDemo.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormValidationDemo.java
index cb46348..89a6e94 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormValidationDemo.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormValidationDemo.java
@@ -17,13 +17,12 @@
 import java.util.Collection;
 import java.util.Date;
 
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Past;
-import javax.validation.constraints.Size;
+import javax.validation.constraints.*;
 
 import org.apache.tapestry5.annotations.Import;
 import org.apache.tapestry5.annotations.Persist;
 import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.annotations.SetupRender;
 import org.apache.tapestry5.beaneditor.Validate;
 import org.apache.tapestry5.internal.services.StringValueEncoder;
 import org.example.testapp.services.Foo;
@@ -65,9 +64,28 @@
 	@Persist
 	private Date date;
 
+    @AssertTrue
+    @Property
+    @Persist
+    private boolean acceptedTermsAndConditions;
+
+    @AssertFalse
+    @Property
+    @Persist
+    private Boolean subscribed;
+
+    @SetupRender
+    private void setupRenderer()
+    {
+        if (subscribed == null)
+        {
+            subscribed = true;
+        }
+    }
+
     public void onPrepare() {
         if (languages == null) {
-            languages = new ArrayList<String>();
+            languages = new ArrayList<>();
         }
     }
 
diff --git a/tapestry-beanvalidator/src/test/webapp/FormValidationDemo.tml b/tapestry-beanvalidator/src/test/webapp/FormValidationDemo.tml
index 2701ea5..283df70 100644
--- a/tapestry-beanvalidator/src/test/webapp/FormValidationDemo.tml
+++ b/tapestry-beanvalidator/src/test/webapp/FormValidationDemo.tml
@@ -28,6 +28,22 @@
         <t:datefield t:id="birthDay" value="date" format="dd.MM.yyyy"/>
 
         <br/>
+        <div class="checkbox">
+            <label>
+                <t:checkbox t:id="acceptedTermsAndConditions"/>
+                Accept Terms and Conditions
+            </label>
+        </div>
+
+        <br/>
+        <div class="checkbox">
+            <label>
+                <t:checkbox t:id="subscribed"/>
+                Subscribed (untick to unsubscribe)
+            </label>
+        </div>
+
+        <br/>
         <input type="submit" value="Go"/>
 
     </t:form>