/******************************************************************************* * Copyright (c) 2012, 2017 Original authors and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Original authors and others - initial API and implementation * Dirk Fauth <dirk.fauth@googlemail.com> - Bug 450927 * Simon Scholz <simon.scholz@vogella.com> - Bug 487913, 488067 ******************************************************************************/ package org.eclipse.nebula.widgets.nattable.data; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Convenience class which uses java reflection to get/set property names from * the row bean. It looks for getter methods for reading and setter methods for * writing according to the Java conventions. * * @param <R> * type of the row object/bean */ public class ReflectiveColumnPropertyAccessor<R> implements IColumnPropertyAccessor<R> { private static final Log LOG = LogFactory.getLog(ReflectiveColumnPropertyAccessor.class); private final List<String> propertyNames; private Map<Class<?>, Map<String, PropertyDescriptor>> propertyDescriptorMap = new HashMap<Class<?>, Map<String, PropertyDescriptor>>(); /** * @param propertyNames * of the members of the row bean */ public ReflectiveColumnPropertyAccessor(String... propertyNames) { this.propertyNames = Arrays.asList(propertyNames); } /** * @param propertyNames * of the members of the row bean * @since 1.4 */ public ReflectiveColumnPropertyAccessor(List<String> propertyNames) { this.propertyNames = propertyNames; } @Override public int getColumnCount() { return this.propertyNames.size(); } @Override public Object getDataValue(R rowObj, int columnIndex) { try { PropertyDescriptor propertyDesc = getPropertyDescriptor(rowObj, columnIndex); Method readMethod = propertyDesc.getReadMethod(); return readMethod.invoke(rowObj); } catch (Exception e) { LOG.warn(e); throw new RuntimeException(e); } } @Override public void setDataValue(R rowObj, int columnIndex, Object newValue) { try { PropertyDescriptor propertyDesc = getPropertyDescriptor(rowObj, columnIndex); Method writeMethod = propertyDesc.getWriteMethod(); if (writeMethod == null) { throw new RuntimeException( "Setter method not found in backing bean for value at column index: " + columnIndex); //$NON-NLS-1$ } writeMethod.invoke(rowObj, newValue); } catch (IllegalArgumentException ex) { LOG.error("Data type being set does not match the data type of the setter method in the backing bean", ex); //$NON-NLS-1$ } catch (Exception e) { LOG.error(e); throw new RuntimeException("Error while setting data value"); //$NON-NLS-1$ } }; @Override public String getColumnProperty(int columnIndex) { return this.propertyNames.get(columnIndex); } @Override public int getColumnIndex(String propertyName) { return this.propertyNames.indexOf(propertyName); } private PropertyDescriptor getPropertyDescriptor(R rowObj, int columnIndex) throws IntrospectionException { synchronized (rowObj) { if (!this.propertyDescriptorMap.containsKey(rowObj.getClass())) { PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(rowObj.getClass()).getPropertyDescriptors(); Map<String, PropertyDescriptor> propertiesByAttribute = new HashMap<String, PropertyDescriptor>(); for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { propertiesByAttribute.put(propertyDescriptor.getName(), propertyDescriptor); } this.propertyDescriptorMap.put(rowObj.getClass(), propertiesByAttribute); } } final String propertyName = this.propertyNames.get(columnIndex); return this.propertyDescriptorMap.get(rowObj.getClass()).get(propertyName); } }