Applying Pete Gieser's enhancement for the CompareToBuilder - a clone of the EqualsBuilder and HashCodeBuilder fixes previously applied. Bugzilla issue #39398

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/lang/trunk@397016 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Henri Yandell 2006-04-25 23:15:05 +00:00
parent 8393f10b79
commit 2f4408d33c
2 changed files with 98 additions and 37 deletions

View File

@ -18,7 +18,10 @@
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.commons.lang.math.NumberUtils;
@ -134,7 +137,7 @@ public CompareToBuilder() {
* with <code>lhs</code>
*/
public static int reflectionCompare(Object lhs, Object rhs) {
return reflectionCompare(lhs, rhs, false, null);
return reflectionCompare(lhs, rhs, false, null, null);
}
/**
@ -166,7 +169,39 @@ public static int reflectionCompare(Object lhs, Object rhs) {
* with <code>lhs</code>
*/
public static int reflectionCompare(Object lhs, Object rhs, boolean compareTransients) {
return reflectionCompare(lhs, rhs, compareTransients, null);
return reflectionCompare(lhs, rhs, compareTransients, null, null);
}
/**
* <p>Compares two <code>Object</code>s via reflection.</p>
*
* <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code>
* is used to bypass normal access control checks. This will fail under a
* security manager unless the appropriate permissions are set.</p>
*
* <ul>
* <li>Static fields will not be compared</li>
* <li>If <code>compareTransients</code> is <code>true</code>,
* compares transient members. Otherwise ignores them, as they
* are likely derived fields.</li>
* <li>Superclass fields will be compared</li>
* </ul>
*
* <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>,
* they are considered equal.</p>
*
* @param lhs left-hand object
* @param rhs right-hand object
* @param excludeFields fields to exclude
* @return a negative integer, zero, or a positive integer as <code>lhs</code>
* is less than, equal to, or greater than <code>rhs</code>
* @throws NullPointerException if either <code>lhs</code> or <code>rhs</code>
* (but not both) is <code>null</code>
* @throws ClassCastException if <code>rhs</code> is not assignment-compatible
* with <code>lhs</code>
*/
public static int reflectionCompare(Object lhs, Object rhs, String[] excludeFields) {
return reflectionCompare(lhs, rhs, false, null, excludeFields);
}
/**
@ -200,7 +235,7 @@ public static int reflectionCompare(Object lhs, Object rhs, boolean compareTrans
* with <code>lhs</code>
* @since 2.0
*/
public static int reflectionCompare(Object lhs, Object rhs, boolean compareTransients, Class reflectUpToClass) {
public static int reflectionCompare(Object lhs, Object rhs, boolean compareTransients, Class reflectUpToClass, String[] excludeFields) {
if (lhs == rhs) {
return 0;
}
@ -212,10 +247,10 @@ public static int reflectionCompare(Object lhs, Object rhs, boolean compareTrans
throw new ClassCastException();
}
CompareToBuilder compareToBuilder = new CompareToBuilder();
reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients);
reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
while (lhsClazz.getSuperclass() != null && lhsClazz != reflectUpToClass) {
lhsClazz = lhsClazz.getSuperclass();
reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients);
reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
}
return compareToBuilder.toComparison();
}
@ -229,19 +264,23 @@ public static int reflectionCompare(Object lhs, Object rhs, boolean compareTrans
* @param clazz <code>Class</code> that defines fields to be compared
* @param builder <code>CompareToBuilder</code> to append to
* @param useTransients whether to compare transient fields
* @param excludeFields fields to exclude
*/
private static void reflectionAppend(
Object lhs,
Object rhs,
Class clazz,
CompareToBuilder builder,
boolean useTransients) {
boolean useTransients,
String[] excludeFields) {
Field[] fields = clazz.getDeclaredFields();
List excludedFieldList = excludeFields != null ? Arrays.asList(excludeFields) : Collections.EMPTY_LIST;
AccessibleObject.setAccessible(fields, true);
for (int i = 0; i < fields.length && builder.comparison == 0; i++) {
Field f = fields[i];
if ((f.getName().indexOf('$') == -1)
if (!excludedFieldList.contains(f.getName())
&& (f.getName().indexOf('$') == -1)
&& (useTransients || !Modifier.isTransient(f.getModifiers()))
&& (!Modifier.isStatic(f.getModifiers()))) {
try {

View File

@ -111,7 +111,7 @@ public TestTransientSubObject(int a, int t) {
this.t = t;
}
}
public void testReflectionCompare() {
TestObject o1 = new TestObject(4);
TestObject o2 = new TestObject(4);
@ -142,11 +142,30 @@ public void testReflectionCompareEx2() {
}
public void testReflectionHierarchyCompare() {
testReflectionHierarchyCompare(false);
testReflectionHierarchyCompare(false, null);
}
public void testReflectionHierarchyCompareExcludeFields() {
String[] excludeFields = new String[] { "b" };
testReflectionHierarchyCompare(true, excludeFields);
TestSubObject x;
TestSubObject y;
TestSubObject z;
x = new TestSubObject(1, 1);
y = new TestSubObject(2, 1);
z = new TestSubObject(3, 1);
assertXYZCompareOrder(x, y, z, true, excludeFields);
x = new TestSubObject(1, 3);
y = new TestSubObject(2, 2);
z = new TestSubObject(3, 1);
assertXYZCompareOrder(x, y, z, true, excludeFields);
}
public void testReflectionHierarchyCompareTransients() {
testReflectionHierarchyCompare(true);
testReflectionHierarchyCompare(true, null);
TestTransientSubObject x;
TestTransientSubObject y;
@ -155,29 +174,29 @@ public void testReflectionHierarchyCompareTransients() {
x = new TestTransientSubObject(1, 1);
y = new TestTransientSubObject(2, 2);
z = new TestTransientSubObject(3, 3);
assertXYZCompareOrder(x, y, z, true);
assertXYZCompareOrder(x, y, z, true, null);
x = new TestTransientSubObject(1, 1);
y = new TestTransientSubObject(1, 2);
z = new TestTransientSubObject(1, 3);
assertXYZCompareOrder(x, y, z, true);
assertXYZCompareOrder(x, y, z, true, null);
}
private void assertXYZCompareOrder(Object x, Object y, Object z, boolean testTransients) {
assertTrue(0 == CompareToBuilder.reflectionCompare(x, x, testTransients));
assertTrue(0 == CompareToBuilder.reflectionCompare(y, y, testTransients));
assertTrue(0 == CompareToBuilder.reflectionCompare(z, z, testTransients));
private void assertXYZCompareOrder(Object x, Object y, Object z, boolean testTransients, String[] excludeFields) {
assertTrue(0 == CompareToBuilder.reflectionCompare(x, x, testTransients, null, excludeFields));
assertTrue(0 == CompareToBuilder.reflectionCompare(y, y, testTransients, null, excludeFields));
assertTrue(0 == CompareToBuilder.reflectionCompare(z, z, testTransients, null, excludeFields));
assertTrue(0 > CompareToBuilder.reflectionCompare(x, y, testTransients));
assertTrue(0 > CompareToBuilder.reflectionCompare(x, z, testTransients));
assertTrue(0 > CompareToBuilder.reflectionCompare(y, z, testTransients));
assertTrue(0 > CompareToBuilder.reflectionCompare(x, y, testTransients, null, excludeFields));
assertTrue(0 > CompareToBuilder.reflectionCompare(x, z, testTransients, null, excludeFields));
assertTrue(0 > CompareToBuilder.reflectionCompare(y, z, testTransients, null, excludeFields));
assertTrue(0 < CompareToBuilder.reflectionCompare(y, x, testTransients));
assertTrue(0 < CompareToBuilder.reflectionCompare(z, x, testTransients));
assertTrue(0 < CompareToBuilder.reflectionCompare(z, y, testTransients));
assertTrue(0 < CompareToBuilder.reflectionCompare(y, x, testTransients, null, excludeFields));
assertTrue(0 < CompareToBuilder.reflectionCompare(z, x, testTransients, null, excludeFields));
assertTrue(0 < CompareToBuilder.reflectionCompare(z, y, testTransients, null, excludeFields));
}
public void testReflectionHierarchyCompare(boolean testTransients) {
public void testReflectionHierarchyCompare(boolean testTransients, String[] excludeFields) {
TestObject to1 = new TestObject(1);
TestObject to2 = new TestObject(2);
TestObject to3 = new TestObject(3);
@ -185,19 +204,19 @@ public void testReflectionHierarchyCompare(boolean testTransients) {
TestSubObject tso2 = new TestSubObject(2, 2);
TestSubObject tso3 = new TestSubObject(3, 3);
assertReflectionCompareContract(to1, to1, to1, false);
assertReflectionCompareContract(to1, to2, to3, false);
assertReflectionCompareContract(tso1, tso1, tso1, false);
assertReflectionCompareContract(tso1, tso2, tso3, false);
assertReflectionCompareContract("1", "2", "3", false);
assertReflectionCompareContract(to1, to1, to1, false, excludeFields);
assertReflectionCompareContract(to1, to2, to3, false, excludeFields);
assertReflectionCompareContract(tso1, tso1, tso1, false, excludeFields);
assertReflectionCompareContract(tso1, tso2, tso3, false, excludeFields);
assertReflectionCompareContract("1", "2", "3", false, excludeFields);
assertTrue(0 != CompareToBuilder.reflectionCompare(tso1, new TestSubObject(1, 0), testTransients));
assertTrue(0 != CompareToBuilder.reflectionCompare(tso1, new TestSubObject(0, 1), testTransients));
// root class
assertXYZCompareOrder(to1, to2, to3, true);
assertXYZCompareOrder(to1, to2, to3, true, null);
// subclass
assertXYZCompareOrder(tso1, tso2, tso3, true);
assertXYZCompareOrder(tso1, tso2, tso3, true, null);
}
/**
@ -207,20 +226,22 @@ public void testReflectionHierarchyCompare(boolean testTransients) {
* @param y an object to compare
* @param z an object to compare
* @param testTransients Whether to include transients in the comparison
* @param excludeFields fields to exclude
*/
public void assertReflectionCompareContract(Object x, Object y, Object z, boolean testTransients) {
public void assertReflectionCompareContract(Object x, Object y, Object z, boolean testTransients, String[] excludeFields) {
// signum
assertTrue(reflectionCompareSignum(x, y, testTransients) == -reflectionCompareSignum(y, x, testTransients));
assertTrue(reflectionCompareSignum(x, y, testTransients, excludeFields) == -reflectionCompareSignum(y, x, testTransients, excludeFields));
// transitive
if (CompareToBuilder.reflectionCompare(x, y, testTransients) > 0 && CompareToBuilder.reflectionCompare(y, z, testTransients) > 0){
assertTrue(CompareToBuilder.reflectionCompare(x, z, testTransients) > 0);
if (CompareToBuilder.reflectionCompare(x, y, testTransients, null, excludeFields) > 0
&& CompareToBuilder.reflectionCompare(y, z, testTransients, null, excludeFields) > 0){
assertTrue(CompareToBuilder.reflectionCompare(x, z, testTransients, null, excludeFields) > 0);
}
// un-named
if (CompareToBuilder.reflectionCompare(x, y, testTransients) == 0) {
assertTrue(reflectionCompareSignum(x, z, testTransients) == -reflectionCompareSignum(y, z, testTransients));
if (CompareToBuilder.reflectionCompare(x, y, testTransients, null, excludeFields) == 0) {
assertTrue(reflectionCompareSignum(x, z, testTransients, excludeFields) == -reflectionCompareSignum(y, z, testTransients, excludeFields));
}
// strongly recommended but not strictly required
@ -234,9 +255,10 @@ public void assertReflectionCompareContract(Object x, Object y, Object z, boolea
* @param lhs The "left-hand-side" of the comparison.
* @param rhs The "right-hand-side" of the comparison.
* @param testTransients Whether to include transients in the comparison
* @param excludeFields fields to exclude
* @return int The signum
*/
private int reflectionCompareSignum(Object lhs, Object rhs, boolean testTransients) {
private int reflectionCompareSignum(Object lhs, Object rhs, boolean testTransients, String[] excludeFields) {
return BigInteger.valueOf(CompareToBuilder.reflectionCompare(lhs, rhs, testTransients)).signum();
}