You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2023/07/12 12:44:48 UTC

[commons-lang] 04/04: Throw IllegalArgumentException instead of InternalError in the builder package

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git

commit 2e3feda04337baa483bc26b66f238161dc6c97ac
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed Jul 12 08:44:38 2023 -0400

    Throw IllegalArgumentException instead of InternalError in the builder
    package
---
 src/changes/changes.xml                            |   1 +
 .../commons/lang3/builder/CompareToBuilder.java    | 752 ++++++++++-----------
 .../commons/lang3/builder/EqualsBuilder.java       |  20 +-
 .../commons/lang3/builder/HashCodeBuilder.java     |   9 +-
 .../apache/commons/lang3/builder/Reflection.java   |  44 ++
 .../lang3/builder/ReflectionDiffBuilder.java       |   7 +-
 .../lang3/builder/ReflectionToStringBuilder.java   |  14 +-
 7 files changed, 434 insertions(+), 413 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 26d7be48c..da3522029 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -123,6 +123,7 @@ The <action> type attribute can be add,update,fix,remove.
     <action                   type="fix" dev="ggregory" due-to="Dimitrios Efthymiou">Update Javadoc for the insert methods in ArrayUtils #1078.</action>
     <action                   type="fix" dev="ggregory" due-to="Gary Gregory">Deprecate ExceptionUtils.ExceptionUtils().</action>
     <action issue="LANG-1697" type="fix" dev="ggregory" due-to="Jan Arne Sparka, Gary Gregory">TypeUtils.getRawType() throws a NullPointerException on Wildcard GenericArrayType.</action>
+    <action                   type="fix" dev="ggregory" due-to="Gary Gregory">Throw IllegalArgumentException instead of InternalError in the builder package.</action>    
     <!-- ADD -->
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add GitHub coverage.yml.</action>
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add EnumUtils.getEnumSystemProperty(...).</action>
diff --git a/src/main/java/org/apache/commons/lang3/builder/CompareToBuilder.java b/src/main/java/org/apache/commons/lang3/builder/CompareToBuilder.java
index 38c69e613..3d411bb15 100644
--- a/src/main/java/org/apache/commons/lang3/builder/CompareToBuilder.java
+++ b/src/main/java/org/apache/commons/lang3/builder/CompareToBuilder.java
@@ -97,19 +97,37 @@ import org.apache.commons.lang3.ObjectUtils;
 public class CompareToBuilder implements Builder<Integer> {
 
     /**
-     * Current state of the comparison as appended fields are checked.
-     */
-    private int comparison;
-
-    /**
-     * Constructor for CompareToBuilder.
+     * Appends to {@code builder} the comparison of {@code lhs}
+     * to {@code rhs} using the fields defined in {@code clazz}.
      *
-     * <p>Starts off assuming that the objects are equal. Multiple calls are
-     * then made to the various append methods, followed by a call to
-     * {@link #toComparison} to get the result.</p>
+     * @param lhs  left-hand object
+     * @param rhs  right-hand object
+     * @param clazz  {@link Class} that defines fields to be compared
+     * @param builder  {@link CompareToBuilder} to append to
+     * @param useTransients  whether to compare transient fields
+     * @param excludeFields  fields to exclude
      */
-    public CompareToBuilder() {
-        comparison = 0;
+    private static void reflectionAppend(
+        final Object lhs,
+        final Object rhs,
+        final Class<?> clazz,
+        final CompareToBuilder builder,
+        final boolean useTransients,
+        final String[] excludeFields) {
+
+        final Field[] fields = clazz.getDeclaredFields();
+        AccessibleObject.setAccessible(fields, true);
+        for (int i = 0; i < fields.length && builder.comparison == 0; i++) {
+            final Field field = fields[i];
+            if (!ArrayUtils.contains(excludeFields, field.getName())
+                && !field.getName().contains("$")
+                && (useTransients || !Modifier.isTransient(field.getModifiers()))
+                && !Modifier.isStatic(field.getModifiers())) {
+                // IllegalAccessException can't happen. Would get a Security exception instead.
+                // Throw a runtime exception in case the impossible happens.
+                builder.append(Reflection.getUnchecked(field, lhs), Reflection.getUnchecked(field, rhs));
+            }
+        }
     }
 
     /**
@@ -183,10 +201,11 @@ public class CompareToBuilder implements Builder<Integer> {
      *
      * <ul>
      * <li>Static fields will not be compared</li>
-     * <li>If {@code compareTransients} is {@code true},
+     * <li>If the {@code compareTransients} is {@code true},
      *     compares transient members.  Otherwise ignores them, as they
      *     are likely derived fields.</li>
-     * <li>Superclass fields will be compared</li>
+     * <li>Compares superclass fields up to and including {@code reflectUpToClass}.
+     *     If {@code reflectUpToClass} is {@code null}, compares all superclass fields.</li>
      * </ul>
      *
      * <p>If both {@code lhs} and {@code rhs} are {@code null},
@@ -194,17 +213,41 @@ public class CompareToBuilder implements Builder<Integer> {
      *
      * @param lhs  left-hand object
      * @param rhs  right-hand object
-     * @param excludeFields  Collection of String fields to exclude
+     * @param compareTransients  whether to compare transient fields
+     * @param reflectUpToClass  last superclass for which fields are compared
+     * @param excludeFields  fields to exclude
      * @return a negative integer, zero, or a positive integer as {@code lhs}
      *  is less than, equal to, or greater than {@code rhs}
      * @throws NullPointerException  if either {@code lhs} or {@code rhs}
      *  (but not both) is {@code null}
      * @throws ClassCastException  if {@code rhs} is not assignment-compatible
      *  with {@code lhs}
-     * @since 2.2
+     * @since 2.2 (2.0 as {@code reflectionCompare(Object, Object, boolean, Class)})
      */
-    public static int reflectionCompare(final Object lhs, final Object rhs, final Collection<String> excludeFields) {
-        return reflectionCompare(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
+    public static int reflectionCompare(
+        final Object lhs,
+        final Object rhs,
+        final boolean compareTransients,
+        final Class<?> reflectUpToClass,
+        final String... excludeFields) {
+
+        if (lhs == rhs) {
+            return 0;
+        }
+        Objects.requireNonNull(lhs, "lhs");
+        Objects.requireNonNull(rhs, "rhs");
+
+        Class<?> lhsClazz = lhs.getClass();
+        if (!lhsClazz.isInstance(rhs)) {
+            throw new ClassCastException();
+        }
+        final CompareToBuilder compareToBuilder = new CompareToBuilder();
+        reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
+        while (lhsClazz.getSuperclass() != null && lhsClazz != reflectUpToClass) {
+            lhsClazz = lhsClazz.getSuperclass();
+            reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
+        }
+        return compareToBuilder.toComparison();
     }
 
     /**
@@ -227,7 +270,7 @@ public class CompareToBuilder implements Builder<Integer> {
      *
      * @param lhs  left-hand object
      * @param rhs  right-hand object
-     * @param excludeFields  array of fields to exclude
+     * @param excludeFields  Collection of String fields to exclude
      * @return a negative integer, zero, or a positive integer as {@code lhs}
      *  is less than, equal to, or greater than {@code rhs}
      * @throws NullPointerException  if either {@code lhs} or {@code rhs}
@@ -236,8 +279,8 @@ public class CompareToBuilder implements Builder<Integer> {
      *  with {@code lhs}
      * @since 2.2
      */
-    public static int reflectionCompare(final Object lhs, final Object rhs, final String... excludeFields) {
-        return reflectionCompare(lhs, rhs, false, null, excludeFields);
+    public static int reflectionCompare(final Object lhs, final Object rhs, final Collection<String> excludeFields) {
+        return reflectionCompare(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
     }
 
     /**
@@ -249,11 +292,10 @@ public class CompareToBuilder implements Builder<Integer> {
      *
      * <ul>
      * <li>Static fields will not be compared</li>
-     * <li>If the {@code compareTransients} is {@code true},
+     * <li>If {@code compareTransients} is {@code true},
      *     compares transient members.  Otherwise ignores them, as they
      *     are likely derived fields.</li>
-     * <li>Compares superclass fields up to and including {@code reflectUpToClass}.
-     *     If {@code reflectUpToClass} is {@code null}, compares all superclass fields.</li>
+     * <li>Superclass fields will be compared</li>
      * </ul>
      *
      * <p>If both {@code lhs} and {@code rhs} are {@code null},
@@ -261,146 +303,74 @@ public class CompareToBuilder implements Builder<Integer> {
      *
      * @param lhs  left-hand object
      * @param rhs  right-hand object
-     * @param compareTransients  whether to compare transient fields
-     * @param reflectUpToClass  last superclass for which fields are compared
-     * @param excludeFields  fields to exclude
+     * @param excludeFields  array of fields to exclude
      * @return a negative integer, zero, or a positive integer as {@code lhs}
      *  is less than, equal to, or greater than {@code rhs}
      * @throws NullPointerException  if either {@code lhs} or {@code rhs}
      *  (but not both) is {@code null}
      * @throws ClassCastException  if {@code rhs} is not assignment-compatible
      *  with {@code lhs}
-     * @since 2.2 (2.0 as {@code reflectionCompare(Object, Object, boolean, Class)})
+     * @since 2.2
      */
-    public static int reflectionCompare(
-        final Object lhs,
-        final Object rhs,
-        final boolean compareTransients,
-        final Class<?> reflectUpToClass,
-        final String... excludeFields) {
-
-        if (lhs == rhs) {
-            return 0;
-        }
-        Objects.requireNonNull(lhs, "lhs");
-        Objects.requireNonNull(rhs, "rhs");
-
-        Class<?> lhsClazz = lhs.getClass();
-        if (!lhsClazz.isInstance(rhs)) {
-            throw new ClassCastException();
-        }
-        final CompareToBuilder compareToBuilder = new CompareToBuilder();
-        reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
-        while (lhsClazz.getSuperclass() != null && lhsClazz != reflectUpToClass) {
-            lhsClazz = lhsClazz.getSuperclass();
-            reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
-        }
-        return compareToBuilder.toComparison();
+    public static int reflectionCompare(final Object lhs, final Object rhs, final String... excludeFields) {
+        return reflectionCompare(lhs, rhs, false, null, excludeFields);
     }
 
     /**
-     * Appends to {@code builder} the comparison of {@code lhs}
-     * to {@code rhs} using the fields defined in {@code clazz}.
-     *
-     * @param lhs  left-hand object
-     * @param rhs  right-hand object
-     * @param clazz  {@link Class} that defines fields to be compared
-     * @param builder  {@link CompareToBuilder} to append to
-     * @param useTransients  whether to compare transient fields
-     * @param excludeFields  fields to exclude
+     * Current state of the comparison as appended fields are checked.
      */
-    private static void reflectionAppend(
-        final Object lhs,
-        final Object rhs,
-        final Class<?> clazz,
-        final CompareToBuilder builder,
-        final boolean useTransients,
-        final String[] excludeFields) {
-
-        final Field[] fields = clazz.getDeclaredFields();
-        AccessibleObject.setAccessible(fields, true);
-        for (int i = 0; i < fields.length && builder.comparison == 0; i++) {
-            final Field field = fields[i];
-            if (!ArrayUtils.contains(excludeFields, field.getName())
-                && !field.getName().contains("$")
-                && (useTransients || !Modifier.isTransient(field.getModifiers()))
-                && !Modifier.isStatic(field.getModifiers())) {
-                try {
-                    builder.append(field.get(lhs), field.get(rhs));
-                } catch (final 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");
-                }
-            }
-        }
-    }
+    private int comparison;
 
     /**
-     * Appends to the {@code builder} the {@code compareTo(Object)}
-     * result of the superclass.
+     * Constructor for CompareToBuilder.
      *
-     * @param superCompareTo  result of calling {@code super.compareTo(Object)}
-     * @return this
-     * @since 2.0
+     * <p>Starts off assuming that the objects are equal. Multiple calls are
+     * then made to the various append methods, followed by a call to
+     * {@link #toComparison} to get the result.</p>
      */
-    public CompareToBuilder appendSuper(final int superCompareTo) {
-        if (comparison != 0) {
-            return this;
-        }
-        comparison = superCompareTo;
-        return this;
+    public CompareToBuilder() {
+        comparison = 0;
     }
 
     /**
      * Appends to the {@code builder} the comparison of
-     * two {@link Object}s.
-     *
-     * <ol>
-     * <li>Check if {@code lhs == rhs}</li>
-     * <li>Check if either {@code lhs} or {@code rhs} is {@code null},
-     *     a {@code null} object is less than a non-{@code null} object</li>
-     * <li>Check the object contents</li>
-     * </ol>
-     *
-     * <p>{@code lhs} must either be an array or implement {@link Comparable}.</p>
+     * two {@code booleans}s.
      *
-     * @param lhs  left-hand object
-     * @param rhs  right-hand object
+     * @param lhs  left-hand value
+     * @param rhs  right-hand value
      * @return this
-     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
-     *  with {@code lhs}
-     */
-    public CompareToBuilder append(final Object lhs, final Object rhs) {
-        return append(lhs, rhs, null);
+      */
+    public CompareToBuilder append(final boolean lhs, final boolean rhs) {
+        if (comparison != 0) {
+            return this;
+        }
+        if (lhs == rhs) {
+            return this;
+        }
+        if (lhs) {
+            comparison = 1;
+        } else {
+            comparison = -1;
+        }
+        return this;
     }
 
     /**
-     * Appends to the {@code builder} the comparison of
-     * two {@link Object}s.
+     * Appends to the {@code builder} the deep comparison of
+     * two {@code boolean} arrays.
      *
      * <ol>
-     * <li>Check if {@code lhs == rhs}</li>
-     * <li>Check if either {@code lhs} or {@code rhs} is {@code null},
-     *     a {@code null} object is less than a non-{@code null} object</li>
-     * <li>Check the object contents</li>
+     *  <li>Check if arrays are the same using {@code ==}</li>
+     *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
+     *  <li>Check array length, a shorter length array is less than a longer length array</li>
+     *  <li>Check array contents element by element using {@link #append(boolean, boolean)}</li>
      * </ol>
      *
-     * <p>If {@code lhs} is an array, array comparison methods will be used.
-     * Otherwise {@code comparator} will be used to compare the objects.
-     * If {@code comparator} is {@code null}, {@code lhs} must
-     * implement {@link Comparable} instead.</p>
-     *
-     * @param lhs  left-hand object
-     * @param rhs  right-hand object
-     * @param comparator  {@link Comparator} used to compare the objects,
-     *  {@code null} means treat lhs as {@link Comparable}
+     * @param lhs  left-hand array
+     * @param rhs  right-hand array
      * @return this
-     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
-     *  with {@code lhs}
-     * @since 2.0
      */
-    public CompareToBuilder append(final Object lhs, final Object rhs, final Comparator<?> comparator) {
+    public CompareToBuilder append(final boolean[] lhs, final boolean[] rhs) {
         if (comparison != 0) {
             return this;
         }
@@ -415,94 +385,69 @@ public class CompareToBuilder implements Builder<Integer> {
             comparison = 1;
             return this;
         }
-        if (ObjectUtils.isArray(lhs)) {
-            // factor out array case in order to keep method small enough to be inlined
-            appendArray(lhs, rhs, comparator);
-        } else // the simple case, not an array, just test the element
-        if (comparator == null) {
-            @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
-            final Comparable<Object> comparable = (Comparable<Object>) lhs;
-            comparison = comparable.compareTo(rhs);
-        } else {
-            @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
-            final Comparator<Object> comparator2 = (Comparator<Object>) comparator;
-            comparison = comparator2.compare(lhs, rhs);
+        if (lhs.length != rhs.length) {
+            comparison = lhs.length < rhs.length ? -1 : 1;
+            return this;
         }
-        return this;
-    }
-
-    private void appendArray(final Object lhs, final Object rhs, final Comparator<?> comparator) {
-        // switch on type of array, to dispatch to the correct handler
-        // handles multidimensional arrays
-        // throws a ClassCastException if rhs is not the correct array type
-        if (lhs instanceof long[]) {
-            append((long[]) lhs, (long[]) rhs);
-        } else if (lhs instanceof int[]) {
-            append((int[]) lhs, (int[]) rhs);
-        } else if (lhs instanceof short[]) {
-            append((short[]) lhs, (short[]) rhs);
-        } else if (lhs instanceof char[]) {
-            append((char[]) lhs, (char[]) rhs);
-        } else if (lhs instanceof byte[]) {
-            append((byte[]) lhs, (byte[]) rhs);
-        } else if (lhs instanceof double[]) {
-            append((double[]) lhs, (double[]) rhs);
-        } else if (lhs instanceof float[]) {
-            append((float[]) lhs, (float[]) rhs);
-        } else if (lhs instanceof boolean[]) {
-            append((boolean[]) lhs, (boolean[]) rhs);
-        } else {
-            // not an array of primitives
-            // throws a ClassCastException if rhs is not an array
-            append((Object[]) lhs, (Object[]) rhs, comparator);
+        for (int i = 0; i < lhs.length && comparison == 0; i++) {
+            append(lhs[i], rhs[i]);
         }
+        return this;
     }
 
     /**
      * Appends to the {@code builder} the comparison of
-     * two {@code long}s.
+     * two {@code byte}s.
      *
      * @param lhs  left-hand value
      * @param rhs  right-hand value
      * @return this
      */
-    public CompareToBuilder append(final long lhs, final long rhs) {
+    public CompareToBuilder append(final byte lhs, final byte rhs) {
         if (comparison != 0) {
             return this;
         }
-        comparison = Long.compare(lhs, rhs);
+        comparison = Byte.compare(lhs, rhs);
         return this;
     }
 
     /**
-     * Appends to the {@code builder} the comparison of
-     * two {@code int}s.
+     * Appends to the {@code builder} the deep comparison of
+     * two {@code byte} arrays.
      *
-     * @param lhs  left-hand value
-     * @param rhs  right-hand value
+     * <ol>
+     *  <li>Check if arrays are the same using {@code ==}</li>
+     *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
+     *  <li>Check array length, a shorter length array is less than a longer length array</li>
+     *  <li>Check array contents element by element using {@link #append(byte, byte)}</li>
+     * </ol>
+     *
+     * @param lhs  left-hand array
+     * @param rhs  right-hand array
      * @return this
      */
-    public CompareToBuilder append(final int lhs, final int rhs) {
+    public CompareToBuilder append(final byte[] lhs, final byte[] rhs) {
         if (comparison != 0) {
             return this;
         }
-        comparison = Integer.compare(lhs, rhs);
-        return this;
-    }
-
-    /**
-     * Appends to the {@code builder} the comparison of
-     * two {@code short}s.
-     *
-     * @param lhs  left-hand value
-     * @param rhs  right-hand value
-     * @return this
-     */
-    public CompareToBuilder append(final short lhs, final short rhs) {
-        if (comparison != 0) {
+        if (lhs == rhs) {
             return this;
         }
-        comparison = Short.compare(lhs, rhs);
+        if (lhs == null) {
+            comparison = -1;
+            return this;
+        }
+        if (rhs == null) {
+            comparison = 1;
+            return this;
+        }
+        if (lhs.length != rhs.length) {
+            comparison = lhs.length < rhs.length ? -1 : 1;
+            return this;
+        }
+        for (int i = 0; i < lhs.length && comparison == 0; i++) {
+            append(lhs[i], rhs[i]);
+        }
         return this;
     }
 
@@ -523,18 +468,42 @@ public class CompareToBuilder implements Builder<Integer> {
     }
 
     /**
-     * Appends to the {@code builder} the comparison of
-     * two {@code byte}s.
+     * Appends to the {@code builder} the deep comparison of
+     * two {@code char} arrays.
      *
-     * @param lhs  left-hand value
-     * @param rhs  right-hand value
+     * <ol>
+     *  <li>Check if arrays are the same using {@code ==}</li>
+     *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
+     *  <li>Check array length, a shorter length array is less than a longer length array</li>
+     *  <li>Check array contents element by element using {@link #append(char, char)}</li>
+     * </ol>
+     *
+     * @param lhs  left-hand array
+     * @param rhs  right-hand array
      * @return this
      */
-    public CompareToBuilder append(final byte lhs, final byte rhs) {
+    public CompareToBuilder append(final char[] lhs, final char[] rhs) {
         if (comparison != 0) {
             return this;
         }
-        comparison = Byte.compare(lhs, rhs);
+        if (lhs == rhs) {
+            return this;
+        }
+        if (lhs == null) {
+            comparison = -1;
+            return this;
+        }
+        if (rhs == null) {
+            comparison = 1;
+            return this;
+        }
+        if (lhs.length != rhs.length) {
+            comparison = lhs.length < rhs.length ? -1 : 1;
+            return this;
+        }
+        for (int i = 0; i < lhs.length && comparison == 0; i++) {
+            append(lhs[i], rhs[i]);
+        }
         return this;
     }
 
@@ -559,98 +528,22 @@ public class CompareToBuilder implements Builder<Integer> {
         return this;
     }
 
-    /**
-     * Appends to the {@code builder} the comparison of
-     * two {@code float}s.
-     *
-     * <p>This handles NaNs, Infinities, and {@code -0.0}.</p>
-     *
-     * <p>It is compatible with the hash code generated by
-     * {@link HashCodeBuilder}.</p>
-     *
-     * @param lhs  left-hand value
-     * @param rhs  right-hand value
-     * @return this
-     */
-    public CompareToBuilder append(final float lhs, final float rhs) {
-        if (comparison != 0) {
-            return this;
-        }
-        comparison = Float.compare(lhs, rhs);
-        return this;
-    }
-
-    /**
-     * Appends to the {@code builder} the comparison of
-     * two {@code booleans}s.
-     *
-     * @param lhs  left-hand value
-     * @param rhs  right-hand value
-     * @return this
-      */
-    public CompareToBuilder append(final boolean lhs, final boolean rhs) {
-        if (comparison != 0) {
-            return this;
-        }
-        if (lhs == rhs) {
-            return this;
-        }
-        if (lhs) {
-            comparison = 1;
-        } else {
-            comparison = -1;
-        }
-        return this;
-    }
-
     /**
      * Appends to the {@code builder} the deep comparison of
-     * two {@link Object} arrays.
-     *
-     * <ol>
-     *  <li>Check if arrays are the same using {@code ==}</li>
-     *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
-     *  <li>Check array length, a short length array is less than a long length array</li>
-     *  <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
-     * </ol>
-     *
-     * <p>This method will also will be called for the top level of multi-dimensional,
-     * ragged, and multi-typed arrays.</p>
-     *
-     * @param lhs  left-hand array
-     * @param rhs  right-hand array
-     * @return this
-     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
-     *  with {@code lhs}
-     */
-    public CompareToBuilder append(final Object[] lhs, final Object[] rhs) {
-        return append(lhs, rhs, null);
-    }
-
-    /**
-     * Appends to the {@code builder} the deep comparison of
-     * two {@link Object} arrays.
+     * two {@code double} arrays.
      *
      * <ol>
      *  <li>Check if arrays are the same using {@code ==}</li>
      *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
-     *  <li>Check array length, a short length array is less than a long length array</li>
-     *  <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
+     *  <li>Check array length, a shorter length array is less than a longer length array</li>
+     *  <li>Check array contents element by element using {@link #append(double, double)}</li>
      * </ol>
      *
-     * <p>This method will also will be called for the top level of multi-dimensional,
-     * ragged, and multi-typed arrays.</p>
-     *
      * @param lhs  left-hand array
      * @param rhs  right-hand array
-     * @param comparator  {@link Comparator} to use to compare the array elements,
-     *  {@code null} means to treat {@code lhs} elements as {@link Comparable}.
      * @return this
-     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
-     *  with {@code lhs}
-     * @since 2.0
      */
-    public CompareToBuilder append(final Object[] lhs, final Object[] rhs, final Comparator<?> comparator) {
+    public CompareToBuilder append(final double[] lhs, final double[] rhs) {
         if (comparison != 0) {
             return this;
         }
@@ -670,67 +563,48 @@ public class CompareToBuilder implements Builder<Integer> {
             return this;
         }
         for (int i = 0; i < lhs.length && comparison == 0; i++) {
-            append(lhs[i], rhs[i], comparator);
+            append(lhs[i], rhs[i]);
         }
         return this;
     }
 
     /**
-     * Appends to the {@code builder} the deep comparison of
-     * two {@code long} arrays.
+     * Appends to the {@code builder} the comparison of
+     * two {@code float}s.
      *
-     * <ol>
-     *  <li>Check if arrays are the same using {@code ==}</li>
-     *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
-     *  <li>Check array length, a shorter length array is less than a longer length array</li>
-     *  <li>Check array contents element by element using {@link #append(long, long)}</li>
-     * </ol>
+     * <p>This handles NaNs, Infinities, and {@code -0.0}.</p>
      *
-     * @param lhs  left-hand array
-     * @param rhs  right-hand array
+     * <p>It is compatible with the hash code generated by
+     * {@link HashCodeBuilder}.</p>
+     *
+     * @param lhs  left-hand value
+     * @param rhs  right-hand value
      * @return this
      */
-    public CompareToBuilder append(final long[] lhs, final long[] rhs) {
+    public CompareToBuilder append(final float lhs, final float rhs) {
         if (comparison != 0) {
-            return this;
-        }
-        if (lhs == rhs) {
-            return this;
-        }
-        if (lhs == null) {
-            comparison = -1;
-            return this;
-        }
-        if (rhs == null) {
-            comparison = 1;
-            return this;
-        }
-        if (lhs.length != rhs.length) {
-            comparison = lhs.length < rhs.length ? -1 : 1;
-            return this;
-        }
-        for (int i = 0; i < lhs.length && comparison == 0; i++) {
-            append(lhs[i], rhs[i]);
+            return this;
         }
+        comparison = Float.compare(lhs, rhs);
         return this;
     }
 
     /**
      * Appends to the {@code builder} the deep comparison of
-     * two {@code int} arrays.
+     * two {@code float} arrays.
      *
      * <ol>
      *  <li>Check if arrays are the same using {@code ==}</li>
      *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
      *  <li>Check array length, a shorter length array is less than a longer length array</li>
-     *  <li>Check array contents element by element using {@link #append(int, int)}</li>
+     *  <li>Check array contents element by element using {@link #append(float, float)}</li>
      * </ol>
      *
      * @param lhs  left-hand array
      * @param rhs  right-hand array
      * @return this
      */
-    public CompareToBuilder append(final int[] lhs, final int[] rhs) {
+    public CompareToBuilder append(final float[] lhs, final float[] rhs) {
         if (comparison != 0) {
             return this;
         }
@@ -755,22 +629,38 @@ public class CompareToBuilder implements Builder<Integer> {
         return this;
     }
 
+    /**
+     * Appends to the {@code builder} the comparison of
+     * two {@code int}s.
+     *
+     * @param lhs  left-hand value
+     * @param rhs  right-hand value
+     * @return this
+     */
+    public CompareToBuilder append(final int lhs, final int rhs) {
+        if (comparison != 0) {
+            return this;
+        }
+        comparison = Integer.compare(lhs, rhs);
+        return this;
+    }
+
     /**
      * Appends to the {@code builder} the deep comparison of
-     * two {@code short} arrays.
+     * two {@code int} arrays.
      *
      * <ol>
      *  <li>Check if arrays are the same using {@code ==}</li>
      *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
      *  <li>Check array length, a shorter length array is less than a longer length array</li>
-     *  <li>Check array contents element by element using {@link #append(short, short)}</li>
+     *  <li>Check array contents element by element using {@link #append(int, int)}</li>
      * </ol>
      *
      * @param lhs  left-hand array
      * @param rhs  right-hand array
      * @return this
      */
-    public CompareToBuilder append(final short[] lhs, final short[] rhs) {
+    public CompareToBuilder append(final int[] lhs, final int[] rhs) {
         if (comparison != 0) {
             return this;
         }
@@ -795,22 +685,38 @@ public class CompareToBuilder implements Builder<Integer> {
         return this;
     }
 
+    /**
+     * Appends to the {@code builder} the comparison of
+     * two {@code long}s.
+     *
+     * @param lhs  left-hand value
+     * @param rhs  right-hand value
+     * @return this
+     */
+    public CompareToBuilder append(final long lhs, final long rhs) {
+        if (comparison != 0) {
+            return this;
+        }
+        comparison = Long.compare(lhs, rhs);
+        return this;
+    }
+
     /**
      * Appends to the {@code builder} the deep comparison of
-     * two {@code char} arrays.
+     * two {@code long} arrays.
      *
      * <ol>
      *  <li>Check if arrays are the same using {@code ==}</li>
      *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
      *  <li>Check array length, a shorter length array is less than a longer length array</li>
-     *  <li>Check array contents element by element using {@link #append(char, char)}</li>
+     *  <li>Check array contents element by element using {@link #append(long, long)}</li>
      * </ol>
      *
      * @param lhs  left-hand array
      * @param rhs  right-hand array
      * @return this
      */
-    public CompareToBuilder append(final char[] lhs, final char[] rhs) {
+    public CompareToBuilder append(final long[] lhs, final long[] rhs) {
         if (comparison != 0) {
             return this;
         }
@@ -836,21 +742,54 @@ public class CompareToBuilder implements Builder<Integer> {
     }
 
     /**
-     * Appends to the {@code builder} the deep comparison of
-     * two {@code byte} arrays.
+     * Appends to the {@code builder} the comparison of
+     * two {@link Object}s.
      *
      * <ol>
-     *  <li>Check if arrays are the same using {@code ==}</li>
-     *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
-     *  <li>Check array length, a shorter length array is less than a longer length array</li>
-     *  <li>Check array contents element by element using {@link #append(byte, byte)}</li>
+     * <li>Check if {@code lhs == rhs}</li>
+     * <li>Check if either {@code lhs} or {@code rhs} is {@code null},
+     *     a {@code null} object is less than a non-{@code null} object</li>
+     * <li>Check the object contents</li>
      * </ol>
      *
-     * @param lhs  left-hand array
-     * @param rhs  right-hand array
+     * <p>{@code lhs} must either be an array or implement {@link Comparable}.</p>
+     *
+     * @param lhs  left-hand object
+     * @param rhs  right-hand object
      * @return this
+     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
+     *  with {@code lhs}
      */
-    public CompareToBuilder append(final byte[] lhs, final byte[] rhs) {
+    public CompareToBuilder append(final Object lhs, final Object rhs) {
+        return append(lhs, rhs, null);
+    }
+
+    /**
+     * Appends to the {@code builder} the comparison of
+     * two {@link Object}s.
+     *
+     * <ol>
+     * <li>Check if {@code lhs == rhs}</li>
+     * <li>Check if either {@code lhs} or {@code rhs} is {@code null},
+     *     a {@code null} object is less than a non-{@code null} object</li>
+     * <li>Check the object contents</li>
+     * </ol>
+     *
+     * <p>If {@code lhs} is an array, array comparison methods will be used.
+     * Otherwise {@code comparator} will be used to compare the objects.
+     * If {@code comparator} is {@code null}, {@code lhs} must
+     * implement {@link Comparable} instead.</p>
+     *
+     * @param lhs  left-hand object
+     * @param rhs  right-hand object
+     * @param comparator  {@link Comparator} used to compare the objects,
+     *  {@code null} means treat lhs as {@link Comparable}
+     * @return this
+     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
+     *  with {@code lhs}
+     * @since 2.0
+     */
+    public CompareToBuilder append(final Object lhs, final Object rhs, final Comparator<?> comparator) {
         if (comparison != 0) {
             return this;
         }
@@ -865,72 +804,70 @@ public class CompareToBuilder implements Builder<Integer> {
             comparison = 1;
             return this;
         }
-        if (lhs.length != rhs.length) {
-            comparison = lhs.length < rhs.length ? -1 : 1;
-            return this;
-        }
-        for (int i = 0; i < lhs.length && comparison == 0; i++) {
-            append(lhs[i], rhs[i]);
+        if (ObjectUtils.isArray(lhs)) {
+            // factor out array case in order to keep method small enough to be inlined
+            appendArray(lhs, rhs, comparator);
+        } else // the simple case, not an array, just test the element
+        if (comparator == null) {
+            @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
+            final Comparable<Object> comparable = (Comparable<Object>) lhs;
+            comparison = comparable.compareTo(rhs);
+        } else {
+            @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
+            final Comparator<Object> comparator2 = (Comparator<Object>) comparator;
+            comparison = comparator2.compare(lhs, rhs);
         }
         return this;
     }
 
     /**
      * Appends to the {@code builder} the deep comparison of
-     * two {@code double} arrays.
+     * two {@link Object} arrays.
      *
      * <ol>
      *  <li>Check if arrays are the same using {@code ==}</li>
      *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
-     *  <li>Check array length, a shorter length array is less than a longer length array</li>
-     *  <li>Check array contents element by element using {@link #append(double, double)}</li>
+     *  <li>Check array length, a short length array is less than a long length array</li>
+     *  <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
      * </ol>
      *
+     * <p>This method will also will be called for the top level of multi-dimensional,
+     * ragged, and multi-typed arrays.</p>
+     *
      * @param lhs  left-hand array
      * @param rhs  right-hand array
      * @return this
+     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
+     *  with {@code lhs}
      */
-    public CompareToBuilder append(final double[] lhs, final double[] rhs) {
-        if (comparison != 0) {
-            return this;
-        }
-        if (lhs == rhs) {
-            return this;
-        }
-        if (lhs == null) {
-            comparison = -1;
-            return this;
-        }
-        if (rhs == null) {
-            comparison = 1;
-            return this;
-        }
-        if (lhs.length != rhs.length) {
-            comparison = lhs.length < rhs.length ? -1 : 1;
-            return this;
-        }
-        for (int i = 0; i < lhs.length && comparison == 0; i++) {
-            append(lhs[i], rhs[i]);
-        }
-        return this;
+    public CompareToBuilder append(final Object[] lhs, final Object[] rhs) {
+        return append(lhs, rhs, null);
     }
 
     /**
      * Appends to the {@code builder} the deep comparison of
-     * two {@code float} arrays.
+     * two {@link Object} arrays.
      *
      * <ol>
      *  <li>Check if arrays are the same using {@code ==}</li>
      *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
-     *  <li>Check array length, a shorter length array is less than a longer length array</li>
-     *  <li>Check array contents element by element using {@link #append(float, float)}</li>
+     *  <li>Check array length, a short length array is less than a long length array</li>
+     *  <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
      * </ol>
      *
+     * <p>This method will also will be called for the top level of multi-dimensional,
+     * ragged, and multi-typed arrays.</p>
+     *
      * @param lhs  left-hand array
      * @param rhs  right-hand array
+     * @param comparator  {@link Comparator} to use to compare the array elements,
+     *  {@code null} means to treat {@code lhs} elements as {@link Comparable}.
      * @return this
+     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
+     *  with {@code lhs}
+     * @since 2.0
      */
-    public CompareToBuilder append(final float[] lhs, final float[] rhs) {
+    public CompareToBuilder append(final Object[] lhs, final Object[] rhs, final Comparator<?> comparator) {
         if (comparison != 0) {
             return this;
         }
@@ -950,27 +887,43 @@ public class CompareToBuilder implements Builder<Integer> {
             return this;
         }
         for (int i = 0; i < lhs.length && comparison == 0; i++) {
-            append(lhs[i], rhs[i]);
+            append(lhs[i], rhs[i], comparator);
+        }
+        return this;
+    }
+
+    /**
+     * Appends to the {@code builder} the comparison of
+     * two {@code short}s.
+     *
+     * @param lhs  left-hand value
+     * @param rhs  right-hand value
+     * @return this
+     */
+    public CompareToBuilder append(final short lhs, final short rhs) {
+        if (comparison != 0) {
+            return this;
         }
+        comparison = Short.compare(lhs, rhs);
         return this;
     }
 
     /**
      * Appends to the {@code builder} the deep comparison of
-     * two {@code boolean} arrays.
+     * two {@code short} arrays.
      *
      * <ol>
      *  <li>Check if arrays are the same using {@code ==}</li>
      *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
      *  <li>Check array length, a shorter length array is less than a longer length array</li>
-     *  <li>Check array contents element by element using {@link #append(boolean, boolean)}</li>
+     *  <li>Check array contents element by element using {@link #append(short, short)}</li>
      * </ol>
      *
      * @param lhs  left-hand array
      * @param rhs  right-hand array
      * @return this
      */
-    public CompareToBuilder append(final boolean[] lhs, final boolean[] rhs) {
+    public CompareToBuilder append(final short[] lhs, final short[] rhs) {
         if (comparison != 0) {
             return this;
         }
@@ -995,17 +948,47 @@ public class CompareToBuilder implements Builder<Integer> {
         return this;
     }
 
+    private void appendArray(final Object lhs, final Object rhs, final Comparator<?> comparator) {
+        // switch on type of array, to dispatch to the correct handler
+        // handles multidimensional arrays
+        // throws a ClassCastException if rhs is not the correct array type
+        if (lhs instanceof long[]) {
+            append((long[]) lhs, (long[]) rhs);
+        } else if (lhs instanceof int[]) {
+            append((int[]) lhs, (int[]) rhs);
+        } else if (lhs instanceof short[]) {
+            append((short[]) lhs, (short[]) rhs);
+        } else if (lhs instanceof char[]) {
+            append((char[]) lhs, (char[]) rhs);
+        } else if (lhs instanceof byte[]) {
+            append((byte[]) lhs, (byte[]) rhs);
+        } else if (lhs instanceof double[]) {
+            append((double[]) lhs, (double[]) rhs);
+        } else if (lhs instanceof float[]) {
+            append((float[]) lhs, (float[]) rhs);
+        } else if (lhs instanceof boolean[]) {
+            append((boolean[]) lhs, (boolean[]) rhs);
+        } else {
+            // not an array of primitives
+            // throws a ClassCastException if rhs is not an array
+            append((Object[]) lhs, (Object[]) rhs, comparator);
+        }
+    }
+
     /**
-     * Returns a negative integer, a positive integer, or zero as
-     * the {@code builder} has judged the "left-hand" side
-     * as less than, greater than, or equal to the "right-hand"
-     * side.
+     * Appends to the {@code builder} the {@code compareTo(Object)}
+     * result of the superclass.
      *
-     * @return final comparison result
-     * @see #build()
+     * @param superCompareTo  result of calling {@code super.compareTo(Object)}
+     * @return this
+     * @since 2.0
      */
-    public int toComparison() {
-        return comparison;
+    public CompareToBuilder appendSuper(final int superCompareTo) {
+        if (comparison != 0) {
+            return this;
+        }
+        comparison = superCompareTo;
+        return this;
     }
 
     /**
@@ -1022,5 +1005,18 @@ public class CompareToBuilder implements Builder<Integer> {
     public Integer build() {
         return Integer.valueOf(toComparison());
     }
+
+    /**
+     * Returns a negative integer, a positive integer, or zero as
+     * the {@code builder} has judged the "left-hand" side
+     * as less than, greater than, or equal to the "right-hand"
+     * side.
+     *
+     * @return final comparison result
+     * @see #build()
+     */
+    public int toComparison() {
+        return comparison;
+    }
 }
 
diff --git a/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java b/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java
index 6f843ca9e..b34e9d390 100644
--- a/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java
+++ b/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java
@@ -561,19 +561,13 @@ public class EqualsBuilder implements Builder<Boolean> {
             final Field[] fields = clazz.getDeclaredFields();
             AccessibleObject.setAccessible(fields, true);
             for (int i = 0; i < fields.length && isEquals; i++) {
-                final Field f = fields[i];
-                if (!ArrayUtils.contains(excludeFields, f.getName())
-                    && !f.getName().contains("$")
-                    && (testTransients || !Modifier.isTransient(f.getModifiers()))
-                    && !Modifier.isStatic(f.getModifiers())
-                    && !f.isAnnotationPresent(EqualsExclude.class)) {
-                    try {
-                        append(f.get(lhs), f.get(rhs));
-                    } catch (final 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");
-                    }
+                final Field field = fields[i];
+                if (!ArrayUtils.contains(excludeFields, field.getName())
+                    && !field.getName().contains("$")
+                    && (testTransients || !Modifier.isTransient(field.getModifiers()))
+                    && !Modifier.isStatic(field.getModifiers())
+                    && !field.isAnnotationPresent(EqualsExclude.class)) {
+                    append(Reflection.getUnchecked(field, lhs), Reflection.getUnchecked(field, rhs));
                 }
             }
         } finally {
diff --git a/src/main/java/org/apache/commons/lang3/builder/HashCodeBuilder.java b/src/main/java/org/apache/commons/lang3/builder/HashCodeBuilder.java
index 9c86c76f7..afe59a8bd 100644
--- a/src/main/java/org/apache/commons/lang3/builder/HashCodeBuilder.java
+++ b/src/main/java/org/apache/commons/lang3/builder/HashCodeBuilder.java
@@ -191,14 +191,7 @@ public class HashCodeBuilder implements Builder<Integer> {
                     && (useTransients || !Modifier.isTransient(field.getModifiers()))
                     && !Modifier.isStatic(field.getModifiers())
                     && !field.isAnnotationPresent(HashCodeExclude.class)) {
-                    try {
-                        final Object fieldValue = field.get(object);
-                        builder.append(fieldValue);
-                    } catch (final 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");
-                    }
+                    builder.append(Reflection.getUnchecked(field, object));
                 }
             }
         } finally {
diff --git a/src/main/java/org/apache/commons/lang3/builder/Reflection.java b/src/main/java/org/apache/commons/lang3/builder/Reflection.java
new file mode 100644
index 000000000..119bfe9b2
--- /dev/null
+++ b/src/main/java/org/apache/commons/lang3/builder/Reflection.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.lang3.builder;
+
+import java.lang.reflect.Field;
+import java.util.Objects;
+
+/**
+ * Package-private reflection code.
+ */
+class Reflection {
+
+    /**
+     * Delegates to {@link Field#get(Object)} and rethrows {@link IllegalAccessException} as {@link IllegalArgumentException}.
+     *
+     * @param field The receiver of the get call.
+     * @param obj   The argument of the get call.
+     * @return The result of the get call.
+     * @throws IllegalArgumentException Thrown after catching {@link IllegalAccessException}.
+     */
+    static Object getUnchecked(final Field field, final Object obj) {
+        try {
+            return Objects.requireNonNull(field, "field").get(obj);
+        } catch (IllegalAccessException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/lang3/builder/ReflectionDiffBuilder.java b/src/main/java/org/apache/commons/lang3/builder/ReflectionDiffBuilder.java
index bacc0cb89..a5a8f9496 100644
--- a/src/main/java/org/apache/commons/lang3/builder/ReflectionDiffBuilder.java
+++ b/src/main/java/org/apache/commons/lang3/builder/ReflectionDiffBuilder.java
@@ -151,12 +151,11 @@ public class ReflectionDiffBuilder<T> implements Builder<DiffResult<T>> {
         for (final Field field : FieldUtils.getAllFields(clazz)) {
             if (accept(field)) {
                 try {
-                    diffBuilder.append(field.getName(), FieldUtils.readField(field, left, true),
-                            FieldUtils.readField(field, right, true));
-                } catch (final IllegalAccessException ex) {
+                    diffBuilder.append(field.getName(), FieldUtils.readField(field, left, true), FieldUtils.readField(field, right, true));
+                } catch (final 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: " + ex.getMessage());
+                    throw new IllegalArgumentException("Unexpected IllegalAccessException: " + e.getMessage(), e);
                 }
             }
         }
diff --git a/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java b/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java
index 8bb0d0e10..d6413681b 100644
--- a/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java
+++ b/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java
@@ -657,16 +657,10 @@ public class ReflectionToStringBuilder extends ToStringBuilder {
         for (final Field field : fields) {
             final String fieldName = field.getName();
             if (this.accept(field)) {
-                try {
-                    // Warning: Field.get(Object) creates wrappers objects for primitive types.
-                    final Object fieldValue = this.getValue(field);
-                    if (!excludeNullValues || fieldValue != null) {
-                        this.append(fieldName, fieldValue, !field.isAnnotationPresent(ToStringSummary.class));
-                    }
-                } catch (final IllegalAccessException ex) {
-                    // this can't happen. Would get a Security exception instead
-                    // throw a runtime exception in case the impossible happens.
-                    throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
+                // Warning: Field.get(Object) creates wrappers objects for primitive types.
+                final Object fieldValue = Reflection.getUnchecked(field, getObject());
+                if (!excludeNullValues || fieldValue != null) {
+                    this.append(fieldName, fieldValue, !field.isAnnotationPresent(ToStringSummary.class));
                 }
             }
         }


Re: [commons-lang] 04/04: Throw IllegalArgumentException instead of InternalError in the builder package

Posted by Gary Gregory <ga...@gmail.com>.
There must be something messed up with EOLs despite my git autocrlf
settings... sorry about that.

Gary

On Wed, Jul 12, 2023, 08:51 Gilles Sadowski <gi...@gmail.com> wrote:

> Hi.
>
> Le mer. 12 juil. 2023 à 14:44, <gg...@apache.org> a écrit :
> >
> > This is an automated email from the ASF dual-hosted git repository.
> >
> > ggregory pushed a commit to branch master
> > in repository https://gitbox.apache.org/repos/asf/commons-lang.git
> >
> > commit 2e3feda04337baa483bc26b66f238161dc6c97ac
> > Author: Gary Gregory <ga...@gmail.com>
> > AuthorDate: Wed Jul 12 08:44:38 2023 -0400
> >
> >     Throw IllegalArgumentException instead of InternalError in the
> builder
> >     package
>
> The diff below contains a bunch of (formatting ?) changes that
> have nothing to do with this commit message.
>
> Regards,
> Gilles
>
> > ---
> >  src/changes/changes.xml                            |   1 +
> >  .../commons/lang3/builder/CompareToBuilder.java    | 752
> ++++++++++-----------
> >  .../commons/lang3/builder/EqualsBuilder.java       |  20 +-
> >  .../commons/lang3/builder/HashCodeBuilder.java     |   9 +-
> >  .../apache/commons/lang3/builder/Reflection.java   |  44 ++
> >  .../lang3/builder/ReflectionDiffBuilder.java       |   7 +-
> >  .../lang3/builder/ReflectionToStringBuilder.java   |  14 +-
> >  7 files changed, 434 insertions(+), 413 deletions(-)
> >
> > diff --git a/src/changes/changes.xml b/src/changes/changes.xml
> > index 26d7be48c..da3522029 100644
> > --- a/src/changes/changes.xml
> > +++ b/src/changes/changes.xml
> > @@ -123,6 +123,7 @@ The <action> type attribute can be
> add,update,fix,remove.
> >      <action                   type="fix" dev="ggregory"
> due-to="Dimitrios Efthymiou">Update Javadoc for the insert methods in
> ArrayUtils #1078.</action>
> >      <action                   type="fix" dev="ggregory" due-to="Gary
> Gregory">Deprecate ExceptionUtils.ExceptionUtils().</action>
> >      <action issue="LANG-1697" type="fix" dev="ggregory" due-to="Jan
> Arne Sparka, Gary Gregory">TypeUtils.getRawType() throws a
> NullPointerException on Wildcard GenericArrayType.</action>
> > +    <action                   type="fix" dev="ggregory" due-to="Gary
> Gregory">Throw IllegalArgumentException instead of InternalError in the
> builder package.</action>
> >      <!-- ADD -->
> >      <action                   type="add" dev="ggregory" due-to="Gary
> Gregory">Add GitHub coverage.yml.</action>
> >      <action                   type="add" dev="ggregory" due-to="Gary
> Gregory">Add EnumUtils.getEnumSystemProperty(...).</action>
> > diff --git
> a/src/main/java/org/apache/commons/lang3/builder/CompareToBuilder.java
> b/src/main/java/org/apache/commons/lang3/builder/CompareToBuilder.java
> > index 38c69e613..3d411bb15 100644
> > ---
> a/src/main/java/org/apache/commons/lang3/builder/CompareToBuilder.java
> > +++
> b/src/main/java/org/apache/commons/lang3/builder/CompareToBuilder.java
> > @@ -97,19 +97,37 @@ import org.apache.commons.lang3.ObjectUtils;
> >  public class CompareToBuilder implements Builder<Integer> {
> >
> >      /**
> > -     * Current state of the comparison as appended fields are checked.
> > -     */
> > -    private int comparison;
> > -
> > -    /**
> > -     * Constructor for CompareToBuilder.
> > +     * Appends to {@code builder} the comparison of {@code lhs}
> > +     * to {@code rhs} using the fields defined in {@code clazz}.
> >       *
> > -     * <p>Starts off assuming that the objects are equal. Multiple
> calls are
> > -     * then made to the various append methods, followed by a call to
> > -     * {@link #toComparison} to get the result.</p>
> > +     * @param lhs  left-hand object
> > +     * @param rhs  right-hand object
> > +     * @param clazz  {@link Class} that defines fields to be compared
> > +     * @param builder  {@link CompareToBuilder} to append to
> > +     * @param useTransients  whether to compare transient fields
> > +     * @param excludeFields  fields to exclude
> >       */
> > -    public CompareToBuilder() {
> > -        comparison = 0;
> > +    private static void reflectionAppend(
> > +        final Object lhs,
> > +        final Object rhs,
> > +        final Class<?> clazz,
> > +        final CompareToBuilder builder,
> > +        final boolean useTransients,
> > +        final String[] excludeFields) {
> > +
> > +        final Field[] fields = clazz.getDeclaredFields();
> > +        AccessibleObject.setAccessible(fields, true);
> > +        for (int i = 0; i < fields.length && builder.comparison == 0;
> i++) {
> > +            final Field field = fields[i];
> > +            if (!ArrayUtils.contains(excludeFields, field.getName())
> > +                && !field.getName().contains("$")
> > +                && (useTransients ||
> !Modifier.isTransient(field.getModifiers()))
> > +                && !Modifier.isStatic(field.getModifiers())) {
> > +                // IllegalAccessException can't happen. Would get a
> Security exception instead.
> > +                // Throw a runtime exception in case the impossible
> happens.
> > +                builder.append(Reflection.getUnchecked(field, lhs),
> Reflection.getUnchecked(field, rhs));
> > +            }
> > +        }
> >      }
> >
> >      /**
> > @@ -183,10 +201,11 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >       *
> >       * <ul>
> >       * <li>Static fields will not be compared</li>
> > -     * <li>If {@code compareTransients} is {@code true},
> > +     * <li>If the {@code compareTransients} is {@code true},
> >       *     compares transient members.  Otherwise ignores them, as they
> >       *     are likely derived fields.</li>
> > -     * <li>Superclass fields will be compared</li>
> > +     * <li>Compares superclass fields up to and including {@code
> reflectUpToClass}.
> > +     *     If {@code reflectUpToClass} is {@code null}, compares all
> superclass fields.</li>
> >       * </ul>
> >       *
> >       * <p>If both {@code lhs} and {@code rhs} are {@code null},
> > @@ -194,17 +213,41 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >       *
> >       * @param lhs  left-hand object
> >       * @param rhs  right-hand object
> > -     * @param excludeFields  Collection of String fields to exclude
> > +     * @param compareTransients  whether to compare transient fields
> > +     * @param reflectUpToClass  last superclass for which fields are
> compared
> > +     * @param excludeFields  fields to exclude
> >       * @return a negative integer, zero, or a positive integer as
> {@code lhs}
> >       *  is less than, equal to, or greater than {@code rhs}
> >       * @throws NullPointerException  if either {@code lhs} or {@code
> rhs}
> >       *  (but not both) is {@code null}
> >       * @throws ClassCastException  if {@code rhs} is not
> assignment-compatible
> >       *  with {@code lhs}
> > -     * @since 2.2
> > +     * @since 2.2 (2.0 as {@code reflectionCompare(Object, Object,
> boolean, Class)})
> >       */
> > -    public static int reflectionCompare(final Object lhs, final Object
> rhs, final Collection<String> excludeFields) {
> > -        return reflectionCompare(lhs, rhs,
> ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
> > +    public static int reflectionCompare(
> > +        final Object lhs,
> > +        final Object rhs,
> > +        final boolean compareTransients,
> > +        final Class<?> reflectUpToClass,
> > +        final String... excludeFields) {
> > +
> > +        if (lhs == rhs) {
> > +            return 0;
> > +        }
> > +        Objects.requireNonNull(lhs, "lhs");
> > +        Objects.requireNonNull(rhs, "rhs");
> > +
> > +        Class<?> lhsClazz = lhs.getClass();
> > +        if (!lhsClazz.isInstance(rhs)) {
> > +            throw new ClassCastException();
> > +        }
> > +        final CompareToBuilder compareToBuilder = new
> CompareToBuilder();
> > +        reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder,
> compareTransients, excludeFields);
> > +        while (lhsClazz.getSuperclass() != null && lhsClazz !=
> reflectUpToClass) {
> > +            lhsClazz = lhsClazz.getSuperclass();
> > +            reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder,
> compareTransients, excludeFields);
> > +        }
> > +        return compareToBuilder.toComparison();
> >      }
> >
> >      /**
> > @@ -227,7 +270,7 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >       *
> >       * @param lhs  left-hand object
> >       * @param rhs  right-hand object
> > -     * @param excludeFields  array of fields to exclude
> > +     * @param excludeFields  Collection of String fields to exclude
> >       * @return a negative integer, zero, or a positive integer as
> {@code lhs}
> >       *  is less than, equal to, or greater than {@code rhs}
> >       * @throws NullPointerException  if either {@code lhs} or {@code
> rhs}
> > @@ -236,8 +279,8 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >       *  with {@code lhs}
> >       * @since 2.2
> >       */
> > -    public static int reflectionCompare(final Object lhs, final Object
> rhs, final String... excludeFields) {
> > -        return reflectionCompare(lhs, rhs, false, null, excludeFields);
> > +    public static int reflectionCompare(final Object lhs, final Object
> rhs, final Collection<String> excludeFields) {
> > +        return reflectionCompare(lhs, rhs,
> ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
> >      }
> >
> >      /**
> > @@ -249,11 +292,10 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >       *
> >       * <ul>
> >       * <li>Static fields will not be compared</li>
> > -     * <li>If the {@code compareTransients} is {@code true},
> > +     * <li>If {@code compareTransients} is {@code true},
> >       *     compares transient members.  Otherwise ignores them, as they
> >       *     are likely derived fields.</li>
> > -     * <li>Compares superclass fields up to and including {@code
> reflectUpToClass}.
> > -     *     If {@code reflectUpToClass} is {@code null}, compares all
> superclass fields.</li>
> > +     * <li>Superclass fields will be compared</li>
> >       * </ul>
> >       *
> >       * <p>If both {@code lhs} and {@code rhs} are {@code null},
> > @@ -261,146 +303,74 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >       *
> >       * @param lhs  left-hand object
> >       * @param rhs  right-hand object
> > -     * @param compareTransients  whether to compare transient fields
> > -     * @param reflectUpToClass  last superclass for which fields are
> compared
> > -     * @param excludeFields  fields to exclude
> > +     * @param excludeFields  array of fields to exclude
> >       * @return a negative integer, zero, or a positive integer as
> {@code lhs}
> >       *  is less than, equal to, or greater than {@code rhs}
> >       * @throws NullPointerException  if either {@code lhs} or {@code
> rhs}
> >       *  (but not both) is {@code null}
> >       * @throws ClassCastException  if {@code rhs} is not
> assignment-compatible
> >       *  with {@code lhs}
> > -     * @since 2.2 (2.0 as {@code reflectionCompare(Object, Object,
> boolean, Class)})
> > +     * @since 2.2
> >       */
> > -    public static int reflectionCompare(
> > -        final Object lhs,
> > -        final Object rhs,
> > -        final boolean compareTransients,
> > -        final Class<?> reflectUpToClass,
> > -        final String... excludeFields) {
> > -
> > -        if (lhs == rhs) {
> > -            return 0;
> > -        }
> > -        Objects.requireNonNull(lhs, "lhs");
> > -        Objects.requireNonNull(rhs, "rhs");
> > -
> > -        Class<?> lhsClazz = lhs.getClass();
> > -        if (!lhsClazz.isInstance(rhs)) {
> > -            throw new ClassCastException();
> > -        }
> > -        final CompareToBuilder compareToBuilder = new
> CompareToBuilder();
> > -        reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder,
> compareTransients, excludeFields);
> > -        while (lhsClazz.getSuperclass() != null && lhsClazz !=
> reflectUpToClass) {
> > -            lhsClazz = lhsClazz.getSuperclass();
> > -            reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder,
> compareTransients, excludeFields);
> > -        }
> > -        return compareToBuilder.toComparison();
> > +    public static int reflectionCompare(final Object lhs, final Object
> rhs, final String... excludeFields) {
> > +        return reflectionCompare(lhs, rhs, false, null, excludeFields);
> >      }
> >
> >      /**
> > -     * Appends to {@code builder} the comparison of {@code lhs}
> > -     * to {@code rhs} using the fields defined in {@code clazz}.
> > -     *
> > -     * @param lhs  left-hand object
> > -     * @param rhs  right-hand object
> > -     * @param clazz  {@link Class} that defines fields to be compared
> > -     * @param builder  {@link CompareToBuilder} to append to
> > -     * @param useTransients  whether to compare transient fields
> > -     * @param excludeFields  fields to exclude
> > +     * Current state of the comparison as appended fields are checked.
> >       */
> > -    private static void reflectionAppend(
> > -        final Object lhs,
> > -        final Object rhs,
> > -        final Class<?> clazz,
> > -        final CompareToBuilder builder,
> > -        final boolean useTransients,
> > -        final String[] excludeFields) {
> > -
> > -        final Field[] fields = clazz.getDeclaredFields();
> > -        AccessibleObject.setAccessible(fields, true);
> > -        for (int i = 0; i < fields.length && builder.comparison == 0;
> i++) {
> > -            final Field field = fields[i];
> > -            if (!ArrayUtils.contains(excludeFields, field.getName())
> > -                && !field.getName().contains("$")
> > -                && (useTransients ||
> !Modifier.isTransient(field.getModifiers()))
> > -                && !Modifier.isStatic(field.getModifiers())) {
> > -                try {
> > -                    builder.append(field.get(lhs), field.get(rhs));
> > -                } catch (final 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");
> > -                }
> > -            }
> > -        }
> > -    }
> > +    private int comparison;
> >
> >      /**
> > -     * Appends to the {@code builder} the {@code compareTo(Object)}
> > -     * result of the superclass.
> > +     * Constructor for CompareToBuilder.
> >       *
> > -     * @param superCompareTo  result of calling {@code
> super.compareTo(Object)}
> > -     * @return this
> > -     * @since 2.0
> > +     * <p>Starts off assuming that the objects are equal. Multiple
> calls are
> > +     * then made to the various append methods, followed by a call to
> > +     * {@link #toComparison} to get the result.</p>
> >       */
> > -    public CompareToBuilder appendSuper(final int superCompareTo) {
> > -        if (comparison != 0) {
> > -            return this;
> > -        }
> > -        comparison = superCompareTo;
> > -        return this;
> > +    public CompareToBuilder() {
> > +        comparison = 0;
> >      }
> >
> >      /**
> >       * Appends to the {@code builder} the comparison of
> > -     * two {@link Object}s.
> > -     *
> > -     * <ol>
> > -     * <li>Check if {@code lhs == rhs}</li>
> > -     * <li>Check if either {@code lhs} or {@code rhs} is {@code null},
> > -     *     a {@code null} object is less than a non-{@code null}
> object</li>
> > -     * <li>Check the object contents</li>
> > -     * </ol>
> > -     *
> > -     * <p>{@code lhs} must either be an array or implement {@link
> Comparable}.</p>
> > +     * two {@code booleans}s.
> >       *
> > -     * @param lhs  left-hand object
> > -     * @param rhs  right-hand object
> > +     * @param lhs  left-hand value
> > +     * @param rhs  right-hand value
> >       * @return this
> > -     * @throws ClassCastException  if {@code rhs} is not
> assignment-compatible
> > -     *  with {@code lhs}
> > -     */
> > -    public CompareToBuilder append(final Object lhs, final Object rhs) {
> > -        return append(lhs, rhs, null);
> > +      */
> > +    public CompareToBuilder append(final boolean lhs, final boolean
> rhs) {
> > +        if (comparison != 0) {
> > +            return this;
> > +        }
> > +        if (lhs == rhs) {
> > +            return this;
> > +        }
> > +        if (lhs) {
> > +            comparison = 1;
> > +        } else {
> > +            comparison = -1;
> > +        }
> > +        return this;
> >      }
> >
> >      /**
> > -     * Appends to the {@code builder} the comparison of
> > -     * two {@link Object}s.
> > +     * Appends to the {@code builder} the deep comparison of
> > +     * two {@code boolean} arrays.
> >       *
> >       * <ol>
> > -     * <li>Check if {@code lhs == rhs}</li>
> > -     * <li>Check if either {@code lhs} or {@code rhs} is {@code null},
> > -     *     a {@code null} object is less than a non-{@code null}
> object</li>
> > -     * <li>Check the object contents</li>
> > +     *  <li>Check if arrays are the same using {@code ==}</li>
> > +     *  <li>Check if for {@code null}, {@code null} is less than
> non-{@code null}</li>
> > +     *  <li>Check array length, a shorter length array is less than a
> longer length array</li>
> > +     *  <li>Check array contents element by element using {@link
> #append(boolean, boolean)}</li>
> >       * </ol>
> >       *
> > -     * <p>If {@code lhs} is an array, array comparison methods will be
> used.
> > -     * Otherwise {@code comparator} will be used to compare the objects.
> > -     * If {@code comparator} is {@code null}, {@code lhs} must
> > -     * implement {@link Comparable} instead.</p>
> > -     *
> > -     * @param lhs  left-hand object
> > -     * @param rhs  right-hand object
> > -     * @param comparator  {@link Comparator} used to compare the
> objects,
> > -     *  {@code null} means treat lhs as {@link Comparable}
> > +     * @param lhs  left-hand array
> > +     * @param rhs  right-hand array
> >       * @return this
> > -     * @throws ClassCastException  if {@code rhs} is not
> assignment-compatible
> > -     *  with {@code lhs}
> > -     * @since 2.0
> >       */
> > -    public CompareToBuilder append(final Object lhs, final Object rhs,
> final Comparator<?> comparator) {
> > +    public CompareToBuilder append(final boolean[] lhs, final boolean[]
> rhs) {
> >          if (comparison != 0) {
> >              return this;
> >          }
> > @@ -415,94 +385,69 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >              comparison = 1;
> >              return this;
> >          }
> > -        if (ObjectUtils.isArray(lhs)) {
> > -            // factor out array case in order to keep method small
> enough to be inlined
> > -            appendArray(lhs, rhs, comparator);
> > -        } else // the simple case, not an array, just test the element
> > -        if (comparator == null) {
> > -            @SuppressWarnings("unchecked") // assume this can be done;
> if not throw CCE as per Javadoc
> > -            final Comparable<Object> comparable = (Comparable<Object>)
> lhs;
> > -            comparison = comparable.compareTo(rhs);
> > -        } else {
> > -            @SuppressWarnings("unchecked") // assume this can be done;
> if not throw CCE as per Javadoc
> > -            final Comparator<Object> comparator2 = (Comparator<Object>)
> comparator;
> > -            comparison = comparator2.compare(lhs, rhs);
> > +        if (lhs.length != rhs.length) {
> > +            comparison = lhs.length < rhs.length ? -1 : 1;
> > +            return this;
> >          }
> > -        return this;
> > -    }
> > -
> > -    private void appendArray(final Object lhs, final Object rhs, final
> Comparator<?> comparator) {
> > -        // switch on type of array, to dispatch to the correct handler
> > -        // handles multidimensional arrays
> > -        // throws a ClassCastException if rhs is not the correct array
> type
> > -        if (lhs instanceof long[]) {
> > -            append((long[]) lhs, (long[]) rhs);
> > -        } else if (lhs instanceof int[]) {
> > -            append((int[]) lhs, (int[]) rhs);
> > -        } else if (lhs instanceof short[]) {
> > -            append((short[]) lhs, (short[]) rhs);
> > -        } else if (lhs instanceof char[]) {
> > -            append((char[]) lhs, (char[]) rhs);
> > -        } else if (lhs instanceof byte[]) {
> > -            append((byte[]) lhs, (byte[]) rhs);
> > -        } else if (lhs instanceof double[]) {
> > -            append((double[]) lhs, (double[]) rhs);
> > -        } else if (lhs instanceof float[]) {
> > -            append((float[]) lhs, (float[]) rhs);
> > -        } else if (lhs instanceof boolean[]) {
> > -            append((boolean[]) lhs, (boolean[]) rhs);
> > -        } else {
> > -            // not an array of primitives
> > -            // throws a ClassCastException if rhs is not an array
> > -            append((Object[]) lhs, (Object[]) rhs, comparator);
> > +        for (int i = 0; i < lhs.length && comparison == 0; i++) {
> > +            append(lhs[i], rhs[i]);
> >          }
> > +        return this;
> >      }
> >
> >      /**
> >       * Appends to the {@code builder} the comparison of
> > -     * two {@code long}s.
> > +     * two {@code byte}s.
> >       *
> >       * @param lhs  left-hand value
> >       * @param rhs  right-hand value
> >       * @return this
> >       */
> > -    public CompareToBuilder append(final long lhs, final long rhs) {
> > +    public CompareToBuilder append(final byte lhs, final byte rhs) {
> >          if (comparison != 0) {
> >              return this;
> >          }
> > -        comparison = Long.compare(lhs, rhs);
> > +        comparison = Byte.compare(lhs, rhs);
> >          return this;
> >      }
> >
> >      /**
> > -     * Appends to the {@code builder} the comparison of
> > -     * two {@code int}s.
> > +     * Appends to the {@code builder} the deep comparison of
> > +     * two {@code byte} arrays.
> >       *
> > -     * @param lhs  left-hand value
> > -     * @param rhs  right-hand value
> > +     * <ol>
> > +     *  <li>Check if arrays are the same using {@code ==}</li>
> > +     *  <li>Check if for {@code null}, {@code null} is less than
> non-{@code null}</li>
> > +     *  <li>Check array length, a shorter length array is less than a
> longer length array</li>
> > +     *  <li>Check array contents element by element using {@link
> #append(byte, byte)}</li>
> > +     * </ol>
> > +     *
> > +     * @param lhs  left-hand array
> > +     * @param rhs  right-hand array
> >       * @return this
> >       */
> > -    public CompareToBuilder append(final int lhs, final int rhs) {
> > +    public CompareToBuilder append(final byte[] lhs, final byte[] rhs) {
> >          if (comparison != 0) {
> >              return this;
> >          }
> > -        comparison = Integer.compare(lhs, rhs);
> > -        return this;
> > -    }
> > -
> > -    /**
> > -     * Appends to the {@code builder} the comparison of
> > -     * two {@code short}s.
> > -     *
> > -     * @param lhs  left-hand value
> > -     * @param rhs  right-hand value
> > -     * @return this
> > -     */
> > -    public CompareToBuilder append(final short lhs, final short rhs) {
> > -        if (comparison != 0) {
> > +        if (lhs == rhs) {
> >              return this;
> >          }
> > -        comparison = Short.compare(lhs, rhs);
> > +        if (lhs == null) {
> > +            comparison = -1;
> > +            return this;
> > +        }
> > +        if (rhs == null) {
> > +            comparison = 1;
> > +            return this;
> > +        }
> > +        if (lhs.length != rhs.length) {
> > +            comparison = lhs.length < rhs.length ? -1 : 1;
> > +            return this;
> > +        }
> > +        for (int i = 0; i < lhs.length && comparison == 0; i++) {
> > +            append(lhs[i], rhs[i]);
> > +        }
> >          return this;
> >      }
> >
> > @@ -523,18 +468,42 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >      }
> >
> >      /**
> > -     * Appends to the {@code builder} the comparison of
> > -     * two {@code byte}s.
> > +     * Appends to the {@code builder} the deep comparison of
> > +     * two {@code char} arrays.
> >       *
> > -     * @param lhs  left-hand value
> > -     * @param rhs  right-hand value
> > +     * <ol>
> > +     *  <li>Check if arrays are the same using {@code ==}</li>
> > +     *  <li>Check if for {@code null}, {@code null} is less than
> non-{@code null}</li>
> > +     *  <li>Check array length, a shorter length array is less than a
> longer length array</li>
> > +     *  <li>Check array contents element by element using {@link
> #append(char, char)}</li>
> > +     * </ol>
> > +     *
> > +     * @param lhs  left-hand array
> > +     * @param rhs  right-hand array
> >       * @return this
> >       */
> > -    public CompareToBuilder append(final byte lhs, final byte rhs) {
> > +    public CompareToBuilder append(final char[] lhs, final char[] rhs) {
> >          if (comparison != 0) {
> >              return this;
> >          }
> > -        comparison = Byte.compare(lhs, rhs);
> > +        if (lhs == rhs) {
> > +            return this;
> > +        }
> > +        if (lhs == null) {
> > +            comparison = -1;
> > +            return this;
> > +        }
> > +        if (rhs == null) {
> > +            comparison = 1;
> > +            return this;
> > +        }
> > +        if (lhs.length != rhs.length) {
> > +            comparison = lhs.length < rhs.length ? -1 : 1;
> > +            return this;
> > +        }
> > +        for (int i = 0; i < lhs.length && comparison == 0; i++) {
> > +            append(lhs[i], rhs[i]);
> > +        }
> >          return this;
> >      }
> >
> > @@ -559,98 +528,22 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >          return this;
> >      }
> >
> > -    /**
> > -     * Appends to the {@code builder} the comparison of
> > -     * two {@code float}s.
> > -     *
> > -     * <p>This handles NaNs, Infinities, and {@code -0.0}.</p>
> > -     *
> > -     * <p>It is compatible with the hash code generated by
> > -     * {@link HashCodeBuilder}.</p>
> > -     *
> > -     * @param lhs  left-hand value
> > -     * @param rhs  right-hand value
> > -     * @return this
> > -     */
> > -    public CompareToBuilder append(final float lhs, final float rhs) {
> > -        if (comparison != 0) {
> > -            return this;
> > -        }
> > -        comparison = Float.compare(lhs, rhs);
> > -        return this;
> > -    }
> > -
> > -    /**
> > -     * Appends to the {@code builder} the comparison of
> > -     * two {@code booleans}s.
> > -     *
> > -     * @param lhs  left-hand value
> > -     * @param rhs  right-hand value
> > -     * @return this
> > -      */
> > -    public CompareToBuilder append(final boolean lhs, final boolean
> rhs) {
> > -        if (comparison != 0) {
> > -            return this;
> > -        }
> > -        if (lhs == rhs) {
> > -            return this;
> > -        }
> > -        if (lhs) {
> > -            comparison = 1;
> > -        } else {
> > -            comparison = -1;
> > -        }
> > -        return this;
> > -    }
> > -
> >      /**
> >       * Appends to the {@code builder} the deep comparison of
> > -     * two {@link Object} arrays.
> > -     *
> > -     * <ol>
> > -     *  <li>Check if arrays are the same using {@code ==}</li>
> > -     *  <li>Check if for {@code null}, {@code null} is less than
> non-{@code null}</li>
> > -     *  <li>Check array length, a short length array is less than a
> long length array</li>
> > -     *  <li>Check array contents element by element using {@link
> #append(Object, Object, Comparator)}</li>
> > -     * </ol>
> > -     *
> > -     * <p>This method will also will be called for the top level of
> multi-dimensional,
> > -     * ragged, and multi-typed arrays.</p>
> > -     *
> > -     * @param lhs  left-hand array
> > -     * @param rhs  right-hand array
> > -     * @return this
> > -     * @throws ClassCastException  if {@code rhs} is not
> assignment-compatible
> > -     *  with {@code lhs}
> > -     */
> > -    public CompareToBuilder append(final Object[] lhs, final Object[]
> rhs) {
> > -        return append(lhs, rhs, null);
> > -    }
> > -
> > -    /**
> > -     * Appends to the {@code builder} the deep comparison of
> > -     * two {@link Object} arrays.
> > +     * two {@code double} arrays.
> >       *
> >       * <ol>
> >       *  <li>Check if arrays are the same using {@code ==}</li>
> >       *  <li>Check if for {@code null}, {@code null} is less than
> non-{@code null}</li>
> > -     *  <li>Check array length, a short length array is less than a
> long length array</li>
> > -     *  <li>Check array contents element by element using {@link
> #append(Object, Object, Comparator)}</li>
> > +     *  <li>Check array length, a shorter length array is less than a
> longer length array</li>
> > +     *  <li>Check array contents element by element using {@link
> #append(double, double)}</li>
> >       * </ol>
> >       *
> > -     * <p>This method will also will be called for the top level of
> multi-dimensional,
> > -     * ragged, and multi-typed arrays.</p>
> > -     *
> >       * @param lhs  left-hand array
> >       * @param rhs  right-hand array
> > -     * @param comparator  {@link Comparator} to use to compare the
> array elements,
> > -     *  {@code null} means to treat {@code lhs} elements as {@link
> Comparable}.
> >       * @return this
> > -     * @throws ClassCastException  if {@code rhs} is not
> assignment-compatible
> > -     *  with {@code lhs}
> > -     * @since 2.0
> >       */
> > -    public CompareToBuilder append(final Object[] lhs, final Object[]
> rhs, final Comparator<?> comparator) {
> > +    public CompareToBuilder append(final double[] lhs, final double[]
> rhs) {
> >          if (comparison != 0) {
> >              return this;
> >          }
> > @@ -670,67 +563,48 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >              return this;
> >          }
> >          for (int i = 0; i < lhs.length && comparison == 0; i++) {
> > -            append(lhs[i], rhs[i], comparator);
> > +            append(lhs[i], rhs[i]);
> >          }
> >          return this;
> >      }
> >
> >      /**
> > -     * Appends to the {@code builder} the deep comparison of
> > -     * two {@code long} arrays.
> > +     * Appends to the {@code builder} the comparison of
> > +     * two {@code float}s.
> >       *
> > -     * <ol>
> > -     *  <li>Check if arrays are the same using {@code ==}</li>
> > -     *  <li>Check if for {@code null}, {@code null} is less than
> non-{@code null}</li>
> > -     *  <li>Check array length, a shorter length array is less than a
> longer length array</li>
> > -     *  <li>Check array contents element by element using {@link
> #append(long, long)}</li>
> > -     * </ol>
> > +     * <p>This handles NaNs, Infinities, and {@code -0.0}.</p>
> >       *
> > -     * @param lhs  left-hand array
> > -     * @param rhs  right-hand array
> > +     * <p>It is compatible with the hash code generated by
> > +     * {@link HashCodeBuilder}.</p>
> > +     *
> > +     * @param lhs  left-hand value
> > +     * @param rhs  right-hand value
> >       * @return this
> >       */
> > -    public CompareToBuilder append(final long[] lhs, final long[] rhs) {
> > +    public CompareToBuilder append(final float lhs, final float rhs) {
> >          if (comparison != 0) {
> > -            return this;
> > -        }
> > -        if (lhs == rhs) {
> > -            return this;
> > -        }
> > -        if (lhs == null) {
> > -            comparison = -1;
> > -            return this;
> > -        }
> > -        if (rhs == null) {
> > -            comparison = 1;
> > -            return this;
> > -        }
> > -        if (lhs.length != rhs.length) {
> > -            comparison = lhs.length < rhs.length ? -1 : 1;
> > -            return this;
> > -        }
> > -        for (int i = 0; i < lhs.length && comparison == 0; i++) {
> > -            append(lhs[i], rhs[i]);
> > +            return this;
> >          }
> > +        comparison = Float.compare(lhs, rhs);
> >          return this;
> >      }
> >
> >      /**
> >       * Appends to the {@code builder} the deep comparison of
> > -     * two {@code int} arrays.
> > +     * two {@code float} arrays.
> >       *
> >       * <ol>
> >       *  <li>Check if arrays are the same using {@code ==}</li>
> >       *  <li>Check if for {@code null}, {@code null} is less than
> non-{@code null}</li>
> >       *  <li>Check array length, a shorter length array is less than a
> longer length array</li>
> > -     *  <li>Check array contents element by element using {@link
> #append(int, int)}</li>
> > +     *  <li>Check array contents element by element using {@link
> #append(float, float)}</li>
> >       * </ol>
> >       *
> >       * @param lhs  left-hand array
> >       * @param rhs  right-hand array
> >       * @return this
> >       */
> > -    public CompareToBuilder append(final int[] lhs, final int[] rhs) {
> > +    public CompareToBuilder append(final float[] lhs, final float[]
> rhs) {
> >          if (comparison != 0) {
> >              return this;
> >          }
> > @@ -755,22 +629,38 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >          return this;
> >      }
> >
> > +    /**
> > +     * Appends to the {@code builder} the comparison of
> > +     * two {@code int}s.
> > +     *
> > +     * @param lhs  left-hand value
> > +     * @param rhs  right-hand value
> > +     * @return this
> > +     */
> > +    public CompareToBuilder append(final int lhs, final int rhs) {
> > +        if (comparison != 0) {
> > +            return this;
> > +        }
> > +        comparison = Integer.compare(lhs, rhs);
> > +        return this;
> > +    }
> > +
> >      /**
> >       * Appends to the {@code builder} the deep comparison of
> > -     * two {@code short} arrays.
> > +     * two {@code int} arrays.
> >       *
> >       * <ol>
> >       *  <li>Check if arrays are the same using {@code ==}</li>
> >       *  <li>Check if for {@code null}, {@code null} is less than
> non-{@code null}</li>
> >       *  <li>Check array length, a shorter length array is less than a
> longer length array</li>
> > -     *  <li>Check array contents element by element using {@link
> #append(short, short)}</li>
> > +     *  <li>Check array contents element by element using {@link
> #append(int, int)}</li>
> >       * </ol>
> >       *
> >       * @param lhs  left-hand array
> >       * @param rhs  right-hand array
> >       * @return this
> >       */
> > -    public CompareToBuilder append(final short[] lhs, final short[]
> rhs) {
> > +    public CompareToBuilder append(final int[] lhs, final int[] rhs) {
> >          if (comparison != 0) {
> >              return this;
> >          }
> > @@ -795,22 +685,38 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >          return this;
> >      }
> >
> > +    /**
> > +     * Appends to the {@code builder} the comparison of
> > +     * two {@code long}s.
> > +     *
> > +     * @param lhs  left-hand value
> > +     * @param rhs  right-hand value
> > +     * @return this
> > +     */
> > +    public CompareToBuilder append(final long lhs, final long rhs) {
> > +        if (comparison != 0) {
> > +            return this;
> > +        }
> > +        comparison = Long.compare(lhs, rhs);
> > +        return this;
> > +    }
> > +
> >      /**
> >       * Appends to the {@code builder} the deep comparison of
> > -     * two {@code char} arrays.
> > +     * two {@code long} arrays.
> >       *
> >       * <ol>
> >       *  <li>Check if arrays are the same using {@code ==}</li>
> >       *  <li>Check if for {@code null}, {@code null} is less than
> non-{@code null}</li>
> >       *  <li>Check array length, a shorter length array is less than a
> longer length array</li>
> > -     *  <li>Check array contents element by element using {@link
> #append(char, char)}</li>
> > +     *  <li>Check array contents element by element using {@link
> #append(long, long)}</li>
> >       * </ol>
> >       *
> >       * @param lhs  left-hand array
> >       * @param rhs  right-hand array
> >       * @return this
> >       */
> > -    public CompareToBuilder append(final char[] lhs, final char[] rhs) {
> > +    public CompareToBuilder append(final long[] lhs, final long[] rhs) {
> >          if (comparison != 0) {
> >              return this;
> >          }
> > @@ -836,21 +742,54 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >      }
> >
> >      /**
> > -     * Appends to the {@code builder} the deep comparison of
> > -     * two {@code byte} arrays.
> > +     * Appends to the {@code builder} the comparison of
> > +     * two {@link Object}s.
> >       *
> >       * <ol>
> > -     *  <li>Check if arrays are the same using {@code ==}</li>
> > -     *  <li>Check if for {@code null}, {@code null} is less than
> non-{@code null}</li>
> > -     *  <li>Check array length, a shorter length array is less than a
> longer length array</li>
> > -     *  <li>Check array contents element by element using {@link
> #append(byte, byte)}</li>
> > +     * <li>Check if {@code lhs == rhs}</li>
> > +     * <li>Check if either {@code lhs} or {@code rhs} is {@code null},
> > +     *     a {@code null} object is less than a non-{@code null}
> object</li>
> > +     * <li>Check the object contents</li>
> >       * </ol>
> >       *
> > -     * @param lhs  left-hand array
> > -     * @param rhs  right-hand array
> > +     * <p>{@code lhs} must either be an array or implement {@link
> Comparable}.</p>
> > +     *
> > +     * @param lhs  left-hand object
> > +     * @param rhs  right-hand object
> >       * @return this
> > +     * @throws ClassCastException  if {@code rhs} is not
> assignment-compatible
> > +     *  with {@code lhs}
> >       */
> > -    public CompareToBuilder append(final byte[] lhs, final byte[] rhs) {
> > +    public CompareToBuilder append(final Object lhs, final Object rhs) {
> > +        return append(lhs, rhs, null);
> > +    }
> > +
> > +    /**
> > +     * Appends to the {@code builder} the comparison of
> > +     * two {@link Object}s.
> > +     *
> > +     * <ol>
> > +     * <li>Check if {@code lhs == rhs}</li>
> > +     * <li>Check if either {@code lhs} or {@code rhs} is {@code null},
> > +     *     a {@code null} object is less than a non-{@code null}
> object</li>
> > +     * <li>Check the object contents</li>
> > +     * </ol>
> > +     *
> > +     * <p>If {@code lhs} is an array, array comparison methods will be
> used.
> > +     * Otherwise {@code comparator} will be used to compare the objects.
> > +     * If {@code comparator} is {@code null}, {@code lhs} must
> > +     * implement {@link Comparable} instead.</p>
> > +     *
> > +     * @param lhs  left-hand object
> > +     * @param rhs  right-hand object
> > +     * @param comparator  {@link Comparator} used to compare the
> objects,
> > +     *  {@code null} means treat lhs as {@link Comparable}
> > +     * @return this
> > +     * @throws ClassCastException  if {@code rhs} is not
> assignment-compatible
> > +     *  with {@code lhs}
> > +     * @since 2.0
> > +     */
> > +    public CompareToBuilder append(final Object lhs, final Object rhs,
> final Comparator<?> comparator) {
> >          if (comparison != 0) {
> >              return this;
> >          }
> > @@ -865,72 +804,70 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >              comparison = 1;
> >              return this;
> >          }
> > -        if (lhs.length != rhs.length) {
> > -            comparison = lhs.length < rhs.length ? -1 : 1;
> > -            return this;
> > -        }
> > -        for (int i = 0; i < lhs.length && comparison == 0; i++) {
> > -            append(lhs[i], rhs[i]);
> > +        if (ObjectUtils.isArray(lhs)) {
> > +            // factor out array case in order to keep method small
> enough to be inlined
> > +            appendArray(lhs, rhs, comparator);
> > +        } else // the simple case, not an array, just test the element
> > +        if (comparator == null) {
> > +            @SuppressWarnings("unchecked") // assume this can be done;
> if not throw CCE as per Javadoc
> > +            final Comparable<Object> comparable = (Comparable<Object>)
> lhs;
> > +            comparison = comparable.compareTo(rhs);
> > +        } else {
> > +            @SuppressWarnings("unchecked") // assume this can be done;
> if not throw CCE as per Javadoc
> > +            final Comparator<Object> comparator2 = (Comparator<Object>)
> comparator;
> > +            comparison = comparator2.compare(lhs, rhs);
> >          }
> >          return this;
> >      }
> >
> >      /**
> >       * Appends to the {@code builder} the deep comparison of
> > -     * two {@code double} arrays.
> > +     * two {@link Object} arrays.
> >       *
> >       * <ol>
> >       *  <li>Check if arrays are the same using {@code ==}</li>
> >       *  <li>Check if for {@code null}, {@code null} is less than
> non-{@code null}</li>
> > -     *  <li>Check array length, a shorter length array is less than a
> longer length array</li>
> > -     *  <li>Check array contents element by element using {@link
> #append(double, double)}</li>
> > +     *  <li>Check array length, a short length array is less than a
> long length array</li>
> > +     *  <li>Check array contents element by element using {@link
> #append(Object, Object, Comparator)}</li>
> >       * </ol>
> >       *
> > +     * <p>This method will also will be called for the top level of
> multi-dimensional,
> > +     * ragged, and multi-typed arrays.</p>
> > +     *
> >       * @param lhs  left-hand array
> >       * @param rhs  right-hand array
> >       * @return this
> > +     * @throws ClassCastException  if {@code rhs} is not
> assignment-compatible
> > +     *  with {@code lhs}
> >       */
> > -    public CompareToBuilder append(final double[] lhs, final double[]
> rhs) {
> > -        if (comparison != 0) {
> > -            return this;
> > -        }
> > -        if (lhs == rhs) {
> > -            return this;
> > -        }
> > -        if (lhs == null) {
> > -            comparison = -1;
> > -            return this;
> > -        }
> > -        if (rhs == null) {
> > -            comparison = 1;
> > -            return this;
> > -        }
> > -        if (lhs.length != rhs.length) {
> > -            comparison = lhs.length < rhs.length ? -1 : 1;
> > -            return this;
> > -        }
> > -        for (int i = 0; i < lhs.length && comparison == 0; i++) {
> > -            append(lhs[i], rhs[i]);
> > -        }
> > -        return this;
> > +    public CompareToBuilder append(final Object[] lhs, final Object[]
> rhs) {
> > +        return append(lhs, rhs, null);
> >      }
> >
> >      /**
> >       * Appends to the {@code builder} the deep comparison of
> > -     * two {@code float} arrays.
> > +     * two {@link Object} arrays.
> >       *
> >       * <ol>
> >       *  <li>Check if arrays are the same using {@code ==}</li>
> >       *  <li>Check if for {@code null}, {@code null} is less than
> non-{@code null}</li>
> > -     *  <li>Check array length, a shorter length array is less than a
> longer length array</li>
> > -     *  <li>Check array contents element by element using {@link
> #append(float, float)}</li>
> > +     *  <li>Check array length, a short length array is less than a
> long length array</li>
> > +     *  <li>Check array contents element by element using {@link
> #append(Object, Object, Comparator)}</li>
> >       * </ol>
> >       *
> > +     * <p>This method will also will be called for the top level of
> multi-dimensional,
> > +     * ragged, and multi-typed arrays.</p>
> > +     *
> >       * @param lhs  left-hand array
> >       * @param rhs  right-hand array
> > +     * @param comparator  {@link Comparator} to use to compare the
> array elements,
> > +     *  {@code null} means to treat {@code lhs} elements as {@link
> Comparable}.
> >       * @return this
> > +     * @throws ClassCastException  if {@code rhs} is not
> assignment-compatible
> > +     *  with {@code lhs}
> > +     * @since 2.0
> >       */
> > -    public CompareToBuilder append(final float[] lhs, final float[]
> rhs) {
> > +    public CompareToBuilder append(final Object[] lhs, final Object[]
> rhs, final Comparator<?> comparator) {
> >          if (comparison != 0) {
> >              return this;
> >          }
> > @@ -950,27 +887,43 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >              return this;
> >          }
> >          for (int i = 0; i < lhs.length && comparison == 0; i++) {
> > -            append(lhs[i], rhs[i]);
> > +            append(lhs[i], rhs[i], comparator);
> > +        }
> > +        return this;
> > +    }
> > +
> > +    /**
> > +     * Appends to the {@code builder} the comparison of
> > +     * two {@code short}s.
> > +     *
> > +     * @param lhs  left-hand value
> > +     * @param rhs  right-hand value
> > +     * @return this
> > +     */
> > +    public CompareToBuilder append(final short lhs, final short rhs) {
> > +        if (comparison != 0) {
> > +            return this;
> >          }
> > +        comparison = Short.compare(lhs, rhs);
> >          return this;
> >      }
> >
> >      /**
> >       * Appends to the {@code builder} the deep comparison of
> > -     * two {@code boolean} arrays.
> > +     * two {@code short} arrays.
> >       *
> >       * <ol>
> >       *  <li>Check if arrays are the same using {@code ==}</li>
> >       *  <li>Check if for {@code null}, {@code null} is less than
> non-{@code null}</li>
> >       *  <li>Check array length, a shorter length array is less than a
> longer length array</li>
> > -     *  <li>Check array contents element by element using {@link
> #append(boolean, boolean)}</li>
> > +     *  <li>Check array contents element by element using {@link
> #append(short, short)}</li>
> >       * </ol>
> >       *
> >       * @param lhs  left-hand array
> >       * @param rhs  right-hand array
> >       * @return this
> >       */
> > -    public CompareToBuilder append(final boolean[] lhs, final boolean[]
> rhs) {
> > +    public CompareToBuilder append(final short[] lhs, final short[]
> rhs) {
> >          if (comparison != 0) {
> >              return this;
> >          }
> > @@ -995,17 +948,47 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >          return this;
> >      }
> >
> > +    private void appendArray(final Object lhs, final Object rhs, final
> Comparator<?> comparator) {
> > +        // switch on type of array, to dispatch to the correct handler
> > +        // handles multidimensional arrays
> > +        // throws a ClassCastException if rhs is not the correct array
> type
> > +        if (lhs instanceof long[]) {
> > +            append((long[]) lhs, (long[]) rhs);
> > +        } else if (lhs instanceof int[]) {
> > +            append((int[]) lhs, (int[]) rhs);
> > +        } else if (lhs instanceof short[]) {
> > +            append((short[]) lhs, (short[]) rhs);
> > +        } else if (lhs instanceof char[]) {
> > +            append((char[]) lhs, (char[]) rhs);
> > +        } else if (lhs instanceof byte[]) {
> > +            append((byte[]) lhs, (byte[]) rhs);
> > +        } else if (lhs instanceof double[]) {
> > +            append((double[]) lhs, (double[]) rhs);
> > +        } else if (lhs instanceof float[]) {
> > +            append((float[]) lhs, (float[]) rhs);
> > +        } else if (lhs instanceof boolean[]) {
> > +            append((boolean[]) lhs, (boolean[]) rhs);
> > +        } else {
> > +            // not an array of primitives
> > +            // throws a ClassCastException if rhs is not an array
> > +            append((Object[]) lhs, (Object[]) rhs, comparator);
> > +        }
> > +    }
> > +
> >      /**
> > -     * Returns a negative integer, a positive integer, or zero as
> > -     * the {@code builder} has judged the "left-hand" side
> > -     * as less than, greater than, or equal to the "right-hand"
> > -     * side.
> > +     * Appends to the {@code builder} the {@code compareTo(Object)}
> > +     * result of the superclass.
> >       *
> > -     * @return final comparison result
> > -     * @see #build()
> > +     * @param superCompareTo  result of calling {@code
> super.compareTo(Object)}
> > +     * @return this
> > +     * @since 2.0
> >       */
> > -    public int toComparison() {
> > -        return comparison;
> > +    public CompareToBuilder appendSuper(final int superCompareTo) {
> > +        if (comparison != 0) {
> > +            return this;
> > +        }
> > +        comparison = superCompareTo;
> > +        return this;
> >      }
> >
> >      /**
> > @@ -1022,5 +1005,18 @@ public class CompareToBuilder implements
> Builder<Integer> {
> >      public Integer build() {
> >          return Integer.valueOf(toComparison());
> >      }
> > +
> > +    /**
> > +     * Returns a negative integer, a positive integer, or zero as
> > +     * the {@code builder} has judged the "left-hand" side
> > +     * as less than, greater than, or equal to the "right-hand"
> > +     * side.
> > +     *
> > +     * @return final comparison result
> > +     * @see #build()
> > +     */
> > +    public int toComparison() {
> > +        return comparison;
> > +    }
> >  }
> >
> > diff --git
> a/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java
> b/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java
> > index 6f843ca9e..b34e9d390 100644
> > --- a/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java
> > +++ b/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java
> > @@ -561,19 +561,13 @@ public class EqualsBuilder implements
> Builder<Boolean> {
> >              final Field[] fields = clazz.getDeclaredFields();
> >              AccessibleObject.setAccessible(fields, true);
> >              for (int i = 0; i < fields.length && isEquals; i++) {
> > -                final Field f = fields[i];
> > -                if (!ArrayUtils.contains(excludeFields, f.getName())
> > -                    && !f.getName().contains("$")
> > -                    && (testTransients ||
> !Modifier.isTransient(f.getModifiers()))
> > -                    && !Modifier.isStatic(f.getModifiers())
> > -                    && !f.isAnnotationPresent(EqualsExclude.class)) {
> > -                    try {
> > -                        append(f.get(lhs), f.get(rhs));
> > -                    } catch (final 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");
> > -                    }
> > +                final Field field = fields[i];
> > +                if (!ArrayUtils.contains(excludeFields, field.getName())
> > +                    && !field.getName().contains("$")
> > +                    && (testTransients ||
> !Modifier.isTransient(field.getModifiers()))
> > +                    && !Modifier.isStatic(field.getModifiers())
> > +                    && !field.isAnnotationPresent(EqualsExclude.class))
> {
> > +                    append(Reflection.getUnchecked(field, lhs),
> Reflection.getUnchecked(field, rhs));
> >                  }
> >              }
> >          } finally {
> > diff --git
> a/src/main/java/org/apache/commons/lang3/builder/HashCodeBuilder.java
> b/src/main/java/org/apache/commons/lang3/builder/HashCodeBuilder.java
> > index 9c86c76f7..afe59a8bd 100644
> > --- a/src/main/java/org/apache/commons/lang3/builder/HashCodeBuilder.java
> > +++ b/src/main/java/org/apache/commons/lang3/builder/HashCodeBuilder.java
> > @@ -191,14 +191,7 @@ public class HashCodeBuilder implements
> Builder<Integer> {
> >                      && (useTransients ||
> !Modifier.isTransient(field.getModifiers()))
> >                      && !Modifier.isStatic(field.getModifiers())
> >                      &&
> !field.isAnnotationPresent(HashCodeExclude.class)) {
> > -                    try {
> > -                        final Object fieldValue = field.get(object);
> > -                        builder.append(fieldValue);
> > -                    } catch (final 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");
> > -                    }
> > +                    builder.append(Reflection.getUnchecked(field,
> object));
> >                  }
> >              }
> >          } finally {
> > diff --git
> a/src/main/java/org/apache/commons/lang3/builder/Reflection.java
> b/src/main/java/org/apache/commons/lang3/builder/Reflection.java
> > new file mode 100644
> > index 000000000..119bfe9b2
> > --- /dev/null
> > +++ b/src/main/java/org/apache/commons/lang3/builder/Reflection.java
> > @@ -0,0 +1,44 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one or more
> > + * contributor license agreements.  See the NOTICE file distributed with
> > + * this work for additional information regarding copyright ownership.
> > + * The ASF licenses this file to You under the Apache License, Version
> 2.0
> > + * (the "License"); you may not use this file except in compliance with
> > + * the License.  You may obtain a copy of the License at
> > + *
> > + *      http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing, software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> > + * See the License for the specific language governing permissions and
> > + * limitations under the License.
> > + */
> > +
> > +package org.apache.commons.lang3.builder;
> > +
> > +import java.lang.reflect.Field;
> > +import java.util.Objects;
> > +
> > +/**
> > + * Package-private reflection code.
> > + */
> > +class Reflection {
> > +
> > +    /**
> > +     * Delegates to {@link Field#get(Object)} and rethrows {@link
> IllegalAccessException} as {@link IllegalArgumentException}.
> > +     *
> > +     * @param field The receiver of the get call.
> > +     * @param obj   The argument of the get call.
> > +     * @return The result of the get call.
> > +     * @throws IllegalArgumentException Thrown after catching {@link
> IllegalAccessException}.
> > +     */
> > +    static Object getUnchecked(final Field field, final Object obj) {
> > +        try {
> > +            return Objects.requireNonNull(field, "field").get(obj);
> > +        } catch (IllegalAccessException e) {
> > +            throw new IllegalArgumentException(e);
> > +        }
> > +    }
> > +
> > +}
> > diff --git
> a/src/main/java/org/apache/commons/lang3/builder/ReflectionDiffBuilder.java
> b/src/main/java/org/apache/commons/lang3/builder/ReflectionDiffBuilder.java
> > index bacc0cb89..a5a8f9496 100644
> > ---
> a/src/main/java/org/apache/commons/lang3/builder/ReflectionDiffBuilder.java
> > +++
> b/src/main/java/org/apache/commons/lang3/builder/ReflectionDiffBuilder.java
> > @@ -151,12 +151,11 @@ public class ReflectionDiffBuilder<T> implements
> Builder<DiffResult<T>> {
> >          for (final Field field : FieldUtils.getAllFields(clazz)) {
> >              if (accept(field)) {
> >                  try {
> > -                    diffBuilder.append(field.getName(),
> FieldUtils.readField(field, left, true),
> > -                            FieldUtils.readField(field, right, true));
> > -                } catch (final IllegalAccessException ex) {
> > +                    diffBuilder.append(field.getName(),
> FieldUtils.readField(field, left, true), FieldUtils.readField(field, right,
> true));
> > +                } catch (final 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: " + ex.getMessage());
> > +                    throw new IllegalArgumentException("Unexpected
> IllegalAccessException: " + e.getMessage(), e);
> >                  }
> >              }
> >          }
> > diff --git
> a/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java
> b/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java
> > index 8bb0d0e10..d6413681b 100644
> > ---
> a/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java
> > +++
> b/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java
> > @@ -657,16 +657,10 @@ public class ReflectionToStringBuilder extends
> ToStringBuilder {
> >          for (final Field field : fields) {
> >              final String fieldName = field.getName();
> >              if (this.accept(field)) {
> > -                try {
> > -                    // Warning: Field.get(Object) creates wrappers
> objects for primitive types.
> > -                    final Object fieldValue = this.getValue(field);
> > -                    if (!excludeNullValues || fieldValue != null) {
> > -                        this.append(fieldName, fieldValue,
> !field.isAnnotationPresent(ToStringSummary.class));
> > -                    }
> > -                } catch (final IllegalAccessException ex) {
> > -                    // this can't happen. Would get a Security
> exception instead
> > -                    // throw a runtime exception in case the impossible
> happens.
> > -                    throw new InternalError("Unexpected
> IllegalAccessException: " + ex.getMessage());
> > +                // Warning: Field.get(Object) creates wrappers objects
> for primitive types.
> > +                final Object fieldValue =
> Reflection.getUnchecked(field, getObject());
> > +                if (!excludeNullValues || fieldValue != null) {
> > +                    this.append(fieldName, fieldValue,
> !field.isAnnotationPresent(ToStringSummary.class));
> >                  }
> >              }
> >          }
> >
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>
>

Re: [commons-lang] 04/04: Throw IllegalArgumentException instead of InternalError in the builder package

Posted by Gilles Sadowski <gi...@gmail.com>.
Hi.

Le mer. 12 juil. 2023 à 14:44, <gg...@apache.org> a écrit :
>
> This is an automated email from the ASF dual-hosted git repository.
>
> ggregory pushed a commit to branch master
> in repository https://gitbox.apache.org/repos/asf/commons-lang.git
>
> commit 2e3feda04337baa483bc26b66f238161dc6c97ac
> Author: Gary Gregory <ga...@gmail.com>
> AuthorDate: Wed Jul 12 08:44:38 2023 -0400
>
>     Throw IllegalArgumentException instead of InternalError in the builder
>     package

The diff below contains a bunch of (formatting ?) changes that
have nothing to do with this commit message.

Regards,
Gilles

> ---
>  src/changes/changes.xml                            |   1 +
>  .../commons/lang3/builder/CompareToBuilder.java    | 752 ++++++++++-----------
>  .../commons/lang3/builder/EqualsBuilder.java       |  20 +-
>  .../commons/lang3/builder/HashCodeBuilder.java     |   9 +-
>  .../apache/commons/lang3/builder/Reflection.java   |  44 ++
>  .../lang3/builder/ReflectionDiffBuilder.java       |   7 +-
>  .../lang3/builder/ReflectionToStringBuilder.java   |  14 +-
>  7 files changed, 434 insertions(+), 413 deletions(-)
>
> diff --git a/src/changes/changes.xml b/src/changes/changes.xml
> index 26d7be48c..da3522029 100644
> --- a/src/changes/changes.xml
> +++ b/src/changes/changes.xml
> @@ -123,6 +123,7 @@ The <action> type attribute can be add,update,fix,remove.
>      <action                   type="fix" dev="ggregory" due-to="Dimitrios Efthymiou">Update Javadoc for the insert methods in ArrayUtils #1078.</action>
>      <action                   type="fix" dev="ggregory" due-to="Gary Gregory">Deprecate ExceptionUtils.ExceptionUtils().</action>
>      <action issue="LANG-1697" type="fix" dev="ggregory" due-to="Jan Arne Sparka, Gary Gregory">TypeUtils.getRawType() throws a NullPointerException on Wildcard GenericArrayType.</action>
> +    <action                   type="fix" dev="ggregory" due-to="Gary Gregory">Throw IllegalArgumentException instead of InternalError in the builder package.</action>
>      <!-- ADD -->
>      <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add GitHub coverage.yml.</action>
>      <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add EnumUtils.getEnumSystemProperty(...).</action>
> diff --git a/src/main/java/org/apache/commons/lang3/builder/CompareToBuilder.java b/src/main/java/org/apache/commons/lang3/builder/CompareToBuilder.java
> index 38c69e613..3d411bb15 100644
> --- a/src/main/java/org/apache/commons/lang3/builder/CompareToBuilder.java
> +++ b/src/main/java/org/apache/commons/lang3/builder/CompareToBuilder.java
> @@ -97,19 +97,37 @@ import org.apache.commons.lang3.ObjectUtils;
>  public class CompareToBuilder implements Builder<Integer> {
>
>      /**
> -     * Current state of the comparison as appended fields are checked.
> -     */
> -    private int comparison;
> -
> -    /**
> -     * Constructor for CompareToBuilder.
> +     * Appends to {@code builder} the comparison of {@code lhs}
> +     * to {@code rhs} using the fields defined in {@code clazz}.
>       *
> -     * <p>Starts off assuming that the objects are equal. Multiple calls are
> -     * then made to the various append methods, followed by a call to
> -     * {@link #toComparison} to get the result.</p>
> +     * @param lhs  left-hand object
> +     * @param rhs  right-hand object
> +     * @param clazz  {@link Class} that defines fields to be compared
> +     * @param builder  {@link CompareToBuilder} to append to
> +     * @param useTransients  whether to compare transient fields
> +     * @param excludeFields  fields to exclude
>       */
> -    public CompareToBuilder() {
> -        comparison = 0;
> +    private static void reflectionAppend(
> +        final Object lhs,
> +        final Object rhs,
> +        final Class<?> clazz,
> +        final CompareToBuilder builder,
> +        final boolean useTransients,
> +        final String[] excludeFields) {
> +
> +        final Field[] fields = clazz.getDeclaredFields();
> +        AccessibleObject.setAccessible(fields, true);
> +        for (int i = 0; i < fields.length && builder.comparison == 0; i++) {
> +            final Field field = fields[i];
> +            if (!ArrayUtils.contains(excludeFields, field.getName())
> +                && !field.getName().contains("$")
> +                && (useTransients || !Modifier.isTransient(field.getModifiers()))
> +                && !Modifier.isStatic(field.getModifiers())) {
> +                // IllegalAccessException can't happen. Would get a Security exception instead.
> +                // Throw a runtime exception in case the impossible happens.
> +                builder.append(Reflection.getUnchecked(field, lhs), Reflection.getUnchecked(field, rhs));
> +            }
> +        }
>      }
>
>      /**
> @@ -183,10 +201,11 @@ public class CompareToBuilder implements Builder<Integer> {
>       *
>       * <ul>
>       * <li>Static fields will not be compared</li>
> -     * <li>If {@code compareTransients} is {@code true},
> +     * <li>If the {@code compareTransients} is {@code true},
>       *     compares transient members.  Otherwise ignores them, as they
>       *     are likely derived fields.</li>
> -     * <li>Superclass fields will be compared</li>
> +     * <li>Compares superclass fields up to and including {@code reflectUpToClass}.
> +     *     If {@code reflectUpToClass} is {@code null}, compares all superclass fields.</li>
>       * </ul>
>       *
>       * <p>If both {@code lhs} and {@code rhs} are {@code null},
> @@ -194,17 +213,41 @@ public class CompareToBuilder implements Builder<Integer> {
>       *
>       * @param lhs  left-hand object
>       * @param rhs  right-hand object
> -     * @param excludeFields  Collection of String fields to exclude
> +     * @param compareTransients  whether to compare transient fields
> +     * @param reflectUpToClass  last superclass for which fields are compared
> +     * @param excludeFields  fields to exclude
>       * @return a negative integer, zero, or a positive integer as {@code lhs}
>       *  is less than, equal to, or greater than {@code rhs}
>       * @throws NullPointerException  if either {@code lhs} or {@code rhs}
>       *  (but not both) is {@code null}
>       * @throws ClassCastException  if {@code rhs} is not assignment-compatible
>       *  with {@code lhs}
> -     * @since 2.2
> +     * @since 2.2 (2.0 as {@code reflectionCompare(Object, Object, boolean, Class)})
>       */
> -    public static int reflectionCompare(final Object lhs, final Object rhs, final Collection<String> excludeFields) {
> -        return reflectionCompare(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
> +    public static int reflectionCompare(
> +        final Object lhs,
> +        final Object rhs,
> +        final boolean compareTransients,
> +        final Class<?> reflectUpToClass,
> +        final String... excludeFields) {
> +
> +        if (lhs == rhs) {
> +            return 0;
> +        }
> +        Objects.requireNonNull(lhs, "lhs");
> +        Objects.requireNonNull(rhs, "rhs");
> +
> +        Class<?> lhsClazz = lhs.getClass();
> +        if (!lhsClazz.isInstance(rhs)) {
> +            throw new ClassCastException();
> +        }
> +        final CompareToBuilder compareToBuilder = new CompareToBuilder();
> +        reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
> +        while (lhsClazz.getSuperclass() != null && lhsClazz != reflectUpToClass) {
> +            lhsClazz = lhsClazz.getSuperclass();
> +            reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
> +        }
> +        return compareToBuilder.toComparison();
>      }
>
>      /**
> @@ -227,7 +270,7 @@ public class CompareToBuilder implements Builder<Integer> {
>       *
>       * @param lhs  left-hand object
>       * @param rhs  right-hand object
> -     * @param excludeFields  array of fields to exclude
> +     * @param excludeFields  Collection of String fields to exclude
>       * @return a negative integer, zero, or a positive integer as {@code lhs}
>       *  is less than, equal to, or greater than {@code rhs}
>       * @throws NullPointerException  if either {@code lhs} or {@code rhs}
> @@ -236,8 +279,8 @@ public class CompareToBuilder implements Builder<Integer> {
>       *  with {@code lhs}
>       * @since 2.2
>       */
> -    public static int reflectionCompare(final Object lhs, final Object rhs, final String... excludeFields) {
> -        return reflectionCompare(lhs, rhs, false, null, excludeFields);
> +    public static int reflectionCompare(final Object lhs, final Object rhs, final Collection<String> excludeFields) {
> +        return reflectionCompare(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
>      }
>
>      /**
> @@ -249,11 +292,10 @@ public class CompareToBuilder implements Builder<Integer> {
>       *
>       * <ul>
>       * <li>Static fields will not be compared</li>
> -     * <li>If the {@code compareTransients} is {@code true},
> +     * <li>If {@code compareTransients} is {@code true},
>       *     compares transient members.  Otherwise ignores them, as they
>       *     are likely derived fields.</li>
> -     * <li>Compares superclass fields up to and including {@code reflectUpToClass}.
> -     *     If {@code reflectUpToClass} is {@code null}, compares all superclass fields.</li>
> +     * <li>Superclass fields will be compared</li>
>       * </ul>
>       *
>       * <p>If both {@code lhs} and {@code rhs} are {@code null},
> @@ -261,146 +303,74 @@ public class CompareToBuilder implements Builder<Integer> {
>       *
>       * @param lhs  left-hand object
>       * @param rhs  right-hand object
> -     * @param compareTransients  whether to compare transient fields
> -     * @param reflectUpToClass  last superclass for which fields are compared
> -     * @param excludeFields  fields to exclude
> +     * @param excludeFields  array of fields to exclude
>       * @return a negative integer, zero, or a positive integer as {@code lhs}
>       *  is less than, equal to, or greater than {@code rhs}
>       * @throws NullPointerException  if either {@code lhs} or {@code rhs}
>       *  (but not both) is {@code null}
>       * @throws ClassCastException  if {@code rhs} is not assignment-compatible
>       *  with {@code lhs}
> -     * @since 2.2 (2.0 as {@code reflectionCompare(Object, Object, boolean, Class)})
> +     * @since 2.2
>       */
> -    public static int reflectionCompare(
> -        final Object lhs,
> -        final Object rhs,
> -        final boolean compareTransients,
> -        final Class<?> reflectUpToClass,
> -        final String... excludeFields) {
> -
> -        if (lhs == rhs) {
> -            return 0;
> -        }
> -        Objects.requireNonNull(lhs, "lhs");
> -        Objects.requireNonNull(rhs, "rhs");
> -
> -        Class<?> lhsClazz = lhs.getClass();
> -        if (!lhsClazz.isInstance(rhs)) {
> -            throw new ClassCastException();
> -        }
> -        final CompareToBuilder compareToBuilder = new CompareToBuilder();
> -        reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
> -        while (lhsClazz.getSuperclass() != null && lhsClazz != reflectUpToClass) {
> -            lhsClazz = lhsClazz.getSuperclass();
> -            reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
> -        }
> -        return compareToBuilder.toComparison();
> +    public static int reflectionCompare(final Object lhs, final Object rhs, final String... excludeFields) {
> +        return reflectionCompare(lhs, rhs, false, null, excludeFields);
>      }
>
>      /**
> -     * Appends to {@code builder} the comparison of {@code lhs}
> -     * to {@code rhs} using the fields defined in {@code clazz}.
> -     *
> -     * @param lhs  left-hand object
> -     * @param rhs  right-hand object
> -     * @param clazz  {@link Class} that defines fields to be compared
> -     * @param builder  {@link CompareToBuilder} to append to
> -     * @param useTransients  whether to compare transient fields
> -     * @param excludeFields  fields to exclude
> +     * Current state of the comparison as appended fields are checked.
>       */
> -    private static void reflectionAppend(
> -        final Object lhs,
> -        final Object rhs,
> -        final Class<?> clazz,
> -        final CompareToBuilder builder,
> -        final boolean useTransients,
> -        final String[] excludeFields) {
> -
> -        final Field[] fields = clazz.getDeclaredFields();
> -        AccessibleObject.setAccessible(fields, true);
> -        for (int i = 0; i < fields.length && builder.comparison == 0; i++) {
> -            final Field field = fields[i];
> -            if (!ArrayUtils.contains(excludeFields, field.getName())
> -                && !field.getName().contains("$")
> -                && (useTransients || !Modifier.isTransient(field.getModifiers()))
> -                && !Modifier.isStatic(field.getModifiers())) {
> -                try {
> -                    builder.append(field.get(lhs), field.get(rhs));
> -                } catch (final 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");
> -                }
> -            }
> -        }
> -    }
> +    private int comparison;
>
>      /**
> -     * Appends to the {@code builder} the {@code compareTo(Object)}
> -     * result of the superclass.
> +     * Constructor for CompareToBuilder.
>       *
> -     * @param superCompareTo  result of calling {@code super.compareTo(Object)}
> -     * @return this
> -     * @since 2.0
> +     * <p>Starts off assuming that the objects are equal. Multiple calls are
> +     * then made to the various append methods, followed by a call to
> +     * {@link #toComparison} to get the result.</p>
>       */
> -    public CompareToBuilder appendSuper(final int superCompareTo) {
> -        if (comparison != 0) {
> -            return this;
> -        }
> -        comparison = superCompareTo;
> -        return this;
> +    public CompareToBuilder() {
> +        comparison = 0;
>      }
>
>      /**
>       * Appends to the {@code builder} the comparison of
> -     * two {@link Object}s.
> -     *
> -     * <ol>
> -     * <li>Check if {@code lhs == rhs}</li>
> -     * <li>Check if either {@code lhs} or {@code rhs} is {@code null},
> -     *     a {@code null} object is less than a non-{@code null} object</li>
> -     * <li>Check the object contents</li>
> -     * </ol>
> -     *
> -     * <p>{@code lhs} must either be an array or implement {@link Comparable}.</p>
> +     * two {@code booleans}s.
>       *
> -     * @param lhs  left-hand object
> -     * @param rhs  right-hand object
> +     * @param lhs  left-hand value
> +     * @param rhs  right-hand value
>       * @return this
> -     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
> -     *  with {@code lhs}
> -     */
> -    public CompareToBuilder append(final Object lhs, final Object rhs) {
> -        return append(lhs, rhs, null);
> +      */
> +    public CompareToBuilder append(final boolean lhs, final boolean rhs) {
> +        if (comparison != 0) {
> +            return this;
> +        }
> +        if (lhs == rhs) {
> +            return this;
> +        }
> +        if (lhs) {
> +            comparison = 1;
> +        } else {
> +            comparison = -1;
> +        }
> +        return this;
>      }
>
>      /**
> -     * Appends to the {@code builder} the comparison of
> -     * two {@link Object}s.
> +     * Appends to the {@code builder} the deep comparison of
> +     * two {@code boolean} arrays.
>       *
>       * <ol>
> -     * <li>Check if {@code lhs == rhs}</li>
> -     * <li>Check if either {@code lhs} or {@code rhs} is {@code null},
> -     *     a {@code null} object is less than a non-{@code null} object</li>
> -     * <li>Check the object contents</li>
> +     *  <li>Check if arrays are the same using {@code ==}</li>
> +     *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
> +     *  <li>Check array length, a shorter length array is less than a longer length array</li>
> +     *  <li>Check array contents element by element using {@link #append(boolean, boolean)}</li>
>       * </ol>
>       *
> -     * <p>If {@code lhs} is an array, array comparison methods will be used.
> -     * Otherwise {@code comparator} will be used to compare the objects.
> -     * If {@code comparator} is {@code null}, {@code lhs} must
> -     * implement {@link Comparable} instead.</p>
> -     *
> -     * @param lhs  left-hand object
> -     * @param rhs  right-hand object
> -     * @param comparator  {@link Comparator} used to compare the objects,
> -     *  {@code null} means treat lhs as {@link Comparable}
> +     * @param lhs  left-hand array
> +     * @param rhs  right-hand array
>       * @return this
> -     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
> -     *  with {@code lhs}
> -     * @since 2.0
>       */
> -    public CompareToBuilder append(final Object lhs, final Object rhs, final Comparator<?> comparator) {
> +    public CompareToBuilder append(final boolean[] lhs, final boolean[] rhs) {
>          if (comparison != 0) {
>              return this;
>          }
> @@ -415,94 +385,69 @@ public class CompareToBuilder implements Builder<Integer> {
>              comparison = 1;
>              return this;
>          }
> -        if (ObjectUtils.isArray(lhs)) {
> -            // factor out array case in order to keep method small enough to be inlined
> -            appendArray(lhs, rhs, comparator);
> -        } else // the simple case, not an array, just test the element
> -        if (comparator == null) {
> -            @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
> -            final Comparable<Object> comparable = (Comparable<Object>) lhs;
> -            comparison = comparable.compareTo(rhs);
> -        } else {
> -            @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
> -            final Comparator<Object> comparator2 = (Comparator<Object>) comparator;
> -            comparison = comparator2.compare(lhs, rhs);
> +        if (lhs.length != rhs.length) {
> +            comparison = lhs.length < rhs.length ? -1 : 1;
> +            return this;
>          }
> -        return this;
> -    }
> -
> -    private void appendArray(final Object lhs, final Object rhs, final Comparator<?> comparator) {
> -        // switch on type of array, to dispatch to the correct handler
> -        // handles multidimensional arrays
> -        // throws a ClassCastException if rhs is not the correct array type
> -        if (lhs instanceof long[]) {
> -            append((long[]) lhs, (long[]) rhs);
> -        } else if (lhs instanceof int[]) {
> -            append((int[]) lhs, (int[]) rhs);
> -        } else if (lhs instanceof short[]) {
> -            append((short[]) lhs, (short[]) rhs);
> -        } else if (lhs instanceof char[]) {
> -            append((char[]) lhs, (char[]) rhs);
> -        } else if (lhs instanceof byte[]) {
> -            append((byte[]) lhs, (byte[]) rhs);
> -        } else if (lhs instanceof double[]) {
> -            append((double[]) lhs, (double[]) rhs);
> -        } else if (lhs instanceof float[]) {
> -            append((float[]) lhs, (float[]) rhs);
> -        } else if (lhs instanceof boolean[]) {
> -            append((boolean[]) lhs, (boolean[]) rhs);
> -        } else {
> -            // not an array of primitives
> -            // throws a ClassCastException if rhs is not an array
> -            append((Object[]) lhs, (Object[]) rhs, comparator);
> +        for (int i = 0; i < lhs.length && comparison == 0; i++) {
> +            append(lhs[i], rhs[i]);
>          }
> +        return this;
>      }
>
>      /**
>       * Appends to the {@code builder} the comparison of
> -     * two {@code long}s.
> +     * two {@code byte}s.
>       *
>       * @param lhs  left-hand value
>       * @param rhs  right-hand value
>       * @return this
>       */
> -    public CompareToBuilder append(final long lhs, final long rhs) {
> +    public CompareToBuilder append(final byte lhs, final byte rhs) {
>          if (comparison != 0) {
>              return this;
>          }
> -        comparison = Long.compare(lhs, rhs);
> +        comparison = Byte.compare(lhs, rhs);
>          return this;
>      }
>
>      /**
> -     * Appends to the {@code builder} the comparison of
> -     * two {@code int}s.
> +     * Appends to the {@code builder} the deep comparison of
> +     * two {@code byte} arrays.
>       *
> -     * @param lhs  left-hand value
> -     * @param rhs  right-hand value
> +     * <ol>
> +     *  <li>Check if arrays are the same using {@code ==}</li>
> +     *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
> +     *  <li>Check array length, a shorter length array is less than a longer length array</li>
> +     *  <li>Check array contents element by element using {@link #append(byte, byte)}</li>
> +     * </ol>
> +     *
> +     * @param lhs  left-hand array
> +     * @param rhs  right-hand array
>       * @return this
>       */
> -    public CompareToBuilder append(final int lhs, final int rhs) {
> +    public CompareToBuilder append(final byte[] lhs, final byte[] rhs) {
>          if (comparison != 0) {
>              return this;
>          }
> -        comparison = Integer.compare(lhs, rhs);
> -        return this;
> -    }
> -
> -    /**
> -     * Appends to the {@code builder} the comparison of
> -     * two {@code short}s.
> -     *
> -     * @param lhs  left-hand value
> -     * @param rhs  right-hand value
> -     * @return this
> -     */
> -    public CompareToBuilder append(final short lhs, final short rhs) {
> -        if (comparison != 0) {
> +        if (lhs == rhs) {
>              return this;
>          }
> -        comparison = Short.compare(lhs, rhs);
> +        if (lhs == null) {
> +            comparison = -1;
> +            return this;
> +        }
> +        if (rhs == null) {
> +            comparison = 1;
> +            return this;
> +        }
> +        if (lhs.length != rhs.length) {
> +            comparison = lhs.length < rhs.length ? -1 : 1;
> +            return this;
> +        }
> +        for (int i = 0; i < lhs.length && comparison == 0; i++) {
> +            append(lhs[i], rhs[i]);
> +        }
>          return this;
>      }
>
> @@ -523,18 +468,42 @@ public class CompareToBuilder implements Builder<Integer> {
>      }
>
>      /**
> -     * Appends to the {@code builder} the comparison of
> -     * two {@code byte}s.
> +     * Appends to the {@code builder} the deep comparison of
> +     * two {@code char} arrays.
>       *
> -     * @param lhs  left-hand value
> -     * @param rhs  right-hand value
> +     * <ol>
> +     *  <li>Check if arrays are the same using {@code ==}</li>
> +     *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
> +     *  <li>Check array length, a shorter length array is less than a longer length array</li>
> +     *  <li>Check array contents element by element using {@link #append(char, char)}</li>
> +     * </ol>
> +     *
> +     * @param lhs  left-hand array
> +     * @param rhs  right-hand array
>       * @return this
>       */
> -    public CompareToBuilder append(final byte lhs, final byte rhs) {
> +    public CompareToBuilder append(final char[] lhs, final char[] rhs) {
>          if (comparison != 0) {
>              return this;
>          }
> -        comparison = Byte.compare(lhs, rhs);
> +        if (lhs == rhs) {
> +            return this;
> +        }
> +        if (lhs == null) {
> +            comparison = -1;
> +            return this;
> +        }
> +        if (rhs == null) {
> +            comparison = 1;
> +            return this;
> +        }
> +        if (lhs.length != rhs.length) {
> +            comparison = lhs.length < rhs.length ? -1 : 1;
> +            return this;
> +        }
> +        for (int i = 0; i < lhs.length && comparison == 0; i++) {
> +            append(lhs[i], rhs[i]);
> +        }
>          return this;
>      }
>
> @@ -559,98 +528,22 @@ public class CompareToBuilder implements Builder<Integer> {
>          return this;
>      }
>
> -    /**
> -     * Appends to the {@code builder} the comparison of
> -     * two {@code float}s.
> -     *
> -     * <p>This handles NaNs, Infinities, and {@code -0.0}.</p>
> -     *
> -     * <p>It is compatible with the hash code generated by
> -     * {@link HashCodeBuilder}.</p>
> -     *
> -     * @param lhs  left-hand value
> -     * @param rhs  right-hand value
> -     * @return this
> -     */
> -    public CompareToBuilder append(final float lhs, final float rhs) {
> -        if (comparison != 0) {
> -            return this;
> -        }
> -        comparison = Float.compare(lhs, rhs);
> -        return this;
> -    }
> -
> -    /**
> -     * Appends to the {@code builder} the comparison of
> -     * two {@code booleans}s.
> -     *
> -     * @param lhs  left-hand value
> -     * @param rhs  right-hand value
> -     * @return this
> -      */
> -    public CompareToBuilder append(final boolean lhs, final boolean rhs) {
> -        if (comparison != 0) {
> -            return this;
> -        }
> -        if (lhs == rhs) {
> -            return this;
> -        }
> -        if (lhs) {
> -            comparison = 1;
> -        } else {
> -            comparison = -1;
> -        }
> -        return this;
> -    }
> -
>      /**
>       * Appends to the {@code builder} the deep comparison of
> -     * two {@link Object} arrays.
> -     *
> -     * <ol>
> -     *  <li>Check if arrays are the same using {@code ==}</li>
> -     *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
> -     *  <li>Check array length, a short length array is less than a long length array</li>
> -     *  <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
> -     * </ol>
> -     *
> -     * <p>This method will also will be called for the top level of multi-dimensional,
> -     * ragged, and multi-typed arrays.</p>
> -     *
> -     * @param lhs  left-hand array
> -     * @param rhs  right-hand array
> -     * @return this
> -     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
> -     *  with {@code lhs}
> -     */
> -    public CompareToBuilder append(final Object[] lhs, final Object[] rhs) {
> -        return append(lhs, rhs, null);
> -    }
> -
> -    /**
> -     * Appends to the {@code builder} the deep comparison of
> -     * two {@link Object} arrays.
> +     * two {@code double} arrays.
>       *
>       * <ol>
>       *  <li>Check if arrays are the same using {@code ==}</li>
>       *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
> -     *  <li>Check array length, a short length array is less than a long length array</li>
> -     *  <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
> +     *  <li>Check array length, a shorter length array is less than a longer length array</li>
> +     *  <li>Check array contents element by element using {@link #append(double, double)}</li>
>       * </ol>
>       *
> -     * <p>This method will also will be called for the top level of multi-dimensional,
> -     * ragged, and multi-typed arrays.</p>
> -     *
>       * @param lhs  left-hand array
>       * @param rhs  right-hand array
> -     * @param comparator  {@link Comparator} to use to compare the array elements,
> -     *  {@code null} means to treat {@code lhs} elements as {@link Comparable}.
>       * @return this
> -     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
> -     *  with {@code lhs}
> -     * @since 2.0
>       */
> -    public CompareToBuilder append(final Object[] lhs, final Object[] rhs, final Comparator<?> comparator) {
> +    public CompareToBuilder append(final double[] lhs, final double[] rhs) {
>          if (comparison != 0) {
>              return this;
>          }
> @@ -670,67 +563,48 @@ public class CompareToBuilder implements Builder<Integer> {
>              return this;
>          }
>          for (int i = 0; i < lhs.length && comparison == 0; i++) {
> -            append(lhs[i], rhs[i], comparator);
> +            append(lhs[i], rhs[i]);
>          }
>          return this;
>      }
>
>      /**
> -     * Appends to the {@code builder} the deep comparison of
> -     * two {@code long} arrays.
> +     * Appends to the {@code builder} the comparison of
> +     * two {@code float}s.
>       *
> -     * <ol>
> -     *  <li>Check if arrays are the same using {@code ==}</li>
> -     *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
> -     *  <li>Check array length, a shorter length array is less than a longer length array</li>
> -     *  <li>Check array contents element by element using {@link #append(long, long)}</li>
> -     * </ol>
> +     * <p>This handles NaNs, Infinities, and {@code -0.0}.</p>
>       *
> -     * @param lhs  left-hand array
> -     * @param rhs  right-hand array
> +     * <p>It is compatible with the hash code generated by
> +     * {@link HashCodeBuilder}.</p>
> +     *
> +     * @param lhs  left-hand value
> +     * @param rhs  right-hand value
>       * @return this
>       */
> -    public CompareToBuilder append(final long[] lhs, final long[] rhs) {
> +    public CompareToBuilder append(final float lhs, final float rhs) {
>          if (comparison != 0) {
> -            return this;
> -        }
> -        if (lhs == rhs) {
> -            return this;
> -        }
> -        if (lhs == null) {
> -            comparison = -1;
> -            return this;
> -        }
> -        if (rhs == null) {
> -            comparison = 1;
> -            return this;
> -        }
> -        if (lhs.length != rhs.length) {
> -            comparison = lhs.length < rhs.length ? -1 : 1;
> -            return this;
> -        }
> -        for (int i = 0; i < lhs.length && comparison == 0; i++) {
> -            append(lhs[i], rhs[i]);
> +            return this;
>          }
> +        comparison = Float.compare(lhs, rhs);
>          return this;
>      }
>
>      /**
>       * Appends to the {@code builder} the deep comparison of
> -     * two {@code int} arrays.
> +     * two {@code float} arrays.
>       *
>       * <ol>
>       *  <li>Check if arrays are the same using {@code ==}</li>
>       *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
>       *  <li>Check array length, a shorter length array is less than a longer length array</li>
> -     *  <li>Check array contents element by element using {@link #append(int, int)}</li>
> +     *  <li>Check array contents element by element using {@link #append(float, float)}</li>
>       * </ol>
>       *
>       * @param lhs  left-hand array
>       * @param rhs  right-hand array
>       * @return this
>       */
> -    public CompareToBuilder append(final int[] lhs, final int[] rhs) {
> +    public CompareToBuilder append(final float[] lhs, final float[] rhs) {
>          if (comparison != 0) {
>              return this;
>          }
> @@ -755,22 +629,38 @@ public class CompareToBuilder implements Builder<Integer> {
>          return this;
>      }
>
> +    /**
> +     * Appends to the {@code builder} the comparison of
> +     * two {@code int}s.
> +     *
> +     * @param lhs  left-hand value
> +     * @param rhs  right-hand value
> +     * @return this
> +     */
> +    public CompareToBuilder append(final int lhs, final int rhs) {
> +        if (comparison != 0) {
> +            return this;
> +        }
> +        comparison = Integer.compare(lhs, rhs);
> +        return this;
> +    }
> +
>      /**
>       * Appends to the {@code builder} the deep comparison of
> -     * two {@code short} arrays.
> +     * two {@code int} arrays.
>       *
>       * <ol>
>       *  <li>Check if arrays are the same using {@code ==}</li>
>       *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
>       *  <li>Check array length, a shorter length array is less than a longer length array</li>
> -     *  <li>Check array contents element by element using {@link #append(short, short)}</li>
> +     *  <li>Check array contents element by element using {@link #append(int, int)}</li>
>       * </ol>
>       *
>       * @param lhs  left-hand array
>       * @param rhs  right-hand array
>       * @return this
>       */
> -    public CompareToBuilder append(final short[] lhs, final short[] rhs) {
> +    public CompareToBuilder append(final int[] lhs, final int[] rhs) {
>          if (comparison != 0) {
>              return this;
>          }
> @@ -795,22 +685,38 @@ public class CompareToBuilder implements Builder<Integer> {
>          return this;
>      }
>
> +    /**
> +     * Appends to the {@code builder} the comparison of
> +     * two {@code long}s.
> +     *
> +     * @param lhs  left-hand value
> +     * @param rhs  right-hand value
> +     * @return this
> +     */
> +    public CompareToBuilder append(final long lhs, final long rhs) {
> +        if (comparison != 0) {
> +            return this;
> +        }
> +        comparison = Long.compare(lhs, rhs);
> +        return this;
> +    }
> +
>      /**
>       * Appends to the {@code builder} the deep comparison of
> -     * two {@code char} arrays.
> +     * two {@code long} arrays.
>       *
>       * <ol>
>       *  <li>Check if arrays are the same using {@code ==}</li>
>       *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
>       *  <li>Check array length, a shorter length array is less than a longer length array</li>
> -     *  <li>Check array contents element by element using {@link #append(char, char)}</li>
> +     *  <li>Check array contents element by element using {@link #append(long, long)}</li>
>       * </ol>
>       *
>       * @param lhs  left-hand array
>       * @param rhs  right-hand array
>       * @return this
>       */
> -    public CompareToBuilder append(final char[] lhs, final char[] rhs) {
> +    public CompareToBuilder append(final long[] lhs, final long[] rhs) {
>          if (comparison != 0) {
>              return this;
>          }
> @@ -836,21 +742,54 @@ public class CompareToBuilder implements Builder<Integer> {
>      }
>
>      /**
> -     * Appends to the {@code builder} the deep comparison of
> -     * two {@code byte} arrays.
> +     * Appends to the {@code builder} the comparison of
> +     * two {@link Object}s.
>       *
>       * <ol>
> -     *  <li>Check if arrays are the same using {@code ==}</li>
> -     *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
> -     *  <li>Check array length, a shorter length array is less than a longer length array</li>
> -     *  <li>Check array contents element by element using {@link #append(byte, byte)}</li>
> +     * <li>Check if {@code lhs == rhs}</li>
> +     * <li>Check if either {@code lhs} or {@code rhs} is {@code null},
> +     *     a {@code null} object is less than a non-{@code null} object</li>
> +     * <li>Check the object contents</li>
>       * </ol>
>       *
> -     * @param lhs  left-hand array
> -     * @param rhs  right-hand array
> +     * <p>{@code lhs} must either be an array or implement {@link Comparable}.</p>
> +     *
> +     * @param lhs  left-hand object
> +     * @param rhs  right-hand object
>       * @return this
> +     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
> +     *  with {@code lhs}
>       */
> -    public CompareToBuilder append(final byte[] lhs, final byte[] rhs) {
> +    public CompareToBuilder append(final Object lhs, final Object rhs) {
> +        return append(lhs, rhs, null);
> +    }
> +
> +    /**
> +     * Appends to the {@code builder} the comparison of
> +     * two {@link Object}s.
> +     *
> +     * <ol>
> +     * <li>Check if {@code lhs == rhs}</li>
> +     * <li>Check if either {@code lhs} or {@code rhs} is {@code null},
> +     *     a {@code null} object is less than a non-{@code null} object</li>
> +     * <li>Check the object contents</li>
> +     * </ol>
> +     *
> +     * <p>If {@code lhs} is an array, array comparison methods will be used.
> +     * Otherwise {@code comparator} will be used to compare the objects.
> +     * If {@code comparator} is {@code null}, {@code lhs} must
> +     * implement {@link Comparable} instead.</p>
> +     *
> +     * @param lhs  left-hand object
> +     * @param rhs  right-hand object
> +     * @param comparator  {@link Comparator} used to compare the objects,
> +     *  {@code null} means treat lhs as {@link Comparable}
> +     * @return this
> +     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
> +     *  with {@code lhs}
> +     * @since 2.0
> +     */
> +    public CompareToBuilder append(final Object lhs, final Object rhs, final Comparator<?> comparator) {
>          if (comparison != 0) {
>              return this;
>          }
> @@ -865,72 +804,70 @@ public class CompareToBuilder implements Builder<Integer> {
>              comparison = 1;
>              return this;
>          }
> -        if (lhs.length != rhs.length) {
> -            comparison = lhs.length < rhs.length ? -1 : 1;
> -            return this;
> -        }
> -        for (int i = 0; i < lhs.length && comparison == 0; i++) {
> -            append(lhs[i], rhs[i]);
> +        if (ObjectUtils.isArray(lhs)) {
> +            // factor out array case in order to keep method small enough to be inlined
> +            appendArray(lhs, rhs, comparator);
> +        } else // the simple case, not an array, just test the element
> +        if (comparator == null) {
> +            @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
> +            final Comparable<Object> comparable = (Comparable<Object>) lhs;
> +            comparison = comparable.compareTo(rhs);
> +        } else {
> +            @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
> +            final Comparator<Object> comparator2 = (Comparator<Object>) comparator;
> +            comparison = comparator2.compare(lhs, rhs);
>          }
>          return this;
>      }
>
>      /**
>       * Appends to the {@code builder} the deep comparison of
> -     * two {@code double} arrays.
> +     * two {@link Object} arrays.
>       *
>       * <ol>
>       *  <li>Check if arrays are the same using {@code ==}</li>
>       *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
> -     *  <li>Check array length, a shorter length array is less than a longer length array</li>
> -     *  <li>Check array contents element by element using {@link #append(double, double)}</li>
> +     *  <li>Check array length, a short length array is less than a long length array</li>
> +     *  <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
>       * </ol>
>       *
> +     * <p>This method will also will be called for the top level of multi-dimensional,
> +     * ragged, and multi-typed arrays.</p>
> +     *
>       * @param lhs  left-hand array
>       * @param rhs  right-hand array
>       * @return this
> +     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
> +     *  with {@code lhs}
>       */
> -    public CompareToBuilder append(final double[] lhs, final double[] rhs) {
> -        if (comparison != 0) {
> -            return this;
> -        }
> -        if (lhs == rhs) {
> -            return this;
> -        }
> -        if (lhs == null) {
> -            comparison = -1;
> -            return this;
> -        }
> -        if (rhs == null) {
> -            comparison = 1;
> -            return this;
> -        }
> -        if (lhs.length != rhs.length) {
> -            comparison = lhs.length < rhs.length ? -1 : 1;
> -            return this;
> -        }
> -        for (int i = 0; i < lhs.length && comparison == 0; i++) {
> -            append(lhs[i], rhs[i]);
> -        }
> -        return this;
> +    public CompareToBuilder append(final Object[] lhs, final Object[] rhs) {
> +        return append(lhs, rhs, null);
>      }
>
>      /**
>       * Appends to the {@code builder} the deep comparison of
> -     * two {@code float} arrays.
> +     * two {@link Object} arrays.
>       *
>       * <ol>
>       *  <li>Check if arrays are the same using {@code ==}</li>
>       *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
> -     *  <li>Check array length, a shorter length array is less than a longer length array</li>
> -     *  <li>Check array contents element by element using {@link #append(float, float)}</li>
> +     *  <li>Check array length, a short length array is less than a long length array</li>
> +     *  <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
>       * </ol>
>       *
> +     * <p>This method will also will be called for the top level of multi-dimensional,
> +     * ragged, and multi-typed arrays.</p>
> +     *
>       * @param lhs  left-hand array
>       * @param rhs  right-hand array
> +     * @param comparator  {@link Comparator} to use to compare the array elements,
> +     *  {@code null} means to treat {@code lhs} elements as {@link Comparable}.
>       * @return this
> +     * @throws ClassCastException  if {@code rhs} is not assignment-compatible
> +     *  with {@code lhs}
> +     * @since 2.0
>       */
> -    public CompareToBuilder append(final float[] lhs, final float[] rhs) {
> +    public CompareToBuilder append(final Object[] lhs, final Object[] rhs, final Comparator<?> comparator) {
>          if (comparison != 0) {
>              return this;
>          }
> @@ -950,27 +887,43 @@ public class CompareToBuilder implements Builder<Integer> {
>              return this;
>          }
>          for (int i = 0; i < lhs.length && comparison == 0; i++) {
> -            append(lhs[i], rhs[i]);
> +            append(lhs[i], rhs[i], comparator);
> +        }
> +        return this;
> +    }
> +
> +    /**
> +     * Appends to the {@code builder} the comparison of
> +     * two {@code short}s.
> +     *
> +     * @param lhs  left-hand value
> +     * @param rhs  right-hand value
> +     * @return this
> +     */
> +    public CompareToBuilder append(final short lhs, final short rhs) {
> +        if (comparison != 0) {
> +            return this;
>          }
> +        comparison = Short.compare(lhs, rhs);
>          return this;
>      }
>
>      /**
>       * Appends to the {@code builder} the deep comparison of
> -     * two {@code boolean} arrays.
> +     * two {@code short} arrays.
>       *
>       * <ol>
>       *  <li>Check if arrays are the same using {@code ==}</li>
>       *  <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
>       *  <li>Check array length, a shorter length array is less than a longer length array</li>
> -     *  <li>Check array contents element by element using {@link #append(boolean, boolean)}</li>
> +     *  <li>Check array contents element by element using {@link #append(short, short)}</li>
>       * </ol>
>       *
>       * @param lhs  left-hand array
>       * @param rhs  right-hand array
>       * @return this
>       */
> -    public CompareToBuilder append(final boolean[] lhs, final boolean[] rhs) {
> +    public CompareToBuilder append(final short[] lhs, final short[] rhs) {
>          if (comparison != 0) {
>              return this;
>          }
> @@ -995,17 +948,47 @@ public class CompareToBuilder implements Builder<Integer> {
>          return this;
>      }
>
> +    private void appendArray(final Object lhs, final Object rhs, final Comparator<?> comparator) {
> +        // switch on type of array, to dispatch to the correct handler
> +        // handles multidimensional arrays
> +        // throws a ClassCastException if rhs is not the correct array type
> +        if (lhs instanceof long[]) {
> +            append((long[]) lhs, (long[]) rhs);
> +        } else if (lhs instanceof int[]) {
> +            append((int[]) lhs, (int[]) rhs);
> +        } else if (lhs instanceof short[]) {
> +            append((short[]) lhs, (short[]) rhs);
> +        } else if (lhs instanceof char[]) {
> +            append((char[]) lhs, (char[]) rhs);
> +        } else if (lhs instanceof byte[]) {
> +            append((byte[]) lhs, (byte[]) rhs);
> +        } else if (lhs instanceof double[]) {
> +            append((double[]) lhs, (double[]) rhs);
> +        } else if (lhs instanceof float[]) {
> +            append((float[]) lhs, (float[]) rhs);
> +        } else if (lhs instanceof boolean[]) {
> +            append((boolean[]) lhs, (boolean[]) rhs);
> +        } else {
> +            // not an array of primitives
> +            // throws a ClassCastException if rhs is not an array
> +            append((Object[]) lhs, (Object[]) rhs, comparator);
> +        }
> +    }
> +
>      /**
> -     * Returns a negative integer, a positive integer, or zero as
> -     * the {@code builder} has judged the "left-hand" side
> -     * as less than, greater than, or equal to the "right-hand"
> -     * side.
> +     * Appends to the {@code builder} the {@code compareTo(Object)}
> +     * result of the superclass.
>       *
> -     * @return final comparison result
> -     * @see #build()
> +     * @param superCompareTo  result of calling {@code super.compareTo(Object)}
> +     * @return this
> +     * @since 2.0
>       */
> -    public int toComparison() {
> -        return comparison;
> +    public CompareToBuilder appendSuper(final int superCompareTo) {
> +        if (comparison != 0) {
> +            return this;
> +        }
> +        comparison = superCompareTo;
> +        return this;
>      }
>
>      /**
> @@ -1022,5 +1005,18 @@ public class CompareToBuilder implements Builder<Integer> {
>      public Integer build() {
>          return Integer.valueOf(toComparison());
>      }
> +
> +    /**
> +     * Returns a negative integer, a positive integer, or zero as
> +     * the {@code builder} has judged the "left-hand" side
> +     * as less than, greater than, or equal to the "right-hand"
> +     * side.
> +     *
> +     * @return final comparison result
> +     * @see #build()
> +     */
> +    public int toComparison() {
> +        return comparison;
> +    }
>  }
>
> diff --git a/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java b/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java
> index 6f843ca9e..b34e9d390 100644
> --- a/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java
> +++ b/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java
> @@ -561,19 +561,13 @@ public class EqualsBuilder implements Builder<Boolean> {
>              final Field[] fields = clazz.getDeclaredFields();
>              AccessibleObject.setAccessible(fields, true);
>              for (int i = 0; i < fields.length && isEquals; i++) {
> -                final Field f = fields[i];
> -                if (!ArrayUtils.contains(excludeFields, f.getName())
> -                    && !f.getName().contains("$")
> -                    && (testTransients || !Modifier.isTransient(f.getModifiers()))
> -                    && !Modifier.isStatic(f.getModifiers())
> -                    && !f.isAnnotationPresent(EqualsExclude.class)) {
> -                    try {
> -                        append(f.get(lhs), f.get(rhs));
> -                    } catch (final 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");
> -                    }
> +                final Field field = fields[i];
> +                if (!ArrayUtils.contains(excludeFields, field.getName())
> +                    && !field.getName().contains("$")
> +                    && (testTransients || !Modifier.isTransient(field.getModifiers()))
> +                    && !Modifier.isStatic(field.getModifiers())
> +                    && !field.isAnnotationPresent(EqualsExclude.class)) {
> +                    append(Reflection.getUnchecked(field, lhs), Reflection.getUnchecked(field, rhs));
>                  }
>              }
>          } finally {
> diff --git a/src/main/java/org/apache/commons/lang3/builder/HashCodeBuilder.java b/src/main/java/org/apache/commons/lang3/builder/HashCodeBuilder.java
> index 9c86c76f7..afe59a8bd 100644
> --- a/src/main/java/org/apache/commons/lang3/builder/HashCodeBuilder.java
> +++ b/src/main/java/org/apache/commons/lang3/builder/HashCodeBuilder.java
> @@ -191,14 +191,7 @@ public class HashCodeBuilder implements Builder<Integer> {
>                      && (useTransients || !Modifier.isTransient(field.getModifiers()))
>                      && !Modifier.isStatic(field.getModifiers())
>                      && !field.isAnnotationPresent(HashCodeExclude.class)) {
> -                    try {
> -                        final Object fieldValue = field.get(object);
> -                        builder.append(fieldValue);
> -                    } catch (final 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");
> -                    }
> +                    builder.append(Reflection.getUnchecked(field, object));
>                  }
>              }
>          } finally {
> diff --git a/src/main/java/org/apache/commons/lang3/builder/Reflection.java b/src/main/java/org/apache/commons/lang3/builder/Reflection.java
> new file mode 100644
> index 000000000..119bfe9b2
> --- /dev/null
> +++ b/src/main/java/org/apache/commons/lang3/builder/Reflection.java
> @@ -0,0 +1,44 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one or more
> + * contributor license agreements.  See the NOTICE file distributed with
> + * this work for additional information regarding copyright ownership.
> + * The ASF licenses this file to You under the Apache License, Version 2.0
> + * (the "License"); you may not use this file except in compliance with
> + * the License.  You may obtain a copy of the License at
> + *
> + *      http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +package org.apache.commons.lang3.builder;
> +
> +import java.lang.reflect.Field;
> +import java.util.Objects;
> +
> +/**
> + * Package-private reflection code.
> + */
> +class Reflection {
> +
> +    /**
> +     * Delegates to {@link Field#get(Object)} and rethrows {@link IllegalAccessException} as {@link IllegalArgumentException}.
> +     *
> +     * @param field The receiver of the get call.
> +     * @param obj   The argument of the get call.
> +     * @return The result of the get call.
> +     * @throws IllegalArgumentException Thrown after catching {@link IllegalAccessException}.
> +     */
> +    static Object getUnchecked(final Field field, final Object obj) {
> +        try {
> +            return Objects.requireNonNull(field, "field").get(obj);
> +        } catch (IllegalAccessException e) {
> +            throw new IllegalArgumentException(e);
> +        }
> +    }
> +
> +}
> diff --git a/src/main/java/org/apache/commons/lang3/builder/ReflectionDiffBuilder.java b/src/main/java/org/apache/commons/lang3/builder/ReflectionDiffBuilder.java
> index bacc0cb89..a5a8f9496 100644
> --- a/src/main/java/org/apache/commons/lang3/builder/ReflectionDiffBuilder.java
> +++ b/src/main/java/org/apache/commons/lang3/builder/ReflectionDiffBuilder.java
> @@ -151,12 +151,11 @@ public class ReflectionDiffBuilder<T> implements Builder<DiffResult<T>> {
>          for (final Field field : FieldUtils.getAllFields(clazz)) {
>              if (accept(field)) {
>                  try {
> -                    diffBuilder.append(field.getName(), FieldUtils.readField(field, left, true),
> -                            FieldUtils.readField(field, right, true));
> -                } catch (final IllegalAccessException ex) {
> +                    diffBuilder.append(field.getName(), FieldUtils.readField(field, left, true), FieldUtils.readField(field, right, true));
> +                } catch (final 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: " + ex.getMessage());
> +                    throw new IllegalArgumentException("Unexpected IllegalAccessException: " + e.getMessage(), e);
>                  }
>              }
>          }
> diff --git a/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java b/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java
> index 8bb0d0e10..d6413681b 100644
> --- a/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java
> +++ b/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java
> @@ -657,16 +657,10 @@ public class ReflectionToStringBuilder extends ToStringBuilder {
>          for (final Field field : fields) {
>              final String fieldName = field.getName();
>              if (this.accept(field)) {
> -                try {
> -                    // Warning: Field.get(Object) creates wrappers objects for primitive types.
> -                    final Object fieldValue = this.getValue(field);
> -                    if (!excludeNullValues || fieldValue != null) {
> -                        this.append(fieldName, fieldValue, !field.isAnnotationPresent(ToStringSummary.class));
> -                    }
> -                } catch (final IllegalAccessException ex) {
> -                    // this can't happen. Would get a Security exception instead
> -                    // throw a runtime exception in case the impossible happens.
> -                    throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
> +                // Warning: Field.get(Object) creates wrappers objects for primitive types.
> +                final Object fieldValue = Reflection.getUnchecked(field, getObject());
> +                if (!excludeNullValues || fieldValue != null) {
> +                    this.append(fieldName, fieldValue, !field.isAnnotationPresent(ToStringSummary.class));
>                  }
>              }
>          }
>

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