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 Throw class has a number of static methods that make it easy to throw an exception under conditions for any Exception
13   * class, including the standard Java exceptions and exceptions from libraries that are used in the project. Instead of:
14   * 
15   * <pre>
16   * if (car == null)
17   * {
18   *     throw new NullPointerException(&quot;Car may not be null.&quot;);
19   * }
20   * if (Double.isNaN(car.getPosition()))
21   * {
22   *     throw new IllegalArgumentException(&quot;Position of car &quot; + car + &quot; is NaN.&quot;);
23   * }
24   * </pre>
25   * 
26   * we can write:
27   * 
28   * <pre>
29   * Throw.whenNull(car, &quot;Car may not be null.&quot;);
30   * Throw.when(Double.isNaN(car.getPosition()), IllegalArgumentException.class, &quot;Position of car %s is NaN.&quot;, car);
31   * </pre>
32   * 
33   * The exception message can be formatted with additional arguments, such that the overhead of building the exception message
34   * only occurs if the exception condition is met. All methods have a version where the first parameter is returned. Thereby, the
35   * Throw can be used as part of a <b>super</b>(...) call in a constructor.
36   * <p>
37   * Copyright (c) 2016-2021 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
38   * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
39   * distributed under a three-clause BSD-style license, which can be found at
40   * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>.
41   * </p>
42   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
43   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
44   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
45   */
46  public final class Throw
47  {
48      /** private constructor for utility class. */
49      private Throw()
50      {
51          // utility class
52      }
53  
54      /**
55       * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. Use as
56       * follows: <br>
57       * 
58       * <pre>
59       * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class, &quot;Value may not be NaN.&quot;);
60       * </pre>
61       * 
62       * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b>
63       * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
64       * @param message String; the message to use in the exception
65       * @throws T the throwable to throw on true condition
66       * @param <T> the Throwable type
67       */
68      public static <T extends Throwable> void when(final boolean condition, final Class<T> throwableClass, final String message)
69              throws T
70      {
71          if (condition)
72          {
73              throwMessage(throwableClass, message, new ArrayList<>());
74          }
75      }
76  
77      /**
78       * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. Use as
79       * follows: <br>
80       * 
81       * <pre>
82       * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class, &quot;Value may not be NaN for object %s.&quot;, object);
83       * </pre>
84       * 
85       * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b>
86       * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
87       * @param message String; the message to use in the exception, with formatting identifiers
88       * @param arg Object; value to use for the formatting identifiers
89       * @throws T the throwable to throw on true condition
90       * @param <T> the Throwable type
91       */
92      public static <T extends Throwable> void when(final boolean condition, final Class<T> throwableClass, final String message,
93              final Object arg) throws T
94      {
95          if (condition)
96          {
97              List<Object> argList = new ArrayList<>();
98              argList.add(arg);
99              throwMessage(throwableClass, message, argList);
100         }
101     }
102 
103     /**
104      * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. Use as
105      * follows: <br>
106      * 
107      * <pre>
108      * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class,
109      *         &quot;Value may not be NaN for object %s with name %s.&quot;, object, name);
110      * </pre>
111      * 
112      * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b>
113      * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
114      * @param message String; the message to use in the exception, with formatting identifiers
115      * @param arg1 Object; 1st value to use for the formatting identifiers
116      * @param arg2 Object; 2nd value to use for the formatting identifiers
117      * @throws T the throwable to throw on true condition
118      * @param <T> the Throwable type
119      */
120     public static <T extends Throwable> void when(final boolean condition, final Class<T> throwableClass, final String message,
121             final Object arg1, final Object arg2) throws T
122     {
123         if (condition)
124         {
125             List<Object> argList = new ArrayList<>();
126             argList.add(arg1);
127             argList.add(arg2);
128             throwMessage(throwableClass, message, argList);
129         }
130     }
131 
132     /**
133      * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. Use as
134      * follows: <br>
135      * 
136      * <pre>
137      * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class,
138      *         &quot;Value may not be NaN for object %s with name %s and id %s.&quot;, object, name, id);
139      * </pre>
140      * 
141      * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b>
142      * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
143      * @param message String; the message to use in the exception, with formatting identifiers
144      * @param arg1 Object; 1st value to use for the formatting identifiers
145      * @param arg2 Object; 2nd value to use for the formatting identifiers
146      * @param arg3 Object; 3rd value to use for the formatting identifiers
147      * @throws T the throwable to throw on true condition
148      * @param <T> the Throwable type
149      */
150     public static <T extends Throwable> void when(final boolean condition, final Class<T> throwableClass, final String message,
151             final Object arg1, final Object arg2, final Object arg3) throws T
152     {
153         if (condition)
154         {
155             List<Object> argList = new ArrayList<>();
156             argList.add(arg1);
157             argList.add(arg2);
158             argList.add(arg3);
159             throwMessage(throwableClass, message, argList);
160         }
161     }
162 
163     /**
164      * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. Use as
165      * follows: <br>
166      * 
167      * <pre>
168      * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class,
169      *         &quot;Value may not be NaN for object %s with name %s, id %s and parent %s.&quot;, object, name, id, parent);
170      * </pre>
171      * 
172      * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b>
173      * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
174      * @param message String; the message to use in the exception, with formatting identifiers
175      * @param arg1 Object; 1st value to use for the formatting identifiers
176      * @param arg2 Object; 2nd value to use for the formatting identifiers
177      * @param arg3 Object; 3rd value to use for the formatting identifiers
178      * @param args Object...; potential 4th and further values to use for the formatting identifiers
179      * @throws T the throwable to throw on true condition
180      * @param <T> the Throwable type
181      */
182     public static <T extends Throwable> void when(final boolean condition, final Class<T> throwableClass, final String message,
183             final Object arg1, final Object arg2, final Object arg3, final Object... args) throws T
184     {
185         if (condition)
186         {
187             List<Object> argList = new ArrayList<>();
188             argList.add(arg1);
189             argList.add(arg2);
190             argList.add(arg3);
191             argList.addAll(Arrays.asList(args));
192             throwMessage(throwableClass, message, argList);
193         }
194     }
195 
196     /**
197      * Private method to handle the throwing an Exception, Throwable or Error.
198      * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
199      * @param message String; the message to use in the exception, with potential formatting identifiers
200      * @param argList List&lt;Object&gt;; List with potential values to use for the formatting identifiers
201      * @throws T the throwable to throw
202      * @param <T> the Throwable type
203      */
204     private static <T extends Throwable> void throwMessage(final Class<T> throwableClass, final String message,
205             final List<Object> argList) throws T
206     {
207         // create a clear message
208         List<StackTraceElement> steList = new ArrayList<>(Arrays.asList(new Throwable().getStackTrace()));
209         steList.remove(0); // remove the throwMessage(...) call
210         steList.remove(0); // remove the when(...) call
211         StackTraceElement[] ste = steList.toArray(new StackTraceElement[steList.size()]);
212         String where = ste[0].getClassName() + "." + ste[0].getMethodName() + " (" + ste[0].getLineNumber() + "): ";
213         Object[] args = argList.toArray();
214         String formattedMessage;
215         try
216         {
217             formattedMessage = where + String.format(message, args);
218         }
219         catch (IllegalFormatException exception)
220         {
221             formattedMessage = where + message + " [FormatException; args=" + argList + "]";
222         }
223 
224         // throw all other exceptions through reflection
225         T exception;
226         try
227         {
228             Constructor<T> constructor = ClassUtil.resolveConstructor(throwableClass, new Class<?>[] {String.class});
229             exception = constructor.newInstance(formattedMessage);
230             exception.setStackTrace(ste);
231         }
232         catch (Throwable t)
233         {
234             RuntimeException rte = new RuntimeException(t.getMessage(), new Exception(formattedMessage));
235             rte.setStackTrace(ste);
236             throw rte;
237         }
238         throw exception;
239     }
240 
241     /**
242      * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. This
243      * version of the method returns its first parameter, so it can be used inside a constructor. Use e.g., as follows:
244      * 
245      * <pre>
246      * super(Throw.when(object, Double.isNaN(object.getValue()), IllegalArgumentException.class, "Value may not be NaN."));
247      * </pre>
248      * 
249      * @param object O; the object to return by this static method
250      * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b>
251      * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
252      * @param message String; the message to use in the exception
253      * @throws T the throwable to throw on true condition
254      * @param <T> the Throwable type
255      * @param <O> the Object type to return
256      * @return the object that was passed as the first parameter
257      */
258     public static <T extends Throwable, O extends Object> O when(final O object, final boolean condition,
259             final Class<T> throwableClass, final String message) throws T
260     {
261         if (condition)
262         {
263             throwMessage(throwableClass, message, new ArrayList<>());
264         }
265         return object;
266     }
267 
268     /**
269      * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. This
270      * version of the method returns its first parameter, so it can be used inside a constructor. Use e.g., as follows:
271      * 
272      * <pre>
273      * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class,
274      *         &quot;Value may not be NaN for object %s.&quot;, object));
275      * </pre>
276      * 
277      * @param object O; the object to return by this static method
278      * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b>
279      * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
280      * @param message String; the message to use in the exception, with formatting identifiers
281      * @param arg Object; value to use for the formatting identifiers
282      * @throws T the throwable to throw on true condition
283      * @param <T> the Throwable type
284      * @param <O> the Object type to return
285      * @return the object that was passed as the first parameter
286      */
287     public static <T extends Throwable, O extends Object> O when(final O object, final boolean condition,
288             final Class<T> throwableClass, final String message, final Object arg) throws T
289     {
290         if (condition)
291         {
292             List<Object> argList = new ArrayList<>();
293             argList.add(arg);
294             throwMessage(throwableClass, message, argList);
295         }
296         return object;
297     }
298 
299     /**
300      * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. This
301      * version of the method returns its first parameter, so it can be used inside a constructor. Use e.g., as follows:
302      * 
303      * <pre>
304      * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class,
305      *         &quot;Value may not be NaN for object %s with name %s.&quot;, object, name));
306      * </pre>
307      * 
308      * @param object O; the object to return by this static method
309      * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b>
310      * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
311      * @param message String; the message to use in the exception, with formatting identifiers
312      * @param arg1 Object; 1st value to use for the formatting identifiers
313      * @param arg2 Object; 2nd value to use for the formatting identifiers
314      * @throws T the throwable to throw on true condition
315      * @param <T> the Throwable type
316      * @param <O> the Object type to return
317      * @return the object that was passed as the first parameter
318      */
319     public static <T extends Throwable, O extends Object> O when(final O object, final boolean condition,
320             final Class<T> throwableClass, final String message, final Object arg1, final Object arg2) throws T
321     {
322         if (condition)
323         {
324             List<Object> argList = new ArrayList<>();
325             argList.add(arg1);
326             argList.add(arg2);
327             throwMessage(throwableClass, message, argList);
328         }
329         return object;
330     }
331 
332     /**
333      * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. This
334      * version of the method returns its first parameter, so it can be used inside a constructor. Use e.g., as follows:
335      * 
336      * <pre>
337      * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class,
338      *         &quot;Value may not be NaN for object %s with name %s and id %s.&quot;, object, name, id));
339      * </pre>
340      * 
341      * @param object O; the object to return by this static method
342      * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b>
343      * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
344      * @param message String; the message to use in the exception, with formatting identifiers
345      * @param arg1 Object; 1st value to use for the formatting identifiers
346      * @param arg2 Object; 2nd value to use for the formatting identifiers
347      * @param arg3 Object; 3rd value to use for the formatting identifiers
348      * @throws T the throwable to throw on true condition
349      * @param <T> the Throwable type
350      * @param <O> the Object type to return
351      * @return the object that was passed as the first parameter
352      */
353     public static <T extends Throwable, O extends Object> O when(final O object, final boolean condition,
354             final Class<T> throwableClass, final String message, final Object arg1, final Object arg2, final Object arg3)
355             throws T
356     {
357         if (condition)
358         {
359             List<Object> argList = new ArrayList<>();
360             argList.add(arg1);
361             argList.add(arg2);
362             argList.add(arg3);
363             throwMessage(throwableClass, message, argList);
364         }
365         return object;
366     }
367 
368     /**
369      * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. This
370      * version of the method returns its first parameter, so it can be used inside a constructor. Use e.g., as follows:
371      * 
372      * <pre>
373      * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class,
374      *         &quot;Value may not be NaN for object %s with name %s, id %s and parent %s.&quot;, object, name, id, parent));
375      * </pre>
376      * 
377      * @param object O; the object to return by this static method
378      * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b>
379      * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
380      * @param message String; the message to use in the exception, 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      * @param arg3 Object; 3rd value to use for the formatting identifiers
384      * @param args Object...; potential 4th and further values to use for the formatting identifiers
385      * @throws T the throwable to throw on true condition
386      * @param <T> the Throwable type
387      * @param <O> the Object type to return
388      * @return the object that was passed as the first parameter
389      */
390     public static <T extends Throwable, O extends Object> O when(final O object, final boolean condition,
391             final Class<T> throwableClass, final String message, final Object arg1, final Object arg2, final Object arg3,
392             final Object... args) throws T
393     {
394         if (condition)
395         {
396             List<Object> argList = new ArrayList<>();
397             argList.add(arg1);
398             argList.add(arg2);
399             argList.add(arg3);
400             argList.addAll(Arrays.asList(args));
401             throwMessage(throwableClass, message, argList);
402         }
403         return object;
404     }
405 
406     /**
407      * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows: <br>
408      * 
409      * <pre>
410      * Throw.when(object.getValue(), &quot;Value may not be null.&quot;);
411      * </pre>
412      * 
413      * @param object object to check; an exception will be thrown if this is <b>null</b>
414      * @param message String; the message to use in the exception
415      * @param <O> the Object type to return
416      * @return the object that was passed as the first parameter
417      * @throws NullPointerException if object is null
418      */
419     public static <O extends Object> O whenNull(final O object, final String message) throws NullPointerException
420     {
421         if (object == null)
422         {
423             throwMessage(NullPointerException.class, message, new ArrayList<>());
424         }
425         return object;
426     }
427 
428     /**
429      * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows: <br>
430      * 
431      * <pre>
432      * Throw.whenNull(object.getValue(), &quot;Value may not be null for object %s.&quot;, object);
433      * </pre>
434      * 
435      * @param object object to check; an exception will be thrown if this is <b>null</b>
436      * @param message String; the message to use in the exception, with formatting identifiers
437      * @param arg Object; value to use for the formatting identifiers
438      * @param <O> the Object type to return
439      * @return the object that was passed as the first parameter
440      * @throws NullPointerException if object is null
441      */
442     public static <O extends Object> O whenNull(final O object, final String message, final Object arg)
443             throws NullPointerException
444     {
445         if (object == null)
446         {
447             List<Object> argList = new ArrayList<>();
448             argList.add(arg);
449             throwMessage(NullPointerException.class, message, argList);
450         }
451         return object;
452     }
453 
454     /**
455      * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows: <br>
456      * 
457      * <pre>
458      * Throw.whenNull(object.getValue(), &quot;Value may not be null for object %s with name %s.&quot;, object, name);
459      * </pre>
460      * 
461      * @param object object to check; an exception will be thrown if this is <b>null</b>
462      * @param message String; the message to use in the exception, with formatting identifiers
463      * @param arg1 Object; 1st value to use for the formatting identifiers
464      * @param arg2 Object; 2nd value to use for the formatting identifiers
465      * @param <O> the Object type to return
466      * @return the object that was passed as the first parameter
467      * @throws NullPointerException if object is null
468      */
469     public static <O extends Object> O whenNull(final O object, final String message, final Object arg1, final Object arg2)
470             throws NullPointerException
471     {
472         if (object == null)
473         {
474             List<Object> argList = new ArrayList<>();
475             argList.add(arg1);
476             argList.add(arg2);
477             throwMessage(NullPointerException.class, message, argList);
478         }
479         return object;
480     }
481 
482     /**
483      * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows: <br>
484      * 
485      * <pre>
486      * Throw.whenNull(object.getValue(), &quot;Value may not be null for object %s with name %s and id %s.&quot;, object, name, id);
487      * </pre>
488      * 
489      * @param object object to check; an exception will be thrown if this is <b>null</b>
490      * @param message String; the message to use in the exception, with formatting identifiers
491      * @param arg1 Object; 1st value to use for the formatting identifiers
492      * @param arg2 Object; 2nd value to use for the formatting identifiers
493      * @param arg3 Object; 3rd value to use for the formatting identifiers
494      * @param <O> the Object type to return
495      * @return the object that was passed as the first parameter
496      * @throws NullPointerException if object is null
497      */
498     public static <O extends Object> O whenNull(final O object, final String message, final Object arg1, final Object arg2,
499             final Object arg3) throws NullPointerException
500     {
501         if (object == null)
502         {
503             List<Object> argList = new ArrayList<>();
504             argList.add(arg1);
505             argList.add(arg2);
506             argList.add(arg3);
507             throwMessage(NullPointerException.class, message, argList);
508         }
509         return object;
510     }
511 
512     /**
513      * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows: <br>
514      * 
515      * <pre>
516      * Throw.whenNull(object.getValue(), &quot;Value may not be null for object %s with name %s, id %s and parent %s.&quot;, object, name, id,
517      *         parent);
518      * </pre>
519      * 
520      * @param object object to check; an exception will be thrown if this is <b>null</b>
521      * @param message String; the message to use in the exception, with formatting identifiers
522      * @param arg1 Object; 1st value to use for the formatting identifiers
523      * @param arg2 Object; 2nd value to use for the formatting identifiers
524      * @param arg3 Object; 3rd value to use for the formatting identifiers
525      * @param args Object...; potential 4th and further values to use for the formatting identifiers
526      * @param <O> the Object type to return
527      * @return the object that was passed as the first parameter
528      * @throws NullPointerException if object is null
529      */
530     public static <O extends Object> O whenNull(final O object, final String message, final Object arg1, final Object arg2,
531             final Object arg3, final Object... args) throws NullPointerException
532     {
533         if (object == null)
534         {
535             List<Object> argList = new ArrayList<>();
536             argList.add(arg1);
537             argList.add(arg2);
538             argList.add(arg3);
539             argList.addAll(Arrays.asList(args));
540             throwMessage(NullPointerException.class, message, argList);
541         }
542         return object;
543     }
544 
545 }