package nl.tudelft.simulation.language; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; import java.util.IllegalFormatException; import java.util.List; import nl.tudelft.simulation.language.reflection.ClassUtil; /** * The Throw class has a number of static methods that make it easy to throw an exception under conditions for any * Exception class, including the standard Java exceptions and exceptions from libraries that are used in the project. * Instead of: * *
 * if (car == null)
 * {
 *     throw new NullPointerException("Car may not be null.");
 * }
 * if (Double.isNaN(car.getPosition()))
 * {
 *     throw new IllegalArgumentException("Position of car " + car + " is NaN.");
 * }
 * 
* * we can write: * *
 * Throw.whenNull(car, "Car may not be null.");
 * Throw.when(Double.isNaN(car.getPosition()), IllegalArgumentException.class, "Position of car %s is NaN.", car);
 * 
* * The exception message can be formatted with additional arguments, such that the overhead of building the exception * message only occurs if the exception condition is met. All methods have a version where the first parameter is * returned. Thereby, the Throw can be used as part of a super(...) call in a constructor. *

* Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights * reserved.
* BSD-style license. See DSOL License. *

* $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck * $, initial version Apr 22, 2016
* @author Alexander Verbraeck * @author Peter Knoppers * @author Wouter Schakel */ public final class Throw { /** private constructor for utility class. */ private Throw() { // utility class } /** * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition * checking. Use as follows:
* *
     * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class, "Value may not be NaN.");
     * 
* * @param condition the condition to check; an exception will be thrown if this is true * @param throwableClass the Throwable type to throw * @param message the message to use in the exception * @throws T the throwable to throw on true condition * @param the Throwable type */ public static void when(final boolean condition, final Class throwableClass, final String message) throws T { if (condition) { throwMessage(throwableClass, message, new ArrayList<>()); } } /** * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition * checking. Use as follows:
* *
     * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class, "Value may not be NaN for object %s.",
     *         object);
     * 
* * @param condition the condition to check; an exception will be thrown if this is true * @param throwableClass the Throwable type to throw * @param message the message to use in the exception, with formatting identifiers * @param arg value to use for the formatting identifiers * @throws T the throwable to throw on true condition * @param the Throwable type */ public static void when(final boolean condition, final Class throwableClass, final String message, final Object arg) throws T { if (condition) { List argList = new ArrayList<>(); argList.add(arg); throwMessage(throwableClass, message, argList); } } /** * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition * checking. Use as follows:
* *
     * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class,
     *         "Value may not be NaN for object %s with name %s.", object, name);
     * 
* * @param condition the condition to check; an exception will be thrown if this is true * @param throwableClass the Throwable type to throw * @param message the message to use in the exception, with formatting identifiers * @param arg1 1st value to use for the formatting identifiers * @param arg2 2nd value to use for the formatting identifiers * @throws T the throwable to throw on true condition * @param the Throwable type */ public static void when(final boolean condition, final Class throwableClass, final String message, final Object arg1, final Object arg2) throws T { if (condition) { List argList = new ArrayList<>(); argList.add(arg1); argList.add(arg2); throwMessage(throwableClass, message, argList); } } /** * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition * checking. Use as follows:
* *
     * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class,
     *         "Value may not be NaN for object %s with name %s and id %s.", object, name, id);
     * 
* * @param condition the condition to check; an exception will be thrown if this is true * @param throwableClass the Throwable type to throw * @param message the message to use in the exception, with formatting identifiers * @param arg1 1st value to use for the formatting identifiers * @param arg2 2nd value to use for the formatting identifiers * @param arg3 3rd value to use for the formatting identifiers * @throws T the throwable to throw on true condition * @param the Throwable type */ public static void when(final boolean condition, final Class throwableClass, final String message, final Object arg1, final Object arg2, final Object arg3) throws T { if (condition) { List argList = new ArrayList<>(); argList.add(arg1); argList.add(arg2); argList.add(arg3); throwMessage(throwableClass, message, argList); } } /** * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition * checking. Use as follows:
* *
     * Throw.when(Double.isNan(object.getValue()), IllegalArgumentException.class,
     *         "Value may not be NaN for object %s with name %s, id %s and parent %s.", object, name, id, parent);
     * 
* * @param condition the condition to check; an exception will be thrown if this is true * @param throwableClass the Throwable type to throw * @param message the message to use in the exception, with formatting identifiers * @param arg1 1st value to use for the formatting identifiers * @param arg2 2nd value to use for the formatting identifiers * @param arg3 3rd value to use for the formatting identifiers * @param args potential 4th and further values to use for the formatting identifiers * @throws T the throwable to throw on true condition * @param the Throwable type */ public static void when(final boolean condition, final Class throwableClass, final String message, final Object arg1, final Object arg2, final Object arg3, final Object... args) throws T { if (condition) { List argList = new ArrayList<>(); argList.add(arg1); argList.add(arg2); argList.add(arg3); argList.addAll(Arrays.asList(args)); throwMessage(throwableClass, message, argList); } } /** * Private method to handle the throwing an Exception, Throwable or Error. * @param throwableClass the Throwable type to throw * @param message the message to use in the exception, with potential formatting identifiers * @param argList List with potential values to use for the formatting identifiers * @throws T the throwable to throw * @param the Throwable type */ private static void throwMessage(final Class throwableClass, final String message, final List argList) throws T { // create a clear message List steList = new ArrayList<>(Arrays.asList(new Throwable().getStackTrace())); steList.remove(0); // remove the throwMessage(...) call steList.remove(0); // remove the when(...) call StackTraceElement[] ste = steList.toArray(new StackTraceElement[steList.size()]); String where = ste[0].getClassName() + "." + ste[0].getMethodName() + " (" + ste[0].getLineNumber() + "): "; Object[] args = argList.toArray(); String formattedMessage; try { formattedMessage = where + String.format(message, args); } catch (IllegalFormatException exception) { formattedMessage = where + message + " [FormatException; args=" + argList + "]"; } // throw all other exceptions through reflection T exception; try { @SuppressWarnings("unchecked") Constructor constructor = (Constructor) ClassUtil.resolveConstructor(throwableClass, new Class[]{String.class}); exception = constructor.newInstance(formattedMessage); exception.setStackTrace(ste); } catch (Throwable t) { RuntimeException rte = new RuntimeException(t.getMessage(), new Exception(formattedMessage)); rte.setStackTrace(ste); throw rte; } throw exception; } /** * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition * checking. This version of the method returns its first parameter, so it can be used inside a constructor. Use * e.g., as follows: * *
     * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class, "Value may not be NaN."));
     * 
* * @param object the object to return by this static method * @param condition the condition to check; an exception will be thrown if this is true * @param throwableClass the Throwable type to throw * @param message the message to use in the exception * @throws T the throwable to throw on true condition * @param the Throwable type * @param the Object type to return * @return the object that was passed as the first parameter */ public static O when(final O object, final boolean condition, final Class throwableClass, final String message) throws T { if (condition) { throwMessage(throwableClass, message, new ArrayList<>()); } return object; } /** * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition * checking. This version of the method returns its first parameter, so it can be used inside a constructor. Use * e.g., as follows: * *
     * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class,
     *         "Value may not be NaN for object %s.", object));
     * 
* * @param object the object to return by this static method * @param condition the condition to check; an exception will be thrown if this is true * @param throwableClass the Throwable type to throw * @param message the message to use in the exception, with formatting identifiers * @param arg value to use for the formatting identifiers * @throws T the throwable to throw on true condition * @param the Throwable type * @param the Object type to return * @return the object that was passed as the first parameter */ public static O when(final O object, final boolean condition, final Class throwableClass, final String message, final Object arg) throws T { if (condition) { List argList = new ArrayList<>(); argList.add(arg); throwMessage(throwableClass, message, argList); } return object; } /** * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition * checking. This version of the method returns its first parameter, so it can be used inside a constructor. Use * e.g., as follows: * *
     * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class,
     *         "Value may not be NaN for object %s with name %s.", object, name));
     * 
* * @param object the object to return by this static method * @param condition the condition to check; an exception will be thrown if this is true * @param throwableClass the Throwable type to throw * @param message the message to use in the exception, with formatting identifiers * @param arg1 1st value to use for the formatting identifiers * @param arg2 2nd value to use for the formatting identifiers * @throws T the throwable to throw on true condition * @param the Throwable type * @param the Object type to return * @return the object that was passed as the first parameter */ public static O when(final O object, final boolean condition, final Class throwableClass, final String message, final Object arg1, final Object arg2) throws T { if (condition) { List argList = new ArrayList<>(); argList.add(arg1); argList.add(arg2); throwMessage(throwableClass, message, argList); } return object; } /** * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition * checking. This version of the method returns its first parameter, so it can be used inside a constructor. Use * e.g., as follows: * *
     * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class,
     *         "Value may not be NaN for object %s with name %s and id %s.", object, name, id));
     * 
* * @param object the object to return by this static method * @param condition the condition to check; an exception will be thrown if this is true * @param throwableClass the Throwable type to throw * @param message the message to use in the exception, with formatting identifiers * @param arg1 1st value to use for the formatting identifiers * @param arg2 2nd value to use for the formatting identifiers * @param arg3 3rd value to use for the formatting identifiers * @throws T the throwable to throw on true condition * @param the Throwable type * @param the Object type to return * @return the object that was passed as the first parameter */ public static O when(final O object, final boolean condition, final Class throwableClass, final String message, final Object arg1, final Object arg2, final Object arg3) throws T { if (condition) { List argList = new ArrayList<>(); argList.add(arg1); argList.add(arg2); argList.add(arg3); throwMessage(throwableClass, message, argList); } return object; } /** * Throw a Throwable (such as an Exception or Error) if a condition is met, e.g. for pre- and postcondition * checking. This version of the method returns its first parameter, so it can be used inside a constructor. Use * e.g., as follows: * *
     * super(Throw.when(object, Double.isNan(object.getValue()), IllegalArgumentException.class,
     *         "Value may not be NaN for object %s with name %s, id %s and parent %s.", object, name, id, parent));
     * 
* * @param object the object to return by this static method * @param condition the condition to check; an exception will be thrown if this is true * @param throwableClass the Throwable type to throw * @param message the message to use in the exception, with formatting identifiers * @param arg1 1st value to use for the formatting identifiers * @param arg2 2nd value to use for the formatting identifiers * @param arg3 3rd value to use for the formatting identifiers * @param args potential 4th and further values to use for the formatting identifiers * @throws T the throwable to throw on true condition * @param the Throwable type * @param the Object type to return * @return the object that was passed as the first parameter */ public static O when(final O object, final boolean condition, final Class throwableClass, final String message, final Object arg1, final Object arg2, final Object arg3, final Object... args) throws T { if (condition) { List argList = new ArrayList<>(); argList.add(arg1); argList.add(arg2); argList.add(arg3); argList.addAll(Arrays.asList(args)); throwMessage(throwableClass, message, argList); } return object; } /** * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows:
* *
     * Throw.when(object.getValue(), "Value may not be null.");
     * 
* * @param object object to check; an exception will be thrown if this is null * @param message the message to use in the exception * @param the Object type to return * @return the object that was passed as the first parameter * @throws NullPointerException if object is null */ public static O whenNull(final O object, final String message) throws NullPointerException { if (object == null) { throwMessage(NullPointerException.class, message, new ArrayList<>()); } return object; } /** * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows:
* *
     * Throw.whenNull(object.getValue(), "Value may not be null for object %s.", object);
     * 
* * @param object object to check; an exception will be thrown if this is null * @param message the message to use in the exception, with formatting identifiers * @param arg value to use for the formatting identifiers * @param the Object type to return * @return the object that was passed as the first parameter * @throws NullPointerException if object is null */ public static O whenNull(final O object, final String message, final Object arg) throws NullPointerException { if (object == null) { List argList = new ArrayList<>(); argList.add(arg); throwMessage(NullPointerException.class, message, argList); } return object; } /** * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows:
* *
     * Throw.whenNull(object.getValue(), "Value may not be null for object %s with name %s.", object, name);
     * 
* * @param object object to check; an exception will be thrown if this is null * @param message the message to use in the exception, with formatting identifiers * @param arg1 1st value to use for the formatting identifiers * @param arg2 2nd value to use for the formatting identifiers * @param the Object type to return * @return the object that was passed as the first parameter * @throws NullPointerException if object is null */ public static O whenNull(final O object, final String message, final Object arg1, final Object arg2) throws NullPointerException { if (object == null) { List argList = new ArrayList<>(); argList.add(arg1); argList.add(arg2); throwMessage(NullPointerException.class, message, argList); } return object; } /** * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows:
* *
     * Throw.whenNull(object.getValue(), "Value may not be null for object %s with name %s and id %s.", object, name, id);
     * 
* * @param object object to check; an exception will be thrown if this is null * @param message the message to use in the exception, with formatting identifiers * @param arg1 1st value to use for the formatting identifiers * @param arg2 2nd value to use for the formatting identifiers * @param arg3 3rd value to use for the formatting identifiers * @param the Object type to return * @return the object that was passed as the first parameter * @throws NullPointerException if object is null */ public static O whenNull(final O object, final String message, final Object arg1, final Object arg2, final Object arg3) throws NullPointerException { if (object == null) { List argList = new ArrayList<>(); argList.add(arg1); argList.add(arg2); argList.add(arg3); throwMessage(NullPointerException.class, message, argList); } return object; } /** * Throw a NullPointerException if object is null, e.g. for pre- and postcondition checking. Use as follows:
* *
     * Throw.whenNull(object.getValue(), "Value may not be null for object %s with name %s, id %s and parent %s.", object,
     *         name, id, parent);
     * 
* * @param object object to check; an exception will be thrown if this is null * @param message the message to use in the exception, with formatting identifiers * @param arg1 1st value to use for the formatting identifiers * @param arg2 2nd value to use for the formatting identifiers * @param arg3 3rd value to use for the formatting identifiers * @param args potential 4th and further values to use for the formatting identifiers * @param the Object type to return * @return the object that was passed as the first parameter * @throws NullPointerException if object is null */ public static O whenNull(final O object, final String message, final Object arg1, final Object arg2, final Object arg3, final Object... args) throws NullPointerException { if (object == null) { List argList = new ArrayList<>(); argList.add(arg1); argList.add(arg2); argList.add(arg3); argList.addAll(Arrays.asList(args)); throwMessage(NullPointerException.class, message, argList); } return object; } }