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-2023 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-2023 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 * @version $Revision$, $LastChangedDate$, by $Author$, initial version 31 jan. 2018 <br>
971 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
972 * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
973 * @param <V> value type
974 */
975 @FunctionalInterface
976 public interface Assignment<V>
977 {
978 /**
979 * Returns a value which is obtained from the context in which the Assignment was created.
980 * @return value which is obtained from the context in which the Assignment was created
981 * @throws Throwable on any throwable in the try
982 */
983 V assign() throws Throwable;
984 }
985
986 /**
987 * Functional interface for calls to Try.execute(...). For this a lambda expression can be used.
988 *
989 * <pre>
990 * Try.execute(() -> fis.close(), "Could not close the file.");
991 * </pre>
992 * <p>
993 * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
994 * <br>
995 * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
996 * <p>
997 * @version $Revision$, $LastChangedDate$, by $Author$, initial version 31 jan. 2018 <br>
998 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
999 * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
1000 */
1001 @FunctionalInterface
1002 public interface Execution
1003 {
1004 /**
1005 * Executes some code using the context in which the Execution was created.
1006 * @throws Throwable on any throwable in the try
1007 */
1008 void execute() throws Throwable;
1009 }
1010
1011 }