001/* 002 * ============================================================================ 003 * Copyright © 2002-2024 by Thomas Thrien. 004 * All Rights Reserved. 005 * ============================================================================ 006 * 007 * Licensed to the public under the agreements of the GNU Lesser General Public 008 * License, version 3.0 (the "License"). You may obtain a copy of the License at 009 * 010 * http://www.gnu.org/licenses/lgpl.html 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 014 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 015 * License for the specific language governing permissions and limitations 016 * under the License. 017 */ 018 019package org.tquadrat.foundation.testutil; 020 021import static java.lang.String.format; 022import static java.lang.Thread.getAllStackTraces; 023import static java.lang.reflect.AccessibleObject.setAccessible; 024import static java.lang.reflect.Modifier.isStatic; 025import static java.lang.reflect.Modifier.isTransient; 026import static java.util.Arrays.asList; 027import static java.util.Arrays.deepToString; 028import static java.util.Collections.emptySet; 029import static java.util.Objects.deepEquals; 030import static java.util.Objects.isNull; 031import static java.util.Objects.nonNull; 032import static org.apiguardian.api.API.Status.STABLE; 033 034import java.lang.reflect.Array; 035import java.util.Arrays; 036import java.util.Collection; 037import java.util.Enumeration; 038import java.util.HashSet; 039import java.util.Map; 040import java.util.Set; 041import java.util.stream.Stream; 042 043import org.apiguardian.api.API; 044 045/** 046 * Some methods that are useful in the context of testing. 047 * 048 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 049 * @version $Id: TestUtils.java 1105 2024-02-28 12:58:46Z tquadrat $ 050 * @since 0.1.0 051 * 052 * @UMLGraph.link 053 */ 054@SuppressWarnings( {"UtilityClass", "UtilityClassCanBeEnum", "MethodCanBeVariableArityMethod", "UseOfObsoleteDateTimeApi"} ) 055@API( status = STABLE, since = "0.0.5" ) 056public final class TestUtils 057{ 058 /*-----------*\ 059 ====** Constants **======================================================== 060 \*-----------*/ 061 /** 062 * The system property for the code of country for the current user: 063 * {@value}. 064 */ 065 @API( status = STABLE, since = "0.0.5" ) 066 public static final String PROPERTY_USER_COUNTRY = "user.country"; 067 068 /*------------*\ 069 ====** Attributes **======================================================= 070 \*------------*/ 071 /** 072 * The flag that tracks the assertion on/off status for this package. 073 */ 074 private static boolean m_AssertionOn; 075 076 /*------------------------*\ 077 ====** Static Initialisations **=========================================== 078 \*------------------------*/ 079 /** 080 * The empty string. 081 */ 082 @API( status = STABLE, since = "0.0.5" ) 083 public static final String EMPTY_STRING = ""; 084 085 /** 086 * An empty array of 087 * {@link String} 088 * instances. 089 */ 090 @API( status = STABLE, since = "0.0.5" ) 091 public static final String [] EMPTY_String_ARRAY = new String [0]; 092 093 /** 094 * A String containing the sequence "null". 095 */ 096 /* 097 * The cast is required because if omitted, String.valueOf( char [] ) would 098 * be called. This in turn would cause an unwanted NullPointerException. 099 */ 100 @API( status = STABLE, since = "0.0.5" ) 101 public static final String NULL_STRING = String.valueOf( (Object) null ); 102 103 static 104 { 105 //---* Determine the assertion status *-------------------------------- 106 m_AssertionOn = false; 107 /* 108 * As the JUnit tests will always be executed with the flag 109 * "-ea" (assertions enabled), this code sequence is not tested in all 110 * branches. 111 */ 112 //noinspection AssertWithSideEffects,PointlessBooleanExpression,NestedAssignment 113 assert (m_AssertionOn = true) == true : "Assertion is switched off"; 114 } 115 116 /*--------------*\ 117 ====** Constructors **===================================================== 118 \*--------------*/ 119 /** 120 * No instance allowed for this class. 121 */ 122 private TestUtils() { /* Just exists */ } 123 124 /*---------*\ 125 ====** Methods **========================================================== 126 \*---------*/ 127 /** 128 * Returns all threads that are currently alive. 129 * 130 * @return The living threads. 131 */ 132 @API( status = STABLE, since = "0.0.5" ) 133 public static final Collection<Thread> getLiveThreads() 134 { 135 final var retValue = getAllStackTraces().keySet(); 136 137 //---* Done *---------------------------------------------------------- 138 return retValue; 139 } // getLiveThreads() 140 141 /** 142 * Checks whether JDK assertion is currently activated, meaning that the 143 * program was started with the command line flags {@code -ea} or 144 * {@code -enableassertions}. If assertions are activated for some 145 * selected packages only and {@code org.tquadrat.test} is not amongst 146 * these, or {@code org.tquadrat.test} is explicitly disabled with 147 * {@code -da} or {@code -disableassertions}, this method will return 148 * {@code false}. But even it may return {@code true}, it is possible that 149 * assertions are still not activated for some packages. 150 * 151 * @return {@code true} if assertions are activated for the 152 * package {@code org.tquadrat.foundation.test} and hopefully also for 153 * any other package, {@code false} otherwise. 154 */ 155 @API( status = STABLE, since = "0.0.5" ) 156 public static final boolean isAssertionOn() { return m_AssertionOn; } 157 158 /** 159 * Tests if the given String is {@code null} or the empty String. 160 * 161 * @param input The String to test. 162 * @return {@code true} if the given String reference is 163 * {@code null} or the empty String. 164 * 165 * @since 0.1.0 166 */ 167 @API( status = STABLE, since = "0.1.0" ) 168 public static final boolean isEmpty( final CharSequence input ) { return isNull( input ) || input.isEmpty(); } 169 170 /** 171 * Tests if the given String is {@code null}, the empty String, or just 172 * containing whitespace. 173 * 174 * @param input The String to test. 175 * @return {@code true} if the given String reference is not 176 * {@code null} and not the empty String. 177 * 178 * @see String#isBlank() 179 * 180 * @since 0.1.0 181 */ 182 @API( status = STABLE, since = "0.1.0" ) 183 public static final boolean isEmptyOrBlank( final CharSequence input ) 184 { 185 final var retValue = isNull( input ) || input.toString().isBlank(); 186 187 //---* Done *---------------------------------------------------------- 188 return retValue; 189 } // isEmptyOrBlank() 190 191 /** 192 * Tests if the given String is not {@code null} and not the empty 193 * String. 194 * 195 * @param input The String to test. 196 * @return {@code true} if the given String reference is not 197 * {@code null} and not the empty String. 198 * 199 * @since 0.1.0 200 */ 201 @API( status = STABLE, since = "0.1.0" ) 202 public static final boolean isNotEmpty( final CharSequence input ) { return nonNull( input ) && !input.isEmpty(); } 203 204 /** 205 * Tests if the given String is not {@code null}, not the empty String, 206 * and that it contains other characters than just whitespace. 207 * 208 * @param input The String to test. 209 * @return {@code true} if the given String reference is not 210 * {@code null} and not the empty String, and it contains other 211 * characters than just whitespace. 212 * 213 * @see String#isBlank() 214 * 215 * @since 0.1.0 216 */ 217 @API( status = STABLE, since = "0.1.0" ) 218 public static final boolean isNotEmptyOrBlank( final CharSequence input ) 219 { 220 final var retValue = nonNull( input ) && !input.toString().isBlank(); 221 222 //---* Done *---------------------------------------------------------- 223 return retValue; 224 } // isNotEmptyOrBlank() 225 226 /** 227 * This method uses reflection to determine if the two objects are 228 * equal.<br> 229 * <br>It uses 230 * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} 231 * to gain access to private fields. This means that it will throw a 232 * security exception if run under a security manager, if the permissions 233 * are not set up correctly. It is also not as efficient as testing 234 * explicitly.<br> 235 * <br>Transient members will be not be tested, as they are likely derived 236 * fields, and not part of the value of the object instance.<br> 237 * <br>Static fields will not be tested. Superclass fields will be 238 * included. 239 * 240 * @param lhs <code>this</code> object. 241 * @param rhs The other object 242 * @return {@code true} if the two objects have tested equals, 243 * {@code false} otherwise. 244 * 245 * @extauthor Apache Software Foundation 246 * @extauthor Steve Downey - steve.downey@netfolio.com 247 * @extauthor Gary Gregory 248 * @extauthor Pete Gieser 249 * @extauthor Arun Mammen Thomas 250 * @modified Thomas Thrien - thomas.thrien@tquadrat.org 251 */ 252 @SuppressWarnings( "BooleanMethodNameMustStartWithQuestion" ) 253 @API( status = STABLE, since = "0.0.5" ) 254 public static final boolean reflectionEquals( final Object lhs, final Object rhs ) 255 { 256 return reflectionEquals( lhs, rhs, false, null, null ); 257 } // reflectionEquals() 258 259 /** 260 * This method uses reflection to determine if the two objects are 261 * equal.<br> 262 * <br>It uses 263 * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} 264 * to gain access to private fields. This means that it will throw a 265 * security exception if run under a security manager and the permissions 266 * are not set up correctly. It is also not as efficient as testing 267 * explicitly.<br> 268 * <br>Transient members will be not be tested, as they are likely derived 269 * fields, and not part of the value of the object instance.<br> 270 * <br>Static fields will not be tested. Superclass fields will be 271 * included. 272 * 273 * @param lhs <code>this</code> object. 274 * @param rhs The other object 275 * @param excludeFields A 276 * {@link Collection} 277 * of String field names to exclude from testing. 278 * @return {@code true} if the two objects have tested equals, 279 * {@code false} otherwise. 280 * 281 * @extauthor Apache Software Foundation 282 * @extauthor Steve Downey - steve.downey@netfolio.com 283 * @extauthor Gary Gregory 284 * @extauthor Pete Gieser 285 * @extauthor Arun Mammen Thomas 286 * @modified Thomas Thrien - thomas.thrien@tquadrat.org 287 */ 288 @SuppressWarnings( "BooleanMethodNameMustStartWithQuestion" ) 289 @API( status = STABLE, since = "0.0.5" ) 290 public static final boolean reflectionEquals( final Object lhs, final Object rhs, final Collection<String> excludeFields ) 291 { 292 return reflectionEquals( lhs, rhs, requireNonNullArgument( excludeFields, "excludeFields" ).toArray( EMPTY_String_ARRAY ) ); 293 } // reflectionEquals() 294 295 /** 296 * This method uses reflection to determine if the two objects are 297 * equal.<br> 298 * <br>It uses 299 * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} 300 * to gain access to private fields. This means that it will throw a 301 * security exception if run under a security manager, if the permissions 302 * are not set up correctly. It is also not as efficient as testing 303 * explicitly.<br> 304 * <br>Transient members will be not be tested, as they are likely derived 305 * fields, and not part of the value of the object instance.<br> 306 * <br>Static fields will not be tested. Superclass fields will be 307 * included. 308 * 309 * @param lhs <code>this</code> object. 310 * @param rhs The other object 311 * @param excludeFields An array of String field names to exclude from 312 * testing; may be {@code null}. 313 * @return {@code true} if the two objects have tested equals, 314 * {@code false} otherwise. 315 * 316 * @extauthor Apache Software Foundation 317 * @extauthor Steve Downey - steve.downey@netfolio.com 318 * @extauthor Gary Gregory 319 * @extauthor Pete Gieser 320 * @extauthor Arun Mammen Thomas 321 * @modified Thomas Thrien - thomas.thrien@tquadrat.org 322 */ 323 @SuppressWarnings( "BooleanMethodNameMustStartWithQuestion" ) 324 @API( status = STABLE, since = "0.0.5" ) 325 public static final boolean reflectionEquals( final Object lhs, final Object rhs, final String [] excludeFields ) 326 { 327 return reflectionEquals( lhs, rhs, false, null, excludeFields ); 328 } // reflectionEquals() 329 330 /** 331 * This method uses reflection to determine if the two objects are 332 * equal.<br> 333 * <br>It uses 334 * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} 335 * to gain access to private fields. This means that it will throw a 336 * security exception if run under a security manager, if the permissions 337 * are not set up correctly. It is also not as efficient as testing 338 * explicitly.<br> 339 * <br>If the <code>testTransients</code> parameter is set to 340 * {@code true}, transient members will be tested, otherwise they are 341 * ignored, as they are likely derived fields, and not part of the value 342 * of the object instance.<br> 343 * <br>Static fields will not be tested. Superclass fields will be 344 * included. 345 * 346 * @param lhs <code>this</code> object. 347 * @param rhs The other object 348 * @param testTransients {@code true} whether to include transient 349 * fields, {@code false} otherwise. 350 * @return {@code true} if the two objects have tested equals, 351 * {@code false} otherwise. 352 * 353 * @extauthor Apache Software Foundation 354 * @extauthor Steve Downey - steve.downey@netfolio.com 355 * @extauthor Gary Gregory 356 * @extauthor Pete Gieser 357 * @extauthor Arun Mammen Thomas 358 * @modified Thomas Thrien - thomas.thrien@tquadrat.org 359 */ 360 @SuppressWarnings( "BooleanMethodNameMustStartWithQuestion" ) 361 @API( status = STABLE, since = "0.0.5" ) 362 public static final boolean reflectionEquals( final Object lhs, final Object rhs, final boolean testTransients ) 363 { 364 return reflectionEquals( lhs, rhs, testTransients, null, null ); 365 } // reflectionEquals() 366 367 /** 368 * This method uses reflection to determine if the two objects are 369 * equal.<br> 370 * <br>It uses 371 * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} 372 * to gain access to private fields. This means that it will throw a 373 * security exception if run under a security manager, if the permissions 374 * are not set up correctly. It is also not as efficient as testing 375 * explicitly.<br> 376 * <br>If the <code>testTransients</code> parameter is set to 377 * {@code true}, transient members will be tested, otherwise they are 378 * ignored, as they are likely derived fields, and not part of the value 379 * of the object instance.<br> 380 * <br>Static fields will not be tested. Superclass fields will be 381 * appended up to and including the specified superclass. A {@code null} 382 * superclass is treated as 383 * {@link java.lang.Object}. 384 * 385 * @param lhs <code>this</code> object. 386 * @param rhs The other object 387 * @param testTransients {@code true} whether to include transient 388 * fields, {@code false} otherwise. 389 * @param reflectUpToClass The superclass to reflect up to 390 * (inclusive), may be {@code null} 391 * @return {@code true} if the two objects have tested equals, 392 * {@code false} otherwise. 393 * 394 * @extauthor Apache Software Foundation 395 * @extauthor Steve Downey - steve.downey@netfolio.com 396 * @extauthor Gary Gregory 397 * @extauthor Pete Gieser 398 * @extauthor Arun Mammen Thomas 399 * @modified Thomas Thrien - thomas.thrien@tquadrat.org 400 */ 401 @SuppressWarnings( "BooleanMethodNameMustStartWithQuestion" ) 402 @API( status = STABLE, since = "0.0.5" ) 403 public static final boolean reflectionEquals( final Object lhs, final Object rhs, final boolean testTransients, final Class<?> reflectUpToClass ) 404 { 405 return reflectionEquals( lhs, rhs, testTransients, reflectUpToClass, null ); 406 } // reflectionEquals() 407 408 /** 409 * This method uses reflection to determine if the two objects are 410 * equal.<br> 411 * <br>It uses 412 * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} 413 * to gain access to private fields. This means that it will throw a 414 * security exception if run under a security manager, if the permissions 415 * are not set up correctly. It is also not as efficient as testing 416 * explicitly.<br> 417 * <br>If the <code>testTransients</code> parameter is set to 418 * {@code true}, transient members will be tested, otherwise they are 419 * ignored, as they are likely derived fields, and not part of the value 420 * of the object instance.<br> 421 * <br>Static fields will not be tested. Superclass fields will be 422 * appended up to and including the specified superclass. A {@code null} 423 * superclass is treated as 424 * {@link java.lang.Object}. 425 * 426 * @param lhs <code>this</code> object. 427 * @param rhs The other object 428 * @param testTransients {@code true} whether to include transient 429 * fields, {@code false} otherwise. 430 * @param reflectUpToClass The superclass to reflect up to 431 * (inclusive), may be {@code null} 432 * @param excludeFields An array of String field names to exclude from 433 * testing; may be {@code null}. 434 * @return {@code true} if the two objects have tested equals, 435 * {@code false} otherwise. 436 * 437 * @extauthor Apache Software Foundation 438 * @extauthor Steve Downey - steve.downey@netfolio.com 439 * @extauthor Gary Gregory 440 * @extauthor Pete Gieser 441 * @extauthor Arun Mammen Thomas 442 * @modified Thomas Thrien - thomas.thrien@tquadrat.org 443 */ 444 @SuppressWarnings( {"BooleanMethodNameMustStartWithQuestion", "OverlyComplexMethod"} ) 445 @API( status = STABLE, since = "0.0.5" ) 446 public static final boolean reflectionEquals( final Object lhs, final Object rhs, final boolean testTransients, final Class<?> reflectUpToClass, final String [] excludeFields ) 447 { 448 var retValue = lhs == rhs; 449 if( !retValue && nonNull( lhs ) && nonNull( rhs ) ) 450 { 451 /* 452 * Find the leaf class since there may be transients in the leaf 453 * class or in classes between the leaf and root. If we are not 454 * testing transients or a subclass has no ivars, then a subclass 455 * can test equals to a superclass. 456 */ 457 final var lhsClass = lhs.getClass(); 458 final var rhsClass = rhs.getClass(); 459 Class<?> testClass = null; 460 if( lhsClass.isInstance( rhs ) ) 461 { 462 testClass = lhsClass; 463 if( !rhsClass.isInstance( lhs ) ) 464 { 465 //---* rhsClass is a subclass of lhsClass *---------------- 466 testClass = rhsClass; 467 } 468 } 469 else if( rhsClass.isInstance( lhs ) ) 470 { 471 testClass = rhsClass; 472 if( !lhsClass.isInstance( rhs ) ) 473 { 474 //---* lhsClass is a subclass of rhsClass *---------------- 475 testClass = lhsClass; 476 } 477 } 478 479 if( nonNull( testClass ) ) 480 { 481 //---* The two classes are related *--------------------------- 482 final Set<String> excluded = nonNull( excludeFields ) ? new HashSet<>( asList( excludeFields ) ) : emptySet(); 483 try 484 { 485 retValue = testReflective( lhs, rhs, testClass, testTransients, excluded ); 486 while( retValue && nonNull( testClass.getSuperclass() ) && (testClass != reflectUpToClass) ) 487 { 488 testClass = testClass.getSuperclass(); 489 retValue = testReflective( lhs, rhs, testClass, testTransients, excluded ); 490 } 491 } 492 catch( final IllegalArgumentException ignored ) 493 { 494 /* 495 * In this case, we tried to test a subclass vs. a 496 * superclass and the subclass has ivars or the ivars are 497 * transient, and we are testing transients. 498 * If a subclass has ivars that we are trying to test them, 499 * we get an exception, and we know that the objects are not 500 * equal. 501 */ 502 retValue = false; 503 } 504 } 505 } 506 507 //---* Done *---------------------------------------------------------- 508 return retValue; 509 } // reflectionEquals() 510 511 /** 512 * Checks if the given value {@code a} is {@code null} and throws 513 * a 514 * {@link NullPointerException} 515 * if it is {@code null}; calls 516 * {@link java.util.Objects#requireNonNull(Object)} 517 * internally. 518 * 519 * @param <T> The type of the value to check. 520 * @param a The value to check. 521 * @return The value if it is not {@code null}. 522 * @throws NullPointerException {@code a} is {@code null}. 523 */ 524 @API( status = STABLE, since = "0.0.5" ) 525 public static final <T> T requireNonNull( final T a ) { return java.util.Objects.requireNonNull( a ); } 526 527 /** 528 * Checks if the given value {@code a} is {@code null} and throws 529 * a 530 * {@link NullPointerException} 531 * with the specified message if it is {@code null}. Calls 532 * {@link java.util.Objects#requireNonNull(Object, String)} 533 * internally. 534 * 535 * @param <T> The type of the value to check. 536 * @param a The value to check. 537 * @param message The message that is set to the thrown exception. 538 * @return The value if it is not {@code null}. 539 * @throws NullPointerException {@code a} is {@code null}. 540 */ 541 @API( status = STABLE, since = "0.0.5" ) 542 public static final <T> T requireNonNull( final T a, final String message ) { return java.util.Objects.requireNonNull( a, message ); } 543 544 /** 545 * Checks if the given argument {@code a} is {@code null} and throws 546 * a 547 * {@link NullPointerException} 548 * if it is {@code null}. 549 * 550 * @param <T> The type of the argument to check. 551 * @param a The argument to check. 552 * @param name The name of the argument; this is used for the error 553 * message. 554 * @return The argument if it is not {@code null}. 555 * @throws IllegalArgumentException {@code name} is empty. 556 * @throws NullPointerException {@code name} or {@code a} is 557 * {@code null}. 558 */ 559 @SuppressWarnings( "ProhibitedExceptionThrown" ) 560 @API( status = STABLE, since = "0.0.5" ) 561 public static final <T> T requireNonNullArgument( final T a, final String name ) 562 { 563 if( isNull( name) ) 564 { 565 throw new NullPointerException( "name is null" ); 566 } 567 else if( name.isEmpty() ) 568 { 569 throw new IllegalArgumentException( "name is empty" ); 570 } 571 if( isNull( a ) ) 572 { 573 throw new NullPointerException( format( "Argument '%s' is null", name ) ); 574 } 575 576 //---* Done *---------------------------------------------------------- 577 return a; 578 } // requireNonNullArgument() 579 580 /** 581 * Checks if the given argument {@code a} is {@code null} or empty 582 * and throws a 583 * {@link NullPointerException} 584 * if it is {@code null}, or a 585 * {@link IllegalArgumentException} 586 * if it is empty.<br> 587 * <br>Only Strings, arrays, 588 * {@link java.util.Collection}s, and 589 * {@link java.util.Map}s 590 * will be checked on being empty.<br> 591 * <br>Because the interface 592 * {@link java.util.Enumeration} 593 * does not provide an API for the check on emptiness 594 * ({@link java.util.Enumeration#hasMoreElements() hasMoreElements()} 595 * will return {@code false} after all elements have been taken from 596 * the {@code Enumeration} instance), the result for arguments of this 597 * type has to be taken with caution.<br> 598 * <br>For instances of 599 * {@link java.util.stream.Stream}, 600 * the method 601 * {@link java.util.stream.Stream#findAny()} 602 * is used to determine if the stream has elements. Using 603 * {@link java.util.stream.Stream#count() count()} 604 * may have negative impact on performance if the argument has a large 605 * amount of elements. 606 * 607 * @param <T> The type of the argument to check. 608 * @param a The argument to check; may be {@code null}. 609 * @param name The name of the argument; this is used for the error 610 * message. 611 * @return The argument if it is not {@code null}. 612 * @throws NullPointerException {@code name} or {@code a} is 613 * {@code null}. 614 * @throws IllegalArgumentException {@code name} or {@code a} is empty. 615 */ 616 @SuppressWarnings( {"ProhibitedExceptionThrown", "OverlyComplexMethod"} ) 617 @API( status = STABLE, since = "0.0.5" ) 618 public static final <T> T requireNotEmptyArgument( final T a, final String name ) 619 { 620 if( isNull( name ) ) 621 { 622 throw new NullPointerException( "name is null" ); 623 } 624 else if( name.isEmpty() ) 625 { 626 throw new IllegalArgumentException( "name is empty" ); 627 } 628 629 //---* Check for null *------------------------------------------------ 630 if( isNull( a ) ) 631 { 632 throw new NullPointerException( format( "Argument '%s' is null", name ) ); 633 } 634 635 //---* Check the type *------------------------------------------------ 636 //noinspection IfStatementWithTooManyBranches 637 if( a instanceof final CharSequence charSequence ) 638 { 639 if( charSequence.isEmpty() ) 640 { 641 throw new IllegalArgumentException( format( "Argument '%s' is empty", name ) ); 642 } 643 } 644 else if( a.getClass().isArray() ) 645 { 646 if( Array.getLength( a ) == 0 ) 647 { 648 throw new IllegalArgumentException( format( "Argument '%s' is empty", name ) ); 649 } 650 } 651 else if( a instanceof final Collection<?> collection ) 652 { 653 if( collection.isEmpty() ) 654 { 655 throw new IllegalArgumentException( format( "Argument '%s' is empty", name ) ); 656 } 657 } 658 else if( a instanceof final Map<?,?> map ) 659 { 660 if( map.isEmpty() ) 661 { 662 throw new IllegalArgumentException( format( "Argument '%s' is empty", name ) ); 663 } 664 } 665 else if( a instanceof final Enumeration<?> enumeration ) 666 { 667 if( !enumeration.hasMoreElements() ) 668 { 669 throw new IllegalArgumentException( format( "Argument '%s' is empty", name ) ); 670 } 671 } 672 else if( a instanceof final Stream<?> stream ) 673 { 674 final var any = stream.findAny(); 675 if( any.isEmpty() ) 676 { 677 throw new IllegalArgumentException( format( "Argument '%s' is empty", name ) ); 678 } 679 } 680 681 //---* Done *---------------------------------------------------------- 682 return a; 683 } // requireNotEmptyArgument() 684 685 /** 686 * Tests the fields on the given instances on equal. 687 * 688 * @param lhs The left-hand object. 689 * @param rhs The right-hand object. 690 * @param testClass The class that defines the details. 691 * @param useTransients {@code true} if to test transient fields 692 * also, {@code false} otherwise. 693 * @param excludeFields Set of field names to exclude from testing. 694 * @return {@code true} if all relevant fields are equal, 695 * {@code false} otherwise. 696 */ 697 @SuppressWarnings( "OverlyComplexBooleanExpression" ) 698 private static boolean testReflective( final Object lhs, final Object rhs, final Class<?> testClass, final boolean useTransients, final Collection<String> excludeFields ) 699 { 700 final var fields = testClass.getDeclaredFields(); 701 setAccessible( fields, true ); 702 var retValue = true; 703 for( var i = 0; (i < fields.length) && retValue; ++i ) 704 { 705 final var field = fields [i]; 706 final var modifiers = field.getModifiers(); 707 if( !excludeFields.contains( field.getName() ) 708 && (field.getName().indexOf( '$' ) == -1) 709 && (useTransients || !isTransient( modifiers )) 710 && (!isStatic( modifiers )) ) 711 { 712 try 713 { 714 retValue = deepEquals( field.get( lhs ), field.get( rhs ) ); 715 } 716 catch( final IllegalAccessException e ) 717 { 718 /* 719 * This can't happen. We would get a SecurityException 720 * instead. But we prefer to throw a runtime exception in 721 * case the impossible happens, instead of silently 722 * swallowing it. 723 */ 724 throw new InternalError( "Unexpected IllegalAccessException", e ); 725 } 726 } 727 } 728 729 //---* Done *---------------------------------------------------------- 730 return retValue; 731 } // testReflective() 732 733 /** 734 * Converts the given argument {@code object} into a 735 * {@link String}, 736 * usually by calling its 737 * {@link Object#toString() toString()} 738 * method. If the value of the argument is {@code null}, the text 739 * "null" will be returned instead. Arrays will be 740 * converted to a string through calling the respective {@code toString()} 741 * method from 742 * {@link java.util.Arrays} 743 * (this distinguishes this implementation from 744 * {link java.util.Objects#toString(Object, String)}). 745 * Values of type 746 * {@link java.util.Date} or 747 * {@link java.util.Calendar} 748 * will be translated based on the default locale - whatever that is. 749 * 750 * @param object The object; may be {@code null}. 751 * @return The object's string representation. 752 * 753 * @see java.util.Arrays#toString(boolean[]) 754 * @see java.util.Arrays#toString(byte[]) 755 * @see java.util.Arrays#toString(char[]) 756 * @see java.util.Arrays#toString(double[]) 757 * @see java.util.Arrays#toString(float[]) 758 * @see java.util.Arrays#toString(int[]) 759 * @see java.util.Arrays#toString(long[]) 760 * @see java.util.Arrays#toString(Object[]) 761 * @see java.util.Arrays#toString(short[]) 762 * @see java.util.Arrays#deepToString(Object[]) 763 * @see java.util.Locale#getDefault() 764 * @see #NULL_STRING 765 */ 766 @API( status = STABLE, since = "0.0.5" ) 767 public static final String toString( final Object object ) 768 { 769 return toString( object, NULL_STRING ); 770 } // toString() 771 772 /** 773 * Converts the given argument {@code object} into a 774 * {@link String}, 775 * usually by calling its 776 * {@link Object#toString() toString()} 777 * method. If the value of the argument is {@code null}, the text 778 * provided as the {@code nullDefault} argument will be returned instead. 779 * Arrays will be converted to a string through calling the respective 780 * {@code toString()} method from 781 * {@link java.util.Arrays} 782 * (this distinguishes this implementation from 783 * {link java.util.Objects#toString(Object, String)}). 784 * Values of type 785 * {@link java.util.Date} or 786 * {@link java.util.Calendar} 787 * will be translated based on the default locale - whatever that is. 788 * 789 * @param object The object; may be {@code null}. 790 * @param nullDefault The text that should be returned if {@code object} 791 * is {@code null}. 792 * @return The object's string representation. 793 * 794 * @see java.util.Arrays#toString(boolean[]) 795 * @see java.util.Arrays#toString(byte[]) 796 * @see java.util.Arrays#toString(char[]) 797 * @see java.util.Arrays#toString(double[]) 798 * @see java.util.Arrays#toString(float[]) 799 * @see java.util.Arrays#toString(int[]) 800 * @see java.util.Arrays#toString(long[]) 801 * @see java.util.Arrays#toString(Object[]) 802 * @see java.util.Arrays#toString(short[]) 803 * @see java.util.Arrays#deepToString(Object[]) 804 * @see java.util.Locale#getDefault() 805 */ 806 @SuppressWarnings( {"IfStatementWithTooManyBranches", "ChainOfInstanceofChecks", "OverlyComplexMethod"} ) 807 @API( status = STABLE, since = "0.0.5" ) 808 public static final String toString( final Object object, final String nullDefault ) 809 { 810 var retValue = requireNonNullArgument( nullDefault, "nullDefault" ); 811 if( nonNull( object ) ) 812 { 813 final var objectClass = object.getClass(); 814 if( objectClass.isArray() ) 815 { 816 if( objectClass == byte [].class ) 817 { 818 retValue = Arrays.toString( (byte []) object ); 819 } 820 else if( objectClass == short [].class ) 821 { 822 retValue = Arrays.toString( (short []) object ); 823 } 824 else if( objectClass == int [].class ) 825 { 826 retValue = Arrays.toString( (int []) object ); 827 } 828 else if( objectClass == long [].class ) 829 { 830 retValue = Arrays.toString( (long []) object ); 831 } 832 else if( objectClass == char [].class ) 833 { 834 retValue = Arrays.toString( (char []) object ); 835 } 836 else if( objectClass == float [].class ) 837 { 838 retValue = Arrays.toString( (float []) object ); 839 } 840 else if( objectClass == double [].class ) 841 { 842 retValue = Arrays.toString( (double []) object ); 843 } 844 else if( objectClass == boolean [].class ) 845 { 846 retValue = Arrays.toString( (boolean []) object ); 847 } 848 else 849 { 850 retValue = deepToString( (Object []) object ); 851 } 852 } 853 else 854 { 855 retValue = object.toString(); 856 } 857 } 858 859 //---* Done *---------------------------------------------------------- 860 return retValue; 861 } // toString() 862} 863// class TestUtils 864 865/* 866 * End of File 867 */