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-2021 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="http://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 }