*/
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;
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;
* {@link #addItemAt(int, Object, Object)}.
* </p>
*
- * <p>
- * It is not possible to add additional properties to the container and nested
- * bean properties are not supported.
- * </p>
- *
* @param <IDTYPE>
* The type of the item identifier
* @param <BEANTYPE>
*/
public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
AbstractInMemoryContainer<IDTYPE, String, BeanItem<BEANTYPE>> implements
- Filterable, SimpleFilterable, Sortable, ValueChangeListener {
+ Filterable, SimpleFilterable, Sortable, ValueChangeListener,
+ PropertySetChangeNotifier {
/**
* Resolver that maps beans to their (item) identifiers, removing the need
BeanIdResolver<IDTYPE, BEANTYPE> {
private final Object propertyId;
- private transient Method getMethod;
public PropertyBasedBeanIdResolver(Object propertyId) {
if (propertyId == null) {
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<BEANTYPE> 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);
}
}
* 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<String, PropertyDescriptor> model;
+ private LinkedHashMap<String, VaadinPropertyDescriptor<BEANTYPE>> model;
/**
* Constructs a {@code AbstractBeanContainer} for beans of the given type.
"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<BEANTYPE>) type);
}
/*
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<BEANTYPE> 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<IDTYPE> i = getAllItemIds().iterator(); i.hasNext();) {
+ getUnfilteredItem(i.next()).removeItemProperty(propertyId);
+ }
+
+ // Sends a change event
+ fireContainerPropertySetChange();
+
+ return true;
+ }
+
}
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.
*
*/
public BeanItem(BT bean) {
- this(bean, getPropertyDescriptors(bean.getClass()));
+ this(bean, getPropertyDescriptors((Class<BT>) bean.getClass()));
}
/**
* pre-computed property descriptors
*/
BeanItem(BT bean,
- LinkedHashMap<String, PropertyDescriptor> propertyDescriptors) {
+ Map<String, VaadinPropertyDescriptor<BT>> 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<Object>(type, bean,
- getMethod, setMethod);
- addItemProperty(name, p);
-
+ for (VaadinPropertyDescriptor<BT> pd : propertyDescriptors.values()) {
+ addItemProperty(pd.getName(), pd.createProperty(bean));
}
}
this.bean = bean;
// Create bean information
- LinkedHashMap<String, PropertyDescriptor> pds = getPropertyDescriptors(bean
+ LinkedHashMap<String, VaadinPropertyDescriptor<BT>> pds = getPropertyDescriptors((Class<BT>) bean
.getClass());
// Add all the bean properties as MethodProperties to this Item
for (Object id : propertyIds) {
- PropertyDescriptor pd = pds.get(id);
+ VaadinPropertyDescriptor<BT> pd = pds.get(id);
if (pd != null) {
- final String name = pd.getName();
- final Method getMethod = pd.getReadMethod();
- final Method setMethod = pd.getWriteMethod();
- final Class<?> type = pd.getPropertyType();
- final Property p = new MethodProperty<Object>(type, bean,
- getMethod, setMethod);
- addItemProperty(name, p);
+ addItemProperty(pd.getName(), pd.createProperty(bean));
}
}
* the Java Bean class to get properties for.
* @return an ordered map from property names to property descriptors
*/
- static LinkedHashMap<String, PropertyDescriptor> getPropertyDescriptors(
- final Class<?> beanClass) {
- final LinkedHashMap<String, PropertyDescriptor> pdMap = new LinkedHashMap<String, PropertyDescriptor>();
+ static <BT> LinkedHashMap<String, VaadinPropertyDescriptor<BT>> getPropertyDescriptors(
+ final Class<BT> beanClass) {
+ final LinkedHashMap<String, VaadinPropertyDescriptor<BT>> pdMap = new LinkedHashMap<String, VaadinPropertyDescriptor<BT>>();
// Try to introspect, if it fails, we just have an empty Item
try {
final Method getMethod = pd.getReadMethod();
if ((getMethod != null)
&& getMethod.getDeclaringClass() != Object.class) {
- pdMap.put(pd.getName(), pd);
+ VaadinPropertyDescriptor<BT> vaadinPropertyDescriptor = new MethodPropertyDescriptor(
+ pd.getName(), pd.getPropertyType(),
+ pd.getReadMethod(), pd.getWriteMethod());
+ pdMap.put(pd.getName(), vaadinPropertyDescriptor);
}
}
} catch (final java.beans.IntrospectionException ignored) {
out.writeObject(getArgs);
if (setMethod != null) {
out.writeObject(setMethod.getName());
+ SerializerHelper.writeClass(out, setMethod.getDeclaringClass());
SerializerHelper
.writeClassArray(out, setMethod.getParameterTypes());
} else {
out.writeObject(null);
out.writeObject(null);
+ out.writeObject(null);
}
if (getMethod != null) {
out.writeObject(getMethod.getName());
+ SerializerHelper.writeClass(out, getMethod.getDeclaringClass());
SerializerHelper
.writeClassArray(out, getMethod.getParameterTypes());
} else {
out.writeObject(null);
out.writeObject(null);
+ out.writeObject(null);
}
};
setArgs = (Object[]) in.readObject();
getArgs = (Object[]) in.readObject();
String name = (String) in.readObject();
+ Class<T> setMethodClass = (Class<T>) SerializerHelper.readClass(in);
Class<?>[] paramTypes = SerializerHelper.readClassArray(in);
if (name != null) {
- setMethod = instance.getClass().getMethod(name, paramTypes);
+ setMethod = setMethodClass.getMethod(name, paramTypes);
} else {
setMethod = null;
}
name = (String) in.readObject();
+ Class<T> getMethodClass = (Class<T>) SerializerHelper.readClass(in);
paramTypes = SerializerHelper.readClassArray(in);
if (name != null) {
- getMethod = instance.getClass().getMethod(name, paramTypes);
+ getMethod = getMethodClass.getMethod(name, paramTypes);
} else {
getMethod = null;
}
// Find the get method
getMethod = null;
try {
- getMethod = beanClass.getMethod("get" + beanPropertyName,
- new Class[] {});
+ getMethod = initGetterMethod(beanPropertyName, beanClass);
} catch (final java.lang.NoSuchMethodException ignored) {
- try {
- getMethod = beanClass.getMethod("is" + beanPropertyName,
- new Class[] {});
- } catch (final java.lang.NoSuchMethodException ignoredAsWell) {
- try {
- getMethod = beanClass.getMethod("are" + beanPropertyName,
- new Class[] {});
- } catch (final java.lang.NoSuchMethodException e) {
- throw new MethodException(this, "Bean property "
- + beanPropertyName + " can not be found");
- }
- }
+ throw new MethodException(this, "Bean property " + beanPropertyName
+ + " can not be found");
}
// In case the get method is found, resolve the type
// Gets the return type from get method
if (returnType.isPrimitive()) {
- if (returnType.equals(Boolean.TYPE)) {
- type = (Class<T>) Boolean.class;
- } else if (returnType.equals(Integer.TYPE)) {
- type = (Class<T>) Integer.class;
- } else if (returnType.equals(Float.TYPE)) {
- type = (Class<T>) Float.class;
- } else if (returnType.equals(Double.TYPE)) {
- type = (Class<T>) Double.class;
- } else if (returnType.equals(Byte.TYPE)) {
- type = (Class<T>) Byte.class;
- } else if (returnType.equals(Character.TYPE)) {
- type = (Class<T>) Character.class;
- } else if (returnType.equals(Short.TYPE)) {
- type = (Class<T>) Short.class;
- } else if (returnType.equals(Long.TYPE)) {
- type = (Class<T>) Long.class;
- } else {
+ type = (Class<T>) convertPrimitiveType(returnType);
+ if (type.isPrimitive()) {
throw new MethodException(this, "Bean property "
+ beanPropertyName
+ " getter return type must not be void");
}
// Gets the return type from get method
- if (type.isPrimitive()) {
- if (type.equals(Boolean.TYPE)) {
- this.type = (Class<T>) Boolean.class;
- } else if (type.equals(Integer.TYPE)) {
- this.type = (Class<T>) Integer.class;
- } else if (type.equals(Float.TYPE)) {
- this.type = (Class<T>) Float.class;
- } else if (type.equals(Double.TYPE)) {
- this.type = (Class<T>) Double.class;
- } else if (type.equals(Byte.TYPE)) {
- this.type = (Class<T>) Byte.class;
- } else if (type.equals(Character.TYPE)) {
- this.type = (Class<T>) Character.class;
- } else if (type.equals(Short.TYPE)) {
- this.type = (Class<T>) Short.class;
- } else if (type.equals(Long.TYPE)) {
- this.type = (Class<T>) Long.class;
- }
- }
+ this.type = (Class<T>) convertPrimitiveType(type);
setArguments(getArgs, setArgs, setArgumentIndex);
readOnly = (setMethod == null);
}
}
+ // Gets the return type from get method
+ type = convertPrimitiveType(type);
+
+ this.getMethod = getMethod;
+ this.setMethod = setMethod;
+ setArguments(getArgs, setArgs, setArgumentIndex);
+ readOnly = (setMethod == null);
+ this.instance = instance;
+ this.type = type;
+ }
+
+ /**
+ * Find a getter method for a property (getXyz(), isXyz() or areXyz()).
+ *
+ * @param propertyName
+ * name of the property
+ * @param beanClass
+ * class in which to look for the getter methods
+ * @return Method
+ * @throws NoSuchMethodException
+ * if no getter found
+ */
+ protected static Method initGetterMethod(String propertyName,
+ final Class<?> beanClass) throws NoSuchMethodException {
+ propertyName = propertyName.substring(0, 1).toUpperCase()
+ + propertyName.substring(1);
+
+ Method getMethod = null;
+ try {
+ getMethod = beanClass.getMethod("get" + propertyName,
+ new Class[] {});
+ } catch (final java.lang.NoSuchMethodException ignored) {
+ try {
+ getMethod = beanClass.getMethod("is" + propertyName,
+ new Class[] {});
+ } catch (final java.lang.NoSuchMethodException ignoredAsWell) {
+ getMethod = beanClass.getMethod("are" + propertyName,
+ new Class[] {});
+ }
+ }
+ return getMethod;
+ }
+
+ protected static Class<?> convertPrimitiveType(Class<?> type) {
// Gets the return type from get method
if (type.isPrimitive()) {
if (type.equals(Boolean.TYPE)) {
type = Long.class;
}
}
-
- this.getMethod = getMethod;
- this.setMethod = setMethod;
- setArguments(getArgs, setArgs, setArgumentIndex);
- readOnly = (setMethod == null);
- this.instance = instance;
- this.type = type;
+ return type;
}
/**
*
* @param value
*/
- private void invokeSetMethod(Object value) {
+ protected void invokeSetMethod(Object value) {
try {
// Construct a temporary argument array only if needed
}
}
+ /**
+ * Returns the bean instance which to which the property applies. For
+ * internal use.
+ *
+ * @return bean instance
+ */
+ protected Object getInstance() {
+ return instance;
+ }
+
+ /**
+ * Returns the setter method to use. For internal use.
+ *
+ * @return setter {@link Method}
+ */
+ protected Method getSetMethod() {
+ return setMethod;
+ }
+
/**
* Sets the Property's read-only mode to the specified status.
*
--- /dev/null
+package com.vaadin.data.util;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.vaadin.data.Property;
+import com.vaadin.util.SerializerHelper;
+
+/**
+ * Property descriptor that is able to create simple {@link MethodProperty}
+ * instances for a bean, using given accessors.
+ *
+ * @param <BT>
+ * bean type
+ *
+ * @since 6.6
+ */
+public class MethodPropertyDescriptor<BT> implements
+ VaadinPropertyDescriptor<BT> {
+
+ private static final Logger logger = Logger
+ .getLogger(MethodPropertyDescriptor.class.getName());
+
+ private final String name;
+ private Class<?> propertyType;
+ private transient Method readMethod;
+ private transient Method writeMethod;
+
+ /**
+ * Creates a property descriptor that can create MethodProperty instances to
+ * access the underlying bean property.
+ *
+ * @param name
+ * of the property
+ * @param propertyType
+ * type (class) of the property
+ * @param readMethod
+ * getter {@link Method} for the property
+ * @param writeMethod
+ * setter {@link Method} for the property or null if read-only
+ * property
+ */
+ public MethodPropertyDescriptor(String name, Class<?> propertyType,
+ Method readMethod, Method writeMethod) {
+ this.name = name;
+ this.propertyType = propertyType;
+ this.readMethod = readMethod;
+ this.writeMethod = writeMethod;
+ }
+
+ /* Special serialization to handle method references */
+ private void writeObject(java.io.ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ SerializerHelper.writeClass(out, propertyType);
+
+ if (writeMethod != null) {
+ out.writeObject(writeMethod.getName());
+ SerializerHelper.writeClass(out, writeMethod.getDeclaringClass());
+ SerializerHelper.writeClassArray(out,
+ writeMethod.getParameterTypes());
+ } else {
+ out.writeObject(null);
+ out.writeObject(null);
+ out.writeObject(null);
+ }
+
+ if (readMethod != null) {
+ out.writeObject(readMethod.getName());
+ SerializerHelper.writeClass(out, readMethod.getDeclaringClass());
+ SerializerHelper.writeClassArray(out,
+ readMethod.getParameterTypes());
+ } else {
+ out.writeObject(null);
+ out.writeObject(null);
+ out.writeObject(null);
+ }
+ }
+
+ /* Special serialization to handle method references */
+ private void readObject(java.io.ObjectInputStream in) throws IOException,
+ ClassNotFoundException {
+ in.defaultReadObject();
+ try {
+ @SuppressWarnings("unchecked")
+ // business assumption; type parameters not checked at runtime
+ Class<BT> class1 = (Class<BT>) SerializerHelper.readClass(in);
+ propertyType = class1;
+
+ String name = (String) in.readObject();
+ Class<?> writeMethodClass = SerializerHelper.readClass(in);
+ Class<?>[] paramTypes = SerializerHelper.readClassArray(in);
+ if (name != null) {
+ writeMethod = writeMethodClass.getMethod(name, paramTypes);
+ } else {
+ writeMethod = null;
+ }
+
+ name = (String) in.readObject();
+ Class<?> readMethodClass = SerializerHelper.readClass(in);
+ paramTypes = SerializerHelper.readClassArray(in);
+ if (name != null) {
+ readMethod = readMethodClass.getMethod(name, paramTypes);
+ } else {
+ readMethod = null;
+ }
+ } catch (SecurityException e) {
+ logger.log(Level.SEVERE, "Internal deserialization error", e);
+ } catch (NoSuchMethodException e) {
+ logger.log(Level.SEVERE, "Internal deserialization error", e);
+ }
+ };
+
+ public String getName() {
+ return name;
+ }
+
+ public Class<?> getPropertyType() {
+ return propertyType;
+ }
+
+ public Property createProperty(Object bean) {
+ return new MethodProperty<Object>(propertyType, bean, readMethod,
+ writeMethod);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package com.vaadin.data.util;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Nested accessor based property for a bean.
+ *
+ * The property is specified in the dotted notation, e.g. "address.street", and
+ * can contain multiple levels of nesting.
+ *
+ * When accessing the property value, all intermediate getters must return
+ * non-null values.
+ *
+ * @see MethodProperty
+ *
+ * @param <T>
+ * property type
+ *
+ * @since 6.6
+ */
+public class NestedMethodProperty<T> extends MethodProperty<T> {
+
+ // needed for de-serialization
+ private String propertyName;
+
+ // chain of getter methods up to but not including the last method handled
+ // by the superclass
+ private transient List<Method> getMethods;
+
+ /* Special serialization to handle method references */
+ private void writeObject(java.io.ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ // getMethods is reconstructed on read based on propertyName
+ }
+
+ /* Special serialization to handle method references */
+ private void readObject(java.io.ObjectInputStream in) throws IOException,
+ ClassNotFoundException {
+ in.defaultReadObject();
+
+ // re-build getMethods: some duplicated code with builder method
+ getMethods = new ArrayList<Method>();
+ Class<?> propertyClass = getInstance().getClass();
+ String[] simplePropertyNames = propertyName.split("\\.");
+ for (int i = 0; i < simplePropertyNames.length; i++) {
+ String simplePropertyName = simplePropertyNames[i].trim();
+ try {
+ Method getter = initGetterMethod(simplePropertyName,
+ propertyClass);
+ propertyClass = getter.getReturnType();
+ getMethods.add(getter);
+ } catch (final java.lang.NoSuchMethodException e) {
+ throw new InvalidObjectException("Bean property '"
+ + simplePropertyName + "' not found");
+ }
+ }
+ }
+
+ /**
+ * Constructs a nested method property for a given object instance. The
+ * property name is a dot separated string pointing to a nested property,
+ * e.g. "manager.address.street".
+ *
+ * @param <T>
+ * property type (deepest nested property)
+ * @param instance
+ * top-level bean to which the property applies
+ * @param propertyName
+ * dot separated nested property name
+ * @return new NestedMethodProperty instance
+ */
+ public static <T> NestedMethodProperty<T> buildNestedMethodProperty(
+ Object instance, String propertyName) {
+ List<Method> getMethods = new ArrayList<Method>();
+
+ String lastSimplePropertyName = propertyName;
+ Class<?> lastClass = instance.getClass();
+
+ // first top-level property, then go deeper in a loop
+ Class<?> propertyClass = instance.getClass();
+ String[] simplePropertyNames = propertyName.split("\\.");
+ if (propertyName.endsWith(".") || 0 == simplePropertyNames.length) {
+ throw new MethodException(null, "Invalid property name '"
+ + propertyName + "'");
+ }
+ for (int i = 0; i < simplePropertyNames.length; i++) {
+ String simplePropertyName = simplePropertyNames[i].trim();
+ if (simplePropertyName.length() > 0) {
+ lastSimplePropertyName = simplePropertyName;
+ lastClass = propertyClass;
+ try {
+ Method getter = initGetterMethod(simplePropertyName,
+ propertyClass);
+ propertyClass = getter.getReturnType();
+ getMethods.add(getter);
+ } catch (final java.lang.NoSuchMethodException e) {
+ throw new MethodException(null, "Bean property '"
+ + simplePropertyName + "' not found");
+ }
+ } else {
+ throw new MethodException(null,
+ "Empty or invalid bean property identifier in '"
+ + propertyName + "'");
+ }
+ }
+
+ // In case the get method is found, resolve the type
+ Method lastGetMethod = getMethods.get(getMethods.size() - 1);
+ Class<?> type = lastGetMethod.getReturnType();
+
+ // Finds the set method
+ Method setMethod = null;
+ try {
+ // Assure that the first letter is upper cased (it is a common
+ // mistake to write firstName, not FirstName).
+ if (Character.isLowerCase(lastSimplePropertyName.charAt(0))) {
+ final char[] buf = lastSimplePropertyName.toCharArray();
+ buf[0] = Character.toUpperCase(buf[0]);
+ lastSimplePropertyName = new String(buf);
+ }
+
+ setMethod = lastClass.getMethod("set" + lastSimplePropertyName,
+ new Class[] { type });
+ } catch (final NoSuchMethodException skipped) {
+ }
+
+ NestedMethodProperty<T> property = new NestedMethodProperty<T>(
+ (Class<T>) convertPrimitiveType(type), instance, propertyName,
+ lastGetMethod, setMethod);
+ property.getMethods = getMethods;
+
+ return property;
+ }
+
+ protected NestedMethodProperty(Class<T> type, Object instance,
+ String propertyName, Method lastGetMethod, Method setMethod) {
+ super(type, instance, lastGetMethod, setMethod);
+ this.propertyName = propertyName;
+ }
+
+ /**
+ * Gets the value stored in the Property. The value is resolved by calling
+ * the specified getter method with the argument specified at instantiation.
+ *
+ * @return the value of the Property
+ */
+ @Override
+ public Object getValue() {
+ try {
+ Object instance = getInstance();
+ for (Method m : getMethods) {
+ instance = m.invoke(instance);
+ }
+ return instance;
+ } catch (final Throwable e) {
+ throw new MethodException(this, e);
+ }
+ }
+
+ /**
+ * Internal method to actually call the setter method of the wrapped
+ * property.
+ *
+ * @param value
+ */
+ @Override
+ protected void invokeSetMethod(Object value) {
+ try {
+ Object instance = getInstance();
+ for (int i = 0; i < getMethods.size() - 1; i++) {
+ instance = getMethods.get(i).invoke(instance);
+ }
+ getSetMethod().invoke(instance, new Object[] { value });
+ } catch (final InvocationTargetException e) {
+ throw new MethodException(this, e.getTargetException());
+ } catch (final Exception e) {
+ throw new MethodException(this, e);
+ }
+ }
+
+ /**
+ * Returns an unmodifiable list of getter methods to call in sequence to get
+ * the property value.
+ *
+ * This API may change in future versions.
+ *
+ * @return unmodifiable list of getter methods corresponding to each segment
+ * of the property name
+ */
+ protected List<Method> getGetMethods() {
+ return Collections.unmodifiableList(getMethods);
+ }
+
+}
--- /dev/null
+package com.vaadin.data.util;
+
+import com.vaadin.data.Property;
+
+/**
+ * Property descriptor that is able to create nested property instances for a
+ * bean.
+ *
+ * The property is specified in the dotted notation, e.g. "address.street", and
+ * can contain multiple levels of nesting.
+ *
+ * @param <BT>
+ * bean type
+ *
+ * @since 6.6
+ */
+public class NestedPropertyDescriptor<BT> implements
+ VaadinPropertyDescriptor<BT> {
+
+ private final String name;
+ private final Class<?> propertyType;
+
+ /**
+ * Creates a property descriptor that can create MethodProperty instances to
+ * access the underlying bean property.
+ *
+ * The property is only validated when trying to access its value.
+ *
+ * @param name
+ * of the property in a dotted path format, e.g. "address.street"
+ * @param propertyType
+ * type (class) of the property
+ */
+ public NestedPropertyDescriptor(String name, Class<?> propertyType) {
+ this.name = name;
+ this.propertyType = propertyType;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Class<?> getPropertyType() {
+ return propertyType;
+ }
+
+ public Property createProperty(BT bean) {
+ return NestedMethodProperty.buildNestedMethodProperty(bean, name);
+ }
+
+}
--- /dev/null
+package com.vaadin.data.util;
+
+import java.io.Serializable;
+
+import com.vaadin.data.Property;
+
+/**
+ * Property descriptor that can create a property instance for a bean.
+ *
+ * Used by {@link BeanItem} and {@link AbstractBeanContainer} to keep track of
+ * the set of properties of items.
+ *
+ * @param <BT>
+ * bean type
+ *
+ * @since 6.6
+ */
+public interface VaadinPropertyDescriptor<BT> extends Serializable {
+ /**
+ * Returns the name of the property.
+ *
+ * @return
+ */
+ public String getName();
+
+ /**
+ * Returns the type of the property.
+ *
+ * @return Class<?>
+ */
+ public Class<?> getPropertyType();
+
+ /**
+ * Creates a new {@link Property} instance for this property for a bean.
+ *
+ * @param bean
+ * @return
+ */
+ public Property createProperty(BT bean);
+}
\ No newline at end of file
// should get exception
}
- try {
- container.removeContainerProperty("name");
- Assert.fail();
- } catch (UnsupportedOperationException e) {
- // should get exception
- }
-
assertEquals(1, container.size());
}
+ public void testRemoveContainerProperty() {
+ BeanContainer<String, Person> container = new BeanContainer<String, Person>(
+ Person.class);
+ container.setBeanIdResolver(new PersonNameResolver());
+ container.addBean(new Person("John"));
+
+ Assert.assertEquals("John",
+ container.getContainerProperty("John", "name").getValue());
+ Assert.assertTrue(container.removeContainerProperty("name"));
+ Assert.assertNull(container.getContainerProperty("John", "name"));
+
+ Assert.assertNotNull(container.getItem("John"));
+ // property removed also from item
+ Assert.assertNull(container.getItem("John").getItemProperty("name"));
+ }
+
public void testAddNullBeans() {
BeanContainer<String, Person> container = new BeanContainer<String, Person>(
Person.class);
assertEquals(5, container.size());
}
+ public void testAddNestedContainerProperty() {
+ BeanContainer<String, NestedMethodPropertyTest.Person> container = new BeanContainer<String, NestedMethodPropertyTest.Person>(
+ NestedMethodPropertyTest.Person.class);
+ container.setBeanIdProperty("name");
+
+ container.addBean(new NestedMethodPropertyTest.Person("John",
+ new NestedMethodPropertyTest.Address("Ruukinkatu 2-4", 20540)));
+
+ assertTrue(container.addNestedContainerProperty("address.street",
+ String.class));
+ assertEquals("Ruukinkatu 2-4",
+ container.getContainerProperty("John", "address.street")
+ .getValue());
+ }
+
}
// should get exception
}
- try {
- container.removeContainerProperty("name");
- Assert.fail();
- } catch (UnsupportedOperationException e) {
- // should get exception
- }
-
assertEquals(1, container.size());
}
+ public void testRemoveContainerProperty() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ Person john = new Person("John");
+ container.addBean(john);
+
+ Assert.assertEquals("John", container
+ .getContainerProperty(john, "name").getValue());
+ Assert.assertTrue(container.removeContainerProperty("name"));
+ Assert.assertNull(container.getContainerProperty(john, "name"));
+
+ Assert.assertNotNull(container.getItem(john));
+ // property removed also from item
+ Assert.assertNull(container.getItem(john).getItemProperty("name"));
+ }
+
public void testAddNullBean() {
BeanItemContainer<Person> container = new BeanItemContainer<Person>(
Person.class);
}
}
+ public void testAddNestedContainerProperty() {
+ BeanItemContainer<NestedMethodPropertyTest.Person> container = new BeanItemContainer<NestedMethodPropertyTest.Person>(
+ NestedMethodPropertyTest.Person.class);
+
+ NestedMethodPropertyTest.Person john = new NestedMethodPropertyTest.Person(
+ "John", new NestedMethodPropertyTest.Address("Ruukinkatu 2-4",
+ 20540));
+ container.addBean(john);
+
+ assertTrue(container.addNestedContainerProperty("address.street",
+ String.class));
+ assertEquals("Ruukinkatu 2-4",
+ container.getContainerProperty(john, "address.street")
+ .getValue());
+ }
+
}
package com.vaadin.tests.server.container;
-import java.beans.PropertyDescriptor;
+import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.Map;
import junit.framework.Assert;
import junit.framework.TestCase;
import com.vaadin.data.util.BeanItem;
+import com.vaadin.data.util.MethodProperty;
+import com.vaadin.data.util.MethodPropertyDescriptor;
+import com.vaadin.data.util.VaadinPropertyDescriptor;
/**
* Test BeanItem specific features.
Method method = BeanItem.class.getDeclaredMethod(
"getPropertyDescriptors", Class.class);
method.setAccessible(true);
- LinkedHashMap<String, PropertyDescriptor> propertyDescriptors = (LinkedHashMap<String, PropertyDescriptor>) method
+ LinkedHashMap<String, VaadinPropertyDescriptor<Class>> propertyDescriptors = (LinkedHashMap<String, VaadinPropertyDescriptor<Class>>) method
.invoke(null, MySuperInterface.class);
Assert.assertEquals(2, propertyDescriptors.size());
Assert.assertTrue(propertyDescriptors.containsKey("super1"));
Assert.assertTrue(propertyDescriptors.containsKey("override"));
- Assert.assertNull(propertyDescriptors.get("override").getWriteMethod());
+ MethodProperty<?> property = (MethodProperty<?>) propertyDescriptors
+ .get("override").createProperty(getClass());
+ Assert.assertTrue(property.isReadOnly());
}
public void testGetSuperInterfaceProperties() throws SecurityException,
Method method = BeanItem.class.getDeclaredMethod(
"getPropertyDescriptors", Class.class);
method.setAccessible(true);
- LinkedHashMap<String, PropertyDescriptor> propertyDescriptors = (LinkedHashMap<String, PropertyDescriptor>) method
+ LinkedHashMap<String, VaadinPropertyDescriptor<Class>> propertyDescriptors = (LinkedHashMap<String, VaadinPropertyDescriptor<Class>>) method
.invoke(null, MySubInterface.class);
Assert.assertEquals(4, propertyDescriptors.size());
Assert.assertTrue(propertyDescriptors.containsKey("super2"));
Assert.assertTrue(propertyDescriptors.containsKey("override"));
- Assert.assertNotNull(propertyDescriptors.get("override")
- .getWriteMethod());
+ MethodProperty<?> property = (MethodProperty<?>) propertyDescriptors
+ .get("override").createProperty(getClass());
+ Assert.assertFalse(property.isReadOnly());
}
public void testPropertyExplicitOrder() {
Assert.assertTrue(item.getItemProperty("name2").isReadOnly());
}
+ public void testCustomProperties() throws Exception {
+ LinkedHashMap<String, VaadinPropertyDescriptor<MyClass>> propertyDescriptors = new LinkedHashMap<String, VaadinPropertyDescriptor<MyClass>>();
+ propertyDescriptors.put(
+ "myname",
+ new MethodPropertyDescriptor<BeanItemTest.MyClass>("myname",
+ MyClass.class, MyClass.class
+ .getDeclaredMethod("getName"), MyClass.class
+ .getDeclaredMethod("setName", String.class)));
+ MyClass instance = new MyClass("bean1");
+ Constructor<BeanItem> constructor = BeanItem.class
+ .getDeclaredConstructor(Object.class, Map.class);
+ constructor.setAccessible(true);
+ BeanItem<MyClass> item = constructor.newInstance(instance,
+ propertyDescriptors);
+
+ Assert.assertEquals(1, item.getItemPropertyIds().size());
+ Assert.assertEquals("bean1", item.getItemProperty("myname").getValue());
+ }
+
+ public void testAddRemoveProperty() throws Exception {
+ MethodPropertyDescriptor<BeanItemTest.MyClass> pd = new MethodPropertyDescriptor<BeanItemTest.MyClass>(
+ "myname", MyClass.class,
+ MyClass.class.getDeclaredMethod("getName"),
+ MyClass.class.getDeclaredMethod("setName", String.class));
+
+ BeanItem<MyClass> item = new BeanItem(new MyClass("bean1"));
+
+ Assert.assertEquals(6, item.getItemPropertyIds().size());
+ Assert.assertEquals(null, item.getItemProperty("myname"));
+
+ item.addItemProperty("myname", pd.createProperty(item.getBean()));
+ Assert.assertEquals(7, item.getItemPropertyIds().size());
+ Assert.assertEquals("bean1", item.getItemProperty("myname").getValue());
+ item.removeItemProperty("myname");
+ Assert.assertEquals(6, item.getItemPropertyIds().size());
+ Assert.assertEquals(null, item.getItemProperty("myname"));
+ }
}
--- /dev/null
+package com.vaadin.tests.server.container;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import com.vaadin.data.util.MethodProperty.MethodException;
+import com.vaadin.data.util.NestedMethodProperty;
+
+public class NestedMethodPropertyTest extends TestCase {
+
+ public static class Address implements Serializable {
+ private String street;
+ private int postalCodePrimitive;
+ private Integer postalCodeObject;
+
+ public Address(String street, int postalCode) {
+ this.street = street;
+ postalCodePrimitive = postalCode;
+ postalCodeObject = postalCode;
+ }
+
+ public void setStreet(String street) {
+ this.street = street;
+ }
+
+ public String getStreet() {
+ return street;
+ }
+
+ public void setPostalCodePrimitive(int postalCodePrimitive) {
+ this.postalCodePrimitive = postalCodePrimitive;
+ }
+
+ public int getPostalCodePrimitive() {
+ return postalCodePrimitive;
+ }
+
+ public void setPostalCodeObject(Integer postalCodeObject) {
+ this.postalCodeObject = postalCodeObject;
+ }
+
+ public Integer getPostalCodeObject() {
+ return postalCodeObject;
+ }
+
+ // read-only boolean property
+ public boolean isBoolean() {
+ return true;
+ }
+ }
+
+ public static class Person implements Serializable {
+ private String name;
+ private Address address;
+
+ public Person(String name, Address address) {
+ this.name = name;
+ this.address = address;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+
+ public Address getAddress() {
+ return address;
+ }
+ }
+
+ public static class Team implements Serializable {
+ private String name;
+ private Person manager;
+
+ public Team(String name, Person manager) {
+ this.name = name;
+ this.manager = manager;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setManager(Person manager) {
+ this.manager = manager;
+ }
+
+ public Person getManager() {
+ return manager;
+ }
+ }
+
+ private Address oldMill;
+ private Person joonas;
+ private Team vaadin;
+
+ @Override
+ public void setUp() {
+ oldMill = new Address("Ruukinkatu 2-4", 20540);
+ joonas = new Person("Joonas", oldMill);
+ vaadin = new Team("Vaadin", joonas);
+ }
+
+ @Override
+ public void tearDown() {
+ vaadin = null;
+ joonas = null;
+ oldMill = null;
+ }
+
+ public void testSingleLevelNestedSimpleProperty() {
+ NestedMethodProperty<String> nameProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin, "name");
+
+ Assert.assertEquals(String.class, nameProperty.getType());
+ Assert.assertEquals("Vaadin", nameProperty.getValue());
+ }
+
+ public void testSingleLevelNestedObjectProperty() {
+ NestedMethodProperty<Person> managerProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin, "manager");
+
+ Assert.assertEquals(Person.class, managerProperty.getType());
+ Assert.assertEquals(joonas, managerProperty.getValue());
+ }
+
+ public void testMultiLevelNestedProperty() {
+ NestedMethodProperty<String> managerNameProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin, "manager.name");
+ NestedMethodProperty<Address> addressProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin, "manager.address");
+ NestedMethodProperty<String> streetProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin, "manager.address.street");
+ NestedMethodProperty<String> postalCodePrimitiveProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin,
+ "manager.address.postalCodePrimitive");
+ NestedMethodProperty<String> postalCodeObjectProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin,
+ "manager.address.postalCodeObject");
+ NestedMethodProperty<String> booleanProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin, "manager.address.boolean");
+
+ Assert.assertEquals(String.class, managerNameProperty.getType());
+ Assert.assertEquals("Joonas", managerNameProperty.getValue());
+
+ Assert.assertEquals(Address.class, addressProperty.getType());
+ Assert.assertEquals(oldMill, addressProperty.getValue());
+
+ Assert.assertEquals(String.class, streetProperty.getType());
+ Assert.assertEquals("Ruukinkatu 2-4", streetProperty.getValue());
+
+ Assert.assertEquals(Integer.class,
+ postalCodePrimitiveProperty.getType());
+ Assert.assertEquals(20540, postalCodePrimitiveProperty.getValue());
+
+ Assert.assertEquals(Integer.class, postalCodeObjectProperty.getType());
+ Assert.assertEquals(20540, postalCodeObjectProperty.getValue());
+
+ Assert.assertEquals(Boolean.class, booleanProperty.getType());
+ Assert.assertEquals(true, booleanProperty.getValue());
+ }
+
+ public void testEmptyPropertyName() {
+ try {
+ NestedMethodProperty.buildNestedMethodProperty(vaadin, "");
+ fail();
+ } catch (MethodException e) {
+ // should get exception
+ }
+
+ try {
+ NestedMethodProperty.buildNestedMethodProperty(vaadin, " ");
+ fail();
+ } catch (MethodException e) {
+ // should get exception
+ }
+ }
+
+ public void testInvalidPropertyName() {
+ try {
+ NestedMethodProperty.buildNestedMethodProperty(vaadin, ".");
+ fail();
+ } catch (MethodException e) {
+ // should get exception
+ }
+ try {
+ NestedMethodProperty.buildNestedMethodProperty(vaadin, ".manager");
+ fail();
+ } catch (MethodException e) {
+ // should get exception
+ }
+ try {
+ NestedMethodProperty.buildNestedMethodProperty(vaadin, "manager.");
+ fail();
+ } catch (MethodException e) {
+ // should get exception
+ }
+ try {
+ NestedMethodProperty.buildNestedMethodProperty(vaadin,
+ "manager..name");
+ fail();
+ } catch (MethodException e) {
+ // should get exception
+ }
+ }
+
+ public void testInvalidNestedPropertyName() {
+ try {
+ NestedMethodProperty.buildNestedMethodProperty(vaadin, "member");
+ fail();
+ } catch (MethodException e) {
+ // should get exception
+ }
+
+ try {
+ NestedMethodProperty.buildNestedMethodProperty(vaadin,
+ "manager.pet");
+ fail();
+ } catch (MethodException e) {
+ // should get exception
+ }
+
+ try {
+ NestedMethodProperty.buildNestedMethodProperty(vaadin,
+ "manager.address.city");
+ fail();
+ } catch (MethodException e) {
+ // should get exception
+ }
+ }
+
+ public void testNullNestedProperty() {
+ NestedMethodProperty<String> managerNameProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin, "manager.name");
+ NestedMethodProperty<String> streetProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin, "manager.address.street");
+
+ joonas.setAddress(null);
+ try {
+ streetProperty.getValue();
+ fail();
+ } catch (MethodException e) {
+ // should get exception
+ }
+
+ vaadin.setManager(null);
+ try {
+ managerNameProperty.getValue();
+ fail();
+ } catch (MethodException e) {
+ // should get exception
+ }
+ try {
+ streetProperty.getValue();
+ fail();
+ } catch (MethodException e) {
+ // should get exception
+ }
+
+ vaadin.setManager(joonas);
+ Assert.assertEquals("Joonas", managerNameProperty.getValue());
+ }
+
+ public void testMultiLevelNestedPropertySetValue() {
+ NestedMethodProperty<String> managerNameProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin, "manager.name");
+ NestedMethodProperty<Address> addressProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin, "manager.address");
+ NestedMethodProperty<String> streetProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin, "manager.address.street");
+ NestedMethodProperty<String> postalCodePrimitiveProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin,
+ "manager.address.postalCodePrimitive");
+ NestedMethodProperty<String> postalCodeObjectProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin,
+ "manager.address.postalCodeObject");
+
+ managerNameProperty.setValue("Joonas L");
+ Assert.assertEquals("Joonas L", joonas.getName());
+ streetProperty.setValue("Ruukinkatu");
+ Assert.assertEquals("Ruukinkatu", oldMill.getStreet());
+ postalCodePrimitiveProperty.setValue(0);
+ postalCodeObjectProperty.setValue(1);
+ Assert.assertEquals(0, oldMill.getPostalCodePrimitive());
+ Assert.assertEquals(Integer.valueOf(1), oldMill.getPostalCodeObject());
+
+ postalCodeObjectProperty.setValue(null);
+ Assert.assertNull(oldMill.getPostalCodeObject());
+
+ Address address2 = new Address("Other street", 12345);
+ addressProperty.setValue(address2);
+ Assert.assertEquals("Other street", streetProperty.getValue());
+ }
+
+ public void testSerialization() throws IOException, ClassNotFoundException {
+ NestedMethodProperty<String> streetProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin, "manager.address.street");
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ new ObjectOutputStream(baos).writeObject(streetProperty);
+ NestedMethodProperty<String> property2 = (NestedMethodProperty<String>) new ObjectInputStream(
+ new ByteArrayInputStream(baos.toByteArray())).readObject();
+
+ Assert.assertEquals("Ruukinkatu 2-4", property2.getValue());
+ }
+
+ public void testIsReadOnly() {
+ NestedMethodProperty<String> streetProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin, "manager.address.street");
+ NestedMethodProperty<String> booleanProperty = NestedMethodProperty
+ .buildNestedMethodProperty(vaadin, "manager.address.boolean");
+
+ Assert.assertFalse(streetProperty.isReadOnly());
+ Assert.assertTrue(booleanProperty.isReadOnly());
+ }
+
+}
--- /dev/null
+package com.vaadin.tests.server.container;
+
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import com.vaadin.data.Property;
+import com.vaadin.data.util.MethodPropertyDescriptor;
+import com.vaadin.data.util.NestedPropertyDescriptor;
+import com.vaadin.data.util.VaadinPropertyDescriptor;
+import com.vaadin.tests.server.container.NestedMethodPropertyTest.Person;
+
+public class PropertyDescriptorTest extends TestCase {
+ public void testMethodPropertyDescriptorSerialization() throws Exception {
+ PropertyDescriptor[] pds = Introspector.getBeanInfo(Person.class)
+ .getPropertyDescriptors();
+
+ MethodPropertyDescriptor<Person> descriptor = null;
+
+ for (PropertyDescriptor pd : pds) {
+ if ("name".equals(pd.getName())) {
+ descriptor = new MethodPropertyDescriptor<Person>(pd.getName(),
+ String.class, pd.getReadMethod(), pd.getWriteMethod());
+ break;
+ }
+ }
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ new ObjectOutputStream(baos).writeObject(descriptor);
+ VaadinPropertyDescriptor<Person> descriptor2 = (VaadinPropertyDescriptor<Person>) new ObjectInputStream(
+ new ByteArrayInputStream(baos.toByteArray())).readObject();
+
+ Property property = descriptor2
+ .createProperty(new Person("John", null));
+ Assert.assertEquals("John", property.getValue());
+ }
+
+ public void testNestedPropertyDescriptorSerialization() throws Exception {
+ NestedPropertyDescriptor<Person> pd = new NestedPropertyDescriptor<Person>(
+ "name", String.class);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ new ObjectOutputStream(baos).writeObject(pd);
+ VaadinPropertyDescriptor<Person> pd2 = (VaadinPropertyDescriptor<Person>) new ObjectInputStream(
+ new ByteArrayInputStream(baos.toByteArray())).readObject();
+
+ Property property = pd2.createProperty(new Person("John", null));
+ Assert.assertEquals("John", property.getValue());
+ }
+}