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-2022 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 @SuppressWarnings("checkstyle:linelength") 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 checking. Use as 57 * 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 boolean; 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, final String message) 70 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 checking. Use as 80 * follows: <br> 81 * 82 * <pre> 83 * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class, "Value may not be NaN for object %s.", object); 84 * </pre> 85 * 86 * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b> 87 * @param throwableClass Class<T>; the Throwable type to throw 88 * @param message String; the message to use in the exception, with formatting identifiers 89 * @param arg Object; value to use for the formatting identifiers 90 * @throws T the throwable to throw on true condition 91 * @param <T> the Throwable type 92 */ 93 public static <T extends Throwable> void when(final boolean condition, final Class<T> throwableClass, final String message, 94 final Object arg) throws T 95 { 96 if (condition) 97 { 98 List<Object> argList = new ArrayList<>(); 99 argList.add(arg); 100 throwMessage(throwableClass, message, argList); 101 } 102 } 103 104 /** 105 * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. Use as 106 * follows: <br> 107 * 108 * <pre> 109 * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class, 110 * "Value may not be NaN for object %s with name %s.", object, name); 111 * </pre> 112 * 113 * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b> 114 * @param throwableClass Class<T>; the Throwable type to throw 115 * @param message String; the message to use in the exception, with formatting identifiers 116 * @param arg1 Object; 1st value to use for the formatting identifiers 117 * @param arg2 Object; 2nd value to use for the formatting identifiers 118 * @throws T the throwable to throw on true condition 119 * @param <T> the Throwable type 120 */ 121 public static <T extends Throwable> void when(final boolean condition, final Class<T> throwableClass, final String message, 122 final Object arg1, final Object arg2) throws T 123 { 124 if (condition) 125 { 126 List<Object> argList = new ArrayList<>(); 127 argList.add(arg1); 128 argList.add(arg2); 129 throwMessage(throwableClass, message, argList); 130 } 131 } 132 133 /** 134 * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. Use as 135 * follows: <br> 136 * 137 * <pre> 138 * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class, 139 * "Value may not be NaN for object %s with name %s and id %s.", object, name, id); 140 * </pre> 141 * 142 * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b> 143 * @param throwableClass Class<T>; the Throwable type to throw 144 * @param message String; the message to use in the exception, with formatting identifiers 145 * @param arg1 Object; 1st value to use for the formatting identifiers 146 * @param arg2 Object; 2nd value to use for the formatting identifiers 147 * @param arg3 Object; 3rd value to use for the formatting identifiers 148 * @throws T the throwable to throw on true condition 149 * @param <T> the Throwable type 150 */ 151 public static <T extends Throwable> void when(final boolean condition, final Class<T> throwableClass, final String message, 152 final Object arg1, final Object arg2, final Object arg3) throws T 153 { 154 if (condition) 155 { 156 List<Object> argList = new ArrayList<>(); 157 argList.add(arg1); 158 argList.add(arg2); 159 argList.add(arg3); 160 throwMessage(throwableClass, message, argList); 161 } 162 } 163 164 /** 165 * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition checking. Use as 166 * follows: <br> 167 * 168 * <pre> 169 * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class, 170 * "Value may not be NaN for object %s with name %s, id %s and parent %s.", object, name, id, parent); 171 * </pre> 172 * 173 * @param condition boolean; the condition to check; an exception will be thrown if this is <b>true</b> 174 * @param throwableClass Class<T>; the Throwable type to throw 175 * @param message String; the message to use in the exception, with formatting identifiers 176 * @param arg1 Object; 1st value to use for the formatting identifiers 177 * @param arg2 Object; 2nd value to use for the formatting identifiers 178 * @param arg3 Object; 3rd value to use for the formatting identifiers 179 * @param args Object...; potential 4th and further values to use for the formatting identifiers 180 * @throws T the throwable to throw on true condition 181 * @param <T> the Throwable type 182 */ 183 public static <T extends Throwable> void when(final boolean condition, final Class<T> throwableClass, final String message, 184 final Object arg1, final Object arg2, final Object arg3, final Object... args) throws T 185 { 186 if (condition) 187 { 188 List<Object> argList = new ArrayList<>(); 189 argList.add(arg1); 190 argList.add(arg2); 191 argList.add(arg3); 192 argList.addAll(Arrays.asList(args)); 193 throwMessage(throwableClass, message, argList); 194 } 195 } 196 197 /** 198 * Private method to handle the throwing an Exception, Throwable or Error. 199 * @param throwableClass Class<T>; the Throwable type to throw 200 * @param message String; the message to use in the exception, with potential formatting identifiers 201 * @param argList List<Object>; List with potential values to use for the formatting identifiers 202 * @throws T the throwable to throw 203 * @param <T> the Throwable type 204 */ 205 private static <T extends Throwable> void throwMessage(final Class<T> throwableClass, final String message, 206 final List<Object> argList) throws T 207 { 208 // create a clear message 209 List<StackTraceElement> steList = new ArrayList<>(Arrays.asList(new Throwable().getStackTrace())); 210 steList.remove(0); // remove the throwMessage(...) call 211 steList.remove(0); // remove the when(...) call 212 StackTraceElement[] ste = steList.toArray(new StackTraceElement[steList.size()]); 213 String where = ste[0].getClassName() + "." + ste[0].getMethodName() + " (" + ste[0].getLineNumber() + "): "; 214 Object[] args = argList.toArray(); 215 String formattedMessage; 216 try 217 { 218 formattedMessage = where + String.format(message, args); 219 } 220 catch (IllegalFormatException exception) 221 { 222 formattedMessage = where + message + " [FormatException; args=" + argList + "]"; 223 } 224 225 // throw all other exceptions through reflection 226 T exception; 227 try 228 { 229 Constructor<T> constructor = 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 @SuppressWarnings("checkstyle:parameternumber") 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 /** 548 * Throw an unchecked exception for a method with a fixed signature (e.g., extending a method from a library that cannot be 549 * changed), without having to declare the exception, which can be impossible when extending a method. The typical use is: 550 * 551 * <pre> 552 * @Override 553 * public void someMethod() { 554 * try { 555 * // some code that throws e.g., an IOException 556 * } catch IOException e { 557 * Throw.throwUnchecked(e); 558 * } 559 * } 560 * </pre> 561 * 562 * From: <a href="http://blog.ragozin.info/2011/10/java-how-to-throw-undeclared-checked.html" target="_blank"> 563 * http://blog.ragozin.info/2011/10/java-how-to-throw-undeclared-checked.html</a> as mentioned in <a href= 564 * "https://stackoverflow.com/questions/11942946/how-to-throw-an-exception-when-your-method-signature-doesnt-allow-to-throw-exce" 565 * target="_blank"> 566 * https://stackoverflow.com/questions/11942946/how-to-throw-an-exception-when-your-method-signature-doesnt-allow-to-throw-exce</a>. 567 * @param e Throwavble; the exception of Throwable to throw in an unchecked manner 568 */ 569 public static void throwUnchecked(final Throwable e) 570 { 571 Throw.<RuntimeException>throwAny(e); 572 } 573 574 /** 575 * @param <E> The exception class 576 * @param e Throwable; The throwable 577 * @throws E The exception to throw 578 */ 579 @SuppressWarnings("unchecked") 580 private static <E extends Throwable> void throwAny(final Throwable e) throws E 581 { 582 throw (E) e; 583 } 584 }