View Javadoc
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(() -&gt; new FileInputStream(fileString), IllegalArgumentException.class,
39   *         "File %s is not a valid file.", fileString);
40   * Try.execute(() -&gt; 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&lt;V&gt;; 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&lt;V&gt;; 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&lt;V&gt;; 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&lt;V&gt;; 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&lt;V&gt;; 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&lt;V&gt;; functional interface to assign value
200      * @param throwableClass Class&lt;T&gt;; 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&lt;V&gt;; functional interface to assign value
223      * @param throwableClass Class&lt;T&gt;; 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&lt;V&gt;; functional interface to assign value
249      * @param throwableClass Class&lt;T&gt;; 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&lt;V&gt;; functional interface to assign value
277      * @param throwableClass Class&lt;T&gt;; 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&lt;V&gt;; functional interface to assign value
307      * @param throwableClass Class&lt;T&gt;; 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&lt;T&gt;; 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&lt;T&gt;; 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&lt;T&gt;; 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&lt;T&gt;; 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&lt;T&gt;; 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&lt;T&gt;; the throwable class
589      * @param message String; the message to construct when an exception is thrown.
590      * @param argList List&lt;Object&gt;; List&lt;Object&gt; the arguments as implied by format escapes in
591      *            &lt;code&gt;message&lt;/code&gt;
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(() -&gt; methodFailsOnNull(null));
659      * </code>
660      * </pre>
661      * 
662      * or
663      * 
664      * <pre><code>
665      *   Try.testFail(new Try.Assignment&lt;Double&gt;()
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&lt;V&gt;; 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(() -&gt; 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&lt;Double&gt;()
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&lt;V&gt;; 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(() -&gt; methodFailsOnNull(null), NullPointerException.class);
729      * </code>
730      * </pre>
731      * 
732      * or
733      * 
734      * <pre><code>
735      *   Try.testFail(new Try.Assignment&lt;Double&gt;()
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&lt;V&gt;; functional interface to assign value
746      * @param expectedThrowableClass Class&lt;T&gt;; 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(() -&gt; 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&lt;Double&gt;()
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&lt;V&gt;; functional interface to assign value
783      * @param message String; message to use in the AssertionError when the test fails
784      * @param expectedThrowableClass Class&lt;T&gt;; 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(() -&gt; 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(() -&gt; 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(() -&gt; 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&lt;T&gt;; 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(() -&gt; 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&lt;T&gt;; 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(() -&gt; 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(() -&gt; 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 }