Hi. Le mer. 12 juil. 2023 à 14:44, <ggreg...@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 <garydgreg...@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-unsubscr...@commons.apache.org For additional commands, e-mail: dev-h...@commons.apache.org