diff --git a/src/main/java/com/sun/syndication/feed/impl/BeanIntrospector.java b/src/main/java/com/sun/syndication/feed/impl/BeanIntrospector.java index 972ac86..ba22272 100644 --- a/src/main/java/com/sun/syndication/feed/impl/BeanIntrospector.java +++ b/src/main/java/com/sun/syndication/feed/impl/BeanIntrospector.java @@ -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). *

- * 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. *

* * @author Alejandro Abdelnur @@ -42,29 +40,98 @@ import java.util.Set; public class BeanIntrospector { private static final Map, PropertyDescriptor[]> introspected = new HashMap, 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 getters = getPDs(methods, false); - final Map setters = getPDs(methods, true); - final List 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 getPropertyDescriptorsWithGetters(final Class clazz) throws IntrospectionException { + + final List relevantDescriptors = new ArrayList(); + + 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"; - private static final String BOOLEAN_GETTER = "is"; + /** + * 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 getPropertyDescriptorsWithGettersAndSetters(final Class clazz) throws IntrospectionException { + + final List relevantDescriptors = new ArrayList(); + + final List 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 getters = getPDs(methods, false); + final Map setters = getPDs(methods, true); + final List propertyDescriptors = merge(getters, setters); + return propertyDescriptors.toArray(new PropertyDescriptor[propertyDescriptors.size()]); + } private static Map getPDs(final Method[] methods, final boolean setters) throws IntrospectionException { final Map pds = new HashMap(); @@ -96,29 +163,30 @@ public class BeanIntrospector { private static List merge(final Map getters, final Map setters) throws IntrospectionException { + final List props = new ArrayList(); final Set processedProps = new HashSet(); - final Iterator 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 writeOnlyProps = new HashSet(setters.keySet()); - writeOnlyProps.removeAll(processedProps); - final Iterator ss = writeOnlyProps.iterator(); - while (ss.hasNext()) { - final String name = ss.next(); - final PropertyDescriptor setter = setters.get(name); + + final Set writeOnlyProperties = new HashSet(); + writeOnlyProperties.removeAll(processedProps); + + for (final String propertyName : writeOnlyProperties) { + final PropertyDescriptor setter = setters.get(propertyName); props.add(setter); } + return props; } diff --git a/src/main/java/com/sun/syndication/feed/impl/CloneableBean.java b/src/main/java/com/sun/syndication/feed/impl/CloneableBean.java index 3c4aa4d..9ac59ba 100644 --- a/src/main/java/com/sun/syndication/feed/impl/CloneableBean.java +++ b/src/main/java/com/sun/syndication/feed/impl/CloneableBean.java @@ -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,34 +163,26 @@ public class CloneableBean implements Serializable, Cloneable { final Object clonedBean = clazz.newInstance(); - final PropertyDescriptor[] propertyDescriptors = BeanIntrospector.getPropertyDescriptors(clazz); - if (propertyDescriptors != null) { - for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) { + final List 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 }); - } - } + Object value = getter.invoke(obj, NO_PARAMS); + if (value != null) { + value = doClone(value); + setter.invoke(clonedBean, new Object[] { value }); } } + } return clonedBean; diff --git a/src/main/java/com/sun/syndication/feed/impl/CopyFromHelper.java b/src/main/java/com/sun/syndication/feed/impl/CopyFromHelper.java index 76136be..668cde0 100644 --- a/src/main/java/com/sun/syndication/feed/impl/CopyFromHelper.java +++ b/src/main/java/com/sun/syndication/feed/impl/CopyFromHelper.java @@ -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> BASIC_TYPES = new HashSet>(); private static final Object[] NO_PARAMS = new Object[0]; private final Class> beanInterfaceClass; private final Map> baseInterfaceMap; // ENTRIES(propertyName,interface.class) private final Map>, 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> beanInterfaceClass, final Map> basePropInterfaceMap, final Map>, 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); - if (value != null) { - final Class baseInterface = baseInterfaceMap.get(propertyName); - value = doCopy(value, baseInterface); - pWriteMethod.invoke(target, new Object[] { value }); - } + + final List 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); + setter.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> interfaceClass) throws Exception { @@ -170,21 +181,6 @@ public class CopyFromHelper { return newMap; } - private static final Set> BASIC_TYPES = new HashSet>(); - - 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); } diff --git a/src/main/java/com/sun/syndication/feed/impl/EqualsBean.java b/src/main/java/com/sun/syndication/feed/impl/EqualsBean.java index 237326b..0b6a077 100644 --- a/src/main/java/com/sun/syndication/feed/impl/EqualsBean.java +++ b/src/main/java/com/sun/syndication/feed/impl/EqualsBean.java @@ -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 Bean equals() and hashCode() functionality for Java - * Beans. + * Provides deep Bean equals() and hashCode() functionality for Java Beans. *

- * 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. *

- * 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. *

* * @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. *

- * 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. *

@@ -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. *

* To be used by classes using EqualsBean in a delegation pattern, * * @see #EqualsBean(Class,Object) constructor. *

* @param obj he reference object with which to compare. - * @return true if the object passed in the constructor is equal to - * the 'obj' object. + * @return true 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)) { // not of the same type eq = false; } else { - if (!beanClass.isInstance(bean2)) { - 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); - eq = doEquals(value1, value2); - } - } + eq = true; + try { + + final List 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); + } + + } catch (final Exception ex) { + throw new RuntimeException("Could not execute equals()", ex); } + } return eq; } @@ -176,12 +168,10 @@ public class EqualsBean implements Serializable { *

* It follows the contract defined by the Object hashCode() method. *

- * 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. *

- * 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. *

@@ -198,8 +188,7 @@ public class EqualsBean implements Serializable { *

* It follows the contract defined by the Object hashCode() method. *

- * 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. *

* To be used by classes using EqualsBean in a delegation pattern, * diff --git a/src/main/java/com/sun/syndication/feed/impl/ObjectBean.java b/src/main/java/com/sun/syndication/feed/impl/ObjectBean.java index ff5bc29..bfb4c43 100644 --- a/src/main/java/com/sun/syndication/feed/impl/ObjectBean.java +++ b/src/main/java/com/sun/syndication/feed/impl/ObjectBean.java @@ -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. *

* It works on all read/write properties, recursively. *

- * It uses the CloneableBean, EqualsBean and ToStringBean classes in a - * delegation pattern. + * It uses the CloneableBean, EqualsBean and ToStringBean classes in a delegation pattern. *

*

ObjectBean programming conventions

*

- * All ObjectBean subclasses having properties that return collections they - * should never return null if the property has been set to null 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 null 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 properties should be live references. *

@@ -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. *

- * 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. *

* * @param beanClass the class/interface to be used for property scanning. @@ -86,8 +80,7 @@ public class ObjectBean implements Serializable, Cloneable { *

* * @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. *

* * @param other he reference object with which to compare. diff --git a/src/main/java/com/sun/syndication/feed/impl/ToStringBean.java b/src/main/java/com/sun/syndication/feed/impl/ToStringBean.java index 3dad8e8..2d99cb1 100644 --- a/src/main/java/com/sun/syndication/feed/impl/ToStringBean.java +++ b/src/main/java/com/sun/syndication/feed/impl/ToStringBean.java @@ -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 Bean toString support. *

- * 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. *

* @@ -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> PREFIX_TL = new ThreadLocal>() { @Override @@ -63,8 +68,7 @@ public class ToStringBean implements Serializable { * To be used by classes extending ToStringBean only. *

* - * @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 { * *

* - * @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 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 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 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(); }