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-2019 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="https://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 =
229                     (Constructor<T>) ClassUtil.resolveConstructor(throwableClass, new Class<?>[] {String.class});
230             exception = constructor.newInstance(formattedMessage);
231             exception.setStackTrace(ste);
232         }
233         catch (Throwable t)
234         {
235             RuntimeException rte = new RuntimeException(t.getMessage(), new Exception(formattedMessage));
236             rte.setStackTrace(ste);
237             throw rte;
238         }
239         throw exception;
240     }
241 
242     /**
243      * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. This
244      * version of the method returns its first parameter, so it can be used inside a constructor. Use e.g., as follows:
245      * 
246      * <pre>
247      * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class, &quot;Value may not be NaN.&quot;));
248      * </pre>
249      * 
250      * @param object O; the object to return by this static method
251      * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b>
252      * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
253      * @param message String; the message to use in the exception
254      * @throws T the throwable to throw on true condition
255      * @param <T> the Throwable type
256      * @param <O> the Object type to return
257      * @return the object that was passed as the first parameter
258      */
259     public static <T extends Throwable, O extends Object> O when(final O object, final boolean condition,
260             final Class<T> throwableClass, final String message) throws T
261     {
262         if (condition)
263         {
264             throwMessage(throwableClass, message, new ArrayList<>());
265         }
266         return object;
267     }
268 
269     /**
270      * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. This
271      * version of the method returns its first parameter, so it can be used inside a constructor. Use e.g., as follows:
272      * 
273      * <pre>
274      * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class,
275      *         &quot;Value may not be NaN for object %s.&quot;, object));
276      * </pre>
277      * 
278      * @param object O; the object to return by this static method
279      * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b>
280      * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
281      * @param message String; the message to use in the exception, with formatting identifiers
282      * @param arg Object; value to use for the formatting identifiers
283      * @throws T the throwable to throw on true condition
284      * @param <T> the Throwable type
285      * @param <O> the Object type to return
286      * @return the object that was passed as the first parameter
287      */
288     public static <T extends Throwable, O extends Object> O when(final O object, final boolean condition,
289             final Class<T> throwableClass, final String message, final Object arg) throws T
290     {
291         if (condition)
292         {
293             List<Object> argList = new ArrayList<>();
294             argList.add(arg);
295             throwMessage(throwableClass, message, argList);
296         }
297         return object;
298     }
299 
300     /**
301      * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. This
302      * version of the method returns its first parameter, so it can be used inside a constructor. Use e.g., as follows:
303      * 
304      * <pre>
305      * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class,
306      *         &quot;Value may not be NaN for object %s with name %s.&quot;, object, name));
307      * </pre>
308      * 
309      * @param object O; the object to return by this static method
310      * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b>
311      * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
312      * @param message String; the message to use in the exception, with formatting identifiers
313      * @param arg1 Object; 1st value to use for the formatting identifiers
314      * @param arg2 Object; 2nd value to use for the formatting identifiers
315      * @throws T the throwable to throw on true condition
316      * @param <T> the Throwable type
317      * @param <O> the Object type to return
318      * @return the object that was passed as the first parameter
319      */
320     public static <T extends Throwable, O extends Object> O when(final O object, final boolean condition,
321             final Class<T> throwableClass, final String message, final Object arg1, final Object arg2) throws T
322     {
323         if (condition)
324         {
325             List<Object> argList = new ArrayList<>();
326             argList.add(arg1);
327             argList.add(arg2);
328             throwMessage(throwableClass, message, argList);
329         }
330         return object;
331     }
332 
333     /**
334      * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. This
335      * version of the method returns its first parameter, so it can be used inside a constructor. Use e.g., as follows:
336      * 
337      * <pre>
338      * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class,
339      *         &quot;Value may not be NaN for object %s with name %s and id %s.&quot;, object, name, id));
340      * </pre>
341      * 
342      * @param object O; the object to return by this static method
343      * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b>
344      * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
345      * @param message String; the message to use in the exception, with formatting identifiers
346      * @param arg1 Object; 1st value to use for the formatting identifiers
347      * @param arg2 Object; 2nd value to use for the formatting identifiers
348      * @param arg3 Object; 3rd value to use for the formatting identifiers
349      * @throws T the throwable to throw on true condition
350      * @param <T> the Throwable type
351      * @param <O> the Object type to return
352      * @return the object that was passed as the first parameter
353      */
354     public static <T extends Throwable, O extends Object> O when(final O object, final boolean condition,
355             final Class<T> throwableClass, final String message, final Object arg1, final Object arg2, final Object arg3)
356             throws T
357     {
358         if (condition)
359         {
360             List<Object> argList = new ArrayList<>();
361             argList.add(arg1);
362             argList.add(arg2);
363             argList.add(arg3);
364             throwMessage(throwableClass, message, argList);
365         }
366         return object;
367     }
368 
369     /**
370      * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. This
371      * version of the method returns its first parameter, so it can be used inside a constructor. Use e.g., as follows:
372      * 
373      * <pre>
374      * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class,
375      *         &quot;Value may not be NaN for object %s with name %s, id %s and parent %s.&quot;, object, name, id, parent));
376      * </pre>
377      * 
378      * @param object O; the object to return by this static method
379      * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b>
380      * @param throwableClass Class&lt;T&gt;; the Throwable type to throw
381      * @param message String; the message to use in the exception, with formatting identifiers
382      * @param arg1 Object; 1st value to use for the formatting identifiers
383      * @param arg2 Object; 2nd value to use for the formatting identifiers
384      * @param arg3 Object; 3rd value to use for the formatting identifiers
385      * @param args Object...; potential 4th and further values to use for the formatting identifiers
386      * @throws T the throwable to throw on true condition
387      * @param <T> the Throwable type
388      * @param <O> the Object type to return
389      * @return the object that was passed as the first parameter
390      */
391     public static <T extends Throwable, O extends Object> O when(final O object, final boolean condition,
392             final Class<T> throwableClass, final String message, final Object arg1, final Object arg2, final Object arg3,
393             final Object... args) throws T
394     {
395         if (condition)
396         {
397             List<Object> argList = new ArrayList<>();
398             argList.add(arg1);
399             argList.add(arg2);
400             argList.add(arg3);
401             argList.addAll(Arrays.asList(args));
402             throwMessage(throwableClass, message, argList);
403         }
404         return object;
405     }
406 
407     /**
408      * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows: <br>
409      * 
410      * <pre>
411      * Throw.when(object.getValue(), &quot;Value may not be null.&quot;);
412      * </pre>
413      * 
414      * @param object object to check; an exception will be thrown if this is <b>null</b>
415      * @param message String; the message to use in the exception
416      * @param <O> the Object type to return
417      * @return the object that was passed as the first parameter
418      * @throws NullPointerException if object is null
419      */
420     public static <O extends Object> O whenNull(final O object, final String message) throws NullPointerException
421     {
422         if (object == null)
423         {
424             throwMessage(NullPointerException.class, message, new ArrayList<>());
425         }
426         return object;
427     }
428 
429     /**
430      * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows: <br>
431      * 
432      * <pre>
433      * Throw.whenNull(object.getValue(), &quot;Value may not be null for object %s.&quot;, object);
434      * </pre>
435      * 
436      * @param object object to check; an exception will be thrown if this is <b>null</b>
437      * @param message String; the message to use in the exception, with formatting identifiers
438      * @param arg Object; value to use for the formatting identifiers
439      * @param <O> the Object type to return
440      * @return the object that was passed as the first parameter
441      * @throws NullPointerException if object is null
442      */
443     public static <O extends Object> O whenNull(final O object, final String message, final Object arg)
444             throws NullPointerException
445     {
446         if (object == null)
447         {
448             List<Object> argList = new ArrayList<>();
449             argList.add(arg);
450             throwMessage(NullPointerException.class, message, argList);
451         }
452         return object;
453     }
454 
455     /**
456      * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows: <br>
457      * 
458      * <pre>
459      * Throw.whenNull(object.getValue(), &quot;Value may not be null for object %s with name %s.&quot;, object, name);
460      * </pre>
461      * 
462      * @param object object to check; an exception will be thrown if this is <b>null</b>
463      * @param message String; the message to use in the exception, with formatting identifiers
464      * @param arg1 Object; 1st value to use for the formatting identifiers
465      * @param arg2 Object; 2nd value to use for the formatting identifiers
466      * @param <O> the Object type to return
467      * @return the object that was passed as the first parameter
468      * @throws NullPointerException if object is null
469      */
470     public static <O extends Object> O whenNull(final O object, final String message, final Object arg1, final Object arg2)
471             throws NullPointerException
472     {
473         if (object == null)
474         {
475             List<Object> argList = new ArrayList<>();
476             argList.add(arg1);
477             argList.add(arg2);
478             throwMessage(NullPointerException.class, message, argList);
479         }
480         return object;
481     }
482 
483     /**
484      * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows: <br>
485      * 
486      * <pre>
487      * Throw.whenNull(object.getValue(), &quot;Value may not be null for object %s with name %s and id %s.&quot;, object, name, id);
488      * </pre>
489      * 
490      * @param object object to check; an exception will be thrown if this is <b>null</b>
491      * @param message String; the message to use in the exception, with formatting identifiers
492      * @param arg1 Object; 1st value to use for the formatting identifiers
493      * @param arg2 Object; 2nd value to use for the formatting identifiers
494      * @param arg3 Object; 3rd value to use for the formatting identifiers
495      * @param <O> the Object type to return
496      * @return the object that was passed as the first parameter
497      * @throws NullPointerException if object is null
498      */
499     public static <O extends Object> O whenNull(final O object, final String message, final Object arg1, final Object arg2,
500             final Object arg3) throws NullPointerException
501     {
502         if (object == null)
503         {
504             List<Object> argList = new ArrayList<>();
505             argList.add(arg1);
506             argList.add(arg2);
507             argList.add(arg3);
508             throwMessage(NullPointerException.class, message, argList);
509         }
510         return object;
511     }
512 
513     /**
514      * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows: <br>
515      * 
516      * <pre>
517      * 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,
518      *         parent);
519      * </pre>
520      * 
521      * @param object object to check; an exception will be thrown if this is <b>null</b>
522      * @param message String; the message to use in the exception, with formatting identifiers
523      * @param arg1 Object; 1st value to use for the formatting identifiers
524      * @param arg2 Object; 2nd value to use for the formatting identifiers
525      * @param arg3 Object; 3rd value to use for the formatting identifiers
526      * @param args Object...; potential 4th and further values to use for the formatting identifiers
527      * @param <O> the Object type to return
528      * @return the object that was passed as the first parameter
529      * @throws NullPointerException if object is null
530      */
531     public static <O extends Object> O whenNull(final O object, final String message, final Object arg1, final Object arg2,
532             final Object arg3, final Object... args) throws NullPointerException
533     {
534         if (object == null)
535         {
536             List<Object> argList = new ArrayList<>();
537             argList.add(arg1);
538             argList.add(arg2);
539             argList.add(arg3);
540             argList.addAll(Arrays.asList(args));
541             throwMessage(NullPointerException.class, message, argList);
542         }
543         return object;
544     }
545 
546 }