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