KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > exception > ExceptionUtils


1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2002-2003 The Apache Software Foundation. All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in
16  * the documentation and/or other materials provided with the
17  * distribution.
18  *
19  * 3. The end-user documentation included with the redistribution, if
20  * any, must include the following acknowledgement:
21  * "This product includes software developed by the
22  * Apache Software Foundation (http://www.apache.org/)."
23  * Alternately, this acknowledgement may appear in the software itself,
24  * if and wherever such third-party acknowledgements normally appear.
25  *
26  * 4. The names "The Jakarta Project", "Commons", and "Apache Software
27  * Foundation" must not be used to endorse or promote products derived
28  * from this software without prior written permission. For written
29  * permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache"
32  * nor may "Apache" appear in their names without prior written
33  * permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation. For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  */

54 package org.hibernate.exception;
55
56 import org.hibernate.util.ArrayHelper;
57
58 import java.io.PrintStream JavaDoc;
59 import java.io.PrintWriter JavaDoc;
60 import java.io.StringWriter JavaDoc;
61 import java.lang.reflect.Field JavaDoc;
62 import java.lang.reflect.InvocationTargetException JavaDoc;
63 import java.lang.reflect.Method JavaDoc;
64 import java.sql.SQLException JavaDoc;
65 import java.util.ArrayList JavaDoc;
66 import java.util.LinkedList JavaDoc;
67 import java.util.List JavaDoc;
68 import java.util.StringTokenizer JavaDoc;
69
70 /**
71  * <p>Provides utilities for manipulating and examining
72  * <code>Throwable</code> objects.</p>
73  *
74  * @author <a HREF="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
75  * @author Dmitri Plotnikov
76  * @author Stephen Colebourne
77  * @author <a HREF="mailto:ggregory@seagullsw.com">Gary Gregory</a>
78  * @author Pete Gieser
79  * @version $Id: ExceptionUtils.java,v 1.2 2004/11/21 00:11:27 pgmjsd Exp $
80  * @since 1.0
81  */

82 public final class ExceptionUtils {
83
84     private static final String JavaDoc LINE_SEPARATOR = System.getProperty( "line.separator" );
85
86     /**
87      * <p>Used when printing stack frames to denote the start of a
88      * wrapped exception.</p>
89      * <p/>
90      * <p>Package private for accessibility by test suite.</p>
91      */

92     static final String JavaDoc WRAPPED_MARKER = " [wrapped] ";
93
94     /**
95      * <p>The names of methods commonly used to access a wrapped exception.</p>
96      */

97     private static final String JavaDoc[] CAUSE_METHOD_NAMES = {
98         "getCause",
99         "getNextException",
100         "getTargetException",
101         "getException",
102         "getSourceException",
103         "getRootCause",
104         "getCausedByException",
105         "getNested"
106     };
107
108     /**
109      * <p>The Method object for JDK1.4 getCause.</p>
110      */

111     private static final Method JavaDoc THROWABLE_CAUSE_METHOD;
112
113     static {
114         Method JavaDoc getCauseMethod;
115         try {
116             getCauseMethod = Throwable JavaDoc.class.getMethod( "getCause", null );
117         }
118         catch ( Exception JavaDoc e ) {
119             getCauseMethod = null;
120         }
121         THROWABLE_CAUSE_METHOD = getCauseMethod;
122     }
123
124     private ExceptionUtils() {
125     }
126
127     //-----------------------------------------------------------------------
128
/**
129      * <p>Adds to the list of method names used in the search for <code>Throwable</code>
130      * objects.</p>
131      *
132      * @param methodName the methodName to add to the list, <code>null</code>
133      * and empty strings are ignored
134      * @since 2.0
135      */

136     /*public static void addCauseMethodName(String methodName) {
137         if ( StringHelper.isNotEmpty(methodName) ) {
138             List list = new ArrayList( Arrays.asList(CAUSE_METHOD_NAMES );
139             list.add(methodName);
140             CAUSE_METHOD_NAMES = (String[]) list.toArray(new String[list.size()]);
141         }
142     }*/

143
144     /**
145      * <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
146      * <p/>
147      * <p>The method searches for methods with specific names that return a
148      * <code>Throwable</code> object. This will pick up most wrapping exceptions,
149      * including those from JDK 1.4, and
150      * {@link org.apache.commons.lang.exception.NestableException NestableException}.
151      * The method names can be added to using {@link #addCauseMethodName(String)}.</p>
152      * <p/>
153      * <p>The default list searched for are:</p>
154      * <ul>
155      * <li><code>getCause()</code></li>
156      * <li><code>getNextException()</code></li>
157      * <li><code>getTargetException()</code></li>
158      * <li><code>getException()</code></li>
159      * <li><code>getSourceException()</code></li>
160      * <li><code>getRootCause()</code></li>
161      * <li><code>getCausedByException()</code></li>
162      * <li><code>getNested()</code></li>
163      * </ul>
164      * <p/>
165      * <p>In the absence of any such method, the object is inspected for a
166      * <code>detail</code> field assignable to a <code>Throwable</code>.</p>
167      * <p/>
168      * <p>If none of the above is found, returns <code>null</code>.</p>
169      *
170      * @param throwable the throwable to introspect for a cause, may be null
171      * @return the cause of the <code>Throwable</code>,
172      * <code>null</code> if none found or null throwable input
173      */

174     public static Throwable JavaDoc getCause(Throwable JavaDoc throwable) {
175         return getCause( throwable, CAUSE_METHOD_NAMES );
176     }
177
178     /**
179      * <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
180      * <p/>
181      * <ol>
182      * <li>Try known exception types.</li>
183      * <li>Try the supplied array of method names.</li>
184      * <li>Try the field 'detail'.</li>
185      * </ol>
186      * <p/>
187      * <p>A <code>null</code> set of method names means use the default set.
188      * A <code>null</code> in the set of method names will be ignored.</p>
189      *
190      * @param throwable the throwable to introspect for a cause, may be null
191      * @param methodNames the method names, null treated as default set
192      * @return the cause of the <code>Throwable</code>,
193      * <code>null</code> if none found or null throwable input
194      */

195     public static Throwable JavaDoc getCause(Throwable JavaDoc throwable, String JavaDoc[] methodNames) {
196         if ( throwable == null ) {
197             return null;
198         }
199         Throwable JavaDoc cause = getCauseUsingWellKnownTypes( throwable );
200         if ( cause == null ) {
201             if ( methodNames == null ) {
202                 methodNames = CAUSE_METHOD_NAMES;
203             }
204             for ( int i = 0; i < methodNames.length; i++ ) {
205                 String JavaDoc methodName = methodNames[i];
206                 if ( methodName != null ) {
207                     cause = getCauseUsingMethodName( throwable, methodName );
208                     if ( cause != null ) {
209                         break;
210                     }
211                 }
212             }
213
214             if ( cause == null ) {
215                 cause = getCauseUsingFieldName( throwable, "detail" );
216             }
217         }
218         return cause;
219     }
220
221     /**
222      * <p>Introspects the <code>Throwable</code> to obtain the root cause.</p>
223      * <p/>
224      * <p>This method walks through the exception chain to the last element,
225      * "root" of the tree, using {@link #getCause(Throwable)}, and
226      * returns that exception.</p>
227      *
228      * @param throwable the throwable to get the root cause for, may be null
229      * @return the root cause of the <code>Throwable</code>,
230      * <code>null</code> if none found or null throwable input
231      */

232     public static Throwable JavaDoc getRootCause(Throwable JavaDoc throwable) {
233         Throwable JavaDoc cause = getCause( throwable );
234         if ( cause != null ) {
235             throwable = cause;
236             while ( ( throwable = getCause( throwable ) ) != null ) {
237                 cause = throwable;
238             }
239         }
240         return cause;
241     }
242
243     /**
244      * <p>Finds a <code>Throwable</code> for known types.</p>
245      * <p/>
246      * <p>Uses <code>instanceof</code> checks to examine the exception,
247      * looking for well known types which could contain chained or
248      * wrapped exceptions.</p>
249      *
250      * @param throwable the exception to examine
251      * @return the wrapped exception, or <code>null</code> if not found
252      */

253     private static Throwable JavaDoc getCauseUsingWellKnownTypes(Throwable JavaDoc throwable) {
254         if ( throwable instanceof Nestable ) {
255             return ( ( Nestable ) throwable ).getCause();
256         }
257         else if ( throwable instanceof SQLException JavaDoc ) {
258             return ( ( SQLException JavaDoc ) throwable ).getNextException();
259         }
260         else if ( throwable instanceof InvocationTargetException JavaDoc ) {
261             return ( ( InvocationTargetException JavaDoc ) throwable ).getTargetException();
262         }
263         else {
264             return null;
265         }
266     }
267
268     /**
269      * <p>Finds a <code>Throwable</code> by method name.</p>
270      *
271      * @param throwable the exception to examine
272      * @param methodName the name of the method to find and invoke
273      * @return the wrapped exception, or <code>null</code> if not found
274      */

275     private static Throwable JavaDoc getCauseUsingMethodName(Throwable JavaDoc throwable, String JavaDoc methodName) {
276         Method JavaDoc method = null;
277         try {
278             method = throwable.getClass().getMethod( methodName, null );
279         }
280         catch ( NoSuchMethodException JavaDoc ignored ) {
281         }
282         catch ( SecurityException JavaDoc ignored ) {
283         }
284
285         if ( method != null && Throwable JavaDoc.class.isAssignableFrom( method.getReturnType() ) ) {
286             try {
287                 return ( Throwable JavaDoc ) method.invoke( throwable, ArrayHelper.EMPTY_OBJECT_ARRAY );
288             }
289             catch ( IllegalAccessException JavaDoc ignored ) {
290             }
291             catch ( IllegalArgumentException JavaDoc ignored ) {
292             }
293             catch ( InvocationTargetException JavaDoc ignored ) {
294             }
295         }
296         return null;
297     }
298
299     /**
300      * <p>Finds a <code>Throwable</code> by field name.</p>
301      *
302      * @param throwable the exception to examine
303      * @param fieldName the name of the attribute to examine
304      * @return the wrapped exception, or <code>null</code> if not found
305      */

306     private static Throwable JavaDoc getCauseUsingFieldName(Throwable JavaDoc throwable, String JavaDoc fieldName) {
307         Field JavaDoc field = null;
308         try {
309             field = throwable.getClass().getField( fieldName );
310         }
311         catch ( NoSuchFieldException JavaDoc ignored ) {
312         }
313         catch ( SecurityException JavaDoc ignored ) {
314         }
315
316         if ( field != null && Throwable JavaDoc.class.isAssignableFrom( field.getType() ) ) {
317             try {
318                 return ( Throwable JavaDoc ) field.get( throwable );
319             }
320             catch ( IllegalAccessException JavaDoc ignored ) {
321             }
322             catch ( IllegalArgumentException JavaDoc ignored ) {
323             }
324         }
325         return null;
326     }
327
328     //-----------------------------------------------------------------------
329
/**
330      * <p>Checks if the Throwable class has a <code>getCause</code> method.</p>
331      * <p/>
332      * <p>This is true for JDK 1.4 and above.</p>
333      *
334      * @return true if Throwable is nestable
335      * @since 2.0
336      */

337     public static boolean isThrowableNested() {
338         return ( THROWABLE_CAUSE_METHOD != null );
339     }
340
341     /**
342      * <p>Checks whether this <code>Throwable</code> class can store a cause.</p>
343      * <p/>
344      * <p>This method does <b>not</b> check whether it actually does store a cause.<p>
345      *
346      * @param throwable the <code>Throwable</code> to examine, may be null
347      * @return boolean <code>true</code> if nested otherwise <code>false</code>
348      * @since 2.0
349      */

350     public static boolean isNestedThrowable(Throwable JavaDoc throwable) {
351         if ( throwable == null ) {
352             return false;
353         }
354
355         if ( throwable instanceof Nestable ) {
356             return true;
357         }
358         else if ( throwable instanceof SQLException JavaDoc ) {
359             return true;
360         }
361         else if ( throwable instanceof InvocationTargetException JavaDoc ) {
362             return true;
363         }
364         else if ( isThrowableNested() ) {
365             return true;
366         }
367
368         Class JavaDoc cls = throwable.getClass();
369         for ( int i = 0, isize = CAUSE_METHOD_NAMES.length; i < isize; i++ ) {
370             try {
371                 Method JavaDoc method = cls.getMethod( CAUSE_METHOD_NAMES[i], null );
372                 if ( method != null && Throwable JavaDoc.class.isAssignableFrom( method.getReturnType() ) ) {
373                     return true;
374                 }
375             }
376             catch ( NoSuchMethodException JavaDoc ignored ) {
377             }
378             catch ( SecurityException JavaDoc ignored ) {
379             }
380         }
381
382         try {
383             Field JavaDoc field = cls.getField( "detail" );
384             if ( field != null ) {
385                 return true;
386             }
387         }
388         catch ( NoSuchFieldException JavaDoc ignored ) {
389         }
390         catch ( SecurityException JavaDoc ignored ) {
391         }
392
393         return false;
394     }
395
396     //-----------------------------------------------------------------------
397
/**
398      * <p>Counts the number of <code>Throwable</code> objects in the
399      * exception chain.</p>
400      * <p/>
401      * <p>A throwable without cause will return <code>1</code>.
402      * A throwable with one cause will return <code>2</code> and so on.
403      * A <code>null</code> throwable will return <code>0</code>.</p>
404      *
405      * @param throwable the throwable to inspect, may be null
406      * @return the count of throwables, zero if null input
407      */

408     public static int getThrowableCount(Throwable JavaDoc throwable) {
409         int count = 0;
410         while ( throwable != null ) {
411             count++;
412             throwable = ExceptionUtils.getCause( throwable );
413         }
414         return count;
415     }
416
417     /**
418      * <p>Returns the list of <code>Throwable</code> objects in the
419      * exception chain.</p>
420      * <p/>
421      * <p>A throwable without cause will return an array containing
422      * one element - the input throwable.
423      * A throwable with one cause will return an array containing
424      * two elements. - the input throwable and the cause throwable.
425      * A <code>null</code> throwable will return an array size zero.</p>
426      *
427      * @param throwable the throwable to inspect, may be null
428      * @return the array of throwables, never null
429      */

430     public static Throwable JavaDoc[] getThrowables(Throwable JavaDoc throwable) {
431         List JavaDoc list = new ArrayList JavaDoc();
432         while ( throwable != null ) {
433             list.add( throwable );
434             throwable = ExceptionUtils.getCause( throwable );
435         }
436         return ( Throwable JavaDoc[] ) list.toArray( new Throwable JavaDoc[list.size()] );
437     }
438
439     //-----------------------------------------------------------------------
440
/**
441      * <p>Returns the (zero based) index of the first <code>Throwable</code>
442      * that matches the specified type in the exception chain.</p>
443      * <p/>
444      * <p>A <code>null</code> throwable returns <code>-1</code>.
445      * A <code>null</code> type returns <code>-1</code>.
446      * No match in the chain returns <code>-1</code>.</p>
447      *
448      * @param throwable the throwable to inspect, may be null
449      * @param type the type to search for
450      * @return the index into the throwable chain, -1 if no match or null input
451      */

452     public static int indexOfThrowable(Throwable JavaDoc throwable, Class JavaDoc type) {
453         return indexOfThrowable( throwable, type, 0 );
454     }
455
456     /**
457      * <p>Returns the (zero based) index of the first <code>Throwable</code>
458      * that matches the specified type in the exception chain from
459      * a specified index.</p>
460      * <p/>
461      * <p>A <code>null</code> throwable returns <code>-1</code>.
462      * A <code>null</code> type returns <code>-1</code>.
463      * No match in the chain returns <code>-1</code>.
464      * A negative start index is treated as zero.
465      * A start index greater than the number of throwables returns <code>-1</code>.</p>
466      *
467      * @param throwable the throwable to inspect, may be null
468      * @param type the type to search for
469      * @param fromIndex the (zero based) index of the starting position,
470      * negative treated as zero, larger than chain size returns -1
471      * @return the index into the throwable chain, -1 if no match or null input
472      */

473     public static int indexOfThrowable(Throwable JavaDoc throwable, Class JavaDoc type, int fromIndex) {
474         if ( throwable == null ) {
475             return -1;
476         }
477         if ( fromIndex < 0 ) {
478             fromIndex = 0;
479         }
480         Throwable JavaDoc[] throwables = ExceptionUtils.getThrowables( throwable );
481         if ( fromIndex >= throwables.length ) {
482             return -1;
483         }
484         for ( int i = fromIndex; i < throwables.length; i++ ) {
485             if ( throwables[i].getClass().equals( type ) ) {
486                 return i;
487             }
488         }
489         return -1;
490     }
491
492     //-----------------------------------------------------------------------
493
/**
494      * <p>Prints a compact stack trace for the root cause of a throwable
495      * to <code>System.err</code>.</p>
496      * <p/>
497      * <p>The compact stack trace starts with the root cause and prints
498      * stack frames up to the place where it was caught and wrapped.
499      * Then it prints the wrapped exception and continues with stack frames
500      * until the wrapper exception is caught and wrapped again, etc.</p>
501      * <p/>
502      * <p>The method is equivalent to <code>printStackTrace</code> for throwables
503      * that don't have nested causes.</p>
504      *
505      * @param throwable the throwable to output
506      * @since 2.0
507      */

508     public static void printRootCauseStackTrace(Throwable JavaDoc throwable) {
509         printRootCauseStackTrace( throwable, System.err );
510     }
511
512     /**
513      * <p>Prints a compact stack trace for the root cause of a throwable.</p>
514      * <p/>
515      * <p>The compact stack trace starts with the root cause and prints
516      * stack frames up to the place where it was caught and wrapped.
517      * Then it prints the wrapped exception and continues with stack frames
518      * until the wrapper exception is caught and wrapped again, etc.</p>
519      * <p/>
520      * <p>The method is equivalent to <code>printStackTrace</code> for throwables
521      * that don't have nested causes.</p>
522      *
523      * @param throwable the throwable to output, may be null
524      * @param stream the stream to output to, may not be null
525      * @throws IllegalArgumentException if the stream is <code>null</code>
526      * @since 2.0
527      */

528     public static void printRootCauseStackTrace(Throwable JavaDoc throwable, PrintStream JavaDoc stream) {
529         if ( throwable == null ) {
530             return;
531         }
532         if ( stream == null ) {
533             throw new IllegalArgumentException JavaDoc( "The PrintStream must not be null" );
534         }
535         String JavaDoc trace[] = getRootCauseStackTrace( throwable );
536         for ( int i = 0; i < trace.length; i++ ) {
537             stream.println( trace[i] );
538         }
539         stream.flush();
540     }
541
542     /**
543      * <p>Prints a compact stack trace for the root cause of a throwable.</p>
544      * <p/>
545      * <p>The compact stack trace starts with the root cause and prints
546      * stack frames up to the place where it was caught and wrapped.
547      * Then it prints the wrapped exception and continues with stack frames
548      * until the wrapper exception is caught and wrapped again, etc.</p>
549      * <p/>
550      * <p>The method is equivalent to <code>printStackTrace</code> for throwables
551      * that don't have nested causes.</p>
552      *
553      * @param throwable the throwable to output, may be null
554      * @param writer the writer to output to, may not be null
555      * @throws IllegalArgumentException if the writer is <code>null</code>
556      * @since 2.0
557      */

558     public static void printRootCauseStackTrace(Throwable JavaDoc throwable, PrintWriter JavaDoc writer) {
559         if ( throwable == null ) {
560             return;
561         }
562         if ( writer == null ) {
563             throw new IllegalArgumentException JavaDoc( "The PrintWriter must not be null" );
564         }
565         String JavaDoc trace[] = getRootCauseStackTrace( throwable );
566         for ( int i = 0; i < trace.length; i++ ) {
567             writer.println( trace[i] );
568         }
569         writer.flush();
570     }
571
572     //-----------------------------------------------------------------------
573
/**
574      * <p>Creates a compact stack trace for the root cause of the supplied
575      * <code>Throwable</code>.</p>
576      *
577      * @param throwable the throwable to examine, may be null
578      * @return an array of stack trace frames, never null
579      * @since 2.0
580      */

581     public static String JavaDoc[] getRootCauseStackTrace(Throwable JavaDoc throwable) {
582         if ( throwable == null ) {
583             return ArrayHelper.EMPTY_STRING_ARRAY;
584         }
585         Throwable JavaDoc throwables[] = getThrowables( throwable );
586         int count = throwables.length;
587         ArrayList JavaDoc frames = new ArrayList JavaDoc();
588         List JavaDoc nextTrace = getStackFrameList( throwables[count - 1] );
589         for ( int i = count; --i >= 0; ) {
590             List JavaDoc trace = nextTrace;
591             if ( i != 0 ) {
592                 nextTrace = getStackFrameList( throwables[i - 1] );
593                 removeCommonFrames( trace, nextTrace );
594             }
595             if ( i == count - 1 ) {
596                 frames.add( throwables[i].toString() );
597             }
598             else {
599                 frames.add( WRAPPED_MARKER + throwables[i].toString() );
600             }
601             for ( int j = 0; j < trace.size(); j++ ) {
602                 frames.add( trace.get( j ) );
603             }
604         }
605         return ( String JavaDoc[] ) frames.toArray( new String JavaDoc[0] );
606     }
607
608     /**
609      * <p>Removes common frames from the cause trace given the two stack traces.</p>
610      *
611      * @param causeFrames stack trace of a cause throwable
612      * @param wrapperFrames stack trace of a wrapper throwable
613      * @throws IllegalArgumentException if either argument is null
614      * @since 2.0
615      */

616     public static void removeCommonFrames(List JavaDoc causeFrames, List JavaDoc wrapperFrames) {
617         if ( causeFrames == null || wrapperFrames == null ) {
618             throw new IllegalArgumentException JavaDoc( "The List must not be null" );
619         }
620         int causeFrameIndex = causeFrames.size() - 1;
621         int wrapperFrameIndex = wrapperFrames.size() - 1;
622         while ( causeFrameIndex >= 0 && wrapperFrameIndex >= 0 ) {
623             // Remove the frame from the cause trace if it is the same
624
// as in the wrapper trace
625
String JavaDoc causeFrame = ( String JavaDoc ) causeFrames.get( causeFrameIndex );
626             String JavaDoc wrapperFrame = ( String JavaDoc ) wrapperFrames.get( wrapperFrameIndex );
627             if ( causeFrame.equals( wrapperFrame ) ) {
628                 causeFrames.remove( causeFrameIndex );
629             }
630             causeFrameIndex--;
631             wrapperFrameIndex--;
632         }
633     }
634
635     //-----------------------------------------------------------------------
636
/**
637      * <p>Gets the stack trace from a Throwable as a String.</p>
638      *
639      * @param throwable the <code>Throwable</code> to be examined
640      * @return the stack trace as generated by the exception's
641      * <code>printStackTrace(PrintWriter)</code> method
642      */

643     public static String JavaDoc getStackTrace(Throwable JavaDoc throwable) {
644         StringWriter JavaDoc sw = new StringWriter JavaDoc();
645         PrintWriter JavaDoc pw = new PrintWriter JavaDoc( sw, true );
646         throwable.printStackTrace( pw );
647         return sw.getBuffer().toString();
648     }
649
650     /**
651      * <p>A way to get the entire nested stack-trace of an throwable.</p>
652      *
653      * @param throwable the <code>Throwable</code> to be examined
654      * @return the nested stack trace, with the root cause first
655      * @since 2.0
656      */

657     public static String JavaDoc getFullStackTrace(Throwable JavaDoc throwable) {
658         StringWriter JavaDoc sw = new StringWriter JavaDoc();
659         PrintWriter JavaDoc pw = new PrintWriter JavaDoc( sw, true );
660         Throwable JavaDoc[] ts = getThrowables( throwable );
661         for ( int i = 0; i < ts.length; i++ ) {
662             ts[i].printStackTrace( pw );
663             if ( isNestedThrowable( ts[i] ) ) {
664                 break;
665             }
666         }
667         return sw.getBuffer().toString();
668     }
669
670     //-----------------------------------------------------------------------
671
/**
672      * <p>Captures the stack trace associated with the specified
673      * <code>Throwable</code> object, decomposing it into a list of
674      * stack frames.</p>
675      *
676      * @param throwable the <code>Throwable</code> to exaamine, may be null
677      * @return an array of strings describing each stack frame, never null
678      */

679     public static String JavaDoc[] getStackFrames(Throwable JavaDoc throwable) {
680         if ( throwable == null ) {
681             return ArrayHelper.EMPTY_STRING_ARRAY;
682         }
683         return getStackFrames( getStackTrace( throwable ) );
684     }
685
686     /**
687      * <p>Functionality shared between the
688      * <code>getStackFrames(Throwable)</code> methods of this and the
689      * {@link org.apache.commons.lang.exception.NestableDelegate}
690      * classes.</p>
691      */

692     static String JavaDoc[] getStackFrames(String JavaDoc stackTrace) {
693         String JavaDoc linebreak = LINE_SEPARATOR;
694         StringTokenizer JavaDoc frames = new StringTokenizer JavaDoc( stackTrace, linebreak );
695         List JavaDoc list = new LinkedList JavaDoc();
696         while ( frames.hasMoreTokens() ) {
697             list.add( frames.nextToken() );
698         }
699         return ( String JavaDoc[] ) list.toArray( new String JavaDoc[list.size()] );
700     }
701
702     /**
703      * <p>Produces a <code>List</code> of stack frames - the message
704      * is not included.</p>
705      * <p/>
706      * <p>This works in most cases - it will only fail if the exception
707      * message contains a line that starts with:
708      * <code>&quot;&nbsp;&nbsp;&nbsp;at&quot;.</code></p>
709      *
710      * @param t is any throwable
711      * @return List of stack frames
712      */

713     static List JavaDoc getStackFrameList(Throwable JavaDoc t) {
714         String JavaDoc stackTrace = getStackTrace( t );
715         String JavaDoc linebreak = LINE_SEPARATOR;
716         StringTokenizer JavaDoc frames = new StringTokenizer JavaDoc( stackTrace, linebreak );
717         List JavaDoc list = new LinkedList JavaDoc();
718         boolean traceStarted = false;
719         while ( frames.hasMoreTokens() ) {
720             String JavaDoc token = frames.nextToken();
721             // Determine if the line starts with <whitespace>at
722
int at = token.indexOf( "at" );
723             if ( at != -1 && token.substring( 0, at ).trim().length() == 0 ) {
724                 traceStarted = true;
725                 list.add( token );
726             }
727             else if ( traceStarted ) {
728                 break;
729             }
730         }
731         return list;
732     }
733
734 }
735
Popular Tags