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