Update null handling in ExceptionUtils

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/lang/trunk@137508 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stephen Colebourne 2003-07-26 13:05:21 +00:00
parent 5943137656
commit 96de08907e
2 changed files with 274 additions and 156 deletions

View File

@ -67,6 +67,7 @@
import java.util.StringTokenizer; import java.util.StringTokenizer;
import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.NullArgumentException;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils; import org.apache.commons.lang.SystemUtils;
@ -79,7 +80,7 @@
* @author Stephen Colebourne * @author Stephen Colebourne
* @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a> * @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a>
* @since 1.0 * @since 1.0
* @version $Id: ExceptionUtils.java,v 1.28 2003/07/26 00:43:08 ggregory Exp $ * @version $Id: ExceptionUtils.java,v 1.29 2003/07/26 13:05:21 scolebourne Exp $
*/ */
public class ExceptionUtils { public class ExceptionUtils {
@ -92,8 +93,7 @@ public class ExceptionUtils {
static final String WRAPPED_MARKER = " [wrapped] "; static final String WRAPPED_MARKER = " [wrapped] ";
/** /**
* <p>The names of methods commonly used to access a wrapped * <p>The names of methods commonly used to access a wrapped exception.</p>
* exception.</p>
*/ */
private static String[] CAUSE_METHOD_NAMES = { private static String[] CAUSE_METHOD_NAMES = {
"getCause", "getCause",
@ -107,12 +107,27 @@ public class ExceptionUtils {
}; };
/** /**
* <p>Constructs a new <code>ExceptionUtils</code>. Protected to * <p>The Method object for JDK1.4 getCause.</p>
* discourage instantiation.</p>
*/ */
protected ExceptionUtils() { private static final Method THROWABLE_CAUSE_METHOD;
static {
Method getCauseMethod;
try {
getCauseMethod = Throwable.class.getMethod("getCause", null);
} catch (Exception e) {
getCauseMethod = null;
}
THROWABLE_CAUSE_METHOD = getCauseMethod;
} }
/**
* <p>Public constructor allows an instance of <code>ExceptionUtils</code>
* to be created, although that is not normally necessary.</p>
*/
public ExceptionUtils() {
}
//-----------------------------------------------------------------------
/** /**
* <p>Adds to the list of method names used in the search for <code>Throwable</code> * <p>Adds to the list of method names used in the search for <code>Throwable</code>
* objects.</p> * objects.</p>
@ -121,7 +136,7 @@ protected ExceptionUtils() {
* and empty strings are ignored * and empty strings are ignored
*/ */
public static void addCauseMethodName(String methodName) { public static void addCauseMethodName(String methodName) {
if(StringUtils.isNotEmpty(methodName)) { if (StringUtils.isNotEmpty(methodName)) {
List list = new ArrayList(Arrays.asList(CAUSE_METHOD_NAMES)); List list = new ArrayList(Arrays.asList(CAUSE_METHOD_NAMES));
list.add(methodName); list.add(methodName);
CAUSE_METHOD_NAMES = (String[]) list.toArray(new String[list.size()]); CAUSE_METHOD_NAMES = (String[]) list.toArray(new String[list.size()]);
@ -129,7 +144,7 @@ public static void addCauseMethodName(String methodName) {
} }
/** /**
* <p>Introspects the specified <code>Throwable</code> to obtain the cause.</p> * <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
* *
* <p>The method searches for methods with specific names that return a * <p>The method searches for methods with specific names that return a
* <code>Throwable</code> object. This will pick up most wrapping exceptions, * <code>Throwable</code> object. This will pick up most wrapping exceptions,
@ -154,33 +169,49 @@ public static void addCauseMethodName(String methodName) {
* *
* <p>If none of the above is found, returns <code>null</code>.</p> * <p>If none of the above is found, returns <code>null</code>.</p>
* *
* @param throwable The exception to introspect for a cause. * @param throwable the throwable to introspect for a cause, may be null
* @return The cause of the <code>Throwable</code>. * @return the cause of the <code>Throwable</code>,
* @throws NullPointerException if the throwable is <code>null</code> * <code>null</code> if none found or null throwable input
*/ */
public static Throwable getCause(Throwable throwable) { public static Throwable getCause(Throwable throwable) {
return getCause(throwable, CAUSE_METHOD_NAMES); return getCause(throwable, CAUSE_METHOD_NAMES);
} }
/** /**
* <p>Introspects the specified <code>Throwable</code> to obtain the cause * <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
* using a supplied array of method names.</p>
* *
* @param throwable The exception to introspect for a cause. * <ol>
* @return The cause of the <code>Throwable</code>. * <li>Try known exception types.</p>
* @throws NullPointerException if the method names array is <code>null</code> * <li>Try the supplied array of method names.</p>
* or contains <code>null</code> * <li>Try the field 'detail'.</p>
* @throws NullPointerException if the throwable is <code>null</code> * </ol>
*
* <p>A <code>null</code> set of method names means use the default set.
* A <code>null</code> in the set of method names will be ignored.</p>
*
* @param throwable the throwable to introspect for a cause, may be null
* @param methodNames the method names, null treated as default set
* @return the cause of the <code>Throwable</code>,
* <code>null</code> if none found or null throwable input
*/ */
public static Throwable getCause(Throwable throwable, String[] methodNames) { public static Throwable getCause(Throwable throwable, String[] methodNames) {
if (throwable == null) {
return null;
}
Throwable cause = getCauseUsingWellKnownTypes(throwable); Throwable cause = getCauseUsingWellKnownTypes(throwable);
if (cause == null) { if (cause == null) {
if (methodNames == null) {
methodNames = CAUSE_METHOD_NAMES;
}
for (int i = 0; i < methodNames.length; i++) { for (int i = 0; i < methodNames.length; i++) {
cause = getCauseUsingMethodName(throwable, methodNames[i]); String methodName = methodNames[i];
if (methodName != null) {
cause = getCauseUsingMethodName(throwable, methodName);
if (cause != null) { if (cause != null) {
break; break;
} }
} }
}
if (cause == null) { if (cause == null) {
cause = getCauseUsingFieldName(throwable, "detail"); cause = getCauseUsingFieldName(throwable, "detail");
@ -190,12 +221,15 @@ public static Throwable getCause(Throwable throwable, String[] methodNames) {
} }
/** /**
* <p>Walks through the exception chain to the last element -- the * <p>Introspects the <code>Throwable</code> to obtain the root cause.</p>
* "root" of the tree -- using {@link #getCause(Throwable)}, and *
* <p>This method walks through the exception chain to the last element,
* "root" of the tree, using {@link #getCause(Throwable)}, and
* returns that exception.</p> * returns that exception.</p>
* *
* @param throwable the throwable to get the root cause for * @param throwable the throwable to get the root cause for, may be null
* @return The root cause of the <code>Throwable</code>. * @return the root cause of the <code>Throwable</code>,
* <code>null</code> if none found or null throwable input
*/ */
public static Throwable getRootCause(Throwable throwable) { public static Throwable getRootCause(Throwable throwable) {
Throwable cause = getCause(throwable); Throwable cause = getCause(throwable);
@ -209,13 +243,14 @@ public static Throwable getRootCause(Throwable throwable) {
} }
/** /**
* <p>Finds a <code>Throwable</code> for known types.</p>
*
* <p>Uses <code>instanceof</code> checks to examine the exception, * <p>Uses <code>instanceof</code> checks to examine the exception,
* looking for well known types which could contain chained or * looking for well known types which could contain chained or
* wrapped exceptions.</p> * wrapped exceptions.</p>
* *
* @param throwable the exception to examine * @param throwable the exception to examine
* @return The wrapped exception, or <code>null</code> if not * @return the wrapped exception, or <code>null</code> if not found
* found.
*/ */
private static Throwable getCauseUsingWellKnownTypes(Throwable throwable) { private static Throwable getCauseUsingWellKnownTypes(Throwable throwable) {
if (throwable instanceof Nestable) { if (throwable instanceof Nestable) {
@ -234,8 +269,7 @@ private static Throwable getCauseUsingWellKnownTypes(Throwable throwable) {
* *
* @param throwable the exception to examine * @param throwable the exception to examine
* @param methodName the name of the method to find and invoke * @param methodName the name of the method to find and invoke
* @return The wrapped exception, or <code>null</code> if not * @return the wrapped exception, or <code>null</code> if not found
* found.
*/ */
private static Throwable getCauseUsingMethodName(Throwable throwable, String methodName) { private static Throwable getCauseUsingMethodName(Throwable throwable, String methodName) {
Method method = null; Method method = null;
@ -261,8 +295,7 @@ private static Throwable getCauseUsingMethodName(Throwable throwable, String met
* *
* @param throwable the exception to examine * @param throwable the exception to examine
* @param fieldName the name of the attribute to examine * @param fieldName the name of the attribute to examine
* @return The wrapped exception, or <code>null</code> if not * @return the wrapped exception, or <code>null</code> if not found
* found.
*/ */
private static Throwable getCauseUsingFieldName(Throwable throwable, String fieldName) { private static Throwable getCauseUsingFieldName(Throwable throwable, String fieldName) {
Field field = null; Field field = null;
@ -282,15 +315,78 @@ private static Throwable getCauseUsingFieldName(Throwable throwable, String fiel
return null; return null;
} }
//-----------------------------------------------------------------------
/** /**
* <p>Returns the number of <code>Throwable</code> objects in the * <p>Checks if the Throwable class has a <code>getCause</code> method.</p>
*
* <p>This is true for JDK 1.4 and above.</p>
*
* @return true if Throwable is nestable
*/
public static boolean isThrowableNested() {
return (THROWABLE_CAUSE_METHOD != null);
}
/**
* <p>Checks whether this <code>Throwable</code> class can store a cause.</p>
*
* <p>This method does <b>not</b> check whether it actually does store a cause.<p>
*
* @param throwable the <code>Throwable</code> to examine, may be null
* @return boolean <code>true</code> if nested otherwise <code>false</code>
*/
public static boolean isNestedThrowable(Throwable throwable) {
if (throwable == null) {
return false;
}
if (throwable instanceof Nestable) {
return true;
} else if (throwable instanceof SQLException) {
return true;
} else if (throwable instanceof InvocationTargetException) {
return true;
} else if (isThrowableNested()) {
return true;
}
Class cls = throwable.getClass();
for (int i = 0, isize = CAUSE_METHOD_NAMES.length; i < isize; i++) {
try {
Method method = cls.getMethod(CAUSE_METHOD_NAMES[i], null);
if (method != null) {
return true;
}
} catch (NoSuchMethodException ignored) {
} catch (SecurityException ignored) {
}
}
try {
Field field = cls.getField("detail");
if (field != null) {
return true;
}
} catch (NoSuchFieldException ignored) {
} catch (SecurityException ignored) {
}
return false;
}
//-----------------------------------------------------------------------
/**
* <p>Counts the number of <code>Throwable</code> objects in the
* exception chain.</p> * exception chain.</p>
* *
* @param throwable the exception to inspect * <p>A throwable without cause will return <code>1</code>.
* @return The throwable count. * A throwable with one cause will return <code>2</code> and so on.
* A <code>null</code> throwable will return <code>0</code>.</p>
*
* @param throwable the throwable to inspect, may be null
* @return the count of throwables, zero if null input
*/ */
public static int getThrowableCount(Throwable throwable) { public static int getThrowableCount(Throwable throwable) {
// Count the number of throwables
int count = 0; int count = 0;
while (throwable != null) { while (throwable != null) {
count++; count++;
@ -303,8 +399,14 @@ public static int getThrowableCount(Throwable throwable) {
* <p>Returns the list of <code>Throwable</code> objects in the * <p>Returns the list of <code>Throwable</code> objects in the
* exception chain.</p> * exception chain.</p>
* *
* @param throwable the exception to inspect * <p>A throwable without cause will return an array containing
* @return The list of <code>Throwable</code> objects. * 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 size zero.</p>
*
* @param throwable the throwable to inspect, may be null
* @return the array of throwables, never null
*/ */
public static Throwable[] getThrowables(Throwable throwable) { public static Throwable[] getThrowables(Throwable throwable) {
List list = new ArrayList(); List list = new ArrayList();
@ -315,40 +417,50 @@ public static Throwable[] getThrowables(Throwable throwable) {
return (Throwable[]) list.toArray(new Throwable[list.size()]); return (Throwable[]) list.toArray(new Throwable[list.size()]);
} }
//-----------------------------------------------------------------------
/** /**
* <p>Delegates to {@link #indexOfThrowable(Throwable, Class, int)}, * <p>Returns the (zero based) index of the first <code>Throwable</code>
* starting the search at the beginning of the exception chain.</p> * that matches the specified type in the exception chain.</p>
* *
* @see #indexOfThrowable(Throwable, Class, int) * <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
* @return the index into the throwable chain, -1 if no match or null input
*/ */
public static int indexOfThrowable(Throwable throwable, Class type) { public static int indexOfThrowable(Throwable throwable, Class type) {
return indexOfThrowable(throwable, type, 0); return indexOfThrowable(throwable, type, 0);
} }
/** /**
* <p>Returns the (zero based) index, of the first * <p>Returns the (zero based) index of the first <code>Throwable</code>
* <code>Throwable</code> that matches the specified type in the * that matches the specified type in the exception chain from
* exception chain of <code>Throwable</code> objects with an index * a specified index.</p>
* greater than or equal to the specified index, or
* <code>-1</code> if the type is not found.</p>
* *
* @param throwable the exception to inspect * <p>A <code>null</code> throwable returns <code>-1</code>.
* @param type <code>Class</code> to look for * A <code>null</code> type returns <code>-1</code>.
* @param fromIndex the (zero based) index of the starting * No match in the chain returns <code>-1</code>.
* position in the chain to be searched * A negative start index is treated as zero.
* @return the first occurrence of the type in the chain, or * A start index greater than the number of throwables returns <code>-1</code>.</p>
* <code>-1</code> if the type is not found *
* @throws IndexOutOfBoundsException If the <code>fromIndex</code> * @param throwable the throwable to inspect, may be null
* argument is negative or not less than the count of * @param type the type to search for
* <code>Throwable</code>s in the chain. * @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(Throwable throwable, Class type, int fromIndex) { public static int indexOfThrowable(Throwable throwable, Class type, int fromIndex) {
if (throwable == null) {
return -1;
}
if (fromIndex < 0) { if (fromIndex < 0) {
throw new IndexOutOfBoundsException("Throwable index out of range: " + fromIndex); fromIndex = 0;
} }
Throwable[] throwables = ExceptionUtils.getThrowables(throwable); Throwable[] throwables = ExceptionUtils.getThrowables(throwable);
if (fromIndex >= throwables.length) { if (fromIndex >= throwables.length) {
throw new IndexOutOfBoundsException("Throwable index out of range: " + fromIndex); return -1;
} }
for (int i = fromIndex; i < throwables.length; i++) { for (int i = fromIndex; i < throwables.length; i++) {
if (throwables[i].getClass().equals(type)) { if (throwables[i].getClass().equals(type)) {
@ -358,6 +470,25 @@ public static int indexOfThrowable(Throwable throwable, Class type, int fromInde
return -1; return -1;
} }
//-----------------------------------------------------------------------
/**
* <p>Prints a compact stack trace for the root cause of a throwable
* to <code>System.err</code>.</p>
*
* <p>The compact stack trace starts with the root cause and prints
* stack frames up to the place where it was caught and wrapped.
* Then it prints the wrapped exception and continues with stack frames
* until the wrapper exception is caught and wrapped again, etc.</p>
*
* <p>The method is equivalent to <code>printStackTrace</code> for throwables
* that don't have nested causes.</p>
*
* @param throwable the throwable to output
*/
public static void printRootCauseStackTrace(Throwable throwable) {
printRootCauseStackTrace(throwable, System.err);
}
/** /**
* <p>Prints a compact stack trace for the root cause of a throwable.</p> * <p>Prints a compact stack trace for the root cause of a throwable.</p>
* *
@ -366,11 +497,21 @@ public static int indexOfThrowable(Throwable throwable, Class type, int fromInde
* Then it prints the wrapped exception and continues with stack frames * Then it prints the wrapped exception and continues with stack frames
* until the wrapper exception is caught and wrapped again, etc.</p> * until the wrapper exception is caught and wrapped again, etc.</p>
* *
* <p>The method is equivalent to t.printStackTrace() for throwables * <p>The method is equivalent to <code>printStackTrace</code> for throwables
* that don't have nested causes.</p> * that don't have nested causes.</p>
*
* @param throwable the throwable to output, may be null
* @param stream the stream to output to, may not be null
* @throws IllegalArgumentException if the stream is <code>null</code>
*/ */
public static void printRootCauseStackTrace(Throwable t, PrintStream stream) { public static void printRootCauseStackTrace(Throwable throwable, PrintStream stream) {
String trace[] = getRootCauseStackTrace(t); if (throwable == null) {
return;
}
if (stream == null) {
throw new NullArgumentException("PrintStream");
}
String trace[] = getRootCauseStackTrace(throwable);
for (int i = 0; i < trace.length; i++) { for (int i = 0; i < trace.length; i++) {
stream.println(trace[i]); stream.println(trace[i]);
} }
@ -378,33 +519,47 @@ public static void printRootCauseStackTrace(Throwable t, PrintStream stream) {
} }
/** /**
* <p>Calls <code>printRootCauseStackTraceprintRootCauseStackTrace</code>.</p> * <p>Prints a compact stack trace for the root cause of a throwable.</p>
* Same as: <pre>printRootCauseStackTrace(t, System.err);</pre>
* *
* @see #printRootCauseStackTrace(Throwable,PrintWriter) * <p>The compact stack trace starts with the root cause and prints
* stack frames up to the place where it was caught and wrapped.
* Then it prints the wrapped exception and continues with stack frames
* until the wrapper exception is caught and wrapped again, etc.</p>
*
* <p>The method is equivalent to <code>printStackTrace</code> for throwables
* that don't have nested causes.</p>
*
* @param throwable the throwable to output, may be null
* @param writer the writer to output to, may not be null
* @throws IllegalArgumentException if the writer is <code>null</code>
*/ */
public static void printRootCauseStackTrace(Throwable t) { public static void printRootCauseStackTrace(Throwable throwable, PrintWriter writer) {
printRootCauseStackTrace(t, System.err); if (throwable == null) {
return;
} }
if (writer == null) {
/** throw new NullArgumentException("PrintWriter");
* <p>Same as {@link #printRootCauseStackTrace(Throwable,java.io.PrintStream)}, }
* except it takes a <code>PrintWriter</code> as an argument.</p> String trace[] = getRootCauseStackTrace(throwable);
*/
public static void printRootCauseStackTrace(Throwable t, PrintWriter writer) {
String trace[] = getRootCauseStackTrace(t);
for (int i = 0; i < trace.length; i++) { for (int i = 0; i < trace.length; i++) {
writer.println(trace[i]); writer.println(trace[i]);
} }
writer.flush(); writer.flush();
} }
//-----------------------------------------------------------------------
/** /**
* <p>Creates a compact stack trace for the root cause of the supplied * <p>Creates a compact stack trace for the root cause of the supplied
* <code>Throwable</code>.</p> * <code>Throwable</code>.</p>
*
* @param throwable the throwable to examine, may be null
* @return an array of stack trace frames, never null
*/ */
public static String[] getRootCauseStackTrace(Throwable t) { public static String[] getRootCauseStackTrace(Throwable throwable) {
Throwable throwables[] = getThrowables(t); if (throwable == null) {
return ArrayUtils.EMPTY_STRING_ARRAY;
}
Throwable throwables[] = getThrowables(throwable);
int count = throwables.length; int count = throwables.length;
ArrayList frames = new ArrayList(); ArrayList frames = new ArrayList();
List nextTrace = getStackFrameList(throwables[count - 1]); List nextTrace = getStackFrameList(throwables[count - 1]);
@ -431,8 +586,12 @@ public static String[] getRootCauseStackTrace(Throwable t) {
* *
* @param causeFrames stack trace of a cause throwable * @param causeFrames stack trace of a cause throwable
* @param wrapperFrames stack trace of a wrapper throwable * @param wrapperFrames stack trace of a wrapper throwable
* @throws IllegalArgumentException if either argument is null
*/ */
public static void removeCommonFrames(List causeFrames, List wrapperFrames) { public static void removeCommonFrames(List causeFrames, List wrapperFrames) {
if (causeFrames == null || wrapperFrames == null) {
throw new NullArgumentException("List");
}
int causeFrameIndex = causeFrames.size() - 1; int causeFrameIndex = causeFrames.size() - 1;
int wrapperFrameIndex = wrapperFrames.size() - 1; int wrapperFrameIndex = wrapperFrames.size() - 1;
while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) {
@ -448,94 +607,54 @@ public static void removeCommonFrames(List causeFrames, List wrapperFrames) {
} }
} }
//-----------------------------------------------------------------------
/** /**
* <p>A convenient way of extracting the stack trace from an * <p>Gets the stack trace from a Throwable as a String.</p>
* exception.</p>
* *
* @param t The <code>Throwable</code>. * @param throwable the <code>Throwable</code> to be examined
* @return The stack trace as generated by the exception's * @return the stack trace as generated by the exception's
* <code>printStackTrace(PrintWriter)</code> method. * <code>printStackTrace(PrintWriter)</code> method
*/ */
public static String getStackTrace(Throwable t) { public static String getStackTrace(Throwable throwable) {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true); PrintWriter pw = new PrintWriter(sw, true);
t.printStackTrace(pw); throwable.printStackTrace(pw);
return sw.getBuffer().toString(); return sw.getBuffer().toString();
} }
/** /**
* <p>A way to get the entire nested stack-trace of an throwable.</p> * <p>A way to get the entire nested stack-trace of an throwable.</p>
* *
* @param t The <code>Throwable</code>. * @param throwable the <code>Throwable</code> to be examined
* @return The nested stack trace, with the root cause first. * @return the nested stack trace, with the root cause first
*/ */
public static String getFullStackTrace(Throwable t) { public static String getFullStackTrace(Throwable throwable) {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true); PrintWriter pw = new PrintWriter(sw, true);
Throwable[] ts = getThrowables(t); Throwable[] ts = getThrowables(throwable);
for(int i=0; i<ts.length; i++) { for (int i = 0; i < ts.length; i++) {
ts[i].printStackTrace(pw); ts[i].printStackTrace(pw);
if(isNestedThrowable(ts[i])) { if (isNestedThrowable(ts[i])) {
break; break;
} }
} }
return sw.getBuffer().toString(); return sw.getBuffer().toString();
} }
/** //-----------------------------------------------------------------------
* <p>Returns whether a <code>Throwable</code> is considered nested
* or not.</p>
*
* @param throwable The <code>Throwable</code>.
* @return boolean <code>true</code> if nested otherwise <code>false</code>
*/
public static boolean isNestedThrowable(Throwable throwable) {
if(throwable == null) {
return false;
}
if (throwable instanceof Nestable) {
return true;
} else if (throwable instanceof SQLException) {
return true;
} else if (throwable instanceof InvocationTargetException) {
return true;
}
int sz = CAUSE_METHOD_NAMES.length;
for(int i=0; i<sz; i++) {
try {
Method method = throwable.getClass().getMethod(CAUSE_METHOD_NAMES[i], null);
if(method != null) {
return true;
}
} catch (NoSuchMethodException ignored) {
} catch (SecurityException ignored) {
}
}
try {
Field field = throwable.getClass().getField("detail");
if(field != null) {
return true;
}
} catch (NoSuchFieldException ignored) {
} catch (SecurityException ignored) {
}
return false;
}
/** /**
* <p>Captures the stack trace associated with the specified * <p>Captures the stack trace associated with the specified
* <code>Throwable</code> object, decomposing it into a list of * <code>Throwable</code> object, decomposing it into a list of
* stack frames.</p> * stack frames.</p>
* *
* @param t The <code>Throwable</code>. * @param throwable the <code>Throwable</code> to exaamine, may be null
* @return An array of strings describing each stack frame. * @return an array of strings describing each stack frame, never null
*/ */
public static String[] getStackFrames(Throwable t) { public static String[] getStackFrames(Throwable throwable) {
return getStackFrames(getStackTrace(t)); if (throwable == null) {
return ArrayUtils.EMPTY_STRING_ARRAY;
}
return getStackFrames(getStackTrace(throwable));
} }
/** /**
@ -551,8 +670,7 @@ static String[] getStackFrames(String stackTrace) {
while (frames.hasMoreTokens()) { while (frames.hasMoreTokens()) {
list.add(frames.nextToken()); list.add(frames.nextToken());
} }
return (String[]) list.toArray(new String[] { return (String[]) list.toArray(new String[list.size()]);
});
} }
/** /**
@ -586,19 +704,4 @@ static List getStackFrameList(Throwable t) {
return list; return list;
} }
private static Object getCauseMethod = null;
static {
try {
getCauseMethod = Throwable.class.getMethod("getCause", null);
} catch (Exception e) {
// ignore
}
}
/**
* <p>Checks if the Throwable class has a <code>getCause</code> method.</p>
*/
public static boolean isThrowableNested() {
return (getCauseMethod != null);
}
} }

View File

@ -79,7 +79,7 @@
* @author Sean C. Sullivan * @author Sean C. Sullivan
* @author Stephen Colebourne * @author Stephen Colebourne
* @since 1.0 * @since 1.0
* @version $Id: NestableDelegate.java,v 1.16 2003/07/25 23:05:22 ggregory Exp $ * @version $Id: NestableDelegate.java,v 1.17 2003/07/26 13:05:21 scolebourne Exp $
*/ */
public class NestableDelegate implements Serializable { public class NestableDelegate implements Serializable {
@ -99,11 +99,13 @@ public class NestableDelegate implements Serializable {
/** /**
* Whether to print the stack trace top-down. * Whether to print the stack trace top-down.
* This public flag may be set by calling code, typically in initialisation.
*/ */
public static boolean topDown = true; public static boolean topDown = true;
/** /**
* Whether to trim the repeated stack trace. * Whether to trim the repeated stack trace.
* This public flag may be set by calling code, typically in initialisation.
*/ */
public static boolean trimStackFrames = true; public static boolean trimStackFrames = true;
@ -253,7 +255,20 @@ public Throwable[] getThrowables() {
* chain * chain
*/ */
public int indexOfThrowable(Class type, int fromIndex) { public int indexOfThrowable(Class type, int fromIndex) {
return ExceptionUtils.indexOfThrowable(this.nestable, type, fromIndex); if (fromIndex < 0) {
throw new IndexOutOfBoundsException("The start index was out of bounds: " + fromIndex);
}
Throwable[] throwables = ExceptionUtils.getThrowables(this.nestable);
if (fromIndex >= throwables.length) {
throw new IndexOutOfBoundsException("The start index was out of bounds: "
+ fromIndex + " >= " + throwables.length);
}
for (int i = fromIndex; i < throwables.length; i++) {
if (throwables[i].getClass().equals(type)) {
return i;
}
}
return -1;
} }
/** /**