1 package org.djutils.exceptions; 2 3 import java.lang.reflect.Constructor; 4 import java.util.ArrayList; 5 import java.util.Arrays; 6 import java.util.IllegalFormatException; 7 import java.util.List; 8 9 import org.djutils.reflection.ClassUtil; 10 11 /** 12 * The Try class has a number of static methods that make it easy to try-catch an exception for any Throwable class, including 13 * the standard Java exceptions and exceptions from libraries that are used in the project. Instead of: 14 * 15 * <pre> 16 * FileInputStream fis; 17 * try 18 * { 19 * fis = new FileInputStream(fileString); 20 * } 21 * catch (FileNotFoundException exception) 22 * { 23 * throw new IllegalArgumentException("File " + fileString + " is not a valid file.", exception); 24 * } 25 * try 26 * { 27 * fis.close(); 28 * } 29 * catch (IOException exception) 30 * { 31 * throw new RuntimeException("Could not close the file.", exception); 32 * } 33 * </pre> 34 * 35 * we can write: 36 * 37 * <pre> 38 * FileInputStream fis = Try.assign(() -> new FileInputStream(fileString), IllegalArgumentException.class, 39 * "File %s is not a valid file.", fileString); 40 * Try.execute(() -> fis.close(), "Could not close the file."); 41 * </pre> 42 * 43 * The exception message can be formatted with additional arguments, such that the overhead of building the exception message 44 * only occurs if the exception condition is met. For each method there is a version without Throwable class, in which case a 45 * RuntimeException will be thrown.<br> 46 * <br> 47 * Try is not suitable for try-with-resource statements.<br> 48 * <br> 49 * Try also has a few methods to aid JUNIT tests: {@code testFail(...)} and {@code testNotFail(...)}. 50 * <p> 51 * Copyright (c) 2016-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See 52 * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is 53 * distributed under a three-clause BSD-style license, which can be found at 54 * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>. 55 * </p> 56 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a> 57 * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a> 58 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a> 59 */ 60 public final class Try 61 { 62 /** private constructor for utility class. */ 63 private Try() 64 { 65 // utility class 66 } 67 68 // Assign 69 70 /** 71 * Tries to return a value to assign. Will throw a RuntimeException if the try fails. 72 * @param assignment Assignment<V>; functional interface to assign value 73 * @param message String; the message to use in the throwable 74 * @param <V> value type 75 * @return V; value to assign 76 * @throws RuntimeException on failed Try 77 */ 78 public static <V> V assign(final Assignment<V> assignment, final String message) throws RuntimeException 79 { 80 try 81 { 82 return assignment.assign(); 83 } 84 catch (Throwable cause) 85 { 86 throw catchThrowable(RuntimeException.class, message, new ArrayList<>(), cause); 87 } 88 } 89 90 /** 91 * Tries to return a value to assign. Will throw a RuntimeException if the try fails. 92 * @param assignment Assignment<V>; functional interface to assign value 93 * @param message String; the message to use in the throwable, with formatting identifier 94 * @param arg Object; value to use for the formatting identifier 95 * @param <V> value type 96 * @return V; value to assign 97 * @throws RuntimeException on failed Try 98 */ 99 public static <V> V assign(final Assignment<V> assignment, final String message, final Object arg) throws RuntimeException 100 { 101 try 102 { 103 return assignment.assign(); 104 } 105 catch (Throwable cause) 106 { 107 List<Object> argList = new ArrayList<>(); 108 argList.add(arg); 109 throw catchThrowable(RuntimeException.class, message, argList, cause); 110 } 111 } 112 113 /** 114 * Tries to return a value to assign. Will throw a RuntimeException if the try fails. 115 * @param assignment Assignment<V>; functional interface to assign value 116 * @param message String; the message to use in the throwable, with formatting identifiers 117 * @param arg1 Object; 1st value to use for the formatting identifiers 118 * @param arg2 Object; 2nd value to use for the formatting identifiers 119 * @param <V> value type 120 * @return V; value to assign 121 * @throws RuntimeException on failed Try 122 */ 123 public static <V> V assign(final Assignment<V> assignment, final String message, final Object arg1, final Object arg2) 124 throws RuntimeException 125 { 126 try 127 { 128 return assignment.assign(); 129 } 130 catch (Throwable cause) 131 { 132 List<Object> argList = new ArrayList<>(); 133 argList.add(arg1); 134 argList.add(arg2); 135 throw catchThrowable(RuntimeException.class, message, argList, cause); 136 } 137 } 138 139 /** 140 * Tries to return a value to assign. Will throw a RuntimeException if the try fails. 141 * @param assignment Assignment<V>; functional interface to assign value 142 * @param message String; the message to use in the throwable, with formatting identifiers 143 * @param arg1 Object; 1st value to use for the formatting identifiers 144 * @param arg2 Object; 2nd value to use for the formatting identifiers 145 * @param arg3 Object; 3rd value to use for the formatting identifiers 146 * @param <V> value type 147 * @return V; value to assign 148 * @throws RuntimeException on failed Try 149 */ 150 public static <V> V assign(final Assignment<V> assignment, final String message, final Object arg1, final Object arg2, 151 final Object arg3) throws RuntimeException 152 { 153 try 154 { 155 return assignment.assign(); 156 } 157 catch (Throwable cause) 158 { 159 List<Object> argList = new ArrayList<>(); 160 argList.add(arg1); 161 argList.add(arg2); 162 argList.add(arg3); 163 throw catchThrowable(RuntimeException.class, message, argList, cause); 164 } 165 } 166 167 /** 168 * Tries to return a value to assign. Will throw a RuntimeException if the try fails. 169 * @param assignment Assignment<V>; functional interface to assign value 170 * @param message String; the message to use in the throwable, with formatting identifiers 171 * @param arg1 Object; 1st value to use for the formatting identifiers 172 * @param arg2 Object; 2nd value to use for the formatting identifiers 173 * @param arg3 Object; 3rd value to use for the formatting identifiers 174 * @param args Object...; potential 4th and further values to use for the formatting identifiers 175 * @param <V> value type 176 * @return V; value to assign 177 * @throws RuntimeException on failed Try 178 */ 179 public static <V> V assign(final Assignment<V> assignment, final String message, final Object arg1, final Object arg2, 180 final Object arg3, final Object... args) throws RuntimeException 181 { 182 try 183 { 184 return assignment.assign(); 185 } 186 catch (Throwable cause) 187 { 188 List<Object> argList = new ArrayList<>(); 189 argList.add(arg1); 190 argList.add(arg2); 191 argList.add(arg3); 192 argList.addAll(Arrays.asList(args)); 193 throw catchThrowable(RuntimeException.class, message, argList, cause); 194 } 195 } 196 197 /** 198 * Tries to return a value to assign. Will throw a specified Throwable if the try fails. 199 * @param assignment Assignment<V>; functional interface to assign value 200 * @param throwableClass Class<T>; class of the throwable to throw 201 * @param message String; the message to use in the throwable 202 * @param <V> value type 203 * @param <T> throwable type 204 * @return V; value to assign 205 * @throws T throwable on failed Try 206 */ 207 public static <V, T extends Throwable> V assign(final Assignment<V> assignment, final Class<T> throwableClass, 208 final String message) throws T 209 { 210 try 211 { 212 return assignment.assign(); 213 } 214 catch (Throwable cause) 215 { 216 throw catchThrowable(throwableClass, message, new ArrayList<>(), cause); 217 } 218 } 219 220 /** 221 * Tries to return a value to assign. Will throw a specified Throwable if the try fails. 222 * @param assignment Assignment<V>; functional interface to assign value 223 * @param throwableClass Class<T>; class of the throwable to throw 224 * @param message String; the message to use in the throwable, with formatting identifier 225 * @param arg Object; value to use for the formatting identifier 226 * @param <V> value type 227 * @param <T> throwable type 228 * @return V; value to assign 229 * @throws T throwable on failed Try 230 */ 231 public static <V, T extends Throwable> V assign(final Assignment<V> assignment, final Class<T> throwableClass, 232 final String message, final Object arg) throws T 233 { 234 try 235 { 236 return assignment.assign(); 237 } 238 catch (Throwable cause) 239 { 240 List<Object> argList = new ArrayList<>(); 241 argList.add(arg); 242 throw catchThrowable(throwableClass, message, argList, cause); 243 } 244 } 245 246 /** 247 * Tries to return a value to assign. Will throw a specified Throwable if the try fails. 248 * @param assignment Assignment<V>; functional interface to assign value 249 * @param throwableClass Class<T>; class of the throwable to throw 250 * @param message String; the message to use in the throwable, with formatting identifiers 251 * @param arg1 Object; 1st value to use for the formatting identifiers 252 * @param arg2 Object; 2nd value to use for the formatting identifiers 253 * @param <V> value type 254 * @param <T> throwable type 255 * @return V; value to assign 256 * @throws T throwable on failed Try 257 */ 258 public static <V, T extends Throwable> V assign(final Assignment<V> assignment, final Class<T> throwableClass, 259 final String message, final Object arg1, final Object arg2) throws T 260 { 261 try 262 { 263 return assignment.assign(); 264 } 265 catch (Throwable cause) 266 { 267 List<Object> argList = new ArrayList<>(); 268 argList.add(arg1); 269 argList.add(arg2); 270 throw catchThrowable(throwableClass, message, argList, cause); 271 } 272 } 273 274 /** 275 * Tries to return a value to assign. Will throw a specified Throwable if the try fails. 276 * @param assignment Assignment<V>; functional interface to assign value 277 * @param throwableClass Class<T>; class of the throwable to throw 278 * @param message String; the message to use in the throwable, with formatting identifiers 279 * @param arg1 Object; 1st value to use for the formatting identifiers 280 * @param arg2 Object; 2nd value to use for the formatting identifiers 281 * @param arg3 Object; 3rd value to use for the formatting identifiers 282 * @param <V> value type 283 * @param <T> throwable type 284 * @return V; value to assign 285 * @throws T throwable on failed Try 286 */ 287 public static <V, T extends Throwable> V assign(final Assignment<V> assignment, final Class<T> throwableClass, 288 final String message, final Object arg1, final Object arg2, final Object arg3) throws T 289 { 290 try 291 { 292 return assignment.assign(); 293 } 294 catch (Throwable cause) 295 { 296 List<Object> argList = new ArrayList<>(); 297 argList.add(arg1); 298 argList.add(arg2); 299 argList.add(arg3); 300 throw catchThrowable(throwableClass, message, argList, cause); 301 } 302 } 303 304 /** 305 * Tries to return a value to assign. Will throw a specified Throwable if the try fails. 306 * @param assignment Assignment<V>; functional interface to assign value 307 * @param throwableClass Class<T>; class of the throwable to throw 308 * @param message String; the message to use in the throwable, with formatting identifiers 309 * @param arg1 Object; 1st value to use for the formatting identifiers 310 * @param arg2 Object; 2nd value to use for the formatting identifiers 311 * @param arg3 Object; 3rd value to use for the formatting identifiers 312 * @param args Object...; potential 4th and further values to use for the formatting identifiers 313 * @param <V> value type 314 * @param <T> throwable type 315 * @return V; value to assign 316 * @throws T throwable on failed Try 317 */ 318 public static <V, T extends Throwable> V assign(final Assignment<V> assignment, final Class<T> throwableClass, 319 final String message, final Object arg1, final Object arg2, final Object arg3, final Object... args) throws T 320 { 321 try 322 { 323 return assignment.assign(); 324 } 325 catch (Throwable cause) 326 { 327 List<Object> argList = new ArrayList<>(); 328 argList.add(arg1); 329 argList.add(arg2); 330 argList.add(arg3); 331 argList.addAll(Arrays.asList(args)); 332 throw catchThrowable(throwableClass, message, argList, cause); 333 } 334 } 335 336 // Execute 337 338 /** 339 * Tries to execute. Will throw a RuntimeException if the try fails. 340 * @param execution Execution; functional interface to execute 341 * @param message String; the message to use in the throwable 342 * @throws RuntimeException on failed Try 343 */ 344 public static void execute(final Execution execution, final String message) throws RuntimeException 345 { 346 try 347 { 348 execution.execute(); 349 } 350 catch (Throwable cause) 351 { 352 throw catchThrowable(RuntimeException.class, message, new ArrayList<>(), cause); 353 } 354 } 355 356 /** 357 * Tries to execute. Will throw a RuntimeException if the try fails. 358 * @param execution Execution; functional interface to execute 359 * @param message String; the message to use in the throwable, with formatting identifier 360 * @param arg Object; value to use for the formatting identifier 361 * @throws RuntimeException on failed Try 362 */ 363 public static void execute(final Execution execution, final String message, final Object arg) throws RuntimeException 364 { 365 try 366 { 367 execution.execute(); 368 } 369 catch (Throwable cause) 370 { 371 List<Object> argList = new ArrayList<>(); 372 argList.add(arg); 373 throw catchThrowable(RuntimeException.class, message, argList, cause); 374 } 375 } 376 377 /** 378 * Tries to execute. Will throw a RuntimeException if the try fails. 379 * @param execution Execution; functional interface to execute 380 * @param message String; the message to use in the throwable, with formatting identifiers 381 * @param arg1 Object; 1st value to use for the formatting identifiers 382 * @param arg2 Object; 2nd value to use for the formatting identifiers 383 * @throws RuntimeException on failed Try 384 */ 385 public static void execute(final Execution execution, final String message, final Object arg1, final Object arg2) 386 throws RuntimeException 387 { 388 try 389 { 390 execution.execute(); 391 } 392 catch (Throwable cause) 393 { 394 List<Object> argList = new ArrayList<>(); 395 argList.add(arg1); 396 argList.add(arg2); 397 throw catchThrowable(RuntimeException.class, message, argList, cause); 398 } 399 } 400 401 /** 402 * Tries to execute. Will throw a RuntimeException if the try fails. 403 * @param execution Execution; functional interface to execute 404 * @param message String; the message to use in the throwable, with formatting identifiers 405 * @param arg1 Object; 1st value to use for the formatting identifiers 406 * @param arg2 Object; 2nd value to use for the formatting identifiers 407 * @param arg3 Object; 3rd value to use for the formatting identifiers 408 * @throws RuntimeException on failed Try 409 */ 410 public static void execute(final Execution execution, final String message, final Object arg1, final Object arg2, 411 final Object arg3) throws RuntimeException 412 { 413 try 414 { 415 execution.execute(); 416 } 417 catch (Throwable cause) 418 { 419 List<Object> argList = new ArrayList<>(); 420 argList.add(arg1); 421 argList.add(arg2); 422 argList.add(arg3); 423 throw catchThrowable(RuntimeException.class, message, argList, cause); 424 } 425 } 426 427 /** 428 * Tries to execute. Will throw a RuntimeException if the try fails. 429 * @param execution Execution; functional interface to execute 430 * @param message String; the message to use in the throwable, with formatting identifiers 431 * @param arg1 Object; 1st value to use for the formatting identifiers 432 * @param arg2 Object; 2nd value to use for the formatting identifiers 433 * @param arg3 Object; 3rd value to use for the formatting identifiers 434 * @param args Object...; potential 4th and further values to use for the formatting identifiers 435 * @throws RuntimeException on failed Try 436 */ 437 public static void execute(final Execution execution, final String message, final Object arg1, final Object arg2, 438 final Object arg3, final Object... args) throws RuntimeException 439 { 440 try 441 { 442 execution.execute(); 443 } 444 catch (Throwable cause) 445 { 446 List<Object> argList = new ArrayList<>(); 447 argList.add(arg1); 448 argList.add(arg2); 449 argList.add(arg3); 450 argList.addAll(Arrays.asList(args)); 451 throw catchThrowable(RuntimeException.class, message, argList, cause); 452 } 453 } 454 455 /** 456 * Tries to execute. Will throw a specified Throwable if the try fails. 457 * @param execution Execution; functional interface to execute 458 * @param throwableClass Class<T>; class of the throwable to throw 459 * @param message String; the message to use in the throwable 460 * @param <T> throwable type 461 * @throws T throwable on failed Try 462 */ 463 public static <T extends Throwable> void execute(final Execution execution, final Class<T> throwableClass, 464 final String message) throws T 465 { 466 try 467 { 468 execution.execute(); 469 } 470 catch (Throwable cause) 471 { 472 throw catchThrowable(throwableClass, message, new ArrayList<>(), cause); 473 } 474 } 475 476 /** 477 * Tries to execute. Will throw a specified Throwable if the try fails. 478 * @param execution Execution; functional interface to execute 479 * @param throwableClass Class<T>; class of the throwable to throw 480 * @param message String; the message to use in the throwable, with formatting identifier 481 * @param arg Object; value to use for the formatting identifier 482 * @param <T> throwable type 483 * @throws T throwable on failed Try 484 */ 485 public static <T extends Throwable> void execute(final Execution execution, final Class<T> throwableClass, 486 final String message, final Object arg) throws T 487 { 488 try 489 { 490 execution.execute(); 491 } 492 catch (Throwable cause) 493 { 494 List<Object> argList = new ArrayList<>(); 495 argList.add(arg); 496 throw catchThrowable(throwableClass, message, argList, cause); 497 } 498 } 499 500 /** 501 * Tries to execute. Will throw a specified Throwable if the try fails. 502 * @param execution Execution; functional interface to execute 503 * @param throwableClass Class<T>; class of the throwable to throw 504 * @param message String; the message to use in the throwable, with formatting identifiers 505 * @param arg1 Object; 1st value to use for the formatting identifiers 506 * @param arg2 Object; 2nd value to use for the formatting identifiers 507 * @param <T> throwable type 508 * @throws T throwable on failed Try 509 */ 510 public static <T extends Throwable> void execute(final Execution execution, final Class<T> throwableClass, 511 final String message, final Object arg1, final Object arg2) throws T 512 { 513 try 514 { 515 execution.execute(); 516 } 517 catch (Throwable cause) 518 { 519 List<Object> argList = new ArrayList<>(); 520 argList.add(arg1); 521 argList.add(arg2); 522 throw catchThrowable(throwableClass, message, argList, cause); 523 } 524 } 525 526 /** 527 * Tries to execute. Will throw a specified Throwable if the try fails. 528 * @param execution Execution; functional interface to execute 529 * @param throwableClass Class<T>; class of the throwable to throw 530 * @param message String; the message to use in the throwable, with formatting identifiers 531 * @param arg1 Object; 1st value to use for the formatting identifiers 532 * @param arg2 Object; 2nd value to use for the formatting identifiers 533 * @param arg3 Object; 3rd value to use for the formatting identifiers 534 * @param <T> throwable type 535 * @throws T throwable on failed Try 536 */ 537 public static <T extends Throwable> void execute(final Execution execution, final Class<T> throwableClass, 538 final String message, final Object arg1, final Object arg2, final Object arg3) throws T 539 { 540 try 541 { 542 execution.execute(); 543 } 544 catch (Throwable cause) 545 { 546 List<Object> argList = new ArrayList<>(); 547 argList.add(arg1); 548 argList.add(arg2); 549 argList.add(arg3); 550 throw catchThrowable(throwableClass, message, argList, cause); 551 } 552 } 553 554 /** 555 * Tries to execute. Will throw a specified Throwable if the try fails. 556 * @param execution Execution; functional interface to execute 557 * @param throwableClass Class<T>; class of the throwable to throw 558 * @param message String; the message to use in the throwable, with formatting identifiers 559 * @param arg1 Object; 1st value to use for the formatting identifiers 560 * @param arg2 Object; 2nd value to use for the formatting identifiers 561 * @param arg3 Object; 3rd value to use for the formatting identifiers 562 * @param args Object...; potential 4th and further values to use for the formatting identifiers 563 * @param <T> throwable type 564 * @throws T throwable on failed Try 565 */ 566 public static <T extends Throwable> void execute(final Execution execution, final Class<T> throwableClass, 567 final String message, final Object arg1, final Object arg2, final Object arg3, final Object... args) throws T 568 { 569 try 570 { 571 execution.execute(); 572 } 573 catch (Throwable cause) 574 { 575 List<Object> argList = new ArrayList<>(); 576 argList.add(arg1); 577 argList.add(arg2); 578 argList.add(arg3); 579 argList.addAll(Arrays.asList(args)); 580 throw catchThrowable(throwableClass, message, argList, cause); 581 } 582 } 583 584 // Core of assign/execute methods 585 586 /** 587 * Core method to create the Throwable to throw. 588 * @param throwableClass Class<T>; the throwable class 589 * @param message String; the message to construct when an exception is thrown. 590 * @param argList List<Object>; List<Object> the arguments as implied by format escapes in 591 * <code>message</code> 592 * @param cause Throwable; underlying cause thrown inside the assign()/execute() 593 * @param <T> throwable type 594 * @return T; throwable 595 */ 596 private static <T extends Throwable> T catchThrowable(final Class<T> throwableClass, final String message, 597 final List<Object> argList, final Throwable cause) 598 { 599 // create a clear message 600 List<StackTraceElement> steList = new ArrayList<>(Arrays.asList(new Throwable().getStackTrace())); 601 // see https://stackoverflow.com/questions/2411487/nullpointerexception-in-java-with-no-stacktrace 602 // and https://hg.openjdk.java.net/jdk/jdk/file/tip/src/hotspot/share/opto/graphKit.cpp 603 if (steList.size() > 2) 604 { 605 steList.remove(0); // remove the catchThrowable(...) call 606 steList.remove(0); // remove the Try.assign/execute(...) call 607 } 608 StackTraceElement[] ste = steList.toArray(new StackTraceElement[steList.size()]); 609 String where = ste[0].getClassName() + "." + ste[0].getMethodName() + " (" + ste[0].getLineNumber() + "): "; 610 Object[] args = argList.toArray(); 611 String formattedMessage; 612 try 613 { 614 formattedMessage = where + String.format(message, args); 615 } 616 catch (IllegalFormatException exception) 617 { 618 formattedMessage = where + message + " [FormatException; args=" + argList + "]"; 619 } 620 621 // throw all other exceptions through reflection 622 T exception; 623 try 624 { 625 Constructor<T> constructor = 626 ClassUtil.resolveConstructor(throwableClass, new Class<?>[] {String.class, Throwable.class}); 627 List<StackTraceElement> steCause = new ArrayList<>(Arrays.asList(cause.getStackTrace())); 628 // see https://stackoverflow.com/questions/2411487/nullpointerexception-in-java-with-no-stacktrace 629 // and https://hg.openjdk.java.net/jdk/jdk/file/tip/src/hotspot/share/opto/graphKit.cpp 630 if (steCause.size() > 3) 631 { 632 steCause.remove(steCause.size() - 1); // remove method that called Try.assign/execute(...) 633 steCause.remove(steCause.size() - 1); // remove the Try.assign/execute(...) call 634 steCause.remove(steCause.size() - 1); // remove the Assignment/Execution implementation (can be lambda$#) 635 cause.setStackTrace(steCause.toArray(new StackTraceElement[steCause.size()])); 636 } 637 exception = constructor.newInstance(formattedMessage, cause); 638 exception.setStackTrace(ste); 639 } 640 catch (Throwable t) 641 { 642 RuntimeException rte = new RuntimeException(t.getMessage(), new Exception(formattedMessage, cause)); 643 rte.setStackTrace(ste); 644 throw rte; 645 } 646 return exception; 647 } 648 649 // Test fail/succeed (JUNIT) 650 651 /** 652 * Method for unit tests to test if an expected exception is thrown on an assignment. This method does not provide an 653 * explanation, and it is not checking for a specific type of exception to be thrown. The testFail() method throws an 654 * AssertionError when the assignment does not throw any exception. A way to use the method is, for instance: <br> 655 * 656 * <pre> 657 * <code> 658 * Try.testFail(() -> methodFailsOnNull(null)); 659 * </code> 660 * </pre> 661 * 662 * or 663 * 664 * <pre><code> 665 * Try.testFail(new Try.Assignment<Double>() 666 * { 667 * {@literal @}Override 668 * public Double assign() throws Throwable 669 * { 670 * return methodFailsOnNull(null); 671 * } 672 * }); 673 * </code></pre> 674 * 675 * @param assignment Assignment<V>; functional interface to assign value 676 * @param <V> value type, which is the return type of the assignment 677 * @return V; assigned value 678 * @throws AssertionError when the assignment fails to throw an exception 679 */ 680 public static <V> V testFail(final Assignment<V> assignment) 681 { 682 return testFail(assignment, null, Throwable.class); 683 } 684 685 /** 686 * Method for unit tests to test if an expected exception is thrown on an assignment. This method provides an explanation 687 * message, but it is not checking for a specific type of exception to be thrown. The testFail() method throws an 688 * AssertionError when the assignment does not throw an exception. A way to use the method is, for instance: <br> 689 * 690 * <pre> 691 * <code> 692 * Try.testFail(() -> methodFailsOnNull(null), "call should have thrown an NPE"); 693 * </code> 694 * </pre> 695 * 696 * or 697 * 698 * <pre><code> 699 * Try.testFail(new Try.Assignment<Double>() 700 * { 701 * {@literal @}Override 702 * public Double assign() throws Throwable 703 * { 704 * return methodFailsOnNull(null); 705 * } 706 * }, "call should have thrown an NPE"); 707 * </code></pre> 708 * 709 * @param assignment Assignment<V>; functional interface to assign value 710 * @param <V> value type, which is the return type of the assignment 711 * @param message String; message to use in the AssertionError when the assignment succeeds 712 * @return V; assigned value 713 * @throws AssertionError when the assignment fails to throw an exception 714 */ 715 public static <V> V testFail(final Assignment<V> assignment, final String message) 716 { 717 return testFail(assignment, message, Throwable.class); 718 } 719 720 /** 721 * Method for unit tests to test if an expected exception is thrown on an assignment. This method does not provide an 722 * explanation, but it is checking for a specific type of exception to be thrown. The testFail() method throws an 723 * AssertionError when the assignment does not throw an exception, or when it throws a different exception than 724 * expectedThrowableClass. A way to use the method is, for instance: <br> 725 * 726 * <pre> 727 * <code> 728 * Try.testFail(() -> methodFailsOnNull(null), NullPointerException.class); 729 * </code> 730 * </pre> 731 * 732 * or 733 * 734 * <pre><code> 735 * Try.testFail(new Try.Assignment<Double>() 736 * { 737 * {@literal @}Override 738 * public Double assign() throws Throwable 739 * { 740 * return methodFailsOnNull(null); 741 * } 742 * }, NullPointerException.class); 743 * </code></pre> 744 * 745 * @param assignment Assignment<V>; functional interface to assign value 746 * @param expectedThrowableClass Class<T>; the class of the exception we expect the assignment to throw 747 * @param <V> value type, which is the return type of the assignment 748 * @param <T> throwable type, which ensures that we provide a throwable class as the argument 749 * @return V; assigned value 750 * @throws AssertionError when the assignment fails to throw an exception or the correct exception 751 */ 752 public static <V, T extends Throwable> V testFail(final Assignment<V> assignment, final Class<T> expectedThrowableClass) 753 { 754 return testFail(assignment, null, expectedThrowableClass); 755 } 756 757 /** 758 * Method for unit tests to test if an expected exception is thrown on an assignment. This method provides an explanation 759 * message, and it is checking for a specific type of exception to be thrown. The testFail() method throws an AssertionError 760 * when the assignment does not throw an exception, or when it throws a different exception than expectedThrowableClass. A 761 * way to use the method is, for instance: <br> 762 * 763 * <pre> 764 * <code> 765 * Try.testFail(() -> methodFailsOnNull(null), "call should have thrown an NPE", NullPointerException.class); 766 * </code> 767 * </pre> 768 * 769 * or 770 * 771 * <pre><code> 772 * Try.testFail(new Try.Assignment<Double>() 773 * { 774 * {@literal @}Override 775 * public Double assign() throws Throwable 776 * { 777 * return methodFailsOnNull(null); 778 * } 779 * }, "call should have thrown an NPE", NullPointerException.class); 780 * </code></pre> 781 * 782 * @param assignment Assignment<V>; functional interface to assign value 783 * @param message String; message to use in the AssertionError when the test fails 784 * @param expectedThrowableClass Class<T>; the class of the exception we expect the assignment to throw 785 * @param <V> value type, which is the return type of the assignment 786 * @param <T> throwable type, which ensures that we provide a throwable class as the argument 787 * @return V; assigned value 788 */ 789 public static <V, T extends Throwable> V testFail(final Assignment<V> assignment, final String message, 790 final Class<T> expectedThrowableClass) 791 { 792 try 793 { 794 assignment.assign(); 795 } 796 catch (Throwable cause) 797 { 798 if (!expectedThrowableClass.isAssignableFrom(cause.getClass())) 799 { 800 throw new AssertionError(message + "; Assignment failed on unexpected Throwable, expected (" 801 + expectedThrowableClass.getSimpleName() + "), but got (" + cause.getClass().getSimpleName() + ")."); 802 } 803 return null; 804 } 805 throw new AssertionError(message + "; Assignment did not throw any exception"); 806 } 807 808 /** 809 * Method for unit tests to test if an expected exception is thrown on code execution. This method does not provide an 810 * explanation message, nor is it checking for a specific type of exception to be thrown. The testFail() method throws an 811 * AssertionError when the execution does not throw an exception. A way to use the method is, for instance: <br> 812 * 813 * <pre> 814 * <code> 815 * Try.testFail(() -> methodFailsOnNull(null)); 816 * </code> 817 * </pre> 818 * 819 * or 820 * 821 * <pre><code> 822 * Try.testFail(new Try.Execution() 823 * { 824 * {@literal @}Override 825 * public void execute() throws Throwable 826 * { 827 * methodFailsOnNull(null); 828 * } 829 * }); 830 * </code></pre> 831 * 832 * @param execution Execution; functional interface to execute a method that does not need to return a value 833 */ 834 public static void testFail(final Execution execution) 835 { 836 testFail(execution, null, Throwable.class); 837 } 838 839 /** 840 * Method for unit tests to test if an expected exception is thrown on code execution. This method provides an explanation 841 * message, but it is not checking for a specific type of exception to be thrown. The testFail() method throws an 842 * AssertionError when the execution does not throw an exception, or when it throws a different exception than 843 * expectedThrowableClass. A way to use the method is, for instance: <br> 844 * 845 * <pre> 846 * <code> 847 * Try.testFail(() -> methodFailsOnNull(null), "call should have thrown an NPE"); 848 * </code> 849 * </pre> 850 * 851 * or 852 * 853 * <pre><code> 854 * Try.testFail(new Try.Execution() 855 * { 856 * {@literal @}Override 857 * public void execute() throws Throwable 858 * { 859 * methodFailsOnNull(null); 860 * } 861 * }, "call should have thrown an NPE"); 862 * </code></pre> 863 * 864 * @param execution Execution; functional interface to execute a method that does not need to return a value 865 * @param message String; message to use in the AssertionError when the test fails 866 */ 867 public static void testFail(final Execution execution, final String message) 868 { 869 testFail(execution, message, Throwable.class); 870 } 871 872 /** 873 * Method for unit tests to test if an expected exception is thrown on code execution. This method does not provide an 874 * explanation message, but it is checking for a specific type of exception to be thrown. The testFail() method throws an 875 * AssertionError when the execution does not throw an exception, or when it throws a different exception than 876 * expectedThrowableClass. A way to use the method is, for instance: <br> 877 * 878 * <pre> 879 * <code> 880 * Try.testFail(() -> methodFailsOnNull(null), NullPointerException.class); 881 * </code> 882 * </pre> 883 * 884 * or 885 * 886 * <pre><code> 887 * Try.testFail(new Try.Execution() 888 * { 889 * {@literal @}Override 890 * public void execute() throws Throwable 891 * { 892 * methodFailsOnNull(null); 893 * } 894 * }, NullPointerException.class); 895 * </code></pre> 896 * 897 * @param execution Execution; functional interface to execute a method that does not need to return a value 898 * @param expectedThrowableClass Class<T>; the class of the exception we expect the execution to throw 899 * @param <T> throwable type, which ensures that we provide a throwable class as the argument 900 */ 901 public static <T extends Throwable> void testFail(final Execution execution, final Class<T> expectedThrowableClass) 902 { 903 testFail(execution, null, expectedThrowableClass); 904 } 905 906 /** 907 * Method for unit tests to test if an expected exception is thrown on code execution. This method provides an explanation 908 * message, and it is checking for a specific type of exception to be thrown. The testFail() method throws an AssertionError 909 * when the execution does not throw an exception, or when it throws a different exception than expectedThrowableClass. A 910 * way to use the method is, for instance: <br> 911 * 912 * <pre> 913 * <code> 914 * Try.testFail(() -> methodFailsOnNull(null), "call should have thrown an NPE", NullPointerException.class); 915 * </code> 916 * </pre> 917 * 918 * or 919 * 920 * <pre><code> 921 * Try.testFail(new Try.Execution() 922 * { 923 * {@literal @}Override 924 * public void execute() throws Throwable 925 * { 926 * methodFailsOnNull(null); 927 * } 928 * }, "call should have thrown an NPE", NullPointerException.class); 929 * </code></pre> 930 * 931 * @param execution Execution; functional interface to execute a method that does not need to return a value 932 * @param message String; message to use in the AssertionError when the test fails 933 * @param expectedThrowableClass Class<T>; the class of the exception we expect the execution to throw 934 * @param <T> throwable type, which ensures that we provide a throwable class as the argument 935 */ 936 public static <T extends Throwable> void testFail(final Execution execution, final String message, 937 final Class<T> expectedThrowableClass) 938 { 939 try 940 { 941 execution.execute(); 942 } 943 catch (Throwable cause) 944 { 945 if (!expectedThrowableClass.isAssignableFrom(cause.getClass())) 946 { 947 throw new AssertionError(message + "; Execution failed on unexpected Throwable, expected (" 948 + expectedThrowableClass.getSimpleName() + "), but got (" + cause.getClass().getSimpleName() + ")."); 949 } 950 // expected to fail 951 return; 952 } 953 throw new AssertionError(message + "; Execution did not throw any exception"); 954 } 955 956 // Interfaces 957 958 /** 959 * Functional interface for calls to Try.assign(...). For this a lambda expression can be used. 960 * 961 * <pre> 962 * FileInputStream fis = Try.assign(() -> new FileInputStream(fileString), IllegalArgumentException.class, 963 * "File %s is not a valid file.", fileString); 964 * </pre> 965 * <p> 966 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. 967 * <br> 968 * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>. 969 * </p> 970 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a> 971 * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a> 972 * @param <V> value type 973 */ 974 @FunctionalInterface 975 public interface Assignment<V> 976 { 977 /** 978 * Returns a value which is obtained from the context in which the Assignment was created. 979 * @return value which is obtained from the context in which the Assignment was created 980 * @throws Throwable on any throwable in the try 981 */ 982 V assign() throws Throwable; 983 } 984 985 /** 986 * Functional interface for calls to Try.execute(...). For this a lambda expression can be used. 987 * 988 * <pre> 989 * Try.execute(() -> fis.close(), "Could not close the file."); 990 * </pre> 991 * <p> 992 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. 993 * <br> 994 * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>. 995 * </p> 996 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a> 997 * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a> 998 */ 999 @FunctionalInterface 1000 public interface Execution 1001 { 1002 /** 1003 * Executes some code using the context in which the Execution was created. 1004 * @throws Throwable on any throwable in the try 1005 */ 1006 void execute() throws Throwable; 1007 } 1008 1009 }