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 * https://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 java.lang.reflect.Field;
20 import java.lang.reflect.Modifier;
21 import java.util.Collection;
22 import java.util.Comparator;
23 import java.util.HashSet;
24 import java.util.Objects;
25 import java.util.Set;
26
27 import org.apache.commons.lang3.ArrayUtils;
28 import org.apache.commons.lang3.ObjectUtils;
29 import org.apache.commons.lang3.builder.AbstractReflection.AbstractBuilder;
30 import org.apache.commons.lang3.tuple.Pair;
31
32 /**
33 * Assists in implementing {@link Comparable#compareTo(Object)} methods.
34 *
35 * <p>
36 * It is consistent with {@code equals(Object)} and
37 * {@code hashCode()} built with {@link EqualsBuilder} and
38 * {@link HashCodeBuilder}.
39 * </p>
40 *
41 * <p>
42 * Two Objects that compare equal using {@code equals(Object)} should normally
43 * also compare equal using {@code compareTo(Object)}.
44 * </p>
45 *
46 * <p>
47 * All relevant fields should be included in the calculation of the
48 * comparison. Derived fields may be ignored. The same fields, in the same
49 * order, should be used in both {@code compareTo(Object)} and
50 * {@code equals(Object)}.
51 * </p>
52 *
53 * <p>
54 * To use this class write code as follows:
55 * </p>
56 *
57 * <pre>
58 * public class MyClass {
59 * String field1;
60 * int field2;
61 * boolean field3;
62 *
63 * ...
64 *
65 * public int compareTo(Object o) {
66 * MyClass myClass = (MyClass) o;
67 * return new CompareToBuilder()
68 * .appendSuper(super.compareTo(o)
69 * .append(this.field1, myClass.field1)
70 * .append(this.field2, myClass.field2)
71 * .append(this.field3, myClass.field3)
72 * .toComparison();
73 * }
74 * }
75 * </pre>
76 *
77 * <p>
78 * Values are compared in the order they are appended to the builder. If any comparison returns
79 * a non-zero result, then that value will be the result returned by {@code toComparison()} and all
80 * subsequent comparisons are skipped.
81 * </p>
82 *
83 * <p>
84 * Alternatively, there are {@link #reflectionCompare(Object, Object) reflectionCompare} methods that use
85 * reflection to determine the fields to append. Because fields can be private,
86 * {@code reflectionCompare} uses {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} to
87 * bypass normal access control checks. This will fail under a security manager,
88 * unless the appropriate permissions are set up correctly. It is also
89 * slower than appending explicitly.
90 * </p>
91 * <p>
92 * See also {@link AbstractBuilder#setForceAccessible(boolean)}
93 * </p>
94 * <p>
95 * A typical implementation of {@code compareTo(Object)} using
96 * {@code reflectionCompare} looks like:
97 * </p>
98
99 * <pre>
100 * public int compareTo(Object o) {
101 * return CompareToBuilder.reflectionCompare(this, o);
102 * }
103 * </pre>
104 *
105 * <p>
106 * The reflective methods compare object fields in the order returned by
107 * {@link Class#getDeclaredFields()}. The fields of the class are compared first, followed by those
108 * of its parent classes (in order from the bottom to the top of the class hierarchy).
109 * </p>
110 *
111 * @see Comparable
112 * @see Object#equals(Object)
113 * @see Object#hashCode()
114 * @see EqualsBuilder
115 * @see HashCodeBuilder
116 * @see AbstractBuilder#setForceAccessible(boolean)
117 * @since 1.0
118 */
119 public class CompareToBuilder extends AbstractReflection implements Builder<Integer> {
120
121 /**
122 * Builds instances of CompareToBuilder.
123 */
124 public static class Builder extends AbstractBuilder<Builder> {
125
126 /**
127 * Constructs a new Builder instance.
128 */
129 private Builder() {
130 // empty
131 }
132
133 @Override
134 public CompareToBuilder get() {
135 return new CompareToBuilder(this);
136 }
137
138 }
139
140 /**
141 * A registry of objects to detect cyclical object references, avoid infinite loops, and stack overflows.
142 */
143 private static final ThreadLocal<Set<Pair<IDKey, IDKey>>> REGISTRY = ThreadLocal.withInitial(HashSet::new);
144
145 /**
146 * Constructs a new Builder.
147 *
148 * @return A new Builder.
149 */
150 public static Builder builder() {
151 return new Builder();
152 }
153
154 /**
155 * Gets the registry of object pairs being traversed by the reflection
156 * methods in the current thread.
157 *
158 * @return Set the registry of objects being traversed.
159 */
160 static Set<Pair<IDKey, IDKey>> getRegistry() {
161 return REGISTRY.get();
162 }
163
164 /**
165 * Tests whether the registry contains the given object pair.
166 * <p>
167 * Used by the reflection methods to avoid infinite loops.
168 * Objects might be swapped therefore a check is needed if the object pair
169 * is registered in the given or swapped order.
170 * </p>
171 *
172 * @param lhs {@code this} object to lookup in registry.
173 * @param rhs The other object to lookup on registry.
174 * @return boolean {@code true} if the registry contains the given object.
175 */
176 static boolean isRegistered(final Object lhs, final Object rhs) {
177 return isRegistered(lhs, rhs, getRegistry());
178 }
179
180 /**
181 * Appends to {@code builder} the comparison of {@code lhs}
182 * to {@code rhs} using the fields defined in {@code clazz}.
183 *
184 * @param lhs left-hand side object.
185 * @param rhs right-hand side object.
186 * @param clazz {@link Class} that defines fields to be compared.
187 * @param builder {@link CompareToBuilder} to append to.
188 * @param useTransients whether to compare transient fields.
189 * @param excludeFields fields to exclude.
190 * @param forceAccessible Whether to set fields' accessible flags.
191 */
192 private static void reflectionAppend(
193 final Object lhs,
194 final Object rhs,
195 final Class<?> clazz,
196 final CompareToBuilder builder,
197 final boolean useTransients,
198 final String[] excludeFields,
199 final boolean forceAccessible) {
200
201 final Field[] fields = clazz.getDeclaredFields();
202 for (int i = 0; i < fields.length && builder.comparison == 0; i++) {
203 final Field field = fields[i];
204 final String name = field.getName();
205 if (!ArrayUtils.contains(excludeFields, name)
206 && !name.contains("$")
207 && (useTransients || !Modifier.isTransient(field.getModifiers()))
208 && !Modifier.isStatic(field.getModifiers())) {
209 if (setAccessible(forceAccessible, field)) {
210 // IllegalAccessException can't happen. Would get a Security exception instead.
211 // Throw a runtime exception in case the impossible happens.
212 builder.append(Reflection.getUnchecked(field, lhs), Reflection.getUnchecked(field, rhs));
213 }
214 }
215 }
216 }
217
218 /**
219 * Compares two {@link Object}s via reflection.
220 * <p>
221 * Fields can be private, thus {@code AccessibleObject.setAccessible} is used to bypass normal access control checks. This will fail under a security
222 * manager unless the appropriate permissions are set.
223 * </p>
224 * <ul>
225 * <li>Static fields will not be compared</li>
226 * <li>Transient members will be not be compared, as they are likely derived fields</li>
227 * <li>Superclass fields will be compared</li>
228 * </ul>
229 * <p>
230 * If both {@code lhs} and {@code rhs} are {@code null}, they are considered equal.
231 * </p>
232 *
233 * @param lhs left-hand side object.
234 * @param rhs right-hand side object.
235 * @return A negative integer, zero, or a positive integer as {@code lhs} is less than, equal to, or greater than {@code rhs}.
236 * @throws NullPointerException Thrown if either (but not both) parameters are {@code null}.
237 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}.
238 */
239 public static int reflectionCompare(final Object lhs, final Object rhs) {
240 return reflectionCompare(lhs, rhs, false, null);
241 }
242
243 /**
244 * Compares two {@link Object}s via reflection.
245 * <p>
246 * Fields can be private, thus {@code AccessibleObject.setAccessible} is used to bypass normal access control checks. This will fail under a security
247 * manager unless the appropriate permissions are set.
248 * </p>
249 * <ul>
250 * <li>Static fields will not be compared</li>
251 * <li>If {@code compareTransients} is {@code true}, compares transient members. Otherwise ignores them, as they are likely derived fields.</li>
252 * <li>Superclass fields will be compared</li>
253 * </ul>
254 * <p>
255 * If both {@code lhs} and {@code rhs} are {@code null}, they are considered equal.
256 * </p>
257 *
258 * @param lhs left-hand side object.
259 * @param rhs right-hand side object.
260 * @param compareTransients whether to compare transient fields.
261 * @return A negative integer, zero, or a positive integer as {@code lhs} is less than, equal to, or greater than {@code rhs}.
262 * @throws NullPointerException Thrown if either {@code lhs} or {@code rhs} (but not both) is {@code null}.
263 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}.
264 */
265 public static int reflectionCompare(final Object lhs, final Object rhs, final boolean compareTransients) {
266 return reflectionCompare(lhs, rhs, compareTransients, null);
267 }
268
269 /**
270 * Compares two {@link Object}s via reflection.
271 * <p>
272 * Fields can be private, thus {@code AccessibleObject.setAccessible} is used to bypass normal access control checks. This will fail under a security
273 * manager unless the appropriate permissions are set.
274 * </p>
275 * <ul>
276 * <li>Static fields will not be compared</li>
277 * <li>If the {@code compareTransients} is {@code true}, compares transient members. Otherwise ignores them, as they are likely derived fields.</li>
278 * <li>Compares superclass fields up to and including {@code reflectUpToClass}. If {@code reflectUpToClass} is {@code null}, compares all superclass
279 * fields.</li>
280 * </ul>
281 * <p>
282 * If both {@code lhs} and {@code rhs} are {@code null}, they are considered equal.
283 * </p>
284 *
285 * @param lhs left-hand side object.
286 * @param rhs right-hand side object.
287 * @param compareTransients whether to compare transient fields.
288 * @param reflectUpToClass last superclass for which fields are compared.
289 * @param excludeFields fields to exclude.
290 * @return A negative integer, zero, or a positive integer as {@code lhs} is less than, equal to, or greater than {@code rhs}.
291 * @throws NullPointerException Thrown if either {@code lhs} or {@code rhs} (but not both) is {@code null}.
292 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}.
293 * @since 2.2 (2.0 as {@code reflectionCompare(Object, Object, boolean, Class)}).
294 */
295 public static int reflectionCompare(
296 final Object lhs,
297 final Object rhs,
298 final boolean compareTransients,
299 final Class<?> reflectUpToClass,
300 final String... excludeFields) {
301 if (lhs == rhs) {
302 return 0;
303 }
304 Objects.requireNonNull(lhs, "lhs");
305 Objects.requireNonNull(rhs, "rhs");
306 Class<?> lhsClazz = lhs.getClass();
307 if (!lhsClazz.isInstance(rhs)) {
308 throw new ClassCastException();
309 }
310 final CompareToBuilder compareToBuilder = new CompareToBuilder();
311 reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields, AbstractReflection.getForceAccessible());
312 while (lhsClazz.getSuperclass() != null && lhsClazz != reflectUpToClass) {
313 lhsClazz = lhsClazz.getSuperclass();
314 reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields, AbstractReflection.getForceAccessible());
315 }
316 return compareToBuilder.toComparison();
317 }
318
319 /**
320 * Compares two {@link Object}s via reflection.
321 * <p>
322 * Fields can be private, thus {@code AccessibleObject.setAccessible} is used to bypass normal access control checks. This will fail under a security
323 * manager unless the appropriate permissions are set.
324 * </p>
325 * <ul>
326 * <li>Static fields will not be compared</li>
327 * <li>If {@code compareTransients} is {@code true}, compares transient members. Otherwise ignores them, as they are likely derived fields.</li>
328 * <li>Superclass fields will be compared</li>
329 * </ul>
330 * <p>
331 * If both {@code lhs} and {@code rhs} are {@code null}, they are considered equal.
332 * </p>
333 *
334 * @param lhs left-hand side object.
335 * @param rhs right-hand side object.
336 * @param excludeFields Collection of String fields to exclude.
337 * @return A negative integer, zero, or a positive integer as {@code lhs} is less than, equal to, or greater than {@code rhs}.
338 * @throws NullPointerException Thrown if either {@code lhs} or {@code rhs} (but not both) is {@code null}.
339 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}.
340 * @since 2.2
341 */
342 public static int reflectionCompare(final Object lhs, final Object rhs, final Collection<String> excludeFields) {
343 return reflectionCompare(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
344 }
345
346 /**
347 * Compares two {@link Object}s via reflection.
348 * <p>
349 * Fields can be private, thus {@code AccessibleObject.setAccessible} is used to bypass normal access control checks. This will fail under a security
350 * manager unless the appropriate permissions are set.
351 * </p>
352 * <ul>
353 * <li>Static fields will not be compared</li>
354 * <li>If {@code compareTransients} is {@code true}, compares transient members. Otherwise ignores them, as they are likely derived fields.</li>
355 * <li>Superclass fields will be compared</li>
356 * </ul>
357 * <p>
358 * If both {@code lhs} and {@code rhs} are {@code null}, they are considered equal.
359 * </p>
360 *
361 * @param lhs left-hand side object.
362 * @param rhs right-hand side object.
363 * @param excludeFields array of fields to exclude.
364 * @return A negative integer, zero, or a positive integer as {@code lhs} is less than, equal to, or greater than {@code rhs}.
365 * @throws NullPointerException Thrown if either {@code lhs} or {@code rhs} (but not both) is {@code null}.
366 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}.
367 * @since 2.2
368 */
369 public static int reflectionCompare(final Object lhs, final Object rhs, final String... excludeFields) {
370 return reflectionCompare(lhs, rhs, false, null, excludeFields);
371 }
372
373 /**
374 * Registers the given object pair. Used by the reflection methods to avoid infinite loops.
375 *
376 * @param lhs {@code this} object to register.
377 * @param rhs The other object to register.
378 */
379 private static void register(final Object lhs, final Object rhs) {
380 register(lhs, rhs, getRegistry());
381 }
382
383 /**
384 * Unregisters the given object pair.
385 * <p>
386 * Used by the reflection methods to avoid infinite loops.
387 * </p>
388 *
389 * @param lhs {@code this} object to unregister.
390 * @param rhs The other object to unregister.
391 */
392 private static void unregister(final Object lhs, final Object rhs) {
393 unregister(lhs, rhs, getRegistry(), REGISTRY);
394 }
395
396 /**
397 * Current state of the comparison as appended fields are checked.
398 */
399 private int comparison;
400
401 /**
402 * Constructor for CompareToBuilder.
403 * <p>
404 * Starts off assuming that the objects are equal. Multiple calls are then made to the various append methods, followed by a call to {@link #toComparison}
405 * to get the result.
406 * </p>
407 */
408 public CompareToBuilder() {
409 super(builder());
410 comparison = 0;
411 }
412
413 private CompareToBuilder(final Builder builder) {
414 super(builder);
415 }
416
417 /**
418 * Appends to the {@code builder} the comparison of two {@code booleans}s.
419 *
420 * @param lhs left-hand side value.
421 * @param rhs right-hand side value.
422 * @return {@code this} instance.
423 */
424 public CompareToBuilder append(final boolean lhs, final boolean rhs) {
425 if (comparison != 0 || lhs == rhs) {
426 return this;
427 }
428 comparison = lhs ? 1 : -1;
429 return this;
430 }
431
432 /**
433 * Appends to the {@code builder} the deep comparison of two {@code boolean} arrays.
434 * <ol>
435 * <li>Check if arrays are the same using {@code ==}</li>
436 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
437 * <li>Check array length, a shorter length array is less than a longer length array</li>
438 * <li>Check array contents element by element using {@link #append(boolean, boolean)}</li>
439 * </ol>
440 *
441 * @param lhs left-hand side array.
442 * @param rhs right-hand side array.
443 * @return {@code this} instance.
444 */
445 public CompareToBuilder append(final boolean[] lhs, final boolean[] rhs) {
446 if (comparison != 0 || lhs == rhs) {
447 return this;
448 }
449 if (lhs == null) {
450 comparison = -1;
451 return this;
452 }
453 if (rhs == null) {
454 comparison = 1;
455 return this;
456 }
457 if (lhs.length != rhs.length) {
458 comparison = lhs.length < rhs.length ? -1 : 1;
459 return this;
460 }
461 for (int i = 0; i < lhs.length && comparison == 0; i++) {
462 append(lhs[i], rhs[i]);
463 }
464 return this;
465 }
466
467 /**
468 * Appends to the {@code builder} the comparison of two {@code byte}s.
469 *
470 * @param lhs left-hand side value.
471 * @param rhs right-hand side value.
472 * @return {@code this} instance.
473 */
474 public CompareToBuilder append(final byte lhs, final byte rhs) {
475 if (comparison != 0) {
476 return this;
477 }
478 comparison = Byte.compare(lhs, rhs);
479 return this;
480 }
481
482 /**
483 * Appends to the {@code builder} the deep comparison of two {@code byte} arrays.
484 * <ol>
485 * <li>Check if arrays are the same using {@code ==}</li>
486 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
487 * <li>Check array length, a shorter length array is less than a longer length array</li>
488 * <li>Check array contents element by element using {@link #append(byte, byte)}</li>
489 * </ol>
490 *
491 * @param lhs left-hand side array.
492 * @param rhs right-hand side array.
493 * @return {@code this} instance.
494 */
495 public CompareToBuilder append(final byte[] lhs, final byte[] rhs) {
496 if (comparison != 0 || lhs == rhs) {
497 return this;
498 }
499 if (lhs == null) {
500 comparison = -1;
501 return this;
502 }
503 if (rhs == null) {
504 comparison = 1;
505 return this;
506 }
507 if (lhs.length != rhs.length) {
508 comparison = lhs.length < rhs.length ? -1 : 1;
509 return this;
510 }
511 for (int i = 0; i < lhs.length && comparison == 0; i++) {
512 append(lhs[i], rhs[i]);
513 }
514 return this;
515 }
516
517 /**
518 * Appends to the {@code builder} the comparison of two {@code char}s.
519 *
520 * @param lhs left-hand side value.
521 * @param rhs right-hand side value.
522 * @return {@code this} instance.
523 */
524 public CompareToBuilder append(final char lhs, final char rhs) {
525 if (comparison != 0) {
526 return this;
527 }
528 comparison = Character.compare(lhs, rhs);
529 return this;
530 }
531
532 /**
533 * Appends to the {@code builder} the deep comparison of two {@code char} arrays.
534 * <ol>
535 * <li>Check if arrays are the same using {@code ==}</li>
536 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
537 * <li>Check array length, a shorter length array is less than a longer length array</li>
538 * <li>Check array contents element by element using {@link #append(char, char)}</li>
539 * </ol>
540 *
541 * @param lhs left-hand side array.
542 * @param rhs right-hand side array.
543 * @return {@code this} instance.
544 */
545 public CompareToBuilder append(final char[] lhs, final char[] rhs) {
546 if (comparison != 0 || lhs == rhs) {
547 return this;
548 }
549 if (lhs == null) {
550 comparison = -1;
551 return this;
552 }
553 if (rhs == null) {
554 comparison = 1;
555 return this;
556 }
557 if (lhs.length != rhs.length) {
558 comparison = lhs.length < rhs.length ? -1 : 1;
559 return this;
560 }
561 for (int i = 0; i < lhs.length && comparison == 0; i++) {
562 append(lhs[i], rhs[i]);
563 }
564 return this;
565 }
566
567 /**
568 * Appends to the {@code builder} the comparison of two {@code double}s.
569 * <p>
570 * This handles NaNs, Infinities, and {@code -0.0}.
571 * </p>
572 * <p>
573 * It is compatible with the hash code generated by {@link HashCodeBuilder}.
574 * </p>
575 *
576 * @param lhs left-hand side value.
577 * @param rhs right-hand side value.
578 * @return {@code this} instance.
579 */
580 public CompareToBuilder append(final double lhs, final double rhs) {
581 if (comparison != 0) {
582 return this;
583 }
584 comparison = Double.compare(lhs, rhs);
585 return this;
586 }
587
588 /**
589 * Appends to the {@code builder} the deep comparison of two {@code double} arrays.
590 * <ol>
591 * <li>Check if arrays are the same using {@code ==}</li>
592 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
593 * <li>Check array length, a shorter length array is less than a longer length array</li>
594 * <li>Check array contents element by element using {@link #append(double, double)}</li>
595 * </ol>
596 *
597 * @param lhs left-hand side array.
598 * @param rhs right-hand side array.
599 * @return {@code this} instance.
600 */
601 public CompareToBuilder append(final double[] lhs, final double[] rhs) {
602 if (comparison != 0 || lhs == rhs) {
603 return this;
604 }
605 if (lhs == null) {
606 comparison = -1;
607 return this;
608 }
609 if (rhs == null) {
610 comparison = 1;
611 return this;
612 }
613 if (lhs.length != rhs.length) {
614 comparison = lhs.length < rhs.length ? -1 : 1;
615 return this;
616 }
617 for (int i = 0; i < lhs.length && comparison == 0; i++) {
618 append(lhs[i], rhs[i]);
619 }
620 return this;
621 }
622
623 /**
624 * Appends to the {@code builder} the comparison of two {@code float}s.
625 * <p>
626 * This handles NaNs, Infinities, and {@code -0.0}.
627 * </p>
628 * <p>
629 * It is compatible with the hash code generated by {@link HashCodeBuilder}.
630 * </p>
631 *
632 * @param lhs left-hand side value.
633 * @param rhs right-hand side value.
634 * @return {@code this} instance.
635 */
636 public CompareToBuilder append(final float lhs, final float rhs) {
637 if (comparison != 0) {
638 return this;
639 }
640 comparison = Float.compare(lhs, rhs);
641 return this;
642 }
643
644 /**
645 * Appends to the {@code builder} the deep comparison of two {@code float} arrays.
646 * <ol>
647 * <li>Check if arrays are the same using {@code ==}</li>
648 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
649 * <li>Check array length, a shorter length array is less than a longer length array</li>
650 * <li>Check array contents element by element using {@link #append(float, float)}</li>
651 * </ol>
652 *
653 * @param lhs left-hand side array.
654 * @param rhs right-hand side array.
655 * @return {@code this} instance.
656 */
657 public CompareToBuilder append(final float[] lhs, final float[] rhs) {
658 if (comparison != 0 || lhs == rhs) {
659 return this;
660 }
661 if (lhs == null) {
662 comparison = -1;
663 return this;
664 }
665 if (rhs == null) {
666 comparison = 1;
667 return this;
668 }
669 if (lhs.length != rhs.length) {
670 comparison = lhs.length < rhs.length ? -1 : 1;
671 return this;
672 }
673 for (int i = 0; i < lhs.length && comparison == 0; i++) {
674 append(lhs[i], rhs[i]);
675 }
676 return this;
677 }
678
679 /**
680 * Appends to the {@code builder} the comparison of two {@code int}s.
681 *
682 * @param lhs left-hand side value.
683 * @param rhs right-hand side value.
684 * @return {@code this} instance.
685 */
686 public CompareToBuilder append(final int lhs, final int rhs) {
687 if (comparison != 0) {
688 return this;
689 }
690 comparison = Integer.compare(lhs, rhs);
691 return this;
692 }
693
694 /**
695 * Appends to the {@code builder} the deep comparison of two {@code int} arrays.
696 * <ol>
697 * <li>Check if arrays are the same using {@code ==}</li>
698 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
699 * <li>Check array length, a shorter length array is less than a longer length array</li>
700 * <li>Check array contents element by element using {@link #append(int, int)}</li>
701 * </ol>
702 *
703 * @param lhs left-hand side array.
704 * @param rhs right-hand side array.
705 * @return {@code this} instance.
706 */
707 public CompareToBuilder append(final int[] lhs, final int[] rhs) {
708 if (comparison != 0 || lhs == rhs) {
709 return this;
710 }
711 if (lhs == null) {
712 comparison = -1;
713 return this;
714 }
715 if (rhs == null) {
716 comparison = 1;
717 return this;
718 }
719 if (lhs.length != rhs.length) {
720 comparison = lhs.length < rhs.length ? -1 : 1;
721 return this;
722 }
723 for (int i = 0; i < lhs.length && comparison == 0; i++) {
724 append(lhs[i], rhs[i]);
725 }
726 return this;
727 }
728
729 /**
730 * Appends to the {@code builder} the comparison of two {@code long}s.
731 *
732 * @param lhs left-hand side value.
733 * @param rhs right-hand side value.
734 * @return {@code this} instance.
735 */
736 public CompareToBuilder append(final long lhs, final long rhs) {
737 if (comparison != 0) {
738 return this;
739 }
740 comparison = Long.compare(lhs, rhs);
741 return this;
742 }
743
744 /**
745 * Appends to the {@code builder} the deep comparison of two {@code long} arrays.
746 * <ol>
747 * <li>Check if arrays are the same using {@code ==}</li>
748 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
749 * <li>Check array length, a shorter length array is less than a longer length array</li>
750 * <li>Check array contents element by element using {@link #append(long, long)}</li>
751 * </ol>
752 *
753 * @param lhs left-hand side array.
754 * @param rhs right-hand side array.
755 * @return {@code this} instance.
756 */
757 public CompareToBuilder append(final long[] lhs, final long[] rhs) {
758 if (comparison != 0 || lhs == rhs) {
759 return this;
760 }
761 if (lhs == null) {
762 comparison = -1;
763 return this;
764 }
765 if (rhs == null) {
766 comparison = 1;
767 return this;
768 }
769 if (lhs.length != rhs.length) {
770 comparison = lhs.length < rhs.length ? -1 : 1;
771 return this;
772 }
773 for (int i = 0; i < lhs.length && comparison == 0; i++) {
774 append(lhs[i], rhs[i]);
775 }
776 return this;
777 }
778
779 /**
780 * Appends to the {@code builder} the comparison of two {@link Object}s.
781 * <ol>
782 * <li>Check if {@code lhs == rhs}</li>
783 * <li>Check if either {@code lhs} or {@code rhs} is {@code null}, a {@code null} object is less than a non-{@code null} object</li>
784 * <li>Check the object contents</li>
785 * </ol>
786 * <p>
787 * {@code lhs} must either be an array or implement {@link Comparable}.
788 * </p>
789 *
790 * @param lhs left-hand side object.
791 * @param rhs right-hand side object.
792 * @return {@code this} instance.
793 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}.
794 */
795 public CompareToBuilder append(final Object lhs, final Object rhs) {
796 return append(lhs, rhs, null);
797 }
798
799 /**
800 * Appends to the {@code builder} the comparison of two {@link Object}s.
801 * <ol>
802 * <li>Check if {@code lhs == rhs}</li>
803 * <li>Check if either {@code lhs} or {@code rhs} is {@code null}, a {@code null} object is less than a non-{@code null} object</li>
804 * <li>Check the object contents</li>
805 * </ol>
806 * <p>
807 * If {@code lhs} is an array, array comparison methods will be used. Otherwise {@code comparator} will be used to compare the objects. If
808 * {@code comparator} is {@code null}, {@code lhs} must implement {@link Comparable} instead.
809 * </p>
810 *
811 * @param lhs left-hand side object.
812 * @param rhs right-hand side object.
813 * @param comparator {@link Comparator} used to compare the objects, {@code null} means treat lhs as {@link Comparable}
814 * @return {@code this} instance.
815 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}.
816 * @since 2.0
817 */
818 public CompareToBuilder append(final Object lhs, final Object rhs, final Comparator<?> comparator) {
819 if (comparison != 0 || lhs == rhs) {
820 return this;
821 }
822 if (lhs == null) {
823 comparison = -1;
824 return this;
825 }
826 if (rhs == null) {
827 comparison = 1;
828 return this;
829 }
830 if (isRegistered(lhs, rhs)) {
831 return this;
832 }
833 try {
834 register(lhs, rhs);
835 if (ObjectUtils.isArray(lhs)) {
836 // factor out array case in order to keep method small enough to be inlined
837 appendArray(lhs, rhs, comparator);
838 } else // the simple case, not an array, just test the element
839 if (comparator == null) {
840 @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
841 final Comparable<Object> comparable = (Comparable<Object>) lhs;
842 comparison = comparable.compareTo(rhs);
843 } else {
844 @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
845 final Comparator<Object> comparator2 = (Comparator<Object>) comparator;
846 comparison = comparator2.compare(lhs, rhs);
847 }
848 return this;
849 } finally {
850 unregister(lhs, rhs);
851 }
852 }
853
854 /**
855 * Appends to the {@code builder} the deep comparison of two {@link Object} arrays.
856 * <ol>
857 * <li>Check if arrays are the same using {@code ==}</li>
858 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
859 * <li>Check array length, a short length array is less than a long length array</li>
860 * <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
861 * </ol>
862 * <p>
863 * This method will also will be called for the top level of multi-dimensional, ragged, and multi-typed arrays.
864 * </p>
865 *
866 * @param lhs left-hand side array.
867 * @param rhs right-hand side array.
868 * @return {@code this} instance.
869 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}.
870 */
871 public CompareToBuilder append(final Object[] lhs, final Object[] rhs) {
872 return append(lhs, rhs, null);
873 }
874
875 /**
876 * Appends to the {@code builder} the deep comparison of two {@link Object} arrays.
877 * <ol>
878 * <li>Check if arrays are the same using {@code ==}</li>
879 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
880 * <li>Check array length, a short length array is less than a long length array</li>
881 * <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
882 * </ol>
883 * <p>
884 * This method will also will be called for the top level of multi-dimensional, ragged, and multi-typed arrays.
885 * </p>
886 *
887 * @param lhs left-hand side array.
888 * @param rhs right-hand side array.
889 * @param comparator {@link Comparator} to use to compare the array elements, {@code null} means to treat {@code lhs} elements as {@link Comparable}.
890 * @return {@code this} instance.
891 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}.
892 * @since 2.0
893 */
894 public CompareToBuilder append(final Object[] lhs, final Object[] rhs, final Comparator<?> comparator) {
895 if (comparison != 0 || lhs == rhs) {
896 return this;
897 }
898 if (lhs == null) {
899 comparison = -1;
900 return this;
901 }
902 if (rhs == null) {
903 comparison = 1;
904 return this;
905 }
906 if (lhs.length != rhs.length) {
907 comparison = lhs.length < rhs.length ? -1 : 1;
908 return this;
909 }
910 for (int i = 0; i < lhs.length && comparison == 0; i++) {
911 append(lhs[i], rhs[i], comparator);
912 }
913 return this;
914 }
915
916 /**
917 * Appends to the {@code builder} the comparison of two {@code short}s.
918 *
919 * @param lhs left-hand side value.
920 * @param rhs right-hand side value.
921 * @return {@code this} instance.
922 */
923 public CompareToBuilder append(final short lhs, final short rhs) {
924 if (comparison != 0) {
925 return this;
926 }
927 comparison = Short.compare(lhs, rhs);
928 return this;
929 }
930
931 /**
932 * Appends to the {@code builder} the deep comparison of two {@code short} arrays.
933 * <ol>
934 * <li>Check if arrays are the same using {@code ==}</li>
935 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li>
936 * <li>Check array length, a shorter length array is less than a longer length array</li>
937 * <li>Check array contents element by element using {@link #append(short, short)}</li>
938 * </ol>
939 *
940 * @param lhs left-hand side array.
941 * @param rhs right-hand side array.
942 * @return {@code this} instance.
943 */
944 public CompareToBuilder append(final short[] lhs, final short[] rhs) {
945 if (comparison != 0 || lhs == rhs) {
946 return this;
947 }
948 if (lhs == null) {
949 comparison = -1;
950 return this;
951 }
952 if (rhs == null) {
953 comparison = 1;
954 return this;
955 }
956 if (lhs.length != rhs.length) {
957 comparison = lhs.length < rhs.length ? -1 : 1;
958 return this;
959 }
960 for (int i = 0; i < lhs.length && comparison == 0; i++) {
961 append(lhs[i], rhs[i]);
962 }
963 return this;
964 }
965
966 private void appendArray(final Object lhs, final Object rhs, final Comparator<?> comparator) {
967 // switch on type of array, to dispatch to the correct handler
968 // handles multidimensional arrays
969 // throws a ClassCastException if rhs is not the correct array type
970 if (lhs instanceof long[]) {
971 append((long[]) lhs, (long[]) rhs);
972 } else if (lhs instanceof int[]) {
973 append((int[]) lhs, (int[]) rhs);
974 } else if (lhs instanceof short[]) {
975 append((short[]) lhs, (short[]) rhs);
976 } else if (lhs instanceof char[]) {
977 append((char[]) lhs, (char[]) rhs);
978 } else if (lhs instanceof byte[]) {
979 append((byte[]) lhs, (byte[]) rhs);
980 } else if (lhs instanceof double[]) {
981 append((double[]) lhs, (double[]) rhs);
982 } else if (lhs instanceof float[]) {
983 append((float[]) lhs, (float[]) rhs);
984 } else if (lhs instanceof boolean[]) {
985 append((boolean[]) lhs, (boolean[]) rhs);
986 } else {
987 // not an array of primitives
988 // throws a ClassCastException if rhs is not an array
989 append((Object[]) lhs, (Object[]) rhs, comparator);
990 }
991 }
992
993 /**
994 * Appends to the {@code builder} the {@code compareTo(Object)} result of the superclass.
995 *
996 * @param superCompareTo result of calling {@code super.compareTo(Object)}.
997 * @return {@code this} instance.
998 * @since 2.0
999 */
1000 public CompareToBuilder appendSuper(final int superCompareTo) {
1001 if (comparison != 0) {
1002 return this;
1003 }
1004 comparison = superCompareTo;
1005 return this;
1006 }
1007
1008 /**
1009 * Returns a negative Integer, a positive Integer, or zero as the {@code builder} has judged the "left-hand" side as less than, greater than, or equal to
1010 * the "right-hand" side.
1011 *
1012 * @return final comparison result as an Integer.
1013 * @see #toComparison()
1014 * @since 3.0
1015 */
1016 @Override
1017 public Integer build() {
1018 return Integer.valueOf(toComparison());
1019 }
1020
1021 /**
1022 * Returns a negative integer, a positive integer, or zero as the {@code builder} has judged the "left-hand" side as less than, greater than, or equal to
1023 * the "right-hand" side.
1024 *
1025 * @return final comparison result.
1026 * @see #build()
1027 */
1028 public int toComparison() {
1029 return comparison;
1030 }
1031 }
1032