View Javadoc
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