From 4da5f803bfb0aca451aaace5165cb55bd4d13850 Mon Sep 17 00:00:00 2001 From: Tatu Lund Date: Fri, 17 Apr 2020 10:12:46 +0300 Subject: [PATCH 1/5] Add methods to control validation - Enable / disable all validators on Binder level - Enable / disable validators on Binding level - add writeBeanAsDraft(bean,boolean) for writing draft bean with validators disabled Fixes: https://github.com/vaadin/flow/issues/5030 --- .../com/vaadin/flow/data/binder/Binder.java | 112 ++++++++++++++++-- 1 file changed, 105 insertions(+), 7 deletions(-) diff --git a/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java b/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java index 9b42f81c780..d85bf3c6fde 100644 --- a/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java +++ b/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java @@ -235,6 +235,21 @@ default BindingValidationStatus validate() { * {@code true} otherwise (default) */ public boolean isAsRequiredEnabled(); + + /** + * Define whether validators are disabled or enabled for this + * specific binding. + * + * @param validatorsDisabled A boolean value + */ + public void setValidatorsDisabled(boolean validatorsDisabled); + + /** + * Returns if validators are currently disabled or not + * + * @return A boolean value + */ + public boolean isValidatorsDisabled(); } /** @@ -754,6 +769,7 @@ protected static class BindingBuilderImpl private final HasValue field; private BindingValidationStatusHandler statusHandler; private boolean isStatusHandlerChanged; + private Binding binding; private boolean bound; @@ -815,6 +831,7 @@ public Binding bind(ValueProvider getter, if (getBinder().incompleteBindings != null) { getBinder().incompleteBindings.remove(getField()); } + this.binding = binding; return binding; } @@ -849,6 +866,7 @@ public Binding bind(String propertyName) { Binding binding = ((BindingBuilder) finalBinding).bind(getter, setter); getBinder().boundProperties.put(propertyName, binding); + this.binding = binding; return binding; } finally { if (getBinder().incompleteMemberFieldBindings != null) { @@ -872,8 +890,17 @@ public BindingBuilder withValidator( checkUnbound(); Objects.requireNonNull(validator, "validator cannot be null"); + Validator wrappedValidator = ((value, context) -> { + if (getBinder().isValidatorsDisabled() || + (binding != null && binding.isValidatorsDisabled())) { + return ValidationResult.ok(); + } else { + return validator.apply(value, context); + } + }); + converterValidatorChain = ((Converter) converterValidatorChain) - .chain(new ValidatorAsConverter<>(validator)); + .chain(new ValidatorAsConverter<>(wrappedValidator)); return this; } @@ -1026,6 +1053,8 @@ protected static class BindingImpl private final boolean asRequiredSet; + private boolean validatorsDisabled = false; + public BindingImpl(BindingBuilderImpl builder, ValueProvider getter, Setter setter) { @@ -1306,6 +1335,16 @@ public void setAsRequiredEnabled(boolean asRequiredEnabled) { public boolean isAsRequiredEnabled() { return field.isRequiredIndicatorVisible(); } + + @Override + public void setValidatorsDisabled(boolean validatorsDisabled) { + this.validatorsDisabled = validatorsDisabled; + } + + @Override + public boolean isValidatorsDisabled() { + return validatorsDisabled; + } } /** @@ -1412,6 +1451,8 @@ void setIdentity() { private Set> changedBindings = new LinkedHashSet<>(); + private boolean validatorsDisabled = false; + /** * Creates a binder using a custom {@link PropertySet} implementation for * finding and resolving property names for @@ -1831,7 +1872,27 @@ public void writeBean(BEAN bean) throws ValidationException { * {@code null} */ public void writeBeanAsDraft(BEAN bean) { - doWriteDraft(bean, new ArrayList<>(bindings)); + doWriteDraft(bean, new ArrayList<>(bindings),false); + } + + /** + * Writes successfully converted changes from the bound fields bypassing + * all the Validation or all fields passing conversion if forced = true. + * If the conversion fails, the value written to the bean will be null. + * + * @see #writeBean(Object) + * @see #writeBeanIfValid(Object) + * @see #readBean(Object) + * @see #setBean(Object) + * + * @param bean + * the object to which to write the field values, not + * {@code null} + * @param forced + * disable all Validators during write + */ + public void writeBeanAsDraft(BEAN bean, boolean forced) { + doWriteDraft(bean, new ArrayList<>(bindings),true); } /** @@ -1929,14 +1990,24 @@ private BinderValidationStatus doWriteIfValid(BEAN bean, * the bean to write field values into * @param bindings * the set of bindings to write to the bean + * @param forced + * disable validators during write if true */ @SuppressWarnings({ "unchecked" }) - private void doWriteDraft(BEAN bean, - Collection> bindings) { + private void doWriteDraft(BEAN bean, + Collection> bindings, boolean forced) { Objects.requireNonNull(bean, "bean cannot be null"); - bindings.forEach(binding -> ((BindingImpl) binding) - .writeFieldValue(bean)); + if (!forced) { + bindings.forEach(binding -> ((BindingImpl) binding) + .writeFieldValue(bean)); + } else { + boolean isDisabled = isValidatorsDisabled(); + setValidatorsDisabled(true); + bindings.forEach(binding -> ((BindingImpl) binding) + .writeFieldValue(bean)); + setValidatorsDisabled(isDisabled); + } } /** @@ -1998,7 +2069,14 @@ protected void restoreBeanState(BEAN bean, */ public Binder withValidator(Validator validator) { Objects.requireNonNull(validator, "validator cannot be null"); - validators.add(validator); + Validator wrappedValidator = ((value, context) -> { + if (isValidatorsDisabled()) { + return ValidationResult.ok(); + } else { + return validator.apply(value, context); + } + }); + validators.add(wrappedValidator); return this; } @@ -2986,4 +3064,24 @@ public void removeBinding(String propertyName) { Optional.ofNullable(boundProperties.get(propertyName)) .ifPresent(Binding::unbind); } + + /** + * Control whether validators including bean level validators are + * disabled or enabled globally for this Binder. + * + * @param validatorsDisabled Boolean value + */ + public void setValidatorsDisabled(boolean validatorsDisabled) { + this.validatorsDisabled = validatorsDisabled; + } + + /** + * Returns if the validators including bean level validators + * are disabled or enabled for this Binder. + * + * @return Boolean value + */ + public boolean isValidatorsDisabled() { + return validatorsDisabled; + } } From f7e8efc317a32d260d7ca729b9cedd87735e511a Mon Sep 17 00:00:00 2001 From: Tatu Lund Date: Fri, 17 Apr 2020 10:15:09 +0300 Subject: [PATCH 2/5] Add unit tests --- .../vaadin/flow/data/binder/BinderTest.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/flow-data/src/test/java/com/vaadin/flow/data/binder/BinderTest.java b/flow-data/src/test/java/com/vaadin/flow/data/binder/BinderTest.java index ac9247018cf..da8690c9d75 100644 --- a/flow-data/src/test/java/com/vaadin/flow/data/binder/BinderTest.java +++ b/flow-data/src/test/java/com/vaadin/flow/data/binder/BinderTest.java @@ -374,6 +374,72 @@ private void do_test_save_bound_beanAsDraft(boolean setBean) { // age is written to draft even if firstname validation // fails assertEquals(age, person.getAge()); + + binder.writeBeanAsDraft(person,true); + // name is now written despite validation as write was forced + assertEquals(fieldValue, person.getFirstName()); + } + + @Test + public void save_bound_bean_disable_validation_binding() throws ValidationException { + Binder binder = new Binder<>(); + Binding nameBinding = binder.forField(nameField) + .withValidator((value,context) -> { + if (value.equals("Mike")) return ValidationResult.ok(); + else return ValidationResult.error("value must be Mike"); + }) + .bind(Person::getFirstName, Person::setFirstName); + binder.forField(ageField) + .withConverter(new StringToIntegerConverter("")) + .bind(Person::getAge, Person::setAge); + + Person person = new Person(); + + String fieldValue = "John"; + nameField.setValue(fieldValue); + + int age = 10; + ageField.setValue("10"); + + person.setFirstName("Mark"); + + nameBinding.setValidatorsDisabled(true); + binder.writeBean(person); + + // name is now written as validation was disabled + assertEquals(fieldValue, person.getFirstName()); + assertEquals(age, person.getAge()); + } + + @Test + public void save_bound_bean_disable_validation_binder() throws ValidationException { + Binder binder = new Binder<>(); + binder.forField(nameField) + .withValidator((value,context) -> { + if (value.equals("Mike")) return ValidationResult.ok(); + else return ValidationResult.error("value must be Mike"); + }) + .bind(Person::getFirstName, Person::setFirstName); + binder.forField(ageField) + .withConverter(new StringToIntegerConverter("")) + .bind(Person::getAge, Person::setAge); + + Person person = new Person(); + + String fieldValue = "John"; + nameField.setValue(fieldValue); + + int age = 10; + ageField.setValue("10"); + + person.setFirstName("Mark"); + + binder.setValidatorsDisabled(true); + binder.writeBean(person); + + // name is now written as validation was disabled + assertEquals(fieldValue, person.getFirstName()); + assertEquals(age, person.getAge()); } @Test From ef7a99c03edea68bebfe27b18a89ba6d8c3c9643 Mon Sep 17 00:00:00 2001 From: Tatu Lund Date: Mon, 20 Apr 2020 08:36:00 +0300 Subject: [PATCH 3/5] Fixing SonarCube complaints --- .../main/java/com/vaadin/flow/data/binder/Binder.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java b/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java index d85bf3c6fde..2a0359c31da 100644 --- a/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java +++ b/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java @@ -240,14 +240,14 @@ default BindingValidationStatus validate() { * Define whether validators are disabled or enabled for this * specific binding. * - * @param validatorsDisabled A boolean value + * @param validatorsDisabled A boolean value. */ public void setValidatorsDisabled(boolean validatorsDisabled); /** - * Returns if validators are currently disabled or not + * Returns if validators are currently disabled or not. * - * @return A boolean value + * @return A boolean value. */ public boolean isValidatorsDisabled(); } @@ -1338,7 +1338,7 @@ public boolean isAsRequiredEnabled() { @Override public void setValidatorsDisabled(boolean validatorsDisabled) { - this.validatorsDisabled = validatorsDisabled; + this.validatorsDisabled = validatorsDisabled; } @Override @@ -3069,7 +3069,7 @@ public void removeBinding(String propertyName) { * Control whether validators including bean level validators are * disabled or enabled globally for this Binder. * - * @param validatorsDisabled Boolean value + * @param validatorsDisabled Boolean value. */ public void setValidatorsDisabled(boolean validatorsDisabled) { this.validatorsDisabled = validatorsDisabled; From 62d2f1d167bfff37e4f1aa8799cf93e1de2517f1 Mon Sep 17 00:00:00 2001 From: Tatu Lund Date: Mon, 20 Apr 2020 09:18:03 +0300 Subject: [PATCH 4/5] Fixing whitespace --- flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java b/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java index 2a0359c31da..72cd5093f46 100644 --- a/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java +++ b/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java @@ -1891,7 +1891,7 @@ public void writeBeanAsDraft(BEAN bean) { * @param forced * disable all Validators during write */ - public void writeBeanAsDraft(BEAN bean, boolean forced) { + public void writeBeanAsDraft(BEAN bean, boolean forced) { doWriteDraft(bean, new ArrayList<>(bindings),true); } From 8b096ea79eefd0e7e1a19891a1998ed52467eb6b Mon Sep 17 00:00:00 2001 From: Tatu Lund Date: Mon, 20 Apr 2020 13:16:10 +0300 Subject: [PATCH 5/5] Fixing --- flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java b/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java index 72cd5093f46..28eeb6c7eb4 100644 --- a/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java +++ b/flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java @@ -1892,7 +1892,7 @@ public void writeBeanAsDraft(BEAN bean) { * disable all Validators during write */ public void writeBeanAsDraft(BEAN bean, boolean forced) { - doWriteDraft(bean, new ArrayList<>(bindings),true); + doWriteDraft(bean, new ArrayList<>(bindings),forced); } /**