]> source.dussan.org Git - vaadin-framework.git/commitdiff
Throw if there are no automatically bound fields via bindInstanceFields. (#8481)
authorDenis <denis@vaadin.com>
Wed, 8 Feb 2017 07:43:34 +0000 (09:43 +0200)
committerPekka Hyvönen <pekka@vaadin.com>
Wed, 8 Feb 2017 07:43:34 +0000 (09:43 +0200)
* Throw if there are no automatically bound fields via bindInstanceFields.

Fixes #8362

server/src/main/java/com/vaadin/data/Binder.java
server/src/test/java/com/vaadin/data/BinderInstanceFieldTest.java

index 5fa486319a05063a58fdc2efb894bbfe0b7af4c9..8c5bd0993852e212d7091c01e9edab29dea5a562 100644 (file)
@@ -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;
     }
 
     /**
index de58be5e4297310e4ddb32c3e23d0e4b59f117e2..4b3bff2db6c1646f3899ed9633286130f585d39d 100644 (file)
@@ -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);
+    }
 }