Refactoring some code

This commit is contained in:
Patrick Gotthard 2013-11-15 23:27:01 +01:00
parent 25aba9758e
commit cb03da8c67
6 changed files with 242 additions and 204 deletions

View file

@ -24,7 +24,6 @@ import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -32,8 +31,7 @@ import java.util.Set;
/**
* Obtains all property descriptors from a bean (interface or implementation).
* <p>
* The java.beans.Introspector does not process the interfaces hierarchy chain,
* this one does.
* The java.beans.Introspector does not process the interfaces hierarchy chain, this one does.
* <p>
*
* @author Alejandro Abdelnur
@ -42,29 +40,98 @@ import java.util.Set;
public class BeanIntrospector {
private static final Map<Class<?>, PropertyDescriptor[]> introspected = new HashMap<Class<?>, PropertyDescriptor[]>();
private static final String SETTER = "set";
private static final String GETTER = "get";
private static final String BOOLEAN_GETTER = "is";
public static synchronized PropertyDescriptor[] getPropertyDescriptors(final Class<?> klass) throws IntrospectionException {
PropertyDescriptor[] descriptors = introspected.get(klass);
/**
* Extract all {@link PropertyDescriptor}s for properties with getters and setters for the given class.
*
* @param clazz The class to extract the desired {@link PropertyDescriptor}s from
* @return All {@link PropertyDescriptor} for properties with getters and setters for the given class.
* @throws IntrospectionException When the extraction of the desired {@link PropertyDescriptor}s failed
*/
private static synchronized PropertyDescriptor[] getPropertyDescriptors(final Class<?> clazz) throws IntrospectionException {
PropertyDescriptor[] descriptors = introspected.get(clazz);
if (descriptors == null) {
descriptors = getPDs(klass);
introspected.put(klass, descriptors);
descriptors = getPDs(clazz);
introspected.put(clazz, descriptors);
}
return descriptors;
}
private static PropertyDescriptor[] getPDs(final Class<?> klass) throws IntrospectionException {
final Method[] methods = klass.getMethods();
final Map<String, PropertyDescriptor> getters = getPDs(methods, false);
final Map<String, PropertyDescriptor> setters = getPDs(methods, true);
final List<PropertyDescriptor> pds = merge(getters, setters);
final PropertyDescriptor[] array = new PropertyDescriptor[pds.size()];
pds.toArray(array);
return array;
/**
* Extract all {@link PropertyDescriptor}s for properties with a getter that does not come from {@link Object} and does not accept parameters.
*
* @param clazz The class to extract the desired {@link PropertyDescriptor}s from
* @return All {@link PropertyDescriptor}s for properties with a getter that does not come from {@link Object} and does not accept parameters.
* @throws IntrospectionException When the extraction of the desired {@link PropertyDescriptor}s failed
*/
public static List<PropertyDescriptor> getPropertyDescriptorsWithGetters(final Class<?> clazz) throws IntrospectionException {
final List<PropertyDescriptor> relevantDescriptors = new ArrayList<PropertyDescriptor>();
final PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(clazz);
if (propertyDescriptors != null) {
for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) {
final Method getter = propertyDescriptor.getReadMethod();
final boolean getterExists = getter != null;
if (getterExists) {
final boolean getterFromObject = getter.getDeclaringClass() == Object.class;
final boolean getterWithoutParams = getter.getParameterTypes().length == 0;
if (!getterFromObject && getterWithoutParams) {
relevantDescriptors.add(propertyDescriptor);
}
private static final String SETTER = "set";
private static final String GETTER = "get";
private static final String BOOLEAN_GETTER = "is";
}
}
}
return relevantDescriptors;
}
/**
* Extract all {@link PropertyDescriptor}s for properties with a getter (that does not come from {@link Object} and does not accept parameters) and a
* setter.
*
* @param clazz The class to extract the desired {@link PropertyDescriptor}s from
* @return All {@link PropertyDescriptor}s for properties with a getter (that does not come from {@link Object} and does not accept parameters) and a
* setter.
* @throws IntrospectionException When the extraction of the desired {@link PropertyDescriptor}s failed
*/
public static List<PropertyDescriptor> getPropertyDescriptorsWithGettersAndSetters(final Class<?> clazz) throws IntrospectionException {
final List<PropertyDescriptor> relevantDescriptors = new ArrayList<PropertyDescriptor>();
final List<PropertyDescriptor> propertyDescriptors = getPropertyDescriptorsWithGetters(clazz);
for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) {
final Method setter = propertyDescriptor.getWriteMethod();
final boolean setterExists = setter != null;
if (setterExists) {
relevantDescriptors.add(propertyDescriptor);
}
}
return relevantDescriptors;
}
private static PropertyDescriptor[] getPDs(final Class<?> clazz) throws IntrospectionException {
final Method[] methods = clazz.getMethods();
final Map<String, PropertyDescriptor> getters = getPDs(methods, false);
final Map<String, PropertyDescriptor> setters = getPDs(methods, true);
final List<PropertyDescriptor> propertyDescriptors = merge(getters, setters);
return propertyDescriptors.toArray(new PropertyDescriptor[propertyDescriptors.size()]);
}
private static Map<String, PropertyDescriptor> getPDs(final Method[] methods, final boolean setters) throws IntrospectionException {
final Map<String, PropertyDescriptor> pds = new HashMap<String, PropertyDescriptor>();
@ -96,29 +163,30 @@ public class BeanIntrospector {
private static List<PropertyDescriptor> merge(final Map<String, PropertyDescriptor> getters, final Map<String, PropertyDescriptor> setters)
throws IntrospectionException {
final List<PropertyDescriptor> props = new ArrayList<PropertyDescriptor>();
final Set<String> processedProps = new HashSet<String>();
final Iterator<String> gs = getters.keySet().iterator();
while (gs.hasNext()) {
final String name = gs.next();
final PropertyDescriptor getter = getters.get(name);
final PropertyDescriptor setter = setters.get(name);
for (final String propertyName : getters.keySet()) {
final PropertyDescriptor getter = getters.get(propertyName);
final PropertyDescriptor setter = setters.get(propertyName);
if (setter != null) {
processedProps.add(name);
final PropertyDescriptor prop = new PropertyDescriptor(name, getter.getReadMethod(), setter.getWriteMethod());
processedProps.add(propertyName);
final PropertyDescriptor prop = new PropertyDescriptor(propertyName, getter.getReadMethod(), setter.getWriteMethod());
props.add(prop);
} else {
props.add(getter);
}
}
final Set<String> writeOnlyProps = new HashSet<String>(setters.keySet());
writeOnlyProps.removeAll(processedProps);
final Iterator<String> ss = writeOnlyProps.iterator();
while (ss.hasNext()) {
final String name = ss.next();
final PropertyDescriptor setter = setters.get(name);
final Set<String> writeOnlyProperties = new HashSet<String>();
writeOnlyProperties.removeAll(processedProps);
for (final String propertyName : writeOnlyProperties) {
final PropertyDescriptor setter = setters.get(propertyName);
props.add(setter);
}
return props;
}

View file

@ -25,6 +25,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@ -162,35 +163,27 @@ public class CloneableBean implements Serializable, Cloneable {
final Object clonedBean = clazz.newInstance();
final PropertyDescriptor[] propertyDescriptors = BeanIntrospector.getPropertyDescriptors(clazz);
if (propertyDescriptors != null) {
final List<PropertyDescriptor> propertyDescriptors = BeanIntrospector.getPropertyDescriptorsWithGettersAndSetters(clazz);
for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) {
final String propertyName = propertyDescriptor.getName();
final boolean ignoredProperty = ignoreProperties.contains(propertyName);
if (!ignoredProperty) {
final Method getter = propertyDescriptor.getReadMethod();
final Method setter = propertyDescriptor.getWriteMethod();
final String propertyName = propertyDescriptor.getName();
final boolean getterExists = getter != null;
final boolean setterExists = setter != null;
final boolean ignoredProperty = ignoreProperties.contains(propertyName);
if (getterExists && setterExists && !ignoredProperty) {
final boolean getterFromObject = getter.getDeclaringClass() == Object.class;
final boolean getterWithoutParams = getter.getParameterTypes().length == 0;
if (!getterFromObject && getterWithoutParams) {
Object value = getter.invoke(obj, NO_PARAMS);
if (value != null) {
value = doClone(value);
setter.invoke(clonedBean, new Object[] { value });
}
}
}
}
}
return clonedBean;

View file

@ -26,22 +26,43 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.syndication.feed.CopyFrom;
/**
* @author Alejandro Abdelnur
*/
public class CopyFromHelper {
private static final Logger LOG = LoggerFactory.getLogger(CopyFromHelper.class);
private static final Set<Class<?>> BASIC_TYPES = new HashSet<Class<?>>();
private static final Object[] NO_PARAMS = new Object[0];
private final Class<? extends CopyFrom<?>> beanInterfaceClass;
private final Map<String, Class<?>> baseInterfaceMap; // ENTRIES(propertyName,interface.class)
private final Map<Class<? extends CopyFrom<?>>, Class<?>> baseImplMap; // ENTRIES(interface.class,implementation.class)
static {
BASIC_TYPES.add(Boolean.class);
BASIC_TYPES.add(Byte.class);
BASIC_TYPES.add(Character.class);
BASIC_TYPES.add(Double.class);
BASIC_TYPES.add(Float.class);
BASIC_TYPES.add(Integer.class);
BASIC_TYPES.add(Long.class);
BASIC_TYPES.add(Short.class);
BASIC_TYPES.add(String.class);
BASIC_TYPES.add(Date.class);
}
public CopyFromHelper(final Class<? extends CopyFrom<?>> beanInterfaceClass, final Map<String, Class<?>> basePropInterfaceMap,
final Map<Class<? extends CopyFrom<?>>, Class<?>> basePropClassImplMap) {
this.beanInterfaceClass = beanInterfaceClass;
@ -50,47 +71,37 @@ public class CopyFromHelper {
}
public void copy(final Object target, final Object source) {
try {
final PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(beanInterfaceClass);
if (pds != null) {
for (final PropertyDescriptor pd : pds) {
final String propertyName = pd.getName();
final Method pReadMethod = pd.getReadMethod();
final Method pWriteMethod = pd.getWriteMethod();
if (pReadMethod != null && pWriteMethod != null && // ensure
// it has
// getter
// and
// setter
// methods
pReadMethod.getDeclaringClass() != Object.class && // filter
// Object.class
// getter
// methods
pReadMethod.getParameterTypes().length == 0 && // filter
// getter
// methods
// that
// take
// parameters
baseInterfaceMap.containsKey(propertyName)) { // only
// copies
// properties
// defined
// as
// copyFrom-able
Object value = pReadMethod.invoke(source, NO_PARAMS);
final List<PropertyDescriptor> propertyDescriptors = BeanIntrospector.getPropertyDescriptorsWithGettersAndSetters(beanInterfaceClass);
for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) {
final String propertyName = propertyDescriptor.getName();
if (baseInterfaceMap.containsKey(propertyName)) {
final Method getter = propertyDescriptor.getReadMethod();
// only copies properties defined as copyFrom-able
Object value = getter.invoke(source, NO_PARAMS);
if (value != null) {
final Method setter = propertyDescriptor.getWriteMethod();
final Class<?> baseInterface = baseInterfaceMap.get(propertyName);
value = doCopy(value, baseInterface);
pWriteMethod.invoke(target, new Object[] { value });
setter.invoke(target, new Object[] { value });
}
}
}
} catch (final Exception e) {
LOG.error("Error while copying object", e);
throw new RuntimeException("Could not do a copyFrom " + e, e);
}
} catch (final Exception ex) {
throw new RuntimeException("Could not do a copyFrom " + ex, ex);
}
}
private CopyFrom<?> createInstance(final Class<? extends CopyFrom<?>> interfaceClass) throws Exception {
@ -170,21 +181,6 @@ public class CopyFromHelper {
return newMap;
}
private static final Set<Class<?>> BASIC_TYPES = new HashSet<Class<?>>();
static {
BASIC_TYPES.add(Boolean.class);
BASIC_TYPES.add(Byte.class);
BASIC_TYPES.add(Character.class);
BASIC_TYPES.add(Double.class);
BASIC_TYPES.add(Float.class);
BASIC_TYPES.add(Integer.class);
BASIC_TYPES.add(Long.class);
BASIC_TYPES.add(Short.class);
BASIC_TYPES.add(String.class);
BASIC_TYPES.add(Date.class);
}
private boolean isBasicType(final Class<?> vClass) {
return BASIC_TYPES.contains(vClass);
}

View file

@ -20,23 +20,22 @@ import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.List;
/**
* Provides deep <b>Bean</b> equals() and hashCode() functionality for Java
* Beans.
* Provides deep <b>Bean</b> equals() and hashCode() functionality for Java Beans.
* <p>
* It works on all read/write properties, recursively. It support all primitive
* types, Strings, Collections, bean-like objects and multi-dimensional arrays
* of any of them.
* It works on all read/write properties, recursively. It support all primitive types, Strings, Collections, bean-like objects and multi-dimensional arrays of
* any of them.
* <p>
* The hashcode is calculated by getting the hashcode of the Bean String
* representation.
* The hashcode is calculated by getting the hashcode of the Bean String representation.
* <p>
*
* @author Alejandro Abdelnur
*
*/
public class EqualsBean implements Serializable {
private static final long serialVersionUID = 9120107899175152601L;
private static final Object[] NO_PARAMS = new Object[0];
@ -96,12 +95,10 @@ public class EqualsBean implements Serializable {
}
/**
* Indicates whether some other object is "equal to" this object as defined
* by the Object equals() method.
* Indicates whether some other object is "equal to" this object as defined by the Object equals() method.
* <p>
* To be used by classes extending EqualsBean. Although it works also for
* classes using EqualsBean in a delegation pattern, for correctness those
* classes should use the
* To be used by classes extending EqualsBean. Although it works also for classes using EqualsBean in a delegation pattern, for correctness those classes
* should use the
*
* @see #beanEquals(Object) beanEquals method.
* <p>
@ -115,58 +112,53 @@ public class EqualsBean implements Serializable {
}
/**
* Indicates whether some other object is "equal to" the object passed in
* the constructor, as defined by the Object equals() method.
* Indicates whether some other object is "equal to" the object passed in the constructor, as defined by the Object equals() method.
* <p>
* To be used by classes using EqualsBean in a delegation pattern,
*
* @see #EqualsBean(Class,Object) constructor.
* <p>
* @param obj he reference object with which to compare.
* @return <b>true</b> if the object passed in the constructor is equal to
* the 'obj' object.
* @return <b>true</b> if the object passed in the constructor is equal to the 'obj' object.
*
*/
public boolean beanEquals(final Object obj) {
final Object bean1 = this.obj;
final Object bean2 = obj;
boolean eq;
if (bean1 == null && bean2 == null) {
if (bean1 == null && bean2 == null) { // both are null
eq = true;
} else if (bean1 == null || bean2 == null) {
} else if (bean1 == null || bean2 == null) { // one of the objects is null
eq = false;
} else {
if (!beanClass.isInstance(bean2)) {
} else if (!beanClass.isInstance(bean2)) { // not of the same type
eq = false;
} else {
eq = true;
try {
final PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(beanClass);
if (pds != null) {
for (int i = 0; eq && i < pds.length; i++) {
final Method pReadMethod = pds[i].getReadMethod();
if (pReadMethod != null && // ensure it has a getter
// method
pReadMethod.getDeclaringClass() != Object.class && // filter
// Object.class
// getter
// methods
pReadMethod.getParameterTypes().length == 0) { // filter
// getter
// methods
// that
// take
// parameters
final Object value1 = pReadMethod.invoke(bean1, NO_PARAMS);
final Object value2 = pReadMethod.invoke(bean2, NO_PARAMS);
final List<PropertyDescriptor> propertyDescriptors = BeanIntrospector.getPropertyDescriptorsWithGetters(beanClass);
for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) {
final Method getter = propertyDescriptor.getReadMethod();
final Object value1 = getter.invoke(bean1, NO_PARAMS);
final Object value2 = getter.invoke(bean2, NO_PARAMS);
eq = doEquals(value1, value2);
if (!eq) {
break;
}
}
}
} catch (final Exception ex) {
throw new RuntimeException("Could not execute equals()", ex);
}
}
}
return eq;
}
@ -176,12 +168,10 @@ public class EqualsBean implements Serializable {
* <p>
* It follows the contract defined by the Object hashCode() method.
* <p>
* The hashcode is calculated by getting the hashcode of the Bean String
* representation.
* The hashcode is calculated by getting the hashcode of the Bean String representation.
* <p>
* To be used by classes extending EqualsBean. Although it works also for
* classes using EqualsBean in a delegation pattern, for correctness those
* classes should use the
* To be used by classes extending EqualsBean. Although it works also for classes using EqualsBean in a delegation pattern, for correctness those classes
* should use the
*
* @see #beanHashCode() beanHashCode method.
* <p>
@ -198,8 +188,7 @@ public class EqualsBean implements Serializable {
* <p>
* It follows the contract defined by the Object hashCode() method.
* <p>
* The hashcode is calculated by getting the hashcode of the Bean String
* representation.
* The hashcode is calculated by getting the hashcode of the Bean String representation.
* <p>
* To be used by classes using EqualsBean in a delegation pattern,
*

View file

@ -20,21 +20,17 @@ import java.io.Serializable;
import java.util.Set;
/**
* Convenience class providing clone(), toString(), equals() and hashCode()
* functionality for Java Beans.
* Convenience class providing clone(), toString(), equals() and hashCode() functionality for Java Beans.
* <p>
* It works on all read/write properties, recursively.
* <p>
* It uses the CloneableBean, EqualsBean and ToStringBean classes in a
* delegation pattern.
* It uses the CloneableBean, EqualsBean and ToStringBean classes in a delegation pattern.
* <p>
* <h3>ObjectBean programming conventions</h3>
* <P>
* All ObjectBean subclasses having properties that return collections they
* should never return null if the property has been set to <b>null</b> or if a
* collection has not been set. They should create and return an empty
* collection, this empty collection instance should also be set to the
* corresponding property.
* All ObjectBean subclasses having properties that return collections they should never return null if the property has been set to <b>null</b> or if a
* collection has not been set. They should create and return an empty collection, this empty collection instance should also be set to the corresponding
* property.
* <P>
* All ObjectBean subclasses properties should be live references.
* <p>
@ -43,7 +39,9 @@ import java.util.Set;
*
*/
public class ObjectBean implements Serializable, Cloneable {
private static final long serialVersionUID = -8784981605711980095L;
private final EqualsBean equalsBean;
private final ToStringBean toStringBean;
private final CloneableBean cloneableBean;
@ -62,13 +60,9 @@ public class ObjectBean implements Serializable, Cloneable {
/**
* Constructor.
* <p>
* The property names in the ignoreProperties Set will not be copied into
* the cloned instance. This is useful for cases where the Bean has
* convenience properties (properties that are actually references to other
* properties or properties of properties). For example SyndFeed and
* SyndEntry beans have convenience properties, publishedDate, author,
* copyright and categories all of them mapped to properties in the DC
* Module.
* The property names in the ignoreProperties Set will not be copied into the cloned instance. This is useful for cases where the Bean has convenience
* properties (properties that are actually references to other properties or properties of properties). For example SyndFeed and SyndEntry beans have
* convenience properties, publishedDate, author, copyright and categories all of them mapped to properties in the DC Module.
* <p>
*
* @param beanClass the class/interface to be used for property scanning.
@ -86,8 +80,7 @@ public class ObjectBean implements Serializable, Cloneable {
* <p>
*
* @return a clone of the object.
* @throws CloneNotSupportedException thrown if an element of the object
* cannot be cloned.
* @throws CloneNotSupportedException thrown if an element of the object cannot be cloned.
*
*/
@Override
@ -96,8 +89,7 @@ public class ObjectBean implements Serializable, Cloneable {
}
/**
* Indicates whether some other object is "equal to" this one as defined by
* the Object equals() method.
* Indicates whether some other object is "equal to" this one as defined by the Object equals() method.
* <p>
*
* @param other he reference object with which to compare.

View file

@ -22,15 +22,18 @@ import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provides deep <b>Bean</b> toString support.
* <p>
* It works on all read/write properties, recursively. It support all primitive
* types, Strings, Collections, ToString objects and multi-dimensional arrays of
* It works on all read/write properties, recursively. It support all primitive types, Strings, Collections, ToString objects and multi-dimensional arrays of
* any of them.
* <p>
*
@ -38,7 +41,9 @@ import java.util.Stack;
*
*/
public class ToStringBean implements Serializable {
private static final long serialVersionUID = -5850496718959612854L;
private static final Logger LOG = LoggerFactory.getLogger(ToStringBean.class);
private static final ThreadLocal<Stack<String[]>> PREFIX_TL = new ThreadLocal<Stack<String[]>>() {
@Override
@ -63,8 +68,7 @@ public class ToStringBean implements Serializable {
* To be used by classes extending ToStringBean only.
* <p>
*
* @param beanClass indicates the class to scan for properties, normally an
* interface class.
* @param beanClass indicates the class to scan for properties, normally an interface class.
*
*/
protected ToStringBean(final Class<?> beanClass) {
@ -93,8 +97,7 @@ public class ToStringBean implements Serializable {
* </code>
* <p>
*
* @param beanClass indicates the class to scan for properties, normally an
* interface class.
* @param beanClass indicates the class to scan for properties, normally an interface class.
* @param obj object bean to create String representation.
*
*/
@ -141,32 +144,29 @@ public class ToStringBean implements Serializable {
*
*/
private String toString(final String prefix) {
final StringBuffer sb = new StringBuffer(128);
try {
final PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(beanClass);
if (pds != null) {
for (final PropertyDescriptor pd : pds) {
final String pName = pd.getName();
final Method pReadMethod = pd.getReadMethod();
if (pReadMethod != null && // ensure it has a getter method
pReadMethod.getDeclaringClass() != Object.class && // filter
// Object.class
// getter
// methods
pReadMethod.getParameterTypes().length == 0) { // filter
// getter
// methods
// that
// take
// parameters
final Object value = pReadMethod.invoke(obj, NO_PARAMS);
printProperty(sb, prefix + "." + pName, value);
final List<PropertyDescriptor> propertyDescriptors = BeanIntrospector.getPropertyDescriptorsWithGetters(beanClass);
for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) {
final String propertyName = propertyDescriptor.getName();
final Method getter = propertyDescriptor.getReadMethod();
final Object value = getter.invoke(obj, NO_PARAMS);
printProperty(sb, prefix + "." + propertyName, value);
}
} catch (final Exception e) {
LOG.error("Error while generating toString", e);
final Class<? extends Object> clazz = obj.getClass();
final String errorMessage = e.getMessage();
sb.append(String.format("\n\nEXCEPTION: Could not complete %s.toString(): %s\n", clazz, errorMessage));
}
}
} catch (final Exception ex) {
sb.append("\n\nEXCEPTION: Could not complete " + obj.getClass() + ".toString(): " + ex.getMessage() + "\n");
}
return sb.toString();
}