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