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