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}