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