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 }