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;
}
}