scolebourne 2003/07/26 06:05:22
Modified: lang/src/java/org/apache/commons/lang/exception
NestableDelegate.java ExceptionUtils.java
Log:
Update null handling in ExceptionUtils
Revision Changes Path
1.17 +17 -2
jakarta-commons/lang/src/java/org/apache/commons/lang/exception/NestableDelegate.java
Index: NestableDelegate.java
===================================================================
RCS file:
/home/cvs/jakarta-commons/lang/src/java/org/apache/commons/lang/exception/NestableDelegate.java,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -r1.16 -r1.17
--- NestableDelegate.java 25 Jul 2003 23:05:22 -0000 1.16
+++ NestableDelegate.java 26 Jul 2003 13:05:21 -0000 1.17
@@ -99,11 +99,13 @@
/**
* 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;
/**
* Whether to trim the repeated stack trace.
+ * This public flag may be set by calling code, typically in initialisation.
*/
public static boolean trimStackFrames = true;
@@ -253,7 +255,20 @@
* chain
*/
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;
}
/**
1.29 +261 -158
jakarta-commons/lang/src/java/org/apache/commons/lang/exception/ExceptionUtils.java
Index: ExceptionUtils.java
===================================================================
RCS file:
/home/cvs/jakarta-commons/lang/src/java/org/apache/commons/lang/exception/ExceptionUtils.java,v
retrieving revision 1.28
retrieving revision 1.29
diff -u -r1.28 -r1.29
--- ExceptionUtils.java 26 Jul 2003 00:43:08 -0000 1.28
+++ ExceptionUtils.java 26 Jul 2003 13:05:21 -0000 1.29
@@ -67,6 +67,7 @@
import java.util.StringTokenizer;
import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.NullArgumentException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
@@ -92,8 +93,7 @@
static final String WRAPPED_MARKER = " [wrapped] ";
/**
- * <p>The names of methods commonly used to access a wrapped
- * exception.</p>
+ * <p>The names of methods commonly used to access a wrapped exception.</p>
*/
private static String[] CAUSE_METHOD_NAMES = {
"getCause",
@@ -107,12 +107,27 @@
};
/**
- * <p>Constructs a new <code>ExceptionUtils</code>. Protected to
- * discourage instantiation.</p>
+ * <p>The Method object for JDK1.4 getCause.</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>
* objects.</p>
@@ -121,7 +136,7 @@
* and empty strings are ignored
*/
public static void addCauseMethodName(String methodName) {
- if(StringUtils.isNotEmpty(methodName)) {
+ if (StringUtils.isNotEmpty(methodName)) {
List list = new ArrayList(Arrays.asList(CAUSE_METHOD_NAMES));
list.add(methodName);
CAUSE_METHOD_NAMES = (String[]) list.toArray(new String[list.size()]);
@@ -129,7 +144,7 @@
}
/**
- * <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
* <code>Throwable</code> object. This will pick up most wrapping exceptions,
@@ -154,31 +169,47 @@
*
* <p>If none of the above is found, returns <code>null</code>.</p>
*
- * @param throwable The exception to introspect for a cause.
- * @return The cause of the <code>Throwable</code>.
- * @throws NullPointerException if the throwable is <code>null</code>
+ * @param throwable the throwable to introspect for a cause, may be null
+ * @return the cause of the <code>Throwable</code>,
+ * <code>null</code> if none found or null throwable input
*/
public static Throwable getCause(Throwable throwable) {
return getCause(throwable, CAUSE_METHOD_NAMES);
}
/**
- * <p>Introspects the specified <code>Throwable</code> to obtain the cause
- * using a supplied array of method names.</p>
+ * <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
+ *
+ * <ol>
+ * <li>Try known exception types.</p>
+ * <li>Try the supplied array of method names.</p>
+ * <li>Try the field 'detail'.</p>
+ * </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 exception to introspect for a cause.
- * @return The cause of the <code>Throwable</code>.
- * @throws NullPointerException if the method names array is <code>null</code>
- * or contains <code>null</code>
- * @throws NullPointerException if the throwable is <code>null</code>
+ * @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) {
+ if (throwable == null) {
+ return null;
+ }
Throwable cause = getCauseUsingWellKnownTypes(throwable);
if (cause == null) {
+ if (methodNames == null) {
+ methodNames = CAUSE_METHOD_NAMES;
+ }
for (int i = 0; i < methodNames.length; i++) {
- cause = getCauseUsingMethodName(throwable, methodNames[i]);
- if (cause != null) {
- break;
+ String methodName = methodNames[i];
+ if (methodName != null) {
+ cause = getCauseUsingMethodName(throwable, methodName);
+ if (cause != null) {
+ break;
+ }
}
}
@@ -190,12 +221,15 @@
}
/**
- * <p>Walks through the exception chain to the last element -- the
- * "root" of the tree -- using [EMAIL PROTECTED] #getCause(Throwable)}, and
+ * <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 [EMAIL PROTECTED] #getCause(Throwable)}, and
* returns that exception.</p>
*
- * @param throwable the throwable to get the root cause for
- * @return The root cause of the <code>Throwable</code>.
+ * @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 none found or null throwable input
*/
public static Throwable getRootCause(Throwable throwable) {
Throwable cause = getCause(throwable);
@@ -209,13 +243,14 @@
}
/**
+ * <p>Finds a <code>Throwable</code> for known types.</p>
+ *
* <p>Uses <code>instanceof</code> checks to examine the exception,
* looking for well known types which could contain chained or
* wrapped exceptions.</p>
*
* @param throwable the exception to examine
- * @return The wrapped exception, or <code>null</code> if not
- * found.
+ * @return the wrapped exception, or <code>null</code> if not found
*/
private static Throwable getCauseUsingWellKnownTypes(Throwable throwable) {
if (throwable instanceof Nestable) {
@@ -234,8 +269,7 @@
*
* @param throwable the exception to examine
* @param methodName the name of the method to find and invoke
- * @return The wrapped exception, or <code>null</code> if not
- * found.
+ * @return the wrapped exception, or <code>null</code> if not found
*/
private static Throwable getCauseUsingMethodName(Throwable throwable, String
methodName) {
Method method = null;
@@ -261,8 +295,7 @@
*
* @param throwable the exception to examine
* @param fieldName the name of the attribute to examine
- * @return The wrapped exception, or <code>null</code> if not
- * found.
+ * @return the wrapped exception, or <code>null</code> if not found
*/
private static Throwable getCauseUsingFieldName(Throwable throwable, String
fieldName) {
Field field = null;
@@ -282,15 +315,78 @@
return null;
}
+ //-----------------------------------------------------------------------
/**
- * <p>Returns the number of <code>Throwable</code> objects in the
- * exception chain.</p>
+ * <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 exception to inspect
- * @return The throwable count.
+ * @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>
+ *
+ * <p>A throwable without cause will return <code>1</code>.
+ * 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) {
- // Count the number of throwables
int count = 0;
while (throwable != null) {
count++;
@@ -302,9 +398,15 @@
/**
* <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 size zero.</p>
*
- * @param throwable the exception to inspect
- * @return The list of <code>Throwable</code> objects.
+ * @param throwable the throwable to inspect, may be null
+ * @return the array of throwables, never null
*/
public static Throwable[] getThrowables(Throwable throwable) {
List list = new ArrayList();
@@ -315,40 +417,50 @@
return (Throwable[]) list.toArray(new Throwable[list.size()]);
}
+ //-----------------------------------------------------------------------
/**
- * <p>Delegates to [EMAIL PROTECTED] #indexOfThrowable(Throwable, Class, int)},
- * starting the search at the beginning of the exception chain.</p>
- *
- * @see #indexOfThrowable(Throwable, Class, int)
+ * <p>Returns the (zero based) index of the first <code>Throwable</code>
+ * that matches the specified type 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>
+ *
+ * @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) {
return indexOfThrowable(throwable, type, 0);
}
/**
- * <p>Returns the (zero based) index, of the first
- * <code>Throwable</code> that matches the specified type in the
- * exception chain of <code>Throwable</code> objects with an index
- * 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
- * @param type <code>Class</code> to look for
- * @param fromIndex the (zero based) index of the starting
- * position in the chain to be searched
- * @return the first occurrence of the type in the chain, or
- * <code>-1</code> if the type is not found
- * @throws IndexOutOfBoundsException If the <code>fromIndex</code>
- * argument is negative or not less than the count of
- * <code>Throwable</code>s in the chain.
+ * <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.</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
+ * @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) {
+ if (throwable == null) {
+ return -1;
+ }
if (fromIndex < 0) {
- throw new IndexOutOfBoundsException("Throwable index out of range: " +
fromIndex);
+ fromIndex = 0;
}
Throwable[] throwables = ExceptionUtils.getThrowables(throwable);
if (fromIndex >= throwables.length) {
- throw new IndexOutOfBoundsException("Throwable index out of range: " +
fromIndex);
+ return -1;
}
for (int i = fromIndex; i < throwables.length; i++) {
if (throwables[i].getClass().equals(type)) {
@@ -358,6 +470,25 @@
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>
*
@@ -366,11 +497,21 @@
* 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 t.printStackTrace() for throwables
+ * <p>The method is equivalent to <code>printStackTrace</code> for throwables
* that don't have nested causes.</p>
- */
- public static void printRootCauseStackTrace(Throwable t, PrintStream stream) {
- String trace[] = getRootCauseStackTrace(t);
+ *
+ * @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 throwable, PrintStream
stream) {
+ if (throwable == null) {
+ return;
+ }
+ if (stream == null) {
+ throw new NullArgumentException("PrintStream");
+ }
+ String trace[] = getRootCauseStackTrace(throwable);
for (int i = 0; i < trace.length; i++) {
stream.println(trace[i]);
}
@@ -378,33 +519,47 @@
}
/**
- * <p>Calls <code>printRootCauseStackTraceprintRootCauseStackTrace</code>.</p>
- * Same as: <pre>printRootCauseStackTrace(t, System.err);</pre>
+ * <p>Prints a compact stack trace for the root cause of a throwable.</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>
*
- * @see #printRootCauseStackTrace(Throwable,PrintWriter)
- */
- public static void printRootCauseStackTrace(Throwable t) {
- printRootCauseStackTrace(t, System.err);
- }
-
- /**
- * <p>Same as [EMAIL PROTECTED]
#printRootCauseStackTrace(Throwable,java.io.PrintStream)},
- * except it takes a <code>PrintWriter</code> as an argument.</p>
- */
- public static void printRootCauseStackTrace(Throwable t, PrintWriter writer) {
- String trace[] = getRootCauseStackTrace(t);
+ * @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 throwable, PrintWriter
writer) {
+ if (throwable == null) {
+ return;
+ }
+ if (writer == null) {
+ throw new NullArgumentException("PrintWriter");
+ }
+ String trace[] = getRootCauseStackTrace(throwable);
for (int i = 0; i < trace.length; i++) {
writer.println(trace[i]);
}
writer.flush();
}
+ //-----------------------------------------------------------------------
/**
* <p>Creates a compact stack trace for the root cause of the supplied
* <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) {
- Throwable throwables[] = getThrowables(t);
+ public static String[] getRootCauseStackTrace(Throwable throwable) {
+ if (throwable == null) {
+ return ArrayUtils.EMPTY_STRING_ARRAY;
+ }
+ Throwable throwables[] = getThrowables(throwable);
int count = throwables.length;
ArrayList frames = new ArrayList();
List nextTrace = getStackFrameList(throwables[count - 1]);
@@ -430,9 +585,13 @@
* <p>Removes common frames from the cause trace given the two stack traces.</p>
*
* @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) {
+ if (causeFrames == null || wrapperFrames == null) {
+ throw new NullArgumentException("List");
+ }
int causeFrameIndex = causeFrames.size() - 1;
int wrapperFrameIndex = wrapperFrames.size() - 1;
while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) {
@@ -448,94 +607,54 @@
}
}
+ //-----------------------------------------------------------------------
/**
- * <p>A convenient way of extracting the stack trace from an
- * exception.</p>
+ * <p>Gets the stack trace from a Throwable as a String.</p>
*
- * @param t The <code>Throwable</code>.
- * @return The stack trace as generated by the exception's
- * <code>printStackTrace(PrintWriter)</code> method.
+ * @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(Throwable t) {
+ public static String getStackTrace(Throwable throwable) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
- t.printStackTrace(pw);
+ throwable.printStackTrace(pw);
return sw.getBuffer().toString();
}
/**
* <p>A way to get the entire nested stack-trace of an throwable.</p>
*
- * @param t The <code>Throwable</code>.
- * @return The nested stack trace, with the root cause first.
+ * @param throwable the <code>Throwable</code> to be examined
+ * @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();
PrintWriter pw = new PrintWriter(sw, true);
- Throwable[] ts = getThrowables(t);
- for(int i=0; i<ts.length; i++) {
+ Throwable[] ts = getThrowables(throwable);
+ for (int i = 0; i < ts.length; i++) {
ts[i].printStackTrace(pw);
- if(isNestedThrowable(ts[i])) {
+ if (isNestedThrowable(ts[i])) {
break;
}
}
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
* <code>Throwable</code> object, decomposing it into a list of
* stack frames.</p>
*
- * @param t The <code>Throwable</code>.
- * @return An array of strings describing each stack frame.
+ * @param throwable the <code>Throwable</code> to exaamine, may be null
+ * @return an array of strings describing each stack frame, never null
*/
- public static String[] getStackFrames(Throwable t) {
- return getStackFrames(getStackTrace(t));
+ public static String[] getStackFrames(Throwable throwable) {
+ if (throwable == null) {
+ return ArrayUtils.EMPTY_STRING_ARRAY;
+ }
+ return getStackFrames(getStackTrace(throwable));
}
/**
@@ -551,8 +670,7 @@
while (frames.hasMoreTokens()) {
list.add(frames.nextToken());
}
- return (String[]) list.toArray(new String[] {
- });
+ return (String[]) list.toArray(new String[list.size()]);
}
/**
@@ -586,19 +704,4 @@
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);
- }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]