]> source.dussan.org Git - vaadin-framework.git/commitdiff
Case-insensitive FieldGroup binding #10426 05/505/3
authorJonni Nakari <jonni@vaadin.com>
Mon, 17 Dec 2012 13:03:04 +0000 (15:03 +0200)
committerVaadin Code Review <review@vaadin.com>
Mon, 17 Dec 2012 13:44:44 +0000 (13:44 +0000)
Modified buildAndBindMemberFields to use a new findPropertyId method
when searching propertyIds.

Change-Id: I97f1c4eb8a3de7350b0333c8b2b2ff913e0263d9

server/src/com/vaadin/data/fieldgroup/FieldGroup.java
server/tests/src/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBinding.java [new file with mode: 0644]

index 88030548570b96ed7d8028e0c78fbe281d652a2a..dad2e26497da63ba714d655bf32ee537db1d066b 100644 (file)
@@ -693,10 +693,11 @@ public class FieldGroup implements Serializable {
      * Binds member fields found in the given object.
      * <p>
      * This method processes all (Java) member fields whose type extends
-     * {@link Field} and that can be mapped to a property id. Property id
-     * mapping is done based on the field name or on a @{@link PropertyId}
-     * annotation on the field. All non-null fields for which a property id can
-     * be determined are bound to the property id.
+     * {@link Field} and that can be mapped to a property id. Property ids are
+     * searched in the following order: @{@link PropertyId} annotations, exact
+     * field name matches and the case-insensitive matching that ignores
+     * underscores. All non-null fields for which a property id can be
+     * determined are bound to the property id.
      * </p>
      * <p>
      * For example:
@@ -733,11 +734,12 @@ public class FieldGroup implements Serializable {
      * that have not been initialized.
      * <p>
      * This method processes all (Java) member fields whose type extends
-     * {@link Field} and that can be mapped to a property id. Property id
-     * mapping is done based on the field name or on a @{@link PropertyId}
-     * annotation on the field. Fields that are not initialized (null) are built
-     * using the field factory. All non-null fields for which a property id can
-     * be determined are bound to the property id.
+     * {@link Field} and that can be mapped to a property id. Property ids are
+     * searched in the following order: @{@link PropertyId} annotations, exact
+     * field name matches and the case-insensitive matching that ignores
+     * underscores. Fields that are not initialized (null) are built using the
+     * field factory. All non-null fields for which a property id can be
+     * determined are bound to the property id.
      * </p>
      * <p>
      * For example:
@@ -777,11 +779,12 @@ public class FieldGroup implements Serializable {
      * member fields that have not been initialized.
      * <p>
      * This method processes all (Java) member fields whose type extends
-     * {@link Field} and that can be mapped to a property id. Property id
-     * mapping is done based on the field name or on a @{@link PropertyId}
-     * annotation on the field. Fields that are not initialized (null) are built
-     * using the field factory is buildFields is true. All non-null fields for
-     * which a property id can be determined are bound to the property id.
+     * {@link Field} and that can be mapped to a property id. Property ids are
+     * searched in the following order: @{@link PropertyId} annotations, exact
+     * field name matches and the case-insensitive matching that ignores
+     * underscores. Fields that are not initialized (null) are built using the
+     * field factory is buildFields is true. All non-null fields for which a
+     * property id can be determined are bound to the property id.
      * </p>
      * 
      * @param objectWithMemberFields
@@ -792,6 +795,10 @@ public class FieldGroup implements Serializable {
      */
     protected void buildAndBindMemberFields(Object objectWithMemberFields,
             boolean buildFields) throws BindException {
+        if (getItemDataSource() == null) {
+            // no data source set, cannot find property ids
+            return;
+        }
         Class<?> objectClass = objectWithMemberFields.getClass();
 
         for (java.lang.reflect.Field memberField : getFieldsInDeclareOrder(objectClass)) {
@@ -812,7 +819,11 @@ public class FieldGroup implements Serializable {
                 // @PropertyId(propertyId) always overrides property id
                 propertyId = propertyIdAnnotation.value();
             } else {
-                propertyId = memberField.getName();
+                propertyId = findPropertyId(memberField);
+                if (propertyId == null) {
+                    // Property id was not found, skip this field
+                    continue;
+                }
             }
 
             // Ensure that the property id exists
@@ -873,6 +884,51 @@ public class FieldGroup implements Serializable {
         }
     }
 
+    /**
+     * Searches for a property id from the current itemDataSource that matches
+     * the given memberField.
+     * <p>
+     * If perfect match is not found, uses a case insensitive search that also
+     * ignores underscores. Returns null if no match is found. Throws a
+     * SearchException if no item data source has been set.
+     * </p>
+     * <p>
+     * The propertyId search logic used by
+     * {@link #buildAndBindMemberFields(Object, boolean)
+     * buildAndBindMemberFields} can easily be customized by overriding this
+     * method. No other changes are needed.
+     * </p>
+     * 
+     * @param memberField
+     *            The field an object id is searched for
+     * @return
+     */
+    protected Object findPropertyId(java.lang.reflect.Field memberField) {
+        String fieldName = memberField.getName();
+        if (getItemDataSource() == null) {
+            throw new SearchException(
+                    "Property id type for field '"
+                            + fieldName
+                            + "' could not be determined. No item data source has been set.");
+        }
+        Item dataSource = getItemDataSource();
+        if (dataSource.getItemProperty(fieldName) != null) {
+            return fieldName;
+        } else {
+            String minifiedFieldName = fieldName.toLowerCase().replace("_", "");
+            for (Object itemPropertyId : dataSource.getItemPropertyIds()) {
+                if (itemPropertyId instanceof String) {
+                    String itemPropertyName = (String) itemPropertyId;
+                    if (minifiedFieldName.equals(itemPropertyName.toLowerCase()
+                            .replace("_", ""))) {
+                        return itemPropertyName;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
     public static class CommitException extends Exception {
 
         public CommitException() {
@@ -909,6 +965,18 @@ public class FieldGroup implements Serializable {
 
     }
 
+    public static class SearchException extends RuntimeException {
+
+        public SearchException(String message) {
+            super(message);
+        }
+
+        public SearchException(String message, Throwable t) {
+            super(message, t);
+        }
+
+    }
+
     /**
      * Builds a field and binds it to the given property id using the field
      * binder.
diff --git a/server/tests/src/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBinding.java b/server/tests/src/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBinding.java
new file mode 100644 (file)
index 0000000..9b768ef
--- /dev/null
@@ -0,0 +1,84 @@
+package com.vaadin.tests.server.component.fieldgroup;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.vaadin.data.fieldgroup.FieldGroup;
+import com.vaadin.data.util.ObjectProperty;
+import com.vaadin.data.util.PropertysetItem;
+import com.vaadin.ui.FormLayout;
+import com.vaadin.ui.TextField;
+
+public class CaseInsensitiveBinding {
+
+    @Test
+    public void caseInsensitivityAndUnderscoreRemoval() {
+        PropertysetItem item = new PropertysetItem();
+        item.addItemProperty("LastName", new ObjectProperty<String>("Sparrow"));
+
+        class MyForm extends FormLayout {
+            TextField lastName = new TextField("Last name");
+
+            public MyForm() {
+
+                // Should bind to the LastName property
+                addComponent(lastName);
+            }
+        }
+
+        MyForm form = new MyForm();
+
+        FieldGroup binder = new FieldGroup(item);
+        binder.bindMemberFields(form);
+
+        assertTrue("Sparrow".equals(form.lastName.getValue()));
+    }
+
+    @Test
+    public void UnderscoreRemoval() {
+        PropertysetItem item = new PropertysetItem();
+        item.addItemProperty("first_name", new ObjectProperty<String>("Jack"));
+
+        class MyForm extends FormLayout {
+            TextField firstName = new TextField("First name");
+
+            public MyForm() {
+                // Should bind to the first_name property
+                addComponent(firstName);
+            }
+        }
+
+        MyForm form = new MyForm();
+
+        FieldGroup binder = new FieldGroup(item);
+        binder.bindMemberFields(form);
+
+        assertTrue("Jack".equals(form.firstName.getValue()));
+    }
+
+    @Test
+    public void perfectMatchPriority() {
+        PropertysetItem item = new PropertysetItem();
+        item.addItemProperty("first_name", new ObjectProperty<String>(
+                "Not this"));
+        item.addItemProperty("firstName", new ObjectProperty<String>("This"));
+
+        class MyForm extends FormLayout {
+            TextField firstName = new TextField("First name");
+
+            public MyForm() {
+                // should bind to the firstName property, not first_name property
+                addComponent(firstName);
+            }
+        }
+
+        MyForm form = new MyForm();
+
+        FieldGroup binder = new FieldGroup(item);
+        binder.bindMemberFields(form);
+
+        assertTrue("This".equals(form.firstName.getValue()));
+    }
+
+}