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