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