summaryrefslogtreecommitdiffstats
path: root/server/src/com/vaadin/data/util/BeanItem.java
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/com/vaadin/data/util/BeanItem.java')
-rw-r--r--server/src/com/vaadin/data/util/BeanItem.java269
1 files changed, 269 insertions, 0 deletions
diff --git a/server/src/com/vaadin/data/util/BeanItem.java b/server/src/com/vaadin/data/util/BeanItem.java
new file mode 100644
index 0000000000..94439471f5
--- /dev/null
+++ b/server/src/com/vaadin/data/util/BeanItem.java
@@ -0,0 +1,269 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+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.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A wrapper class for adding the Item interface to any Java Bean.
+ *
+ * @author Vaadin Ltd.
+ * @version
+ * @VERSION@
+ * @since 3.0
+ */
+@SuppressWarnings("serial")
+public class BeanItem<BT> extends PropertysetItem {
+
+ /**
+ * The bean which this Item is based on.
+ */
+ private final BT bean;
+
+ /**
+ * <p>
+ * Creates a new instance of <code>BeanItem</code> and adds all properties
+ * of a Java Bean to it. The properties are identified by their respective
+ * bean names.
+ * </p>
+ *
+ * <p>
+ * Note : This version only supports introspectable bean properties and
+ * their getter and setter methods. Stand-alone <code>is</code> and
+ * <code>are</code> methods are not supported.
+ * </p>
+ *
+ * @param bean
+ * the Java Bean to copy properties from.
+ *
+ */
+ public BeanItem(BT bean) {
+ this(bean, getPropertyDescriptors((Class<BT>) bean.getClass()));
+ }
+
+ /**
+ * <p>
+ * Creates a new instance of <code>BeanItem</code> using a pre-computed set
+ * of properties. The properties are identified by their respective bean
+ * names.
+ * </p>
+ *
+ * @param bean
+ * the Java Bean to copy properties from.
+ * @param propertyDescriptors
+ * pre-computed property descriptors
+ */
+ BeanItem(BT bean,
+ Map<String, VaadinPropertyDescriptor<BT>> propertyDescriptors) {
+
+ this.bean = bean;
+
+ for (VaadinPropertyDescriptor<BT> pd : propertyDescriptors.values()) {
+ addItemProperty(pd.getName(), pd.createProperty(bean));
+ }
+ }
+
+ /**
+ * <p>
+ * Creates a new instance of <code>BeanItem</code> and adds all listed
+ * properties of a Java Bean to it - in specified order. The properties are
+ * identified by their respective bean names.
+ * </p>
+ *
+ * <p>
+ * Note : This version only supports introspectable bean properties and
+ * their getter and setter methods. Stand-alone <code>is</code> and
+ * <code>are</code> methods are not supported.
+ * </p>
+ *
+ * @param bean
+ * the Java Bean to copy properties from.
+ * @param propertyIds
+ * id of the property.
+ */
+ public BeanItem(BT bean, Collection<?> propertyIds) {
+
+ this.bean = bean;
+
+ // Create bean information
+ LinkedHashMap<String, VaadinPropertyDescriptor<BT>> pds = getPropertyDescriptors((Class<BT>) bean
+ .getClass());
+
+ // Add all the bean properties as MethodProperties to this Item
+ for (Object id : propertyIds) {
+ VaadinPropertyDescriptor<BT> pd = pds.get(id);
+ if (pd != null) {
+ addItemProperty(pd.getName(), pd.createProperty(bean));
+ }
+ }
+
+ }
+
+ /**
+ * <p>
+ * Creates a new instance of <code>BeanItem</code> and adds all listed
+ * properties of a Java Bean to it - in specified order. The properties are
+ * identified by their respective bean names.
+ * </p>
+ *
+ * <p>
+ * Note : This version only supports introspectable bean properties and
+ * their getter and setter methods. Stand-alone <code>is</code> and
+ * <code>are</code> methods are not supported.
+ * </p>
+ *
+ * @param bean
+ * the Java Bean to copy properties from.
+ * @param propertyIds
+ * ids of the properties.
+ */
+ public BeanItem(BT bean, String[] propertyIds) {
+ this(bean, Arrays.asList(propertyIds));
+ }
+
+ /**
+ * <p>
+ * Perform introspection on a Java Bean class to find its properties.
+ * </p>
+ *
+ * <p>
+ * Note : This version only supports introspectable bean properties and
+ * their getter and setter methods. Stand-alone <code>is</code> and
+ * <code>are</code> methods are not supported.
+ * </p>
+ *
+ * @param beanClass
+ * the Java Bean class to get properties for.
+ * @return an ordered map from property names to property descriptors
+ */
+ 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 {
+ List<PropertyDescriptor> propertyDescriptors = getBeanPropertyDescriptor(beanClass);
+
+ // Add all the bean properties as MethodProperties to this Item
+ // later entries on the list overwrite earlier ones
+ for (PropertyDescriptor pd : propertyDescriptors) {
+ final Method getMethod = pd.getReadMethod();
+ if ((getMethod != null)
+ && getMethod.getDeclaringClass() != Object.class) {
+ VaadinPropertyDescriptor<BT> vaadinPropertyDescriptor = new MethodPropertyDescriptor<BT>(
+ pd.getName(), pd.getPropertyType(),
+ pd.getReadMethod(), pd.getWriteMethod());
+ pdMap.put(pd.getName(), vaadinPropertyDescriptor);
+ }
+ }
+ } 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());
+ }
+ }
+
+ /**
+ * Expands nested bean properties by replacing a top-level property with
+ * some or all of its sub-properties. The expansion is not recursive.
+ *
+ * @param propertyId
+ * property id for the property whose sub-properties are to be
+ * expanded,
+ * @param subPropertyIds
+ * sub-properties to expand, all sub-properties are expanded if
+ * not specified
+ */
+ public void expandProperty(String propertyId, String... subPropertyIds) {
+ Set<String> subPropertySet = new HashSet<String>(
+ Arrays.asList(subPropertyIds));
+
+ if (0 == subPropertyIds.length) {
+ // Enumerate all sub-properties
+ Class<?> propertyType = getItemProperty(propertyId).getType();
+ Map<String, ?> pds = getPropertyDescriptors(propertyType);
+ subPropertySet.addAll(pds.keySet());
+ }
+
+ for (String subproperty : subPropertySet) {
+ String qualifiedPropertyId = propertyId + "." + subproperty;
+ addNestedProperty(qualifiedPropertyId);
+ }
+
+ removeItemProperty(propertyId);
+ }
+
+ /**
+ * Adds a nested property to the item.
+ *
+ * @param nestedPropertyId
+ * property id to add. This property must not exist in the item
+ * already and must of of form "field1.field2" where field2 is a
+ * field in the object referenced to by field1
+ */
+ public void addNestedProperty(String nestedPropertyId) {
+ addItemProperty(nestedPropertyId, new NestedMethodProperty<Object>(
+ getBean(), nestedPropertyId));
+ }
+
+ /**
+ * Gets the underlying JavaBean object.
+ *
+ * @return the bean object.
+ */
+ public BT getBean() {
+ return bean;
+ }
+
+}