Browse Source

Add methods to control validation (#11945)

- 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/framework/issues/10709

Co-authored-by: Zhe Sun <31067185+ZheSun88@users.noreply.github.com>
tags/8.11.0.alpha1
Tatu Lund 4 years ago
parent
commit
d35bd4cde4
No account linked to committer's email address

+ 14
- 0
pom.xml View File

</properties> </properties>


<pluginRepositories> <pluginRepositories>
<pluginRepository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository> <pluginRepository>
<id>vaadin-snapshots</id> <id>vaadin-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url> <url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>
</pluginRepositories> </pluginRepositories>


<repositories> <repositories>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository> <repository>
<id>vaadin-snapshots</id> <id>vaadin-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url> <url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>

+ 114
- 8
server/src/main/java/com/vaadin/data/Binder.java View File

* @since 8.10 * @since 8.10
*/ */
public boolean isAsRequiredEnabled(); public boolean isAsRequiredEnabled();

/**
* Define whether validators are disabled or enabled for this
* specific binding.
*
* @param validatorsDisabled A boolean value
*
* @since
*/
public void setValidatorsDisabled(boolean validatorsDisabled);

/**
* Returns if validators are currently disabled or not
*
* @return A boolean value
*
* @since
*/
public boolean isValidatorsDisabled();
} }


/** /**
private final HasValue<FIELDVALUE> field; private final HasValue<FIELDVALUE> field;
private BindingValidationStatusHandler statusHandler; private BindingValidationStatusHandler statusHandler;
private boolean isStatusHandlerChanged; private boolean isStatusHandlerChanged;
private Binding<BEAN, TARGET> binding;


private boolean bound; private boolean bound;




bound = true; bound = true;
getBinder().incompleteBindings.remove(getField()); getBinder().incompleteBindings.remove(getField());
this.binding = binding;


return binding; return binding;
} }
Binding binding = ((BindingBuilder) finalBinding).bind(getter, Binding binding = ((BindingBuilder) finalBinding).bind(getter,
setter); setter);
getBinder().boundProperties.put(propertyName, binding); getBinder().boundProperties.put(propertyName, binding);
this.binding = binding;
return binding; return binding;
} finally { } finally {
getBinder().incompleteMemberFieldBindings.remove(getField()); getBinder().incompleteMemberFieldBindings.remove(getField());
checkUnbound(); checkUnbound();
Objects.requireNonNull(validator, "validator cannot be null"); Objects.requireNonNull(validator, "validator cannot be null");


Validator<? super TARGET> wrappedValidator = ((value, context) -> {
if (getBinder().isValidatorsDisabled() ||
(binding != null && binding.isValidatorsDisabled())) {
return ValidationResult.ok();
} else {
return validator.apply(value, context);
}
});

converterValidatorChain = ((Converter<FIELDVALUE, TARGET>) converterValidatorChain) converterValidatorChain = ((Converter<FIELDVALUE, TARGET>) converterValidatorChain)
.chain(new ValidatorAsConverter<>(validator));
.chain(new ValidatorAsConverter<>(wrappedValidator));
return this; return this;
} }




private boolean asRequiredSet; private boolean asRequiredSet;


private boolean validatorsDisabled = false;

public BindingImpl(BindingBuilderImpl<BEAN, FIELDVALUE, TARGET> builder, public BindingImpl(BindingBuilderImpl<BEAN, FIELDVALUE, TARGET> builder,
ValueProvider<BEAN, TARGET> getter, ValueProvider<BEAN, TARGET> getter,
Setter<BEAN, TARGET> setter) { Setter<BEAN, TARGET> setter) {
public boolean isAsRequiredEnabled() { public boolean isAsRequiredEnabled() {
return field.isRequiredIndicatorVisible(); return field.isRequiredIndicatorVisible();
} }

@Override
public void setValidatorsDisabled(boolean validatorsDisabled) {
this.validatorsDisabled = validatorsDisabled;
}

@Override
public boolean isValidatorsDisabled() {
return validatorsDisabled;
}
} }


/** /**


private Set<Binding<BEAN, ?>> changedBindings = new LinkedHashSet<>(); private Set<Binding<BEAN, ?>> changedBindings = new LinkedHashSet<>();


private boolean validatorsDisabled = false;

/** /**
* Creates a binder using a custom {@link PropertySet} implementation for * Creates a binder using a custom {@link PropertySet} implementation for
* finding and resolving property names for * finding and resolving property names for
* @since 8.10 * @since 8.10
*/ */
public void writeBeanAsDraft(BEAN bean) { 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
* @since
*/
public void writeBeanAsDraft(BEAN bean, boolean forced) {
doWriteDraft(bean, new ArrayList<>(bindings),forced);
} }


/** /**
* the bean to write field values into * the bean to write field values into
* @param bindings * @param bindings
* the set of bindings to write to the bean * 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<Binding<BEAN, ?>> bindings) {
private void doWriteDraft(BEAN bean,
Collection<Binding<BEAN, ?>> bindings, boolean forced) {
Objects.requireNonNull(bean, "bean cannot be null"); Objects.requireNonNull(bean, "bean cannot be null");


bindings.forEach(binding -> ((BindingImpl<BEAN, ?, ?>) binding)
.writeFieldValue(bean));
if (!forced) {
bindings.forEach(binding -> ((BindingImpl<BEAN, ?, ?>) binding)
.writeFieldValue(bean));
} else {
boolean isDisabled = isValidatorsDisabled();
setValidatorsDisabled(true);
bindings.forEach(binding -> ((BindingImpl<BEAN, ?, ?>) binding)
.writeFieldValue(bean));
setValidatorsDisabled(isDisabled);
}
} }


/** /**
*/ */
public Binder<BEAN> withValidator(Validator<? super BEAN> validator) { public Binder<BEAN> withValidator(Validator<? super BEAN> validator) {
Objects.requireNonNull(validator, "validator cannot be null"); Objects.requireNonNull(validator, "validator cannot be null");
validators.add(validator);
Validator<? super BEAN> wrappedValidator = ((value, context) -> {
if (isValidatorsDisabled()) {
return ValidationResult.ok();
} else {
return validator.apply(value, context);
}
});
validators.add(wrappedValidator);
return this; return this;
} }


.ifPresent(Binding::unbind); .ifPresent(Binding::unbind);
} }


/**
* Control whether validators including bean level validators are
* disabled or enabled globally for this Binder.
*
* @param validatorsDisabled Boolean value
*
* @since
*/
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
*
* @since
*/
public boolean isValidatorsDisabled() {
return validatorsDisabled;
}

private static final Logger getLogger() { private static final Logger getLogger() {
return Logger.getLogger(Binder.class.getName()); return Logger.getLogger(Binder.class.getName());
} }

+ 77
- 2
server/src/test/java/com/vaadin/data/BinderTest.java View File

Binder<Person> binder = new Binder<>(); Binder<Person> binder = new Binder<>();
binder.forField(nameField) binder.forField(nameField)
.withValidator((value,context) -> { .withValidator((value,context) -> {
if (value.equals("Mike")) return ValidationResult.ok();
else return ValidationResult.error("value must be Mike");
if (value.equals("Mike")) {
return ValidationResult.ok();
} else {
return ValidationResult.error("value must be Mike");
}
}) })
.bind(Person::getFirstName, Person::setFirstName); .bind(Person::getFirstName, Person::setFirstName);
binder.forField(ageField) binder.forField(ageField)
// age is written to draft even if firstname validation // age is written to draft even if firstname validation
// fails // fails
assertEquals(age, person.getAge()); 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<Person> binder = new Binder<>();
Binding<Person, String> 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<Person> 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 @Test

Loading…
Cancel
Save