package org.vaadin.viritin.v7; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.apache.commons.beanutils.DynaBean; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.beanutils.WrapDynaBean; import org.apache.commons.beanutils.expression.DefaultResolver; import org.apache.commons.lang3.ClassUtils; import com.vaadin.v7.data.Item; import com.vaadin.v7.data.Property; /** * A standalone version of the DynaBeanItem originally introduced in * ListContainer. Can be used to implement e.g. efficient LazyQueryContainer * instances for service layers. * * TODO check if this could be used in ListContainer without performance * drawbacks. * * TODO check if some staff could be abstracted away * * @author Matti Tahvonen * @param <T> the type of the bean wrapped by the item */ public class DynaBeanItem<T> implements Item { private static final long serialVersionUID = -5073690046197951234L; /* Container-Item-Property specifications don't say item should always return the same property instance, but some components depend on this :-( */ private final Map<Object, DynaProperty> propertyIdToProperty = new HashMap<>(); private class DynaProperty implements Property { private static final long serialVersionUID = -2419540615310696644L; private final String propertyName; DynaProperty(String property) { propertyName = property; } @Override public Object getValue() { try { return getDynaBean().get(propertyName); } catch (Exception e) { try { return PropertyUtils.getProperty(bean, propertyName); } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) { throw new RuntimeException(ex); } } } @Override public void setValue(Object newValue) throws Property.ReadOnlyException { getDynaBean().set(propertyName, newValue); } @Override public Class<?> getType() { try { final org.apache.commons.beanutils.DynaProperty dynaProperty = getDynaBean(). getDynaClass(). getDynaProperty( propertyName); final Class<?> type = dynaProperty.getType(); if (type.isPrimitive()) { // Vaadin can't handle primitive types in _all_ places, so use // wrappers instead. FieldGroup works, but e.g. Table in _editable_ // mode fails for some reason return ClassUtils.primitiveToWrapper(type); } return type; } catch (Exception e) { // If type can't be found via dynaClass, it is most likely // nested/indexed/mapped property try { return org.vaadin.viritin.v7.ListContainer. getNestedPropertyType(getDynaBean().getDynaClass(), propertyName); } catch (ClassNotFoundException | IllegalAccessException | NoSuchFieldException | NoSuchMethodException | InvocationTargetException ex) { throw new RuntimeException(ex); } } } @Override public boolean isReadOnly() { return true; } @Override public void setReadOnly(boolean newStatus) { // Silently ignore, e.g. LazyQueryContainer calls this // throw new UnsupportedOperationException("Not supported yet."); } } private final T bean; private transient DynaBean db; public DynaBeanItem(T bean) { this.bean = bean; } public T getBean() { return bean; } private DynaBean getDynaBean() { if (db == null) { db = new WrapDynaBean(bean); } return db; } @Override public Property getItemProperty(Object id) { final String propertyName = id.toString(); if (getDynaBean().getDynaClass().getDynaProperty(propertyName) == null) { DefaultResolver defaultResolver = new DefaultResolver(); if (!(defaultResolver.hasNested(propertyName) || defaultResolver. isIndexed(propertyName) || defaultResolver.hasNested( propertyName))) { // Lazy query container detects some debug properties via // Item!! return null; } } DynaProperty prop = propertyIdToProperty.get(id); if (prop == null) { prop = new DynaProperty(propertyName); propertyIdToProperty.put(id, prop); } return prop; } @Override public Collection<String> getItemPropertyIds() { ArrayList<String> properties = new ArrayList<String>(); for (org.apache.commons.beanutils.DynaProperty dp : getDynaBean(). getDynaClass().getDynaProperties()) { properties.add(dp.getName()); } properties.remove("class"); return properties; } @Override public boolean addItemProperty(Object id, Property property) throws UnsupportedOperationException { throw new UnsupportedOperationException("Not supported yet."); } @Override public boolean removeItemProperty(Object id) throws UnsupportedOperationException { throw new UnsupportedOperationException("Not supported yet."); } }