LANG-1034: Add support for recursive comparison to EqualsBuilder#reflectionEquals (closes #202)
minimal clean-ups: remove getter methods, improve javadoc, add unit test for reflectionAppend Project: http://git-wip-us.apache.org/repos/asf/commons-lang/repo Commit: http://git-wip-us.apache.org/repos/asf/commons-lang/commit/0f6a292a Tree: http://git-wip-us.apache.org/repos/asf/commons-lang/tree/0f6a292a Diff: http://git-wip-us.apache.org/repos/asf/commons-lang/diff/0f6a292a Branch: refs/heads/master Commit: 0f6a292a29fedd49741310cd517ac4ba907bf8d4 Parents: 0095d8a Author: pascalschumacher <pascalschumac...@gmx.net> Authored: Sun Nov 6 19:38:43 2016 +0100 Committer: pascalschumacher <pascalschumac...@gmx.net> Committed: Sun Nov 13 18:47:41 2016 +0100 ---------------------------------------------------------------------- src/changes/changes.xml | 1 + .../commons/lang3/builder/EqualsBuilder.java | 78 ++++++-------------- .../lang3/builder/EqualsBuilderTest.java | 18 +++++ 3 files changed, 40 insertions(+), 57 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-lang/blob/0f6a292a/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index ecfa481..bed48fb 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -55,6 +55,7 @@ The <action> type attribute can be add,update,fix,remove. <action issue="LANG-1070" type="fix" dev="pschumacher" due-to="Paul Pogonyshev">ArrayUtils#add confusing example in javadoc</action> <action issue="LANG-1271" type="fix" dev="pschumacher" due-to="Pierre Templier">StringUtils#isAnyEmpty and #isAnyBlank should return false for an empty array</action> <action issue="LANG-1155" type="fix" dev="pschumacher" due-to="Saif Asif, Thiago Andrade">Add StringUtils#unwrap</action> + <action issue="LANG-1034" type="add" dev="pschumacher" due-to="Yathos UG">Add support for recursive comparison to EqualsBuilder#reflectionEquals</action> <action issue="LANG-740" type="add" dev="pschumacher" due-to="James Sawle">Implementation of a Memomizer</action> <action issue="LANG-1258" type="add" dev="pschumacher" due-to="IG, Grzegorz Rożniecki">Add ArrayUtils#toStringArray method</action> <action issue="LANG-1160" type="add" dev="kinow">StringUtils#abbreviate should support 'custom ellipses' parameter</action> http://git-wip-us.apache.org/repos/asf/commons-lang/blob/0f6a292a/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java ---------------------------------------------------------------------- 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 5f1c8e0..a58e799 100644 --- a/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java +++ b/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java @@ -229,19 +229,10 @@ public class EqualsBuilder implements Builder<Boolean> { //------------------------------------------------------------------------- /** - * Whether calls of {@link #reflectionAppend(Object, Object)} - * will test transient fields, too. - * @return boolean - */ - public boolean isTestTransients() { - return testTransients; - } - - /** - * Set testing transients behavior for calls - * of {@link #reflectionAppend(Object, Object)}. + * Set whether to include transient fields when reflectively comparing objects. * @param testTransients whether to test transient fields * @return EqualsBuilder - used to chain calls. + * @since 3.6 */ public EqualsBuilder setTestTransients(boolean testTransients) { this.testTransients = testTransients; @@ -249,21 +240,10 @@ public class EqualsBuilder implements Builder<Boolean> { } /** - * Whether calls of {@link #append(Object, Object)} - * will recursively test non primitive fields by - * using this <code>EqualsBuilder</code> or b< - * using <code>equals()</code>. - * @return boolean - */ - public boolean isTestRecursive() { - return testRecursive; - } - - /** - * Set recursive test behavior - * of {@link #reflectionAppend(Object, Object)}. + * Set whether to include transient fields when reflectively comparing objects. * @param testRecursive whether to do a recursive test * @return EqualsBuilder - used to chain calls. + * @since 3.6 */ public EqualsBuilder setTestRecursive(boolean testRecursive) { this.testRecursive = testRecursive; @@ -271,19 +251,10 @@ public class EqualsBuilder implements Builder<Boolean> { } /** - * The superclass to reflect up to (maybe <code>null</code>) - * at reflective tests. - * @return Class <code>null</code> is same as - * <code>java.lang.Object</code> - */ - public Class<?> getReflectUpToClass() { - return reflectUpToClass; - } - - /** - * Set the superclass to reflect up to - * at reflective tests. + * Set the superclass to reflect up to at reflective tests. + * @param reflectUpToClass the super class to reflect up to * @return EqualsBuilder - used to chain calls. + * @since 3.6 */ public EqualsBuilder setReflectUpToClass(Class<?> reflectUpToClass) { this.reflectUpToClass = reflectUpToClass; @@ -291,18 +262,10 @@ public class EqualsBuilder implements Builder<Boolean> { } /** - * Fields names which will be ignored in any class - * by reflection tests. - * @return String[] maybe null. - */ - public String[] getExcludeFields() { - return excludeFields; - } - - /** * Set field names to be excluded by reflection tests. - * @param excludeFields + * @param excludeFields the fields to exclude * @return EqualsBuilder - used to chain calls. + * @since 3.6 */ public EqualsBuilder setExcludeFields(String... excludeFields) { this.excludeFields = excludeFields; @@ -430,7 +393,7 @@ public class EqualsBuilder implements Builder<Boolean> { * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private * fields. This means that it will throw a security exception if run under * a security manager, if the permissions are not set up correctly. It is also - * not as efficient as testing explicitly. Non-primitive fields are compared using + * not as efficient as testing explicitly. Non-primitive fields are compared using * <code>equals()</code>.</p> * * <p>If the testTransients parameter is set to <code>true</code>, transient @@ -457,6 +420,7 @@ public class EqualsBuilder implements Builder<Boolean> { * @return <code>true</code> if the two Objects have tested equals. * * @see EqualsExclude + * @since 3.6 */ public static boolean reflectionEquals(final Object lhs, final Object rhs, final boolean testTransients, final Class<?> reflectUpToClass, boolean testRecursive, final String... excludeFields) { @@ -466,14 +430,13 @@ public class EqualsBuilder implements Builder<Boolean> { if (lhs == null || rhs == null) { return false; } - final EqualsBuilder equalsBuilder = new EqualsBuilder(); - equalsBuilder.setExcludeFields(excludeFields) + return new EqualsBuilder() + .setExcludeFields(excludeFields) .setReflectUpToClass(reflectUpToClass) .setTestTransients(testTransients) - .setTestRecursive(testRecursive); - - equalsBuilder.reflectionAppend(lhs, rhs); - return equalsBuilder.isEquals(); + .setTestRecursive(testRecursive) + .reflectionAppend(lhs, rhs) + .isEquals(); } /** @@ -500,9 +463,9 @@ public class EqualsBuilder implements Builder<Boolean> { * @return EqualsBuilder - used to chain calls. */ public EqualsBuilder reflectionAppend(final Object lhs, final Object rhs) { - if(!isEquals) + if (!isEquals) { return this; - + } if (lhs == rhs) { return this; } @@ -510,6 +473,7 @@ public class EqualsBuilder implements Builder<Boolean> { isEquals = false; return this; } + // Find the leaf class since there may be transients in the leaf // class or in classes between the leaf and root. // If we are not testing transients or a subclass has no ivars, @@ -534,7 +498,7 @@ public class EqualsBuilder implements Builder<Boolean> { isEquals = false; return this; } - + try { if (testClass.isArray()) { append(lhs, rhs); @@ -643,7 +607,7 @@ public class EqualsBuilder implements Builder<Boolean> { final Class<?> lhsClass = lhs.getClass(); if (!lhsClass.isArray()) { // The simple case, not an array, just test the element - if(testRecursive && !ClassUtils.isPrimitiveOrWrapper(lhsClass)) { + if (testRecursive && !ClassUtils.isPrimitiveOrWrapper(lhsClass)) { reflectionAppend(lhs, rhs); } else { isEquals = lhs.equals(rhs); http://git-wip-us.apache.org/repos/asf/commons-lang/blob/0f6a292a/src/test/java/org/apache/commons/lang3/builder/EqualsBuilderTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/lang3/builder/EqualsBuilderTest.java b/src/test/java/org/apache/commons/lang3/builder/EqualsBuilderTest.java index c2551af..ccdd16f 100644 --- a/src/test/java/org/apache/commons/lang3/builder/EqualsBuilderTest.java +++ b/src/test/java/org/apache/commons/lang3/builder/EqualsBuilderTest.java @@ -1298,5 +1298,23 @@ public class EqualsBuilderTest { assertTrue(EqualsBuilder.reflectionEquals(one, two)); } + @Test + public void testReflectionAppend() { + assertTrue(EqualsBuilder.reflectionEquals(null, null)); + + final TestObject o1 = new TestObject(4); + final TestObject o2 = new TestObject(5); + assertTrue(new EqualsBuilder().reflectionAppend(o1, o1).build()); + assertFalse(new EqualsBuilder().reflectionAppend(o1, o2).build()); + + o2.setA(4); + assertTrue(new EqualsBuilder().reflectionAppend(o1, o2).build()); + + assertFalse(new EqualsBuilder().reflectionAppend(o1, this).build()); + + assertFalse(new EqualsBuilder().reflectionAppend(o1, null).build()); + assertFalse(new EqualsBuilder().reflectionAppend(null, o2).build()); + } + }