/******************************************************************************* * Copyright 2013 Thomas Letsch (contact@thomas-letsch.de) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package org.vaadin.addons.javaee.container; 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.LinkedHashMap; import java.util.LinkedList; import java.util.List; import com.googlecode.javaeeutils.jpa.PersistentEntity; import com.vaadin.data.Buffered; import com.vaadin.data.Property; import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.data.util.MethodPropertyDescriptor; import com.vaadin.data.util.PropertysetItem; import com.vaadin.data.util.VaadinPropertyDescriptor; public class EntityItem<ENTITY extends PersistentEntity> extends PropertysetItem implements Buffered { private static final long serialVersionUID = 1L; private final EntityContainer<ENTITY> entityContainer; private ENTITY entity; public EntityItem(EntityContainer<ENTITY> entityContainer, ENTITY entity) { this.entityContainer = entityContainer; this.entity = entity; addPropertyDescriptors(entity); } @Override public Property<?> getItemProperty(Object id) { Property<?> itemProperty = super.getItemProperty(id); if (itemProperty == null) { addNestedItem(id); itemProperty = super.getItemProperty(id); } return itemProperty; } /** * 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 EntityProperty<Object>(getEntity(), nestedPropertyId)); } private void addNestedItem(Object id) { if (id instanceof String) { String nestedId = (String) id; if (nestedId.contains(".")) { addNestedProperty(nestedId); } } } @Override public void commit() throws SourceException, InvalidValueException { if (getEntity().getId() == null) { EntityItem<ENTITY> addedItem = entityContainer.addItem(getEntity()); setEntity(addedItem.getEntity()); } else { entityContainer.updateItem(this); } } @Override public void discard() throws SourceException { entityContainer.refreshItem(this); } @Override public void setBuffered(boolean buffered) { if (true) { } } @Override public boolean isBuffered() { return true; } @Override public boolean isModified() { return false; } /** * <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 <ENTITY> LinkedHashMap<String, VaadinPropertyDescriptor<ENTITY>> getPropertyDescriptors(final Class<ENTITY> beanClass) { final LinkedHashMap<String, VaadinPropertyDescriptor<ENTITY>> pdMap = new LinkedHashMap<String, VaadinPropertyDescriptor<ENTITY>>(); // 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<ENTITY> vaadinPropertyDescriptor = new MethodPropertyDescriptor<ENTITY>(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; } BeanInfo info = Introspector.getBeanInfo(beanClass); return Arrays.asList(info.getPropertyDescriptors()); } public ENTITY getEntity() { return entity; } public void setEntity(ENTITY entity) { this.entity = entity; for (Object id : new LinkedList<>(getItemPropertyIds())) { removeItemProperty(id); } addPropertyDescriptors(entity); } @SuppressWarnings("unchecked") private void addPropertyDescriptors(ENTITY entity) { for (VaadinPropertyDescriptor<ENTITY> pd : getPropertyDescriptors((Class<ENTITY>) entity.getClass()).values()) { addItemProperty(pd.getName(), pd.createProperty(entity)); } } }