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-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, "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 @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, "Value may not be NaN."));
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<T>; 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 * "Value may not be NaN for object %s.", 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<T>; 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 * "Value may not be NaN for object %s with name %s.", 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<T>; 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 * "Value may not be NaN for object %s with name %s and id %s.", 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<T>; 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 * "Value may not be NaN for object %s with name %s, id %s and parent %s.", 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<T>; 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(), "Value may not be null.");
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(), "Value may not be null for object %s.", 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(), "Value may not be null for object %s with name %s.", 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(), "Value may not be null for object %s with name %s and id %s.", 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(), "Value may not be null for object %s with name %s, id %s and parent %s.", 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 }