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