Skip to content

Commit d70d4c7

Browse files
author
Marc Cappelletti
committed
https://issues.apache.org/jira/browse/LANG-1712
Add TypedEqualsBuilder class and test
1 parent 14be5e5 commit d70d4c7

File tree

3 files changed

+210
-22
lines changed

3 files changed

+210
-22
lines changed

src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -195,10 +195,10 @@ private static void unregister(final Object lhs, final Object rhs) {
195195
}
196196

197197
/**
198-
* If the fields tested are equals.
198+
* If the fields tested are equal
199199
* The default value is {@code true}.
200200
*/
201-
private boolean isEquals = true;
201+
protected boolean isEquals = true;
202202

203203
private boolean testTransients;
204204
private boolean testRecursive;
@@ -450,6 +450,15 @@ public static boolean reflectionEquals(final Object lhs, final Object rhs, final
450450
.isEquals();
451451
}
452452

453+
/**
454+
* Indicates if we should interrupt the comparison during an appending.
455+
*
456+
* @return true if the equality is false
457+
*/
458+
protected boolean interruptEqualityCheck() {
459+
return !isEquals;
460+
}
461+
453462
/**
454463
* Tests if two {@code objects} by using reflection.
455464
*
@@ -478,7 +487,7 @@ public static boolean reflectionEquals(final Object lhs, final Object rhs, final
478487
* @return this
479488
*/
480489
public EqualsBuilder reflectionAppend(final Object lhs, final Object rhs) {
481-
if (!isEquals) {
490+
if (interruptEqualityCheck()) {
482491
return this;
483492
}
484493
if (lhs == rhs) {
@@ -583,7 +592,7 @@ private void reflectionAppend(
583592
* @since 2.0
584593
*/
585594
public EqualsBuilder appendSuper(final boolean superEquals) {
586-
if (!isEquals) {
595+
if (interruptEqualityCheck()) {
587596
return this;
588597
}
589598
isEquals = superEquals;
@@ -602,7 +611,7 @@ public EqualsBuilder appendSuper(final boolean superEquals) {
602611
* @return this
603612
*/
604613
public EqualsBuilder append(final Object lhs, final Object rhs) {
605-
if (!isEquals) {
614+
if (interruptEqualityCheck()) {
606615
return this;
607616
}
608617
if (lhs == rhs) {
@@ -670,7 +679,7 @@ private void appendArray(final Object lhs, final Object rhs) {
670679
* @return this
671680
*/
672681
public EqualsBuilder append(final long lhs, final long rhs) {
673-
if (!isEquals) {
682+
if (interruptEqualityCheck()) {
674683
return this;
675684
}
676685
isEquals = lhs == rhs;
@@ -685,7 +694,7 @@ public EqualsBuilder append(final long lhs, final long rhs) {
685694
* @return this
686695
*/
687696
public EqualsBuilder append(final int lhs, final int rhs) {
688-
if (!isEquals) {
697+
if (interruptEqualityCheck()) {
689698
return this;
690699
}
691700
isEquals = lhs == rhs;
@@ -700,7 +709,7 @@ public EqualsBuilder append(final int lhs, final int rhs) {
700709
* @return this
701710
*/
702711
public EqualsBuilder append(final short lhs, final short rhs) {
703-
if (!isEquals) {
712+
if (interruptEqualityCheck()) {
704713
return this;
705714
}
706715
isEquals = lhs == rhs;
@@ -715,7 +724,7 @@ public EqualsBuilder append(final short lhs, final short rhs) {
715724
* @return this
716725
*/
717726
public EqualsBuilder append(final char lhs, final char rhs) {
718-
if (!isEquals) {
727+
if (interruptEqualityCheck()) {
719728
return this;
720729
}
721730
isEquals = lhs == rhs;
@@ -730,7 +739,7 @@ public EqualsBuilder append(final char lhs, final char rhs) {
730739
* @return this
731740
*/
732741
public EqualsBuilder append(final byte lhs, final byte rhs) {
733-
if (!isEquals) {
742+
if (interruptEqualityCheck()) {
734743
return this;
735744
}
736745
isEquals = lhs == rhs;
@@ -751,7 +760,7 @@ public EqualsBuilder append(final byte lhs, final byte rhs) {
751760
* @return this
752761
*/
753762
public EqualsBuilder append(final double lhs, final double rhs) {
754-
if (!isEquals) {
763+
if (interruptEqualityCheck()) {
755764
return this;
756765
}
757766
return append(Double.doubleToLongBits(lhs), Double.doubleToLongBits(rhs));
@@ -771,7 +780,7 @@ public EqualsBuilder append(final double lhs, final double rhs) {
771780
* @return this
772781
*/
773782
public EqualsBuilder append(final float lhs, final float rhs) {
774-
if (!isEquals) {
783+
if (interruptEqualityCheck()) {
775784
return this;
776785
}
777786
return append(Float.floatToIntBits(lhs), Float.floatToIntBits(rhs));
@@ -785,7 +794,7 @@ public EqualsBuilder append(final float lhs, final float rhs) {
785794
* @return this
786795
*/
787796
public EqualsBuilder append(final boolean lhs, final boolean rhs) {
788-
if (!isEquals) {
797+
if (interruptEqualityCheck()) {
789798
return this;
790799
}
791800
isEquals = lhs == rhs;
@@ -806,7 +815,7 @@ public EqualsBuilder append(final boolean lhs, final boolean rhs) {
806815
* @return this
807816
*/
808817
public EqualsBuilder append(final Object[] lhs, final Object[] rhs) {
809-
if (!isEquals) {
818+
if (interruptEqualityCheck()) {
810819
return this;
811820
}
812821
if (lhs == rhs) {
@@ -837,7 +846,7 @@ public EqualsBuilder append(final Object[] lhs, final Object[] rhs) {
837846
* @return this
838847
*/
839848
public EqualsBuilder append(final long[] lhs, final long[] rhs) {
840-
if (!isEquals) {
849+
if (interruptEqualityCheck()) {
841850
return this;
842851
}
843852
if (lhs == rhs) {
@@ -868,7 +877,7 @@ public EqualsBuilder append(final long[] lhs, final long[] rhs) {
868877
* @return this
869878
*/
870879
public EqualsBuilder append(final int[] lhs, final int[] rhs) {
871-
if (!isEquals) {
880+
if (interruptEqualityCheck()) {
872881
return this;
873882
}
874883
if (lhs == rhs) {
@@ -899,7 +908,7 @@ public EqualsBuilder append(final int[] lhs, final int[] rhs) {
899908
* @return this
900909
*/
901910
public EqualsBuilder append(final short[] lhs, final short[] rhs) {
902-
if (!isEquals) {
911+
if (interruptEqualityCheck()) {
903912
return this;
904913
}
905914
if (lhs == rhs) {
@@ -930,7 +939,7 @@ public EqualsBuilder append(final short[] lhs, final short[] rhs) {
930939
* @return this
931940
*/
932941
public EqualsBuilder append(final char[] lhs, final char[] rhs) {
933-
if (!isEquals) {
942+
if (interruptEqualityCheck()) {
934943
return this;
935944
}
936945
if (lhs == rhs) {
@@ -961,7 +970,7 @@ public EqualsBuilder append(final char[] lhs, final char[] rhs) {
961970
* @return this
962971
*/
963972
public EqualsBuilder append(final byte[] lhs, final byte[] rhs) {
964-
if (!isEquals) {
973+
if (interruptEqualityCheck()) {
965974
return this;
966975
}
967976
if (lhs == rhs) {
@@ -992,7 +1001,7 @@ public EqualsBuilder append(final byte[] lhs, final byte[] rhs) {
9921001
* @return this
9931002
*/
9941003
public EqualsBuilder append(final double[] lhs, final double[] rhs) {
995-
if (!isEquals) {
1004+
if (interruptEqualityCheck()) {
9961005
return this;
9971006
}
9981007
if (lhs == rhs) {
@@ -1023,7 +1032,7 @@ public EqualsBuilder append(final double[] lhs, final double[] rhs) {
10231032
* @return this
10241033
*/
10251034
public EqualsBuilder append(final float[] lhs, final float[] rhs) {
1026-
if (!isEquals) {
1035+
if (interruptEqualityCheck()) {
10271036
return this;
10281037
}
10291038
if (lhs == rhs) {
@@ -1054,7 +1063,7 @@ public EqualsBuilder append(final float[] lhs, final float[] rhs) {
10541063
* @return this
10551064
*/
10561065
public EqualsBuilder append(final boolean[] lhs, final boolean[] rhs) {
1057-
if (!isEquals) {
1066+
if (interruptEqualityCheck()) {
10581067
return this;
10591068
}
10601069
if (lhs == rhs) {
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.lang3.builder;
18+
19+
import org.apache.commons.lang3.function.FailableFunction;
20+
21+
import java.util.Objects;
22+
import java.util.function.Function;
23+
24+
/**
25+
* An extension of {@link EqualsBuilder} that is typed and meant to append field getter functions.
26+
* it does the nullity and class equality checks before checking the appended values.
27+
*
28+
* <p>Typical use for the code is as follows:</p>
29+
* <pre>
30+
* public boolean equals(Object obj) {
31+
* return new TypedEqualsBuilder&lt;&gt;(this)
32+
* .appendBaseObject(obj)
33+
* .append(TestObject::getA)
34+
* .append(TestObject::getB)
35+
* .isEquals();
36+
* }
37+
* </pre>
38+
*
39+
* @param <T> the type of the compared object.
40+
*
41+
* @since 3.14.0
42+
*/
43+
public class TypedEqualsBuilder<T> extends EqualsBuilder {
44+
45+
private final T currentInstance;
46+
47+
private boolean sameReference = false;
48+
private T other;
49+
50+
@SuppressWarnings("unchecked")
51+
public TypedEqualsBuilder(T currentInstance, Object other) {
52+
Objects.requireNonNull(currentInstance);
53+
this.currentInstance = currentInstance;
54+
if (currentInstance == other) {
55+
sameReference = true;
56+
return;
57+
}
58+
Class<T> currentInstanceClass = (Class<T>) currentInstance.getClass();
59+
if (other == null || currentInstanceClass != other.getClass()) {
60+
isEquals = false;
61+
return;
62+
}
63+
this.other = (T) other;
64+
}
65+
66+
@Override
67+
protected boolean interruptEqualityCheck() {
68+
return sameReference || super.interruptEqualityCheck();
69+
}
70+
71+
public TypedEqualsBuilder<T> append(FailableFunction<T, ?, Exception> extractor) {
72+
if (interruptEqualityCheck()) {
73+
return this;
74+
}
75+
try {
76+
super.append(extractor.apply(currentInstance), extractor.apply(other));
77+
} catch (Exception e) {
78+
throw new RuntimeException(e);
79+
}
80+
return this;
81+
}
82+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.lang3.builder;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import static org.junit.jupiter.api.Assertions.*;
22+
23+
class TypedEqualsBuilderTest {
24+
25+
@Test
26+
void test_complete_equals() {
27+
TestObject testObject1 = new TestObject(1, 2);
28+
TestObject testObject2 = new TestObject(1, 3);
29+
TestObject testObject3 = new TestObject(1, 2);
30+
31+
assertEquals(testObject1, testObject1);
32+
assertNotEquals(testObject1, testObject2);
33+
assertNotEquals(testObject2, testObject1);
34+
35+
assertTrue(
36+
new TypedEqualsBuilder<>(testObject1, testObject2)
37+
.append(TestObject::getA)
38+
.isEquals());
39+
assertTrue(
40+
new TypedEqualsBuilder<>(testObject1, testObject3)
41+
.append(TestObject::getA)
42+
.append(TestObject::getB)
43+
.isEquals());
44+
assertFalse(
45+
new TypedEqualsBuilder<>(testObject1, testObject2)
46+
.append(TestObject::getA)
47+
.append(TestObject::getB)
48+
.isEquals());
49+
assertFalse(
50+
new TypedEqualsBuilder<>(testObject2, testObject1)
51+
.append(TestObject::getA)
52+
.append(TestObject::getB)
53+
.isEquals());
54+
assertFalse(
55+
new TypedEqualsBuilder<>(testObject1, null)
56+
.append(TestObject::getA)
57+
.isEquals());
58+
assertFalse(
59+
new TypedEqualsBuilder<>(testObject1, "")
60+
.append(TestObject::getA)
61+
.isEquals());
62+
assertThrows(RuntimeException.class,
63+
() -> new TypedEqualsBuilder<>(testObject1, testObject2)
64+
.append(TestObject::getSomethingWithException)
65+
.isEquals());
66+
}
67+
68+
private static class TestObject {
69+
int a;
70+
int b;
71+
72+
public TestObject(int a, int b) {
73+
this.a = a;
74+
this.b = b;
75+
}
76+
77+
public int getA() {
78+
return a;
79+
}
80+
81+
public int getB() {
82+
return b;
83+
}
84+
85+
public int getSomethingWithException() throws Exception {
86+
throw new Exception("something went wrong");
87+
}
88+
89+
@Override
90+
public boolean equals(Object obj) {
91+
return new TypedEqualsBuilder<>(this, obj)
92+
.append(TestObject::getA)
93+
.append(TestObject::getB)
94+
.isEquals();
95+
}
96+
}
97+
}

0 commit comments

Comments
 (0)