    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     * <p>
    089     * This delegates to {@link #DiffBuilder(Object, Object, ToStringStyle, boolean)}
    090     * with the testTriviallyEqual flag enabled.
    091     * </p>
    092     *
    093     * @param lhs
    094     *            {@code this} object
    095     * @param rhs
    096     *            the object to diff against
    097     * @param style
    098     *            the style will use when outputting the objects, {@code null}
    099     *            uses the default
    100     * @throws NullPointerException
    101     *             if {@code lhs} or {@code rhs} is {@code null}
    102     */
    103    public DiffBuilder(final T lhs, final T rhs,
    104            final ToStringStyle style) {
    105
    106            this(lhs, rhs, style, true);
    107    }
    108
    109    /**
    110     * Constructs a builder for the specified objects with the specified style.
    111     *
    112     * <p>
    113     * If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will
    114     * not evaluate any calls to {@code append(...)} and will return an empty
    115     * {@link DiffResult} when {@link #build()} is executed.
    116     * </p>
    117     *
    118     * @param lhs
    119     *            {@code this} object
    120     * @param rhs
    121     *            the object to diff against
    122     * @param style
    123     *            the style will use when outputting the objects, {@code null}
    124     *            uses the default
    125     * @param testTriviallyEqual
    126     *            If true, this will test if lhs and rhs are the same or equal.
    127     *            All of the append(fieldName, lhs, rhs) methods will abort
    128     *            without creating a field {@link Diff} if the trivially equal
    129     *            test is enabled and returns true.  The result of this test
    130     *            is never changed throughout the life of this {@link DiffBuilder}.
    131     * @throws NullPointerException
    132     *             if {@code lhs} or {@code rhs} is {@code null}
    133     * @since 3.4
    134     */
    135    public DiffBuilder(final T lhs, final T rhs,
    136            final ToStringStyle style, final boolean testTriviallyEqual) {
    137
    138        Objects.requireNonNull(lhs, "lhs");
    139        Objects.requireNonNull(rhs, "rhs");
    140
    141        this.diffs = new ArrayList<>();
    142        this.left = lhs;
    143        this.right = rhs;
    144        this.style = style;
    145
    146        // Don't compare any fields if objects equal
    147        this.objectsTriviallyEqual = testTriviallyEqual && Objects.equals(lhs, rhs);
    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     * Append diffs from another {@link DiffResult}.
    378     *
    379     * <p>
    380     * This method is useful if you want to compare properties which are
    381     * themselves Diffable and would like to know which specific part of
    382     * it is different.
    383     * </p>
    384     *
    385     * <pre>
    386     * public class Person implements Diffable&lt;Person&gt; {
    387     *   String name;
    388     *   Address address; // implements Diffable&lt;Address&gt;
    389     *
    390     *   ...
    391     *
    392     *   public DiffResult diff(Person obj) {
    393     *     return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
    394     *       .append("name", this.name, obj.name)
    395     *       .append("address", this.address.diff(obj.address))
    396     *       .build();
    397     *   }
    398     * }
    399     * </pre>
    400     *
    401     * @param fieldName
    402     *            the field name
    403     * @param diffResult
    404     *            the {@link DiffResult} to append
    405     * @return this
    406     * @throws NullPointerException if field name is {@code null} or diffResult is {@code null}
    407     * @since 3.5
    408     */
    409    public DiffBuilder<T> append(final String fieldName, final DiffResult<T> diffResult) {
    410        validateFieldNameNotNull(fieldName);
    411        Objects.requireNonNull(diffResult, "diffResult");
    412        if (objectsTriviallyEqual) {
    413            return this;
    414        }
    415        diffResult.getDiffs().forEach(diff -> append(fieldName + "." + diff.getFieldName(), diff.getLeft(), diff.getRight()));
    416        return this;
    417    }
    418
    419    /**
    420     * Test if two {@code double}s are equal.
    421     *
    422     * @param fieldName
    423     *            the field name
    424     * @param lhs
    425     *            the left-hand {@code double}
    426     * @param rhs
    427     *            the right-hand {@code double}
    428     * @return this
    429     * @throws NullPointerException
    430     *             if field name is {@code null}
    431     */
    432    public DiffBuilder<T> append(final String fieldName, final double lhs,
    433            final double rhs) {
    434        validateFieldNameNotNull(fieldName);
    435
    436        if (objectsTriviallyEqual) {
    437            return this;
    438        }
    439        if (Double.doubleToLongBits(lhs) != Double.doubleToLongBits(rhs)) {
    440            diffs.add(new Diff<Double>(fieldName) {
    441                private static final long serialVersionUID = 1L;
    442
    443                @Override
    444                public Double getLeft() {
    445                    return Double.valueOf(lhs);
    446                }
    447
    448                @Override
    449                public Double getRight() {
    450                    return Double.valueOf(rhs);
    451                }
    452            });
    453        }
    454        return this;
    455    }
    456
    457    /**
    458     * Test if two {@code double[]}s are equal.
    459     *
    460     * @param fieldName
    461     *            the field name
    462     * @param lhs
    463     *            the left-hand {@code double[]}
    464     * @param rhs
    465     *            the right-hand {@code double[]}
    466     * @return this
    467     * @throws NullPointerException
    468     *             if field name is {@code null}
    469     */
    470    public DiffBuilder<T> append(final String fieldName, final double[] lhs,
    471            final double[] rhs) {
    472        validateFieldNameNotNull(fieldName);
    473
    474        if (objectsTriviallyEqual) {
    475            return this;
    476        }
    477        if (!Arrays.equals(lhs, rhs)) {
    478            diffs.add(new Diff<Double[]>(fieldName) {
    479                private static final long serialVersionUID = 1L;
    480
    481                @Override
    482                public Double[] getLeft() {
    483                    return ArrayUtils.toObject(lhs);
    484                }
    485
    486                @Override
    487                public Double[] getRight() {
    488                    return ArrayUtils.toObject(rhs);
    489                }
    490            });
    491        }
    492        return this;
    493    }
    494
    495    /**
    496     * Test if two {@code float}s are equal.
    497     *
    498     * @param fieldName
    499     *            the field name
    500     * @param lhs
    501     *            the left-hand {@code float}
    502     * @param rhs
    503     *            the right-hand {@code float}
    504     * @return this
    505     * @throws NullPointerException
    506     *             if field name is {@code null}
    507     */
    508    public DiffBuilder<T> append(final String fieldName, final float lhs,
    509            final float rhs) {
    510        validateFieldNameNotNull(fieldName);
    511
    512        if (objectsTriviallyEqual) {
    513            return this;
    514        }
    515        if (Float.floatToIntBits(lhs) != Float.floatToIntBits(rhs)) {
    516            diffs.add(new Diff<Float>(fieldName) {
    517                private static final long serialVersionUID = 1L;
    518
    519                @Override
    520                public Float getLeft() {
    521                    return Float.valueOf(lhs);
    522                }
    523
    524                @Override
    525                public Float getRight() {
    526                    return Float.valueOf(rhs);
    527                }
    528            });
    529        }
    530        return this;
    531    }
    532
    533    /**
    534     * Test if two {@code float[]}s are equal.
    535     *
    536     * @param fieldName
    537     *            the field name
    538     * @param lhs
    539     *            the left-hand {@code float[]}
    540     * @param rhs
    541     *            the right-hand {@code float[]}
    542     * @return this
    543     * @throws NullPointerException
    544     *             if field name is {@code null}
    545     */
    546    public DiffBuilder<T> append(final String fieldName, final float[] lhs,
    547            final float[] rhs) {
    548        validateFieldNameNotNull(fieldName);
    549
    550        if (objectsTriviallyEqual) {
    551            return this;
    552        }
    553        if (!Arrays.equals(lhs, rhs)) {
    554            diffs.add(new Diff<Float[]>(fieldName) {
    555                private static final long serialVersionUID = 1L;
    556
    557                @Override
    558                public Float[] getLeft() {
    559                    return ArrayUtils.toObject(lhs);
    560                }
    561
    562                @Override
    563                public Float[] getRight() {
    564                    return ArrayUtils.toObject(rhs);
    565                }
    566            });
    567        }
    568        return this;
    569    }
    570
    571    /**
    572     * Test if two {@code int}s are equal.
    573     *
    574     * @param fieldName
    575     *            the field name
    576     * @param lhs
    577     *            the left-hand {@code int}
    578     * @param rhs
    579     *            the right-hand {@code int}
    580     * @return this
    581     * @throws NullPointerException
    582     *             if field name is {@code null}
    583     */
    584    public DiffBuilder<T> append(final String fieldName, final int lhs,
    585            final int rhs) {
    586        validateFieldNameNotNull(fieldName);
    587
    588        if (objectsTriviallyEqual) {
    589            return this;
    590        }
    591        if (lhs != rhs) {
    592            diffs.add(new Diff<Integer>(fieldName) {
    593                private static final long serialVersionUID = 1L;
    594
    595                @Override
    596                public Integer getLeft() {
    597                    return Integer.valueOf(lhs);
    598                }
    599
    600                @Override
    601                public Integer getRight() {
    602                    return Integer.valueOf(rhs);
    603                }
    604            });
    605        }
    606        return this;
    607    }
    608
    609    /**
    610     * Test if two {@code int[]}s are equal.
    611     *
    612     * @param fieldName
    613     *            the field name
    614     * @param lhs
    615     *            the left-hand {@code int[]}
    616     * @param rhs
    617     *            the right-hand {@code int[]}
    618     * @return this
    619     * @throws NullPointerException
    620     *             if field name is {@code null}
    621     */
    622    public DiffBuilder<T> append(final String fieldName, final int[] lhs,
    623            final int[] rhs) {
    624        validateFieldNameNotNull(fieldName);
    625
    626        if (objectsTriviallyEqual) {
    627            return this;
    628        }
    629        if (!Arrays.equals(lhs, rhs)) {
    630            diffs.add(new Diff<Integer[]>(fieldName) {
    631                private static final long serialVersionUID = 1L;
    632
    633                @Override
    634                public Integer[] getLeft() {
    635                    return ArrayUtils.toObject(lhs);
    636                }
    637
    638                @Override
    639                public Integer[] getRight() {
    640                    return ArrayUtils.toObject(rhs);
    641                }
    642            });
    643        }
    644        return this;
    645    }
    646
    647    /**
    648     * Test if two {@code long}s are equal.
    649     *
    650     * @param fieldName
    651     *            the field name
    652     * @param lhs
    653     *            the left-hand {@code long}
    654     * @param rhs
    655     *            the right-hand {@code long}
    656     * @return this
    657     * @throws NullPointerException
    658     *             if field name is {@code null}
    659     */
    660    public DiffBuilder<T> append(final String fieldName, final long lhs,
    661            final long rhs) {
    662        validateFieldNameNotNull(fieldName);
    663
    664        if (objectsTriviallyEqual) {
    665            return this;
    666        }
    667        if (lhs != rhs) {
    668            diffs.add(new Diff<Long>(fieldName) {
    669                private static final long serialVersionUID = 1L;
    670
    671                @Override
    672                public Long getLeft() {
    673                    return Long.valueOf(lhs);
    674                }
    675
    676                @Override
    677                public Long getRight() {
    678                    return Long.valueOf(rhs);
    679                }
    680            });
    681        }
    682        return this;
    683    }
    684
    685    /**
    686     * Test if two {@code long[]}s are equal.
    687     *
    688     * @param fieldName
    689     *            the field name
    690     * @param lhs
    691     *            the left-hand {@code long[]}
    692     * @param rhs
    693     *            the right-hand {@code long[]}
    694     * @return this
    695     * @throws NullPointerException
    696     *             if field name is {@code null}
    697     */
    698    public DiffBuilder<T> append(final String fieldName, final long[] lhs,
    699            final long[] rhs) {
    700        validateFieldNameNotNull(fieldName);
    701
    702        if (objectsTriviallyEqual) {
    703            return this;
    704        }
    705        if (!Arrays.equals(lhs, rhs)) {
    706            diffs.add(new Diff<Long[]>(fieldName) {
    707                private static final long serialVersionUID = 1L;
    708
    709                @Override
    710                public Long[] getLeft() {
    711                    return ArrayUtils.toObject(lhs);
    712                }
    713
    714                @Override
    715                public Long[] getRight() {
    716                    return ArrayUtils.toObject(rhs);
    717                }
    718            });
    719        }
    720        return this;
    721    }
    722
    723    /**
    724     * Test if two {@link Objects}s are equal.
    725     *
    726     * @param fieldName
    727     *            the field name
    728     * @param lhs
    729     *            the left-hand {@link Object}
    730     * @param rhs
    731     *            the right-hand {@link Object}
    732     * @return this
    733     * @throws NullPointerException
    734     *             if field name is {@code null}
    735     */
    736    public DiffBuilder<T> append(final String fieldName, final Object lhs,
    737            final Object rhs) {
    738        validateFieldNameNotNull(fieldName);
    739        if (objectsTriviallyEqual) {
    740            return this;
    741        }
    742        if (lhs == rhs) {
    743            return this;
    744        }
    745
    746        final Object objectToTest;
    747        if (lhs != null) {
    748            objectToTest = lhs;
    749        } else {
    750            // rhs cannot be null, as lhs != rhs
    751            objectToTest = rhs;
    752        }
    753
    754        if (ObjectUtils.isArray(objectToTest)) {
    755            if (objectToTest instanceof boolean[]) {
    756                return append(fieldName, (boolean[]) lhs, (boolean[]) rhs);
    757            }
    758            if (objectToTest instanceof byte[]) {
    759                return append(fieldName, (byte[]) lhs, (byte[]) rhs);
    760            }
    761            if (objectToTest instanceof char[]) {
    762                return append(fieldName, (char[]) lhs, (char[]) rhs);
    763            }
    764            if (objectToTest instanceof double[]) {
    765                return append(fieldName, (double[]) lhs, (double[]) rhs);
    766            }
    767            if (objectToTest instanceof float[]) {
    768                return append(fieldName, (float[]) lhs, (float[]) rhs);
    769            }
    770            if (objectToTest instanceof int[]) {
    771                return append(fieldName, (int[]) lhs, (int[]) rhs);
    772            }
    773            if (objectToTest instanceof long[]) {
    774                return append(fieldName, (long[]) lhs, (long[]) rhs);
    775            }
    776            if (objectToTest instanceof short[]) {
    777                return append(fieldName, (short[]) lhs, (short[]) rhs);
    778            }
    779
    780            return append(fieldName, (Object[]) lhs, (Object[]) rhs);
    781        }
    782
    783        // Not array type
    784        if (Objects.equals(lhs, rhs)) {
    785            return this;
    786        }
    787
    788        diffs.add(new Diff<Object>(fieldName) {
    789            private static final long serialVersionUID = 1L;
    790
    791            @Override
    792            public Object getLeft() {
    793                return lhs;
    794            }
    795
    796            @Override
    797            public Object getRight() {
    798                return rhs;
    799            }
    800        });
    801
    802        return this;
    803    }
    804
    805    /**
    806     * Test if two {@code Object[]}s are equal.
    807     *
    808     * @param fieldName
    809     *            the field name
    810     * @param lhs
    811     *            the left-hand {@code Object[]}
    812     * @param rhs
    813     *            the right-hand {@code Object[]}
    814     * @return this
    815     * @throws NullPointerException
    816     *             if field name is {@code null}
    817     */
    818    public DiffBuilder<T> append(final String fieldName, final Object[] lhs,
    819            final Object[] rhs) {
    820        validateFieldNameNotNull(fieldName);
    821        if (objectsTriviallyEqual) {
    822            return this;
    823        }
    824
    825        if (!Arrays.equals(lhs, rhs)) {
    826            diffs.add(new Diff<Object[]>(fieldName) {
    827                private static final long serialVersionUID = 1L;
    828
    829                @Override
    830                public Object[] getLeft() {
    831                    return lhs;
    832                }
    833
    834                @Override
    835                public Object[] getRight() {
    836                    return rhs;
    837                }
    838            });
    839        }
    840
    841        return this;
    842    }
    843
    844    /**
    845     * Test if two {@code short}s are equal.
    846     *
    847     * @param fieldName
    848     *            the field name
    849     * @param lhs
    850     *            the left-hand {@code short}
    851     * @param rhs
    852     *            the right-hand {@code short}
    853     * @return this
    854     * @throws NullPointerException
    855     *             if field name is {@code null}
    856     */
    857    public DiffBuilder<T> append(final String fieldName, final short lhs,
    858            final short rhs) {
    859        validateFieldNameNotNull(fieldName);
    860
    861        if (objectsTriviallyEqual) {
    862            return this;
    863        }
    864        if (lhs != rhs) {
    865            diffs.add(new Diff<Short>(fieldName) {
    866                private static final long serialVersionUID = 1L;
    867
    868                @Override
    869                public Short getLeft() {
    870                    return Short.valueOf(lhs);
    871                }
    872
    873                @Override
    874                public Short getRight() {
    875                    return Short.valueOf(rhs);
    876                }
    877            });
    878        }
    879        return this;
    880    }
    881
    882    /**
    883     * Test if two {@code short[]}s are equal.
    884     *
    885     * @param fieldName
    886     *            the field name
    887     * @param lhs
    888     *            the left-hand {@code short[]}
    889     * @param rhs
    890     *            the right-hand {@code short[]}
    891     * @return this
    892     * @throws NullPointerException
    893     *             if field name is {@code null}
    894     */
    895    public DiffBuilder<T> append(final String fieldName, final short[] lhs,
    896            final short[] rhs) {
    897        validateFieldNameNotNull(fieldName);
    898
    899        if (objectsTriviallyEqual) {
    900            return this;
    901        }
    902        if (!Arrays.equals(lhs, rhs)) {
    903            diffs.add(new Diff<Short[]>(fieldName) {
    904                private static final long serialVersionUID = 1L;
    905
    906                @Override
    907                public Short[] getLeft() {
    908                    return ArrayUtils.toObject(lhs);
    909                }
    910
    911                @Override
    912                public Short[] getRight() {
    913                    return ArrayUtils.toObject(rhs);
    914                }
    915            });
    916        }
    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}