commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ggreg...@apache.org
Subject svn commit: r447989 - in /jakarta/commons/proper/lang/trunk/src: java/org/apache/commons/lang/builder/HashCodeBuilder.java test/org/apache/commons/lang/builder/HashCodeBuilderTest.java
Date Tue, 19 Sep 2006 21:58:12 GMT
Author: ggregory
Date: Tue Sep 19 14:58:11 2006
New Revision: 447989

URL: http://svn.apache.org/viewvc?view=rev&rev=447989
Log:
HashCodeBuilder throws java.lang.StackOverflowError when an object contains a cycle.
https://issues.apache.org/jira/browse/LANG-279

Modified:
    jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/builder/HashCodeBuilder.java
    jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/builder/HashCodeBuilderTest.java

Modified: jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/builder/HashCodeBuilder.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/builder/HashCodeBuilder.java?view=diff&rev=447989&r1=447988&r2=447989
==============================================================================
--- jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/builder/HashCodeBuilder.java (original)
+++ jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/builder/HashCodeBuilder.java Tue Sep 19 14:58:11 2006
@@ -14,31 +14,40 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.commons.lang.builder;
 
 import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
- * <p>Assists in implementing {@link Object#hashCode()} methods.</p>
- *
- * <p> This class enables a good <code>hashCode</code> method to be built for any class. It
- * follows the rules laid out in the book
- * <a href="http://java.sun.com/docs/books/effective/index.html">Effective Java</a>
- * by Joshua Bloch. Writing a good <code>hashCode</code> method is actually quite
- * difficult. This class aims to simplify the process.</p>
- *
- * <p>All relevant fields from the object should be included in the
- * <code>hashCode</code> method. Derived fields may be excluded. In general, any
- * field used in the <code>equals</code> method must be used in the <code>hashCode</code>
- * method.</p>
- *
- * <p>To use this class write code as follows:</p>
+ * <p>
+ * Assists in implementing {@link Object#hashCode()} methods.
+ * </p>
+ * 
+ * <p>
+ * This class enables a good <code>hashCode</code> method to be built for any class. It follows the rules laid out in
+ * the book <a href="http://java.sun.com/docs/books/effective/index.html">Effective Java</a> by Joshua Bloch. Writing a
+ * good <code>hashCode</code> method is actually quite difficult. This class aims to simplify the process.
+ * </p>
+ * 
+ * <p>
+ * All relevant fields from the object should be included in the <code>hashCode</code> method. Derived fields may be
+ * excluded. In general, any field used in the <code>equals</code> method must be used in the <code>hashCode</code>
+ * method.
+ * </p>
+ * 
+ * <p>
+ * To use this class write code as follows:
+ * </p>
+ * 
  * <pre>
  * public class Person {
  *   String name;
@@ -57,24 +66,28 @@
  *   }
  * }
  * </pre>
- *
- * <p>If required, the superclass <code>hashCode()</code> can be added
- * using {@link #appendSuper}.</p>
- *
- * <p>Alternatively, there is a method that uses reflection to determine
- * the fields to test. Because these fields are usually private, the method,
- * <code>reflectionHashCode</code>, uses <code>AccessibleObject.setAccessible</code> to
- * change the visibility of the fields. This will fail under a security manager,
- * unless the appropriate permissions are set up correctly. It is also slower
- * than testing explicitly.</p>
- *
- * <p>A typical invocation for this method would look like:</p>
+ * 
+ * <p>
+ * If required, the superclass <code>hashCode()</code> can be added using {@link #appendSuper}.
+ * </p>
+ * 
+ * <p>
+ * Alternatively, there is a method that uses reflection to determine the fields to test. Because these fields are
+ * usually private, the method, <code>reflectionHashCode</code>, uses <code>AccessibleObject.setAccessible</code>
+ * to change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions
+ * are set up correctly. It is also slower than testing explicitly.
+ * </p>
+ * 
+ * <p>
+ * A typical invocation for this method would look like:
+ * </p>
+ * 
  * <pre>
  * public int hashCode() {
  *   return HashCodeBuilder.reflectionHashCode(this);
  * }
  * </pre>
- *
+ * 
  * @author Stephen Colebourne
  * @author Gary Gregory
  * @author Pete Gieser
@@ -82,410 +95,586 @@
  * @version $Id$
  */
 public class HashCodeBuilder {
-
     /**
-     * Constant to use in building the hashCode.
+     * <p>
+     * A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops.
+     * </p>
+     * 
+     * @since 2.3
      */
-    private final int iConstant;
+    private static ThreadLocal registry = new ThreadLocal() {
+        protected synchronized Object initialValue() {
+            // The HashSet implementation is not synchronized,
+            // which is just what we need here.
+            return new HashSet();
+        }
+    };
+
     /**
-     * Running total of the hashCode.
+     * <p>
+     * Returns the registry of objects being traversed by the reflection methods in the current thread.
+     * </p>
+     * 
+     * @return Set the registry of objects being traversed
+     * @since 2.3
      */
-    private int iTotal = 0;
+    static Set getRegistry() {
+        return (Set) registry.get();
+    }
 
     /**
-     * <p>Uses two hard coded choices for the constants
-     * needed to build a <code>hashCode</code>.</p>
+     * <p>
+     * Returns <code>true</code> if the registry contains the given object. Used by the reflection methods to avoid
+     * infinite loops.
+     * </p>
+     * 
+     * @param value
+     *            The object to lookup in the registry.
+     * @return boolean <code>true</code> if the registry contains the given object.
+     * @since 2.3
      */
-    public HashCodeBuilder() {
-        iConstant = 37;
-        iTotal = 17;
+    static boolean isRegistered(Object value) {
+        return getRegistry().contains(toIdentityHashCodeInteger(value));
     }
 
     /**
-     * <p>Two randomly chosen, non-zero, odd numbers must be passed in.
-     * Ideally these should be different for each class, however this is
-     * not vital.</p>
-     *
-     * <p>Prime numbers are preferred, especially for the multiplier.</p>
-     *
-     * @param initialNonZeroOddNumber  a non-zero, odd number used as the initial value
-     * @param multiplierNonZeroOddNumber  a non-zero, odd number used as the multiplier
-     * @throws IllegalArgumentException if the number is zero or even
+     * <p>
+     * Appends the fields and values defined by the given object of the given <code>Class</code>.
+     * </p>
+     * 
+     * @param object
+     *            the object to append details of
+     * @param clazz
+     *            the class to append details of
+     * @param builder
+     *            the builder to append to
+     * @param useTransients
+     *            whether to use transient fields
+     * @param excludeFields
+     *            Collection of String field names to exclude from use in calculation of hash code
      */
-    public HashCodeBuilder(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber) {
-        if (initialNonZeroOddNumber == 0) {
-            throw new IllegalArgumentException("HashCodeBuilder requires a non zero initial value");
-        }
-        if (initialNonZeroOddNumber % 2 == 0) {
-            throw new IllegalArgumentException("HashCodeBuilder requires an odd initial value");
-        }
-        if (multiplierNonZeroOddNumber == 0) {
-            throw new IllegalArgumentException("HashCodeBuilder requires a non zero multiplier");
+    private static void reflectionAppend(Object object, Class clazz, HashCodeBuilder builder, boolean useTransients,
+            String[] excludeFields) {
+        if (isRegistered(object)) {
+            return;
         }
-        if (multiplierNonZeroOddNumber % 2 == 0) {
-            throw new IllegalArgumentException("HashCodeBuilder requires an odd multiplier");
+        try {
+            register(object);
+            Field[] fields = clazz.getDeclaredFields();
+            List excludedFieldList = excludeFields != null ? Arrays.asList(excludeFields) : Collections.EMPTY_LIST;
+            AccessibleObject.setAccessible(fields, true);
+            for (int i = 0; i < fields.length; i++) {
+                Field field = fields[i];
+                if (!excludedFieldList.contains(field.getName())
+                    && (field.getName().indexOf('$') == -1)
+                    && (useTransients || !Modifier.isTransient(field.getModifiers()))
+                    && (!Modifier.isStatic(field.getModifiers()))) {
+                    try {
+                        Object fieldValue = field.get(object);
+                        builder.append(fieldValue);
+                    } catch (IllegalAccessException e) {
+                        // this can't happen. Would get a Security exception instead
+                        // throw a runtime exception in case the impossible happens.
+                        throw new InternalError("Unexpected IllegalAccessException");
+                    }
+                }
+            }
+        } finally {
+            unregister(object);
         }
-        iConstant = multiplierNonZeroOddNumber;
-        iTotal = initialNonZeroOddNumber;
     }
 
-    //-------------------------------------------------------------------------
-
     /**
-     * <p>This method uses reflection to build a valid hash code.</p>
-     *
-     * <p>This constructor uses two hard coded choices for the constants
-     * needed to build a hash code.</p>
-     *
-     * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
-     * fields. This means that it will throw a security exception if run under
-     * a security manager, if the permissions are not set up correctly. It is
-     * also not as efficient as testing explicitly.</p>
-     *
-     * <p>Transient members will be not be used, as they are likely derived
-     * fields, and not part of the value of the <code>Object</code>.</p>
-     *
-     * <p>Static fields will not be tested. Superclass fields will be included.</p>
-     *
-     * @param object  the Object to create a <code>hashCode</code> for
+     * <p>
+     * This method uses reflection to build a valid hash code.
+     * </p>
+     * 
+     * <p>
+     * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
+     * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
+     * also not as efficient as testing explicitly.
+     * </p>
+     * 
+     * <p>
+     * Transient members will be not be used, as they are likely derived fields, and not part of the value of the
+     * <code>Object</code>.
+     * </p>
+     * 
+     * <p>
+     * Static fields will not be tested. Superclass fields will be included.
+     * </p>
+     * 
+     * <p>
+     * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
+     * however this is not vital. Prime numbers are preferred, especially for the multiplier.
+     * </p>
+     * 
+     * @param initialNonZeroOddNumber
+     *            a non-zero, odd number used as the initial value
+     * @param multiplierNonZeroOddNumber
+     *            a non-zero, odd number used as the multiplier
+     * @param object
+     *            the Object to create a <code>hashCode</code> for
      * @return int hash code
-     * @throws IllegalArgumentException if the object is <code>null</code>
+     * @throws IllegalArgumentException
+     *             if the Object is <code>null</code>
+     * @throws IllegalArgumentException
+     *             if the number is zero or even
      */
-    public static int reflectionHashCode(Object object) {
-        return reflectionHashCode(17, 37, object, false, null, null);
+    public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object) {
+        return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, false, null, null);
     }
 
     /**
-     * <p>This method uses reflection to build a valid hash code.</p>
-     *
-     * <p>This constructor uses two hard coded choices for the constants
-     * needed to build a hash code.</p>
-     *
-     * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
-     * fields. This means that it will throw a security exception if run under
-     * a security manager, if the permissions are not set up correctly. It is
-     * also not as efficient as testing explicitly.</p>
-     *
-     * <p>Transient members will be not be used, as they are likely derived
-     * fields, and not part of the value of the <code>Object</code>.</p>
-     *
-     * <p>Static fields will not be tested. Superclass fields will be included.</p>
-     *
-     * @param object  the Object to create a <code>hashCode</code> for
-     * @param excludeFields  Collection of String field names to exclude from use in calculation of hash code
+     * <p>
+     * This method uses reflection to build a valid hash code.
+     * </p>
+     * 
+     * <p>
+     * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
+     * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
+     * also not as efficient as testing explicitly.
+     * </p>
+     * 
+     * <p>
+     * If the TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they
+     * are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>.
+     * </p>
+     * 
+     * <p>
+     * Static fields will not be tested. Superclass fields will be included.
+     * </p>
+     * 
+     * <p>
+     * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
+     * however this is not vital. Prime numbers are preferred, especially for the multiplier.
+     * </p>
+     * 
+     * @param initialNonZeroOddNumber
+     *            a non-zero, odd number used as the initial value
+     * @param multiplierNonZeroOddNumber
+     *            a non-zero, odd number used as the multiplier
+     * @param object
+     *            the Object to create a <code>hashCode</code> for
+     * @param testTransients
+     *            whether to include transient fields
      * @return int hash code
-     * @throws IllegalArgumentException if the object is <code>null</code>
+     * @throws IllegalArgumentException
+     *             if the Object is <code>null</code>
+     * @throws IllegalArgumentException
+     *             if the number is zero or even
      */
-    public static int reflectionHashCode(Object object, Collection /*String*/ excludeFields) {
-        return reflectionHashCode(object, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
+    public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object,
+            boolean testTransients) {
+        return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, testTransients, null,
+                null);
     }
 
     /**
-     * <p>This method uses reflection to build a valid hash code.</p>
-     *
-     * <p>This constructor uses two hard coded choices for the constants
-     * needed to build a hash code.</p>
-     *
-     * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
-     * fields. This means that it will throw a security exception if run under
-     * a security manager, if the permissions are not set up correctly. It is
-     * also not as efficient as testing explicitly.</p>
-     *
-     * <p>Transient members will be not be used, as they are likely derived
-     * fields, and not part of the value of the <code>Object</code>.</p>
-     *
-     * <p>Static fields will not be tested. Superclass fields will be included.</p>
-     *
-     * @param object  the Object to create a <code>hashCode</code> for
-     * @param excludeFields  array of field names to exclude from use in calculation of hash code
+     * Calls {@link #reflectionHashCode(int, int, Object, boolean, Class, String[])} with excludeFields set to
+     * <code>null</code>.
+     * 
+     * @param initialNonZeroOddNumber
+     *            a non-zero, odd number used as the initial value
+     * @param multiplierNonZeroOddNumber
+     *            a non-zero, odd number used as the multiplier
+     * @param object
+     *            the Object to create a <code>hashCode</code> for
+     * @param testTransients
+     *            whether to include transient fields
+     * @param reflectUpToClass
+     *            the superclass to reflect up to (inclusive), may be <code>null</code>
      * @return int hash code
-     * @throws IllegalArgumentException if the object is <code>null</code>
      */
-    public static int reflectionHashCode(Object object, String[] excludeFields) {
-        return reflectionHashCode(17, 37, object, false, null, excludeFields);
+    public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object,
+            boolean testTransients, Class reflectUpToClass) {
+        return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, testTransients,
+                reflectUpToClass, null);
     }
 
     /**
-     * <p>This method uses reflection to build a valid hash code.</p>
-     *
-     * <p>This constructor uses two hard coded choices for the constants needed
-     * to build a hash code.</p>
-     *
-     * <p> It uses <code>AccessibleObject.setAccessible</code> to gain access to private
-     * fields. This means that it will throw a security exception if run under
-     * a security manager, if the permissions are not set up correctly. It is
-     * also not as efficient as testing explicitly.</p>
-     *
-     * <P>If the TestTransients parameter is set to <code>true</code>, transient
-     * members will be tested, otherwise they are ignored, as they are likely
-     * derived fields, and not part of the value of the <code>Object</code>.</p>
-     *
-     * <p>Static fields will not be tested. Superclass fields will be included.</p>
-     *
-     * @param object  the Object to create a <code>hashCode</code> for
-     * @param testTransients  whether to include transient fields
+     * <p>
+     * This method uses reflection to build a valid hash code.
+     * </p>
+     * 
+     * <p>
+     * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
+     * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
+     * also not as efficient as testing explicitly.
+     * </p>
+     * 
+     * <p>
+     * If the TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they
+     * are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>.
+     * </p>
+     * 
+     * <p>
+     * Static fields will not be included. Superclass fields will be included up to and including the specified
+     * superclass. A null superclass is treated as java.lang.Object.
+     * </p>
+     * 
+     * <p>
+     * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
+     * however this is not vital. Prime numbers are preferred, especially for the multiplier.
+     * </p>
+     * 
+     * @param initialNonZeroOddNumber
+     *            a non-zero, odd number used as the initial value
+     * @param multiplierNonZeroOddNumber
+     *            a non-zero, odd number used as the multiplier
+     * @param object
+     *            the Object to create a <code>hashCode</code> for
+     * @param testTransients
+     *            whether to include transient fields
+     * @param reflectUpToClass
+     *            the superclass to reflect up to (inclusive), may be <code>null</code>
+     * @param excludeFields
+     *            array of field names to exclude from use in calculation of hash code
      * @return int hash code
-     * @throws IllegalArgumentException if the object is <code>null</code>
+     * @throws IllegalArgumentException
+     *             if the Object is <code>null</code>
+     * @throws IllegalArgumentException
+     *             if the number is zero or even
+     * @since 2.0
      */
-    public static int reflectionHashCode(Object object, boolean testTransients) {
-        return reflectionHashCode(17, 37, object, testTransients, null, null);
+    public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object,
+            boolean testTransients, Class reflectUpToClass, String[] excludeFields) {
+
+        if (object == null) {
+            throw new IllegalArgumentException("The object to build a hash code for must not be null");
+        }
+        HashCodeBuilder builder = new HashCodeBuilder(initialNonZeroOddNumber, multiplierNonZeroOddNumber);
+        Class clazz = object.getClass();
+        reflectionAppend(object, clazz, builder, testTransients, excludeFields);
+        while (clazz.getSuperclass() != null && clazz != reflectUpToClass) {
+            clazz = clazz.getSuperclass();
+            reflectionAppend(object, clazz, builder, testTransients, excludeFields);
+        }
+        return builder.toHashCode();
     }
 
     /**
-     * <p>This method uses reflection to build a valid hash code.</p>
-     *
-     * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
-     * fields. This means that it will throw a security exception if run under
-     * a security manager, if the permissions are not set up correctly. It is
-     * also not as efficient as testing explicitly.</p>
-     *
-     * <p>Transient members will be not be used, as they are likely derived
-     * fields, and not part of the value of the <code>Object</code>.</p>
-     *
-     * <p>Static fields will not be tested. Superclass fields will be included.</p>
-     *
-     * <p>Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
-     * these should be different for each class, however this is not vital.
-     * Prime numbers are preferred, especially for the multiplier.</p>
-     *
-     * @param initialNonZeroOddNumber  a non-zero, odd number used as the initial value
-     * @param multiplierNonZeroOddNumber  a non-zero, odd number used as the multiplier
-     * @param object  the Object to create a <code>hashCode</code> for
+     * <p>
+     * This method uses reflection to build a valid hash code.
+     * </p>
+     * 
+     * <p>
+     * This constructor uses two hard coded choices for the constants needed to build a hash code.
+     * </p>
+     * 
+     * <p>
+     * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
+     * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
+     * also not as efficient as testing explicitly.
+     * </p>
+     * 
+     * <p>
+     * Transient members will be not be used, as they are likely derived fields, and not part of the value of the
+     * <code>Object</code>.
+     * </p>
+     * 
+     * <p>
+     * Static fields will not be tested. Superclass fields will be included.
+     * </p>
+     * 
+     * @param object
+     *            the Object to create a <code>hashCode</code> for
      * @return int hash code
-     * @throws IllegalArgumentException if the Object is <code>null</code>
-     * @throws IllegalArgumentException if the number is zero or even
+     * @throws IllegalArgumentException
+     *             if the object is <code>null</code>
      */
-    public static int reflectionHashCode(
-            int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object) {
-        return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, false, null, null);
+    public static int reflectionHashCode(Object object) {
+        return reflectionHashCode(17, 37, object, false, null, null);
     }
 
     /**
-     * <p>This method uses reflection to build a valid hash code.</p>
-     *
-     * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
-     * fields. This means that it will throw a security exception if run under
-     * a security manager, if the permissions are not set up correctly. It is also
-     * not as efficient as testing explicitly.</p>
-     *
-     * <p>If the TestTransients parameter is set to <code>true</code>, transient
-     * members will be tested, otherwise they are ignored, as they are likely
-     * derived fields, and not part of the value of the <code>Object</code>.</p>
-     *
-     * <p>Static fields will not be tested. Superclass fields will be included.</p>
-     *
-     * <p>Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
-     * these should be different for each class, however this is not vital.
-     * Prime numbers are preferred, especially for the multiplier.</p>
-     *
-     * @param initialNonZeroOddNumber  a non-zero, odd number used as the initial value
-     * @param multiplierNonZeroOddNumber  a non-zero, odd number used as the multiplier
-     * @param object  the Object to create a <code>hashCode</code> for
-     * @param testTransients  whether to include transient fields
+     * <p>
+     * This method uses reflection to build a valid hash code.
+     * </p>
+     * 
+     * <p>
+     * This constructor uses two hard coded choices for the constants needed to build a hash code.
+     * </p>
+     * 
+     * <p>
+     * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
+     * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
+     * also not as efficient as testing explicitly.
+     * </p>
+     * 
+     * <P>
+     * If the TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they
+     * are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>.
+     * </p>
+     * 
+     * <p>
+     * Static fields will not be tested. Superclass fields will be included.
+     * </p>
+     * 
+     * @param object
+     *            the Object to create a <code>hashCode</code> for
+     * @param testTransients
+     *            whether to include transient fields
      * @return int hash code
-     * @throws IllegalArgumentException if the Object is <code>null</code>
-     * @throws IllegalArgumentException if the number is zero or even
+     * @throws IllegalArgumentException
+     *             if the object is <code>null</code>
      */
-    public static int reflectionHashCode(
-            int initialNonZeroOddNumber, int multiplierNonZeroOddNumber,
-            Object object, boolean testTransients) {
-        return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, 
-                                  object, testTransients, null, null
-                                 );
-    }
-            
-    public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber,
-                                         Object object, boolean testTransients, Class reflectUpToClass) 
-    {
-        return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, 
-                                  testTransients, reflectUpToClass, null);
+    public static int reflectionHashCode(Object object, boolean testTransients) {
+        return reflectionHashCode(17, 37, object, testTransients, null, null);
     }
 
     /**
-     * <p>This method uses reflection to build a valid hash code.</p>
-     *
-     * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
-     * fields. This means that it will throw a security exception if run under
-     * a security manager, if the permissions are not set up correctly. It is also
-     * not as efficient as testing explicitly.</p>
-     *
-     * <p>If the TestTransients parameter is set to <code>true</code>, transient
-     * members will be tested, otherwise they are ignored, as they are likely
-     * derived fields, and not part of the value of the <code>Object</code>.</p>
-     *
-     * <p>Static fields will not be included. Superclass fields will be included
-     * up to and including the specified superclass. A null superclass is treated
-     * as java.lang.Object.</p>
-     *
-     * <p>Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
-     * these should be different for each class, however this is not vital.
-     * Prime numbers are preferred, especially for the multiplier.</p>
-     *
-     * @param initialNonZeroOddNumber  a non-zero, odd number used as the initial value
-     * @param multiplierNonZeroOddNumber  a non-zero, odd number used as the multiplier
-     * @param object  the Object to create a <code>hashCode</code> for
-     * @param testTransients  whether to include transient fields
-     * @param reflectUpToClass  the superclass to reflect up to (inclusive),
-     *  may be <code>null</code>
-     * @param excludeFields  array of field names to exclude from use in calculation of hash code
+     * <p>
+     * This method uses reflection to build a valid hash code.
+     * </p>
+     * 
+     * <p>
+     * This constructor uses two hard coded choices for the constants needed to build a hash code.
+     * </p>
+     * 
+     * <p>
+     * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
+     * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
+     * also not as efficient as testing explicitly.
+     * </p>
+     * 
+     * <p>
+     * Transient members will be not be used, as they are likely derived fields, and not part of the value of the
+     * <code>Object</code>.
+     * </p>
+     * 
+     * <p>
+     * Static fields will not be tested. Superclass fields will be included.
+     * </p>
+     * 
+     * @param object
+     *            the Object to create a <code>hashCode</code> for
+     * @param excludeFields
+     *            Collection of String field names to exclude from use in calculation of hash code
      * @return int hash code
-     * @throws IllegalArgumentException if the Object is <code>null</code>
-     * @throws IllegalArgumentException if the number is zero or even
-     * @since 2.0
+     * @throws IllegalArgumentException
+     *             if the object is <code>null</code>
      */
-    public static int reflectionHashCode(
-        int initialNonZeroOddNumber,
-        int multiplierNonZeroOddNumber,
-        Object object,
-        boolean testTransients,
-        Class reflectUpToClass,
-        String[] excludeFields) {
+    public static int reflectionHashCode(Object object, Collection /* String */excludeFields) {
+        return reflectionHashCode(object, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
+    }
 
-        if (object == null) {
-            throw new IllegalArgumentException("The object to build a hash code for must not be null");
-        }
-        HashCodeBuilder builder = new HashCodeBuilder(initialNonZeroOddNumber, multiplierNonZeroOddNumber);
-        Class clazz = object.getClass();
-        reflectionAppend(object, clazz, builder, testTransients, excludeFields);
-        while (clazz.getSuperclass() != null && clazz != reflectUpToClass) {
-            clazz = clazz.getSuperclass();
-            reflectionAppend(object, clazz, builder, testTransients, excludeFields);
-        }
-        return builder.toHashCode();
+    // -------------------------------------------------------------------------
+
+    /**
+     * <p>
+     * This method uses reflection to build a valid hash code.
+     * </p>
+     * 
+     * <p>
+     * This constructor uses two hard coded choices for the constants needed to build a hash code.
+     * </p>
+     * 
+     * <p>
+     * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
+     * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
+     * also not as efficient as testing explicitly.
+     * </p>
+     * 
+     * <p>
+     * Transient members will be not be used, as they are likely derived fields, and not part of the value of the
+     * <code>Object</code>.
+     * </p>
+     * 
+     * <p>
+     * Static fields will not be tested. Superclass fields will be included.
+     * </p>
+     * 
+     * @param object
+     *            the Object to create a <code>hashCode</code> for
+     * @param excludeFields
+     *            array of field names to exclude from use in calculation of hash code
+     * @return int hash code
+     * @throws IllegalArgumentException
+     *             if the object is <code>null</code>
+     */
+    public static int reflectionHashCode(Object object, String[] excludeFields) {
+        return reflectionHashCode(17, 37, object, false, null, excludeFields);
     }
 
     /**
-     * <p>Appends the fields and values defined by the given object of the
-     * given <code>Class</code>.</p>
+     * <p>
+     * Registers the given object. Used by the reflection methods to avoid infinite loops.
+     * </p>
      * 
-     * @param object  the object to append details of
-     * @param clazz  the class to append details of
-     * @param builder  the builder to append to
-     * @param useTransients  whether to use transient fields
-     * @param excludeFields  Collection of String field names to exclude from use in calculation of hash code
-     */
-    private static void reflectionAppend(
-            Object object, 
-            Class clazz, 
-            HashCodeBuilder builder, 
-            boolean useTransients,
-            String[] excludeFields) {
-        Field[] fields = clazz.getDeclaredFields();
-        List excludedFieldList = excludeFields != null ? Arrays.asList(excludeFields) : Collections.EMPTY_LIST;
-        AccessibleObject.setAccessible(fields, true);
-        for (int i = 0; i < fields.length; i++) {
-            Field f = fields[i];
-            if (!excludedFieldList.contains(f.getName())
-                && (f.getName().indexOf('$') == -1)
-                && (useTransients || !Modifier.isTransient(f.getModifiers()))
-                && (!Modifier.isStatic(f.getModifiers()))) {
-                try {
-                    builder.append(f.get(object));
-                } catch (IllegalAccessException e) {
-                    //this can't happen. Would get a Security exception instead
-                    //throw a runtime exception in case the impossible happens.
-                    throw new InternalError("Unexpected IllegalAccessException");
-                }
-            }
-        }
+     * @param value
+     *            The object to register.
+     */
+    static void register(Object value) {
+        getRegistry().add(toIdentityHashCodeInteger(value));
     }
 
-    //-------------------------------------------------------------------------
+    /**
+     * Returns an Integer for the given object's default hash code.
+     * 
+     * @see System#identityHashCode(Object)
+     * @param value
+     *            object for which the hashCode is to be calculated
+     * @return Default int hash code
+     */
+    private static Integer toIdentityHashCodeInteger(Object value) {
+        return new Integer(System.identityHashCode(value));
+    }
 
     /**
-     * <p>Adds the result of super.hashCode() to this builder.</p>
-     *
-     * @param superHashCode  the result of calling <code>super.hashCode()</code>
-     * @return this HashCodeBuilder, used to chain calls.
-     * @since 2.0
+     * <p>
+     * Unregisters the given object.
+     * </p>
+     * 
+     * <p>
+     * Used by the reflection methods to avoid infinite loops.
+     * 
+     * @param value
+     *            The object to unregister.
+     * @since 2.3
      */
-    public HashCodeBuilder appendSuper(int superHashCode) {
-        iTotal = iTotal * iConstant + superHashCode;
-        return this;
+    static void unregister(Object value) {
+        getRegistry().remove(toIdentityHashCodeInteger(value));
     }
 
-    //-------------------------------------------------------------------------
+    /**
+     * Constant to use in building the hashCode.
+     */
+    private final int iConstant;
 
     /**
-     * <p>Append a <code>hashCode</code> for an <code>Object</code>.</p>
-     *
-     * @param object  the Object to add to the <code>hashCode</code>
-     * @return this
+     * Running total of the hashCode.
      */
-    public HashCodeBuilder append(Object object) {
-        if (object == null) {
-            iTotal = iTotal * iConstant;
+    private int iTotal = 0;
 
-        } else {
-            if (object.getClass().isArray() == false) {
-                //the simple case, not an array, just the element
-                iTotal = iTotal * iConstant + object.hashCode();
+    /**
+     * <p>
+     * Uses two hard coded choices for the constants needed to build a <code>hashCode</code>.
+     * </p>
+     */
+    public HashCodeBuilder() {
+        iConstant = 37;
+        iTotal = 17;
+    }
 
-            } else {
-                //'Switch' on type of array, to dispatch to the correct handler
-                // This handles multi dimensional arrays
-                if (object instanceof long[]) {
-                    append((long[]) object);
-                } else if (object instanceof int[]) {
-                    append((int[]) object);
-                } else if (object instanceof short[]) {
-                    append((short[]) object);
-                } else if (object instanceof char[]) {
-                    append((char[]) object);
-                } else if (object instanceof byte[]) {
-                    append((byte[]) object);
-                } else if (object instanceof double[]) {
-                    append((double[]) object);
-                } else if (object instanceof float[]) {
-                    append((float[]) object);
-                } else if (object instanceof boolean[]) {
-                    append((boolean[]) object);
-                } else {
-                    // Not an array of primitives
-                    append((Object[]) object);
-                }
-            }
+    /**
+     * <p>
+     * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
+     * however this is not vital.
+     * </p>
+     * 
+     * <p>
+     * Prime numbers are preferred, especially for the multiplier.
+     * </p>
+     * 
+     * @param initialNonZeroOddNumber
+     *            a non-zero, odd number used as the initial value
+     * @param multiplierNonZeroOddNumber
+     *            a non-zero, odd number used as the multiplier
+     * @throws IllegalArgumentException
+     *             if the number is zero or even
+     */
+    public HashCodeBuilder(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber) {
+        if (initialNonZeroOddNumber == 0) {
+            throw new IllegalArgumentException("HashCodeBuilder requires a non zero initial value");
         }
+        if (initialNonZeroOddNumber % 2 == 0) {
+            throw new IllegalArgumentException("HashCodeBuilder requires an odd initial value");
+        }
+        if (multiplierNonZeroOddNumber == 0) {
+            throw new IllegalArgumentException("HashCodeBuilder requires a non zero multiplier");
+        }
+        if (multiplierNonZeroOddNumber % 2 == 0) {
+            throw new IllegalArgumentException("HashCodeBuilder requires an odd multiplier");
+        }
+        iConstant = multiplierNonZeroOddNumber;
+        iTotal = initialNonZeroOddNumber;
+    }
+
+    /**
+     * <p>
+     * Append a <code>hashCode</code> for a <code>boolean</code>.
+     * </p>
+     * <p>
+     * This adds <code>iConstant * 1</code> to the <code>hashCode</code> and not a <code>1231</code> or
+     * <code>1237</code> as done in java.lang.Boolean. This is in accordance with the <quote>Effective Java</quote>
+     * design.
+     * </p>
+     * 
+     * @param value
+     *            the boolean to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append(boolean value) {
+        iTotal = iTotal * iConstant + (value ? 0 : 1);
         return this;
     }
 
     /**
-     * <p>Append a <code>hashCode</code> for a <code>long</code>.</p>
-     *
-     * @param value  the long to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for a <code>boolean</code> array.
+     * </p>
+     * 
+     * @param array
+     *            the array to add to the <code>hashCode</code>
      * @return this
      */
-    public HashCodeBuilder append(long value) {
-        iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32)));
+    public HashCodeBuilder append(boolean[] array) {
+        if (array == null) {
+            iTotal = iTotal * iConstant;
+        } else {
+            for (int i = 0; i < array.length; i++) {
+                append(array[i]);
+            }
+        }
         return this;
     }
 
+    // -------------------------------------------------------------------------
+
     /**
-     * <p>Append a <code>hashCode</code> for an <code>int</code>.</p>
-     *
-     * @param value  the int to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for a <code>byte</code>.
+     * </p>
+     * 
+     * @param value
+     *            the byte to add to the <code>hashCode</code>
      * @return this
      */
-    public HashCodeBuilder append(int value) {
+    public HashCodeBuilder append(byte value) {
         iTotal = iTotal * iConstant + value;
         return this;
     }
 
+    // -------------------------------------------------------------------------
+
     /**
-     * <p>Append a <code>hashCode</code> for a <code>short</code>.</p>
-     *
-     * @param value  the short to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for a <code>byte</code> array.
+     * </p>
+     * 
+     * @param array
+     *            the array to add to the <code>hashCode</code>
      * @return this
      */
-    public HashCodeBuilder append(short value) {
-        iTotal = iTotal * iConstant + value;
+    public HashCodeBuilder append(byte[] array) {
+        if (array == null) {
+            iTotal = iTotal * iConstant;
+        } else {
+            for (int i = 0; i < array.length; i++) {
+                append(array[i]);
+            }
+        }
         return this;
     }
 
     /**
-     * <p>Append a <code>hashCode</code> for a <code>char</code>.</p>
-     *
-     * @param value  the char to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for a <code>char</code>.
+     * </p>
+     * 
+     * @param value
+     *            the char to add to the <code>hashCode</code>
      * @return this
      */
     public HashCodeBuilder append(char value) {
@@ -494,20 +683,32 @@
     }
 
     /**
-     * <p>Append a <code>hashCode</code> for a <code>byte</code>.</p>
-     *
-     * @param value  the byte to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for a <code>char</code> array.
+     * </p>
+     * 
+     * @param array
+     *            the array to add to the <code>hashCode</code>
      * @return this
      */
-    public HashCodeBuilder append(byte value) {
-        iTotal = iTotal * iConstant + value;
+    public HashCodeBuilder append(char[] array) {
+        if (array == null) {
+            iTotal = iTotal * iConstant;
+        } else {
+            for (int i = 0; i < array.length; i++) {
+                append(array[i]);
+            }
+        }
         return this;
     }
 
     /**
-     * <p>Append a <code>hashCode</code> for a <code>double</code>.</p>
-     *
-     * @param value  the double to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for a <code>double</code>.
+     * </p>
+     * 
+     * @param value
+     *            the double to add to the <code>hashCode</code>
      * @return this
      */
     public HashCodeBuilder append(double value) {
@@ -515,37 +716,49 @@
     }
 
     /**
-     * <p>Append a <code>hashCode</code> for a <code>float</code>.</p>
-     *
-     * @param value  the float to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for a <code>double</code> array.
+     * </p>
+     * 
+     * @param array
+     *            the array to add to the <code>hashCode</code>
      * @return this
      */
-    public HashCodeBuilder append(float value) {
-        iTotal = iTotal * iConstant + Float.floatToIntBits(value);
+    public HashCodeBuilder append(double[] array) {
+        if (array == null) {
+            iTotal = iTotal * iConstant;
+        } else {
+            for (int i = 0; i < array.length; i++) {
+                append(array[i]);
+            }
+        }
         return this;
     }
 
     /**
-     * <p>Append a <code>hashCode</code> for a <code>boolean</code>.</p>
-     * <p>This adds <code>iConstant * 1</code> to the <code>hashCode</code>
-     * and not a <code>1231</code> or <code>1237</code> as done in java.lang.Boolean. 
-     * This is in accordance with the <quote>Effective Java</quote> design. </p>
-     *
-     * @param value  the boolean to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for a <code>float</code>.
+     * </p>
+     * 
+     * @param value
+     *            the float to add to the <code>hashCode</code>
      * @return this
      */
-    public HashCodeBuilder append(boolean value) {
-        iTotal = iTotal * iConstant + (value ? 0 : 1);
+    public HashCodeBuilder append(float value) {
+        iTotal = iTotal * iConstant + Float.floatToIntBits(value);
         return this;
     }
 
     /**
-     * <p>Append a <code>hashCode</code> for an <code>Object</code> array.</p>
-     *
-     * @param array  the array to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for a <code>float</code> array.
+     * </p>
+     * 
+     * @param array
+     *            the array to add to the <code>hashCode</code>
      * @return this
      */
-    public HashCodeBuilder append(Object[] array) {
+    public HashCodeBuilder append(float[] array) {
         if (array == null) {
             iTotal = iTotal * iConstant;
         } else {
@@ -557,26 +770,26 @@
     }
 
     /**
-     * <p>Append a <code>hashCode</code> for a <code>long</code> array.</p>
-     *
-     * @param array  the array to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for an <code>int</code>.
+     * </p>
+     * 
+     * @param value
+     *            the int to add to the <code>hashCode</code>
      * @return this
      */
-    public HashCodeBuilder append(long[] array) {
-        if (array == null) {
-            iTotal = iTotal * iConstant;
-        } else {
-            for (int i = 0; i < array.length; i++) {
-                append(array[i]);
-            }
-        }
+    public HashCodeBuilder append(int value) {
+        iTotal = iTotal * iConstant + value;
         return this;
     }
 
     /**
-     * <p>Append a <code>hashCode</code> for an <code>int</code> array.</p>
-     *
-     * @param array  the array to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for an <code>int</code> array.
+     * </p>
+     * 
+     * @param array
+     *            the array to add to the <code>hashCode</code>
      * @return this
      */
     public HashCodeBuilder append(int[] array) {
@@ -591,29 +804,29 @@
     }
 
     /**
-     * <p>Append a <code>hashCode</code> for a <code>short</code> array.</p>
-     *
-     * @param array  the array to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for a <code>long</code>.
+     * </p>
+     * 
+     * @param value
+     *            the long to add to the <code>hashCode</code>
      * @return this
      */
-    public HashCodeBuilder append(short[] array) {
-        if (array == null) {
-            iTotal = iTotal * iConstant;
-        } else {
-            for (int i = 0; i < array.length; i++) {
-                append(array[i]);
-            }
-        }
+    public HashCodeBuilder append(long value) {
+        iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32)));
         return this;
     }
 
     /**
-     * <p>Append a <code>hashCode</code> for a <code>char</code> array.</p>
-     *
-     * @param array  the array to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for a <code>long</code> array.
+     * </p>
+     * 
+     * @param array
+     *            the array to add to the <code>hashCode</code>
      * @return this
      */
-    public HashCodeBuilder append(char[] array) {
+    public HashCodeBuilder append(long[] array) {
         if (array == null) {
             iTotal = iTotal * iConstant;
         } else {
@@ -625,29 +838,61 @@
     }
 
     /**
-     * <p>Append a <code>hashCode</code> for a <code>byte</code> array.</p>
-     *
-     * @param array  the array to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for an <code>Object</code>.
+     * </p>
+     * 
+     * @param object
+     *            the Object to add to the <code>hashCode</code>
      * @return this
      */
-    public HashCodeBuilder append(byte[] array) {
-        if (array == null) {
+    public HashCodeBuilder append(Object object) {
+        if (object == null) {
             iTotal = iTotal * iConstant;
+
         } else {
-            for (int i = 0; i < array.length; i++) {
-                append(array[i]);
+            if (object.getClass().isArray() == false) {
+                // the simple case, not an array, just the element
+                iTotal = iTotal * iConstant + object.hashCode();
+
+            } else {
+                // 'Switch' on type of array, to dispatch to the correct handler
+                // This handles multi dimensional arrays
+                if (object instanceof long[]) {
+                    append((long[]) object);
+                } else if (object instanceof int[]) {
+                    append((int[]) object);
+                } else if (object instanceof short[]) {
+                    append((short[]) object);
+                } else if (object instanceof char[]) {
+                    append((char[]) object);
+                } else if (object instanceof byte[]) {
+                    append((byte[]) object);
+                } else if (object instanceof double[]) {
+                    append((double[]) object);
+                } else if (object instanceof float[]) {
+                    append((float[]) object);
+                } else if (object instanceof boolean[]) {
+                    append((boolean[]) object);
+                } else {
+                    // Not an array of primitives
+                    append((Object[]) object);
+                }
             }
         }
         return this;
     }
 
     /**
-     * <p>Append a <code>hashCode</code> for a <code>double</code> array.</p>
-     *
-     * @param array  the array to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for an <code>Object</code> array.
+     * </p>
+     * 
+     * @param array
+     *            the array to add to the <code>hashCode</code>
      * @return this
      */
-    public HashCodeBuilder append(double[] array) {
+    public HashCodeBuilder append(Object[] array) {
         if (array == null) {
             iTotal = iTotal * iConstant;
         } else {
@@ -659,29 +904,29 @@
     }
 
     /**
-     * <p>Append a <code>hashCode</code> for a <code>float</code> array.</p>
-     *
-     * @param array  the array to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for a <code>short</code>.
+     * </p>
+     * 
+     * @param value
+     *            the short to add to the <code>hashCode</code>
      * @return this
      */
-    public HashCodeBuilder append(float[] array) {
-        if (array == null) {
-            iTotal = iTotal * iConstant;
-        } else {
-            for (int i = 0; i < array.length; i++) {
-                append(array[i]);
-            }
-        }
+    public HashCodeBuilder append(short value) {
+        iTotal = iTotal * iConstant + value;
         return this;
     }
 
     /**
-     * <p>Append a <code>hashCode</code> for a <code>boolean</code> array.</p>
-     *
-     * @param array  the array to add to the <code>hashCode</code>
+     * <p>
+     * Append a <code>hashCode</code> for a <code>short</code> array.
+     * </p>
+     * 
+     * @param array
+     *            the array to add to the <code>hashCode</code>
      * @return this
      */
-    public HashCodeBuilder append(boolean[] array) {
+    public HashCodeBuilder append(short[] array) {
         if (array == null) {
             iTotal = iTotal * iConstant;
         } else {
@@ -693,8 +938,25 @@
     }
 
     /**
-     * <p>Return the computed <code>hashCode</code>.</p>
-     *
+     * <p>
+     * Adds the result of super.hashCode() to this builder.
+     * </p>
+     * 
+     * @param superHashCode
+     *            the result of calling <code>super.hashCode()</code>
+     * @return this HashCodeBuilder, used to chain calls.
+     * @since 2.0
+     */
+    public HashCodeBuilder appendSuper(int superHashCode) {
+        iTotal = iTotal * iConstant + superHashCode;
+        return this;
+    }
+
+    /**
+     * <p>
+     * Return the computed <code>hashCode</code>.
+     * </p>
+     * 
      * @return <code>hashCode</code> based on the fields appended
      */
     public int toHashCode() {

Modified: jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/builder/HashCodeBuilderTest.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/builder/HashCodeBuilderTest.java?view=diff&rev=447989&r1=447988&r2=447989
==============================================================================
--- jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/builder/HashCodeBuilderTest.java (original)
+++ jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/builder/HashCodeBuilderTest.java Tue Sep 19 14:58:11 2006
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.commons.lang.builder;
 
 import org.apache.commons.lang.builder.ToStringBuilderTest.ReflectionTestCycleA;
@@ -26,7 +27,7 @@
 
 /**
  * Unit tests {@link org.apache.commons.lang.builder.HashCodeBuilder}.
- *
+ * 
  * @author <a href="mailto:scolebourne@joda.org">Stephen Colebourne</a>
  * @version $Id$
  */
@@ -53,7 +54,7 @@
             return HashCodeBuilder.reflectionHashCode(this);
         }
     }
-    
+
     public HashCodeBuilderTest(String name) {
         super(name);
     }
@@ -76,12 +77,12 @@
         super.tearDown();
     }
 
-    //-----------------------------------------------------------------------
+    // -----------------------------------------------------------------------
 
     public void testConstructorEx1() {
         try {
             new HashCodeBuilder(0, 0);
-            
+
         } catch (IllegalArgumentException ex) {
             return;
         }
@@ -91,7 +92,7 @@
     public void testConstructorEx2() {
         try {
             new HashCodeBuilder(2, 2);
-            
+
         } catch (IllegalArgumentException ex) {
             return;
         }
@@ -100,9 +101,11 @@
 
     static class TestObject {
         private int a;
+
         public TestObject(int a) {
             this.a = a;
         }
+
         public boolean equals(Object o) {
             if (o == this) {
                 return true;
@@ -125,15 +128,19 @@
 
     static class TestSubObject extends TestObject {
         private int b;
+
         transient private int t;
+
         public TestSubObject() {
             super(0);
         }
+
         public TestSubObject(int a, int b, int t) {
             super(a);
             this.b = b;
             this.t = t;
         }
+
         public boolean equals(Object o) {
             if (o == this) {
                 return true;
@@ -154,8 +161,10 @@
     public void testReflectionHierarchyHashCode() {
         assertEquals(17 * 37 * 37, HashCodeBuilder.reflectionHashCode(new TestSubObject(0, 0, 0)));
         assertEquals(17 * 37 * 37 * 37, HashCodeBuilder.reflectionHashCode(new TestSubObject(0, 0, 0), true));
-        assertEquals((17 * 37 + 7890) * 37 + 123456, HashCodeBuilder.reflectionHashCode(new TestSubObject(123456, 7890, 0)));
-        assertEquals(((17 * 37 + 7890) * 37 + 0) * 37 + 123456, HashCodeBuilder.reflectionHashCode(new TestSubObject(123456, 7890, 0), true));
+        assertEquals((17 * 37 + 7890) * 37 + 123456, HashCodeBuilder.reflectionHashCode(new TestSubObject(123456, 7890,
+                0)));
+        assertEquals(((17 * 37 + 7890) * 37 + 0) * 37 + 123456, HashCodeBuilder.reflectionHashCode(new TestSubObject(
+                123456, 7890, 0), true));
     }
 
     public void testReflectionHierarchyHashCodeEx1() {
@@ -206,8 +215,7 @@
     public void testSuper() {
         Object obj = new Object();
         assertEquals(17 * 37 + (19 * 41 + obj.hashCode()), new HashCodeBuilder(17, 37).appendSuper(
-            new HashCodeBuilder(19, 41).append(obj).toHashCode()
-        ).toHashCode());
+                new HashCodeBuilder(19, 41).append(obj).toHashCode()).toHashCode());
     }
 
     public void testObject() {
@@ -219,7 +227,8 @@
 
     public void testLong() {
         assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((long) 0L).toHashCode());
-        assertEquals(17 * 37 + (int) (123456789L ^ (123456789L >> 32)), new HashCodeBuilder(17, 37).append((long) 123456789L).toHashCode());
+        assertEquals(17 * 37 + (int) (123456789L ^ (123456789L >> 32)), new HashCodeBuilder(17, 37).append(
+                (long) 123456789L).toHashCode());
     }
 
     public void testInt() {
@@ -264,125 +273,127 @@
     public void testObjectArray() {
         assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((Object[]) null).toHashCode());
         Object[] obj = new Object[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[0] = new Object();
         assertEquals((17 * 37 + obj[0].hashCode()) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[1] = new Object();
-        assertEquals( (17 * 37 + obj[0].hashCode()) * 37 + obj[1].hashCode(), new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37 + obj[0].hashCode()) * 37 + obj[1].hashCode(), new HashCodeBuilder(17, 37).append(obj)
+                .toHashCode());
     }
 
     public void testObjectArrayAsObject() {
         Object[] obj = new Object[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[0] = new Object();
         assertEquals((17 * 37 + obj[0].hashCode()) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[1] = new Object();
-        assertEquals( (17 * 37 + obj[0].hashCode()) * 37 + obj[1].hashCode(), new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37 + obj[0].hashCode()) * 37 + obj[1].hashCode(), new HashCodeBuilder(17, 37).append(
+                (Object) obj).toHashCode());
     }
 
     public void testLongArray() {
         assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((long[]) null).toHashCode());
         long[] obj = new long[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[0] = 5L;
         int h1 = (int) (5L ^ (5L >> 32));
         assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[1] = 6L;
         int h2 = (int) (6L ^ (6L >> 32));
-        assertEquals( (17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append(obj).toHashCode());
     }
 
     public void testLongArrayAsObject() {
         long[] obj = new long[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[0] = 5L;
         int h1 = (int) (5L ^ (5L >> 32));
         assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[1] = 6L;
         int h2 = (int) (6L ^ (6L >> 32));
-        assertEquals( (17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
     }
 
     public void testIntArray() {
         assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((int[]) null).toHashCode());
         int[] obj = new int[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[0] = 5;
         assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[1] = 6;
-        assertEquals( (17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode());
     }
 
     public void testIntArrayAsObject() {
         int[] obj = new int[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[0] = 5;
         assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[1] = 6;
-        assertEquals( (17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
     }
 
     public void testShortArray() {
         assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((short[]) null).toHashCode());
         short[] obj = new short[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[0] = (short) 5;
         assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[1] = (short) 6;
-        assertEquals( (17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode());
     }
 
     public void testShortArrayAsObject() {
         short[] obj = new short[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[0] = (short) 5;
         assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[1] = (short) 6;
-        assertEquals( (17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
     }
 
     public void testCharArray() {
         assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((char[]) null).toHashCode());
         char[] obj = new char[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[0] = (char) 5;
         assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[1] = (char) 6;
-        assertEquals( (17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode());
     }
 
     public void testCharArrayAsObject() {
         char[] obj = new char[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[0] = (char) 5;
         assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[1] = (char) 6;
-        assertEquals( (17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
     }
 
     public void testByteArray() {
         assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((byte[]) null).toHashCode());
         byte[] obj = new byte[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[0] = (byte) 5;
         assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[1] = (byte) 6;
-        assertEquals( (17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode());
     }
 
     public void testByteArrayAsObject() {
         byte[] obj = new byte[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[0] = (byte) 5;
         assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[1] = (byte) 6;
-        assertEquals( (17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
     }
 
     public void testDoubleArray() {
         assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((double[]) null).toHashCode());
         double[] obj = new double[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[0] = 5.4d;
         long l1 = Double.doubleToLongBits(5.4d);
         int h1 = (int) (l1 ^ (l1 >> 32));
@@ -390,12 +401,12 @@
         obj[1] = 6.3d;
         long l2 = Double.doubleToLongBits(6.3d);
         int h2 = (int) (l2 ^ (l2 >> 32));
-        assertEquals( (17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append(obj).toHashCode());
     }
 
     public void testDoubleArrayAsObject() {
         double[] obj = new double[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[0] = 5.4d;
         long l1 = Double.doubleToLongBits(5.4d);
         int h1 = (int) (l1 ^ (l1 >> 32));
@@ -403,49 +414,49 @@
         obj[1] = 6.3d;
         long l2 = Double.doubleToLongBits(6.3d);
         int h2 = (int) (l2 ^ (l2 >> 32));
-        assertEquals( (17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
     }
 
     public void testFloatArray() {
         assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((float[]) null).toHashCode());
         float[] obj = new float[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[0] = 5.4f;
         int h1 = Float.floatToIntBits(5.4f);
         assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[1] = 6.3f;
         int h2 = Float.floatToIntBits(6.3f);
-        assertEquals( (17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append(obj).toHashCode());
     }
 
     public void testFloatArrayAsObject() {
         float[] obj = new float[2];
-        assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[0] = 5.4f;
         int h1 = Float.floatToIntBits(5.4f);
         assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[1] = 6.3f;
         int h2 = Float.floatToIntBits(6.3f);
-        assertEquals( (17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
     }
 
     public void testBooleanArray() {
         assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((boolean[]) null).toHashCode());
         boolean[] obj = new boolean[2];
-        assertEquals((17 * 37 + 1) * 37 + 1 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37 + 1) * 37 + 1, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[0] = true;
         assertEquals((17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[1] = false;
-        assertEquals( (17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append(obj).toHashCode());
     }
 
     public void testBooleanArrayAsObject() {
         boolean[] obj = new boolean[2];
-        assertEquals((17 * 37 + 1) * 37 + 1 , new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37 + 1) * 37 + 1, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[0] = true;
         assertEquals((17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
         obj[1] = false;
-        assertEquals( (17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
+        assertEquals((17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode());
     }
 
     public void testBooleanMultiArray() {
@@ -460,7 +471,7 @@
         obj[0][0] = true;
         assertEquals(((17 * 37 + 0) * 37 + 1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
         obj[1] = new boolean[1];
-        assertEquals( (((17 * 37 + 0) * 37 + 1) * 37 + 1), new HashCodeBuilder(17, 37).append(obj).toHashCode());
+        assertEquals((((17 * 37 + 0) * 37 + 1) * 37 + 1), new HashCodeBuilder(17, 37).append(obj).toHashCode());
     }
 
     public void testReflectionHashCodeExcludeFields() throws Exception {
@@ -469,21 +480,23 @@
         assertEquals((((17 * 37 + 1) * 37 + 2) * 37 + 3), HashCodeBuilder.reflectionHashCode(x));
 
         assertEquals((((17 * 37 + 1) * 37 + 2) * 37 + 3), HashCodeBuilder.reflectionHashCode(x, (String[]) null));
-        assertEquals((((17 * 37 + 1) * 37 + 2) * 37 + 3), HashCodeBuilder.reflectionHashCode(x, new String[] {}));
-        assertEquals((((17 * 37 + 1) * 37 + 2) * 37 + 3), HashCodeBuilder.reflectionHashCode(x, new String[] {"xxx"}));
+        assertEquals((((17 * 37 + 1) * 37 + 2) * 37 + 3), HashCodeBuilder.reflectionHashCode(x, new String[]{}));
+        assertEquals((((17 * 37 + 1) * 37 + 2) * 37 + 3), HashCodeBuilder.reflectionHashCode(x, new String[]{"xxx"}));
 
-        assertEquals(((17 * 37 + 1) * 37 + 3), HashCodeBuilder.reflectionHashCode(x, new String[] {"two"}));
-        assertEquals(((17 * 37 + 1) * 37 + 2), HashCodeBuilder.reflectionHashCode(x, new String[] {"three"}));
+        assertEquals(((17 * 37 + 1) * 37 + 3), HashCodeBuilder.reflectionHashCode(x, new String[]{"two"}));
+        assertEquals(((17 * 37 + 1) * 37 + 2), HashCodeBuilder.reflectionHashCode(x, new String[]{"three"}));
 
-        assertEquals((17 * 37 + 1), HashCodeBuilder.reflectionHashCode(x, new String[] {"two", "three"}));
+        assertEquals((17 * 37 + 1), HashCodeBuilder.reflectionHashCode(x, new String[]{"two", "three"}));
 
-        assertEquals(17, HashCodeBuilder.reflectionHashCode(x, new String[] {"one", "two", "three"}));
-        assertEquals(17, HashCodeBuilder.reflectionHashCode(x, new String[] {"one", "two", "three", "xxx"}));
+        assertEquals(17, HashCodeBuilder.reflectionHashCode(x, new String[]{"one", "two", "three"}));
+        assertEquals(17, HashCodeBuilder.reflectionHashCode(x, new String[]{"one", "two", "three", "xxx"}));
     }
 
     static class TestObjectWithMultipleFields {
         private int one = 0;
+
         private int two = 0;
+
         private int three = 0;
 
         public TestObjectWithMultipleFields(int one, int two, int three) {
@@ -492,7 +505,7 @@
             this.three = three;
         }
     }
-    
+
     /**
      * Test Objects pointing to each other.
      */
@@ -518,8 +531,8 @@
         // org.apache.commons.lang.builder.HashCodeBuilderTest$ReflectionTestCycleA.hashCode(HashCodeBuilderTest.java:42)
         // at org.apache.commons.lang.builder.HashCodeBuilder.append(HashCodeBuilder.java:422)
 
-        // a.hashCode();
-        // b.hashCode();
+        a.hashCode();
+        b.hashCode();
     }
 
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Mime
View raw message