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