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:
parent
8393f10b79
commit
2f4408d33c
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue