From 4b13cfab7c0adaefdf8b4cfcb3c68bddf9111dc2 Mon Sep 17 00:00:00 2001
From: Henri Sara
Date: Mon, 18 Apr 2011 12:13:36 +0000
Subject: [PATCH] #4995 Nested bean property support
svn changeset:18356/svn branch:6.6
---
.../data/util/AbstractBeanContainer.java | 145 ++++++--
src/com/vaadin/data/util/BeanItem.java | 41 +--
src/com/vaadin/data/util/MethodProperty.java | 139 ++++----
.../data/util/MethodPropertyDescriptor.java | 128 +++++++
.../data/util/NestedMethodProperty.java | 200 +++++++++++
.../data/util/NestedPropertyDescriptor.java | 51 +++
.../data/util/VaadinPropertyDescriptor.java | 40 +++
.../server/container/BeanContainerTest.java | 38 +-
.../container/BeanItemContainerTest.java | 39 +-
.../tests/server/container/BeanItemTest.java | 56 ++-
.../container/NestedMethodPropertyTest.java | 334 ++++++++++++++++++
.../container/PropertyDescriptorTest.java | 56 +++
12 files changed, 1123 insertions(+), 144 deletions(-)
create mode 100644 src/com/vaadin/data/util/MethodPropertyDescriptor.java
create mode 100644 src/com/vaadin/data/util/NestedMethodProperty.java
create mode 100644 src/com/vaadin/data/util/NestedPropertyDescriptor.java
create mode 100644 src/com/vaadin/data/util/VaadinPropertyDescriptor.java
create mode 100644 tests/src/com/vaadin/tests/server/container/NestedMethodPropertyTest.java
create mode 100644 tests/src/com/vaadin/tests/server/container/PropertyDescriptorTest.java
diff --git a/src/com/vaadin/data/util/AbstractBeanContainer.java b/src/com/vaadin/data/util/AbstractBeanContainer.java
index fab61786d1..1daa4050c5 100644
--- a/src/com/vaadin/data/util/AbstractBeanContainer.java
+++ b/src/com/vaadin/data/util/AbstractBeanContainer.java
@@ -3,17 +3,16 @@
*/
package com.vaadin.data.util;
-import java.beans.PropertyDescriptor;
-import java.io.IOException;
import java.io.Serializable;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
+import com.vaadin.data.Container;
import com.vaadin.data.Container.Filterable;
+import com.vaadin.data.Container.PropertySetChangeNotifier;
import com.vaadin.data.Container.SimpleFilterable;
import com.vaadin.data.Container.Sortable;
import com.vaadin.data.Item;
@@ -21,6 +20,7 @@ import com.vaadin.data.Property;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.Property.ValueChangeNotifier;
+import com.vaadin.data.util.MethodProperty.MethodException;
import com.vaadin.data.util.filter.SimpleStringFilter;
import com.vaadin.data.util.filter.UnsupportedFilterException;
@@ -40,11 +40,6 @@ import com.vaadin.data.util.filter.UnsupportedFilterException;
* {@link #addItemAt(int, Object, Object)}.
*
*
- *
- * It is not possible to add additional properties to the container and nested
- * bean properties are not supported.
- *
- *
* @param
* The type of the item identifier
* @param
@@ -54,7 +49,8 @@ import com.vaadin.data.util.filter.UnsupportedFilterException;
*/
public abstract class AbstractBeanContainer extends
AbstractInMemoryContainer> implements
- Filterable, SimpleFilterable, Sortable, ValueChangeListener {
+ Filterable, SimpleFilterable, Sortable, ValueChangeListener,
+ PropertySetChangeNotifier {
/**
* Resolver that maps beans to their (item) identifiers, removing the need
@@ -90,7 +86,6 @@ public abstract class AbstractBeanContainer extends
BeanIdResolver {
private final Object propertyId;
- private transient Method getMethod;
public PropertyBasedBeanIdResolver(Object propertyId) {
if (propertyId == null) {
@@ -100,25 +95,18 @@ public abstract class AbstractBeanContainer extends
this.propertyId = propertyId;
}
- private Method getGetter() throws IllegalStateException {
- if (getMethod == null) {
- if (!model.containsKey(propertyId)) {
- throw new IllegalStateException("Property " + propertyId
- + " not found");
- }
- getMethod = model.get(propertyId).getReadMethod();
- }
- return getMethod;
- }
-
@SuppressWarnings("unchecked")
public IDTYPE getIdForBean(BEANTYPE bean)
throws IllegalArgumentException {
+ VaadinPropertyDescriptor pd = model.get(propertyId);
+ if (null == pd) {
+ throw new IllegalStateException("Property " + propertyId
+ + " not found");
+ }
try {
- return (IDTYPE) getGetter().invoke(bean);
- } catch (IllegalAccessException e) {
- throw new IllegalArgumentException(e);
- } catch (InvocationTargetException e) {
+ Property property = pd.createProperty(bean);
+ return (IDTYPE) property.getValue();
+ } catch (MethodException e) {
throw new IllegalArgumentException(e);
}
}
@@ -149,7 +137,7 @@ public abstract class AbstractBeanContainer extends
* A description of the properties found in beans of type {@link #type}.
* Determines the property ids that are present in the container.
*/
- private transient LinkedHashMap model;
+ private LinkedHashMap> model;
/**
* Constructs a {@code AbstractBeanContainer} for beans of the given type.
@@ -165,17 +153,7 @@ public abstract class AbstractBeanContainer extends
"The bean type passed to AbstractBeanContainer must not be null");
}
this.type = type;
- model = BeanItem.getPropertyDescriptors(type);
- }
-
- /**
- * A special deserialization method that resolves {@link #model} is needed
- * as PropertyDescriptor is not {@link Serializable}.
- */
- private void readObject(java.io.ObjectInputStream in) throws IOException,
- ClassNotFoundException {
- in.defaultReadObject();
- model = BeanItem.getPropertyDescriptors(type);
+ model = BeanItem.getPropertyDescriptors((Class) type);
}
/*
@@ -725,4 +703,95 @@ public abstract class AbstractBeanContainer extends
return new PropertyBasedBeanIdResolver(propertyId);
}
+ @Override
+ public void addListener(Container.PropertySetChangeListener listener) {
+ super.addListener(listener);
+ }
+
+ @Override
+ public void removeListener(Container.PropertySetChangeListener listener) {
+ super.removeListener(listener);
+ }
+
+ @Override
+ public boolean addContainerProperty(Object propertyId, Class> type,
+ Object defaultValue) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException(
+ "Use addNestedContainerProperty(String) to add container properties to a "
+ + getClass().getSimpleName());
+ }
+
+ /**
+ * Adds a property for the container and all its items.
+ *
+ * Primarily for internal use, may change in future versions.
+ *
+ * @param propertyId
+ * @param propertyDescriptor
+ * @return true if the property was added
+ */
+ protected final boolean addContainerProperty(String propertyId,
+ VaadinPropertyDescriptor propertyDescriptor) {
+ if (null == propertyId || null == propertyDescriptor) {
+ return false;
+ }
+
+ // Fails if the Property is already present
+ if (model.containsKey(propertyId)) {
+ return false;
+ }
+
+ model.put(propertyId, propertyDescriptor);
+ for (BeanItem item : itemIdToItem.values()) {
+ item.addItemProperty(propertyId, propertyDescriptor
+ .createProperty((BEANTYPE) item.getBean()));
+ }
+
+ // Sends a change event
+ fireContainerPropertySetChange();
+
+ return true;
+ }
+
+ /**
+ * Adds a nested container property for the container, e.g.
+ * "manager.address.street".
+ *
+ * All intermediate getters must exist and must return non-null values when
+ * the property value is accessed.
+ *
+ * @see NestedMethodProperty
+ *
+ * @param propertyId
+ * @param propertyType
+ * @return true if the property was added
+ */
+ public boolean addNestedContainerProperty(String propertyId,
+ Class> propertyType) {
+ return addContainerProperty(propertyId, new NestedPropertyDescriptor(
+ propertyId, propertyType));
+ }
+
+ @Override
+ public boolean removeContainerProperty(Object propertyId)
+ throws UnsupportedOperationException {
+ // Fails if the Property is not present
+ if (!model.containsKey(propertyId)) {
+ return false;
+ }
+
+ // Removes the Property to Property list and types
+ model.remove(propertyId);
+
+ // If remove the Property from all Items
+ for (final Iterator i = getAllItemIds().iterator(); i.hasNext();) {
+ getUnfilteredItem(i.next()).removeItemProperty(propertyId);
+ }
+
+ // Sends a change event
+ fireContainerPropertySetChange();
+
+ return true;
+ }
+
}
diff --git a/src/com/vaadin/data/util/BeanItem.java b/src/com/vaadin/data/util/BeanItem.java
index eb0d2f44c3..bb8dd6cdc2 100644
--- a/src/com/vaadin/data/util/BeanItem.java
+++ b/src/com/vaadin/data/util/BeanItem.java
@@ -14,8 +14,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
-
-import com.vaadin.data.Property;
+import java.util.Map;
/**
* A wrapper class for adding the Item interface to any Java Bean.
@@ -51,7 +50,7 @@ public class BeanItem extends PropertysetItem {
*
*/
public BeanItem(BT bean) {
- this(bean, getPropertyDescriptors(bean.getClass()));
+ this(bean, getPropertyDescriptors((Class) bean.getClass()));
}
/**
@@ -67,19 +66,12 @@ public class BeanItem extends PropertysetItem {
* pre-computed property descriptors
*/
BeanItem(BT bean,
- LinkedHashMap propertyDescriptors) {
+ Map> propertyDescriptors) {
this.bean = bean;
- for (PropertyDescriptor pd : propertyDescriptors.values()) {
- final Method getMethod = pd.getReadMethod();
- final Method setMethod = pd.getWriteMethod();
- final Class> type = pd.getPropertyType();
- final String name = pd.getName();
- final Property p = new MethodProperty