Sort by method name.

This commit is contained in:
Gary Gregory 2019-05-05 17:23:43 -04:00
parent 1ec6c0ae9a
commit f698f9e64e
2 changed files with 783 additions and 783 deletions

View File

@ -66,31 +66,6 @@ public class ExceptionUtils {
"getThrowable",
};
/**
* <p>
* Public constructor allows an instance of <code>ExceptionUtils</code> to be created, although that is not
* normally necessary.
* </p>
*/
public ExceptionUtils() {
super();
}
//-----------------------------------------------------------------------
/**
* <p>Returns the default names used when searching for the cause of an exception.</p>
*
* <p>This may be modified and used in the overloaded getCause(Throwable, String[]) method.</p>
*
* @return cloned array of the default method names
* @since 3.0
* @deprecated This feature will be removed in Lang 4.0
*/
@Deprecated
public static String[] getDefaultCauseMethodNames() {
return ArrayUtils.clone(CAUSE_METHOD_NAMES);
}
//-----------------------------------------------------------------------
/**
* <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
@ -164,28 +139,6 @@ public class ExceptionUtils {
return null;
}
/**
* <p>Introspects the <code>Throwable</code> to obtain the root cause.</p>
*
* <p>This method walks through the exception chain to the last element,
* "root" of the tree, using {@link Throwable#getCause()}, and
* returns that exception.</p>
*
* <p>From version 2.2, this method handles recursive cause structures
* that might otherwise cause infinite loops. If the throwable parameter
* has a cause of itself, then null will be returned. If the throwable
* parameter cause chain loops, the last element in the chain before the
* loop is returned.</p>
*
* @param throwable the throwable to get the root cause for, may be null
* @return the root cause of the <code>Throwable</code>,
* <code>null</code> if null throwable input
*/
public static Throwable getRootCause(final Throwable throwable) {
final List<Throwable> list = getThrowableList(throwable);
return list.isEmpty() ? null : list.get(list.size() - 1);
}
/**
* <p>Finds a <code>Throwable</code> by method name.</p>
*
@ -212,6 +165,209 @@ public class ExceptionUtils {
return null;
}
//-----------------------------------------------------------------------
/**
* <p>Returns the default names used when searching for the cause of an exception.</p>
*
* <p>This may be modified and used in the overloaded getCause(Throwable, String[]) method.</p>
*
* @return cloned array of the default method names
* @since 3.0
* @deprecated This feature will be removed in Lang 4.0
*/
@Deprecated
public static String[] getDefaultCauseMethodNames() {
return ArrayUtils.clone(CAUSE_METHOD_NAMES);
}
//-----------------------------------------------------------------------
/**
* Gets a short message summarising the exception.
* <p>
* The message returned is of the form
* {ClassNameWithoutPackage}: {ThrowableMessage}
*
* @param th the throwable to get a message for, null returns empty string
* @return the message, non-null
* @since 2.2
*/
public static String getMessage(final Throwable th) {
if (th == null) {
return StringUtils.EMPTY;
}
final String clsName = ClassUtils.getShortClassName(th, null);
final String msg = th.getMessage();
return clsName + ": " + StringUtils.defaultString(msg);
}
/**
* <p>Introspects the <code>Throwable</code> to obtain the root cause.</p>
*
* <p>This method walks through the exception chain to the last element,
* "root" of the tree, using {@link Throwable#getCause()}, and
* returns that exception.</p>
*
* <p>From version 2.2, this method handles recursive cause structures
* that might otherwise cause infinite loops. If the throwable parameter
* has a cause of itself, then null will be returned. If the throwable
* parameter cause chain loops, the last element in the chain before the
* loop is returned.</p>
*
* @param throwable the throwable to get the root cause for, may be null
* @return the root cause of the <code>Throwable</code>,
* <code>null</code> if null throwable input
*/
public static Throwable getRootCause(final Throwable throwable) {
final List<Throwable> list = getThrowableList(throwable);
return list.isEmpty() ? null : list.get(list.size() - 1);
}
//-----------------------------------------------------------------------
/**
* Gets a short message summarising the root cause exception.
* <p>
* The message returned is of the form
* {ClassNameWithoutPackage}: {ThrowableMessage}
*
* @param th the throwable to get a message for, null returns empty string
* @return the message, non-null
* @since 2.2
*/
public static String getRootCauseMessage(final Throwable th) {
Throwable root = getRootCause(th);
root = root == null ? th : root;
return getMessage(root);
}
//-----------------------------------------------------------------------
/**
* <p>Creates a compact stack trace for the root cause of the supplied
* <code>Throwable</code>.</p>
*
* <p>The output of this method is consistent across JDK versions.
* It consists of the root exception followed by each of its wrapping
* exceptions separated by '[wrapped]'. Note that this is the opposite
* order to the JDK1.4 display.</p>
*
* @param throwable the throwable to examine, may be null
* @return an array of stack trace frames, never null
* @since 2.0
*/
public static String[] getRootCauseStackTrace(final Throwable throwable) {
if (throwable == null) {
return ArrayUtils.EMPTY_STRING_ARRAY;
}
final Throwable throwables[] = getThrowables(throwable);
final int count = throwables.length;
final List<String> frames = new ArrayList<>();
List<String> nextTrace = getStackFrameList(throwables[count - 1]);
for (int i = count; --i >= 0;) {
final List<String> trace = nextTrace;
if (i != 0) {
nextTrace = getStackFrameList(throwables[i - 1]);
removeCommonFrames(trace, nextTrace);
}
if (i == count - 1) {
frames.add(throwables[i].toString());
} else {
frames.add(WRAPPED_MARKER + throwables[i].toString());
}
frames.addAll(trace);
}
return frames.toArray(new String[frames.size()]);
}
/**
* <p>Produces a <code>List</code> of stack frames - the message
* is not included. Only the trace of the specified exception is
* returned, any caused by trace is stripped.</p>
*
* <p>This works in most cases - it will only fail if the exception
* message contains a line that starts with:
* <code>&quot;&nbsp;&nbsp;&nbsp;at&quot;.</code></p>
*
* @param t is any throwable
* @return List of stack frames
*/
static List<String> getStackFrameList(final Throwable t) {
final String stackTrace = getStackTrace(t);
final String linebreak = System.lineSeparator();
final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
final List<String> list = new ArrayList<>();
boolean traceStarted = false;
while (frames.hasMoreTokens()) {
final String token = frames.nextToken();
// Determine if the line starts with <whitespace>at
final int at = token.indexOf("at");
if (at != -1 && token.substring(0, at).trim().isEmpty()) {
traceStarted = true;
list.add(token);
} else if (traceStarted) {
break;
}
}
return list;
}
//-----------------------------------------------------------------------
/**
* <p>Returns an array where each element is a line from the argument.</p>
*
* <p>The end of line is determined by the value of {@link System#lineSeparator()}.</p>
*
* @param stackTrace a stack trace String
* @return an array where each element is a line from the argument
*/
static String[] getStackFrames(final String stackTrace) {
final String linebreak = System.lineSeparator();
final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
final List<String> list = new ArrayList<>();
while (frames.hasMoreTokens()) {
list.add(frames.nextToken());
}
return list.toArray(new String[list.size()]);
}
/**
* <p>Captures the stack trace associated with the specified
* <code>Throwable</code> object, decomposing it into a list of
* stack frames.</p>
*
* <p>The result of this method vary by JDK version as this method
* uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
* On JDK1.3 and earlier, the cause exception will not be shown
* unless the specified throwable alters printStackTrace.</p>
*
* @param throwable the <code>Throwable</code> to examine, may be null
* @return an array of strings describing each stack frame, never null
*/
public static String[] getStackFrames(final Throwable throwable) {
if (throwable == null) {
return ArrayUtils.EMPTY_STRING_ARRAY;
}
return getStackFrames(getStackTrace(throwable));
}
//-----------------------------------------------------------------------
/**
* <p>Gets the stack trace from a Throwable as a String.</p>
*
* <p>The result of this method vary by JDK version as this method
* uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
* On JDK1.3 and earlier, the cause exception will not be shown
* unless the specified throwable alters printStackTrace.</p>
*
* @param throwable the <code>Throwable</code> to be examined
* @return the stack trace as generated by the exception's
* <code>printStackTrace(PrintWriter)</code> method
*/
public static String getStackTrace(final Throwable throwable) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw, true);
throwable.printStackTrace(pw);
return sw.getBuffer().toString();
}
//-----------------------------------------------------------------------
/**
* <p>Counts the number of <code>Throwable</code> objects in the
@ -232,31 +388,7 @@ public class ExceptionUtils {
public static int getThrowableCount(final Throwable throwable) {
return getThrowableList(throwable).size();
}
/**
* <p>Returns the list of <code>Throwable</code> objects in the
* exception chain.</p>
*
* <p>A throwable without cause will return an array containing
* one element - the input throwable.
* A throwable with one cause will return an array containing
* two elements. - the input throwable and the cause throwable.
* A <code>null</code> throwable will return an array of size zero.</p>
*
* <p>From version 2.2, this method handles recursive cause structures
* that might otherwise cause infinite loops. The cause chain is
* processed until the end is reached, or until the next item in the
* chain is already in the result set.</p>
*
* @see #getThrowableList(Throwable)
* @param throwable the throwable to inspect, may be null
* @return the array of throwables, never null
*/
public static Throwable[] getThrowables(final Throwable throwable) {
final List<Throwable> list = getThrowableList(throwable);
return list.toArray(new Throwable[list.size()]);
}
/**
* <p>Returns the list of <code>Throwable</code> objects in the
* exception chain.</p>
@ -286,177 +418,48 @@ public class ExceptionUtils {
}
/**
* <p>Returns the (zero-based) index of the first <code>Throwable</code>
* that matches the specified class (exactly) in the exception chain.
* Subclasses of the specified class do not match - see
* {@link #indexOfType(Throwable, Class)} for the opposite.</p>
* <p>Returns the list of <code>Throwable</code> objects in the
* exception chain.</p>
*
* <p>A <code>null</code> throwable returns <code>-1</code>.
* A <code>null</code> type returns <code>-1</code>.
* No match in the chain returns <code>-1</code>.</p>
* <p>A throwable without cause will return an array containing
* one element - the input throwable.
* A throwable with one cause will return an array containing
* two elements. - the input throwable and the cause throwable.
* A <code>null</code> throwable will return an array of size zero.</p>
*
* <p>From version 2.2, this method handles recursive cause structures
* that might otherwise cause infinite loops. The cause chain is
* processed until the end is reached, or until the next item in the
* chain is already in the result set.</p>
*
* @see #getThrowableList(Throwable)
* @param throwable the throwable to inspect, may be null
* @param clazz the class to search for, subclasses do not match, null returns -1
* @return the index into the throwable chain, -1 if no match or null input
* @return the array of throwables, never null
*/
public static int indexOfThrowable(final Throwable throwable, final Class<?> clazz) {
return indexOf(throwable, clazz, 0, false);
public static Throwable[] getThrowables(final Throwable throwable) {
final List<Throwable> list = getThrowableList(throwable);
return list.toArray(new Throwable[list.size()]);
}
/**
* <p>Returns the first <code>Throwable</code>
* that matches the specified class (exactly) in the exception chain.
* Subclasses of the specified class do not match - see
* {@link #throwableOfType(Throwable, Class)} for the opposite.</p>
* Does the throwable's causal chain have an immediate or wrapped exception
* of the given type?
*
* <p>A <code>null</code> throwable returns <code>null</code>.
* A <code>null</code> type returns <code>null</code>.
* No match in the chain returns <code>null</code>.</p>
*
* @param <T> the type of Throwable you are searching.
* @param throwable the throwable to inspect, may be null
* @param clazz the class to search for, subclasses do not match, null returns null
* @return the index into the throwable chain, null if no match or null input
* @since 3.10
* @param chain
* The root of a Throwable causal chain.
* @param type
* The exception type to test.
* @return true, if chain is an instance of type or is an
* UndeclaredThrowableException wrapping a cause.
* @since 3.5
* @see #wrapAndThrow(Throwable)
*/
public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz) {
return throwableOf(throwable, clazz, 0, false);
}
/**
* <p>Returns the (zero-based) index of the first <code>Throwable</code>
* that matches the specified type in the exception chain from
* a specified index.
* Subclasses of the specified class do not match - see
* {@link #indexOfType(Throwable, Class, int)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>-1</code>.
* A <code>null</code> type returns <code>-1</code>.
* No match in the chain returns <code>-1</code>.
* A negative start index is treated as zero.
* A start index greater than the number of throwables returns <code>-1</code>.</p>
*
* @param throwable the throwable to inspect, may be null
* @param clazz the class to search for, subclasses do not match, null returns -1
* @param fromIndex the (zero-based) index of the starting position,
* negative treated as zero, larger than chain size returns -1
* @return the index into the throwable chain, -1 if no match or null input
*/
public static int indexOfThrowable(final Throwable throwable, final Class<?> clazz, final int fromIndex) {
return indexOf(throwable, clazz, fromIndex, false);
}
/**
* <p>Returns the first <code>Throwable</code>
* that matches the specified type in the exception chain from
* a specified index.
* Subclasses of the specified class do not match - see
* {@link #throwableOfType(Throwable, Class, int)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>null</code>.
* A <code>null</code> type returns <code>null</code>.
* No match in the chain returns <code>null</code>.
* A negative start index is treated as zero.
* A start index greater than the number of throwables returns <code>null</code>.</p>
*
* @param <T> the type of Throwable you are searching.
* @param throwable the throwable to inspect, may be null
* @param clazz the class to search for, subclasses do not match, null returns null
* @param fromIndex the (zero-based) index of the starting position,
* negative treated as zero, larger than chain size returns null
* @return the index into the throwable chain, null if no match or null input
* @since 3.10
*/
public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz, final int fromIndex) {
return throwableOf(throwable, clazz, fromIndex, false);
}
/**
* <p>Returns the (zero-based) index of the first <code>Throwable</code>
* that matches the specified class or subclass in the exception chain.
* Subclasses of the specified class do match - see
* {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>-1</code>.
* A <code>null</code> type returns <code>-1</code>.
* No match in the chain returns <code>-1</code>.</p>
*
* @param throwable the throwable to inspect, may be null
* @param type the type to search for, subclasses match, null returns -1
* @return the index into the throwable chain, -1 if no match or null input
* @since 2.1
*/
public static int indexOfType(final Throwable throwable, final Class<?> type) {
return indexOf(throwable, type, 0, true);
}
/**
* <p>Returns the throwable of the first <code>Throwable</code>
* that matches the specified class or subclass in the exception chain.
* Subclasses of the specified class do match - see
* {@link #throwableOfThrowable(Throwable, Class)} for the opposite..</p>
*
* <p>A <code>null</code> throwable returns <code>null</code>.
* A <code>null</code> type returns <code>null</code>.
* No match in the chain returns <code>null</code>.</p>
*
* @param <T> the type of Throwable you are searching.
* @param throwable the throwable to inspect, may be null
* @param type the type to search for, subclasses match, null returns null
* @return the index into the throwable chain, null if no match or null input
* @since 3.10
*/
public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type) {
return throwableOf(throwable, type, 0, true);
}
/**
* <p>Returns the first <code>Throwable</code>
* that matches the specified type in the exception chain from
* a specified index.
* Subclasses of the specified class do match - see
* {@link #throwableOfThrowable(Throwable, Class)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>null</code>.
* A <code>null</code> type returns <code>null</code>.
* No match in the chain returns <code>null</code>.
* A negative start index is treated as zero.
* A start index greater than the number of throwables returns <code>null</code>.</p>
*
* @param <T> the type of Throwable you are searching.
* @param throwable the throwable to inspect, may be null
* @param type the type to search for, subclasses match, null returns null
* @param fromIndex the (zero-based) index of the starting position,
* negative treated as zero, larger than chain size returns null
* @return the index into the throwable chain, null if no match or null input
* @since 3.10
*/
public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type, final int fromIndex) {
return throwableOf(throwable, type, fromIndex, true);
}
/**
* <p>Returns the (zero-based) index of the first <code>Throwable</code>
* that matches the specified type in the exception chain from
* a specified index.
* Subclasses of the specified class do match - see
* {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>-1</code>.
* A <code>null</code> type returns <code>-1</code>.
* No match in the chain returns <code>-1</code>.
* A negative start index is treated as zero.
* A start index greater than the number of throwables returns <code>-1</code>.</p>
*
* @param throwable the throwable to inspect, may be null
* @param type the type to search for, subclasses match, null returns -1
* @param fromIndex the (zero-based) index of the starting position,
* negative treated as zero, larger than chain size returns -1
* @return the index into the throwable chain, -1 if no match or null input
* @since 2.1
*/
public static int indexOfType(final Throwable throwable, final Class<?> type, final int fromIndex) {
return indexOf(throwable, type, fromIndex, true);
public static boolean hasCause(Throwable chain,
final Class<? extends Throwable> type) {
if (chain instanceof UndeclaredThrowableException) {
chain = chain.getCause();
}
return type.isInstance(chain);
}
/**
@ -498,42 +501,87 @@ public class ExceptionUtils {
}
/**
* <p>Worker method for the <code>throwableOfType</code> methods.</p>
* <p>Returns the (zero-based) index of the first <code>Throwable</code>
* that matches the specified class (exactly) in the exception chain.
* Subclasses of the specified class do not match - see
* {@link #indexOfType(Throwable, Class)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>-1</code>.
* A <code>null</code> type returns <code>-1</code>.
* No match in the chain returns <code>-1</code>.</p>
*
* @param <T> the type of Throwable you are searching.
* @param throwable the throwable to inspect, may be null
* @param type the type to search, subclasses match, null returns null
* @param fromIndex the (zero-based) index of the starting position,
* negative treated as zero, larger than chain size returns null
* @param subclass if <code>true</code>, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares
* using references
* @return throwable of the <code>type</code> within throwables nested within the specified <code>throwable</code>
* @param clazz the class to search for, subclasses do not match, null returns -1
* @return the index into the throwable chain, -1 if no match or null input
*/
private static <T extends Throwable> T throwableOf(final Throwable throwable, final Class<T> type, int fromIndex, final boolean subclass) {
if (throwable == null || type == null) {
return null;
}
if (fromIndex < 0) {
fromIndex = 0;
}
final Throwable[] throwables = getThrowables(throwable);
if (fromIndex >= throwables.length) {
return null;
}
if (subclass) {
for (int i = fromIndex; i < throwables.length; i++) {
if (type.isAssignableFrom(throwables[i].getClass())) {
return type.cast(throwables[i]);
}
}
} else {
for (int i = fromIndex; i < throwables.length; i++) {
if (type.equals(throwables[i].getClass())) {
return type.cast(throwables[i]);
}
}
}
return null;
public static int indexOfThrowable(final Throwable throwable, final Class<?> clazz) {
return indexOf(throwable, clazz, 0, false);
}
/**
* <p>Returns the (zero-based) index of the first <code>Throwable</code>
* that matches the specified type in the exception chain from
* a specified index.
* Subclasses of the specified class do not match - see
* {@link #indexOfType(Throwable, Class, int)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>-1</code>.
* A <code>null</code> type returns <code>-1</code>.
* No match in the chain returns <code>-1</code>.
* A negative start index is treated as zero.
* A start index greater than the number of throwables returns <code>-1</code>.</p>
*
* @param throwable the throwable to inspect, may be null
* @param clazz the class to search for, subclasses do not match, null returns -1
* @param fromIndex the (zero-based) index of the starting position,
* negative treated as zero, larger than chain size returns -1
* @return the index into the throwable chain, -1 if no match or null input
*/
public static int indexOfThrowable(final Throwable throwable, final Class<?> clazz, final int fromIndex) {
return indexOf(throwable, clazz, fromIndex, false);
}
/**
* <p>Returns the (zero-based) index of the first <code>Throwable</code>
* that matches the specified class or subclass in the exception chain.
* Subclasses of the specified class do match - see
* {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>-1</code>.
* A <code>null</code> type returns <code>-1</code>.
* No match in the chain returns <code>-1</code>.</p>
*
* @param throwable the throwable to inspect, may be null
* @param type the type to search for, subclasses match, null returns -1
* @return the index into the throwable chain, -1 if no match or null input
* @since 2.1
*/
public static int indexOfType(final Throwable throwable, final Class<?> type) {
return indexOf(throwable, type, 0, true);
}
/**
* <p>Returns the (zero-based) index of the first <code>Throwable</code>
* that matches the specified type in the exception chain from
* a specified index.
* Subclasses of the specified class do match - see
* {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>-1</code>.
* A <code>null</code> type returns <code>-1</code>.
* No match in the chain returns <code>-1</code>.
* A negative start index is treated as zero.
* A start index greater than the number of throwables returns <code>-1</code>.</p>
*
* @param throwable the throwable to inspect, may be null
* @param type the type to search for, subclasses match, null returns -1
* @param fromIndex the (zero-based) index of the starting position,
* negative treated as zero, larger than chain size returns -1
* @return the index into the throwable chain, -1 if no match or null input
* @since 2.1
*/
public static int indexOfType(final Throwable throwable, final Class<?> type, final int fromIndex) {
return indexOf(throwable, type, fromIndex, true);
}
//-----------------------------------------------------------------------
@ -621,44 +669,6 @@ public class ExceptionUtils {
writer.flush();
}
//-----------------------------------------------------------------------
/**
* <p>Creates a compact stack trace for the root cause of the supplied
* <code>Throwable</code>.</p>
*
* <p>The output of this method is consistent across JDK versions.
* It consists of the root exception followed by each of its wrapping
* exceptions separated by '[wrapped]'. Note that this is the opposite
* order to the JDK1.4 display.</p>
*
* @param throwable the throwable to examine, may be null
* @return an array of stack trace frames, never null
* @since 2.0
*/
public static String[] getRootCauseStackTrace(final Throwable throwable) {
if (throwable == null) {
return ArrayUtils.EMPTY_STRING_ARRAY;
}
final Throwable throwables[] = getThrowables(throwable);
final int count = throwables.length;
final List<String> frames = new ArrayList<>();
List<String> nextTrace = getStackFrameList(throwables[count - 1]);
for (int i = count; --i >= 0;) {
final List<String> trace = nextTrace;
if (i != 0) {
nextTrace = getStackFrameList(throwables[i - 1]);
removeCommonFrames(trace, nextTrace);
}
if (i == count - 1) {
frames.add(throwables[i].toString());
} else {
frames.add(WRAPPED_MARKER + throwables[i].toString());
}
frames.addAll(trace);
}
return frames.toArray(new String[frames.size()]);
}
/**
* <p>Removes common frames from the cause trace given the two stack traces.</p>
*
@ -686,134 +696,6 @@ public class ExceptionUtils {
}
}
//-----------------------------------------------------------------------
/**
* <p>Gets the stack trace from a Throwable as a String.</p>
*
* <p>The result of this method vary by JDK version as this method
* uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
* On JDK1.3 and earlier, the cause exception will not be shown
* unless the specified throwable alters printStackTrace.</p>
*
* @param throwable the <code>Throwable</code> to be examined
* @return the stack trace as generated by the exception's
* <code>printStackTrace(PrintWriter)</code> method
*/
public static String getStackTrace(final Throwable throwable) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw, true);
throwable.printStackTrace(pw);
return sw.getBuffer().toString();
}
/**
* <p>Captures the stack trace associated with the specified
* <code>Throwable</code> object, decomposing it into a list of
* stack frames.</p>
*
* <p>The result of this method vary by JDK version as this method
* uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
* On JDK1.3 and earlier, the cause exception will not be shown
* unless the specified throwable alters printStackTrace.</p>
*
* @param throwable the <code>Throwable</code> to examine, may be null
* @return an array of strings describing each stack frame, never null
*/
public static String[] getStackFrames(final Throwable throwable) {
if (throwable == null) {
return ArrayUtils.EMPTY_STRING_ARRAY;
}
return getStackFrames(getStackTrace(throwable));
}
//-----------------------------------------------------------------------
/**
* <p>Returns an array where each element is a line from the argument.</p>
*
* <p>The end of line is determined by the value of {@link System#lineSeparator()}.</p>
*
* @param stackTrace a stack trace String
* @return an array where each element is a line from the argument
*/
static String[] getStackFrames(final String stackTrace) {
final String linebreak = System.lineSeparator();
final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
final List<String> list = new ArrayList<>();
while (frames.hasMoreTokens()) {
list.add(frames.nextToken());
}
return list.toArray(new String[list.size()]);
}
/**
* <p>Produces a <code>List</code> of stack frames - the message
* is not included. Only the trace of the specified exception is
* returned, any caused by trace is stripped.</p>
*
* <p>This works in most cases - it will only fail if the exception
* message contains a line that starts with:
* <code>&quot;&nbsp;&nbsp;&nbsp;at&quot;.</code></p>
*
* @param t is any throwable
* @return List of stack frames
*/
static List<String> getStackFrameList(final Throwable t) {
final String stackTrace = getStackTrace(t);
final String linebreak = System.lineSeparator();
final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
final List<String> list = new ArrayList<>();
boolean traceStarted = false;
while (frames.hasMoreTokens()) {
final String token = frames.nextToken();
// Determine if the line starts with <whitespace>at
final int at = token.indexOf("at");
if (at != -1 && token.substring(0, at).trim().isEmpty()) {
traceStarted = true;
list.add(token);
} else if (traceStarted) {
break;
}
}
return list;
}
//-----------------------------------------------------------------------
/**
* Gets a short message summarising the exception.
* <p>
* The message returned is of the form
* {ClassNameWithoutPackage}: {ThrowableMessage}
*
* @param th the throwable to get a message for, null returns empty string
* @return the message, non-null
* @since 2.2
*/
public static String getMessage(final Throwable th) {
if (th == null) {
return StringUtils.EMPTY;
}
final String clsName = ClassUtils.getShortClassName(th, null);
final String msg = th.getMessage();
return clsName + ": " + StringUtils.defaultString(msg);
}
//-----------------------------------------------------------------------
/**
* Gets a short message summarising the root cause exception.
* <p>
* The message returned is of the form
* {ClassNameWithoutPackage}: {ThrowableMessage}
*
* @param th the throwable to get a message for, null returns empty string
* @return the message, non-null
* @since 2.2
*/
public static String getRootCauseMessage(final Throwable th) {
Throwable root = getRootCause(th);
root = root == null ? th : root;
return getMessage(root);
}
/**
* Throw a checked exception without adding the exception to the throws
* clause of the calling method. This method prevents throws clause
@ -874,6 +756,135 @@ public class ExceptionUtils {
return ExceptionUtils.<R, RuntimeException>typeErasure(throwable);
}
/**
* <p>Worker method for the <code>throwableOfType</code> methods.</p>
*
* @param <T> the type of Throwable you are searching.
* @param throwable the throwable to inspect, may be null
* @param type the type to search, subclasses match, null returns null
* @param fromIndex the (zero-based) index of the starting position,
* negative treated as zero, larger than chain size returns null
* @param subclass if <code>true</code>, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares
* using references
* @return throwable of the <code>type</code> within throwables nested within the specified <code>throwable</code>
*/
private static <T extends Throwable> T throwableOf(final Throwable throwable, final Class<T> type, int fromIndex, final boolean subclass) {
if (throwable == null || type == null) {
return null;
}
if (fromIndex < 0) {
fromIndex = 0;
}
final Throwable[] throwables = getThrowables(throwable);
if (fromIndex >= throwables.length) {
return null;
}
if (subclass) {
for (int i = fromIndex; i < throwables.length; i++) {
if (type.isAssignableFrom(throwables[i].getClass())) {
return type.cast(throwables[i]);
}
}
} else {
for (int i = fromIndex; i < throwables.length; i++) {
if (type.equals(throwables[i].getClass())) {
return type.cast(throwables[i]);
}
}
}
return null;
}
/**
* <p>Returns the first <code>Throwable</code>
* that matches the specified class (exactly) in the exception chain.
* Subclasses of the specified class do not match - see
* {@link #throwableOfType(Throwable, Class)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>null</code>.
* A <code>null</code> type returns <code>null</code>.
* No match in the chain returns <code>null</code>.</p>
*
* @param <T> the type of Throwable you are searching.
* @param throwable the throwable to inspect, may be null
* @param clazz the class to search for, subclasses do not match, null returns null
* @return the index into the throwable chain, null if no match or null input
* @since 3.10
*/
public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz) {
return throwableOf(throwable, clazz, 0, false);
}
/**
* <p>Returns the first <code>Throwable</code>
* that matches the specified type in the exception chain from
* a specified index.
* Subclasses of the specified class do not match - see
* {@link #throwableOfType(Throwable, Class, int)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>null</code>.
* A <code>null</code> type returns <code>null</code>.
* No match in the chain returns <code>null</code>.
* A negative start index is treated as zero.
* A start index greater than the number of throwables returns <code>null</code>.</p>
*
* @param <T> the type of Throwable you are searching.
* @param throwable the throwable to inspect, may be null
* @param clazz the class to search for, subclasses do not match, null returns null
* @param fromIndex the (zero-based) index of the starting position,
* negative treated as zero, larger than chain size returns null
* @return the index into the throwable chain, null if no match or null input
* @since 3.10
*/
public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz, final int fromIndex) {
return throwableOf(throwable, clazz, fromIndex, false);
}
/**
* <p>Returns the throwable of the first <code>Throwable</code>
* that matches the specified class or subclass in the exception chain.
* Subclasses of the specified class do match - see
* {@link #throwableOfThrowable(Throwable, Class)} for the opposite..</p>
*
* <p>A <code>null</code> throwable returns <code>null</code>.
* A <code>null</code> type returns <code>null</code>.
* No match in the chain returns <code>null</code>.</p>
*
* @param <T> the type of Throwable you are searching.
* @param throwable the throwable to inspect, may be null
* @param type the type to search for, subclasses match, null returns null
* @return the index into the throwable chain, null if no match or null input
* @since 3.10
*/
public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type) {
return throwableOf(throwable, type, 0, true);
}
/**
* <p>Returns the first <code>Throwable</code>
* that matches the specified type in the exception chain from
* a specified index.
* Subclasses of the specified class do match - see
* {@link #throwableOfThrowable(Throwable, Class)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>null</code>.
* A <code>null</code> type returns <code>null</code>.
* No match in the chain returns <code>null</code>.
* A negative start index is treated as zero.
* A start index greater than the number of throwables returns <code>null</code>.</p>
*
* @param <T> the type of Throwable you are searching.
* @param throwable the throwable to inspect, may be null
* @param type the type to search for, subclasses match, null returns null
* @param fromIndex the (zero-based) index of the starting position,
* negative treated as zero, larger than chain size returns null
* @return the index into the throwable chain, null if no match or null input
* @since 3.10
*/
public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type, final int fromIndex) {
return throwableOf(throwable, type, fromIndex, true);
}
/**
* Claim a Throwable is another Exception type using type erasure. This
* hides a checked exception from the java compiler, allowing a checked
@ -917,23 +928,12 @@ public class ExceptionUtils {
}
/**
* Does the throwable's causal chain have an immediate or wrapped exception
* of the given type?
*
* @param chain
* The root of a Throwable causal chain.
* @param type
* The exception type to test.
* @return true, if chain is an instance of type or is an
* UndeclaredThrowableException wrapping a cause.
* @since 3.5
* @see #wrapAndThrow(Throwable)
* <p>
* Public constructor allows an instance of <code>ExceptionUtils</code> to be created, although that is not
* normally necessary.
* </p>
*/
public static boolean hasCause(Throwable chain,
final Class<? extends Throwable> type) {
if (chain instanceof UndeclaredThrowableException) {
chain = chain.getCause();
}
return type.isInstance(chain);
public ExceptionUtils() {
super();
}
}

View File

@ -44,46 +44,91 @@ import org.junit.jupiter.api.Test;
*/
public class ExceptionUtilsTest {
private NestableException nested;
private Throwable withCause;
private Throwable withoutCause;
private Throwable jdkNoCause;
private ExceptionWithCause cyclicCause;
private Throwable notVisibleException;
/**
* Provides a method with a well known chained/nested exception
* name which matches the full signature (e.g. has a return value
* of <code>Throwable</code>.
*/
private static class ExceptionWithCause extends Exception {
private static final long serialVersionUID = 1L;
private Throwable cause;
@BeforeEach
public void setUp() {
withoutCause = createExceptionWithoutCause();
nested = new NestableException(withoutCause);
withCause = new ExceptionWithCause(nested);
jdkNoCause = new NullPointerException();
final ExceptionWithCause a = new ExceptionWithCause(null);
final ExceptionWithCause b = new ExceptionWithCause(a);
a.setCause(b);
cyclicCause = new ExceptionWithCause(a);
notVisibleException = NotVisibleExceptionFactory.createException(withoutCause);
}
ExceptionWithCause(final String str, final Throwable cause) {
super(str);
setCause(cause);
}
ExceptionWithCause(final Throwable cause) {
super();
setCause(cause);
}
@AfterEach
public void tearDown() {
withoutCause = null;
nested = null;
withCause = null;
jdkNoCause = null;
cyclicCause = null;
notVisibleException = null;
}
@Override
public Throwable getCause() {
return cause;
}
//-----------------------------------------------------------------------
private Throwable createExceptionWithoutCause() {
try {
throw new ExceptionWithoutCause();
} catch (final Throwable t) {
return t;
public void setCause(final Throwable cause) {
this.cause = cause;
}
}
/**
* Provides a method with a well known chained/nested exception
* name which does not match the full signature (e.g. lacks a
* return value of <code>Throwable</code>.
*/
private static class ExceptionWithoutCause extends Exception {
private static final long serialVersionUID = 1L;
@SuppressWarnings("unused")
public void getTargetException() {
// noop
}
}
// Temporary classes to allow the nested exception code to be removed
// prior to a rewrite of this test class.
private static class NestableException extends Exception {
private static final long serialVersionUID = 1L;
@SuppressWarnings("unused")
NestableException() {
super();
}
NestableException(final Throwable t) {
super(t);
}
}
public static class TestThrowable extends Throwable {
private static final long serialVersionUID = 1L;
}
private static int redeclareCheckedException() {
return throwsCheckedException();
}
private static int throwsCheckedException() {
try {
throw new IOException();
} catch (final Exception e) {
return ExceptionUtils.<Integer>rethrow(e);
}
}
private NestableException nested;
private Throwable withCause;
private Throwable withoutCause;
private Throwable jdkNoCause;
//-----------------------------------------------------------------------
private ExceptionWithCause cyclicCause;
private Throwable notVisibleException;
private Throwable createExceptionWithCause() {
try {
@ -98,6 +143,69 @@ public class ExceptionUtilsTest {
}
//-----------------------------------------------------------------------
private Throwable createExceptionWithoutCause() {
try {
throw new ExceptionWithoutCause();
} catch (final Throwable t) {
return t;
}
}
@BeforeEach
public void setUp() {
withoutCause = createExceptionWithoutCause();
nested = new NestableException(withoutCause);
withCause = new ExceptionWithCause(nested);
jdkNoCause = new NullPointerException();
final ExceptionWithCause a = new ExceptionWithCause(null);
final ExceptionWithCause b = new ExceptionWithCause(a);
a.setCause(b);
cyclicCause = new ExceptionWithCause(a);
notVisibleException = NotVisibleExceptionFactory.createException(withoutCause);
}
@AfterEach
public void tearDown() {
withoutCause = null;
nested = null;
withCause = null;
jdkNoCause = null;
cyclicCause = null;
notVisibleException = null;
}
@Test
public void test_getMessage_Throwable() {
Throwable th = null;
assertEquals("", ExceptionUtils.getMessage(th));
th = new IllegalArgumentException("Base");
assertEquals("IllegalArgumentException: Base", ExceptionUtils.getMessage(th));
th = new ExceptionWithCause("Wrapper", th);
assertEquals("ExceptionUtilsTest.ExceptionWithCause: Wrapper", ExceptionUtils.getMessage(th));
}
@Test
public void test_getRootCauseMessage_Throwable() {
Throwable th = null;
assertEquals("", ExceptionUtils.getRootCauseMessage(th));
th = new IllegalArgumentException("Base");
assertEquals("IllegalArgumentException: Base", ExceptionUtils.getRootCauseMessage(th));
th = new ExceptionWithCause("Wrapper", th);
assertEquals("IllegalArgumentException: Base", ExceptionUtils.getRootCauseMessage(th));
}
@Test
public void testCatchTechniques() {
IOException ioe = assertThrows(IOException.class, ExceptionUtilsTest::throwsCheckedException);
assertEquals(1, ExceptionUtils.getThrowableCount(ioe));
ioe = assertThrows(IOException.class, ExceptionUtilsTest::redeclareCheckedException);
assertEquals(1, ExceptionUtils.getThrowableCount(ioe));
}
@Test
public void testConstructor() {
@ -154,6 +262,33 @@ public class ExceptionUtilsTest {
assertSame(cyclicCause.getCause().getCause(), ExceptionUtils.getRootCause(cyclicCause));
}
//-----------------------------------------------------------------------
@Test
public void testGetRootCauseStackTrace_Throwable() {
assertEquals(0, ExceptionUtils.getRootCauseStackTrace(null).length);
final Throwable cause = createExceptionWithCause();
String[] stackTrace = ExceptionUtils.getRootCauseStackTrace(cause);
boolean match = false;
for (final String element : stackTrace) {
if (element.startsWith(ExceptionUtils.WRAPPED_MARKER)) {
match = true;
break;
}
}
assertTrue(match);
stackTrace = ExceptionUtils.getRootCauseStackTrace(withoutCause);
match = false;
for (final String element : stackTrace) {
if (element.startsWith(ExceptionUtils.WRAPPED_MARKER)) {
match = true;
break;
}
}
assertFalse(match);
}
//-----------------------------------------------------------------------
@Test
public void testGetThrowableCount_Throwable() {
@ -165,50 +300,19 @@ public class ExceptionUtilsTest {
assertEquals(3, ExceptionUtils.getThrowableCount(cyclicCause));
}
//-----------------------------------------------------------------------
@Test
public void testGetThrowables_Throwable_null() {
assertEquals(0, ExceptionUtils.getThrowables(null).length);
public void testGetThrowableList_Throwable_jdkNoCause() {
final List<?> throwables = ExceptionUtils.getThrowableList(jdkNoCause);
assertEquals(1, throwables.size());
assertSame(jdkNoCause, throwables.get(0));
}
@Test
public void testGetThrowables_Throwable_withoutCause() {
final Throwable[] throwables = ExceptionUtils.getThrowables(withoutCause);
assertEquals(1, throwables.length);
assertSame(withoutCause, throwables[0]);
}
@Test
public void testGetThrowables_Throwable_nested() {
final Throwable[] throwables = ExceptionUtils.getThrowables(nested);
assertEquals(2, throwables.length);
assertSame(nested, throwables[0]);
assertSame(withoutCause, throwables[1]);
}
@Test
public void testGetThrowables_Throwable_withCause() {
final Throwable[] throwables = ExceptionUtils.getThrowables(withCause);
assertEquals(3, throwables.length);
assertSame(withCause, throwables[0]);
assertSame(nested, throwables[1]);
assertSame(withoutCause, throwables[2]);
}
@Test
public void testGetThrowables_Throwable_jdkNoCause() {
final Throwable[] throwables = ExceptionUtils.getThrowables(jdkNoCause);
assertEquals(1, throwables.length);
assertSame(jdkNoCause, throwables[0]);
}
@Test
public void testGetThrowables_Throwable_recursiveCause() {
final Throwable[] throwables = ExceptionUtils.getThrowables(cyclicCause);
assertEquals(3, throwables.length);
assertSame(cyclicCause, throwables[0]);
assertSame(cyclicCause.getCause(), throwables[1]);
assertSame(cyclicCause.getCause().getCause(), throwables[2]);
public void testGetThrowableList_Throwable_nested() {
final List<?> throwables = ExceptionUtils.getThrowableList(nested);
assertEquals(2, throwables.size());
assertSame(nested, throwables.get(0));
assertSame(withoutCause, throwables.get(1));
}
//-----------------------------------------------------------------------
@ -219,18 +323,12 @@ public class ExceptionUtilsTest {
}
@Test
public void testGetThrowableList_Throwable_withoutCause() {
final List<?> throwables = ExceptionUtils.getThrowableList(withoutCause);
assertEquals(1, throwables.size());
assertSame(withoutCause, throwables.get(0));
}
@Test
public void testGetThrowableList_Throwable_nested() {
final List<?> throwables = ExceptionUtils.getThrowableList(nested);
assertEquals(2, throwables.size());
assertSame(nested, throwables.get(0));
assertSame(withoutCause, throwables.get(1));
public void testGetThrowableList_Throwable_recursiveCause() {
final List<?> throwables = ExceptionUtils.getThrowableList(cyclicCause);
assertEquals(3, throwables.size());
assertSame(cyclicCause, throwables.get(0));
assertSame(cyclicCause.getCause(), throwables.get(1));
assertSame(cyclicCause.getCause().getCause(), throwables.get(2));
}
@Test
@ -243,19 +341,56 @@ public class ExceptionUtilsTest {
}
@Test
public void testGetThrowableList_Throwable_jdkNoCause() {
final List<?> throwables = ExceptionUtils.getThrowableList(jdkNoCause);
public void testGetThrowableList_Throwable_withoutCause() {
final List<?> throwables = ExceptionUtils.getThrowableList(withoutCause);
assertEquals(1, throwables.size());
assertSame(jdkNoCause, throwables.get(0));
assertSame(withoutCause, throwables.get(0));
}
@Test
public void testGetThrowableList_Throwable_recursiveCause() {
final List<?> throwables = ExceptionUtils.getThrowableList(cyclicCause);
assertEquals(3, throwables.size());
assertSame(cyclicCause, throwables.get(0));
assertSame(cyclicCause.getCause(), throwables.get(1));
assertSame(cyclicCause.getCause().getCause(), throwables.get(2));
public void testGetThrowables_Throwable_jdkNoCause() {
final Throwable[] throwables = ExceptionUtils.getThrowables(jdkNoCause);
assertEquals(1, throwables.length);
assertSame(jdkNoCause, throwables[0]);
}
@Test
public void testGetThrowables_Throwable_nested() {
final Throwable[] throwables = ExceptionUtils.getThrowables(nested);
assertEquals(2, throwables.length);
assertSame(nested, throwables[0]);
assertSame(withoutCause, throwables[1]);
}
//-----------------------------------------------------------------------
@Test
public void testGetThrowables_Throwable_null() {
assertEquals(0, ExceptionUtils.getThrowables(null).length);
}
@Test
public void testGetThrowables_Throwable_recursiveCause() {
final Throwable[] throwables = ExceptionUtils.getThrowables(cyclicCause);
assertEquals(3, throwables.length);
assertSame(cyclicCause, throwables[0]);
assertSame(cyclicCause.getCause(), throwables[1]);
assertSame(cyclicCause.getCause().getCause(), throwables[2]);
}
@Test
public void testGetThrowables_Throwable_withCause() {
final Throwable[] throwables = ExceptionUtils.getThrowables(withCause);
assertEquals(3, throwables.length);
assertSame(withCause, throwables[0]);
assertSame(nested, throwables[1]);
assertSame(withoutCause, throwables[2]);
}
@Test
public void testGetThrowables_Throwable_withoutCause() {
final Throwable[] throwables = ExceptionUtils.getThrowables(withoutCause);
assertEquals(1, throwables.length);
assertSame(withoutCause, throwables[0]);
}
@Test
@ -282,30 +417,6 @@ public class ExceptionUtilsTest {
assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, Throwable.class));
}
@Test
public void testThrowableOf_ThrowableClass() {
assertEquals(null, ExceptionUtils.throwableOfThrowable(null, null));
assertEquals(null, ExceptionUtils.throwableOfThrowable(null, NestableException.class));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withoutCause, null));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withoutCause, ExceptionWithCause.class));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withoutCause, NestableException.class));
assertEquals(withoutCause, ExceptionUtils.throwableOfThrowable(withoutCause, ExceptionWithoutCause.class));
assertEquals(null, ExceptionUtils.throwableOfThrowable(nested, null));
assertEquals(null, ExceptionUtils.throwableOfThrowable(nested, ExceptionWithCause.class));
assertEquals(nested, ExceptionUtils.throwableOfThrowable(nested, NestableException.class));
assertEquals(nested.getCause(), ExceptionUtils.throwableOfThrowable(nested, ExceptionWithoutCause.class));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, null));
assertEquals(withCause, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class));
assertEquals(withCause.getCause(), ExceptionUtils.throwableOfThrowable(withCause, NestableException.class));
assertEquals(withCause.getCause().getCause(), ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithoutCause.class));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, Exception.class));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, Throwable.class));
}
@Test
public void testIndexOf_ThrowableClassInt() {
assertEquals(-1, ExceptionUtils.indexOfThrowable(null, null, 0));
@ -335,35 +446,6 @@ public class ExceptionUtilsTest {
assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, Throwable.class, 0));
}
@Test
public void testThrowableOf_ThrowableClassInt() {
assertEquals(null, ExceptionUtils.throwableOfThrowable(null, null, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(null, NestableException.class, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withoutCause, null));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withoutCause, ExceptionWithCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withoutCause, NestableException.class, 0));
assertEquals(withoutCause, ExceptionUtils.throwableOfThrowable(withoutCause, ExceptionWithoutCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(nested, null, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(nested, ExceptionWithCause.class, 0));
assertEquals(nested, ExceptionUtils.throwableOfThrowable(nested, NestableException.class, 0));
assertEquals(nested.getCause(), ExceptionUtils.throwableOfThrowable(nested, ExceptionWithoutCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, null));
assertEquals(withCause, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class, 0));
assertEquals(withCause.getCause(), ExceptionUtils.throwableOfThrowable(withCause, NestableException.class, 0));
assertEquals(withCause.getCause().getCause(), ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithoutCause.class, 0));
assertEquals(withCause, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class, -1));
assertEquals(withCause, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class, 1));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class, 9));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, Exception.class, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, Throwable.class, 0));
}
//-----------------------------------------------------------------------
@Test
public void testIndexOfType_ThrowableClass() {
@ -389,30 +471,6 @@ public class ExceptionUtilsTest {
assertEquals(0, ExceptionUtils.indexOfType(withCause, Throwable.class));
}
@Test
public void testThrowableOfType_ThrowableClass() {
assertEquals(null, ExceptionUtils.throwableOfType(null, null));
assertEquals(null, ExceptionUtils.throwableOfType(null, NestableException.class));
assertEquals(null, ExceptionUtils.throwableOfType(withoutCause, null));
assertEquals(null, ExceptionUtils.throwableOfType(withoutCause, ExceptionWithCause.class));
assertEquals(null, ExceptionUtils.throwableOfType(withoutCause, NestableException.class));
assertEquals(withoutCause, ExceptionUtils.throwableOfType(withoutCause, ExceptionWithoutCause.class));
assertEquals(null, ExceptionUtils.throwableOfType(nested, null));
assertEquals(null, ExceptionUtils.throwableOfType(nested, ExceptionWithCause.class));
assertEquals(nested, ExceptionUtils.throwableOfType(nested, NestableException.class));
assertEquals(nested.getCause(), ExceptionUtils.throwableOfType(nested, ExceptionWithoutCause.class));
assertEquals(null, ExceptionUtils.throwableOfType(withCause, null));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class));
assertEquals(withCause.getCause(), ExceptionUtils.throwableOfType(withCause, NestableException.class));
assertEquals(withCause.getCause().getCause(), ExceptionUtils.throwableOfType(withCause, ExceptionWithoutCause.class));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, Exception.class));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, Throwable.class));
}
@Test
public void testIndexOfType_ThrowableClassInt() {
assertEquals(-1, ExceptionUtils.indexOfType(null, null, 0));
@ -442,35 +500,6 @@ public class ExceptionUtilsTest {
assertEquals(0, ExceptionUtils.indexOfType(withCause, Throwable.class, 0));
}
@Test
public void testThrowableOfType_ThrowableClassInt() {
assertEquals(null, ExceptionUtils.throwableOfType(null, null, 0));
assertEquals(null, ExceptionUtils.throwableOfType(null, NestableException.class, 0));
assertEquals(null, ExceptionUtils.throwableOfType(withoutCause, null));
assertEquals(null, ExceptionUtils.throwableOfType(withoutCause, ExceptionWithCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfType(withoutCause, NestableException.class, 0));
assertEquals(withoutCause, ExceptionUtils.throwableOfType(withoutCause, ExceptionWithoutCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfType(nested, null, 0));
assertEquals(null, ExceptionUtils.throwableOfType(nested, ExceptionWithCause.class, 0));
assertEquals(nested, ExceptionUtils.throwableOfType(nested, NestableException.class, 0));
assertEquals(nested.getCause(), ExceptionUtils.throwableOfType(nested, ExceptionWithoutCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfType(withCause, null));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class, 0));
assertEquals(withCause.getCause(), ExceptionUtils.throwableOfType(withCause, NestableException.class, 0));
assertEquals(withCause.getCause().getCause(), ExceptionUtils.throwableOfType(withCause, ExceptionWithoutCause.class, 0));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class, -1));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class, 1));
assertEquals(null, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class, 9));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, Exception.class, 0));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, Throwable.class, 0));
}
//-----------------------------------------------------------------------
@Test
public void testPrintRootCauseStackTrace_Throwable() {
@ -479,6 +508,8 @@ public class ExceptionUtilsTest {
// internally this method calls stream method anyway
}
//-----------------------------------------------------------------------
@Test
public void testPrintRootCauseStackTrace_ThrowableStream() {
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
@ -527,123 +558,11 @@ public class ExceptionUtilsTest {
assertFalse(stackTrace.contains(ExceptionUtils.WRAPPED_MARKER));
}
//-----------------------------------------------------------------------
@Test
public void testGetRootCauseStackTrace_Throwable() {
assertEquals(0, ExceptionUtils.getRootCauseStackTrace(null).length);
final Throwable cause = createExceptionWithCause();
String[] stackTrace = ExceptionUtils.getRootCauseStackTrace(cause);
boolean match = false;
for (final String element : stackTrace) {
if (element.startsWith(ExceptionUtils.WRAPPED_MARKER)) {
match = true;
break;
}
}
assertTrue(match);
stackTrace = ExceptionUtils.getRootCauseStackTrace(withoutCause);
match = false;
for (final String element : stackTrace) {
if (element.startsWith(ExceptionUtils.WRAPPED_MARKER)) {
match = true;
break;
}
}
assertFalse(match);
}
@Test
public void testRemoveCommonFrames_ListList() {
assertThrows(IllegalArgumentException.class, () -> ExceptionUtils.removeCommonFrames(null, null));
}
@Test
public void test_getMessage_Throwable() {
Throwable th = null;
assertEquals("", ExceptionUtils.getMessage(th));
th = new IllegalArgumentException("Base");
assertEquals("IllegalArgumentException: Base", ExceptionUtils.getMessage(th));
th = new ExceptionWithCause("Wrapper", th);
assertEquals("ExceptionUtilsTest.ExceptionWithCause: Wrapper", ExceptionUtils.getMessage(th));
}
@Test
public void test_getRootCauseMessage_Throwable() {
Throwable th = null;
assertEquals("", ExceptionUtils.getRootCauseMessage(th));
th = new IllegalArgumentException("Base");
assertEquals("IllegalArgumentException: Base", ExceptionUtils.getRootCauseMessage(th));
th = new ExceptionWithCause("Wrapper", th);
assertEquals("IllegalArgumentException: Base", ExceptionUtils.getRootCauseMessage(th));
}
//-----------------------------------------------------------------------
/**
* Provides a method with a well known chained/nested exception
* name which matches the full signature (e.g. has a return value
* of <code>Throwable</code>.
*/
private static class ExceptionWithCause extends Exception {
private static final long serialVersionUID = 1L;
private Throwable cause;
ExceptionWithCause(final String str, final Throwable cause) {
super(str);
setCause(cause);
}
ExceptionWithCause(final Throwable cause) {
super();
setCause(cause);
}
@Override
public Throwable getCause() {
return cause;
}
public void setCause(final Throwable cause) {
this.cause = cause;
}
}
/**
* Provides a method with a well known chained/nested exception
* name which does not match the full signature (e.g. lacks a
* return value of <code>Throwable</code>.
*/
private static class ExceptionWithoutCause extends Exception {
private static final long serialVersionUID = 1L;
@SuppressWarnings("unused")
public void getTargetException() {
// noop
}
}
// Temporary classes to allow the nested exception code to be removed
// prior to a rewrite of this test class.
private static class NestableException extends Exception {
private static final long serialVersionUID = 1L;
@SuppressWarnings("unused")
NestableException() {
super();
}
NestableException(final Throwable t) {
super(t);
}
}
@Test
public void testThrow() {
final Exception expected = new InterruptedException();
@ -652,28 +571,115 @@ public class ExceptionUtilsTest {
}
@Test
public void testCatchTechniques() {
IOException ioe = assertThrows(IOException.class, ExceptionUtilsTest::throwsCheckedException);
assertEquals(1, ExceptionUtils.getThrowableCount(ioe));
public void testThrowableOf_ThrowableClass() {
assertEquals(null, ExceptionUtils.throwableOfThrowable(null, null));
assertEquals(null, ExceptionUtils.throwableOfThrowable(null, NestableException.class));
ioe = assertThrows(IOException.class, ExceptionUtilsTest::redeclareCheckedException);
assertEquals(1, ExceptionUtils.getThrowableCount(ioe));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withoutCause, null));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withoutCause, ExceptionWithCause.class));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withoutCause, NestableException.class));
assertEquals(withoutCause, ExceptionUtils.throwableOfThrowable(withoutCause, ExceptionWithoutCause.class));
assertEquals(null, ExceptionUtils.throwableOfThrowable(nested, null));
assertEquals(null, ExceptionUtils.throwableOfThrowable(nested, ExceptionWithCause.class));
assertEquals(nested, ExceptionUtils.throwableOfThrowable(nested, NestableException.class));
assertEquals(nested.getCause(), ExceptionUtils.throwableOfThrowable(nested, ExceptionWithoutCause.class));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, null));
assertEquals(withCause, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class));
assertEquals(withCause.getCause(), ExceptionUtils.throwableOfThrowable(withCause, NestableException.class));
assertEquals(withCause.getCause().getCause(), ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithoutCause.class));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, Exception.class));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, Throwable.class));
}
private static int redeclareCheckedException() {
return throwsCheckedException();
@Test
public void testThrowableOf_ThrowableClassInt() {
assertEquals(null, ExceptionUtils.throwableOfThrowable(null, null, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(null, NestableException.class, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withoutCause, null));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withoutCause, ExceptionWithCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withoutCause, NestableException.class, 0));
assertEquals(withoutCause, ExceptionUtils.throwableOfThrowable(withoutCause, ExceptionWithoutCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(nested, null, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(nested, ExceptionWithCause.class, 0));
assertEquals(nested, ExceptionUtils.throwableOfThrowable(nested, NestableException.class, 0));
assertEquals(nested.getCause(), ExceptionUtils.throwableOfThrowable(nested, ExceptionWithoutCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, null));
assertEquals(withCause, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class, 0));
assertEquals(withCause.getCause(), ExceptionUtils.throwableOfThrowable(withCause, NestableException.class, 0));
assertEquals(withCause.getCause().getCause(), ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithoutCause.class, 0));
assertEquals(withCause, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class, -1));
assertEquals(withCause, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class, 1));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class, 9));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, Exception.class, 0));
assertEquals(null, ExceptionUtils.throwableOfThrowable(withCause, Throwable.class, 0));
}
private static int throwsCheckedException() {
try {
throw new IOException();
} catch (final Exception e) {
return ExceptionUtils.<Integer>rethrow(e);
}
@Test
public void testThrowableOfType_ThrowableClass() {
assertEquals(null, ExceptionUtils.throwableOfType(null, null));
assertEquals(null, ExceptionUtils.throwableOfType(null, NestableException.class));
assertEquals(null, ExceptionUtils.throwableOfType(withoutCause, null));
assertEquals(null, ExceptionUtils.throwableOfType(withoutCause, ExceptionWithCause.class));
assertEquals(null, ExceptionUtils.throwableOfType(withoutCause, NestableException.class));
assertEquals(withoutCause, ExceptionUtils.throwableOfType(withoutCause, ExceptionWithoutCause.class));
assertEquals(null, ExceptionUtils.throwableOfType(nested, null));
assertEquals(null, ExceptionUtils.throwableOfType(nested, ExceptionWithCause.class));
assertEquals(nested, ExceptionUtils.throwableOfType(nested, NestableException.class));
assertEquals(nested.getCause(), ExceptionUtils.throwableOfType(nested, ExceptionWithoutCause.class));
assertEquals(null, ExceptionUtils.throwableOfType(withCause, null));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class));
assertEquals(withCause.getCause(), ExceptionUtils.throwableOfType(withCause, NestableException.class));
assertEquals(withCause.getCause().getCause(), ExceptionUtils.throwableOfType(withCause, ExceptionWithoutCause.class));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, Exception.class));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, Throwable.class));
}
public static class TestThrowable extends Throwable {
private static final long serialVersionUID = 1L;
@Test
public void testThrowableOfType_ThrowableClassInt() {
assertEquals(null, ExceptionUtils.throwableOfType(null, null, 0));
assertEquals(null, ExceptionUtils.throwableOfType(null, NestableException.class, 0));
assertEquals(null, ExceptionUtils.throwableOfType(withoutCause, null));
assertEquals(null, ExceptionUtils.throwableOfType(withoutCause, ExceptionWithCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfType(withoutCause, NestableException.class, 0));
assertEquals(withoutCause, ExceptionUtils.throwableOfType(withoutCause, ExceptionWithoutCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfType(nested, null, 0));
assertEquals(null, ExceptionUtils.throwableOfType(nested, ExceptionWithCause.class, 0));
assertEquals(nested, ExceptionUtils.throwableOfType(nested, NestableException.class, 0));
assertEquals(nested.getCause(), ExceptionUtils.throwableOfType(nested, ExceptionWithoutCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfType(withCause, null));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class, 0));
assertEquals(withCause.getCause(), ExceptionUtils.throwableOfType(withCause, NestableException.class, 0));
assertEquals(withCause.getCause().getCause(), ExceptionUtils.throwableOfType(withCause, ExceptionWithoutCause.class, 0));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class, -1));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class, 0));
assertEquals(null, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class, 1));
assertEquals(null, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class, 9));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, Exception.class, 0));
assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, Throwable.class, 0));
}
@Test
public void testWrapAndUnwrapCheckedException() {
Throwable t = assertThrows(Throwable.class, () -> ExceptionUtils.wrapAndThrow(new IOException()));
assertTrue(ExceptionUtils.hasCause(t, IOException.class));
}
@Test
@ -688,12 +694,6 @@ public class ExceptionUtilsTest {
assertTrue(ExceptionUtils.hasCause(t, RuntimeException.class));
}
@Test
public void testWrapAndUnwrapCheckedException() {
Throwable t = assertThrows(Throwable.class, () -> ExceptionUtils.wrapAndThrow(new IOException()));
assertTrue(ExceptionUtils.hasCause(t, IOException.class));
}
@Test
public void testWrapAndUnwrapThrowable() {
Throwable t = assertThrows(Throwable.class, () -> ExceptionUtils.wrapAndThrow(new TestThrowable()));