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("Car may not be null.");
20 * }
21 * if (Double.isNaN(car.getPosition()))
22 * {
23 * throw new IllegalArgumentException("Position of car " + car + " is NaN.");
24 * }
25 * </pre>
26 *
27 * we can write:
28 *
29 * <pre>
30 * Throw.whenNull(car, "Car may not be null.");
31 * Throw.when(Double.isNaN(car.getPosition()), IllegalArgumentException.class, "Position of car %s is NaN.", 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, "Value may not be NaN.");
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<T>; 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, "Value may not be NaN for object %s.",
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<T>; 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 * "Value may not be NaN for object %s with name %s.", 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<T>; 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 * "Value may not be NaN for object %s with name %s and id %s.", 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<T>; 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 * "Value may not be NaN for object %s with name %s, id %s and parent %s.", 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<T>; 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<T>; the Throwable type to throw
202 * @param message String; the message to use in the exception, with potential formatting identifiers
203 * @param argList List<Object>; 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, "Value may not be NaN."));
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<T>; 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 * "Value may not be NaN for object %s.", 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<T>; 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 * "Value may not be NaN for object %s with name %s.", 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<T>; 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 * "Value may not be NaN for object %s with name %s and id %s.", 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<T>; 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 * "Value may not be NaN for object %s with name %s, id %s and parent %s.", 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<T>; 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(), "Value may not be null.");
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(), "Value may not be null for object %s.", 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(), "Value may not be null for object %s with name %s.", 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(), "Value may not be null for object %s with name %s and id %s.", 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(), "Value may not be null for object %s with name %s, id %s and parent %s.", 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 }