blob: 0cdf941b0994a0ce7cc39c694c77e8f25a59cb4a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2020 Original authors and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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<>();
/**
* @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 IllegalStateException(e);
}
}
@Override
public void setDataValue(R rowObj, int columnIndex, Object newValue) {
try {
PropertyDescriptor propertyDesc = getPropertyDescriptor(rowObj, columnIndex);
Method writeMethod = propertyDesc != null ? propertyDesc.getWriteMethod() : null;
if (writeMethod == null) {
throw new IllegalStateException(
"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 IllegalStateException("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 {
String propertyName = this.propertyNames.get(columnIndex);
return getPropertyDescriptor(rowObj, propertyName);
}
/**
*
* @param rowObj
* The Java Bean for which the {@link PropertyDescriptor} is
* requested.
* @param propertyName
* The name of the property for which the
* {@link PropertyDescriptor} is requested.
* @return The {@link PropertyDescriptor} that describes the property with
* the given name in the given Java Bean object that exports it via
* a pair of accessor methods.
* @throws IntrospectionException
* if an exception occurs during introspection
* @since 1.6
*/
protected PropertyDescriptor getPropertyDescriptor(Object rowObj, String propertyName) throws IntrospectionException {
synchronized (this.propertyDescriptorMap) {
Map<String, PropertyDescriptor> descriptorMap = this.propertyDescriptorMap.get(rowObj.getClass());
if (descriptorMap == null) {
PropertyDescriptor[] propertyDescriptors =
Introspector.getBeanInfo(rowObj.getClass()).getPropertyDescriptors();
Map<String, PropertyDescriptor> propertiesByAttribute = new HashMap<>();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
propertiesByAttribute.put(propertyDescriptor.getName(), propertyDescriptor);
}
descriptorMap = propertiesByAttribute;
this.propertyDescriptorMap.put(rowObj.getClass(), propertiesByAttribute);
}
return descriptorMap.get(propertyName);
}
}
}