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