Refactoring some code
This commit is contained in:
parent
25aba9758e
commit
cb03da8c67
6 changed files with 242 additions and 204 deletions
|
@ -24,7 +24,6 @@ import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -32,8 +31,7 @@ import java.util.Set;
|
||||||
/**
|
/**
|
||||||
* Obtains all property descriptors from a bean (interface or implementation).
|
* Obtains all property descriptors from a bean (interface or implementation).
|
||||||
* <p>
|
* <p>
|
||||||
* The java.beans.Introspector does not process the interfaces hierarchy chain,
|
* The java.beans.Introspector does not process the interfaces hierarchy chain, this one does.
|
||||||
* this one does.
|
|
||||||
* <p>
|
* <p>
|
||||||
*
|
*
|
||||||
* @author Alejandro Abdelnur
|
* @author Alejandro Abdelnur
|
||||||
|
@ -42,29 +40,98 @@ import java.util.Set;
|
||||||
public class BeanIntrospector {
|
public class BeanIntrospector {
|
||||||
|
|
||||||
private static final Map<Class<?>, PropertyDescriptor[]> introspected = new HashMap<Class<?>, PropertyDescriptor[]>();
|
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) {
|
if (descriptors == null) {
|
||||||
descriptors = getPDs(klass);
|
descriptors = getPDs(clazz);
|
||||||
introspected.put(klass, descriptors);
|
introspected.put(clazz, descriptors);
|
||||||
}
|
}
|
||||||
return descriptors;
|
return descriptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PropertyDescriptor[] getPDs(final Class<?> klass) throws IntrospectionException {
|
/**
|
||||||
final Method[] methods = klass.getMethods();
|
* Extract all {@link PropertyDescriptor}s for properties with a getter that does not come from {@link Object} and does not accept parameters.
|
||||||
final Map<String, PropertyDescriptor> getters = getPDs(methods, false);
|
*
|
||||||
final Map<String, PropertyDescriptor> setters = getPDs(methods, true);
|
* @param clazz The class to extract the desired {@link PropertyDescriptor}s from
|
||||||
final List<PropertyDescriptor> pds = merge(getters, setters);
|
* @return All {@link PropertyDescriptor}s for properties with a getter that does not come from {@link Object} and does not accept parameters.
|
||||||
final PropertyDescriptor[] array = new PropertyDescriptor[pds.size()];
|
* @throws IntrospectionException When the extraction of the desired {@link PropertyDescriptor}s failed
|
||||||
pds.toArray(array);
|
*/
|
||||||
return array;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return relevantDescriptors;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String SETTER = "set";
|
/**
|
||||||
private static final String GETTER = "get";
|
* Extract all {@link PropertyDescriptor}s for properties with a getter (that does not come from {@link Object} and does not accept parameters) and a
|
||||||
private static final String BOOLEAN_GETTER = "is";
|
* 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 {
|
private static Map<String, PropertyDescriptor> getPDs(final Method[] methods, final boolean setters) throws IntrospectionException {
|
||||||
final Map<String, PropertyDescriptor> pds = new HashMap<String, PropertyDescriptor>();
|
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)
|
private static List<PropertyDescriptor> merge(final Map<String, PropertyDescriptor> getters, final Map<String, PropertyDescriptor> setters)
|
||||||
throws IntrospectionException {
|
throws IntrospectionException {
|
||||||
|
|
||||||
final List<PropertyDescriptor> props = new ArrayList<PropertyDescriptor>();
|
final List<PropertyDescriptor> props = new ArrayList<PropertyDescriptor>();
|
||||||
final Set<String> processedProps = new HashSet<String>();
|
final Set<String> processedProps = new HashSet<String>();
|
||||||
final Iterator<String> gs = getters.keySet().iterator();
|
|
||||||
while (gs.hasNext()) {
|
for (final String propertyName : getters.keySet()) {
|
||||||
final String name = gs.next();
|
final PropertyDescriptor getter = getters.get(propertyName);
|
||||||
final PropertyDescriptor getter = getters.get(name);
|
final PropertyDescriptor setter = setters.get(propertyName);
|
||||||
final PropertyDescriptor setter = setters.get(name);
|
|
||||||
if (setter != null) {
|
if (setter != null) {
|
||||||
processedProps.add(name);
|
processedProps.add(propertyName);
|
||||||
final PropertyDescriptor prop = new PropertyDescriptor(name, getter.getReadMethod(), setter.getWriteMethod());
|
final PropertyDescriptor prop = new PropertyDescriptor(propertyName, getter.getReadMethod(), setter.getWriteMethod());
|
||||||
props.add(prop);
|
props.add(prop);
|
||||||
} else {
|
} else {
|
||||||
props.add(getter);
|
props.add(getter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final Set<String> writeOnlyProps = new HashSet<String>(setters.keySet());
|
|
||||||
writeOnlyProps.removeAll(processedProps);
|
final Set<String> writeOnlyProperties = new HashSet<String>();
|
||||||
final Iterator<String> ss = writeOnlyProps.iterator();
|
writeOnlyProperties.removeAll(processedProps);
|
||||||
while (ss.hasNext()) {
|
|
||||||
final String name = ss.next();
|
for (final String propertyName : writeOnlyProperties) {
|
||||||
final PropertyDescriptor setter = setters.get(name);
|
final PropertyDescriptor setter = setters.get(propertyName);
|
||||||
props.add(setter);
|
props.add(setter);
|
||||||
}
|
}
|
||||||
|
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -162,34 +163,26 @@ public class CloneableBean implements Serializable, Cloneable {
|
||||||
|
|
||||||
final Object clonedBean = clazz.newInstance();
|
final Object clonedBean = clazz.newInstance();
|
||||||
|
|
||||||
final PropertyDescriptor[] propertyDescriptors = BeanIntrospector.getPropertyDescriptors(clazz);
|
final List<PropertyDescriptor> propertyDescriptors = BeanIntrospector.getPropertyDescriptorsWithGettersAndSetters(clazz);
|
||||||
if (propertyDescriptors != null) {
|
for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) {
|
||||||
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 getter = propertyDescriptor.getReadMethod();
|
||||||
final Method setter = propertyDescriptor.getWriteMethod();
|
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 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Object value = getter.invoke(obj, NO_PARAMS);
|
||||||
|
if (value != null) {
|
||||||
|
value = doClone(value);
|
||||||
|
setter.invoke(clonedBean, new Object[] { value });
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return clonedBean;
|
return clonedBean;
|
||||||
|
|
|
@ -26,22 +26,43 @@ import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.sun.syndication.feed.CopyFrom;
|
import com.sun.syndication.feed.CopyFrom;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Alejandro Abdelnur
|
* @author Alejandro Abdelnur
|
||||||
*/
|
*/
|
||||||
public class CopyFromHelper {
|
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 static final Object[] NO_PARAMS = new Object[0];
|
||||||
|
|
||||||
private final Class<? extends CopyFrom<?>> beanInterfaceClass;
|
private final Class<? extends CopyFrom<?>> beanInterfaceClass;
|
||||||
private final Map<String, Class<?>> baseInterfaceMap; // ENTRIES(propertyName,interface.class)
|
private final Map<String, Class<?>> baseInterfaceMap; // ENTRIES(propertyName,interface.class)
|
||||||
private final Map<Class<? extends CopyFrom<?>>, Class<?>> baseImplMap; // ENTRIES(interface.class,implementation.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,
|
public CopyFromHelper(final Class<? extends CopyFrom<?>> beanInterfaceClass, final Map<String, Class<?>> basePropInterfaceMap,
|
||||||
final Map<Class<? extends CopyFrom<?>>, Class<?>> basePropClassImplMap) {
|
final Map<Class<? extends CopyFrom<?>>, Class<?>> basePropClassImplMap) {
|
||||||
this.beanInterfaceClass = beanInterfaceClass;
|
this.beanInterfaceClass = beanInterfaceClass;
|
||||||
|
@ -50,47 +71,37 @@ public class CopyFromHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void copy(final Object target, final Object source) {
|
public void copy(final Object target, final Object source) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(beanInterfaceClass);
|
|
||||||
if (pds != null) {
|
final List<PropertyDescriptor> propertyDescriptors = BeanIntrospector.getPropertyDescriptorsWithGettersAndSetters(beanInterfaceClass);
|
||||||
for (final PropertyDescriptor pd : pds) {
|
for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) {
|
||||||
final String propertyName = pd.getName();
|
|
||||||
final Method pReadMethod = pd.getReadMethod();
|
final String propertyName = propertyDescriptor.getName();
|
||||||
final Method pWriteMethod = pd.getWriteMethod();
|
|
||||||
if (pReadMethod != null && pWriteMethod != null && // ensure
|
if (baseInterfaceMap.containsKey(propertyName)) {
|
||||||
// it has
|
|
||||||
// getter
|
final Method getter = propertyDescriptor.getReadMethod();
|
||||||
// and
|
|
||||||
// setter
|
// only copies properties defined as copyFrom-able
|
||||||
// methods
|
Object value = getter.invoke(source, NO_PARAMS);
|
||||||
pReadMethod.getDeclaringClass() != Object.class && // filter
|
if (value != null) {
|
||||||
// Object.class
|
|
||||||
// getter
|
final Method setter = propertyDescriptor.getWriteMethod();
|
||||||
// methods
|
|
||||||
pReadMethod.getParameterTypes().length == 0 && // filter
|
final Class<?> baseInterface = baseInterfaceMap.get(propertyName);
|
||||||
// getter
|
value = doCopy(value, baseInterface);
|
||||||
// methods
|
setter.invoke(target, new Object[] { value });
|
||||||
// that
|
|
||||||
// take
|
|
||||||
// parameters
|
|
||||||
baseInterfaceMap.containsKey(propertyName)) { // only
|
|
||||||
// copies
|
|
||||||
// properties
|
|
||||||
// defined
|
|
||||||
// as
|
|
||||||
// copyFrom-able
|
|
||||||
Object value = pReadMethod.invoke(source, NO_PARAMS);
|
|
||||||
if (value != null) {
|
|
||||||
final Class<?> baseInterface = baseInterfaceMap.get(propertyName);
|
|
||||||
value = doCopy(value, baseInterface);
|
|
||||||
pWriteMethod.invoke(target, new Object[] { value });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (final Exception ex) {
|
|
||||||
throw new RuntimeException("Could not do a copyFrom " + ex, ex);
|
} catch (final Exception e) {
|
||||||
|
LOG.error("Error while copying object", e);
|
||||||
|
throw new RuntimeException("Could not do a copyFrom " + e, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CopyFrom<?> createInstance(final Class<? extends CopyFrom<?>> interfaceClass) throws Exception {
|
private CopyFrom<?> createInstance(final Class<? extends CopyFrom<?>> interfaceClass) throws Exception {
|
||||||
|
@ -170,21 +181,6 @@ public class CopyFromHelper {
|
||||||
return newMap;
|
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) {
|
private boolean isBasicType(final Class<?> vClass) {
|
||||||
return BASIC_TYPES.contains(vClass);
|
return BASIC_TYPES.contains(vClass);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,23 +20,22 @@ import java.beans.PropertyDescriptor;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides deep <b>Bean</b> equals() and hashCode() functionality for Java
|
* Provides deep <b>Bean</b> equals() and hashCode() functionality for Java Beans.
|
||||||
* Beans.
|
|
||||||
* <p>
|
* <p>
|
||||||
* It works on all read/write properties, recursively. It support all primitive
|
* It works on all read/write properties, recursively. It support all primitive types, Strings, Collections, bean-like objects and multi-dimensional arrays of
|
||||||
* types, Strings, Collections, bean-like objects and multi-dimensional arrays
|
* any of them.
|
||||||
* of any of them.
|
|
||||||
* <p>
|
* <p>
|
||||||
* The hashcode is calculated by getting the hashcode of the Bean String
|
* The hashcode is calculated by getting the hashcode of the Bean String representation.
|
||||||
* representation.
|
|
||||||
* <p>
|
* <p>
|
||||||
*
|
*
|
||||||
* @author Alejandro Abdelnur
|
* @author Alejandro Abdelnur
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class EqualsBean implements Serializable {
|
public class EqualsBean implements Serializable {
|
||||||
|
|
||||||
private static final long serialVersionUID = 9120107899175152601L;
|
private static final long serialVersionUID = 9120107899175152601L;
|
||||||
|
|
||||||
private static final Object[] NO_PARAMS = new Object[0];
|
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
|
* Indicates whether some other object is "equal to" this object as defined by the Object equals() method.
|
||||||
* by the Object equals() method.
|
|
||||||
* <p>
|
* <p>
|
||||||
* To be used by classes extending EqualsBean. Although it works also for
|
* To be used by classes extending EqualsBean. Although it works also for classes using EqualsBean in a delegation pattern, for correctness those classes
|
||||||
* classes using EqualsBean in a delegation pattern, for correctness those
|
* should use the
|
||||||
* classes should use the
|
|
||||||
*
|
*
|
||||||
* @see #beanEquals(Object) beanEquals method.
|
* @see #beanEquals(Object) beanEquals method.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -115,58 +112,53 @@ public class EqualsBean implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether some other object is "equal to" the object passed in
|
* Indicates whether some other object is "equal to" the object passed in the constructor, as defined by the Object equals() method.
|
||||||
* the constructor, as defined by the Object equals() method.
|
|
||||||
* <p>
|
* <p>
|
||||||
* To be used by classes using EqualsBean in a delegation pattern,
|
* To be used by classes using EqualsBean in a delegation pattern,
|
||||||
*
|
*
|
||||||
* @see #EqualsBean(Class,Object) constructor.
|
* @see #EqualsBean(Class,Object) constructor.
|
||||||
* <p>
|
* <p>
|
||||||
* @param obj he reference object with which to compare.
|
* @param obj he reference object with which to compare.
|
||||||
* @return <b>true</b> if the object passed in the constructor is equal to
|
* @return <b>true</b> if the object passed in the constructor is equal to the 'obj' object.
|
||||||
* the 'obj' object.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public boolean beanEquals(final Object obj) {
|
public boolean beanEquals(final Object obj) {
|
||||||
|
|
||||||
final Object bean1 = this.obj;
|
final Object bean1 = this.obj;
|
||||||
final Object bean2 = obj;
|
final Object bean2 = obj;
|
||||||
|
|
||||||
boolean eq;
|
boolean eq;
|
||||||
if (bean1 == null && bean2 == null) {
|
|
||||||
|
if (bean1 == null && bean2 == null) { // both are null
|
||||||
eq = true;
|
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)) { // not of the same type
|
||||||
eq = false;
|
eq = false;
|
||||||
} else {
|
} else {
|
||||||
if (!beanClass.isInstance(bean2)) {
|
eq = true;
|
||||||
eq = false;
|
try {
|
||||||
} else {
|
|
||||||
eq = true;
|
final List<PropertyDescriptor> propertyDescriptors = BeanIntrospector.getPropertyDescriptorsWithGetters(beanClass);
|
||||||
try {
|
for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) {
|
||||||
final PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(beanClass);
|
|
||||||
if (pds != null) {
|
final Method getter = propertyDescriptor.getReadMethod();
|
||||||
for (int i = 0; eq && i < pds.length; i++) {
|
|
||||||
final Method pReadMethod = pds[i].getReadMethod();
|
final Object value1 = getter.invoke(bean1, NO_PARAMS);
|
||||||
if (pReadMethod != null && // ensure it has a getter
|
final Object value2 = getter.invoke(bean2, NO_PARAMS);
|
||||||
// method
|
|
||||||
pReadMethod.getDeclaringClass() != Object.class && // filter
|
eq = doEquals(value1, value2);
|
||||||
// Object.class
|
|
||||||
// getter
|
if (!eq) {
|
||||||
// methods
|
break;
|
||||||
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);
|
|
||||||
eq = doEquals(value1, value2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (final Exception ex) {
|
|
||||||
throw new RuntimeException("Could not execute equals()", ex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} catch (final Exception ex) {
|
||||||
|
throw new RuntimeException("Could not execute equals()", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return eq;
|
return eq;
|
||||||
}
|
}
|
||||||
|
@ -176,12 +168,10 @@ public class EqualsBean implements Serializable {
|
||||||
* <p>
|
* <p>
|
||||||
* It follows the contract defined by the Object hashCode() method.
|
* It follows the contract defined by the Object hashCode() method.
|
||||||
* <p>
|
* <p>
|
||||||
* The hashcode is calculated by getting the hashcode of the Bean String
|
* The hashcode is calculated by getting the hashcode of the Bean String representation.
|
||||||
* representation.
|
|
||||||
* <p>
|
* <p>
|
||||||
* To be used by classes extending EqualsBean. Although it works also for
|
* To be used by classes extending EqualsBean. Although it works also for classes using EqualsBean in a delegation pattern, for correctness those classes
|
||||||
* classes using EqualsBean in a delegation pattern, for correctness those
|
* should use the
|
||||||
* classes should use the
|
|
||||||
*
|
*
|
||||||
* @see #beanHashCode() beanHashCode method.
|
* @see #beanHashCode() beanHashCode method.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -198,8 +188,7 @@ public class EqualsBean implements Serializable {
|
||||||
* <p>
|
* <p>
|
||||||
* It follows the contract defined by the Object hashCode() method.
|
* It follows the contract defined by the Object hashCode() method.
|
||||||
* <p>
|
* <p>
|
||||||
* The hashcode is calculated by getting the hashcode of the Bean String
|
* The hashcode is calculated by getting the hashcode of the Bean String representation.
|
||||||
* representation.
|
|
||||||
* <p>
|
* <p>
|
||||||
* To be used by classes using EqualsBean in a delegation pattern,
|
* To be used by classes using EqualsBean in a delegation pattern,
|
||||||
*
|
*
|
||||||
|
|
|
@ -20,21 +20,17 @@ import java.io.Serializable;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience class providing clone(), toString(), equals() and hashCode()
|
* Convenience class providing clone(), toString(), equals() and hashCode() functionality for Java Beans.
|
||||||
* functionality for Java Beans.
|
|
||||||
* <p>
|
* <p>
|
||||||
* It works on all read/write properties, recursively.
|
* It works on all read/write properties, recursively.
|
||||||
* <p>
|
* <p>
|
||||||
* It uses the CloneableBean, EqualsBean and ToStringBean classes in a
|
* It uses the CloneableBean, EqualsBean and ToStringBean classes in a delegation pattern.
|
||||||
* delegation pattern.
|
|
||||||
* <p>
|
* <p>
|
||||||
* <h3>ObjectBean programming conventions</h3>
|
* <h3>ObjectBean programming conventions</h3>
|
||||||
* <P>
|
* <P>
|
||||||
* All ObjectBean subclasses having properties that return collections they
|
* 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
|
||||||
* 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
|
||||||
* collection has not been set. They should create and return an empty
|
* property.
|
||||||
* collection, this empty collection instance should also be set to the
|
|
||||||
* corresponding property.
|
|
||||||
* <P>
|
* <P>
|
||||||
* All ObjectBean subclasses properties should be live references.
|
* All ObjectBean subclasses properties should be live references.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -43,7 +39,9 @@ import java.util.Set;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class ObjectBean implements Serializable, Cloneable {
|
public class ObjectBean implements Serializable, Cloneable {
|
||||||
|
|
||||||
private static final long serialVersionUID = -8784981605711980095L;
|
private static final long serialVersionUID = -8784981605711980095L;
|
||||||
|
|
||||||
private final EqualsBean equalsBean;
|
private final EqualsBean equalsBean;
|
||||||
private final ToStringBean toStringBean;
|
private final ToStringBean toStringBean;
|
||||||
private final CloneableBean cloneableBean;
|
private final CloneableBean cloneableBean;
|
||||||
|
@ -62,13 +60,9 @@ public class ObjectBean implements Serializable, Cloneable {
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
* <p>
|
* <p>
|
||||||
* The property names in the ignoreProperties Set will not be copied into
|
* 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
|
||||||
* the cloned instance. This is useful for cases where the Bean has
|
* properties (properties that are actually references to other properties or properties of properties). For example SyndFeed and SyndEntry beans have
|
||||||
* convenience properties (properties that are actually references to other
|
* convenience properties, publishedDate, author, copyright and categories all of them mapped to properties in the DC Module.
|
||||||
* 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>
|
* <p>
|
||||||
*
|
*
|
||||||
* @param beanClass the class/interface to be used for property scanning.
|
* @param beanClass the class/interface to be used for property scanning.
|
||||||
|
@ -86,8 +80,7 @@ public class ObjectBean implements Serializable, Cloneable {
|
||||||
* <p>
|
* <p>
|
||||||
*
|
*
|
||||||
* @return a clone of the object.
|
* @return a clone of the object.
|
||||||
* @throws CloneNotSupportedException thrown if an element of the object
|
* @throws CloneNotSupportedException thrown if an element of the object cannot be cloned.
|
||||||
* cannot be cloned.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -96,8 +89,7 @@ public class ObjectBean implements Serializable, Cloneable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether some other object is "equal to" this one as defined by
|
* Indicates whether some other object is "equal to" this one as defined by the Object equals() method.
|
||||||
* the Object equals() method.
|
|
||||||
* <p>
|
* <p>
|
||||||
*
|
*
|
||||||
* @param other he reference object with which to compare.
|
* @param other he reference object with which to compare.
|
||||||
|
|
|
@ -22,15 +22,18 @@ import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides deep <b>Bean</b> toString support.
|
* Provides deep <b>Bean</b> toString support.
|
||||||
* <p>
|
* <p>
|
||||||
* It works on all read/write properties, recursively. It support all primitive
|
* It works on all read/write properties, recursively. It support all primitive types, Strings, Collections, ToString objects and multi-dimensional arrays of
|
||||||
* types, Strings, Collections, ToString objects and multi-dimensional arrays of
|
|
||||||
* any of them.
|
* any of them.
|
||||||
* <p>
|
* <p>
|
||||||
*
|
*
|
||||||
|
@ -38,7 +41,9 @@ import java.util.Stack;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class ToStringBean implements Serializable {
|
public class ToStringBean implements Serializable {
|
||||||
|
|
||||||
private static final long serialVersionUID = -5850496718959612854L;
|
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[]>>() {
|
private static final ThreadLocal<Stack<String[]>> PREFIX_TL = new ThreadLocal<Stack<String[]>>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -63,8 +68,7 @@ public class ToStringBean implements Serializable {
|
||||||
* To be used by classes extending ToStringBean only.
|
* To be used by classes extending ToStringBean only.
|
||||||
* <p>
|
* <p>
|
||||||
*
|
*
|
||||||
* @param beanClass indicates the class to scan for properties, normally an
|
* @param beanClass indicates the class to scan for properties, normally an interface class.
|
||||||
* interface class.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
protected ToStringBean(final Class<?> beanClass) {
|
protected ToStringBean(final Class<?> beanClass) {
|
||||||
|
@ -93,8 +97,7 @@ public class ToStringBean implements Serializable {
|
||||||
* </code>
|
* </code>
|
||||||
* <p>
|
* <p>
|
||||||
*
|
*
|
||||||
* @param beanClass indicates the class to scan for properties, normally an
|
* @param beanClass indicates the class to scan for properties, normally an interface class.
|
||||||
* interface class.
|
|
||||||
* @param obj object bean to create String representation.
|
* @param obj object bean to create String representation.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -141,32 +144,29 @@ public class ToStringBean implements Serializable {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private String toString(final String prefix) {
|
private String toString(final String prefix) {
|
||||||
|
|
||||||
final StringBuffer sb = new StringBuffer(128);
|
final StringBuffer sb = new StringBuffer(128);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(beanClass);
|
|
||||||
if (pds != null) {
|
final List<PropertyDescriptor> propertyDescriptors = BeanIntrospector.getPropertyDescriptorsWithGetters(beanClass);
|
||||||
for (final PropertyDescriptor pd : pds) {
|
for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) {
|
||||||
final String pName = pd.getName();
|
|
||||||
final Method pReadMethod = pd.getReadMethod();
|
final String propertyName = propertyDescriptor.getName();
|
||||||
if (pReadMethod != null && // ensure it has a getter method
|
final Method getter = propertyDescriptor.getReadMethod();
|
||||||
pReadMethod.getDeclaringClass() != Object.class && // filter
|
|
||||||
// Object.class
|
final Object value = getter.invoke(obj, NO_PARAMS);
|
||||||
// getter
|
printProperty(sb, prefix + "." + propertyName, value);
|
||||||
// methods
|
|
||||||
pReadMethod.getParameterTypes().length == 0) { // filter
|
|
||||||
// getter
|
|
||||||
// methods
|
|
||||||
// that
|
|
||||||
// take
|
|
||||||
// parameters
|
|
||||||
final Object value = pReadMethod.invoke(obj, NO_PARAMS);
|
|
||||||
printProperty(sb, prefix + "." + pName, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (final Exception ex) {
|
|
||||||
sb.append("\n\nEXCEPTION: Could not complete " + obj.getClass() + ".toString(): " + ex.getMessage() + "\n");
|
} 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue