diff options
author | Denis <denis@vaadin.com> | 2017-02-08 09:43:34 +0200 |
---|---|---|
committer | Pekka Hyvönen <pekka@vaadin.com> | 2017-02-08 09:43:34 +0200 |
commit | 8c7974592f2a3fb289773c53c5ffe06481ff51b8 (patch) | |
tree | 16bf3f3f28d784af539e9d752ff3d864460739df | |
parent | b57a9cbd6e2d6eeed83e7d9ddd0e61561b3cd557 (diff) | |
download | vaadin-framework-8c7974592f2a3fb289773c53c5ffe06481ff51b8.tar.gz vaadin-framework-8c7974592f2a3fb289773c53c5ffe06481ff51b8.zip |
Throw if there are no automatically bound fields via bindInstanceFields. (#8481)
* Throw if there are no automatically bound fields via bindInstanceFields.
Fixes #8362
-rw-r--r-- | server/src/main/java/com/vaadin/data/Binder.java | 49 | ||||
-rw-r--r-- | server/src/test/java/com/vaadin/data/BinderInstanceFieldTest.java | 44 |
2 files changed, 69 insertions, 24 deletions
diff --git a/server/src/main/java/com/vaadin/data/Binder.java b/server/src/main/java/com/vaadin/data/Binder.java index 5fa486319a..8c5bd09938 100644 --- a/server/src/main/java/com/vaadin/data/Binder.java +++ b/server/src/main/java/com/vaadin/data/Binder.java @@ -31,7 +31,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.function.BiConsumer; +import java.util.function.BiFunction; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -1579,7 +1579,7 @@ public class Binder<BEAN> implements Serializable { fireStatusChangeEvent(validationStatus.hasErrors()); return validationStatus; } - + /** * Runs all currently configured field level validators, as well as all bean * level validators if a bean is currently set with @@ -1593,10 +1593,9 @@ public class Binder<BEAN> implements Serializable { */ public boolean isValid() { if (getBean() == null && !validators.isEmpty()) { - throw new IllegalStateException( - "Cannot validate binder: " - + "bean level validators have been configured " - + "but no bean is currently set"); + throw new IllegalStateException("Cannot validate binder: " + + "bean level validators have been configured " + + "but no bean is currently set"); } if (validateBindings().stream().filter(BindingValidationStatus::isError) .findAny().isPresent()) { @@ -2104,13 +2103,23 @@ public class Binder<BEAN> implements Serializable { public void bindInstanceFields(Object objectWithMemberFields) { Class<?> objectClass = objectWithMemberFields.getClass(); - getFieldsInDeclareOrder(objectClass).stream() + Integer numberOfBoundFields = getFieldsInDeclareOrder(objectClass) + .stream() .filter(memberField -> HasValue.class .isAssignableFrom(memberField.getType())) - .forEach(memberField -> handleProperty(memberField, + .map(memberField -> handleProperty(memberField, objectWithMemberFields, (property, type) -> bindProperty(objectWithMemberFields, - memberField, property, type))); + memberField, property, type))) + .reduce(0, this::accumulate, Integer::sum); + if (numberOfBoundFields == 0) { + throw new IllegalStateException("There are no instance fields " + + "found for automatic binding"); + } + } + + private int accumulate(int count, boolean value) { + return value ? count + 1 : count; } @SuppressWarnings("unchecked") @@ -2141,9 +2150,10 @@ public class Binder<BEAN> implements Serializable { * property name to bind * @param propertyType * type of the property + * @return {@code true} if property is successfully bound */ - private void bindProperty(Object objectWithMemberFields, Field memberField, - String property, Class<?> propertyType) { + private boolean bindProperty(Object objectWithMemberFields, + Field memberField, String property, Class<?> propertyType) { Type valueType = GenericTypeReflector.getTypeParameter( memberField.getGenericType(), HasValue.class.getTypeParameters()[0]); @@ -2163,7 +2173,7 @@ public class Binder<BEAN> implements Serializable { } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { // If we cannot determine the value, just skip the field - return; + return false; } if (field == null) { field = makeFieldInstance( @@ -2171,6 +2181,7 @@ public class Binder<BEAN> implements Serializable { initializeField(objectWithMemberFields, memberField, field); } forField(field).bind(property); + return true; } else { throw new IllegalStateException(String.format( "Property type '%s' doesn't " @@ -2243,29 +2254,31 @@ public class Binder<BEAN> implements Serializable { } } - private void handleProperty(Field field, Object objectWithMemberFields, - BiConsumer<String, Class<?>> propertyHandler) { + private boolean handleProperty(Field field, Object objectWithMemberFields, + BiFunction<String, Class<?>, Boolean> propertyHandler) { Optional<PropertyDefinition<BEAN, ?>> descriptor = getPropertyDescriptor( field); if (!descriptor.isPresent()) { - return; + return false; } String propertyName = descriptor.get().getName(); if (boundProperties.containsKey(propertyName)) { - return; + return false; } BindingBuilder<BEAN, ?> tentativeBinding = getIncompleteMemberFieldBinding( field, objectWithMemberFields); if (tentativeBinding != null) { tentativeBinding.bind(propertyName); - return; + return false; } - propertyHandler.accept(propertyName, descriptor.get().getType()); + Boolean isPropertyBound = propertyHandler.apply(propertyName, + descriptor.get().getType()); assert boundProperties.containsKey(propertyName); + return isPropertyBound; } /** diff --git a/server/src/test/java/com/vaadin/data/BinderInstanceFieldTest.java b/server/src/test/java/com/vaadin/data/BinderInstanceFieldTest.java index de58be5e42..4b3bff2db6 100644 --- a/server/src/test/java/com/vaadin/data/BinderInstanceFieldTest.java +++ b/server/src/test/java/com/vaadin/data/BinderInstanceFieldTest.java @@ -50,8 +50,15 @@ public class BinderInstanceFieldTest { private TextField noFieldInPerson; } - public static class BindNoHasValueField extends FormLayout { + public static class BindWithNoFieldInPerson extends FormLayout { + private TextField firstName; + private DateField birthDate; + private TextField noFieldInPerson; + } + + public static class BindFieldHasWrongType extends FormLayout { private String firstName; + private DateField birthDate; } public static class BindGenericField extends FormLayout { @@ -174,7 +181,7 @@ public class BinderInstanceFieldTest { @Test public void bindInstanceFields_bindNotHasValueField_fieldIsNull() { - BindNoHasValueField form = new BindNoHasValueField(); + BindFieldHasWrongType form = new BindFieldHasWrongType(); Binder<Person> binder = new Binder<>(Person.class); binder.bindInstanceFields(form); @@ -266,14 +273,12 @@ public class BinderInstanceFieldTest { @Test public void bindInstanceFields_bindNotHasValueField_fieldIsNotReplaced() { - BindNoHasValueField form = new BindNoHasValueField(); + BindFieldHasWrongType form = new BindFieldHasWrongType(); Binder<Person> binder = new Binder<>(Person.class); String name = "foo"; form.firstName = name; - binder.bindInstanceFields(form); - Person person = new Person(); person.setFirstName("foo"); @@ -343,7 +348,7 @@ public class BinderInstanceFieldTest { @Test public void bindInstanceFields_fieldsAreConfigured_customBindingIsNotReplaced() { - BindOnlyOneField form = new BindOnlyOneField(); + BindWithNoFieldInPerson form = new BindWithNoFieldInPerson(); Binder<Person> binder = new Binder<>(Person.class); TextField name = new TextField(); @@ -385,4 +390,31 @@ public class BinderInstanceFieldTest { Assert.assertFalse(binder.validate().isOk()); } + + @Test(expected = IllegalStateException.class) + public void bindInstanceFields_explicitelyBoundFieldAndNotBoundField_throwNoBoundFields() { + BindOnlyOneField form = new BindOnlyOneField(); + Binder<Person> binder = new Binder<>(Person.class); + + binder.forField(new TextField()).bind("firstName"); + + binder.bindInstanceFields(form); + } + + @Test(expected = IllegalStateException.class) + public void bindInstanceFields_tentativelyBoundFieldAndNotBoundField_throwNoBoundFields() { + BindOnlyOneField form = new BindOnlyOneField(); + Binder<Person> binder = new Binder<>(Person.class); + + TextField field = new TextField(); + form.firstName = field; + + // This is an incomplete binding which is supposed to be configured + // manually + binder.forMemberField(field); + + // bindInstance expects at least one auto configured field (there is no + // such, only incomplete one) and fails + binder.bindInstanceFields(form); + } } |