// $Id$
package org.yajul.reflection;
import org.yajul.collections.CollectionUtil;
import org.yajul.util.ExceptionList;
import org.yajul.util.ReflectionUtil;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.logging.Logger;
import static org.yajul.juli.LogHelper.unexpected;
/**
* Represents the property accessor methods for a given class, and provides methods
* for accessing the properties at run-time.
*
* @author josh Apr 4, 2004 9:11:19 AM
*/
public class BeanProperties {
private static Logger log = Logger.getLogger(BeanProperties.class.getName());
private Map<String, PropertyAccessors> accessorsByName = CollectionUtil.newHashMap();
private Class clazz;
/**
* Constructs a set of property accessors from the class.
*
* @param c the class
*/
public BeanProperties(Class c) {
clazz = c;
Method[] methods = c.getMethods();
Map<String, Method> getters = CollectionUtil.newHashMap();
Map<String, Method> setters = CollectionUtil.newHashMap();
for (Method method : methods) {
String propertyName = ReflectionUtil.getterPropertyName(method);
if (propertyName != null)
getters.put(propertyName, method);
else {
propertyName = ReflectionUtil.setterPropertyName(method);
if (propertyName != null)
setters.put(propertyName, method);
}
}
Set<String> propertyNames = CollectionUtil.newHashSet(getters.keySet());
propertyNames.addAll(setters.keySet());
for (String propertyName : propertyNames) {
Method getter = getters.get(propertyName);
Method setter = setters.get(propertyName);
accessorsByName.put(propertyName,
new PropertyAccessors(clazz, propertyName, getter, setter));
}
}
/**
* Returns the name of the class.
*
* @return the name of the class.
*/
public String getClassName() {
return clazz.getName();
}
/**
* Returns the number of properties in the class.
*
* @return the number of properties in the class.
*/
public int size() {
return accessorsByName.size();
}
/**
* Returns the value of the specified property.
*
* @param bean the object
* @param propertyName the name of the property to get
* @return the value of the property in the specified bean
*/
public Object getProperty(Object bean, String propertyName) {
try {
PropertyAccessors accessorMethods = getAccessorMethods(bean, propertyName);
return accessorMethods.invokeGetter(bean);
} catch (NoSuchMethodException e) {
unexpected(log, e);
return null;
} catch (IllegalAccessException e) {
unexpected(log, e);
return null;
} catch (InvocationTargetException e) {
unexpected(log, e);
return null;
}
}
/**
* Sets the property in the bean to the specified value.
*
* @param bean the object to set the property in
* @param propertyName the name of the property
* @param value the new property value
* @return the new property value
*/
public Object setProperty(Object bean, String propertyName, Object value) {
try {
PropertyAccessors accessorMethods = getAccessorMethods(bean, propertyName);
return accessorMethods.invokeSetter(bean, value);
} catch (Exception e) {
unexpected(log, e);
return null;
}
}
/**
* Returns an iterator that returns the String names of all properties in the class.
*
* @return an iterator that returns the String names of all properties in the class.
*/
public Iterator<String> propertyNames() {
return accessorsByName.keySet().iterator();
}
/**
* Returns the set of all the property name Strings in the class.
*
* @return the set of all the property name Strings in the class.
*/
public Set<String> getPropertyNames() {
return accessorsByName.keySet();
}
/**
* Returns the pair of accessor methods for the given property name.
*
* @param propertyName the property name.
* @return the accessor methods for the property
*/
public PropertyAccessors getAccessorMethods(String propertyName) {
return accessorsByName.get(propertyName);
}
/**
* Returns an iterator that returns all AccessorMethods for all properties in the class.
*
* @return an iterator that returns all AccessorMethods for all properties in the class.
*/
public Iterator<PropertyAccessors> accessorMethods() {
return accessorsByName.values().iterator();
}
/**
* Returns a double property value.
*
* @param bean the bean
* @param propertyName the property name
* @return the property value as a double
*/
public double getDoubleProperty(Object bean, String propertyName) {
return (Double) getProperty(bean, propertyName);
}
/**
* Returns an int property value.
*
* @param bean the bean
* @param propertyName the property name
* @return the int property value
*/
public int getIntProperty(Object bean, String propertyName) {
return (Integer) getProperty(bean, propertyName);
}
/**
* Returns a collection of all of the property values in the specified bean.
*
* @param bean the bean
* @return a collection of all of the property values in the specified bean
* @throws Exception if a property cannot be accessed.
*/
public Collection<Object> values(Object bean) throws Exception {
ArrayList<Object> values = CollectionUtil.newArrayList(size());
Iterator iter = accessorMethods();
ExceptionList exceptions = new ExceptionList();
while (iter.hasNext()) {
PropertyAccessors accessorMethods = (PropertyAccessors) iter.next();
try {
values.add(accessorMethods.invokeGetter(bean));
} catch (Exception e) {
exceptions.add(e);
}
}
exceptions.throwIfException();
return values;
}
/**
* Copies all of the properties from one bean into another.
* The two beans must be of the same class.
*
* @param bean the source bean.
* @param copy the target bean.
* @throws Exception if a property could not be accessed.
*/
public void copy(Object bean, Object copy) throws Exception {
Iterator iter = accessorMethods();
ExceptionList exceptions = new ExceptionList();
while (iter.hasNext()) {
PropertyAccessors accessorMethods = (PropertyAccessors) iter.next();
try {
Object value = accessorMethods.invokeGetter(bean);
accessorMethods.invokeSetter(copy, value);
} catch (Exception e) {
exceptions.add(e);
}
}
exceptions.throwIfException();
}
// --- Implementation methods ---
private PropertyAccessors getAccessorMethods(Object bean, String key)
throws NoSuchMethodException {
checkBean(bean);
// Look up the Method for the property name.
PropertyAccessors accessorMethods = getAccessorMethods(key);
if (accessorMethods == null)
throw new NoSuchMethodException("No property " + key + " in class " + getClassName());
return accessorMethods;
}
private void checkBean(Object bean) {
if (bean == null)
throw new NullPointerException("Bean cannot be null!");
if (bean.getClass() != clazz)
throw new IllegalArgumentException("Bean class "
+ bean.getClass().getName() + " does not match class " + getClassName());
}
}