+++ /dev/null
-/* \r
-@VaadinApache2LicenseForJavaFiles@\r
- */\r
-package com.vaadin.data.fieldbinder;\r
-\r
-import com.vaadin.data.Item;\r
-import com.vaadin.data.util.BeanItem;\r
-import com.vaadin.data.validator.BeanValidationValidator;\r
-import com.vaadin.ui.Field;\r
-\r
-public class BeanFieldBinder<T> extends FieldBinder {\r
-\r
- private Class<T> beanType;\r
-\r
- public BeanFieldBinder(Class<T> beanType) {\r
- this.beanType = beanType;\r
- }\r
-\r
- @Override\r
- protected Class<?> getPropertyType(Object propertyId) {\r
- if (getItemDataSource() != null) {\r
- return super.getPropertyType(propertyId);\r
- } else {\r
- // Data source not set so we need to figure out the type manually\r
- /*\r
- * toString should never really be needed as propertyId should be of\r
- * form "fieldName" or "fieldName.subField[.subField2]" but the\r
- * method declaration comes from parent.\r
- */\r
- java.lang.reflect.Field f;\r
- try {\r
- f = getField(beanType, propertyId.toString());\r
- return f.getType();\r
- } catch (SecurityException e) {\r
- throw new BindException("Cannot determine type of propertyId '"\r
- + propertyId + "'.", e);\r
- } catch (NoSuchFieldException e) {\r
- throw new BindException("Cannot determine type of propertyId '"\r
- + propertyId + "'. The propertyId was not found in "\r
- + beanType.getName(), e);\r
- }\r
- }\r
- }\r
-\r
- private static java.lang.reflect.Field getField(Class<?> cls,\r
- String propertyId) throws SecurityException, NoSuchFieldException {\r
- if (propertyId.contains(".")) {\r
- String[] parts = propertyId.split("\\.", 2);\r
- // Get the type of the field in the "cls" class\r
- java.lang.reflect.Field field1 = getField(cls, parts[0]);\r
- // Find the rest from the sub type\r
- return getField(field1.getType(), parts[1]);\r
- } else {\r
- try {\r
- // Try to find the field directly in the given class\r
- java.lang.reflect.Field field1 = cls\r
- .getDeclaredField(propertyId);\r
- return field1;\r
- } catch (NoSuchFieldError e) {\r
- // Try super classes until we reach Object\r
- Class<?> superClass = cls.getSuperclass();\r
- if (superClass != Object.class) {\r
- return getField(superClass, propertyId);\r
- } else {\r
- throw e;\r
- }\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Helper method for setting the data source directly using a bean. This\r
- * method wraps the bean in a {@link BeanItem} and calls\r
- * {@link #setItemDataSource(Item)}.\r
- * \r
- * @param bean\r
- * The bean to use as data source.\r
- */\r
- public void setItemDataSource(T bean) {\r
- setItemDataSource(new BeanItem(bean));\r
- }\r
-\r
- @Override\r
- public void setItemDataSource(Item item) {\r
- if (!(item instanceof BeanItem)) {\r
- throw new RuntimeException(getClass().getSimpleName()\r
- + " only supports BeanItems as item data source");\r
- }\r
- super.setItemDataSource(item);\r
- }\r
-\r
- @Override\r
- public BeanItem<T> getItemDataSource() {\r
- return (BeanItem<T>) super.getItemDataSource();\r
- }\r
-\r
- @Override\r
- public void bind(Field field, Object propertyId) {\r
- if (getItemDataSource() != null) {\r
- // The data source is set so the property must be found in the item.\r
- // If it is not we try to add it.\r
- try {\r
- getItemProperty(propertyId);\r
- } catch (BindException e) {\r
- // Not found, try to add a nested property;\r
- // BeanItem property ids are always strings so this is safe\r
- getItemDataSource().addNestedProperty((String) propertyId);\r
- }\r
- }\r
-\r
- super.bind(field, propertyId);\r
- }\r
-\r
- @Override\r
- protected void configureField(Field<?> field) {\r
- super.configureField(field);\r
- // Add Bean validators if there are annotations\r
- if (BeanValidationValidator.isImplementationAvailable()) {\r
- BeanValidationValidator validator = new BeanValidationValidator(\r
- beanType, getPropertyIdForField(field).toString());\r
- field.addValidator(validator);\r
- if (field.getLocale() != null) {\r
- validator.setLocale(field.getLocale());\r
- }\r
- }\r
- }\r
-\r
-}
\ No newline at end of file
--- /dev/null
+/* \r
+@VaadinApache2LicenseForJavaFiles@\r
+ */\r
+package com.vaadin.data.fieldbinder;\r
+\r
+import com.vaadin.data.Item;\r
+import com.vaadin.data.util.BeanItem;\r
+import com.vaadin.data.validator.BeanValidationValidator;\r
+import com.vaadin.ui.Field;\r
+\r
+public class BeanFieldGroup<T> extends FieldGroup {\r
+\r
+ private Class<T> beanType;\r
+\r
+ public BeanFieldGroup(Class<T> beanType) {\r
+ this.beanType = beanType;\r
+ }\r
+\r
+ @Override\r
+ protected Class<?> getPropertyType(Object propertyId) {\r
+ if (getItemDataSource() != null) {\r
+ return super.getPropertyType(propertyId);\r
+ } else {\r
+ // Data source not set so we need to figure out the type manually\r
+ /*\r
+ * toString should never really be needed as propertyId should be of\r
+ * form "fieldName" or "fieldName.subField[.subField2]" but the\r
+ * method declaration comes from parent.\r
+ */\r
+ java.lang.reflect.Field f;\r
+ try {\r
+ f = getField(beanType, propertyId.toString());\r
+ return f.getType();\r
+ } catch (SecurityException e) {\r
+ throw new BindException("Cannot determine type of propertyId '"\r
+ + propertyId + "'.", e);\r
+ } catch (NoSuchFieldException e) {\r
+ throw new BindException("Cannot determine type of propertyId '"\r
+ + propertyId + "'. The propertyId was not found in "\r
+ + beanType.getName(), e);\r
+ }\r
+ }\r
+ }\r
+\r
+ private static java.lang.reflect.Field getField(Class<?> cls,\r
+ String propertyId) throws SecurityException, NoSuchFieldException {\r
+ if (propertyId.contains(".")) {\r
+ String[] parts = propertyId.split("\\.", 2);\r
+ // Get the type of the field in the "cls" class\r
+ java.lang.reflect.Field field1 = getField(cls, parts[0]);\r
+ // Find the rest from the sub type\r
+ return getField(field1.getType(), parts[1]);\r
+ } else {\r
+ try {\r
+ // Try to find the field directly in the given class\r
+ java.lang.reflect.Field field1 = cls\r
+ .getDeclaredField(propertyId);\r
+ return field1;\r
+ } catch (NoSuchFieldError e) {\r
+ // Try super classes until we reach Object\r
+ Class<?> superClass = cls.getSuperclass();\r
+ if (superClass != Object.class) {\r
+ return getField(superClass, propertyId);\r
+ } else {\r
+ throw e;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Helper method for setting the data source directly using a bean. This\r
+ * method wraps the bean in a {@link BeanItem} and calls\r
+ * {@link #setItemDataSource(Item)}.\r
+ * \r
+ * @param bean\r
+ * The bean to use as data source.\r
+ */\r
+ public void setItemDataSource(T bean) {\r
+ setItemDataSource(new BeanItem(bean));\r
+ }\r
+\r
+ @Override\r
+ public void setItemDataSource(Item item) {\r
+ if (!(item instanceof BeanItem)) {\r
+ throw new RuntimeException(getClass().getSimpleName()\r
+ + " only supports BeanItems as item data source");\r
+ }\r
+ super.setItemDataSource(item);\r
+ }\r
+\r
+ @Override\r
+ public BeanItem<T> getItemDataSource() {\r
+ return (BeanItem<T>) super.getItemDataSource();\r
+ }\r
+\r
+ @Override\r
+ public void bind(Field field, Object propertyId) {\r
+ if (getItemDataSource() != null) {\r
+ // The data source is set so the property must be found in the item.\r
+ // If it is not we try to add it.\r
+ try {\r
+ getItemProperty(propertyId);\r
+ } catch (BindException e) {\r
+ // Not found, try to add a nested property;\r
+ // BeanItem property ids are always strings so this is safe\r
+ getItemDataSource().addNestedProperty((String) propertyId);\r
+ }\r
+ }\r
+\r
+ super.bind(field, propertyId);\r
+ }\r
+\r
+ @Override\r
+ protected void configureField(Field<?> field) {\r
+ super.configureField(field);\r
+ // Add Bean validators if there are annotations\r
+ if (BeanValidationValidator.isImplementationAvailable()) {\r
+ BeanValidationValidator validator = new BeanValidationValidator(\r
+ beanType, getPropertyIdForField(field).toString());\r
+ field.addValidator(validator);\r
+ if (field.getLocale() != null) {\r
+ validator.setLocale(field.getLocale());\r
+ }\r
+ }\r
+ }\r
+\r
+}
\ No newline at end of file
+++ /dev/null
-/* \r
-@VaadinApache2LicenseForJavaFiles@\r
- */\r
-package com.vaadin.data.fieldbinder;\r
-\r
-import java.io.Serializable;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.LinkedHashMap;\r
-import java.util.List;\r
-import java.util.logging.Logger;\r
-\r
-import com.vaadin.data.Item;\r
-import com.vaadin.data.Property;\r
-import com.vaadin.data.TransactionalProperty;\r
-import com.vaadin.data.Validator.InvalidValueException;\r
-import com.vaadin.data.fieldbinder.FormBuilder.FormBuilderException;\r
-import com.vaadin.data.util.TransactionalPropertyWrapper;\r
-import com.vaadin.tools.ReflectTools;\r
-import com.vaadin.ui.Field;\r
-\r
-/**\r
- * FIXME Javadoc\r
- * \r
- * See also {@link BeanFieldBinder} which makes binding fields easier when your\r
- * data is in a bean.\r
- * \r
- * @author Vaadin Ltd\r
- * @version @version@\r
- * @since 7.0\r
- */\r
-public class FieldBinder implements Serializable {\r
-\r
- private static final Logger logger = Logger.getLogger(FieldBinder.class\r
- .getName());\r
-\r
- private Item itemDataSource;\r
- private boolean fieldsBuffered = true;\r
-\r
- private boolean fieldsEnabled = true;\r
- private boolean fieldsReadOnly = false;\r
-\r
- private HashMap<Object, Field<?>> propertyIdToField = new HashMap<Object, Field<?>>();\r
- private LinkedHashMap<Field<?>, Object> fieldToPropertyId = new LinkedHashMap<Field<?>, Object>();\r
- private List<CommitHandler> commitHandlers = new ArrayList<CommitHandler>();\r
-\r
- /**\r
- * Constructs a field binder. Use {@link #setItemDataSource(Item)} to set a\r
- * data source for the field binder.\r
- * \r
- */\r
- public FieldBinder() {\r
-\r
- }\r
-\r
- /**\r
- * Constructs a field binder that uses the given data source.\r
- * \r
- * @param itemDataSource\r
- * The data source to bind the fields to\r
- */\r
- public FieldBinder(Item itemDataSource) {\r
- setItemDataSource(itemDataSource);\r
- }\r
-\r
- /**\r
- * Updates the item that is used by this FieldBinder. Rebinds all fields to\r
- * the properties in the new item.\r
- * \r
- * @param itemDataSource\r
- * The new item to use\r
- */\r
- public void setItemDataSource(Item itemDataSource) {\r
- this.itemDataSource = itemDataSource;\r
-\r
- for (Field<?> f : fieldToPropertyId.keySet()) {\r
- bind(f, fieldToPropertyId.get(f));\r
- }\r
- }\r
-\r
- /**\r
- * Gets the item used by this FieldBinder. Note that you must call\r
- * {@link #commit()} for the item to be updated unless buffered mode has\r
- * been switched off.\r
- * \r
- * @see #setFieldsBuffered(boolean)\r
- * @see #commit()\r
- * \r
- * @return The item used by this FieldBinder\r
- */\r
- public Item getItemDataSource() {\r
- return itemDataSource;\r
- }\r
-\r
- /**\r
- * Checks the buffered mode for the bound fields.\r
- * <p>\r
- * \r
- * @see #setFieldsBuffered(boolean) for more details on buffered mode\r
- * \r
- * @see Field#isFieldsBuffered()\r
- * @return true if buffered mode is on, false otherwise\r
- * \r
- */\r
- public boolean isFieldsBuffered() {\r
- return fieldsBuffered;\r
- }\r
-\r
- /**\r
- * Sets the buffered mode for the bound fields.\r
- * <p>\r
- * When buffered mode is on the item will not be updated until\r
- * {@link #commit()} is called. If buffered mode is off the item will be\r
- * updated once the fields are updated.\r
- * </p>\r
- * <p>\r
- * The default is to use buffered mode.\r
- * </p>\r
- * \r
- * @see Field#setFieldsBuffered(boolean)\r
- * @param fieldsBuffered\r
- * true to turn on buffered mode, false otherwise\r
- */\r
- public void setFieldsBuffered(boolean fieldsBuffered) {\r
- if (fieldsBuffered == this.fieldsBuffered) {\r
- return;\r
- }\r
-\r
- this.fieldsBuffered = fieldsBuffered;\r
- for (Field<?> field : getFields()) {\r
- field.setBuffered(fieldsBuffered);\r
- }\r
- }\r
-\r
- /**\r
- * Returns the enabled status for the fields.\r
- * <p>\r
- * Note that this will not accurately represent the enabled status of all\r
- * fields if you change the enabled status of the fields through some other\r
- * method than {@link #setFieldsEnabled(boolean)}.\r
- * \r
- * @return true if the fields are enabled, false otherwise\r
- */\r
- public boolean isFieldsEnabled() {\r
- return fieldsEnabled;\r
- }\r
-\r
- /**\r
- * Updates the enabled state of all bound fields.\r
- * \r
- * @param fieldsEnabled\r
- * true to enable all bound fields, false to disable them\r
- */\r
- public void setFieldsEnabled(boolean fieldsEnabled) {\r
- this.fieldsEnabled = fieldsEnabled;\r
- for (Field<?> field : getFields()) {\r
- field.setEnabled(fieldsEnabled);\r
- }\r
- }\r
-\r
- /**\r
- * Returns the read only status for the fields.\r
- * <p>\r
- * Note that this will not accurately represent the read only status of all\r
- * fields if you change the read only status of the fields through some\r
- * other method than {@link #setFieldsReadOnly(boolean)}.\r
- * \r
- * @return true if the fields are set to read only, false otherwise\r
- */\r
- public boolean isFieldsReadOnly() {\r
- return fieldsReadOnly;\r
- }\r
-\r
- /**\r
- * Updates the read only state of all bound fields.\r
- * \r
- * @param fieldsReadOnly\r
- * true to set all bound fields to read only, false to set them\r
- * to read write\r
- */\r
- public void setFieldsReadOnly(boolean fieldsReadOnly) {\r
- this.fieldsReadOnly = fieldsReadOnly;\r
- }\r
-\r
- /**\r
- * Returns a collection of all fields that have been bound.\r
- * <p>\r
- * The fields are not returned in any specific order.\r
- * </p>\r
- * \r
- * @return A collection with all bound Fields\r
- */\r
- public Collection<Field<?>> getFields() {\r
- return fieldToPropertyId.keySet();\r
- }\r
-\r
- /**\r
- * Binds the field with the given propertyId from the current item. If an\r
- * item has not been set then the binding is postponed until the item is set\r
- * using {@link #setItemDataSource(Item)}.\r
- * <p>\r
- * This method also adds validators when applicable.\r
- * </p>\r
- * \r
- * @param field\r
- * The field to bind\r
- * @param propertyId\r
- * The propertyId to bind to the field\r
- * @throws BindException\r
- * If the property id is already bound to another field by this\r
- * field binder\r
- */\r
- public void bind(Field<?> field, Object propertyId) throws BindException {\r
- if (propertyIdToField.containsKey(propertyId)\r
- && propertyIdToField.get(propertyId) != field) {\r
- throw new BindException("Property id " + propertyId\r
- + " is already bound to another field");\r
- }\r
- fieldToPropertyId.put(field, propertyId);\r
- propertyIdToField.put(propertyId, field);\r
- if (itemDataSource == null) {\r
- // Will be bound when data source is set\r
- return;\r
- }\r
-\r
- field.setPropertyDataSource(wrapInTransactionalProperty(getItemProperty(propertyId)));\r
- configureField(field);\r
- }\r
-\r
- private <T> TransactionalProperty<T> wrapInTransactionalProperty(\r
- Property<T> itemProperty) {\r
- return new TransactionalPropertyWrapper<T>(itemProperty);\r
- }\r
-\r
- /**\r
- * Gets the property with the given property id from the item.\r
- * \r
- * @param propertyId\r
- * The id if the property to find\r
- * @return The property with the given id from the item\r
- * @throws BindException\r
- * If the property was not found in the item or no item has been\r
- * set\r
- */\r
- protected Property<?> getItemProperty(Object propertyId)\r
- throws BindException {\r
- Item item = getItemDataSource();\r
- if (item == null) {\r
- throw new BindException("Could not lookup property with id "\r
- + propertyId + " as no item has been set");\r
- }\r
- Property<?> p = item.getItemProperty(propertyId);\r
- if (p == null) {\r
- throw new BindException("A property with id " + propertyId\r
- + " was not found in the item");\r
- }\r
- return p;\r
- }\r
-\r
- /**\r
- * Detaches the field from its property id and removes it from this\r
- * FieldBinder.\r
- * <p>\r
- * Note that the field is not detached from its property data source if it\r
- * is no longer connected to the same property id it was bound to using this\r
- * FieldBinder.\r
- * \r
- * @param field\r
- * The field to detach\r
- * @throws BindException\r
- * If the field is not bound by this field binder or not bound\r
- * to the correct property id\r
- */\r
- public void remove(Field<?> field) throws BindException {\r
- Object propertyId = fieldToPropertyId.get(field);\r
- if (propertyId == null) {\r
- throw new BindException(\r
- "The given field is not part of this FieldBinder");\r
- }\r
-\r
- Property fieldDataSource = field.getPropertyDataSource();\r
- if (fieldDataSource instanceof TransactionalPropertyWrapper) {\r
- fieldDataSource = ((TransactionalPropertyWrapper) fieldDataSource)\r
- .getWrappedProperty();\r
- }\r
- if (fieldDataSource == getItemProperty(propertyId)) {\r
- field.setPropertyDataSource(null);\r
- }\r
- fieldToPropertyId.remove(field);\r
- propertyIdToField.remove(propertyId);\r
- }\r
-\r
- /**\r
- * Configures a field with the settings set for this FieldBinder.\r
- * <p>\r
- * By default this updates the buffered, read only and enabled state of the\r
- * field. Also adds validators when applicable.\r
- * \r
- * @param field\r
- * The field to update\r
- */\r
- protected void configureField(Field<?> field) {\r
- field.setBuffered(isFieldsBuffered());\r
-\r
- field.setEnabled(isFieldsEnabled());\r
- field.setReadOnly(isFieldsReadOnly());\r
- }\r
-\r
- /**\r
- * Gets the type of the property with the given property id.\r
- * \r
- * @param propertyId\r
- * The propertyId. Must be find\r
- * @return The type of the property\r
- */\r
- protected Class<?> getPropertyType(Object propertyId) throws BindException {\r
- if (getItemDataSource() == null) {\r
- throw new BindException(\r
- "Property type for '"\r
- + propertyId\r
- + "' could not be determined. No item data source has been set.");\r
- }\r
- Property<?> p = getItemDataSource().getItemProperty(propertyId);\r
- if (p == null) {\r
- throw new BindException(\r
- "Property type for '"\r
- + propertyId\r
- + "' could not be determined. No property with that id was found.");\r
- }\r
-\r
- return p.getType();\r
- }\r
-\r
- /**\r
- * Returns a collection of all property ids that have been bound to fields.\r
- * <p>\r
- * Note that this will return property ids even before the item has been\r
- * set. In that case it returns the property ids that will be bound once the\r
- * item is set.\r
- * </p>\r
- * <p>\r
- * No guarantee is given for the order of the property ids\r
- * </p>\r
- * \r
- * @return A collection of bound property ids\r
- */\r
- public Collection<Object> getBoundPropertyIds() {\r
- return Collections.unmodifiableCollection(propertyIdToField.keySet());\r
- }\r
-\r
- /**\r
- * Returns a collection of all property ids that exist in the item set using\r
- * {@link #setItemDataSource(Item)} but have not been bound to fields.\r
- * <p>\r
- * Will always return an empty collection before an item has been set using\r
- * {@link #setItemDataSource(Item)}.\r
- * </p>\r
- * <p>\r
- * No guarantee is given for the order of the property ids\r
- * </p>\r
- * \r
- * @return A collection of property ids that have not been bound to fields\r
- */\r
- protected Collection<Object> getUnboundPropertyIds() {\r
- if (getItemDataSource() == null) {\r
- return new ArrayList<Object>();\r
- }\r
- List<Object> unboundPropertyIds = new ArrayList<Object>();\r
- unboundPropertyIds.addAll(getItemDataSource().getItemPropertyIds());\r
- unboundPropertyIds.removeAll(propertyIdToField.keySet());\r
- return unboundPropertyIds;\r
- }\r
-\r
- /**\r
- * Commits all changes done to the bound fields.\r
- * <p>\r
- * Calls all {@link CommitHandler}s before and after committing the field\r
- * changes to the item data source. The whole commit is aborted and state is\r
- * restored to what it was before commit was called if any\r
- * {@link CommitHandler} throws a CommitException or there is a problem\r
- * committing the fields\r
- * \r
- * @throws CommitException\r
- * If the commit was aborted\r
- */\r
- public void commit() throws CommitException {\r
- if (!isFieldsBuffered()) {\r
- // Not using buffered mode, nothing to do\r
- return;\r
- }\r
- for (Field<?> f : fieldToPropertyId.keySet()) {\r
- ((TransactionalProperty<?>) f.getPropertyDataSource())\r
- .startTransaction();\r
- }\r
- try {\r
- firePreCommitEvent();\r
- // Commit the field values to the properties\r
- for (Field<?> f : fieldToPropertyId.keySet()) {\r
- f.commit();\r
- }\r
- firePostCommitEvent();\r
-\r
- // Commit the properties\r
- for (Field<?> f : fieldToPropertyId.keySet()) {\r
- ((TransactionalProperty<?>) f.getPropertyDataSource()).commit();\r
- }\r
-\r
- } catch (Exception e) {\r
- for (Field<?> f : fieldToPropertyId.keySet()) {\r
- try {\r
- ((TransactionalProperty<?>) f.getPropertyDataSource())\r
- .rollback();\r
- } catch (Exception rollbackException) {\r
- // FIXME: What to do ?\r
- }\r
- }\r
-\r
- throw new CommitException("Commit failed", e);\r
- }\r
-\r
- }\r
-\r
- /**\r
- * Sends a preCommit event to all registered commit handlers\r
- * \r
- * @throws CommitException\r
- * If the commit should be aborted\r
- */\r
- private void firePreCommitEvent() throws CommitException {\r
- CommitHandler[] handlers = commitHandlers\r
- .toArray(new CommitHandler[commitHandlers.size()]);\r
-\r
- for (CommitHandler handler : handlers) {\r
- handler.preCommit(new CommitEvent(this));\r
- }\r
- }\r
-\r
- /**\r
- * Sends a postCommit event to all registered commit handlers\r
- * \r
- * @throws CommitException\r
- * If the commit should be aborted\r
- */\r
- private void firePostCommitEvent() throws CommitException {\r
- CommitHandler[] handlers = commitHandlers\r
- .toArray(new CommitHandler[commitHandlers.size()]);\r
-\r
- for (CommitHandler handler : handlers) {\r
- handler.postCommit(new CommitEvent(this));\r
- }\r
- }\r
-\r
- /**\r
- * Discards all changes done to the bound fields.\r
- * <p>\r
- * Only has effect if buffered mode is used.\r
- * \r
- */\r
- public void discard() {\r
- for (Field<?> f : fieldToPropertyId.keySet()) {\r
- try {\r
- f.discard();\r
- } catch (Exception e) {\r
- // TODO: handle exception\r
- // What can we do if discard fails other than try to discard all\r
- // other fields?\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Returns the field that is bound to the given property id\r
- * \r
- * @param propertyId\r
- * The property id to use to lookup the field\r
- * @return The field that is bound to the property id or null if no field is\r
- * bound to that property id\r
- */\r
- public Field<?> getFieldForPropertyId(Object propertyId) {\r
- return propertyIdToField.get(propertyId);\r
- }\r
-\r
- /**\r
- * Returns the property id that is bound to the given field\r
- * \r
- * @param field\r
- * The field to use to lookup the property id\r
- * @return The property id that is bound to the field or null if the field\r
- * is not bound to any property id by this FieldBinder\r
- */\r
- public Object getPropertyIdForField(Field field) {\r
- return fieldToPropertyId.get(field);\r
- }\r
-\r
- /**\r
- * Adds a commit handler.\r
- * <p>\r
- * The commit handler is called before the field values are committed to the\r
- * item ( {@link CommitHandler#preCommit(CommitEvent)}) and after the item\r
- * has been updated ({@link CommitHandler#postCommit(CommitEvent)}). If a\r
- * {@link CommitHandler} throws a CommitException the whole commit is\r
- * aborted and the fields retain their old values.\r
- * \r
- * @param commitHandler\r
- * The commit handler to add\r
- */\r
- public void addCommitHandler(CommitHandler commitHandler) {\r
- commitHandlers.add(commitHandler);\r
- }\r
-\r
- /**\r
- * Removes the given commit handler.\r
- * \r
- * @see #addCommitHandler(CommitHandler)\r
- * \r
- * @param commitHandler\r
- * The commit handler to remove\r
- */\r
- public void removeCommitHandler(CommitHandler commitHandler) {\r
- commitHandlers.remove(commitHandler);\r
- }\r
-\r
- /**\r
- * Returns a list of all commit handlers for this {@link FieldBinder}.\r
- * <p>\r
- * Use {@link #addCommitHandler(CommitHandler)} and\r
- * {@link #removeCommitHandler(CommitHandler)} to register or unregister a\r
- * commit handler.\r
- * \r
- * @return A collection of commit handlers\r
- */\r
- protected Collection<CommitHandler> getCommitHandlers() {\r
- return Collections.unmodifiableCollection(commitHandlers);\r
- }\r
-\r
- /**\r
- * FIXME Javadoc\r
- * \r
- */\r
- public interface CommitHandler extends Serializable {\r
- /**\r
- * Called before changes are committed to the field and the item is\r
- * updated.\r
- * <p>\r
- * Throw a {@link CommitException} to abort the commit.\r
- * \r
- * @param commitEvent\r
- * An event containing information regarding the commit\r
- * @throws CommitException\r
- * if the commit should be aborted\r
- */\r
- public void preCommit(CommitEvent commitEvent) throws CommitException;\r
-\r
- /**\r
- * Called after changes are committed to the fields and the item is\r
- * updated..\r
- * <p>\r
- * Throw a {@link CommitException} to abort the commit.\r
- * \r
- * @param commitEvent\r
- * An event containing information regarding the commit\r
- * @throws CommitException\r
- * if the commit should be aborted\r
- */\r
- public void postCommit(CommitEvent commitEvent) throws CommitException;\r
- }\r
-\r
- /**\r
- * FIXME javadoc\r
- * \r
- */\r
- public static class CommitEvent implements Serializable {\r
- private FieldBinder fieldBinder;\r
-\r
- private CommitEvent(FieldBinder fieldBinder) {\r
- this.fieldBinder = fieldBinder;\r
- }\r
-\r
- /**\r
- * Returns the field binder that this commit relates to\r
- * \r
- * @return The FieldBinder that is being committed.\r
- */\r
- public FieldBinder getFieldBinder() {\r
- return fieldBinder;\r
- }\r
-\r
- }\r
-\r
- /**\r
- * Checks the validity of the bound fields.\r
- * <p>\r
- * Call the {@link Field#validate()} for the fields to get the individual\r
- * error messages.\r
- * \r
- * @return true if all bound fields are valid, false otherwise.\r
- */\r
- public boolean isAllFieldsValid() {\r
- try {\r
- for (Field<?> field : getFields()) {\r
- field.validate();\r
- }\r
- return true;\r
- } catch (InvalidValueException e) {\r
- return false;\r
- }\r
- }\r
-\r
- /**\r
- * Checks if any bound field has been modified.\r
- * \r
- * @return true if at least on field has been modified, false otherwise\r
- */\r
- public boolean isAnyFieldModified() {\r
- for (Field<?> field : getFields()) {\r
- if (field.isModified()) {\r
- return true;\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * Binds fields for the given class.\r
- * <p>\r
- * This method processes all fields whose type extends {@link Field} and\r
- * that can be mapped to a property id. Property id mapping is done based on\r
- * the field name or on a {@link PropertyId} annotation on the field. All\r
- * non-null fields for which a property id can be determined are bound to\r
- * the property id.\r
- * \r
- * @param object\r
- * The object to process\r
- * @throws FormBuilderException\r
- * If there is a problem building or binding a field\r
- */\r
- public void bindFields(Object object) throws BindException {\r
- Class<?> objectClass = object.getClass();\r
-\r
- for (java.lang.reflect.Field f : objectClass.getDeclaredFields()) {\r
-\r
- if (!Field.class.isAssignableFrom(f.getType())) {\r
- // Process next field\r
- continue;\r
- }\r
-\r
- PropertyId propertyIdAnnotation = f.getAnnotation(PropertyId.class);\r
-\r
- Class<? extends Field> fieldType = (Class<? extends Field>) f\r
- .getType();\r
-\r
- Object propertyId = null;\r
- if (propertyIdAnnotation != null) {\r
- // @PropertyId(propertyId) always overrides property id\r
- propertyId = propertyIdAnnotation.value();\r
- } else {\r
- propertyId = f.getName();\r
- }\r
-\r
- // Ensure that the property id exists\r
- Class<?> propertyType;\r
-\r
- try {\r
- propertyType = getPropertyType(propertyId);\r
- } catch (BindException e) {\r
- // Property id was not found, skip this field\r
- continue;\r
- }\r
-\r
- try {\r
- // Get the field from the object\r
- Field<?> field = (Field<?>) ReflectTools.getJavaFieldValue(\r
- object, f);\r
- // Bind it to the property id\r
- bind(field, propertyId);\r
- } catch (Exception e) {\r
- // If we cannot determine the value, just skip the field and try\r
- // the next one\r
- continue;\r
- }\r
-\r
- }\r
- }\r
-\r
- public static class CommitException extends Exception {\r
-\r
- public CommitException() {\r
- super();\r
- // TODO Auto-generated constructor stub\r
- }\r
-\r
- public CommitException(String message, Throwable cause) {\r
- super(message, cause);\r
- // TODO Auto-generated constructor stub\r
- }\r
-\r
- public CommitException(String message) {\r
- super(message);\r
- // TODO Auto-generated constructor stub\r
- }\r
-\r
- public CommitException(Throwable cause) {\r
- super(cause);\r
- // TODO Auto-generated constructor stub\r
- }\r
-\r
- }\r
-\r
- public static class BindException extends RuntimeException {\r
-\r
- public BindException(String message) {\r
- super(message);\r
- }\r
-\r
- public BindException(String message, Throwable t) {\r
- super(message, t);\r
- }\r
-\r
- }\r
-}
\ No newline at end of file
--- /dev/null
+/* \r
+@VaadinApache2LicenseForJavaFiles@\r
+ */\r
+package com.vaadin.data.fieldbinder;\r
+\r
+import java.io.Serializable;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.HashMap;\r
+import java.util.LinkedHashMap;\r
+import java.util.List;\r
+import java.util.logging.Logger;\r
+\r
+import com.vaadin.data.Item;\r
+import com.vaadin.data.Property;\r
+import com.vaadin.data.TransactionalProperty;\r
+import com.vaadin.data.Validator.InvalidValueException;\r
+import com.vaadin.data.fieldbinder.FormBuilder.FormBuilderException;\r
+import com.vaadin.data.util.TransactionalPropertyWrapper;\r
+import com.vaadin.tools.ReflectTools;\r
+import com.vaadin.ui.Field;\r
+import com.vaadin.ui.Form;\r
+\r
+/**\r
+ * FieldGroup provides an easy way of binding fields to data and handling\r
+ * commits of these fields.\r
+ * <p>\r
+ * The functionality of FieldGroup is similar to {@link Form} but\r
+ * {@link FieldGroup} does not handle layouts in any way. The typical use case\r
+ * is to create a layout outside the FieldGroup and then use FieldGroup to bind\r
+ * the fields to a data source.\r
+ * </p>\r
+ * <p>\r
+ * {@link FieldGroup} is not a UI component so it cannot be added to a layout.\r
+ * Using the buildAndBind methods {@link FieldGroup} can create fields for you\r
+ * using a FieldGroupFieldFactory but you still have to add them to the correct\r
+ * position in your layout.\r
+ * </p>\r
+ * \r
+ * @author Vaadin Ltd\r
+ * @version @version@\r
+ * @since 7.0\r
+ */\r
+public class FieldGroup implements Serializable {\r
+\r
+ private static final Logger logger = Logger.getLogger(FieldGroup.class\r
+ .getName());\r
+\r
+ private Item itemDataSource;\r
+ private boolean fieldsBuffered = true;\r
+\r
+ private boolean fieldsEnabled = true;\r
+ private boolean fieldsReadOnly = false;\r
+\r
+ private HashMap<Object, Field<?>> propertyIdToField = new HashMap<Object, Field<?>>();\r
+ private LinkedHashMap<Field<?>, Object> fieldToPropertyId = new LinkedHashMap<Field<?>, Object>();\r
+ private List<CommitHandler> commitHandlers = new ArrayList<CommitHandler>();\r
+\r
+ /**\r
+ * Constructs a field binder. Use {@link #setItemDataSource(Item)} to set a\r
+ * data source for the field binder.\r
+ * \r
+ */\r
+ public FieldGroup() {\r
+\r
+ }\r
+\r
+ /**\r
+ * Constructs a field binder that uses the given data source.\r
+ * \r
+ * @param itemDataSource\r
+ * The data source to bind the fields to\r
+ */\r
+ public FieldGroup(Item itemDataSource) {\r
+ setItemDataSource(itemDataSource);\r
+ }\r
+\r
+ /**\r
+ * Updates the item that is used by this FieldBinder. Rebinds all fields to\r
+ * the properties in the new item.\r
+ * \r
+ * @param itemDataSource\r
+ * The new item to use\r
+ */\r
+ public void setItemDataSource(Item itemDataSource) {\r
+ this.itemDataSource = itemDataSource;\r
+\r
+ for (Field<?> f : fieldToPropertyId.keySet()) {\r
+ bind(f, fieldToPropertyId.get(f));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Gets the item used by this FieldBinder. Note that you must call\r
+ * {@link #commit()} for the item to be updated unless buffered mode has\r
+ * been switched off.\r
+ * \r
+ * @see #setFieldsBuffered(boolean)\r
+ * @see #commit()\r
+ * \r
+ * @return The item used by this FieldBinder\r
+ */\r
+ public Item getItemDataSource() {\r
+ return itemDataSource;\r
+ }\r
+\r
+ /**\r
+ * Checks the buffered mode for the bound fields.\r
+ * <p>\r
+ * \r
+ * @see #setFieldsBuffered(boolean) for more details on buffered mode\r
+ * \r
+ * @see Field#isFieldsBuffered()\r
+ * @return true if buffered mode is on, false otherwise\r
+ * \r
+ */\r
+ public boolean isFieldsBuffered() {\r
+ return fieldsBuffered;\r
+ }\r
+\r
+ /**\r
+ * Sets the buffered mode for the bound fields.\r
+ * <p>\r
+ * When buffered mode is on the item will not be updated until\r
+ * {@link #commit()} is called. If buffered mode is off the item will be\r
+ * updated once the fields are updated.\r
+ * </p>\r
+ * <p>\r
+ * The default is to use buffered mode.\r
+ * </p>\r
+ * \r
+ * @see Field#setFieldsBuffered(boolean)\r
+ * @param fieldsBuffered\r
+ * true to turn on buffered mode, false otherwise\r
+ */\r
+ public void setFieldsBuffered(boolean fieldsBuffered) {\r
+ if (fieldsBuffered == this.fieldsBuffered) {\r
+ return;\r
+ }\r
+\r
+ this.fieldsBuffered = fieldsBuffered;\r
+ for (Field<?> field : getFields()) {\r
+ field.setBuffered(fieldsBuffered);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Returns the enabled status for the fields.\r
+ * <p>\r
+ * Note that this will not accurately represent the enabled status of all\r
+ * fields if you change the enabled status of the fields through some other\r
+ * method than {@link #setFieldsEnabled(boolean)}.\r
+ * \r
+ * @return true if the fields are enabled, false otherwise\r
+ */\r
+ public boolean isFieldsEnabled() {\r
+ return fieldsEnabled;\r
+ }\r
+\r
+ /**\r
+ * Updates the enabled state of all bound fields.\r
+ * \r
+ * @param fieldsEnabled\r
+ * true to enable all bound fields, false to disable them\r
+ */\r
+ public void setFieldsEnabled(boolean fieldsEnabled) {\r
+ this.fieldsEnabled = fieldsEnabled;\r
+ for (Field<?> field : getFields()) {\r
+ field.setEnabled(fieldsEnabled);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Returns the read only status for the fields.\r
+ * <p>\r
+ * Note that this will not accurately represent the read only status of all\r
+ * fields if you change the read only status of the fields through some\r
+ * other method than {@link #setFieldsReadOnly(boolean)}.\r
+ * \r
+ * @return true if the fields are set to read only, false otherwise\r
+ */\r
+ public boolean isFieldsReadOnly() {\r
+ return fieldsReadOnly;\r
+ }\r
+\r
+ /**\r
+ * Updates the read only state of all bound fields.\r
+ * \r
+ * @param fieldsReadOnly\r
+ * true to set all bound fields to read only, false to set them\r
+ * to read write\r
+ */\r
+ public void setFieldsReadOnly(boolean fieldsReadOnly) {\r
+ this.fieldsReadOnly = fieldsReadOnly;\r
+ }\r
+\r
+ /**\r
+ * Returns a collection of all fields that have been bound.\r
+ * <p>\r
+ * The fields are not returned in any specific order.\r
+ * </p>\r
+ * \r
+ * @return A collection with all bound Fields\r
+ */\r
+ public Collection<Field<?>> getFields() {\r
+ return fieldToPropertyId.keySet();\r
+ }\r
+\r
+ /**\r
+ * Binds the field with the given propertyId from the current item. If an\r
+ * item has not been set then the binding is postponed until the item is set\r
+ * using {@link #setItemDataSource(Item)}.\r
+ * <p>\r
+ * This method also adds validators when applicable.\r
+ * </p>\r
+ * \r
+ * @param field\r
+ * The field to bind\r
+ * @param propertyId\r
+ * The propertyId to bind to the field\r
+ * @throws BindException\r
+ * If the property id is already bound to another field by this\r
+ * field binder\r
+ */\r
+ public void bind(Field<?> field, Object propertyId) throws BindException {\r
+ if (propertyIdToField.containsKey(propertyId)\r
+ && propertyIdToField.get(propertyId) != field) {\r
+ throw new BindException("Property id " + propertyId\r
+ + " is already bound to another field");\r
+ }\r
+ fieldToPropertyId.put(field, propertyId);\r
+ propertyIdToField.put(propertyId, field);\r
+ if (itemDataSource == null) {\r
+ // Will be bound when data source is set\r
+ return;\r
+ }\r
+\r
+ field.setPropertyDataSource(wrapInTransactionalProperty(getItemProperty(propertyId)));\r
+ configureField(field);\r
+ }\r
+\r
+ private <T> TransactionalProperty<T> wrapInTransactionalProperty(\r
+ Property<T> itemProperty) {\r
+ return new TransactionalPropertyWrapper<T>(itemProperty);\r
+ }\r
+\r
+ /**\r
+ * Gets the property with the given property id from the item.\r
+ * \r
+ * @param propertyId\r
+ * The id if the property to find\r
+ * @return The property with the given id from the item\r
+ * @throws BindException\r
+ * If the property was not found in the item or no item has been\r
+ * set\r
+ */\r
+ protected Property<?> getItemProperty(Object propertyId)\r
+ throws BindException {\r
+ Item item = getItemDataSource();\r
+ if (item == null) {\r
+ throw new BindException("Could not lookup property with id "\r
+ + propertyId + " as no item has been set");\r
+ }\r
+ Property<?> p = item.getItemProperty(propertyId);\r
+ if (p == null) {\r
+ throw new BindException("A property with id " + propertyId\r
+ + " was not found in the item");\r
+ }\r
+ return p;\r
+ }\r
+\r
+ /**\r
+ * Detaches the field from its property id and removes it from this\r
+ * FieldBinder.\r
+ * <p>\r
+ * Note that the field is not detached from its property data source if it\r
+ * is no longer connected to the same property id it was bound to using this\r
+ * FieldBinder.\r
+ * \r
+ * @param field\r
+ * The field to detach\r
+ * @throws BindException\r
+ * If the field is not bound by this field binder or not bound\r
+ * to the correct property id\r
+ */\r
+ public void remove(Field<?> field) throws BindException {\r
+ Object propertyId = fieldToPropertyId.get(field);\r
+ if (propertyId == null) {\r
+ throw new BindException(\r
+ "The given field is not part of this FieldBinder");\r
+ }\r
+\r
+ Property fieldDataSource = field.getPropertyDataSource();\r
+ if (fieldDataSource instanceof TransactionalPropertyWrapper) {\r
+ fieldDataSource = ((TransactionalPropertyWrapper) fieldDataSource)\r
+ .getWrappedProperty();\r
+ }\r
+ if (fieldDataSource == getItemProperty(propertyId)) {\r
+ field.setPropertyDataSource(null);\r
+ }\r
+ fieldToPropertyId.remove(field);\r
+ propertyIdToField.remove(propertyId);\r
+ }\r
+\r
+ /**\r
+ * Configures a field with the settings set for this FieldBinder.\r
+ * <p>\r
+ * By default this updates the buffered, read only and enabled state of the\r
+ * field. Also adds validators when applicable.\r
+ * \r
+ * @param field\r
+ * The field to update\r
+ */\r
+ protected void configureField(Field<?> field) {\r
+ field.setBuffered(isFieldsBuffered());\r
+\r
+ field.setEnabled(isFieldsEnabled());\r
+ field.setReadOnly(isFieldsReadOnly());\r
+ }\r
+\r
+ /**\r
+ * Gets the type of the property with the given property id.\r
+ * \r
+ * @param propertyId\r
+ * The propertyId. Must be find\r
+ * @return The type of the property\r
+ */\r
+ protected Class<?> getPropertyType(Object propertyId) throws BindException {\r
+ if (getItemDataSource() == null) {\r
+ throw new BindException(\r
+ "Property type for '"\r
+ + propertyId\r
+ + "' could not be determined. No item data source has been set.");\r
+ }\r
+ Property<?> p = getItemDataSource().getItemProperty(propertyId);\r
+ if (p == null) {\r
+ throw new BindException(\r
+ "Property type for '"\r
+ + propertyId\r
+ + "' could not be determined. No property with that id was found.");\r
+ }\r
+\r
+ return p.getType();\r
+ }\r
+\r
+ /**\r
+ * Returns a collection of all property ids that have been bound to fields.\r
+ * <p>\r
+ * Note that this will return property ids even before the item has been\r
+ * set. In that case it returns the property ids that will be bound once the\r
+ * item is set.\r
+ * </p>\r
+ * <p>\r
+ * No guarantee is given for the order of the property ids\r
+ * </p>\r
+ * \r
+ * @return A collection of bound property ids\r
+ */\r
+ public Collection<Object> getBoundPropertyIds() {\r
+ return Collections.unmodifiableCollection(propertyIdToField.keySet());\r
+ }\r
+\r
+ /**\r
+ * Returns a collection of all property ids that exist in the item set using\r
+ * {@link #setItemDataSource(Item)} but have not been bound to fields.\r
+ * <p>\r
+ * Will always return an empty collection before an item has been set using\r
+ * {@link #setItemDataSource(Item)}.\r
+ * </p>\r
+ * <p>\r
+ * No guarantee is given for the order of the property ids\r
+ * </p>\r
+ * \r
+ * @return A collection of property ids that have not been bound to fields\r
+ */\r
+ protected Collection<Object> getUnboundPropertyIds() {\r
+ if (getItemDataSource() == null) {\r
+ return new ArrayList<Object>();\r
+ }\r
+ List<Object> unboundPropertyIds = new ArrayList<Object>();\r
+ unboundPropertyIds.addAll(getItemDataSource().getItemPropertyIds());\r
+ unboundPropertyIds.removeAll(propertyIdToField.keySet());\r
+ return unboundPropertyIds;\r
+ }\r
+\r
+ /**\r
+ * Commits all changes done to the bound fields.\r
+ * <p>\r
+ * Calls all {@link CommitHandler}s before and after committing the field\r
+ * changes to the item data source. The whole commit is aborted and state is\r
+ * restored to what it was before commit was called if any\r
+ * {@link CommitHandler} throws a CommitException or there is a problem\r
+ * committing the fields\r
+ * \r
+ * @throws CommitException\r
+ * If the commit was aborted\r
+ */\r
+ public void commit() throws CommitException {\r
+ if (!isFieldsBuffered()) {\r
+ // Not using buffered mode, nothing to do\r
+ return;\r
+ }\r
+ for (Field<?> f : fieldToPropertyId.keySet()) {\r
+ ((TransactionalProperty<?>) f.getPropertyDataSource())\r
+ .startTransaction();\r
+ }\r
+ try {\r
+ firePreCommitEvent();\r
+ // Commit the field values to the properties\r
+ for (Field<?> f : fieldToPropertyId.keySet()) {\r
+ f.commit();\r
+ }\r
+ firePostCommitEvent();\r
+\r
+ // Commit the properties\r
+ for (Field<?> f : fieldToPropertyId.keySet()) {\r
+ ((TransactionalProperty<?>) f.getPropertyDataSource()).commit();\r
+ }\r
+\r
+ } catch (Exception e) {\r
+ for (Field<?> f : fieldToPropertyId.keySet()) {\r
+ try {\r
+ ((TransactionalProperty<?>) f.getPropertyDataSource())\r
+ .rollback();\r
+ } catch (Exception rollbackException) {\r
+ // FIXME: What to do ?\r
+ }\r
+ }\r
+\r
+ throw new CommitException("Commit failed", e);\r
+ }\r
+\r
+ }\r
+\r
+ /**\r
+ * Sends a preCommit event to all registered commit handlers\r
+ * \r
+ * @throws CommitException\r
+ * If the commit should be aborted\r
+ */\r
+ private void firePreCommitEvent() throws CommitException {\r
+ CommitHandler[] handlers = commitHandlers\r
+ .toArray(new CommitHandler[commitHandlers.size()]);\r
+\r
+ for (CommitHandler handler : handlers) {\r
+ handler.preCommit(new CommitEvent(this));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Sends a postCommit event to all registered commit handlers\r
+ * \r
+ * @throws CommitException\r
+ * If the commit should be aborted\r
+ */\r
+ private void firePostCommitEvent() throws CommitException {\r
+ CommitHandler[] handlers = commitHandlers\r
+ .toArray(new CommitHandler[commitHandlers.size()]);\r
+\r
+ for (CommitHandler handler : handlers) {\r
+ handler.postCommit(new CommitEvent(this));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Discards all changes done to the bound fields.\r
+ * <p>\r
+ * Only has effect if buffered mode is used.\r
+ * \r
+ */\r
+ public void discard() {\r
+ for (Field<?> f : fieldToPropertyId.keySet()) {\r
+ try {\r
+ f.discard();\r
+ } catch (Exception e) {\r
+ // TODO: handle exception\r
+ // What can we do if discard fails other than try to discard all\r
+ // other fields?\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Returns the field that is bound to the given property id\r
+ * \r
+ * @param propertyId\r
+ * The property id to use to lookup the field\r
+ * @return The field that is bound to the property id or null if no field is\r
+ * bound to that property id\r
+ */\r
+ public Field<?> getFieldForPropertyId(Object propertyId) {\r
+ return propertyIdToField.get(propertyId);\r
+ }\r
+\r
+ /**\r
+ * Returns the property id that is bound to the given field\r
+ * \r
+ * @param field\r
+ * The field to use to lookup the property id\r
+ * @return The property id that is bound to the field or null if the field\r
+ * is not bound to any property id by this FieldBinder\r
+ */\r
+ public Object getPropertyIdForField(Field field) {\r
+ return fieldToPropertyId.get(field);\r
+ }\r
+\r
+ /**\r
+ * Adds a commit handler.\r
+ * <p>\r
+ * The commit handler is called before the field values are committed to the\r
+ * item ( {@link CommitHandler#preCommit(CommitEvent)}) and after the item\r
+ * has been updated ({@link CommitHandler#postCommit(CommitEvent)}). If a\r
+ * {@link CommitHandler} throws a CommitException the whole commit is\r
+ * aborted and the fields retain their old values.\r
+ * \r
+ * @param commitHandler\r
+ * The commit handler to add\r
+ */\r
+ public void addCommitHandler(CommitHandler commitHandler) {\r
+ commitHandlers.add(commitHandler);\r
+ }\r
+\r
+ /**\r
+ * Removes the given commit handler.\r
+ * \r
+ * @see #addCommitHandler(CommitHandler)\r
+ * \r
+ * @param commitHandler\r
+ * The commit handler to remove\r
+ */\r
+ public void removeCommitHandler(CommitHandler commitHandler) {\r
+ commitHandlers.remove(commitHandler);\r
+ }\r
+\r
+ /**\r
+ * Returns a list of all commit handlers for this {@link FieldGroup}.\r
+ * <p>\r
+ * Use {@link #addCommitHandler(CommitHandler)} and\r
+ * {@link #removeCommitHandler(CommitHandler)} to register or unregister a\r
+ * commit handler.\r
+ * \r
+ * @return A collection of commit handlers\r
+ */\r
+ protected Collection<CommitHandler> getCommitHandlers() {\r
+ return Collections.unmodifiableCollection(commitHandlers);\r
+ }\r
+\r
+ /**\r
+ * FIXME Javadoc\r
+ * \r
+ */\r
+ public interface CommitHandler extends Serializable {\r
+ /**\r
+ * Called before changes are committed to the field and the item is\r
+ * updated.\r
+ * <p>\r
+ * Throw a {@link CommitException} to abort the commit.\r
+ * \r
+ * @param commitEvent\r
+ * An event containing information regarding the commit\r
+ * @throws CommitException\r
+ * if the commit should be aborted\r
+ */\r
+ public void preCommit(CommitEvent commitEvent) throws CommitException;\r
+\r
+ /**\r
+ * Called after changes are committed to the fields and the item is\r
+ * updated..\r
+ * <p>\r
+ * Throw a {@link CommitException} to abort the commit.\r
+ * \r
+ * @param commitEvent\r
+ * An event containing information regarding the commit\r
+ * @throws CommitException\r
+ * if the commit should be aborted\r
+ */\r
+ public void postCommit(CommitEvent commitEvent) throws CommitException;\r
+ }\r
+\r
+ /**\r
+ * FIXME javadoc\r
+ * \r
+ */\r
+ public static class CommitEvent implements Serializable {\r
+ private FieldGroup fieldBinder;\r
+\r
+ private CommitEvent(FieldGroup fieldBinder) {\r
+ this.fieldBinder = fieldBinder;\r
+ }\r
+\r
+ /**\r
+ * Returns the field binder that this commit relates to\r
+ * \r
+ * @return The FieldBinder that is being committed.\r
+ */\r
+ public FieldGroup getFieldBinder() {\r
+ return fieldBinder;\r
+ }\r
+\r
+ }\r
+\r
+ /**\r
+ * Checks the validity of the bound fields.\r
+ * <p>\r
+ * Call the {@link Field#validate()} for the fields to get the individual\r
+ * error messages.\r
+ * \r
+ * @return true if all bound fields are valid, false otherwise.\r
+ */\r
+ public boolean isAllFieldsValid() {\r
+ try {\r
+ for (Field<?> field : getFields()) {\r
+ field.validate();\r
+ }\r
+ return true;\r
+ } catch (InvalidValueException e) {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Checks if any bound field has been modified.\r
+ * \r
+ * @return true if at least on field has been modified, false otherwise\r
+ */\r
+ public boolean isAnyFieldModified() {\r
+ for (Field<?> field : getFields()) {\r
+ if (field.isModified()) {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Binds fields for the given class.\r
+ * <p>\r
+ * This method processes all fields whose type extends {@link Field} and\r
+ * that can be mapped to a property id. Property id mapping is done based on\r
+ * the field name or on a {@link PropertyId} annotation on the field. All\r
+ * non-null fields for which a property id can be determined are bound to\r
+ * the property id.\r
+ * \r
+ * @param object\r
+ * The object to process\r
+ * @throws FormBuilderException\r
+ * If there is a problem building or binding a field\r
+ */\r
+ public void bindFields(Object object) throws BindException {\r
+ Class<?> objectClass = object.getClass();\r
+\r
+ for (java.lang.reflect.Field f : objectClass.getDeclaredFields()) {\r
+\r
+ if (!Field.class.isAssignableFrom(f.getType())) {\r
+ // Process next field\r
+ continue;\r
+ }\r
+\r
+ PropertyId propertyIdAnnotation = f.getAnnotation(PropertyId.class);\r
+\r
+ Class<? extends Field> fieldType = (Class<? extends Field>) f\r
+ .getType();\r
+\r
+ Object propertyId = null;\r
+ if (propertyIdAnnotation != null) {\r
+ // @PropertyId(propertyId) always overrides property id\r
+ propertyId = propertyIdAnnotation.value();\r
+ } else {\r
+ propertyId = f.getName();\r
+ }\r
+\r
+ // Ensure that the property id exists\r
+ Class<?> propertyType;\r
+\r
+ try {\r
+ propertyType = getPropertyType(propertyId);\r
+ } catch (BindException e) {\r
+ // Property id was not found, skip this field\r
+ continue;\r
+ }\r
+\r
+ try {\r
+ // Get the field from the object\r
+ Field<?> field = (Field<?>) ReflectTools.getJavaFieldValue(\r
+ object, f);\r
+ // Bind it to the property id\r
+ bind(field, propertyId);\r
+ } catch (Exception e) {\r
+ // If we cannot determine the value, just skip the field and try\r
+ // the next one\r
+ continue;\r
+ }\r
+\r
+ }\r
+ }\r
+\r
+ public static class CommitException extends Exception {\r
+\r
+ public CommitException() {\r
+ super();\r
+ // TODO Auto-generated constructor stub\r
+ }\r
+\r
+ public CommitException(String message, Throwable cause) {\r
+ super(message, cause);\r
+ // TODO Auto-generated constructor stub\r
+ }\r
+\r
+ public CommitException(String message) {\r
+ super(message);\r
+ // TODO Auto-generated constructor stub\r
+ }\r
+\r
+ public CommitException(Throwable cause) {\r
+ super(cause);\r
+ // TODO Auto-generated constructor stub\r
+ }\r
+\r
+ }\r
+\r
+ public static class BindException extends RuntimeException {\r
+\r
+ public BindException(String message) {\r
+ super(message);\r
+ }\r
+\r
+ public BindException(String message, Throwable t) {\r
+ super(message, t);\r
+ }\r
+\r
+ }\r
+}
\ No newline at end of file
import java.lang.reflect.InvocationTargetException;\r
import java.util.logging.Logger;\r
\r
-import com.vaadin.data.fieldbinder.FieldBinder.BindException;\r
+import com.vaadin.data.fieldbinder.FieldGroup.BindException;\r
import com.vaadin.tools.ReflectTools;\r
import com.vaadin.ui.DefaultFieldFactory;\r
import com.vaadin.ui.Field;\r
public class FormBuilder implements Serializable {\r
\r
private FormBuilderFieldFactory fieldFactory = new DefaultFormBuilderFieldFactory();\r
- private FieldBinder fieldBinder;\r
+ private FieldGroup fieldBinder;\r
private static final Logger logger = Logger.getLogger(FormBuilder.class\r
.getName());\r
\r
* The FieldBinder to use for binding the fields to the data\r
* source\r
*/\r
- public FormBuilder(FieldBinder fieldBinder) {\r
+ public FormBuilder(FieldGroup fieldBinder) {\r
this.fieldBinder = fieldBinder;\r
}\r
\r
/**\r
* TODO: javadoc\r
*/\r
- protected FieldBinder getFieldBinder() {\r
+ protected FieldGroup getFieldBinder() {\r
return fieldBinder;\r
}\r
\r
package com.vaadin.tests.fieldbinder;\r
\r
-import com.vaadin.data.fieldbinder.BeanFieldBinder;\r
-import com.vaadin.data.fieldbinder.FieldBinder.CommitException;\r
+import com.vaadin.data.fieldbinder.BeanFieldGroup;\r
+import com.vaadin.data.fieldbinder.FieldGroup.CommitException;\r
import com.vaadin.tests.components.TestBase;\r
import com.vaadin.tests.util.Log;\r
import com.vaadin.ui.Button;\r
\r
private Button discardButton;\r
private Button showBeanButton;\r
- private BeanFieldBinder fieldBinder;\r
+ private BeanFieldGroup fieldBinder;\r
\r
@Override\r
protected void setup() {\r
return commitButton;\r
}\r
\r
- protected BeanFieldBinder getFieldBinder() {\r
+ protected BeanFieldGroup getFieldBinder() {\r
return fieldBinder;\r
}\r
\r
- protected void setFieldBinder(BeanFieldBinder beanFieldBinder) {\r
+ protected void setFieldBinder(BeanFieldGroup beanFieldBinder) {\r
fieldBinder = beanFieldBinder;\r
}\r
\r
package com.vaadin.tests.fieldbinder;\r
\r
-import com.vaadin.data.fieldbinder.BeanFieldBinder;\r
-import com.vaadin.data.fieldbinder.FieldBinder;\r
-import com.vaadin.data.fieldbinder.FieldBinder.CommitEvent;\r
-import com.vaadin.data.fieldbinder.FieldBinder.CommitException;\r
-import com.vaadin.data.fieldbinder.FieldBinder.CommitHandler;\r
+import com.vaadin.data.fieldbinder.BeanFieldGroup;\r
+import com.vaadin.data.fieldbinder.FieldGroup;\r
+import com.vaadin.data.fieldbinder.FieldGroup.CommitEvent;\r
+import com.vaadin.data.fieldbinder.FieldGroup.CommitException;\r
+import com.vaadin.data.fieldbinder.FieldGroup.CommitHandler;\r
import com.vaadin.data.fieldbinder.FormBuilder;\r
import com.vaadin.data.util.BeanItem;\r
import com.vaadin.data.util.converter.StringToBooleanConverter;\r
super("Configuration");\r
BeanItem<Configuration> bi = new BeanItem<BasicPersonForm.Configuration>(\r
configuration);\r
- FieldBinder confBinder = new FieldBinder(bi);\r
+ FieldGroup confBinder = new FieldGroup(bi);\r
confBinder.setItemDataSource(bi);\r
confBinder.setFieldsBuffered(false);\r
\r
Panel confPanel = new ConfigurationPanel();\r
addComponent(confPanel);\r
\r
- final FieldBinder binder = new BeanFieldBinder<Person>(Person.class);\r
+ final FieldGroup binder = new BeanFieldGroup<Person>(Person.class);\r
binder.addCommitHandler(new CommitHandler() {\r
\r
public void preCommit(CommitEvent commitEvent)\r
binder.setItemDataSource(new BeanItem<Person>(p));\r
}\r
\r
- public static Person getPerson(FieldBinder binder) {\r
+ public static Person getPerson(FieldGroup binder) {\r
return ((BeanItem<Person>) binder.getItemDataSource()).getBean();\r
}\r
\r
package com.vaadin.tests.fieldbinder;\r
\r
-import com.vaadin.data.fieldbinder.BeanFieldBinder;\r
-import com.vaadin.data.fieldbinder.FieldBinder;\r
-import com.vaadin.data.fieldbinder.FieldBinder.CommitException;\r
+import com.vaadin.data.fieldbinder.BeanFieldGroup;\r
+import com.vaadin.data.fieldbinder.FieldGroup;\r
+import com.vaadin.data.fieldbinder.FieldGroup.CommitException;\r
import com.vaadin.data.fieldbinder.FormBuilder;\r
import com.vaadin.data.util.BeanItem;\r
import com.vaadin.tests.components.TestBase;\r
protected void setup() {\r
addComponent(log);\r
\r
- final BeanFieldBinder<PersonWithBeanValidationAnnotations> binder = new BeanFieldBinder<PersonWithBeanValidationAnnotations>(\r
+ final BeanFieldGroup<PersonWithBeanValidationAnnotations> binder = new BeanFieldGroup<PersonWithBeanValidationAnnotations>(\r
PersonWithBeanValidationAnnotations.class);\r
\r
FormBuilder builder = new FormBuilder(binder);\r
p));\r
}\r
\r
- public static Person getPerson(FieldBinder binder) {\r
+ public static Person getPerson(FieldGroup binder) {\r
return ((BeanItem<Person>) binder.getItemDataSource()).getBean();\r
}\r
\r
package com.vaadin.tests.fieldbinder;\r
\r
-import com.vaadin.data.fieldbinder.BeanFieldBinder;\r
-import com.vaadin.data.fieldbinder.FieldBinder;\r
+import com.vaadin.data.fieldbinder.BeanFieldGroup;\r
+import com.vaadin.data.fieldbinder.FieldGroup;\r
import com.vaadin.data.fieldbinder.FormBuilder;\r
import com.vaadin.data.fieldbinder.PropertyId;\r
import com.vaadin.data.util.BeanItem;\r
\r
@Override\r
protected void setup() {\r
- FieldBinder fieldBinder = new BeanFieldBinder<Person>(Person.class);\r
+ FieldGroup fieldBinder = new BeanFieldGroup<Person>(Person.class);\r
FormBuilder b = new FormBuilder(fieldBinder);\r
b.buildAndBindFields(this);\r
\r
package com.vaadin.tests.fieldbinder;\r
\r
-import com.vaadin.data.fieldbinder.BeanFieldBinder;\r
+import com.vaadin.data.fieldbinder.BeanFieldGroup;\r
import com.vaadin.data.fieldbinder.FormBuilder;\r
import com.vaadin.data.fieldbinder.PropertyId;\r
import com.vaadin.tests.data.bean.Address;\r
protected void setup() {\r
super.setup();\r
\r
- setFieldBinder(new BeanFieldBinder<Person>(Person.class));\r
+ setFieldBinder(new BeanFieldGroup<Person>(Person.class));\r
getFieldBinder().bindFields(this);\r
country = new FormBuilder(getFieldBinder()).buildAndBind("country",\r
"address.country", NativeSelect.class);\r