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