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