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 * https://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.lang.reflect.Field; 020import java.lang.reflect.Modifier; 021import java.util.Collection; 022import java.util.Comparator; 023import java.util.HashSet; 024import java.util.Objects; 025import java.util.Set; 026 027import org.apache.commons.lang3.ArrayUtils; 028import org.apache.commons.lang3.ObjectUtils; 029import org.apache.commons.lang3.builder.AbstractReflection.AbstractBuilder; 030import org.apache.commons.lang3.tuple.Pair; 031 032/** 033 * Assists in implementing {@link Comparable#compareTo(Object)} methods. 034 * 035 * <p> 036 * It is consistent with {@code equals(Object)} and 037 * {@code hashCode()} built with {@link EqualsBuilder} and 038 * {@link HashCodeBuilder}. 039 * </p> 040 * 041 * <p> 042 * Two Objects that compare equal using {@code equals(Object)} should normally 043 * also compare equal using {@code compareTo(Object)}. 044 * </p> 045 * 046 * <p> 047 * All relevant fields should be included in the calculation of the 048 * comparison. Derived fields may be ignored. The same fields, in the same 049 * order, should be used in both {@code compareTo(Object)} and 050 * {@code equals(Object)}. 051 * </p> 052 * 053 * <p> 054 * To use this class write code as follows: 055 * </p> 056 * 057 * <pre> 058 * public class MyClass { 059 * String field1; 060 * int field2; 061 * boolean field3; 062 * 063 * ... 064 * 065 * public int compareTo(Object o) { 066 * MyClass myClass = (MyClass) o; 067 * return new CompareToBuilder() 068 * .appendSuper(super.compareTo(o) 069 * .append(this.field1, myClass.field1) 070 * .append(this.field2, myClass.field2) 071 * .append(this.field3, myClass.field3) 072 * .toComparison(); 073 * } 074 * } 075 * </pre> 076 * 077 * <p> 078 * Values are compared in the order they are appended to the builder. If any comparison returns 079 * a non-zero result, then that value will be the result returned by {@code toComparison()} and all 080 * subsequent comparisons are skipped. 081 * </p> 082 * 083 * <p> 084 * Alternatively, there are {@link #reflectionCompare(Object, Object) reflectionCompare} methods that use 085 * reflection to determine the fields to append. Because fields can be private, 086 * {@code reflectionCompare} uses {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} to 087 * bypass normal access control checks. This will fail under a security manager, 088 * unless the appropriate permissions are set up correctly. It is also 089 * slower than appending explicitly. 090 * </p> 091 * <p> 092 * See also {@link AbstractBuilder#setForceAccessible(boolean)} 093 * </p> 094 * <p> 095 * A typical implementation of {@code compareTo(Object)} using 096 * {@code reflectionCompare} looks like: 097 * </p> 098 099 * <pre> 100 * public int compareTo(Object o) { 101 * return CompareToBuilder.reflectionCompare(this, o); 102 * } 103 * </pre> 104 * 105 * <p> 106 * The reflective methods compare object fields in the order returned by 107 * {@link Class#getDeclaredFields()}. The fields of the class are compared first, followed by those 108 * of its parent classes (in order from the bottom to the top of the class hierarchy). 109 * </p> 110 * 111 * @see Comparable 112 * @see Object#equals(Object) 113 * @see Object#hashCode() 114 * @see EqualsBuilder 115 * @see HashCodeBuilder 116 * @see AbstractBuilder#setForceAccessible(boolean) 117 * @since 1.0 118 */ 119public class CompareToBuilder extends AbstractReflection implements Builder<Integer> { 120 121 /** 122 * Builds instances of CompareToBuilder. 123 */ 124 public static class Builder extends AbstractBuilder<Builder> { 125 126 /** 127 * Constructs a new Builder instance. 128 */ 129 private Builder() { 130 // empty 131 } 132 133 @Override 134 public CompareToBuilder get() { 135 return new CompareToBuilder(this); 136 } 137 138 } 139 140 /** 141 * A registry of objects to detect cyclical object references, avoid infinite loops, and stack overflows. 142 */ 143 private static final ThreadLocal<Set<Pair<IDKey, IDKey>>> REGISTRY = ThreadLocal.withInitial(HashSet::new); 144 145 /** 146 * Constructs a new Builder. 147 * 148 * @return A new Builder. 149 */ 150 public static Builder builder() { 151 return new Builder(); 152 } 153 154 /** 155 * Gets the registry of object pairs being traversed by the reflection 156 * methods in the current thread. 157 * 158 * @return Set the registry of objects being traversed. 159 */ 160 static Set<Pair<IDKey, IDKey>> getRegistry() { 161 return REGISTRY.get(); 162 } 163 164 /** 165 * Tests whether the registry contains the given object pair. 166 * <p> 167 * Used by the reflection methods to avoid infinite loops. 168 * Objects might be swapped therefore a check is needed if the object pair 169 * is registered in the given or swapped order. 170 * </p> 171 * 172 * @param lhs {@code this} object to lookup in registry. 173 * @param rhs The other object to lookup on registry. 174 * @return boolean {@code true} if the registry contains the given object. 175 */ 176 static boolean isRegistered(final Object lhs, final Object rhs) { 177 return isRegistered(lhs, rhs, getRegistry()); 178 } 179 180 /** 181 * Appends to {@code builder} the comparison of {@code lhs} 182 * to {@code rhs} using the fields defined in {@code clazz}. 183 * 184 * @param lhs left-hand side object. 185 * @param rhs right-hand side object. 186 * @param clazz {@link Class} that defines fields to be compared. 187 * @param builder {@link CompareToBuilder} to append to. 188 * @param useTransients whether to compare transient fields. 189 * @param excludeFields fields to exclude. 190 * @param forceAccessible Whether to set fields' accessible flags. 191 */ 192 private static void reflectionAppend( 193 final Object lhs, 194 final Object rhs, 195 final Class<?> clazz, 196 final CompareToBuilder builder, 197 final boolean useTransients, 198 final String[] excludeFields, 199 final boolean forceAccessible) { 200 201 final Field[] fields = clazz.getDeclaredFields(); 202 for (int i = 0; i < fields.length && builder.comparison == 0; i++) { 203 final Field field = fields[i]; 204 final String name = field.getName(); 205 if (!ArrayUtils.contains(excludeFields, name) 206 && !name.contains("$") 207 && (useTransients || !Modifier.isTransient(field.getModifiers())) 208 && !Modifier.isStatic(field.getModifiers())) { 209 if (setAccessible(forceAccessible, field)) { 210 // IllegalAccessException can't happen. Would get a Security exception instead. 211 // Throw a runtime exception in case the impossible happens. 212 builder.append(Reflection.getUnchecked(field, lhs), Reflection.getUnchecked(field, rhs)); 213 } 214 } 215 } 216 } 217 218 /** 219 * Compares two {@link Object}s via reflection. 220 * <p> 221 * Fields can be private, thus {@code AccessibleObject.setAccessible} is used to bypass normal access control checks. This will fail under a security 222 * manager unless the appropriate permissions are set. 223 * </p> 224 * <ul> 225 * <li>Static fields will not be compared</li> 226 * <li>Transient members will be not be compared, as they are likely derived fields</li> 227 * <li>Superclass fields will be compared</li> 228 * </ul> 229 * <p> 230 * If both {@code lhs} and {@code rhs} are {@code null}, they are considered equal. 231 * </p> 232 * 233 * @param lhs left-hand side object. 234 * @param rhs right-hand side object. 235 * @return A negative integer, zero, or a positive integer as {@code lhs} is less than, equal to, or greater than {@code rhs}. 236 * @throws NullPointerException Thrown if either (but not both) parameters are {@code null}. 237 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}. 238 */ 239 public static int reflectionCompare(final Object lhs, final Object rhs) { 240 return reflectionCompare(lhs, rhs, false, null); 241 } 242 243 /** 244 * Compares two {@link Object}s via reflection. 245 * <p> 246 * Fields can be private, thus {@code AccessibleObject.setAccessible} is used to bypass normal access control checks. This will fail under a security 247 * manager unless the appropriate permissions are set. 248 * </p> 249 * <ul> 250 * <li>Static fields will not be compared</li> 251 * <li>If {@code compareTransients} is {@code true}, compares transient members. Otherwise ignores them, as they are likely derived fields.</li> 252 * <li>Superclass fields will be compared</li> 253 * </ul> 254 * <p> 255 * If both {@code lhs} and {@code rhs} are {@code null}, they are considered equal. 256 * </p> 257 * 258 * @param lhs left-hand side object. 259 * @param rhs right-hand side object. 260 * @param compareTransients whether to compare transient fields. 261 * @return A negative integer, zero, or a positive integer as {@code lhs} is less than, equal to, or greater than {@code rhs}. 262 * @throws NullPointerException Thrown if either {@code lhs} or {@code rhs} (but not both) is {@code null}. 263 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}. 264 */ 265 public static int reflectionCompare(final Object lhs, final Object rhs, final boolean compareTransients) { 266 return reflectionCompare(lhs, rhs, compareTransients, null); 267 } 268 269 /** 270 * Compares two {@link Object}s via reflection. 271 * <p> 272 * Fields can be private, thus {@code AccessibleObject.setAccessible} is used to bypass normal access control checks. This will fail under a security 273 * manager unless the appropriate permissions are set. 274 * </p> 275 * <ul> 276 * <li>Static fields will not be compared</li> 277 * <li>If the {@code compareTransients} is {@code true}, compares transient members. Otherwise ignores them, as they are likely derived fields.</li> 278 * <li>Compares superclass fields up to and including {@code reflectUpToClass}. If {@code reflectUpToClass} is {@code null}, compares all superclass 279 * fields.</li> 280 * </ul> 281 * <p> 282 * If both {@code lhs} and {@code rhs} are {@code null}, they are considered equal. 283 * </p> 284 * 285 * @param lhs left-hand side object. 286 * @param rhs right-hand side object. 287 * @param compareTransients whether to compare transient fields. 288 * @param reflectUpToClass last superclass for which fields are compared. 289 * @param excludeFields fields to exclude. 290 * @return A negative integer, zero, or a positive integer as {@code lhs} is less than, equal to, or greater than {@code rhs}. 291 * @throws NullPointerException Thrown if either {@code lhs} or {@code rhs} (but not both) is {@code null}. 292 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}. 293 * @since 2.2 (2.0 as {@code reflectionCompare(Object, Object, boolean, Class)}). 294 */ 295 public static int reflectionCompare( 296 final Object lhs, 297 final Object rhs, 298 final boolean compareTransients, 299 final Class<?> reflectUpToClass, 300 final String... excludeFields) { 301 if (lhs == rhs) { 302 return 0; 303 } 304 Objects.requireNonNull(lhs, "lhs"); 305 Objects.requireNonNull(rhs, "rhs"); 306 Class<?> lhsClazz = lhs.getClass(); 307 if (!lhsClazz.isInstance(rhs)) { 308 throw new ClassCastException(); 309 } 310 final CompareToBuilder compareToBuilder = new CompareToBuilder(); 311 reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields, AbstractReflection.getForceAccessible()); 312 while (lhsClazz.getSuperclass() != null && lhsClazz != reflectUpToClass) { 313 lhsClazz = lhsClazz.getSuperclass(); 314 reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields, AbstractReflection.getForceAccessible()); 315 } 316 return compareToBuilder.toComparison(); 317 } 318 319 /** 320 * Compares two {@link Object}s via reflection. 321 * <p> 322 * Fields can be private, thus {@code AccessibleObject.setAccessible} is used to bypass normal access control checks. This will fail under a security 323 * manager unless the appropriate permissions are set. 324 * </p> 325 * <ul> 326 * <li>Static fields will not be compared</li> 327 * <li>If {@code compareTransients} is {@code true}, compares transient members. Otherwise ignores them, as they are likely derived fields.</li> 328 * <li>Superclass fields will be compared</li> 329 * </ul> 330 * <p> 331 * If both {@code lhs} and {@code rhs} are {@code null}, they are considered equal. 332 * </p> 333 * 334 * @param lhs left-hand side object. 335 * @param rhs right-hand side object. 336 * @param excludeFields Collection of String fields to exclude. 337 * @return A negative integer, zero, or a positive integer as {@code lhs} is less than, equal to, or greater than {@code rhs}. 338 * @throws NullPointerException Thrown if either {@code lhs} or {@code rhs} (but not both) is {@code null}. 339 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}. 340 * @since 2.2 341 */ 342 public static int reflectionCompare(final Object lhs, final Object rhs, final Collection<String> excludeFields) { 343 return reflectionCompare(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields)); 344 } 345 346 /** 347 * Compares two {@link Object}s via reflection. 348 * <p> 349 * Fields can be private, thus {@code AccessibleObject.setAccessible} is used to bypass normal access control checks. This will fail under a security 350 * manager unless the appropriate permissions are set. 351 * </p> 352 * <ul> 353 * <li>Static fields will not be compared</li> 354 * <li>If {@code compareTransients} is {@code true}, compares transient members. Otherwise ignores them, as they are likely derived fields.</li> 355 * <li>Superclass fields will be compared</li> 356 * </ul> 357 * <p> 358 * If both {@code lhs} and {@code rhs} are {@code null}, they are considered equal. 359 * </p> 360 * 361 * @param lhs left-hand side object. 362 * @param rhs right-hand side object. 363 * @param excludeFields array of fields to exclude. 364 * @return A negative integer, zero, or a positive integer as {@code lhs} is less than, equal to, or greater than {@code rhs}. 365 * @throws NullPointerException Thrown if either {@code lhs} or {@code rhs} (but not both) is {@code null}. 366 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}. 367 * @since 2.2 368 */ 369 public static int reflectionCompare(final Object lhs, final Object rhs, final String... excludeFields) { 370 return reflectionCompare(lhs, rhs, false, null, excludeFields); 371 } 372 373 /** 374 * Registers the given object pair. Used by the reflection methods to avoid infinite loops. 375 * 376 * @param lhs {@code this} object to register. 377 * @param rhs The other object to register. 378 */ 379 private static void register(final Object lhs, final Object rhs) { 380 register(lhs, rhs, getRegistry()); 381 } 382 383 /** 384 * Unregisters the given object pair. 385 * <p> 386 * Used by the reflection methods to avoid infinite loops. 387 * </p> 388 * 389 * @param lhs {@code this} object to unregister. 390 * @param rhs The other object to unregister. 391 */ 392 private static void unregister(final Object lhs, final Object rhs) { 393 unregister(lhs, rhs, getRegistry(), REGISTRY); 394 } 395 396 /** 397 * Current state of the comparison as appended fields are checked. 398 */ 399 private int comparison; 400 401 /** 402 * Constructor for CompareToBuilder. 403 * <p> 404 * Starts off assuming that the objects are equal. Multiple calls are then made to the various append methods, followed by a call to {@link #toComparison} 405 * to get the result. 406 * </p> 407 */ 408 public CompareToBuilder() { 409 super(builder()); 410 comparison = 0; 411 } 412 413 private CompareToBuilder(final Builder builder) { 414 super(builder); 415 } 416 417 /** 418 * Appends to the {@code builder} the comparison of two {@code booleans}s. 419 * 420 * @param lhs left-hand side value. 421 * @param rhs right-hand side value. 422 * @return {@code this} instance. 423 */ 424 public CompareToBuilder append(final boolean lhs, final boolean rhs) { 425 if (comparison != 0 || lhs == rhs) { 426 return this; 427 } 428 comparison = lhs ? 1 : -1; 429 return this; 430 } 431 432 /** 433 * Appends to the {@code builder} the deep comparison of two {@code boolean} arrays. 434 * <ol> 435 * <li>Check if arrays are the same using {@code ==}</li> 436 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li> 437 * <li>Check array length, a shorter length array is less than a longer length array</li> 438 * <li>Check array contents element by element using {@link #append(boolean, boolean)}</li> 439 * </ol> 440 * 441 * @param lhs left-hand side array. 442 * @param rhs right-hand side array. 443 * @return {@code this} instance. 444 */ 445 public CompareToBuilder append(final boolean[] lhs, final boolean[] rhs) { 446 if (comparison != 0 || lhs == rhs) { 447 return this; 448 } 449 if (lhs == null) { 450 comparison = -1; 451 return this; 452 } 453 if (rhs == null) { 454 comparison = 1; 455 return this; 456 } 457 if (lhs.length != rhs.length) { 458 comparison = lhs.length < rhs.length ? -1 : 1; 459 return this; 460 } 461 for (int i = 0; i < lhs.length && comparison == 0; i++) { 462 append(lhs[i], rhs[i]); 463 } 464 return this; 465 } 466 467 /** 468 * Appends to the {@code builder} the comparison of two {@code byte}s. 469 * 470 * @param lhs left-hand side value. 471 * @param rhs right-hand side value. 472 * @return {@code this} instance. 473 */ 474 public CompareToBuilder append(final byte lhs, final byte rhs) { 475 if (comparison != 0) { 476 return this; 477 } 478 comparison = Byte.compare(lhs, rhs); 479 return this; 480 } 481 482 /** 483 * Appends to the {@code builder} the deep comparison of two {@code byte} arrays. 484 * <ol> 485 * <li>Check if arrays are the same using {@code ==}</li> 486 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li> 487 * <li>Check array length, a shorter length array is less than a longer length array</li> 488 * <li>Check array contents element by element using {@link #append(byte, byte)}</li> 489 * </ol> 490 * 491 * @param lhs left-hand side array. 492 * @param rhs right-hand side array. 493 * @return {@code this} instance. 494 */ 495 public CompareToBuilder append(final byte[] lhs, final byte[] rhs) { 496 if (comparison != 0 || lhs == rhs) { 497 return this; 498 } 499 if (lhs == null) { 500 comparison = -1; 501 return this; 502 } 503 if (rhs == null) { 504 comparison = 1; 505 return this; 506 } 507 if (lhs.length != rhs.length) { 508 comparison = lhs.length < rhs.length ? -1 : 1; 509 return this; 510 } 511 for (int i = 0; i < lhs.length && comparison == 0; i++) { 512 append(lhs[i], rhs[i]); 513 } 514 return this; 515 } 516 517 /** 518 * Appends to the {@code builder} the comparison of two {@code char}s. 519 * 520 * @param lhs left-hand side value. 521 * @param rhs right-hand side value. 522 * @return {@code this} instance. 523 */ 524 public CompareToBuilder append(final char lhs, final char rhs) { 525 if (comparison != 0) { 526 return this; 527 } 528 comparison = Character.compare(lhs, rhs); 529 return this; 530 } 531 532 /** 533 * Appends to the {@code builder} the deep comparison of two {@code char} arrays. 534 * <ol> 535 * <li>Check if arrays are the same using {@code ==}</li> 536 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li> 537 * <li>Check array length, a shorter length array is less than a longer length array</li> 538 * <li>Check array contents element by element using {@link #append(char, char)}</li> 539 * </ol> 540 * 541 * @param lhs left-hand side array. 542 * @param rhs right-hand side array. 543 * @return {@code this} instance. 544 */ 545 public CompareToBuilder append(final char[] lhs, final char[] rhs) { 546 if (comparison != 0 || lhs == rhs) { 547 return this; 548 } 549 if (lhs == null) { 550 comparison = -1; 551 return this; 552 } 553 if (rhs == null) { 554 comparison = 1; 555 return this; 556 } 557 if (lhs.length != rhs.length) { 558 comparison = lhs.length < rhs.length ? -1 : 1; 559 return this; 560 } 561 for (int i = 0; i < lhs.length && comparison == 0; i++) { 562 append(lhs[i], rhs[i]); 563 } 564 return this; 565 } 566 567 /** 568 * Appends to the {@code builder} the comparison of two {@code double}s. 569 * <p> 570 * This handles NaNs, Infinities, and {@code -0.0}. 571 * </p> 572 * <p> 573 * It is compatible with the hash code generated by {@link HashCodeBuilder}. 574 * </p> 575 * 576 * @param lhs left-hand side value. 577 * @param rhs right-hand side value. 578 * @return {@code this} instance. 579 */ 580 public CompareToBuilder append(final double lhs, final double rhs) { 581 if (comparison != 0) { 582 return this; 583 } 584 comparison = Double.compare(lhs, rhs); 585 return this; 586 } 587 588 /** 589 * Appends to the {@code builder} the deep comparison of two {@code double} arrays. 590 * <ol> 591 * <li>Check if arrays are the same using {@code ==}</li> 592 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li> 593 * <li>Check array length, a shorter length array is less than a longer length array</li> 594 * <li>Check array contents element by element using {@link #append(double, double)}</li> 595 * </ol> 596 * 597 * @param lhs left-hand side array. 598 * @param rhs right-hand side array. 599 * @return {@code this} instance. 600 */ 601 public CompareToBuilder append(final double[] lhs, final double[] rhs) { 602 if (comparison != 0 || lhs == rhs) { 603 return this; 604 } 605 if (lhs == null) { 606 comparison = -1; 607 return this; 608 } 609 if (rhs == null) { 610 comparison = 1; 611 return this; 612 } 613 if (lhs.length != rhs.length) { 614 comparison = lhs.length < rhs.length ? -1 : 1; 615 return this; 616 } 617 for (int i = 0; i < lhs.length && comparison == 0; i++) { 618 append(lhs[i], rhs[i]); 619 } 620 return this; 621 } 622 623 /** 624 * Appends to the {@code builder} the comparison of two {@code float}s. 625 * <p> 626 * This handles NaNs, Infinities, and {@code -0.0}. 627 * </p> 628 * <p> 629 * It is compatible with the hash code generated by {@link HashCodeBuilder}. 630 * </p> 631 * 632 * @param lhs left-hand side value. 633 * @param rhs right-hand side value. 634 * @return {@code this} instance. 635 */ 636 public CompareToBuilder append(final float lhs, final float rhs) { 637 if (comparison != 0) { 638 return this; 639 } 640 comparison = Float.compare(lhs, rhs); 641 return this; 642 } 643 644 /** 645 * Appends to the {@code builder} the deep comparison of two {@code float} arrays. 646 * <ol> 647 * <li>Check if arrays are the same using {@code ==}</li> 648 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li> 649 * <li>Check array length, a shorter length array is less than a longer length array</li> 650 * <li>Check array contents element by element using {@link #append(float, float)}</li> 651 * </ol> 652 * 653 * @param lhs left-hand side array. 654 * @param rhs right-hand side array. 655 * @return {@code this} instance. 656 */ 657 public CompareToBuilder append(final float[] lhs, final float[] rhs) { 658 if (comparison != 0 || lhs == rhs) { 659 return this; 660 } 661 if (lhs == null) { 662 comparison = -1; 663 return this; 664 } 665 if (rhs == null) { 666 comparison = 1; 667 return this; 668 } 669 if (lhs.length != rhs.length) { 670 comparison = lhs.length < rhs.length ? -1 : 1; 671 return this; 672 } 673 for (int i = 0; i < lhs.length && comparison == 0; i++) { 674 append(lhs[i], rhs[i]); 675 } 676 return this; 677 } 678 679 /** 680 * Appends to the {@code builder} the comparison of two {@code int}s. 681 * 682 * @param lhs left-hand side value. 683 * @param rhs right-hand side value. 684 * @return {@code this} instance. 685 */ 686 public CompareToBuilder append(final int lhs, final int rhs) { 687 if (comparison != 0) { 688 return this; 689 } 690 comparison = Integer.compare(lhs, rhs); 691 return this; 692 } 693 694 /** 695 * Appends to the {@code builder} the deep comparison of two {@code int} arrays. 696 * <ol> 697 * <li>Check if arrays are the same using {@code ==}</li> 698 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li> 699 * <li>Check array length, a shorter length array is less than a longer length array</li> 700 * <li>Check array contents element by element using {@link #append(int, int)}</li> 701 * </ol> 702 * 703 * @param lhs left-hand side array. 704 * @param rhs right-hand side array. 705 * @return {@code this} instance. 706 */ 707 public CompareToBuilder append(final int[] lhs, final int[] rhs) { 708 if (comparison != 0 || lhs == rhs) { 709 return this; 710 } 711 if (lhs == null) { 712 comparison = -1; 713 return this; 714 } 715 if (rhs == null) { 716 comparison = 1; 717 return this; 718 } 719 if (lhs.length != rhs.length) { 720 comparison = lhs.length < rhs.length ? -1 : 1; 721 return this; 722 } 723 for (int i = 0; i < lhs.length && comparison == 0; i++) { 724 append(lhs[i], rhs[i]); 725 } 726 return this; 727 } 728 729 /** 730 * Appends to the {@code builder} the comparison of two {@code long}s. 731 * 732 * @param lhs left-hand side value. 733 * @param rhs right-hand side value. 734 * @return {@code this} instance. 735 */ 736 public CompareToBuilder append(final long lhs, final long rhs) { 737 if (comparison != 0) { 738 return this; 739 } 740 comparison = Long.compare(lhs, rhs); 741 return this; 742 } 743 744 /** 745 * Appends to the {@code builder} the deep comparison of two {@code long} arrays. 746 * <ol> 747 * <li>Check if arrays are the same using {@code ==}</li> 748 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li> 749 * <li>Check array length, a shorter length array is less than a longer length array</li> 750 * <li>Check array contents element by element using {@link #append(long, long)}</li> 751 * </ol> 752 * 753 * @param lhs left-hand side array. 754 * @param rhs right-hand side array. 755 * @return {@code this} instance. 756 */ 757 public CompareToBuilder append(final long[] lhs, final long[] rhs) { 758 if (comparison != 0 || lhs == rhs) { 759 return this; 760 } 761 if (lhs == null) { 762 comparison = -1; 763 return this; 764 } 765 if (rhs == null) { 766 comparison = 1; 767 return this; 768 } 769 if (lhs.length != rhs.length) { 770 comparison = lhs.length < rhs.length ? -1 : 1; 771 return this; 772 } 773 for (int i = 0; i < lhs.length && comparison == 0; i++) { 774 append(lhs[i], rhs[i]); 775 } 776 return this; 777 } 778 779 /** 780 * Appends to the {@code builder} the comparison of two {@link Object}s. 781 * <ol> 782 * <li>Check if {@code lhs == rhs}</li> 783 * <li>Check if either {@code lhs} or {@code rhs} is {@code null}, a {@code null} object is less than a non-{@code null} object</li> 784 * <li>Check the object contents</li> 785 * </ol> 786 * <p> 787 * {@code lhs} must either be an array or implement {@link Comparable}. 788 * </p> 789 * 790 * @param lhs left-hand side object. 791 * @param rhs right-hand side object. 792 * @return {@code this} instance. 793 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}. 794 */ 795 public CompareToBuilder append(final Object lhs, final Object rhs) { 796 return append(lhs, rhs, null); 797 } 798 799 /** 800 * Appends to the {@code builder} the comparison of two {@link Object}s. 801 * <ol> 802 * <li>Check if {@code lhs == rhs}</li> 803 * <li>Check if either {@code lhs} or {@code rhs} is {@code null}, a {@code null} object is less than a non-{@code null} object</li> 804 * <li>Check the object contents</li> 805 * </ol> 806 * <p> 807 * If {@code lhs} is an array, array comparison methods will be used. Otherwise {@code comparator} will be used to compare the objects. If 808 * {@code comparator} is {@code null}, {@code lhs} must implement {@link Comparable} instead. 809 * </p> 810 * 811 * @param lhs left-hand side object. 812 * @param rhs right-hand side object. 813 * @param comparator {@link Comparator} used to compare the objects, {@code null} means treat lhs as {@link Comparable} 814 * @return {@code this} instance. 815 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}. 816 * @since 2.0 817 */ 818 public CompareToBuilder append(final Object lhs, final Object rhs, final Comparator<?> comparator) { 819 if (comparison != 0 || lhs == rhs) { 820 return this; 821 } 822 if (lhs == null) { 823 comparison = -1; 824 return this; 825 } 826 if (rhs == null) { 827 comparison = 1; 828 return this; 829 } 830 if (isRegistered(lhs, rhs)) { 831 return this; 832 } 833 try { 834 register(lhs, rhs); 835 if (ObjectUtils.isArray(lhs)) { 836 // factor out array case in order to keep method small enough to be inlined 837 appendArray(lhs, rhs, comparator); 838 } else // the simple case, not an array, just test the element 839 if (comparator == null) { 840 @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc 841 final Comparable<Object> comparable = (Comparable<Object>) lhs; 842 comparison = comparable.compareTo(rhs); 843 } else { 844 @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc 845 final Comparator<Object> comparator2 = (Comparator<Object>) comparator; 846 comparison = comparator2.compare(lhs, rhs); 847 } 848 return this; 849 } finally { 850 unregister(lhs, rhs); 851 } 852 } 853 854 /** 855 * Appends to the {@code builder} the deep comparison of two {@link Object} arrays. 856 * <ol> 857 * <li>Check if arrays are the same using {@code ==}</li> 858 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li> 859 * <li>Check array length, a short length array is less than a long length array</li> 860 * <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li> 861 * </ol> 862 * <p> 863 * This method will also will be called for the top level of multi-dimensional, ragged, and multi-typed arrays. 864 * </p> 865 * 866 * @param lhs left-hand side array. 867 * @param rhs right-hand side array. 868 * @return {@code this} instance. 869 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}. 870 */ 871 public CompareToBuilder append(final Object[] lhs, final Object[] rhs) { 872 return append(lhs, rhs, null); 873 } 874 875 /** 876 * Appends to the {@code builder} the deep comparison of two {@link Object} arrays. 877 * <ol> 878 * <li>Check if arrays are the same using {@code ==}</li> 879 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li> 880 * <li>Check array length, a short length array is less than a long length array</li> 881 * <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li> 882 * </ol> 883 * <p> 884 * This method will also will be called for the top level of multi-dimensional, ragged, and multi-typed arrays. 885 * </p> 886 * 887 * @param lhs left-hand side array. 888 * @param rhs right-hand side array. 889 * @param comparator {@link Comparator} to use to compare the array elements, {@code null} means to treat {@code lhs} elements as {@link Comparable}. 890 * @return {@code this} instance. 891 * @throws ClassCastException Thrown if {@code rhs} is not assignment-compatible with {@code lhs}. 892 * @since 2.0 893 */ 894 public CompareToBuilder append(final Object[] lhs, final Object[] rhs, final Comparator<?> comparator) { 895 if (comparison != 0 || lhs == rhs) { 896 return this; 897 } 898 if (lhs == null) { 899 comparison = -1; 900 return this; 901 } 902 if (rhs == null) { 903 comparison = 1; 904 return this; 905 } 906 if (lhs.length != rhs.length) { 907 comparison = lhs.length < rhs.length ? -1 : 1; 908 return this; 909 } 910 for (int i = 0; i < lhs.length && comparison == 0; i++) { 911 append(lhs[i], rhs[i], comparator); 912 } 913 return this; 914 } 915 916 /** 917 * Appends to the {@code builder} the comparison of two {@code short}s. 918 * 919 * @param lhs left-hand side value. 920 * @param rhs right-hand side value. 921 * @return {@code this} instance. 922 */ 923 public CompareToBuilder append(final short lhs, final short rhs) { 924 if (comparison != 0) { 925 return this; 926 } 927 comparison = Short.compare(lhs, rhs); 928 return this; 929 } 930 931 /** 932 * Appends to the {@code builder} the deep comparison of two {@code short} arrays. 933 * <ol> 934 * <li>Check if arrays are the same using {@code ==}</li> 935 * <li>Check if for {@code null}, {@code null} is less than non-{@code null}</li> 936 * <li>Check array length, a shorter length array is less than a longer length array</li> 937 * <li>Check array contents element by element using {@link #append(short, short)}</li> 938 * </ol> 939 * 940 * @param lhs left-hand side array. 941 * @param rhs right-hand side array. 942 * @return {@code this} instance. 943 */ 944 public CompareToBuilder append(final short[] lhs, final short[] rhs) { 945 if (comparison != 0 || lhs == rhs) { 946 return this; 947 } 948 if (lhs == null) { 949 comparison = -1; 950 return this; 951 } 952 if (rhs == null) { 953 comparison = 1; 954 return this; 955 } 956 if (lhs.length != rhs.length) { 957 comparison = lhs.length < rhs.length ? -1 : 1; 958 return this; 959 } 960 for (int i = 0; i < lhs.length && comparison == 0; i++) { 961 append(lhs[i], rhs[i]); 962 } 963 return this; 964 } 965 966 private void appendArray(final Object lhs, final Object rhs, final Comparator<?> comparator) { 967 // switch on type of array, to dispatch to the correct handler 968 // handles multidimensional arrays 969 // throws a ClassCastException if rhs is not the correct array type 970 if (lhs instanceof long[]) { 971 append((long[]) lhs, (long[]) rhs); 972 } else if (lhs instanceof int[]) { 973 append((int[]) lhs, (int[]) rhs); 974 } else if (lhs instanceof short[]) { 975 append((short[]) lhs, (short[]) rhs); 976 } else if (lhs instanceof char[]) { 977 append((char[]) lhs, (char[]) rhs); 978 } else if (lhs instanceof byte[]) { 979 append((byte[]) lhs, (byte[]) rhs); 980 } else if (lhs instanceof double[]) { 981 append((double[]) lhs, (double[]) rhs); 982 } else if (lhs instanceof float[]) { 983 append((float[]) lhs, (float[]) rhs); 984 } else if (lhs instanceof boolean[]) { 985 append((boolean[]) lhs, (boolean[]) rhs); 986 } else { 987 // not an array of primitives 988 // throws a ClassCastException if rhs is not an array 989 append((Object[]) lhs, (Object[]) rhs, comparator); 990 } 991 } 992 993 /** 994 * Appends to the {@code builder} the {@code compareTo(Object)} result of the superclass. 995 * 996 * @param superCompareTo result of calling {@code super.compareTo(Object)}. 997 * @return {@code this} instance. 998 * @since 2.0 999 */ 1000 public CompareToBuilder appendSuper(final int superCompareTo) { 1001 if (comparison != 0) { 1002 return this; 1003 } 1004 comparison = superCompareTo; 1005 return this; 1006 } 1007 1008 /** 1009 * Returns a negative Integer, a positive Integer, or zero as the {@code builder} has judged the "left-hand" side as less than, greater than, or equal to 1010 * the "right-hand" side. 1011 * 1012 * @return final comparison result as an Integer. 1013 * @see #toComparison() 1014 * @since 3.0 1015 */ 1016 @Override 1017 public Integer build() { 1018 return Integer.valueOf(toComparison()); 1019 } 1020 1021 /** 1022 * Returns a negative integer, a positive integer, or zero as the {@code builder} has judged the "left-hand" side as less than, greater than, or equal to 1023 * the "right-hand" side. 1024 * 1025 * @return final comparison result. 1026 * @see #build() 1027 */ 1028 public int toComparison() { 1029 return comparison; 1030 } 1031} 1032