TAP5-2075 - Checkbox Checked and Unchecked validator with client side validation
Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/41b4c5e2 Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/41b4c5e2 Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/41b4c5e2 Branch: refs/heads/master Commit: 41b4c5e2d2d48d6359ddb96a383349f1d30f686d Parents: dcd45ed Author: Balázs Palcsó <[email protected]> Authored: Sat Dec 29 15:58:14 2018 +0100 Committer: Balázs Palcsó <[email protected]> Committed: Thu Jan 17 18:33:18 2019 +0100 ---------------------------------------------------------------------- .../META-INF/modules/t5/core/fields.coffee | 2 + .../META-INF/modules/t5/core/utils.coffee | 2 + .../META-INF/modules/t5/core/validation.coffee | 10 +++ .../tapestry5/corelib/components/Checkbox.java | 25 +++--- .../tapestry5/modules/TapestryModule.java | 2 + .../tapestry5/validator/CheckboxValidator.java | 84 ++++++++++++++++++++ .../org/apache/tapestry5/validator/Checked.java | 49 ++++++++++++ .../apache/tapestry5/validator/Unchecked.java | 32 ++++++++ .../org/apache/tapestry5/core.properties | 6 ++ .../org/apache/tapestry5/core_hu.properties | 6 ++ .../test/app1/ValidateCheckboxMustBeChecked.tml | 7 ++ .../app1/ValidateCheckboxMustBeUnchecked.tml | 7 ++ .../tapestry5/integration/app1/FormTests.java | 36 +++++++++ .../tapestry5/integration/app1/pages/Index.java | 6 ++ .../pages/ValidateCheckboxMustBeChecked.java | 25 ++++++ .../pages/ValidateCheckboxMustBeUnchecked.java | 25 ++++++ 16 files changed, 313 insertions(+), 11 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/fields.coffee ---------------------------------------------------------------------- diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/fields.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/fields.coffee index 6ae7fcb..272be71 100644 --- a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/fields.coffee +++ b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/fields.coffee @@ -110,6 +110,8 @@ define ["underscore", "./events", "./dom", "./utils", "./forms"], fieldValue = if (@attr "data-value-mode") is "options" collectOptionValues this + else if @element.type is "checkbox" + @checked() else @value() http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/utils.coffee ---------------------------------------------------------------------- diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/utils.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/utils.coffee index 5829ea0..c89b294 100644 --- a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/utils.coffee +++ b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/utils.coffee @@ -50,6 +50,8 @@ define ["underscore"], if _.isArray input return input.length is 0 + + return false if typeof input is "boolean" return (exports.trim input).length is 0 http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/validation.coffee ---------------------------------------------------------------------- diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/validation.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/validation.coffee index b44f043..7269bb8 100644 --- a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/validation.coffee +++ b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/validation.coffee @@ -164,5 +164,15 @@ define ["underscore", "./dom", "./events", "./utils", "./messages", "./fields"], memo.error = (@attr "data-regexp-message") or "INVALID" return false + dom.onDocument events.field.validate, "[data-expected-status=checked]", (event, memo) -> + + unless memo.value + memo.error = (@attr "data-checked-message") or "MUST BE CHECKED" + + dom.onDocument events.field.validate, "[data-expected-status=unchecked]", (event, memo) -> + + if memo.value + memo.error = (@attr "data-checked-message") or "MUST NOT BE CHECKED" + # Export the number parser, just to be nice (and to support some testing). return { parseNumber } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Checkbox.java ---------------------------------------------------------------------- diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Checkbox.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Checkbox.java index d8654f7..1492ff9 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Checkbox.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Checkbox.java @@ -14,11 +14,7 @@ package org.apache.tapestry5.corelib.components; -import org.apache.tapestry5.Binding; -import org.apache.tapestry5.BindingConstants; -import org.apache.tapestry5.FieldValidator; -import org.apache.tapestry5.MarkupWriter; -import org.apache.tapestry5.ValidationException; +import org.apache.tapestry5.*; import org.apache.tapestry5.annotations.AfterRender; import org.apache.tapestry5.annotations.BeginRender; import org.apache.tapestry5.annotations.Mixin; @@ -47,7 +43,7 @@ public class Checkbox extends AbstractField @Parameter(defaultPrefix = BindingConstants.VALIDATE, allowNull = false) @SuppressWarnings("unchecked") private FieldValidator<Object> validate; - + @SuppressWarnings("unused") @Mixin private RenderDisabled renderDisabled; @@ -63,11 +59,16 @@ public class Checkbox extends AbstractField "name", getControlName(), "id", getClientId(), "checked", checked ? "checked" : null); - + + putPropertyNameIntoBeanValidationContext("value"); + validate.render(writer); + removePropertyNameFromBeanValidationContext(); + resources.renderInformalParameters(writer); + decorateInsideField(); } @@ -83,21 +84,23 @@ public class Checkbox extends AbstractField String postedValue = request.getParameter(controlName); boolean translated = postedValue != null; - + // record as "true" or "false" validationTracker.recordInput(this, Boolean.toString(translated)); - + + putPropertyNameIntoBeanValidationContext("value"); try { fieldValidationSupport.validate(translated, resources, validate); - + value = translated; } catch (ValidationException ex) { validationTracker.recordError(this, ex.getMessage()); } + removePropertyNameFromBeanValidationContext(); } - + /** * Computes a default value for the "validate" parameter using * {@link org.apache.tapestry5.services.FieldValidatorDefaultSource}. http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java ---------------------------------------------------------------------- diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java index 7529edc..52fdf72 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java @@ -694,6 +694,8 @@ public final class TapestryModule configuration.addInstance("max", Max.class); configuration.addInstance("regexp", Regexp.class); configuration.addInstance("email", Email.class); + configuration.addInstance("checked", Checked.class); + configuration.addInstance("unchecked", Unchecked.class); configuration.add("none", new None()); } http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/main/java/org/apache/tapestry5/validator/CheckboxValidator.java ---------------------------------------------------------------------- diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/CheckboxValidator.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/CheckboxValidator.java new file mode 100644 index 0000000..9ae7af0 --- /dev/null +++ b/tapestry-core/src/main/java/org/apache/tapestry5/validator/CheckboxValidator.java @@ -0,0 +1,84 @@ +// Copyright 2018 The Apache Software Foundation +// +// 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.apache.tapestry5.validator; + +import org.apache.tapestry5.Field; +import org.apache.tapestry5.MarkupWriter; +import org.apache.tapestry5.ValidationException; +import org.apache.tapestry5.ioc.MessageFormatter; +import org.apache.tapestry5.services.FormSupport; +import org.apache.tapestry5.services.javascript.DataConstants; +import org.apache.tapestry5.services.javascript.JavaScriptSupport; + +/** + * A validator that enforces that the value is true or false. This validator is not configurable. + * + * @since 5.5.0 + */ +class CheckboxValidator extends AbstractValidator<Void, Object> +{ + private final Boolean expectedValue; + private final String expectedStatus; + + public CheckboxValidator(JavaScriptSupport javaScriptSupport, String messageKey, Boolean expectedValue, String expectedStatus) + { + super(null, Object.class, messageKey, javaScriptSupport); + this.expectedValue = expectedValue; + this.expectedStatus = expectedStatus; + } + + @Override + public void validate(Field field, Void constraintValue, MessageFormatter formatter, Object value) + throws ValidationException + { + if (!expectedValue.equals(value)) + throw new ValidationException(buildMessage(formatter, field)); + } + + private String buildMessage(MessageFormatter formatter, Field field) + { + return formatter.format(field.getLabel()); + } + + /** + * The exception to the rule. + */ + @Override + public boolean isRequired() + { + return true; + } + + @Override + public void render(Field field, Void constraintValue, MessageFormatter formatter, MarkupWriter writer, + FormSupport formSupport) + { + if (formSupport.isClientValidationEnabled()) + { + javaScriptSupport.require("t5/core/validation"); + + writer.attributes( + DataConstants.VALIDATION_ATTRIBUTE, true, + "data-expected-status", expectedStatus, + "data-checked-message", buildMessage(formatter, field)); + } + } + + @Override + public String toString() + { + return "Checkbox must be " + expectedStatus + " validator"; + } +} http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/main/java/org/apache/tapestry5/validator/Checked.java ---------------------------------------------------------------------- diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Checked.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Checked.java new file mode 100644 index 0000000..6a24cc9 --- /dev/null +++ b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Checked.java @@ -0,0 +1,49 @@ +// Copyright 2018 The Apache Software Foundation +// +// 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.apache.tapestry5.validator; + +import org.apache.tapestry5.Field; +import org.apache.tapestry5.MarkupWriter; +import org.apache.tapestry5.ioc.MessageFormatter; +import org.apache.tapestry5.services.FormSupport; +import org.apache.tapestry5.services.Html5Support; +import org.apache.tapestry5.services.javascript.JavaScriptSupport; + +/** + * A validator that enforces that the value is true. This validator is not configurable. + * + * @since 5.5.0 + */ +public final class Checked extends CheckboxValidator +{ + final private Html5Support html5Support; + + public Checked(JavaScriptSupport javaScriptSupport, Html5Support html5Support) + { + super(javaScriptSupport, "checked", Boolean.TRUE, "checked"); + this.html5Support = html5Support; + } + + public void render(Field field, Void constraintValue, MessageFormatter formatter, MarkupWriter writer, + FormSupport formSupport) + { + super.render(field, constraintValue, formatter, writer, formSupport); + + if (html5Support.isHtml5SupportEnabled()) + { + writer.attributes("required", "required"); + } + } +} http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/main/java/org/apache/tapestry5/validator/Unchecked.java ---------------------------------------------------------------------- diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Unchecked.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Unchecked.java new file mode 100644 index 0000000..e358ee3 --- /dev/null +++ b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Unchecked.java @@ -0,0 +1,32 @@ +// Copyright 2018 The Apache Software Foundation +// +// 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.apache.tapestry5.validator; + +import org.apache.tapestry5.services.Html5Support; +import org.apache.tapestry5.services.javascript.JavaScriptSupport; + +/** + * A validator that enforces that the value is false. This validator is not configurable. + * + * @since 5.5.0 + */ +public final class Unchecked extends CheckboxValidator +{ + public Unchecked(JavaScriptSupport javaScriptSupport, Html5Support html5Support) + { + super(javaScriptSupport, "unchecked", Boolean.FALSE, "unchecked"); + } + +} http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/main/resources/org/apache/tapestry5/core.properties ---------------------------------------------------------------------- diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/core.properties b/tapestry-core/src/main/resources/org/apache/tapestry5/core.properties index 17281ca..3c04f2c 100644 --- a/tapestry-core/src/main/resources/org/apache/tapestry5/core.properties +++ b/tapestry-core/src/main/resources/org/apache/tapestry5/core.properties @@ -95,6 +95,12 @@ regexp=%2$s does not match pattern '%1$s'. # "required" validator error: required=You must provide a value for %s. +# "checked" validator error: +checked=You must check %s. + +# "unchecked" validator error: +unchecked=You must uncheck %s. + # Client-side numeric validation error: core-input-not-numeric=Value is not numeric. http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/main/resources/org/apache/tapestry5/core_hu.properties ---------------------------------------------------------------------- diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/core_hu.properties b/tapestry-core/src/main/resources/org/apache/tapestry5/core_hu.properties index 5711e98..fb96493 100644 --- a/tapestry-core/src/main/resources/org/apache/tapestry5/core_hu.properties +++ b/tapestry-core/src/main/resources/org/apache/tapestry5/core_hu.properties @@ -95,6 +95,12 @@ regexp=%2$s nem felel meg az elvárt '%1$s' formátumnak. # "required" validator error: required=%s megadása kötelezÅ. +# "checked" validator error: +checked=%s bepipálása kötelezÅ. + +# "unchecked" validator error: +unchecked=%s bepipálása nem megengedett. + # Client-side numeric validation error: core-input-not-numeric=A megadott érték nem szám. http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/test/app1/ValidateCheckboxMustBeChecked.tml ---------------------------------------------------------------------- diff --git a/tapestry-core/src/test/app1/ValidateCheckboxMustBeChecked.tml b/tapestry-core/src/test/app1/ValidateCheckboxMustBeChecked.tml new file mode 100644 index 0000000..4f8315e --- /dev/null +++ b/tapestry-core/src/test/app1/ValidateCheckboxMustBeChecked.tml @@ -0,0 +1,7 @@ +<html t:type="border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd"> +<t:form> + <t:Checkbox value="value" validate="checked" /> + <t:submit/> +</t:form> +Checkbox's value: ${value} +</html> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/test/app1/ValidateCheckboxMustBeUnchecked.tml ---------------------------------------------------------------------- diff --git a/tapestry-core/src/test/app1/ValidateCheckboxMustBeUnchecked.tml b/tapestry-core/src/test/app1/ValidateCheckboxMustBeUnchecked.tml new file mode 100644 index 0000000..ae7b639 --- /dev/null +++ b/tapestry-core/src/test/app1/ValidateCheckboxMustBeUnchecked.tml @@ -0,0 +1,7 @@ +<html t:type="border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd"> +<t:form> + <t:Checkbox value="value" validate="unchecked" /> + <t:submit/> +</t:form> +Checkbox's value: ${value} +</html> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java ---------------------------------------------------------------------- diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java index 5db2f79..ad7a3f6 100644 --- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java @@ -1266,6 +1266,42 @@ public class FormTests extends App1TestCase assertTextPresent("Validate in error"); } + /** TAP5-2075 **/ + @Test + public void validate_checkbox_must_be_checked() + { + openLinks("Validate Checkbox Must Be Checked"); + + clickAndWait(SUBMIT); + + assertTextPresent("You must check Checkbox."); + + check("//input[@type='checkbox']"); + + clickAndWait(SUBMIT); + + assertTextPresent("Checkbox's value: true"); + } + + /** TAP5-2075 **/ + @Test + public void validate_checkbox_must_be_unchecked() + { + openLinks("Validate Checkbox Must Be Unchecked"); + + check("//input[@type='checkbox']"); + + clickAndWait(SUBMIT); + + assertTextPresent("You must uncheck Checkbox."); + + uncheck("//input[@type='checkbox']"); + + clickAndWait(SUBMIT); + + assertTextPresent("Checkbox's value: false"); + } + // TAP5-2204 @Test public void select_model_with_auto_security_and_non_persistent_model() throws Exception http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java ---------------------------------------------------------------------- diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java index df579be..2c87ae7 100644 --- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java @@ -601,6 +601,12 @@ public class Index new Item("textfieldwithnullvalidateparameter", "TextField with null validate parameter", "A TextField whose validate parameter is bound to null"), + new Item("validateCheckboxMustBeChecked", "Validate Checkbox Must Be Checked", "A form that trigger validate in " + + "error event on submit when checkbox is not checked"), + + new Item("validateCheckboxMustBeUnchecked", "Validate Checkbox Must Be Unchecked", "A form that trigger validate in " + + "error event on submit when checkbox is checked"), + new Item("validateInErrorEvent", "Validate in error Event", "A form that trigger validate in " + "error event on submit when textfield is empty"), http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ValidateCheckboxMustBeChecked.java ---------------------------------------------------------------------- diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ValidateCheckboxMustBeChecked.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ValidateCheckboxMustBeChecked.java new file mode 100644 index 0000000..85ef23e --- /dev/null +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ValidateCheckboxMustBeChecked.java @@ -0,0 +1,25 @@ +// Copyright 2018 The Apache Software Foundation +// +// 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.apache.tapestry5.integration.app1.pages; + +import org.apache.tapestry5.annotations.Persist; +import org.apache.tapestry5.annotations.Property; + +public class ValidateCheckboxMustBeChecked { + + @Property + @Persist + private boolean value; +} http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/41b4c5e2/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ValidateCheckboxMustBeUnchecked.java ---------------------------------------------------------------------- diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ValidateCheckboxMustBeUnchecked.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ValidateCheckboxMustBeUnchecked.java new file mode 100644 index 0000000..d8c28ef --- /dev/null +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ValidateCheckboxMustBeUnchecked.java @@ -0,0 +1,25 @@ +// Copyright 2018 The Apache Software Foundation +// +// 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.apache.tapestry5.integration.app1.pages; + +import org.apache.tapestry5.annotations.Persist; +import org.apache.tapestry5.annotations.Property; + +public class ValidateCheckboxMustBeUnchecked { + + @Property + @Persist + private boolean value; +}
