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