1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.lang3.builder;
19
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertTrue;
22
23 import java.time.Duration;
24 import java.time.Instant;
25 import java.time.LocalDate;
26 import java.time.LocalDateTime;
27 import java.time.LocalTime;
28 import java.time.OffsetDateTime;
29 import java.time.OffsetTime;
30 import java.time.Period;
31 import java.time.Year;
32 import java.time.YearMonth;
33 import java.time.ZoneId;
34 import java.time.ZoneOffset;
35 import java.time.ZonedDateTime;
36 import java.time.chrono.HijrahDate;
37 import java.time.chrono.JapaneseDate;
38 import java.time.chrono.MinguoDate;
39 import java.time.chrono.ThaiBuddhistDate;
40 import java.time.temporal.Temporal;
41 import java.time.temporal.TemporalAccessor;
42 import java.time.temporal.TemporalAmount;
43 import java.time.temporal.TemporalField;
44 import java.time.temporal.TemporalUnit;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.List;
48 import java.util.function.Supplier;
49
50 import org.apache.commons.lang3.stream.IntStreams;
51 import org.junit.jupiter.api.Test;
52
53
54
55
56 class EqualsBuilderReflectJreImplementationTest extends AbstractBuilderTest {
57
58 static class MyCharSequence implements CharSequence {
59
60 private final char[] chars;
61
62 MyCharSequence(final char[] chars) {
63 this.chars = Arrays.copyOf(chars, chars.length);
64 }
65
66 MyCharSequence(final char[] chars, final int start, final int end) {
67 this.chars = Arrays.copyOfRange(chars, start, end);
68 }
69
70 MyCharSequence(final String string) {
71 this.chars = string.toCharArray();
72 }
73
74 @Override
75 public char charAt(final int index) {
76 return chars[index];
77 }
78
79 @Override
80 public int length() {
81 return chars.length;
82 }
83
84 @Override
85 public CharSequence subSequence(final int start, final int end) {
86 return new MyCharSequence(chars, start, end);
87 }
88
89 @Override
90 public String toString() {
91 return new String(chars);
92 }
93 }
94
95 static class MyClass implements Cloneable {
96
97 private final MyCharSequence charSequence;
98 private final MyTemporal temporal;
99 private final MyTemporalAccessor temporalAccessor;
100 private final MyTemporalAmount temporalAmount;
101 private final Object[] objects;
102 private final List<Supplier<?>> list = new ArrayList<>();
103
104 MyClass(final MyCharSequence charSequence, final MyTemporal temporal, final MyTemporalAccessor temporalAccessor,
105 final MyTemporalAmount temporalAmount) {
106 this.charSequence = charSequence;
107 this.temporal = temporal;
108 this.temporalAccessor = temporalAccessor;
109 this.temporalAmount = temporalAmount;
110 final int value = Integer.parseInt(charSequence.toString());
111 final LocalDate localDate = LocalDate.ofEpochDay(value);
112 final LocalTime localTime = LocalTime.of(value, value);
113 final LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
114 final OffsetDateTime offsetDateTime = OffsetDateTime.of(localDateTime, ZoneOffset.UTC);
115 final ZoneOffset zoneOffset = ZoneOffset.ofHours(value);
116 this.objects = new Object[] {
117
118 value,
119
120 localDate, HijrahDate.from(localDate), JapaneseDate.from(localDate), MinguoDate.from(localDate), ThaiBuddhistDate.from(localDate),
121 localDate, localTime, localDateTime, offsetDateTime, OffsetTime.of(localTime, zoneOffset), Year.of(value), YearMonth.of(value, value),
122 ZonedDateTime.of(localDateTime, zoneOffset), zoneOffset, ZoneId.of(zoneOffset.getId()) };
123 IntStreams.range(100).forEach(i -> list.add(() -> charSequence));
124 }
125
126 @Override
127 public String toString() {
128 return String.format("%s[%s, %s, %s, %s, %s]", getClass().getSimpleName(), charSequence, temporal, temporalAccessor, temporalAmount,
129 Arrays.toString(objects));
130 }
131 }
132
133 static class MyTemporal implements Temporal {
134
135 private final String string;
136 private final int value;
137 private final Duration duration;
138 private final Instant instant;
139 private final Period period;
140
141 MyTemporal(final String string) {
142 this.string = string;
143 this.value = Integer.parseInt(string);
144 this.instant = Instant.ofEpochMilli(value);
145 this.duration = Duration.between(instant, instant.plusMillis(value));
146 this.period = Period.ofDays(value);
147 }
148
149 @Override
150 public long getLong(final TemporalField field) {
151 return instant.get(field);
152 }
153
154 @Override
155 public boolean isSupported(final TemporalField field) {
156 return instant.isSupported(field);
157 }
158
159 @Override
160 public boolean isSupported(final TemporalUnit unit) {
161 return instant.isSupported(unit);
162 }
163
164 @Override
165 public Temporal plus(final long amountToAdd, final TemporalUnit unit) {
166 return instant.plus(amountToAdd, unit);
167 }
168
169 @Override
170 public String toString() {
171 return String.format("%s[%s, %s, %s, %s]", getClass().getSimpleName(), string, instant, duration, period);
172 }
173
174 @Override
175 public long until(final Temporal endExclusive, final TemporalUnit unit) {
176 return instant.until(endExclusive, unit);
177 }
178
179 @Override
180 public Temporal with(final TemporalField field, final long newValue) {
181 return instant.with(field, newValue);
182 }
183
184 }
185
186 static class MyTemporalAccessor implements TemporalAccessor {
187
188 private final String string;
189 private final int value;
190 private final Instant instant;
191 private final Duration duration;
192 private final Period period;
193
194 MyTemporalAccessor(final String string) {
195 this.string = string;
196 this.value = Integer.parseInt(string);
197 this.instant = Instant.ofEpochMilli(value);
198 this.duration = Duration.between(instant, instant.plusMillis(value));
199 this.period = Period.ofDays(value);
200 }
201
202 @Override
203 public long getLong(final TemporalField field) {
204 return instant.get(field);
205 }
206
207 @Override
208 public boolean isSupported(final TemporalField field) {
209 return instant.isSupported(field);
210 }
211
212 @Override
213 public String toString() {
214 return String.format("%s[%s, %s, %s, %s]", getClass().getSimpleName(), string, instant, duration, period);
215 }
216
217 }
218
219 static class MyTemporalAmount implements TemporalAmount {
220
221 private final String string;
222 private final int value;
223 private final Instant instant;
224 private final Duration duration;
225 private final Period period;
226
227 MyTemporalAmount(final String string) {
228 this.string = string;
229 this.value = Integer.parseInt(string);
230 this.instant = Instant.ofEpochMilli(value);
231 this.duration = Duration.between(instant, instant.plusMillis(value));
232 this.period = Period.ofDays(value);
233 }
234
235 @Override
236 public Temporal addTo(final Temporal temporal) {
237 return duration.addTo(temporal);
238 }
239
240 @Override
241 public long get(final TemporalUnit unit) {
242 return duration.get(unit);
243 }
244
245 @Override
246 public List<TemporalUnit> getUnits() {
247 return duration.getUnits();
248 }
249
250 @Override
251 public Temporal subtractFrom(final Temporal temporal) {
252 return duration.subtractFrom(temporal);
253 }
254
255 @Override
256 public String toString() {
257 return String.format("%s[%s - %s - %s - %s]", getClass().getSimpleName(), string, instant, duration, period);
258 }
259
260 }
261
262 private EqualsBuilder newRecursive() {
263 return new EqualsBuilder().setTestRecursive(true);
264 }
265
266 @Test
267 void testRecursive() {
268
269 final MyClass o1 = new MyClass(new MyCharSequence("1"), new MyTemporal("2"), new MyTemporalAccessor("3"), new MyTemporalAmount("4"));
270
271 final MyClass o1Bis = new MyClass(new MyCharSequence("1"), new MyTemporal("2"), new MyTemporalAccessor("3"), new MyTemporalAmount("4"));
272 final MyClass o2 = new MyClass(new MyCharSequence("5"), new MyTemporal("6"), new MyTemporalAccessor("7"), new MyTemporalAmount("8"));
273 final MyClass o2Bis = new MyClass(new MyCharSequence("5"), new MyTemporal("6"), new MyTemporalAccessor("7"), new MyTemporalAmount("8"));
274
275 final EqualsBuilder builder1 = newRecursive();
276 boolean defaultEq = builder1.isEquals();
277 assertEquals(defaultEq, builder1.append(new MyTemporal("1"), new MyTemporal("1")).isEquals());
278
279 final EqualsBuilder builder2 = newRecursive();
280 defaultEq = builder2.isEquals();
281 assertEquals(defaultEq, builder2.append(new MyTemporalAccessor("1"), new MyTemporalAccessor("1")).isEquals());
282
283 final EqualsBuilder builder3 = newRecursive();
284 defaultEq = builder3.isEquals();
285 assertEquals(defaultEq, builder3.append(new MyCharSequence("1"), new MyCharSequence("1")).isEquals());
286
287 assertTrue(newRecursive().append(o1, o1).isEquals(), o1::toString);
288 final EqualsBuilder builder4 = newRecursive();
289 defaultEq = builder4.isEquals();
290 assertEquals(defaultEq, builder4.append(o1, o1Bis).isEquals(), o1::toString);
291 assertTrue(newRecursive().append(o2, o2).isEquals(), o2::toString);
292 final EqualsBuilder builder5 = newRecursive();
293 defaultEq = builder5.isEquals();
294 assertEquals(defaultEq, builder5.append(o2, o2Bis).isEquals(), o2::toString);
295
296 final EqualsBuilder builder6 = newRecursive();
297 defaultEq = !isForceAccessible();
298 assertEquals(defaultEq, builder6.append(o1, o2).isEquals());
299 final EqualsBuilder builder7 = newRecursive();
300 assertEquals(defaultEq, builder7.append(o2, o1).isEquals());
301 }
302
303 @Test
304 void testRetention() throws Exception {
305
306 for (int i = 0; i < Integer.getInteger("testRetention", 10_000); i++) {
307 final Class<?> clazz = TestClassBuilder.defineSimpleClass(getClass().getPackage().getName(), i);
308 assertTrue(newRecursive().append(clazz.newInstance(), clazz.newInstance()).isEquals());
309 }
310
311 }
312
313 }