001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.lang3.exception;
018
019import java.io.PrintStream;
020import java.io.PrintWriter;
021import java.io.StringWriter;
022import java.lang.reflect.InvocationTargetException;
023import java.lang.reflect.Method;
024import java.lang.reflect.UndeclaredThrowableException;
025import java.util.ArrayList;
026import java.util.List;
027import java.util.Objects;
028import java.util.StringTokenizer;
029
030import org.apache.commons.lang3.ArrayUtils;
031import org.apache.commons.lang3.ClassUtils;
032import org.apache.commons.lang3.StringUtils;
033
034/**
035 * <p>Provides utilities for manipulating and examining
036 * {@code Throwable} objects.</p>
037 *
038 * @since 1.0
039 */
040public class ExceptionUtils {
041
042    private static final int NOT_FOUND = -1;
043
044    /**
045     * <p>The names of methods commonly used to access a wrapped exception.</p>
046     */
047    // TODO: Remove in Lang 4.0
048    private static final String[] CAUSE_METHOD_NAMES = {
049        "getCause",
050        "getNextException",
051        "getTargetException",
052        "getException",
053        "getSourceException",
054        "getRootCause",
055        "getCausedByException",
056        "getNested",
057        "getLinkedException",
058        "getNestedException",
059        "getLinkedCause",
060        "getThrowable",
061    };
062
063    /**
064     * <p>Used when printing stack frames to denote the start of a
065     * wrapped exception.</p>
066     *
067     * <p>Package private for accessibility by test suite.</p>
068     */
069    static final String WRAPPED_MARKER = " [wrapped] ";
070
071    //-----------------------------------------------------------------------
072    /**
073     * <p>Introspects the {@code Throwable} to obtain the cause.</p>
074     *
075     * <p>The method searches for methods with specific names that return a
076     * {@code Throwable} object. This will pick up most wrapping exceptions,
077     * including those from JDK 1.4.
078     *
079     * <p>The default list searched for are:</p>
080     * <ul>
081     *  <li>{@code getCause()}</li>
082     *  <li>{@code getNextException()}</li>
083     *  <li>{@code getTargetException()}</li>
084     *  <li>{@code getException()}</li>
085     *  <li>{@code getSourceException()}</li>
086     *  <li>{@code getRootCause()}</li>
087     *  <li>{@code getCausedByException()}</li>
088     *  <li>{@code getNested()}</li>
089     * </ul>
090     *
091     * <p>If none of the above is found, returns {@code null}.</p>
092     *
093     * @param throwable  the throwable to introspect for a cause, may be null
094     * @return the cause of the {@code Throwable},
095     *  {@code null} if none found or null throwable input
096     * @since 1.0
097     * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead
098     */
099    @Deprecated
100    public static Throwable getCause(final Throwable throwable) {
101        return getCause(throwable, null);
102    }
103
104    /**
105     * <p>Introspects the {@code Throwable} to obtain the cause.</p>
106     *
107     * <p>A {@code null} set of method names means use the default set.
108     * A {@code null} in the set of method names will be ignored.</p>
109     *
110     * @param throwable  the throwable to introspect for a cause, may be null
111     * @param methodNames  the method names, null treated as default set
112     * @return the cause of the {@code Throwable},
113     *  {@code null} if none found or null throwable input
114     * @since 1.0
115     * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead
116     */
117    @Deprecated
118    public static Throwable getCause(final Throwable throwable, String[] methodNames) {
119        if (throwable == null) {
120            return null;
121        }
122
123        if (methodNames == null) {
124            final Throwable cause = throwable.getCause();
125            if (cause != null) {
126                return cause;
127            }
128
129            methodNames = CAUSE_METHOD_NAMES;
130        }
131
132        for (final String methodName : methodNames) {
133            if (methodName != null) {
134                final Throwable legacyCause = getCauseUsingMethodName(throwable, methodName);
135                if (legacyCause != null) {
136                    return legacyCause;
137                }
138            }
139        }
140
141        return null;
142    }
143
144    /**
145     * <p>Finds a {@code Throwable} by method name.</p>
146     *
147     * @param throwable  the exception to examine
148     * @param methodName  the name of the method to find and invoke
149     * @return the wrapped exception, or {@code null} if not found
150     */
151    // TODO: Remove in Lang 4.0
152    private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) {
153        Method method = null;
154        try {
155            method = throwable.getClass().getMethod(methodName);
156        } catch (final NoSuchMethodException | SecurityException ignored) { // NOPMD
157            // exception ignored
158        }
159
160        if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) {
161            try {
162                return (Throwable) method.invoke(throwable);
163            } catch (final IllegalAccessException | IllegalArgumentException | InvocationTargetException ignored) { // NOPMD
164                // exception ignored
165            }
166        }
167        return null;
168    }
169
170    //-----------------------------------------------------------------------
171    /**
172     * <p>Returns the default names used when searching for the cause of an exception.</p>
173     *
174     * <p>This may be modified and used in the overloaded getCause(Throwable, String[]) method.</p>
175     *
176     * @return cloned array of the default method names
177     * @since 3.0
178     * @deprecated This feature will be removed in Lang 4.0
179     */
180    @Deprecated
181    public static String[] getDefaultCauseMethodNames() {
182        return ArrayUtils.clone(CAUSE_METHOD_NAMES);
183    }
184
185    //-----------------------------------------------------------------------
186    /**
187     * Gets a short message summarising the exception.
188     * <p>
189     * The message returned is of the form
190     * {ClassNameWithoutPackage}: {ThrowableMessage}
191     *
192     * @param th  the throwable to get a message for, null returns empty string
193     * @return the message, non-null
194     * @since 2.2
195     */
196    public static String getMessage(final Throwable th) {
197        if (th == null) {
198            return StringUtils.EMPTY;
199        }
200        final String clsName = ClassUtils.getShortClassName(th, null);
201        final String msg = th.getMessage();
202        return clsName + ": " + StringUtils.defaultString(msg);
203    }
204
205    /**
206     * <p>Introspects the {@code Throwable} to obtain the root cause.</p>
207     *
208     * <p>This method walks through the exception chain to the last element,
209     * "root" of the tree, using {@link Throwable#getCause()}, and
210     * returns that exception.</p>
211     *
212     * <p>From version 2.2, this method handles recursive cause structures
213     * that might otherwise cause infinite loops. If the throwable parameter
214     * has a cause of itself, then null will be returned. If the throwable
215     * parameter cause chain loops, the last element in the chain before the
216     * loop is returned.</p>
217     *
218     * @param throwable  the throwable to get the root cause for, may be null
219     * @return the root cause of the {@code Throwable},
220     *  {@code null} if null throwable input
221     */
222    public static Throwable getRootCause(final Throwable throwable) {
223        final List<Throwable> list = getThrowableList(throwable);
224        return list.isEmpty() ? null : list.get(list.size() - 1);
225    }
226
227    //-----------------------------------------------------------------------
228    /**
229     * Gets a short message summarising the root cause exception.
230     * <p>
231     * The message returned is of the form
232     * {ClassNameWithoutPackage}: {ThrowableMessage}
233     *
234     * @param th  the throwable to get a message for, null returns empty string
235     * @return the message, non-null
236     * @since 2.2
237     */
238    public static String getRootCauseMessage(final Throwable th) {
239        Throwable root = getRootCause(th);
240        root = root == null ? th : root;
241        return getMessage(root);
242    }
243
244    //-----------------------------------------------------------------------
245    /**
246     * <p>Creates a compact stack trace for the root cause of the supplied
247     * {@code Throwable}.</p>
248     *
249     * <p>The output of this method is consistent across JDK versions.
250     * It consists of the root exception followed by each of its wrapping
251     * exceptions separated by '[wrapped]'. Note that this is the opposite
252     * order to the JDK1.4 display.</p>
253     *
254     * @param throwable  the throwable to examine, may be null
255     * @return an array of stack trace frames, never null
256     * @since 2.0
257     */
258    public static String[] getRootCauseStackTrace(final Throwable throwable) {
259        if (throwable == null) {
260            return ArrayUtils.EMPTY_STRING_ARRAY;
261        }
262        final Throwable[] throwables = getThrowables(throwable);
263        final int count = throwables.length;
264        final List<String> frames = new ArrayList<>();
265        List<String> nextTrace = getStackFrameList(throwables[count - 1]);
266        for (int i = count; --i >= 0;) {
267            final List<String> trace = nextTrace;
268            if (i != 0) {
269                nextTrace = getStackFrameList(throwables[i - 1]);
270                removeCommonFrames(trace, nextTrace);
271            }
272            if (i == count - 1) {
273                frames.add(throwables[i].toString());
274            } else {
275                frames.add(WRAPPED_MARKER + throwables[i].toString());
276            }
277            frames.addAll(trace);
278        }
279        return frames.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
280    }
281
282    /**
283     * <p>Produces a {@code List} of stack frames - the message
284     * is not included. Only the trace of the specified exception is
285     * returned, any caused by trace is stripped.</p>
286     *
287     * <p>This works in most cases - it will only fail if the exception
288     * message contains a line that starts with:
289     * {@code &quot;&nbsp;&nbsp;&nbsp;at&quot;.}</p>
290     *
291     * @param t is any throwable
292     * @return List of stack frames
293     */
294    static List<String> getStackFrameList(final Throwable t) {
295        final String stackTrace = getStackTrace(t);
296        final String linebreak = System.lineSeparator();
297        final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
298        final List<String> list = new ArrayList<>();
299        boolean traceStarted = false;
300        while (frames.hasMoreTokens()) {
301            final String token = frames.nextToken();
302            // Determine if the line starts with <whitespace>at
303            final int at = token.indexOf("at");
304            if (at != NOT_FOUND && token.substring(0, at).trim().isEmpty()) {
305                traceStarted = true;
306                list.add(token);
307            } else if (traceStarted) {
308                break;
309            }
310        }
311        return list;
312    }
313
314    //-----------------------------------------------------------------------
315    /**
316     * <p>Returns an array where each element is a line from the argument.</p>
317     *
318     * <p>The end of line is determined by the value of {@link System#lineSeparator()}.</p>
319     *
320     * @param stackTrace  a stack trace String
321     * @return an array where each element is a line from the argument
322     */
323    static String[] getStackFrames(final String stackTrace) {
324        final String linebreak = System.lineSeparator();
325        final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
326        final List<String> list = new ArrayList<>();
327        while (frames.hasMoreTokens()) {
328            list.add(frames.nextToken());
329        }
330        return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
331    }
332
333    /**
334     * <p>Captures the stack trace associated with the specified
335     * {@code Throwable} object, decomposing it into a list of
336     * stack frames.</p>
337     *
338     * <p>The result of this method vary by JDK version as this method
339     * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
340     * On JDK1.3 and earlier, the cause exception will not be shown
341     * unless the specified throwable alters printStackTrace.</p>
342     *
343     * @param throwable  the {@code Throwable} to examine, may be null
344     * @return an array of strings describing each stack frame, never null
345     */
346    public static String[] getStackFrames(final Throwable throwable) {
347        if (throwable == null) {
348            return ArrayUtils.EMPTY_STRING_ARRAY;
349        }
350        return getStackFrames(getStackTrace(throwable));
351    }
352
353    //-----------------------------------------------------------------------
354    /**
355     * <p>Gets the stack trace from a Throwable as a String.</p>
356     *
357     * <p>The result of this method vary by JDK version as this method
358     * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
359     * On JDK1.3 and earlier, the cause exception will not be shown
360     * unless the specified throwable alters printStackTrace.</p>
361     *
362     * @param throwable  the {@code Throwable} to be examined
363     * @return the stack trace as generated by the exception's
364     *  {@code printStackTrace(PrintWriter)} method
365     */
366    public static String getStackTrace(final Throwable throwable) {
367        final StringWriter sw = new StringWriter();
368        final PrintWriter pw = new PrintWriter(sw, true);
369        throwable.printStackTrace(pw);
370        return sw.getBuffer().toString();
371    }
372
373    //-----------------------------------------------------------------------
374    /**
375     * <p>Counts the number of {@code Throwable} objects in the
376     * exception chain.</p>
377     *
378     * <p>A throwable without cause will return {@code 1}.
379     * A throwable with one cause will return {@code 2} and so on.
380     * A {@code null} throwable will return {@code 0}.</p>
381     *
382     * <p>From version 2.2, this method handles recursive cause structures
383     * that might otherwise cause infinite loops. The cause chain is
384     * processed until the end is reached, or until the next item in the
385     * chain is already in the result set.</p>
386     *
387     * @param throwable  the throwable to inspect, may be null
388     * @return the count of throwables, zero if null input
389     */
390    public static int getThrowableCount(final Throwable throwable) {
391        return getThrowableList(throwable).size();
392    }
393
394    /**
395     * <p>Returns the list of {@code Throwable} objects in the
396     * exception chain.</p>
397     *
398     * <p>A throwable without cause will return a list containing
399     * one element - the input throwable.
400     * A throwable with one cause will return a list containing
401     * two elements. - the input throwable and the cause throwable.
402     * A {@code null} throwable will return a list of size zero.</p>
403     *
404     * <p>This method handles recursive cause structures that might
405     * otherwise cause infinite loops. The cause chain is processed until
406     * the end is reached, or until the next item in the chain is already
407     * in the result set.</p>
408     *
409     * @param throwable  the throwable to inspect, may be null
410     * @return the list of throwables, never null
411     * @since 2.2
412     */
413    public static List<Throwable> getThrowableList(Throwable throwable) {
414        final List<Throwable> list = new ArrayList<>();
415        while (throwable != null && !list.contains(throwable)) {
416            list.add(throwable);
417            throwable = throwable.getCause();
418        }
419        return list;
420    }
421
422    /**
423     * <p>Returns the list of {@code Throwable} objects in the
424     * exception chain.</p>
425     *
426     * <p>A throwable without cause will return an array containing
427     * one element - the input throwable.
428     * A throwable with one cause will return an array containing
429     * two elements. - the input throwable and the cause throwable.
430     * A {@code null} throwable will return an array of size zero.</p>
431     *
432     * <p>From version 2.2, this method handles recursive cause structures
433     * that might otherwise cause infinite loops. The cause chain is
434     * processed until the end is reached, or until the next item in the
435     * chain is already in the result set.</p>
436     *
437     * @see #getThrowableList(Throwable)
438     * @param throwable  the throwable to inspect, may be null
439     * @return the array of throwables, never null
440     */
441    public static Throwable[] getThrowables(final Throwable throwable) {
442        final List<Throwable> list = getThrowableList(throwable);
443        return list.toArray(ArrayUtils.EMPTY_THROWABLE_ARRAY);
444    }
445
446    /**
447     * Does the throwable's causal chain have an immediate or wrapped exception
448     * of the given type?
449     *
450     * @param chain
451     *            The root of a Throwable causal chain.
452     * @param type
453     *            The exception type to test.
454     * @return true, if chain is an instance of type or is an
455     *         UndeclaredThrowableException wrapping a cause.
456     * @since 3.5
457     * @see #wrapAndThrow(Throwable)
458     */
459    public static boolean hasCause(Throwable chain,
460            final Class<? extends Throwable> type) {
461        if (chain instanceof UndeclaredThrowableException) {
462            chain = chain.getCause();
463        }
464        return type.isInstance(chain);
465    }
466
467    /**
468     * <p>Worker method for the {@code indexOfType} methods.</p>
469     *
470     * @param throwable  the throwable to inspect, may be null
471     * @param type  the type to search for, subclasses match, null returns -1
472     * @param fromIndex  the (zero-based) index of the starting position,
473     *  negative treated as zero, larger than chain size returns -1
474     * @param subclass if {@code true}, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares
475     * using references
476     * @return index of the {@code type} within throwables nested within the specified {@code throwable}
477     */
478    private static int indexOf(final Throwable throwable, final Class<? extends Throwable> type, int fromIndex, final boolean subclass) {
479        if (throwable == null || type == null) {
480            return NOT_FOUND;
481        }
482        if (fromIndex < 0) {
483            fromIndex = 0;
484        }
485        final Throwable[] throwables = getThrowables(throwable);
486        if (fromIndex >= throwables.length) {
487            return NOT_FOUND;
488        }
489        if (subclass) {
490            for (int i = fromIndex; i < throwables.length; i++) {
491                if (type.isAssignableFrom(throwables[i].getClass())) {
492                    return i;
493                }
494            }
495        } else {
496            for (int i = fromIndex; i < throwables.length; i++) {
497                if (type.equals(throwables[i].getClass())) {
498                    return i;
499                }
500            }
501        }
502        return NOT_FOUND;
503    }
504
505    /**
506     * <p>Returns the (zero-based) index of the first {@code Throwable}
507     * that matches the specified class (exactly) in the exception chain.
508     * Subclasses of the specified class do not match - see
509     * {@link #indexOfType(Throwable, Class)} for the opposite.</p>
510     *
511     * <p>A {@code null} throwable returns {@code -1}.
512     * A {@code null} type returns {@code -1}.
513     * No match in the chain returns {@code -1}.</p>
514     *
515     * @param throwable  the throwable to inspect, may be null
516     * @param clazz  the class to search for, subclasses do not match, null returns -1
517     * @return the index into the throwable chain, -1 if no match or null input
518     */
519    public static int indexOfThrowable(final Throwable throwable, final Class<? extends Throwable> clazz) {
520        return indexOf(throwable, clazz, 0, false);
521    }
522
523    /**
524     * <p>Returns the (zero-based) index of the first {@code Throwable}
525     * that matches the specified type in the exception chain from
526     * a specified index.
527     * Subclasses of the specified class do not match - see
528     * {@link #indexOfType(Throwable, Class, int)} for the opposite.</p>
529     *
530     * <p>A {@code null} throwable returns {@code -1}.
531     * A {@code null} type returns {@code -1}.
532     * No match in the chain returns {@code -1}.
533     * A negative start index is treated as zero.
534     * A start index greater than the number of throwables returns {@code -1}.</p>
535     *
536     * @param throwable  the throwable to inspect, may be null
537     * @param clazz  the class to search for, subclasses do not match, null returns -1
538     * @param fromIndex  the (zero-based) index of the starting position,
539     *  negative treated as zero, larger than chain size returns -1
540     * @return the index into the throwable chain, -1 if no match or null input
541     */
542    public static int indexOfThrowable(final Throwable throwable, final Class<? extends Throwable> clazz, final int fromIndex) {
543        return indexOf(throwable, clazz, fromIndex, false);
544    }
545
546    /**
547     * <p>Returns the (zero-based) index of the first {@code Throwable}
548     * that matches the specified class or subclass in the exception chain.
549     * Subclasses of the specified class do match - see
550     * {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p>
551     *
552     * <p>A {@code null} throwable returns {@code -1}.
553     * A {@code null} type returns {@code -1}.
554     * No match in the chain returns {@code -1}.</p>
555     *
556     * @param throwable  the throwable to inspect, may be null
557     * @param type  the type to search for, subclasses match, null returns -1
558     * @return the index into the throwable chain, -1 if no match or null input
559     * @since 2.1
560     */
561    public static int indexOfType(final Throwable throwable, final Class<? extends Throwable> type) {
562        return indexOf(throwable, type, 0, true);
563    }
564
565    /**
566     * <p>Returns the (zero-based) index of the first {@code Throwable}
567     * that matches the specified type in the exception chain from
568     * a specified index.
569     * Subclasses of the specified class do match - see
570     * {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p>
571     *
572     * <p>A {@code null} throwable returns {@code -1}.
573     * A {@code null} type returns {@code -1}.
574     * No match in the chain returns {@code -1}.
575     * A negative start index is treated as zero.
576     * A start index greater than the number of throwables returns {@code -1}.</p>
577     *
578     * @param throwable  the throwable to inspect, may be null
579     * @param type  the type to search for, subclasses match, null returns -1
580     * @param fromIndex  the (zero-based) index of the starting position,
581     *  negative treated as zero, larger than chain size returns -1
582     * @return the index into the throwable chain, -1 if no match or null input
583     * @since 2.1
584     */
585    public static int indexOfType(final Throwable throwable, final Class<? extends Throwable> type, final int fromIndex) {
586        return indexOf(throwable, type, fromIndex, true);
587    }
588
589    //-----------------------------------------------------------------------
590    /**
591     * <p>Prints a compact stack trace for the root cause of a throwable
592     * to {@code System.err}.</p>
593     *
594     * <p>The compact stack trace starts with the root cause and prints
595     * stack frames up to the place where it was caught and wrapped.
596     * Then it prints the wrapped exception and continues with stack frames
597     * until the wrapper exception is caught and wrapped again, etc.</p>
598     *
599     * <p>The output of this method is consistent across JDK versions.
600     * Note that this is the opposite order to the JDK1.4 display.</p>
601     *
602     * <p>The method is equivalent to {@code printStackTrace} for throwables
603     * that don't have nested causes.</p>
604     *
605     * @param throwable  the throwable to output
606     * @since 2.0
607     */
608    public static void printRootCauseStackTrace(final Throwable throwable) {
609        printRootCauseStackTrace(throwable, System.err);
610    }
611
612    /**
613     * <p>Prints a compact stack trace for the root cause of a throwable.</p>
614     *
615     * <p>The compact stack trace starts with the root cause and prints
616     * stack frames up to the place where it was caught and wrapped.
617     * Then it prints the wrapped exception and continues with stack frames
618     * until the wrapper exception is caught and wrapped again, etc.</p>
619     *
620     * <p>The output of this method is consistent across JDK versions.
621     * Note that this is the opposite order to the JDK1.4 display.</p>
622     *
623     * <p>The method is equivalent to {@code printStackTrace} for throwables
624     * that don't have nested causes.</p>
625     *
626     * @param throwable  the throwable to output, may be null
627     * @param printStream  the stream to output to, may not be null
628     * @throws NullPointerException if the printStream is {@code null}
629     * @since 2.0
630     */
631    @SuppressWarnings("resource")
632    public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream printStream) {
633        if (throwable == null) {
634            return;
635        }
636        Objects.requireNonNull(printStream, "printStream");
637        final String[] trace = getRootCauseStackTrace(throwable);
638        for (final String element : trace) {
639            printStream.println(element);
640        }
641        printStream.flush();
642    }
643
644    /**
645     * <p>Prints a compact stack trace for the root cause of a throwable.</p>
646     *
647     * <p>The compact stack trace starts with the root cause and prints
648     * stack frames up to the place where it was caught and wrapped.
649     * Then it prints the wrapped exception and continues with stack frames
650     * until the wrapper exception is caught and wrapped again, etc.</p>
651     *
652     * <p>The output of this method is consistent across JDK versions.
653     * Note that this is the opposite order to the JDK1.4 display.</p>
654     *
655     * <p>The method is equivalent to {@code printStackTrace} for throwables
656     * that don't have nested causes.</p>
657     *
658     * @param throwable  the throwable to output, may be null
659     * @param printWriter  the writer to output to, may not be null
660     * @throws NullPointerException if the printWriter is {@code null}
661     * @since 2.0
662     */
663    @SuppressWarnings("resource")
664    public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter printWriter) {
665        if (throwable == null) {
666            return;
667        }
668        Objects.requireNonNull(printWriter, "printWriter");
669        final String[] trace = getRootCauseStackTrace(throwable);
670        for (final String element : trace) {
671            printWriter.println(element);
672        }
673        printWriter.flush();
674    }
675
676    /**
677     * <p>Removes common frames from the cause trace given the two stack traces.</p>
678     *
679     * @param causeFrames  stack trace of a cause throwable
680     * @param wrapperFrames  stack trace of a wrapper throwable
681     * @throws IllegalArgumentException if either argument is null
682     * @since 2.0
683     */
684    public static void removeCommonFrames(final List<String> causeFrames, final List<String> wrapperFrames) {
685        if (causeFrames == null || wrapperFrames == null) {
686            throw new IllegalArgumentException("The List must not be null");
687        }
688        int causeFrameIndex = causeFrames.size() - 1;
689        int wrapperFrameIndex = wrapperFrames.size() - 1;
690        while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) {
691            // Remove the frame from the cause trace if it is the same
692            // as in the wrapper trace
693            final String causeFrame = causeFrames.get(causeFrameIndex);
694            final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex);
695            if (causeFrame.equals(wrapperFrame)) {
696                causeFrames.remove(causeFrameIndex);
697            }
698            causeFrameIndex--;
699            wrapperFrameIndex--;
700        }
701    }
702
703    /**
704     * Throw a checked exception without adding the exception to the throws
705     * clause of the calling method. This method prevents throws clause
706     * pollution and reduces the clutter of "Caused by" exceptions in the
707     * stacktrace.
708     * <p>
709     * The use of this technique may be controversial, but exceedingly useful to
710     * library developers.
711     * <code>
712     *  public int propagateExample { // note that there is no throws clause
713     *      try {
714     *          return invocation(); // throws IOException
715     *      } catch (Exception e) {
716     *          return ExceptionUtils.rethrow(e);  // propagates a checked exception
717     *      }
718     *  }
719     * </code>
720     * <p>
721     * This is an alternative to the more conservative approach of wrapping the
722     * checked exception in a RuntimeException:
723     * <code>
724     *  public int wrapExample { // note that there is no throws clause
725     *      try {
726     *          return invocation(); // throws IOException
727     *      } catch (Error e) {
728     *          throw e;
729     *      } catch (RuntimeException e) {
730     *          throw e;  // wraps a checked exception
731     *      } catch (Exception e) {
732     *          throw new UndeclaredThrowableException(e);  // wraps a checked exception
733     *      }
734     *  }
735     * </code>
736     * <p>
737     * One downside to using this approach is that the java compiler will not
738     * allow invoking code to specify a checked exception in a catch clause
739     * unless there is some code path within the try block that has invoked a
740     * method declared with that checked exception. If the invoking site wishes
741     * to catch the shaded checked exception, it must either invoke the shaded
742     * code through a method re-declaring the desired checked exception, or
743     * catch Exception and use the instanceof operator. Either of these
744     * techniques are required when interacting with non-java jvm code such as
745     * Jython, Scala, or Groovy, since these languages do not consider any
746     * exceptions as checked.
747     *
748     * @param throwable
749     *            The throwable to rethrow.
750     * @param <R> The type of the returned value.
751     * @return Never actually returned, this generic type matches any type
752     *         which the calling site requires. "Returning" the results of this
753     *         method, as done in the propagateExample above, will satisfy the
754     *         java compiler requirement that all code paths return a value.
755     * @since 3.5
756     * @see #wrapAndThrow(Throwable)
757     */
758    public static <R> R rethrow(final Throwable throwable) {
759        // claim that the typeErasure invocation throws a RuntimeException
760        return ExceptionUtils.<R, RuntimeException>typeErasure(throwable);
761    }
762
763    /**
764     * <p>Worker method for the {@code throwableOfType} methods.</p>
765     *
766     * @param <T> the type of Throwable you are searching.
767     * @param throwable  the throwable to inspect, may be null
768     * @param type  the type to search, subclasses match, null returns null
769     * @param fromIndex  the (zero-based) index of the starting position,
770     *  negative treated as zero, larger than chain size returns null
771     * @param subclass if {@code true}, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares
772     * using references
773     * @return throwable of the {@code type} within throwables nested within the specified {@code throwable}
774     */
775    private static <T extends Throwable> T throwableOf(final Throwable throwable, final Class<T> type, int fromIndex, final boolean subclass) {
776        if (throwable == null || type == null) {
777            return null;
778        }
779        if (fromIndex < 0) {
780            fromIndex = 0;
781        }
782        final Throwable[] throwables = getThrowables(throwable);
783        if (fromIndex >= throwables.length) {
784            return null;
785        }
786        if (subclass) {
787            for (int i = fromIndex; i < throwables.length; i++) {
788                if (type.isAssignableFrom(throwables[i].getClass())) {
789                    return type.cast(throwables[i]);
790                }
791            }
792        } else {
793            for (int i = fromIndex; i < throwables.length; i++) {
794                if (type.equals(throwables[i].getClass())) {
795                    return type.cast(throwables[i]);
796                }
797            }
798        }
799        return null;
800    }
801
802    /**
803     * <p>Returns the first {@code Throwable}
804     * that matches the specified class (exactly) in the exception chain.
805     * Subclasses of the specified class do not match - see
806     * {@link #throwableOfType(Throwable, Class)} for the opposite.</p>
807     *
808     * <p>A {@code null} throwable returns {@code null}.
809     * A {@code null} type returns {@code null}.
810     * No match in the chain returns {@code null}.</p>
811     *
812     * @param <T> the type of Throwable you are searching.
813     * @param throwable  the throwable to inspect, may be null
814     * @param clazz  the class to search for, subclasses do not match, null returns null
815     * @return the first matching throwable from the throwable chain, null if no match or null input
816     * @since 3.10
817     */
818    public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz) {
819        return throwableOf(throwable, clazz, 0, false);
820    }
821
822    /**
823     * <p>Returns the first {@code Throwable}
824     * that matches the specified type in the exception chain from
825     * a specified index.
826     * Subclasses of the specified class do not match - see
827     * {@link #throwableOfType(Throwable, Class, int)} for the opposite.</p>
828     *
829     * <p>A {@code null} throwable returns {@code null}.
830     * A {@code null} type returns {@code null}.
831     * No match in the chain returns {@code null}.
832     * A negative start index is treated as zero.
833     * A start index greater than the number of throwables returns {@code null}.</p>
834     *
835     * @param <T> the type of Throwable you are searching.
836     * @param throwable  the throwable to inspect, may be null
837     * @param clazz  the class to search for, subclasses do not match, null returns null
838     * @param fromIndex  the (zero-based) index of the starting position,
839     *  negative treated as zero, larger than chain size returns null
840     * @return the first matching throwable from the throwable chain, null if no match or null input
841     * @since 3.10
842     */
843    public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz, final int fromIndex) {
844        return throwableOf(throwable, clazz, fromIndex, false);
845    }
846
847    /**
848     * <p>Returns the throwable of the first {@code Throwable}
849     * that matches the specified class or subclass in the exception chain.
850     * Subclasses of the specified class do match - see
851     * {@link #throwableOfThrowable(Throwable, Class)} for the opposite..</p>
852     *
853     * <p>A {@code null} throwable returns {@code null}.
854     * A {@code null} type returns {@code null}.
855     * No match in the chain returns {@code null}.</p>
856     *
857     * @param <T> the type of Throwable you are searching.
858     * @param throwable  the throwable to inspect, may be null
859     * @param type  the type to search for, subclasses match, null returns null
860     * @return the first matching throwable from the throwable chain, null if no match or null input
861     * @since 3.10
862     */
863    public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type) {
864        return throwableOf(throwable, type, 0, true);
865    }
866
867    /**
868     * <p>Returns the first {@code Throwable}
869     * that matches the specified type in the exception chain from
870     * a specified index.
871     * Subclasses of the specified class do match - see
872     * {@link #throwableOfThrowable(Throwable, Class)} for the opposite.</p>
873     *
874     * <p>A {@code null} throwable returns {@code null}.
875     * A {@code null} type returns {@code null}.
876     * No match in the chain returns {@code null}.
877     * A negative start index is treated as zero.
878     * A start index greater than the number of throwables returns {@code null}.</p>
879     *
880     * @param <T> the type of Throwable you are searching.
881     * @param throwable  the throwable to inspect, may be null
882     * @param type  the type to search for, subclasses match, null returns null
883     * @param fromIndex  the (zero-based) index of the starting position,
884     *  negative treated as zero, larger than chain size returns null
885     * @return the first matching throwable from the throwable chain, null if no match or null input
886     * @since 3.10
887     */
888    public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type, final int fromIndex) {
889        return throwableOf(throwable, type, fromIndex, true);
890    }
891
892    /**
893     * Claim a Throwable is another Exception type using type erasure. This
894     * hides a checked exception from the java compiler, allowing a checked
895     * exception to be thrown without having the exception in the method's throw
896     * clause.
897     */
898    @SuppressWarnings("unchecked")
899    private static <R, T extends Throwable> R typeErasure(final Throwable throwable) throws T {
900        throw (T) throwable;
901    }
902
903    /**
904     * Throw a checked exception without adding the exception to the throws
905     * clause of the calling method. For checked exceptions, this method throws
906     * an UndeclaredThrowableException wrapping the checked exception. For
907     * Errors and RuntimeExceptions, the original exception is rethrown.
908     * <p>
909     * The downside to using this approach is that invoking code which needs to
910     * handle specific checked exceptions must sniff up the exception chain to
911     * determine if the caught exception was caused by the checked exception.
912     *
913     * @param throwable
914     *            The throwable to rethrow.
915     * @param <R> The type of the returned value.
916     * @return Never actually returned, this generic type matches any type
917     *         which the calling site requires. "Returning" the results of this
918     *         method will satisfy the java compiler requirement that all code
919     *         paths return a value.
920     * @since 3.5
921     * @see #rethrow(Throwable)
922     * @see #hasCause(Throwable, Class)
923     */
924    public static <R> R wrapAndThrow(final Throwable throwable) {
925        if (throwable instanceof RuntimeException) {
926            throw (RuntimeException) throwable;
927        }
928        if (throwable instanceof Error) {
929            throw (Error) throwable;
930        }
931        throw new UndeclaredThrowableException(throwable);
932    }
933
934    /**
935     * <p>
936     * Public constructor allows an instance of {@code ExceptionUtils} to be created, although that is not
937     * normally necessary.
938     * </p>
939     */
940    public ExceptionUtils() {
941    }
942}