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}