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 type attribute can be add,update,fix,remove. Update Javadoc for the insert methods in ArrayUtils #1078. Deprecate ExceptionUtils.ExceptionUtils(). TypeUtils.getRawType() throws a NullPointerException on Wildcard GenericArrayType. + Throw IllegalArgumentException instead of InternalError in the builder package. Add GitHub coverage.yml. Add EnumUtils.getEnumSystemProperty(...). 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 { /** - * 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}. * - *

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.

+ * @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)); + } + } } /** @@ -174,6 +192,64 @@ public class CompareToBuilder implements Builder { return reflectionCompare(lhs, rhs, compareTransients, null); } + /** + * Compares two {@link Object}s via reflection. + * + *

Fields can be private, thus {@code AccessibleObject.setAccessible} + * is used to bypass normal access control checks. This will fail under a + * security manager unless the appropriate permissions are set.

+ * + *
    + *
  • Static fields will not be compared
  • + *
  • If the {@code compareTransients} is {@code true}, + * compares transient members. Otherwise ignores them, as they + * are likely derived fields.
  • + *
  • Compares superclass fields up to and including {@code reflectUpToClass}. + * If {@code reflectUpToClass} is {@code null}, compares all superclass fields.
  • + *
+ * + *

If both {@code lhs} and {@code rhs} are {@code null}, + * they are considered equal.

+ * + * @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 + * @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)}) + */ + 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(); + } + /** * Compares two {@link Object}s via reflection. * @@ -241,114 +317,427 @@ public class CompareToBuilder implements Builder { } /** - * Compares two {@link Object}s via reflection. - * - *

Fields can be private, thus {@code AccessibleObject.setAccessible} - * is used to bypass normal access control checks. This will fail under a - * security manager unless the appropriate permissions are set.

- * - *
    - *
  • Static fields will not be compared
  • - *
  • If the {@code compareTransients} is {@code true}, - * compares transient members. Otherwise ignores them, as they - * are likely derived fields.
  • - *
  • Compares superclass fields up to and including {@code reflectUpToClass}. - * If {@code reflectUpToClass} is {@code null}, compares all superclass fields.
  • - *
- * - *

If both {@code lhs} and {@code rhs} are {@code null}, - * they are considered equal.

- * - * @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 - * @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)}) + * Current state of the comparison as appended fields are checked. */ - public static int reflectionCompare( - final Object lhs, - final Object rhs, - final boolean compareTransients, - final Class reflectUpToClass, - final String... excludeFields) { + private int comparison; - 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(); + /** + * Constructor for CompareToBuilder. + * + *

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.

+ */ + public CompareToBuilder() { + comparison = 0; } /** - * Appends to {@code builder} the comparison of {@code lhs} - * to {@code rhs} using the fields defined in {@code clazz}. + * Appends to the {@code builder} the comparison of + * two {@code booleans}s. * - * @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 - */ - 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"); - } - } - } - } - - /** - * Appends to the {@code builder} the {@code compareTo(Object)} - * result of the superclass. - * - * @param superCompareTo result of calling {@code super.compareTo(Object)} + * @param lhs left-hand value + * @param rhs right-hand value * @return this - * @since 2.0 - */ - public CompareToBuilder appendSuper(final int superCompareTo) { + */ + public CompareToBuilder append(final boolean lhs, final boolean rhs) { if (comparison != 0) { return this; } - comparison = superCompareTo; + if (lhs == rhs) { + return this; + } + if (lhs) { + comparison = 1; + } else { + comparison = -1; + } + return this; + } + + /** + * Appends to the {@code builder} the deep comparison of + * two {@code boolean} arrays. + * + *
    + *
  1. Check if arrays are the same using {@code ==}
  2. + *
  3. Check if for {@code null}, {@code null} is less than non-{@code null}
  4. + *
  5. Check array length, a shorter length array is less than a longer length array
  6. + *
  7. Check array contents element by element using {@link #append(boolean, boolean)}
  8. + *
+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this + */ + public CompareToBuilder append(final boolean[] lhs, final boolean[] 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; + } + + /** + * Appends to the {@code builder} the comparison of + * two {@code byte}s. + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this + */ + public CompareToBuilder append(final byte lhs, final byte rhs) { + if (comparison != 0) { + return this; + } + comparison = Byte.compare(lhs, rhs); + return this; + } + + /** + * Appends to the {@code builder} the deep comparison of + * two {@code byte} arrays. + * + *
    + *
  1. Check if arrays are the same using {@code ==}
  2. + *
  3. Check if for {@code null}, {@code null} is less than non-{@code null}
  4. + *
  5. Check array length, a shorter length array is less than a longer length array
  6. + *
  7. Check array contents element by element using {@link #append(byte, byte)}
  8. + *
+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this + */ + public CompareToBuilder append(final byte[] lhs, final byte[] 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; + } + + /** + * Appends to the {@code builder} the comparison of + * two {@code char}s. + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this + */ + public CompareToBuilder append(final char lhs, final char rhs) { + if (comparison != 0) { + return this; + } + comparison = Character.compare(lhs, rhs); + return this; + } + + /** + * Appends to the {@code builder} the deep comparison of + * two {@code char} arrays. + * + *
    + *
  1. Check if arrays are the same using {@code ==}
  2. + *
  3. Check if for {@code null}, {@code null} is less than non-{@code null}
  4. + *
  5. Check array length, a shorter length array is less than a longer length array
  6. + *
  7. Check array contents element by element using {@link #append(char, char)}
  8. + *
+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this + */ + public CompareToBuilder append(final char[] lhs, final char[] 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; + } + + /** + * Appends to the {@code builder} the comparison of + * two {@code double}s. + * + *

This handles NaNs, Infinities, and {@code -0.0}.

+ * + *

It is compatible with the hash code generated by + * {@link HashCodeBuilder}.

+ * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this + */ + public CompareToBuilder append(final double lhs, final double rhs) { + if (comparison != 0) { + return this; + } + comparison = Double.compare(lhs, rhs); + return this; + } + + /** + * Appends to the {@code builder} the deep comparison of + * two {@code double} arrays. + * + *
    + *
  1. Check if arrays are the same using {@code ==}
  2. + *
  3. Check if for {@code null}, {@code null} is less than non-{@code null}
  4. + *
  5. Check array length, a shorter length array is less than a longer length array
  6. + *
  7. Check array contents element by element using {@link #append(double, double)}
  8. + *
+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this + */ + 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; + } + + /** + * Appends to the {@code builder} the comparison of + * two {@code float}s. + * + *

This handles NaNs, Infinities, and {@code -0.0}.

+ * + *

It is compatible with the hash code generated by + * {@link HashCodeBuilder}.

+ * + * @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 deep comparison of + * two {@code float} arrays. + * + *
    + *
  1. Check if arrays are the same using {@code ==}
  2. + *
  3. Check if for {@code null}, {@code null} is less than non-{@code null}
  4. + *
  5. Check array length, a shorter length array is less than a longer length array
  6. + *
  7. Check array contents element by element using {@link #append(float, float)}
  8. + *
+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this + */ + 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; + } + + /** + * 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 int} arrays. + * + *
    + *
  1. Check if arrays are the same using {@code ==}
  2. + *
  3. Check if for {@code null}, {@code null} is less than non-{@code null}
  4. + *
  5. Check array length, a shorter length array is less than a longer length array
  6. + *
  7. Check array contents element by element using {@link #append(int, int)}
  8. + *
+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this + */ + public CompareToBuilder append(final int[] lhs, final int[] 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; + } + + /** + * 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 long} arrays. + * + *
    + *
  1. Check if arrays are the same using {@code ==}
  2. + *
  3. Check if for {@code null}, {@code null} is less than non-{@code null}
  4. + *
  5. Check array length, a shorter length array is less than a longer length array
  6. + *
  7. Check array contents element by element using {@link #append(long, long)}
  8. + *
+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this + */ + public CompareToBuilder append(final long[] lhs, final long[] 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; } @@ -431,178 +820,6 @@ public class CompareToBuilder implements Builder { 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); - } - } - - /** - * 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 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 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 comparison of - * two {@code char}s. - * - * @param lhs left-hand value - * @param rhs right-hand value - * @return this - */ - public CompareToBuilder append(final char lhs, final char rhs) { - if (comparison != 0) { - return this; - } - comparison = Character.compare(lhs, rhs); - return this; - } - - /** - * Appends to the {@code builder} the comparison of - * two {@code byte}s. - * - * @param lhs left-hand value - * @param rhs right-hand value - * @return this - */ - public CompareToBuilder append(final byte lhs, final byte rhs) { - if (comparison != 0) { - return this; - } - comparison = Byte.compare(lhs, rhs); - return this; - } - - /** - * Appends to the {@code builder} the comparison of - * two {@code double}s. - * - *

This handles NaNs, Infinities, and {@code -0.0}.

- * - *

It is compatible with the hash code generated by - * {@link HashCodeBuilder}.

- * - * @param lhs left-hand value - * @param rhs right-hand value - * @return this - */ - public CompareToBuilder append(final double lhs, final double rhs) { - if (comparison != 0) { - return this; - } - comparison = Double.compare(lhs, rhs); - return this; - } - - /** - * Appends to the {@code builder} the comparison of - * two {@code float}s. - * - *

This handles NaNs, Infinities, and {@code -0.0}.

- * - *

It is compatible with the hash code generated by - * {@link HashCodeBuilder}.

- * - * @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. @@ -676,82 +893,18 @@ public class CompareToBuilder implements Builder { } /** - * Appends to the {@code builder} the deep comparison of - * two {@code long} arrays. + * Appends to the {@code builder} the comparison of + * two {@code short}s. * - *
    - *
  1. Check if arrays are the same using {@code ==}
  2. - *
  3. Check if for {@code null}, {@code null} is less than non-{@code null}
  4. - *
  5. Check array length, a shorter length array is less than a longer length array
  6. - *
  7. Check array contents element by element using {@link #append(long, long)}
  8. - *
- * - * @param lhs left-hand array - * @param rhs right-hand array + * @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 short lhs, final short 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; - } - - /** - * Appends to the {@code builder} the deep comparison of - * two {@code int} arrays. - * - *
    - *
  1. Check if arrays are the same using {@code ==}
  2. - *
  3. Check if for {@code null}, {@code null} is less than non-{@code null}
  4. - *
  5. Check array length, a shorter length array is less than a longer length array
  6. - *
  7. Check array contents element by element using {@link #append(int, int)}
  8. - *
- * - * @param lhs left-hand array - * @param rhs right-hand array - * @return this - */ - public CompareToBuilder append(final int[] lhs, final int[] 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]); - } + comparison = Short.compare(lhs, rhs); return this; } @@ -795,219 +948,49 @@ public class CompareToBuilder implements Builder { return this; } - /** - * Appends to the {@code builder} the deep comparison of - * two {@code char} arrays. - * - *
    - *
  1. Check if arrays are the same using {@code ==}
  2. - *
  3. Check if for {@code null}, {@code null} is less than non-{@code null}
  4. - *
  5. Check array length, a shorter length array is less than a longer length array
  6. - *
  7. Check array contents element by element using {@link #append(char, char)}
  8. - *
- * - * @param lhs left-hand array - * @param rhs right-hand array - * @return this - */ - public CompareToBuilder append(final char[] lhs, final char[] rhs) { - if (comparison != 0) { - 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); } - 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; } /** - * Appends to the {@code builder} the deep comparison of - * two {@code byte} arrays. + * Appends to the {@code builder} the {@code compareTo(Object)} + * result of the superclass. * - *
    - *
  1. Check if arrays are the same using {@code ==}
  2. - *
  3. Check if for {@code null}, {@code null} is less than non-{@code null}
  4. - *
  5. Check array length, a shorter length array is less than a longer length array
  6. - *
  7. Check array contents element by element using {@link #append(byte, byte)}
  8. - *
- * - * @param lhs left-hand array - * @param rhs right-hand array + * @param superCompareTo result of calling {@code super.compareTo(Object)} * @return this + * @since 2.0 */ - public CompareToBuilder append(final byte[] lhs, final byte[] rhs) { + public CompareToBuilder appendSuper(final int superCompareTo) { 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]); - } + comparison = superCompareTo; return this; } - /** - * Appends to the {@code builder} the deep comparison of - * two {@code double} arrays. - * - *
    - *
  1. Check if arrays are the same using {@code ==}
  2. - *
  3. Check if for {@code null}, {@code null} is less than non-{@code null}
  4. - *
  5. Check array length, a shorter length array is less than a longer length array
  6. - *
  7. Check array contents element by element using {@link #append(double, double)}
  8. - *
- * - * @param lhs left-hand array - * @param rhs right-hand array - * @return this - */ - 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; - } - - /** - * Appends to the {@code builder} the deep comparison of - * two {@code float} arrays. - * - *
    - *
  1. Check if arrays are the same using {@code ==}
  2. - *
  3. Check if for {@code null}, {@code null} is less than non-{@code null}
  4. - *
  5. Check array length, a shorter length array is less than a longer length array
  6. - *
  7. Check array contents element by element using {@link #append(float, float)}
  8. - *
- * - * @param lhs left-hand array - * @param rhs right-hand array - * @return this - */ - 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; - } - - /** - * Appends to the {@code builder} the deep comparison of - * two {@code boolean} arrays. - * - *
    - *
  1. Check if arrays are the same using {@code ==}
  2. - *
  3. Check if for {@code null}, {@code null} is less than non-{@code null}
  4. - *
  5. Check array length, a shorter length array is less than a longer length array
  6. - *
  7. Check array contents element by element using {@link #append(boolean, boolean)}
  8. - *
- * - * @param lhs left-hand array - * @param rhs right-hand array - * @return this - */ - public CompareToBuilder append(final boolean[] lhs, final boolean[] 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; - } - - /** - * 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; - } - /** * Returns a negative Integer, a positive Integer, or zero as * the {@code builder} has judged the "left-hand" side @@ -1022,5 +1005,18 @@ public class CompareToBuilder implements Builder { 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 { 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 { && (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 implements Builder> { 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)); } } }