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