    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 = Long.compare(lhs, rhs);
    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 = Integer.compare(lhs, rhs);
    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 = Short.compare(lhs, rhs);
    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 = Character.compare(lhs, rhs);
    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 = Byte.compare(lhs, rhs);
    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) {
    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