Browse Source

Fix bean validation when using sub property bindings (#9248)

Fixes #9242
tags/8.1.0.alpha7
Artur 7 years ago
parent
commit
a9fdc693a0

+ 20
- 2
server/src/main/java/com/vaadin/data/BeanPropertySet.java View File

@@ -21,7 +21,6 @@ import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -201,7 +200,17 @@ public class BeanPropertySet<T> implements PropertySet<T> {
}
}

private static class NestedBeanPropertyDefinition<T, V>
/**
* Contains properties for a bean type which is nested in another
* definition.
*
* @since
* @param <T>
* the bean type
* @param <V>
* the value type returned by the getter and set by the setter
*/
public static class NestedBeanPropertyDefinition<T, V>
extends AbstractBeanPropertyDefinition<T, V> {

private final PropertyDefinition<T, ?> parent;
@@ -250,6 +259,15 @@ public class BeanPropertySet<T> implements PropertySet<T> {
return new SerializedPropertyDefinition(getPropertySet().beanType,
parent.getName() + "." + getName());
}

/**
* Gets the parent property definition.
*
* @return the property definition for the parent
*/
public PropertyDefinition<T, ?> getParent() {
return parent;
}
}

private static final ConcurrentMap<Class<?>, BeanPropertySet<?>> instances = new ConcurrentHashMap<>();

+ 28
- 4
server/src/main/java/com/vaadin/data/BeanValidationBinder.java View File

@@ -19,6 +19,7 @@ import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.ConstraintDescriptor;
import javax.validation.metadata.PropertyDescriptor;

import com.vaadin.data.BeanPropertySet.NestedBeanPropertyDefinition;
import com.vaadin.data.util.BeanUtil;
import com.vaadin.data.validator.BeanValidator;

@@ -69,7 +70,7 @@ public class BeanValidationBinder<BEAN> extends Binder<BEAN> {
* <p>
* By default the {@link RequiredFieldConfigurator#DEFAULT} configurator is
* used.
*
*
* @param configurator
* required indicator configurator, may be {@code null}
*/
@@ -80,9 +81,9 @@ public class BeanValidationBinder<BEAN> extends Binder<BEAN> {

/**
* Gets field required indicator configuration logic.
*
*
* @see #setRequiredConfigurator(RequiredFieldConfigurator)
*
*
* @return required indicator configurator, may be {@code null}
*/
public RequiredFieldConfigurator getRequiredConfigurator() {
@@ -93,7 +94,8 @@ public class BeanValidationBinder<BEAN> extends Binder<BEAN> {
protected BindingBuilder<BEAN, ?> configureBinding(
BindingBuilder<BEAN, ?> binding,
PropertyDefinition<BEAN, ?> definition) {
BeanValidator validator = new BeanValidator(beanType,
Class<?> actualBeanType = findBeanType(beanType, definition);
BeanValidator validator = new BeanValidator(actualBeanType,
definition.getName());
if (requiredConfigurator != null) {
configureRequired(binding, definition, validator);
@@ -101,6 +103,28 @@ public class BeanValidationBinder<BEAN> extends Binder<BEAN> {
return binding.withValidator(validator);
}

/**
* Finds the bean type containing the property the given definition refers
* to.
*
* @param beanType
* the root beanType
* @param definition
* the definition for the property
* @return the bean type containing the given property
*/
@SuppressWarnings({ "rawtypes" })
private Class<?> findBeanType(Class<BEAN> beanType,
PropertyDefinition<BEAN, ?> definition) {
if (definition instanceof NestedBeanPropertyDefinition) {
return ((NestedBeanPropertyDefinition) definition).getParent()
.getType();
} else {
// Non nested properties must be defined in the main type
return beanType;
}
}

private void configureRequired(BindingBuilder<BEAN, ?> binding,
PropertyDefinition<BEAN, ?> definition, BeanValidator validator) {
assert requiredConfigurator != null;

+ 87
- 9
server/src/test/java/com/vaadin/data/BeanBinderTest.java View File

@@ -17,6 +17,8 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.data.BeanBinderTest.RequiredConstraints.SubConstraint;
import com.vaadin.data.BeanBinderTest.RequiredConstraints.SubSubConstraint;
import com.vaadin.data.converter.StringToIntegerConverter;
import com.vaadin.tests.data.bean.BeanToValidate;
import com.vaadin.ui.CheckBoxGroup;
@@ -33,7 +35,7 @@ public class BeanBinderTest
private TextField number = new TextField();
}

private static class TestBean implements Serializable{
private static class TestBean implements Serializable {
private Set<TestEnum> enums;
private int number;

@@ -54,7 +56,7 @@ public class BeanBinderTest
}
}

public static class RequiredConstraints implements Serializable{
public static class RequiredConstraints implements Serializable {
@NotNull
@Max(10)
private String firstname;
@@ -67,7 +69,7 @@ public class BeanBinderTest
private String lastname;

private SubConstraint subfield;
public String getFirstname() {
return firstname;
}
@@ -91,8 +93,7 @@ public class BeanBinderTest
public void setLastname(String lastname) {
this.lastname = lastname;
}

public SubConstraint getSubfield() {
return subfield;
}
@@ -101,12 +102,15 @@ public class BeanBinderTest
this.subfield = subfield;
}

public static class SubConstraint implements Serializable {

public static class SubConstraint implements Serializable{
@NotNull
@NotEmpty
@Size(min = 5)
private String name;

private SubSubConstraint subsub;

public String getName() {
return name;
}
@@ -114,6 +118,30 @@ public class BeanBinderTest
public void setName(String name) {
this.name = name;
}

public SubSubConstraint getSubsub() {
return subsub;
}

public void setSubsub(SubSubConstraint subsub) {
this.subsub = subsub;
}

}

public static class SubSubConstraint implements Serializable {

@Size(min = 10)
private String value;

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

}
}

@@ -363,14 +391,14 @@ public class BeanBinderTest
Assert.assertTrue(field.isRequiredIndicatorVisible());
testSerialization(binder);
}
@Test
public void subfield_name_fieldIsRequired() {
BeanValidationBinder<RequiredConstraints> binder = new BeanValidationBinder<>(
RequiredConstraints.class);
RequiredConstraints bean = new RequiredConstraints();
bean.setSubfield(new RequiredConstraints.SubConstraint());
TextField field = new TextField();
binder.bind(field, "subfield.name");
binder.setBean(bean);
@@ -379,6 +407,56 @@ public class BeanBinderTest
testSerialization(binder);
}

@Test
public void subsubfield_name_fieldIsRequired() {
BeanValidationBinder<RequiredConstraints> binder = new BeanValidationBinder<>(
RequiredConstraints.class);
RequiredConstraints bean = new RequiredConstraints();
RequiredConstraints.SubConstraint subfield = new RequiredConstraints.SubConstraint();
subfield.setSubsub(new SubSubConstraint());
bean.setSubfield(subfield);

TextField field = new TextField();
binder.bind(field, "subfield.subsub.value");
binder.setBean(bean);

Assert.assertTrue(field.isRequiredIndicatorVisible());
testSerialization(binder);
}

@Test
public void subfield_name_valueCanBeValidated() {
BeanValidationBinder<RequiredConstraints> binder = new BeanValidationBinder<>(
RequiredConstraints.class);
TextField field = new TextField();

binder.bind(field, "subfield.name");
RequiredConstraints bean = new RequiredConstraints();
bean.setSubfield(new SubConstraint());
binder.setBean(bean);
Assert.assertFalse(binder.validate().isOk());
field.setValue("overfive");
Assert.assertTrue(binder.validate().isOk());
}

@Test
public void subSubfield_name_valueCanBeValidated() {
BeanValidationBinder<RequiredConstraints> binder = new BeanValidationBinder<>(
RequiredConstraints.class);
TextField field = new TextField();

binder.bind(field, "subfield.subsub.value");
RequiredConstraints bean = new RequiredConstraints();
SubConstraint subfield = new SubConstraint();
bean.setSubfield(subfield);
subfield.setSubsub(new SubSubConstraint());
binder.setBean(bean);

Assert.assertFalse(binder.validate().isOk());
field.setValue("overtencharacters");
Assert.assertTrue(binder.validate().isOk());
}

private void assertInvalid(HasValue<?> field, String message) {
BinderValidationStatus<?> status = binder.validate();
List<BindingValidationStatus<?>> errors = status

Loading…
Cancel
Save