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