    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.util.ArrayList;
    020import java.util.Arrays;
    021import java.util.List;
    022
    023import org.apache.commons.lang3.ArrayUtils;
    024import org.apache.commons.lang3.Validate;
    025
    026/**
    027 * <p>
    028 * Assists in implementing {@link Diffable#diff(Object)} methods.
    029 * </p>
    030 *
    031 * <p>
    032 * To use this class, write code as follows:
    033 * </p>
    034 *
    035 * <pre>
    036 * public class Person implements Diffable&lt;Person&gt; {
    037 *   String name;
    038 *   int age;
    039 *   boolean smoker;
    040 *
    041 *   ...
    042 *
    043 *   public DiffResult diff(Person obj) {
    044 *     // No need for null check, as NullPointerException correct if obj is null
    045 *     return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
    046 *       .append("name", this.name, obj.name)
    047 *       .append("age", this.age, obj.age)
    048 *       .append("smoker", this.smoker, obj.smoker)
    049 *       .build();
    050 *   }
    051 * }
    052 * </pre>
    053 *
    054 * <p>
    055 * The {@code ToStringStyle} passed to the constructor is embedded in the
    056 * returned {@code DiffResult} and influences the style of the
    057 * {@code DiffResult.toString()} method. This style choice can be overridden by
    058 * calling {@link DiffResult#toString(ToStringStyle)}.
    059 * </p>
    060 *
    061 * @since 3.3
    062 * @see Diffable
    063 * @see Diff
    064 * @see DiffResult
    065 * @see ToStringStyle
    066 */
    067public class DiffBuilder implements Builder<DiffResult> {
    068
    069    private final List<Diff<?>> diffs;
    070    private final boolean objectsTriviallyEqual;
    071    private final Object left;
    072    private final Object right;
    073    private final ToStringStyle style;
    074
    075    /**
    076     * <p>
    077     * Constructs a builder for the specified objects with the specified style.
    078     * </p>
    079     *
    080     * <p>
    081     * If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will
    082     * not evaluate any calls to {@code append(...)} and will return an empty
    083     * {@link DiffResult} when {@link #build()} is executed.
    084     * </p>
    085     *
    086     * @param lhs
    087     *            {@code this} object
    088     * @param rhs
    089     *            the object to diff against
    090     * @param style
    091     *            the style will use when outputting the objects, {@code null}
    092     *            uses the default
    093     * @param testTriviallyEqual
    094     *            If true, this will test if lhs and rhs are the same or equal.
    095     *            All of the append(fieldName, lhs, rhs) methods will abort
    096     *            without creating a field {@link Diff} if the trivially equal
    097     *            test is enabled and returns true.  The result of this test
    098     *            is never changed throughout the life of this {@link DiffBuilder}.
    099     * @throws IllegalArgumentException
    100     *             if {@code lhs} or {@code rhs} is {@code null}
    101     * @since 3.4
    102     */
    103    public DiffBuilder(final Object lhs, final Object rhs,
    104            final ToStringStyle style, final boolean testTriviallyEqual) {
    105
    106        Validate.isTrue(lhs != null, "lhs cannot be null");
    107        Validate.isTrue(rhs != null, "rhs cannot be null");
    108
    109        this.diffs = new ArrayList<>();
    110        this.left = lhs;
    111        this.right = rhs;
    112        this.style = style;
    113
    114        // Don't compare any fields if objects equal
    115        this.objectsTriviallyEqual = testTriviallyEqual && (lhs == rhs || lhs.equals(rhs));
    116    }
    117
    118    /**
    119     * <p>
    120     * Constructs a builder for the specified objects with the specified style.
    121     * </p>
    122     *
    123     * <p>
    124     * If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will
    125     * not evaluate any calls to {@code append(...)} and will return an empty
    126     * {@link DiffResult} when {@link #build()} is executed.
    127     * </p>
    128     *
    129     * <p>
    130     * This delegates to {@link #DiffBuilder(Object, Object, ToStringStyle, boolean)}
    131     * with the testTriviallyEqual flag enabled.
    132     * </p>
    133     *
    134     * @param lhs
    135     *            {@code this} object
    136     * @param rhs
    137     *            the object to diff against
    138     * @param style
    139     *            the style will use when outputting the objects, {@code null}
    140     *            uses the default
    141     * @throws IllegalArgumentException
    142     *             if {@code lhs} or {@code rhs} is {@code null}
    143     */
    144    public DiffBuilder(final Object lhs, final Object rhs,
    145            final ToStringStyle style) {
    146
    147            this(lhs, rhs, style, true);
    148    }
    149
    150    /**
    151     * <p>
    152     * Test if two {@code boolean}s are equal.
    153     * </p>
    154     *
    155     * @param fieldName
    156     *            the field name
    157     * @param lhs
    158     *            the left hand {@code boolean}
    159     * @param rhs
    160     *            the right hand {@code boolean}
    161     * @return this
    162     * @throws IllegalArgumentException
    163     *             if field name is {@code null}
    164     */
    165    public DiffBuilder append(final String fieldName, final boolean lhs,
    166            final boolean rhs) {
    167        validateFieldNameNotNull(fieldName);
    168
    169        if (objectsTriviallyEqual) {
    170            return this;
    171        }
    172        if (lhs != rhs) {
    173            diffs.add(new Diff<Boolean>(fieldName) {
    174                private static final long serialVersionUID = 1L;
    175
    176                @Override
    177                public Boolean getLeft() {
    178                    return Boolean.valueOf(lhs);
    179                }
    180
    181                @Override
    182                public Boolean getRight() {
    183                    return Boolean.valueOf(rhs);
    184                }
    185            });
    186        }
    187        return this;
    188    }
    189
    190    /**
    191     * <p>
    192     * Test if two {@code boolean[]}s are equal.
    193     * </p>
    194     *
    195     * @param fieldName
    196     *            the field name
    197     * @param lhs
    198     *            the left hand {@code boolean[]}
    199     * @param rhs
    200     *            the right hand {@code boolean[]}
    201     * @return this
    202     * @throws IllegalArgumentException
    203     *             if field name is {@code null}
    204     */
    205    public DiffBuilder append(final String fieldName, final boolean[] lhs,
    206            final boolean[] rhs) {
    207        validateFieldNameNotNull(fieldName);
    208        if (objectsTriviallyEqual) {
    209            return this;
    210        }
    211        if (!Arrays.equals(lhs, rhs)) {
    212            diffs.add(new Diff<Boolean[]>(fieldName) {
    213                private static final long serialVersionUID = 1L;
    214
    215                @Override
    216                public Boolean[] getLeft() {
    217                    return ArrayUtils.toObject(lhs);
    218                }
    219
    220                @Override
    221                public Boolean[] getRight() {
    222                    return ArrayUtils.toObject(rhs);
    223                }
    224            });
    225        }
    226        return this;
    227    }
    228
    229    /**
    230     * <p>
    231     * Test if two {@code byte}s are equal.
    232     * </p>
    233     *
    234     * @param fieldName
    235     *            the field name
    236     * @param lhs
    237     *            the left hand {@code byte}
    238     * @param rhs
    239     *            the right hand {@code byte}
    240     * @return this
    241     * @throws IllegalArgumentException
    242     *             if field name is {@code null}
    243     */
    244    public DiffBuilder append(final String fieldName, final byte lhs,
    245            final byte rhs) {
    246        validateFieldNameNotNull(fieldName);
    247        if (objectsTriviallyEqual) {
    248            return this;
    249        }
    250        if (lhs != rhs) {
    251            diffs.add(new Diff<Byte>(fieldName) {
    252                private static final long serialVersionUID = 1L;
    253
    254                @Override
    255                public Byte getLeft() {
    256                    return Byte.valueOf(lhs);
    257                }
    258
    259                @Override
    260                public Byte getRight() {
    261                    return Byte.valueOf(rhs);
    262                }
    263            });
    264        }
    265        return this;
    266    }
    267
    268    /**
    269     * <p>
    270     * Test if two {@code byte[]}s are equal.
    271     * </p>
    272     *
    273     * @param fieldName
    274     *            the field name
    275     * @param lhs
    276     *            the left hand {@code byte[]}
    277     * @param rhs
    278     *            the right hand {@code byte[]}
    279     * @return this
    280     * @throws IllegalArgumentException
    281     *             if field name is {@code null}
    282     */
    283    public DiffBuilder append(final String fieldName, final byte[] lhs,
    284            final byte[] rhs) {
    285        validateFieldNameNotNull(fieldName);
    286
    287        if (objectsTriviallyEqual) {
    288            return this;
    289        }
    290        if (!Arrays.equals(lhs, rhs)) {
    291            diffs.add(new Diff<Byte[]>(fieldName) {
    292                private static final long serialVersionUID = 1L;
    293
    294                @Override
    295                public Byte[] getLeft() {
    296                    return ArrayUtils.toObject(lhs);
    297                }
    298
    299                @Override
    300                public Byte[] getRight() {
    301                    return ArrayUtils.toObject(rhs);
    302                }
    303            });
    304        }
    305        return this;
    306    }
    307
    308    /**
    309     * <p>
    310     * Test if two {@code char}s are equal.
    311     * </p>
    312     *
    313     * @param fieldName
    314     *            the field name
    315     * @param lhs
    316     *            the left hand {@code char}
    317     * @param rhs
    318     *            the right hand {@code char}
    319     * @return this
    320     * @throws IllegalArgumentException
    321     *             if field name is {@code null}
    322     */
    323    public DiffBuilder append(final String fieldName, final char lhs,
    324            final char rhs) {
    325        validateFieldNameNotNull(fieldName);
    326
    327        if (objectsTriviallyEqual) {
    328            return this;
    329        }
    330        if (lhs != rhs) {
    331            diffs.add(new Diff<Character>(fieldName) {
    332                private static final long serialVersionUID = 1L;
    333
    334                @Override
    335                public Character getLeft() {
    336                    return Character.valueOf(lhs);
    337                }
    338
    339                @Override
    340                public Character getRight() {
    341                    return Character.valueOf(rhs);
    342                }
    343            });
    344        }
    345        return this;
    346    }
    347
    348    /**
    349     * <p>
    350     * Test if two {@code char[]}s are equal.
    351     * </p>
    352     *
    353     * @param fieldName
    354     *            the field name
    355     * @param lhs
    356     *            the left hand {@code char[]}
    357     * @param rhs
    358     *            the right hand {@code char[]}
    359     * @return this
    360     * @throws IllegalArgumentException
    361     *             if field name is {@code null}
    362     */
    363    public DiffBuilder append(final String fieldName, final char[] lhs,
    364            final char[] rhs) {
    365        validateFieldNameNotNull(fieldName);
    366
    367        if (objectsTriviallyEqual) {
    368            return this;
    369        }
    370        if (!Arrays.equals(lhs, rhs)) {
    371            diffs.add(new Diff<Character[]>(fieldName) {
    372                private static final long serialVersionUID = 1L;
    373
    374                @Override
    375                public Character[] getLeft() {
    376                    return ArrayUtils.toObject(lhs);
    377                }
    378
    379                @Override
    380                public Character[] getRight() {
    381                    return ArrayUtils.toObject(rhs);
    382                }
    383            });
    384        }
    385        return this;
    386    }
    387
    388    /**
    389     * <p>
    390     * Test if two {@code double}s are equal.
    391     * </p>
    392     *
    393     * @param fieldName
    394     *            the field name
    395     * @param lhs
    396     *            the left hand {@code double}
    397     * @param rhs
    398     *            the right hand {@code double}
    399     * @return this
    400     * @throws IllegalArgumentException
    401     *             if field name is {@code null}
    402     */
    403    public DiffBuilder append(final String fieldName, final double lhs,
    404            final double rhs) {
    405        validateFieldNameNotNull(fieldName);
    406
    407        if (objectsTriviallyEqual) {
    408            return this;
    409        }
    410        if (Double.doubleToLongBits(lhs) != Double.doubleToLongBits(rhs)) {
    411            diffs.add(new Diff<Double>(fieldName) {
    412                private static final long serialVersionUID = 1L;
    413
    414                @Override
    415                public Double getLeft() {
    416                    return Double.valueOf(lhs);
    417                }
    418
    419                @Override
    420                public Double getRight() {
    421                    return Double.valueOf(rhs);
    422                }
    423            });
    424        }
    425        return this;
    426    }
    427
    428    /**
    429     * <p>
    430     * Test if two {@code double[]}s are equal.
    431     * </p>
    432     *
    433     * @param fieldName
    434     *            the field name
    435     * @param lhs
    436     *            the left hand {@code double[]}
    437     * @param rhs
    438     *            the right hand {@code double[]}
    439     * @return this
    440     * @throws IllegalArgumentException
    441     *             if field name is {@code null}
    442     */
    443    public DiffBuilder append(final String fieldName, final double[] lhs,
    444            final double[] rhs) {
    445        validateFieldNameNotNull(fieldName);
    446
    447        if (objectsTriviallyEqual) {
    448            return this;
    449        }
    450        if (!Arrays.equals(lhs, rhs)) {
    451            diffs.add(new Diff<Double[]>(fieldName) {
    452                private static final long serialVersionUID = 1L;
    453
    454                @Override
    455                public Double[] getLeft() {
    456                    return ArrayUtils.toObject(lhs);
    457                }
    458
    459                @Override
    460                public Double[] getRight() {
    461                    return ArrayUtils.toObject(rhs);
    462                }
    463            });
    464        }
    465        return this;
    466    }
    467
    468    /**
    469     * <p>
    470     * Test if two {@code float}s are equal.
    471     * </p>
    472     *
    473     * @param fieldName
    474     *            the field name
    475     * @param lhs
    476     *            the left hand {@code float}
    477     * @param rhs
    478     *            the right hand {@code float}
    479     * @return this
    480     * @throws IllegalArgumentException
    481     *             if field name is {@code null}
    482     */
    483    public DiffBuilder append(final String fieldName, final float lhs,
    484            final float rhs) {
    485        validateFieldNameNotNull(fieldName);
    486
    487        if (objectsTriviallyEqual) {
    488            return this;
    489        }
    490        if (Float.floatToIntBits(lhs) != Float.floatToIntBits(rhs)) {
    491            diffs.add(new Diff<Float>(fieldName) {
    492                private static final long serialVersionUID = 1L;
    493
    494                @Override
    495                public Float getLeft() {
    496                    return Float.valueOf(lhs);
    497                }
    498
    499                @Override
    500                public Float getRight() {
    501                    return Float.valueOf(rhs);
    502                }
    503            });
    504        }
    505        return this;
    506    }
    507
    508    /**
    509     * <p>
    510     * Test if two {@code float[]}s are equal.
    511     * </p>
    512     *
    513     * @param fieldName
    514     *            the field name
    515     * @param lhs
    516     *            the left hand {@code float[]}
    517     * @param rhs
    518     *            the right hand {@code float[]}
    519     * @return this
    520     * @throws IllegalArgumentException
    521     *             if field name is {@code null}
    522     */
    523    public DiffBuilder append(final String fieldName, final float[] lhs,
    524            final float[] rhs) {
    525        validateFieldNameNotNull(fieldName);
    526
    527        if (objectsTriviallyEqual) {
    528            return this;
    529        }
    530        if (!Arrays.equals(lhs, rhs)) {
    531            diffs.add(new Diff<Float[]>(fieldName) {
    532                private static final long serialVersionUID = 1L;
    533
    534                @Override
    535                public Float[] getLeft() {
    536                    return ArrayUtils.toObject(lhs);
    537                }
    538
    539                @Override
    540                public Float[] getRight() {
    541                    return ArrayUtils.toObject(rhs);
    542                }
    543            });
    544        }
    545        return this;
    546    }
    547
    548    /**
    549     * <p>
    550     * Test if two {@code int}s are equal.
    551     * </p>
    552     *
    553     * @param fieldName
    554     *            the field name
    555     * @param lhs
    556     *            the left hand {@code int}
    557     * @param rhs
    558     *            the right hand {@code int}
    559     * @return this
    560     * @throws IllegalArgumentException
    561     *             if field name is {@code null}
    562     */
    563    public DiffBuilder append(final String fieldName, final int lhs,
    564            final int rhs) {
    565        validateFieldNameNotNull(fieldName);
    566
    567        if (objectsTriviallyEqual) {
    568            return this;
    569        }
    570        if (lhs != rhs) {
    571            diffs.add(new Diff<Integer>(fieldName) {
    572                private static final long serialVersionUID = 1L;
    573
    574                @Override
    575                public Integer getLeft() {
    576                    return Integer.valueOf(lhs);
    577                }
    578
    579                @Override
    580                public Integer getRight() {
    581                    return Integer.valueOf(rhs);
    582                }
    583            });
    584        }
    585        return this;
    586    }
    587
    588    /**
    589     * <p>
    590     * Test if two {@code int[]}s are equal.
    591     * </p>
    592     *
    593     * @param fieldName
    594     *            the field name
    595     * @param lhs
    596     *            the left hand {@code int[]}
    597     * @param rhs
    598     *            the right hand {@code int[]}
    599     * @return this
    600     * @throws IllegalArgumentException
    601     *             if field name is {@code null}
    602     */
    603    public DiffBuilder append(final String fieldName, final int[] lhs,
    604            final int[] rhs) {
    605        validateFieldNameNotNull(fieldName);
    606
    607        if (objectsTriviallyEqual) {
    608            return this;
    609        }
    610        if (!Arrays.equals(lhs, rhs)) {
    611            diffs.add(new Diff<Integer[]>(fieldName) {
    612                private static final long serialVersionUID = 1L;
    613
    614                @Override
    615                public Integer[] getLeft() {
    616                    return ArrayUtils.toObject(lhs);
    617                }
    618
    619                @Override
    620                public Integer[] getRight() {
    621                    return ArrayUtils.toObject(rhs);
    622                }
    623            });
    624        }
    625        return this;
    626    }
    627
    628    /**
    629     * <p>
    630     * Test if two {@code long}s are equal.
    631     * </p>
    632     *
    633     * @param fieldName
    634     *            the field name
    635     * @param lhs
    636     *            the left hand {@code long}
    637     * @param rhs
    638     *            the right hand {@code long}
    639     * @return this
    640     * @throws IllegalArgumentException
    641     *             if field name is {@code null}
    642     */
    643    public DiffBuilder append(final String fieldName, final long lhs,
    644            final long rhs) {
    645        validateFieldNameNotNull(fieldName);
    646
    647        if (objectsTriviallyEqual) {
    648            return this;
    649        }
    650        if (lhs != rhs) {
    651            diffs.add(new Diff<Long>(fieldName) {
    652                private static final long serialVersionUID = 1L;
    653
    654                @Override
    655                public Long getLeft() {
    656                    return Long.valueOf(lhs);
    657                }
    658
    659                @Override
    660                public Long getRight() {
    661                    return Long.valueOf(rhs);
    662                }
    663            });
    664        }
    665        return this;
    666    }
    667
    668    /**
    669     * <p>
    670     * Test if two {@code long[]}s are equal.
    671     * </p>
    672     *
    673     * @param fieldName
    674     *            the field name
    675     * @param lhs
    676     *            the left hand {@code long[]}
    677     * @param rhs
    678     *            the right hand {@code long[]}
    679     * @return this
    680     * @throws IllegalArgumentException
    681     *             if field name is {@code null}
    682     */
    683    public DiffBuilder append(final String fieldName, final long[] lhs,
    684            final long[] rhs) {
    685        validateFieldNameNotNull(fieldName);
    686
    687        if (objectsTriviallyEqual) {
    688            return this;
    689        }
    690        if (!Arrays.equals(lhs, rhs)) {
    691            diffs.add(new Diff<Long[]>(fieldName) {
    692                private static final long serialVersionUID = 1L;
    693
    694                @Override
    695                public Long[] getLeft() {
    696                    return ArrayUtils.toObject(lhs);
    697                }
    698
    699                @Override
    700                public Long[] getRight() {
    701                    return ArrayUtils.toObject(rhs);
    702                }
    703            });
    704        }
    705        return this;
    706    }
    707
    708    /**
    709     * <p>
    710     * Test if two {@code short}s are equal.
    711     * </p>
    712     *
    713     * @param fieldName
    714     *            the field name
    715     * @param lhs
    716     *            the left hand {@code short}
    717     * @param rhs
    718     *            the right hand {@code short}
    719     * @return this
    720     * @throws IllegalArgumentException
    721     *             if field name is {@code null}
    722     */
    723    public DiffBuilder append(final String fieldName, final short lhs,
    724            final short rhs) {
    725        validateFieldNameNotNull(fieldName);
    726
    727        if (objectsTriviallyEqual) {
    728            return this;
    729        }
    730        if (lhs != rhs) {
    731            diffs.add(new Diff<Short>(fieldName) {
    732                private static final long serialVersionUID = 1L;
    733
    734                @Override
    735                public Short getLeft() {
    736                    return Short.valueOf(lhs);
    737                }
    738
    739                @Override
    740                public Short getRight() {
    741                    return Short.valueOf(rhs);
    742                }
    743            });
    744        }
    745        return this;
    746    }
    747
    748    /**
    749     * <p>
    750     * Test if two {@code short[]}s are equal.
    751     * </p>
    752     *
    753     * @param fieldName
    754     *            the field name
    755     * @param lhs
    756     *            the left hand {@code short[]}
    757     * @param rhs
    758     *            the right hand {@code short[]}
    759     * @return this
    760     * @throws IllegalArgumentException
    761     *             if field name is {@code null}
    762     */
    763    public DiffBuilder append(final String fieldName, final short[] lhs,
    764            final short[] rhs) {
    765        validateFieldNameNotNull(fieldName);
    766
    767        if (objectsTriviallyEqual) {
    768            return this;
    769        }
    770        if (!Arrays.equals(lhs, rhs)) {
    771            diffs.add(new Diff<Short[]>(fieldName) {
    772                private static final long serialVersionUID = 1L;
    773
    774                @Override
    775                public Short[] getLeft() {
    776                    return ArrayUtils.toObject(lhs);
    777                }
    778
    779                @Override
    780                public Short[] getRight() {
    781                    return ArrayUtils.toObject(rhs);
    782                }
    783            });
    784        }
    785        return this;
    786    }
    787
    788    /**
    789     * <p>
    790     * Test if two {@code Objects}s are equal.
    791     * </p>
    792     *
    793     * @param fieldName
    794     *            the field name
    795     * @param lhs
    796     *            the left hand {@code Object}
    797     * @param rhs
    798     *            the right hand {@code Object}
    799     * @return this
    800     * @throws IllegalArgumentException
    801     *             if field name is {@code null}
    802     */
    803    public DiffBuilder append(final String fieldName, final Object lhs,
    804            final Object rhs) {
    805        validateFieldNameNotNull(fieldName);
    806        if (objectsTriviallyEqual) {
    807            return this;
    808        }
    809        if (lhs == rhs) {
    810            return this;
    811        }
    812
    813        Object objectToTest;
    814        if (lhs != null) {
    815            objectToTest = lhs;
    816        } else {
    817            // rhs cannot be null, as lhs != rhs
    818            objectToTest = rhs;
    819        }
    820
    821        if (objectToTest.getClass().isArray()) {
    822            if (objectToTest instanceof boolean[]) {
    823                return append(fieldName, (boolean[]) lhs, (boolean[]) rhs);
    824            }
    825            if (objectToTest instanceof byte[]) {
    826                return append(fieldName, (byte[]) lhs, (byte[]) rhs);
    827            }
    828            if (objectToTest instanceof char[]) {
    829                return append(fieldName, (char[]) lhs, (char[]) rhs);
    830            }
    831            if (objectToTest instanceof double[]) {
    832                return append(fieldName, (double[]) lhs, (double[]) rhs);
    833            }
    834            if (objectToTest instanceof float[]) {
    835                return append(fieldName, (float[]) lhs, (float[]) rhs);
    836            }
    837            if (objectToTest instanceof int[]) {
    838                return append(fieldName, (int[]) lhs, (int[]) rhs);
    839            }
    840            if (objectToTest instanceof long[]) {
    841                return append(fieldName, (long[]) lhs, (long[]) rhs);
    842            }
    843            if (objectToTest instanceof short[]) {
    844                return append(fieldName, (short[]) lhs, (short[]) rhs);
    845            }
    846
    847            return append(fieldName, (Object[]) lhs, (Object[]) rhs);
    848        }
    849
    850        // Not array type
    851        if (lhs != null && lhs.equals(rhs)) {
    852            return this;
    853        }
    854
    855        diffs.add(new Diff<Object>(fieldName) {
    856            private static final long serialVersionUID = 1L;
    857
    858            @Override
    859            public Object getLeft() {
    860                return lhs;
    861            }
    862
    863            @Override
    864            public Object getRight() {
    865                return rhs;
    866            }
    867        });
    868
    869        return this;
    870    }
    871
    872    /**
    873     * <p>
    874     * Test if two {@code Object[]}s are equal.
    875     * </p>
    876     *
    877     * @param fieldName
    878     *            the field name
    879     * @param lhs
    880     *            the left hand {@code Object[]}
    881     * @param rhs
    882     *            the right hand {@code Object[]}
    883     * @return this
    884     * @throws IllegalArgumentException
    885     *             if field name is {@code null}
    886     */
    887    public DiffBuilder append(final String fieldName, final Object[] lhs,
    888            final Object[] rhs) {
    889        validateFieldNameNotNull(fieldName);
    890        if (objectsTriviallyEqual) {
    891            return this;
    892        }
    893
    894        if (!Arrays.equals(lhs, rhs)) {
    895            diffs.add(new Diff<Object[]>(fieldName) {
    896                private static final long serialVersionUID = 1L;
    897
    898                @Override
    899                public Object[] getLeft() {
    900                    return lhs;
    901                }
    902
    903                @Override
    904                public Object[] getRight() {
    905                    return rhs;
    906                }
    907            });
    908        }
    909
    910        return this;
    911    }
    912
    913    /**
    914     * <p>
    915     * Append diffs from another {@code DiffResult}.
    916     * </p>
    917     *
    918     * <p>
    919     * This method is useful if you want to compare properties which are
    920     * themselves Diffable and would like to know which specific part of
    921     * it is different.
    922     * </p>
    923     *
    924     * <pre>
    925     * public class Person implements Diffable&lt;Person&gt; {
    926     *   String name;
    927     *   Address address; // implements Diffable&lt;Address&gt;
    928     *
    929     *   ...
    930     *
    931     *   public DiffResult diff(Person obj) {
    932     *     return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
    933     *       .append("name", this.name, obj.name)
    934     *       .append("address", this.address.diff(obj.address))
    935     *       .build();
    936     *   }
    937     * }
    938     * </pre>
    939     *
    940     * @param fieldName
    941     *            the field name
    942     * @param diffResult
    943     *            the {@code DiffResult} to append
    944     * @return this
    945     * @throws IllegalArgumentException
    946     *             if field name is {@code null}
    947     * @since 3.5
    948     */
    949    public DiffBuilder append(final String fieldName,
    950            final DiffResult diffResult) {
    951        validateFieldNameNotNull(fieldName);
    952        Validate.isTrue(diffResult != null, "Diff result cannot be null");
    953        if (objectsTriviallyEqual) {
    954            return this;
    955        }
    956
    957        for (final Diff<?> diff : diffResult.getDiffs()) {
    958            append(fieldName + "." + diff.getFieldName(),
    959                   diff.getLeft(), diff.getRight());
    960        }
    961
    962        return this;
    963    }
    964
    965    /**
    966     * <p>
    967     * Builds a {@link DiffResult} based on the differences appended to this
    968     * builder.
    969     * </p>
    970     *
    971     * @return a {@code DiffResult} containing the differences between the two
    972     *         objects.
    973     */
    974    @Override
    975    public DiffResult build() {
    976        return new DiffResult(left, right, diffs, style);
    977    }
    978
    979    private void validateFieldNameNotNull(final String fieldName) {
    980        Validate.isTrue(fieldName != null, "Field name cannot be null");
    981    }
    982
    983}