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