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