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