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