From 2f4408d33c1de88bceff83d7ea03e47b3aadacaf Mon Sep 17 00:00:00 2001 From: Henri Yandell Date: Tue, 25 Apr 2006 23:15:05 +0000 Subject: [PATCH] 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 --- .../lang/builder/CompareToBuilder.java | 53 ++++++++++-- .../lang/builder/CompareToBuilderTest.java | 82 ++++++++++++------- 2 files changed, 98 insertions(+), 37 deletions(-) diff --git a/src/java/org/apache/commons/lang/builder/CompareToBuilder.java b/src/java/org/apache/commons/lang/builder/CompareToBuilder.java index cc7cf2321..5a398fcea 100644 --- a/src/java/org/apache/commons/lang/builder/CompareToBuilder.java +++ b/src/java/org/apache/commons/lang/builder/CompareToBuilder.java @@ -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 lhs */ 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 lhs */ public static int reflectionCompare(Object lhs, Object rhs, boolean compareTransients) { - return reflectionCompare(lhs, rhs, compareTransients, null); + return reflectionCompare(lhs, rhs, compareTransients, null, null); + } + + /** + *

Compares two Objects via reflection.

+ * + *

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

+ * + * + * + *

If both lhs and rhs are null, + * they are considered equal.

+ * + * @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 lhs + * is less than, equal to, or greater than rhs + * @throws NullPointerException if either lhs or rhs + * (but not both) is null + * @throws ClassCastException if rhs is not assignment-compatible + * with lhs + */ + 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 lhs * @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 Class that defines fields to be compared * @param builder CompareToBuilder 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 { diff --git a/src/test/org/apache/commons/lang/builder/CompareToBuilderTest.java b/src/test/org/apache/commons/lang/builder/CompareToBuilderTest.java index a85479e02..bef56cc98 100644 --- a/src/test/org/apache/commons/lang/builder/CompareToBuilderTest.java +++ b/src/test/org/apache/commons/lang/builder/CompareToBuilderTest.java @@ -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(); }