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<Person> { 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<Person> { 927 * String name; 928 * Address address; // implements Diffable<Address> 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 NullPointerException if field name is {@code null} 947 * @since 3.5 948 */ 949 public DiffBuilder<T> append(final String fieldName, 950 final DiffResult<T> diffResult) { 951 validateFieldNameNotNull(fieldName); 952 Validate.notNull(diffResult, "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<T> build() { 976 return new DiffResult<>(left, right, diffs, style); 977 } 978 979 private void validateFieldNameNotNull(final String fieldName) { 980 Validate.notNull(fieldName, "Field name cannot be null"); 981 } 982 983}