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