1 package org.djutils.exceptions; 2 3 import java.lang.reflect.Constructor; 4 import java.util.ArrayList; 5 import java.util.Arrays; 6 import java.util.IllegalFormatException; 7 import java.util.List; 8 9 import org.djutils.reflection.ClassUtil; 10 11 /** 12 * The Throw class has a number of static methods that make it easy to throw an exception under conditions for any 13 * Exception class, including the standard Java exceptions and exceptions from libraries that are used in the project. 14 * Instead of: 15 * 16 * <pre> 17 * if (car == null) 18 * { 19 * throw new NullPointerException("Car may not be null."); 20 * } 21 * if (Double.isNaN(car.getPosition())) 22 * { 23 * throw new IllegalArgumentException("Position of car " + car + " is NaN."); 24 * } 25 * </pre> 26 * 27 * we can write: 28 * 29 * <pre> 30 * Throw.whenNull(car, "Car may not be null."); 31 * Throw.when(Double.isNaN(car.getPosition()), IllegalArgumentException.class, "Position of car %s is NaN.", car); 32 * </pre> 33 * 34 * The exception message can be formatted with additional arguments, such that the overhead of building the exception 35 * message only occurs if the exception condition is met. All methods have a version where the first parameter is 36 * returned. Thereby, the Throw can be used as part of a <b>super</b>(...) call in a constructor. 37 * <p> 38 * Copyright (c) 2016-2019 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See 39 * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is 40 * distributed under a three-clause BSD-style license, which can be found at 41 * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>. 42 * </p> 43 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a> 44 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a> 45 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a> 46 */ 47 public final class Throw 48 { 49 /** private constructor for utility class. */ 50 private Throw() 51 { 52 // utility class 53 } 54 55 /** 56 * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition 57 * checking. Use as follows: <br> 58 * 59 * <pre> 60 * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class, "Value may not be NaN."); 61 * </pre> 62 * 63 * @param condition the condition to check; an exception will be thrown if this is <b>true</b> 64 * @param throwableClass Class<T>; the Throwable type to throw 65 * @param message String; the message to use in the exception 66 * @throws T the throwable to throw on true condition 67 * @param <T> the Throwable type 68 */ 69 public static <T extends Throwable> void when(final boolean condition, final Class<T> throwableClass, 70 final String message) throws T 71 { 72 if (condition) 73 { 74 throwMessage(throwableClass, message, new ArrayList<>()); 75 } 76 } 77 78 /** 79 * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition 80 * checking. Use as follows: <br> 81 * 82 * <pre> 83 * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class, "Value may not be NaN for object %s.", 84 * object); 85 * </pre> 86 * 87 * @param condition the condition to check; an exception will be thrown if this is <b>true</b> 88 * @param throwableClass Class<T>; the Throwable type to throw 89 * @param message String; the message to use in the exception, with formatting identifiers 90 * @param arg Object; value to use for the formatting identifiers 91 * @throws T the throwable to throw on true condition 92 * @param <T> the Throwable type 93 */ 94 public static <T extends Throwable> void when(final boolean condition, final Class<T> throwableClass, 95 final String message, final Object arg) throws T 96 { 97 if (condition) 98 { 99 List<Object> argList = new ArrayList<>(); 100 argList.add(arg); 101 throwMessage(throwableClass, message, argList); 102 } 103 } 104 105 /** 106 * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition 107 * checking. Use as follows: <br> 108 * 109 * <pre> 110 * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class, 111 * "Value may not be NaN for object %s with name %s.", object, name); 112 * </pre> 113 * 114 * @param condition the condition to check; an exception will be thrown if this is <b>true</b> 115 * @param throwableClass Class<T>; the Throwable type to throw 116 * @param message String; the message to use in the exception, with formatting identifiers 117 * @param arg1 Object; 1st value to use for the formatting identifiers 118 * @param arg2 Object; 2nd value to use for the formatting identifiers 119 * @throws T the throwable to throw on true condition 120 * @param <T> the Throwable type 121 */ 122 public static <T extends Throwable> void when(final boolean condition, final Class<T> throwableClass, 123 final String message, final Object arg1, final Object arg2) throws T 124 { 125 if (condition) 126 { 127 List<Object> argList = new ArrayList<>(); 128 argList.add(arg1); 129 argList.add(arg2); 130 throwMessage(throwableClass, message, argList); 131 } 132 } 133 134 /** 135 * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition 136 * checking. Use as follows: <br> 137 * 138 * <pre> 139 * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class, 140 * "Value may not be NaN for object %s with name %s and id %s.", object, name, id); 141 * </pre> 142 * 143 * @param condition the condition to check; an exception will be thrown if this is <b>true</b> 144 * @param throwableClass Class<T>; the Throwable type to throw 145 * @param message String; the message to use in the exception, with formatting identifiers 146 * @param arg1 Object; 1st value to use for the formatting identifiers 147 * @param arg2 Object; 2nd value to use for the formatting identifiers 148 * @param arg3 Object; 3rd value to use for the formatting identifiers 149 * @throws T the throwable to throw on true condition 150 * @param <T> the Throwable type 151 */ 152 public static <T extends Throwable> void when(final boolean condition, final Class<T> throwableClass, 153 final String message, final Object arg1, final Object arg2, final Object arg3) throws T 154 { 155 if (condition) 156 { 157 List<Object> argList = new ArrayList<>(); 158 argList.add(arg1); 159 argList.add(arg2); 160 argList.add(arg3); 161 throwMessage(throwableClass, message, argList); 162 } 163 } 164 165 /** 166 * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition 167 * checking. Use as follows: <br> 168 * 169 * <pre> 170 * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class, 171 * "Value may not be NaN for object %s with name %s, id %s and parent %s.", object, name, id, parent); 172 * </pre> 173 * 174 * @param condition the condition to check; an exception will be thrown if this is <b>true</b> 175 * @param throwableClass Class<T>; the Throwable type to throw 176 * @param message String; the message to use in the exception, with formatting identifiers 177 * @param arg1 Object; 1st value to use for the formatting identifiers 178 * @param arg2 Object; 2nd value to use for the formatting identifiers 179 * @param arg3 Object; 3rd value to use for the formatting identifiers 180 * @param args Object...; potential 4th and further values to use for the formatting identifiers 181 * @throws T the throwable to throw on true condition 182 * @param <T> the Throwable type 183 */ 184 public static <T extends Throwable> void when(final boolean condition, final Class<T> throwableClass, 185 final String message, final Object arg1, final Object arg2, final Object arg3, final Object... args) 186 throws T 187 { 188 if (condition) 189 { 190 List<Object> argList = new ArrayList<>(); 191 argList.add(arg1); 192 argList.add(arg2); 193 argList.add(arg3); 194 argList.addAll(Arrays.asList(args)); 195 throwMessage(throwableClass, message, argList); 196 } 197 } 198 199 /** 200 * Private method to handle the throwing an Exception, Throwable or Error. 201 * @param throwableClass Class<T>; the Throwable type to throw 202 * @param message String; the message to use in the exception, with potential formatting identifiers 203 * @param argList List<Object>; List with potential values to use for the formatting identifiers 204 * @throws T the throwable to throw 205 * @param <T> the Throwable type 206 */ 207 private static <T extends Throwable> void throwMessage(final Class<T> throwableClass, final String message, 208 final List<Object> argList) throws T 209 { 210 // create a clear message 211 List<StackTraceElement> steList = new ArrayList<>(Arrays.asList(new Throwable().getStackTrace())); 212 steList.remove(0); // remove the throwMessage(...) call 213 steList.remove(0); // remove the when(...) call 214 StackTraceElement[] ste = steList.toArray(new StackTraceElement[steList.size()]); 215 String where = ste[0].getClassName() + "." + ste[0].getMethodName() + " (" + ste[0].getLineNumber() + "): "; 216 Object[] args = argList.toArray(); 217 String formattedMessage; 218 try 219 { 220 formattedMessage = where + String.format(message, args); 221 } 222 catch (IllegalFormatException exception) 223 { 224 formattedMessage = where + message + " [FormatException; args=" + argList + "]"; 225 } 226 227 // throw all other exceptions through reflection 228 T exception; 229 try 230 { 231 @SuppressWarnings("unchecked") 232 Constructor<T> constructor = 233 (Constructor<T>) ClassUtil.resolveConstructor(throwableClass, new Class<?>[]{String.class}); 234 exception = constructor.newInstance(formattedMessage); 235 exception.setStackTrace(ste); 236 } 237 catch (Throwable t) 238 { 239 RuntimeException rte = new RuntimeException(t.getMessage(), new Exception(formattedMessage)); 240 rte.setStackTrace(ste); 241 throw rte; 242 } 243 throw exception; 244 } 245 246 /** 247 * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition 248 * checking. This version of the method returns its first parameter, so it can be used inside a constructor. Use 249 * e.g., as follows: 250 * 251 * <pre> 252 * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class, "Value may not be NaN.")); 253 * </pre> 254 * 255 * @param object O; the object to return by this static method 256 * @param condition the condition to check; an exception will be thrown if this is <b>true</b> 257 * @param throwableClass Class<T>; the Throwable type to throw 258 * @param message String; the message to use in the exception 259 * @throws T the throwable to throw on true condition 260 * @param <T> the Throwable type 261 * @param <O> the Object type to return 262 * @return the object that was passed as the first parameter 263 */ 264 public static <T extends Throwable, O extends Object> O when(final O object, final boolean condition, 265 final Class<T> throwableClass, final String message) throws T 266 { 267 if (condition) 268 { 269 throwMessage(throwableClass, message, new ArrayList<>()); 270 } 271 return object; 272 } 273 274 /** 275 * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition 276 * checking. This version of the method returns its first parameter, so it can be used inside a constructor. Use 277 * e.g., as follows: 278 * 279 * <pre> 280 * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class, 281 * "Value may not be NaN for object %s.", object)); 282 * </pre> 283 * 284 * @param object O; the object to return by this static method 285 * @param condition the condition to check; an exception will be thrown if this is <b>true</b> 286 * @param throwableClass Class<T>; the Throwable type to throw 287 * @param message String; the message to use in the exception, with formatting identifiers 288 * @param arg Object; value to use for the formatting identifiers 289 * @throws T the throwable to throw on true condition 290 * @param <T> the Throwable type 291 * @param <O> the Object type to return 292 * @return the object that was passed as the first parameter 293 */ 294 public static <T extends Throwable, O extends Object> O when(final O object, final boolean condition, 295 final Class<T> throwableClass, final String message, final Object arg) throws T 296 { 297 if (condition) 298 { 299 List<Object> argList = new ArrayList<>(); 300 argList.add(arg); 301 throwMessage(throwableClass, message, argList); 302 } 303 return object; 304 } 305 306 /** 307 * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition 308 * checking. This version of the method returns its first parameter, so it can be used inside a constructor. Use 309 * e.g., as follows: 310 * 311 * <pre> 312 * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class, 313 * "Value may not be NaN for object %s with name %s.", object, name)); 314 * </pre> 315 * 316 * @param object O; the object to return by this static method 317 * @param condition the condition to check; an exception will be thrown if this is <b>true</b> 318 * @param throwableClass Class<T>; the Throwable type to throw 319 * @param message String; the message to use in the exception, with formatting identifiers 320 * @param arg1 Object; 1st value to use for the formatting identifiers 321 * @param arg2 Object; 2nd value to use for the formatting identifiers 322 * @throws T the throwable to throw on true condition 323 * @param <T> the Throwable type 324 * @param <O> the Object type to return 325 * @return the object that was passed as the first parameter 326 */ 327 public static <T extends Throwable, O extends Object> O when(final O object, final boolean condition, 328 final Class<T> throwableClass, final String message, final Object arg1, final Object arg2) throws T 329 { 330 if (condition) 331 { 332 List<Object> argList = new ArrayList<>(); 333 argList.add(arg1); 334 argList.add(arg2); 335 throwMessage(throwableClass, message, argList); 336 } 337 return object; 338 } 339 340 /** 341 * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition 342 * checking. This version of the method returns its first parameter, so it can be used inside a constructor. Use 343 * e.g., as follows: 344 * 345 * <pre> 346 * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class, 347 * "Value may not be NaN for object %s with name %s and id %s.", object, name, id)); 348 * </pre> 349 * 350 * @param object O; the object to return by this static method 351 * @param condition the condition to check; an exception will be thrown if this is <b>true</b> 352 * @param throwableClass Class<T>; the Throwable type to throw 353 * @param message String; the message to use in the exception, with formatting identifiers 354 * @param arg1 Object; 1st value to use for the formatting identifiers 355 * @param arg2 Object; 2nd value to use for the formatting identifiers 356 * @param arg3 Object; 3rd value to use for the formatting identifiers 357 * @throws T the throwable to throw on true condition 358 * @param <T> the Throwable type 359 * @param <O> the Object type to return 360 * @return the object that was passed as the first parameter 361 */ 362 public static <T extends Throwable, O extends Object> O when(final O object, final boolean condition, 363 final Class<T> throwableClass, final String message, final Object arg1, final Object arg2, 364 final Object arg3) throws T 365 { 366 if (condition) 367 { 368 List<Object> argList = new ArrayList<>(); 369 argList.add(arg1); 370 argList.add(arg2); 371 argList.add(arg3); 372 throwMessage(throwableClass, message, argList); 373 } 374 return object; 375 } 376 377 /** 378 * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition 379 * checking. This version of the method returns its first parameter, so it can be used inside a constructor. Use 380 * e.g., as follows: 381 * 382 * <pre> 383 * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class, 384 * "Value may not be NaN for object %s with name %s, id %s and parent %s.", object, name, id, parent)); 385 * </pre> 386 * 387 * @param object O; the object to return by this static method 388 * @param condition the condition to check; an exception will be thrown if this is <b>true</b> 389 * @param throwableClass Class<T>; the Throwable type to throw 390 * @param message String; the message to use in the exception, with formatting identifiers 391 * @param arg1 Object; 1st value to use for the formatting identifiers 392 * @param arg2 Object; 2nd value to use for the formatting identifiers 393 * @param arg3 Object; 3rd value to use for the formatting identifiers 394 * @param args Object...; potential 4th and further values to use for the formatting identifiers 395 * @throws T the throwable to throw on true condition 396 * @param <T> the Throwable type 397 * @param <O> the Object type to return 398 * @return the object that was passed as the first parameter 399 */ 400 public static <T extends Throwable, O extends Object> O when(final O object, final boolean condition, 401 final Class<T> throwableClass, final String message, final Object arg1, final Object arg2, 402 final Object arg3, final Object... args) throws T 403 { 404 if (condition) 405 { 406 List<Object> argList = new ArrayList<>(); 407 argList.add(arg1); 408 argList.add(arg2); 409 argList.add(arg3); 410 argList.addAll(Arrays.asList(args)); 411 throwMessage(throwableClass, message, argList); 412 } 413 return object; 414 } 415 416 /** 417 * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows: <br> 418 * 419 * <pre> 420 * Throw.when(object.getValue(), "Value may not be null."); 421 * </pre> 422 * 423 * @param object object to check; an exception will be thrown if this is <b>null</b> 424 * @param message String; the message to use in the exception 425 * @param <O> the Object type to return 426 * @return the object that was passed as the first parameter 427 * @throws NullPointerException if object is null 428 */ 429 public static <O extends Object> O whenNull(final O object, final String message) throws NullPointerException 430 { 431 if (object == null) 432 { 433 throwMessage(NullPointerException.class, message, new ArrayList<>()); 434 } 435 return object; 436 } 437 438 /** 439 * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows: <br> 440 * 441 * <pre> 442 * Throw.whenNull(object.getValue(), "Value may not be null for object %s.", object); 443 * </pre> 444 * 445 * @param object object to check; an exception will be thrown if this is <b>null</b> 446 * @param message String; the message to use in the exception, with formatting identifiers 447 * @param arg Object; value to use for the formatting identifiers 448 * @param <O> the Object type to return 449 * @return the object that was passed as the first parameter 450 * @throws NullPointerException if object is null 451 */ 452 public static <O extends Object> O whenNull(final O object, final String message, final Object arg) 453 throws NullPointerException 454 { 455 if (object == null) 456 { 457 List<Object> argList = new ArrayList<>(); 458 argList.add(arg); 459 throwMessage(NullPointerException.class, message, argList); 460 } 461 return object; 462 } 463 464 /** 465 * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows: <br> 466 * 467 * <pre> 468 * Throw.whenNull(object.getValue(), "Value may not be null for object %s with name %s.", object, name); 469 * </pre> 470 * 471 * @param object object to check; an exception will be thrown if this is <b>null</b> 472 * @param message String; the message to use in the exception, with formatting identifiers 473 * @param arg1 Object; 1st value to use for the formatting identifiers 474 * @param arg2 Object; 2nd value to use for the formatting identifiers 475 * @param <O> the Object type to return 476 * @return the object that was passed as the first parameter 477 * @throws NullPointerException if object is null 478 */ 479 public static <O extends Object> O whenNull(final O object, final String message, final Object arg1, 480 final Object arg2) throws NullPointerException 481 { 482 if (object == null) 483 { 484 List<Object> argList = new ArrayList<>(); 485 argList.add(arg1); 486 argList.add(arg2); 487 throwMessage(NullPointerException.class, message, argList); 488 } 489 return object; 490 } 491 492 /** 493 * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows: <br> 494 * 495 * <pre> 496 * Throw.whenNull(object.getValue(), "Value may not be null for object %s with name %s and id %s.", object, name, id); 497 * </pre> 498 * 499 * @param object object to check; an exception will be thrown if this is <b>null</b> 500 * @param message String; the message to use in the exception, with formatting identifiers 501 * @param arg1 Object; 1st value to use for the formatting identifiers 502 * @param arg2 Object; 2nd value to use for the formatting identifiers 503 * @param arg3 Object; 3rd value to use for the formatting identifiers 504 * @param <O> the Object type to return 505 * @return the object that was passed as the first parameter 506 * @throws NullPointerException if object is null 507 */ 508 public static <O extends Object> O whenNull(final O object, final String message, final Object arg1, 509 final Object arg2, final Object arg3) throws NullPointerException 510 { 511 if (object == null) 512 { 513 List<Object> argList = new ArrayList<>(); 514 argList.add(arg1); 515 argList.add(arg2); 516 argList.add(arg3); 517 throwMessage(NullPointerException.class, message, argList); 518 } 519 return object; 520 } 521 522 /** 523 * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows: <br> 524 * 525 * <pre> 526 * Throw.whenNull(object.getValue(), "Value may not be null for object %s with name %s, id %s and parent %s.", object, 527 * name, id, parent); 528 * </pre> 529 * 530 * @param object object to check; an exception will be thrown if this is <b>null</b> 531 * @param message String; the message to use in the exception, with formatting identifiers 532 * @param arg1 Object; 1st value to use for the formatting identifiers 533 * @param arg2 Object; 2nd value to use for the formatting identifiers 534 * @param arg3 Object; 3rd value to use for the formatting identifiers 535 * @param args Object...; potential 4th and further values to use for the formatting identifiers 536 * @param <O> the Object type to return 537 * @return the object that was passed as the first parameter 538 * @throws NullPointerException if object is null 539 */ 540 public static <O extends Object> O whenNull(final O object, final String message, final Object arg1, 541 final Object arg2, final Object arg3, final Object... args) throws NullPointerException 542 { 543 if (object == null) 544 { 545 List<Object> argList = new ArrayList<>(); 546 argList.add(arg1); 547 argList.add(arg2); 548 argList.add(arg3); 549 argList.addAll(Arrays.asList(args)); 550 throwMessage(NullPointerException.class, message, argList); 551 } 552 return object; 553 } 554 555 }