package com.vaadin.data.util;
import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
+import java.util.List;
import com.vaadin.data.Property;
// Try to introspect, if it fails, we just have an empty Item
try {
- final BeanInfo info = Introspector.getBeanInfo(beanClass);
- final PropertyDescriptor[] pds = info.getPropertyDescriptors();
+ List<PropertyDescriptor> propertyDescriptors = getBeanPropertyDescriptor(beanClass);
// Add all the bean properties as MethodProperties to this Item
- for (int i = 0; i < pds.length; i++) {
- final Method getMethod = pds[i].getReadMethod();
+ // later entries on the list overwrite earlier ones
+ for (PropertyDescriptor pd : propertyDescriptors) {
+ final Method getMethod = pd.getReadMethod();
if ((getMethod != null)
&& getMethod.getDeclaringClass() != Object.class) {
- pdMap.put(pds[i].getName(), pds[i]);
+ pdMap.put(pd.getName(), pd);
}
}
} catch (final java.beans.IntrospectionException ignored) {
return pdMap;
}
+ /**
+ * Returns the property descriptors of a class or an interface.
+ *
+ * For an interface, superinterfaces are also iterated as Introspector does
+ * not take them into account (Oracle Java bug 4275879), but in that case,
+ * both the setter and the getter for a property must be in the same
+ * interface and should not be overridden in subinterfaces for the discovery
+ * to work correctly.
+ *
+ * For interfaces, the iteration is depth first and the properties of
+ * superinterfaces are returned before those of their subinterfaces.
+ *
+ * @param beanClass
+ * @return
+ * @throws IntrospectionException
+ */
+ private static List<PropertyDescriptor> getBeanPropertyDescriptor(
+ final Class<?> beanClass) throws IntrospectionException {
+ // Oracle bug 4275879: Introspector does not consider superinterfaces of
+ // an interface
+ if (beanClass.isInterface()) {
+ List<PropertyDescriptor> propertyDescriptors = new ArrayList<PropertyDescriptor>();
+
+ for (Class<?> cls : beanClass.getInterfaces()) {
+ propertyDescriptors.addAll(getBeanPropertyDescriptor(cls));
+ }
+
+ BeanInfo info = Introspector.getBeanInfo(beanClass);
+ propertyDescriptors.addAll(Arrays.asList(info
+ .getPropertyDescriptors()));
+
+ return propertyDescriptors;
+ } else {
+ BeanInfo info = Introspector.getBeanInfo(beanClass);
+ return Arrays.asList(info.getPropertyDescriptors());
+ }
+ }
+
/**
* Gets the underlying JavaBean object.
*
package com.vaadin.tests.server.container;
+import java.beans.PropertyDescriptor;
+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 junit.framework.Assert;
import junit.framework.TestCase;
}
}
+ protected static interface MySuperInterface {
+ public int getSuper1();
+
+ public void setSuper1(int i);
+
+ public int getOverride();
+ }
+
+ protected static interface MySuperInterface2 {
+ public int getSuper2();
+ }
+
+ protected static interface MySubInterface extends MySuperInterface,
+ MySuperInterface2 {
+ public int getSub();
+
+ public void setSub(int i);
+
+ public int getOverride();
+
+ public void setOverride(int i);
+ }
+
public void testGetProperties() {
BeanItem<MySuperClass> item = new BeanItem<MySuperClass>(
new MySuperClass());
Assert.assertFalse(item.getItemProperty("name2").isReadOnly());
}
+ public void testGetInterfaceProperties() throws SecurityException,
+ NoSuchMethodException, IllegalArgumentException,
+ IllegalAccessException, InvocationTargetException {
+ Method method = BeanItem.class.getDeclaredMethod(
+ "getPropertyDescriptors", Class.class);
+ method.setAccessible(true);
+ LinkedHashMap<String, PropertyDescriptor> propertyDescriptors = (LinkedHashMap<String, PropertyDescriptor>) 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());
+ }
+
+ public void testGetSuperInterfaceProperties() throws SecurityException,
+ NoSuchMethodException, IllegalArgumentException,
+ IllegalAccessException, InvocationTargetException {
+ Method method = BeanItem.class.getDeclaredMethod(
+ "getPropertyDescriptors", Class.class);
+ method.setAccessible(true);
+ LinkedHashMap<String, PropertyDescriptor> propertyDescriptors = (LinkedHashMap<String, PropertyDescriptor>) method
+ .invoke(null, MySubInterface.class);
+
+ Assert.assertEquals(4, propertyDescriptors.size());
+ Assert.assertTrue(propertyDescriptors.containsKey("sub"));
+ Assert.assertTrue(propertyDescriptors.containsKey("super1"));
+ Assert.assertTrue(propertyDescriptors.containsKey("super2"));
+ Assert.assertTrue(propertyDescriptors.containsKey("override"));
+
+ Assert.assertNotNull(propertyDescriptors.get("override")
+ .getWriteMethod());
+ }
+
public void testPropertyExplicitOrder() {
Collection<String> ids = new ArrayList<String>();
ids.add("name");