    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.StringTokenizer;
    028
    029import org.apache.commons.lang3.ArrayUtils;
    030import org.apache.commons.lang3.ClassUtils;
    031import org.apache.commons.lang3.StringUtils;
    032import org.apache.commons.lang3.Validate;
    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 stream  the stream to output to, may not be null
    628     * @throws IllegalArgumentException if the stream is {@code null}
    629     * @since 2.0
    630     */
    631    public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream stream) {
    632        if (throwable == null) {
    633            return;
    634        }
    635        Validate.notNull(stream, "The PrintStream must not be null");
    636        final String trace[] = getRootCauseStackTrace(throwable);
    637        for (final String element : trace) {
    638            stream.println(element);
    639        }
    640        stream.flush();
    641    }
    642
    643    /**
    644     * <p>Prints a compact stack trace for the root cause of a throwable.</p>
    645     *
    646     * <p>The compact stack trace starts with the root cause and prints
    647     * stack frames up to the place where it was caught and wrapped.
    648     * Then it prints the wrapped exception and continues with stack frames
    649     * until the wrapper exception is caught and wrapped again, etc.</p>
    650     *
    651     * <p>The output of this method is consistent across JDK versions.
    652     * Note that this is the opposite order to the JDK1.4 display.</p>
    653     *
    654     * <p>The method is equivalent to {@code printStackTrace} for throwables
    655     * that don't have nested causes.</p>
    656     *
    657     * @param throwable  the throwable to output, may be null
    658     * @param writer  the writer to output to, may not be null
    659     * @throws IllegalArgumentException if the writer is {@code null}
    660     * @since 2.0
    661     */
    662    public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter writer) {
    663        if (throwable == null) {
    664            return;
    665        }
    666        Validate.notNull(writer, "The PrintWriter must not be null");
    667        final String trace[] = getRootCauseStackTrace(throwable);
    668        for (final String element : trace) {
    669            writer.println(element);
    670        }
    671        writer.flush();
    672    }
    673
    674    /**
    675     * <p>Removes common frames from the cause trace given the two stack traces.</p>
    676     *
    677     * @param causeFrames  stack trace of a cause throwable
    678     * @param wrapperFrames  stack trace of a wrapper throwable
    679     * @throws IllegalArgumentException if either argument is null
    680     * @since 2.0
    681     */
    682    public static void removeCommonFrames(final List<String> causeFrames, final List<String> wrapperFrames) {
    683        if (causeFrames == null || wrapperFrames == null) {
    684            throw new IllegalArgumentException("The List must not be null");
    685        }
    686        int causeFrameIndex = causeFrames.size() - 1;
    687        int wrapperFrameIndex = wrapperFrames.size() - 1;
    688        while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) {
    689            // Remove the frame from the cause trace if it is the same
    690            // as in the wrapper trace
    691            final String causeFrame = causeFrames.get(causeFrameIndex);
    692            final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex);
    693            if (causeFrame.equals(wrapperFrame)) {
    694                causeFrames.remove(causeFrameIndex);
    695            }
    696            causeFrameIndex--;
    697            wrapperFrameIndex--;
    698        }
    699    }
    700
    701    /**
    702     * Throw a checked exception without adding the exception to the throws
    703     * clause of the calling method. This method prevents throws clause
    704     * pollution and reduces the clutter of "Caused by" exceptions in the
    705     * stacktrace.
    706     * <p>
    707     * The use of this technique may be controversial, but exceedingly useful to
    708     * library developers.
    709     * <code>
    710     *  public int propagateExample { // note that there is no throws clause
    711     *      try {
    712     *          return invocation(); // throws IOException
    713     *      } catch (Exception e) {
    714     *          return ExceptionUtils.rethrow(e);  // propagates a checked exception
    715     *      }
    716     *  }
    717     * </code>
    718     * <p>
    719     * This is an alternative to the more conservative approach of wrapping the
    720     * checked exception in a RuntimeException:
    721     * <code>
    722     *  public int wrapExample { // note that there is no throws clause
    723     *      try {
    724     *          return invocation(); // throws IOException
    725     *      } catch (Error e) {
    726     *          throw e;
    727     *      } catch (RuntimeException e) {
    728     *          throw e;  // wraps a checked exception
    729     *      } catch (Exception e) {
    730     *          throw new UndeclaredThrowableException(e);  // wraps a checked exception
    731     *      }
    732     *  }
    733     * </code>
    734     * <p>
    735     * One downside to using this approach is that the java compiler will not
    736     * allow invoking code to specify a checked exception in a catch clause
    737     * unless there is some code path within the try block that has invoked a
    738     * method declared with that checked exception. If the invoking site wishes
    739     * to catch the shaded checked exception, it must either invoke the shaded
    740     * code through a method re-declaring the desired checked exception, or
    741     * catch Exception and use the instanceof operator. Either of these
    742     * techniques are required when interacting with non-java jvm code such as
    743     * Jython, Scala, or Groovy, since these languages do not consider any
    744     * exceptions as checked.
    745     *
    746     * @param throwable
    747     *            The throwable to rethrow.
    748     * @param <R> The type of the returned value.
    749     * @return Never actually returned, this generic type matches any type
    750     *         which the calling site requires. "Returning" the results of this
    751     *         method, as done in the propagateExample above, will satisfy the
    752     *         java compiler requirement that all code paths return a value.
    753     * @since 3.5
    754     * @see #wrapAndThrow(Throwable)
    755     */
    756    public static <R> R rethrow(final Throwable throwable) {
    757        // claim that the typeErasure invocation throws a RuntimeException
    758        return ExceptionUtils.<R, RuntimeException>typeErasure(throwable);
    759    }
    760
    761    /**
    762     * <p>Worker method for the {@code throwableOfType} methods.</p>
    763     *
    764     * @param <T> the type of Throwable you are searching.
    765     * @param throwable  the throwable to inspect, may be null
    766     * @param type  the type to search, subclasses match, null returns null
    767     * @param fromIndex  the (zero-based) index of the starting position,
    768     *  negative treated as zero, larger than chain size returns null
    769     * @param subclass if {@code true}, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares
    770     * using references
    771     * @return throwable of the {@code type} within throwables nested within the specified {@code throwable}
    772     */
    773    private static <T extends Throwable> T throwableOf(final Throwable throwable, final Class<T> type, int fromIndex, final boolean subclass) {
    774        if (throwable == null || type == null) {
    775            return null;
    776        }
    777        if (fromIndex < 0) {
    778            fromIndex = 0;
    779        }
    780        final Throwable[] throwables = getThrowables(throwable);
    781        if (fromIndex >= throwables.length) {
    782            return null;
    783        }
    784        if (subclass) {
    785            for (int i = fromIndex; i < throwables.length; i++) {
    786                if (type.isAssignableFrom(throwables[i].getClass())) {
    787                    return type.cast(throwables[i]);
    788                }
    789            }
    790        } else {
    791            for (int i = fromIndex; i < throwables.length; i++) {
    792                if (type.equals(throwables[i].getClass())) {
    793                    return type.cast(throwables[i]);
    794                }
    795            }
    796        }
    797        return null;
    798    }
    799
    800    /**
    801     * <p>Returns the first {@code Throwable}
    802     * that matches the specified class (exactly) in the exception chain.
    803     * Subclasses of the specified class do not match - see
    804     * {@link #throwableOfType(Throwable, Class)} for the opposite.</p>
    805     *
    806     * <p>A {@code null} throwable returns {@code null}.
    807     * A {@code null} type returns {@code null}.
    808     * No match in the chain returns {@code null}.</p>
    809     *
    810     * @param <T> the type of Throwable you are searching.
    811     * @param throwable  the throwable to inspect, may be null
    812     * @param clazz  the class to search for, subclasses do not match, null returns null
    813     * @return the index into the throwable chain, null if no match or null input
    814     * @since 3.10
    815     */
    816    public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz) {
    817        return throwableOf(throwable, clazz, 0, false);
    818    }
    819
    820    /**
    821     * <p>Returns the first {@code Throwable}
    822     * that matches the specified type in the exception chain from
    823     * a specified index.
    824     * Subclasses of the specified class do not match - see
    825     * {@link #throwableOfType(Throwable, Class, int)} for the opposite.</p>
    826     *
    827     * <p>A {@code null} throwable returns {@code null}.
    828     * A {@code null} type returns {@code null}.
    829     * No match in the chain returns {@code null}.
    830     * A negative start index is treated as zero.
    831     * A start index greater than the number of throwables returns {@code null}.</p>
    832     *
    833     * @param <T> the type of Throwable you are searching.
    834     * @param throwable  the throwable to inspect, may be null
    835     * @param clazz  the class to search for, subclasses do not match, null returns null
    836     * @param fromIndex  the (zero-based) index of the starting position,
    837     *  negative treated as zero, larger than chain size returns null
    838     * @return the index into the throwable chain, null if no match or null input
    839     * @since 3.10
    840     */
    841    public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz, final int fromIndex) {
    842        return throwableOf(throwable, clazz, fromIndex, false);
    843    }
    844
    845    /**
    846     * <p>Returns the throwable of the first {@code Throwable}
    847     * that matches the specified class or subclass in the exception chain.
    848     * Subclasses of the specified class do match - see
    849     * {@link #throwableOfThrowable(Throwable, Class)} for the opposite..</p>
    850     *
    851     * <p>A {@code null} throwable returns {@code null}.
    852     * A {@code null} type returns {@code null}.
    853     * No match in the chain returns {@code null}.</p>
    854     *
    855     * @param <T> the type of Throwable you are searching.
    856     * @param throwable  the throwable to inspect, may be null
    857     * @param type  the type to search for, subclasses match, null returns null
    858     * @return the index into the throwable chain, null if no match or null input
    859     * @since 3.10
    860     */
    861    public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type) {
    862        return throwableOf(throwable, type, 0, true);
    863    }
    864
    865    /**
    866     * <p>Returns the first {@code Throwable}
    867     * that matches the specified type in the exception chain from
    868     * a specified index.
    869     * Subclasses of the specified class do match - see
    870     * {@link #throwableOfThrowable(Throwable, Class)} for the opposite.</p>
    871     *
    872     * <p>A {@code null} throwable returns {@code null}.
    873     * A {@code null} type returns {@code null}.
    874     * No match in the chain returns {@code null}.
    875     * A negative start index is treated as zero.
    876     * A start index greater than the number of throwables returns {@code null}.</p>
    877     *
    878     * @param <T> the type of Throwable you are searching.
    879     * @param throwable  the throwable to inspect, may be null
    880     * @param type  the type to search for, subclasses match, null returns null
    881     * @param fromIndex  the (zero-based) index of the starting position,
    882     *  negative treated as zero, larger than chain size returns null
    883     * @return the index into the throwable chain, null if no match or null input
    884     * @since 3.10
    885     */
    886    public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type, final int fromIndex) {
    887        return throwableOf(throwable, type, fromIndex, true);
    888    }
    889
    890    /**
    891     * Claim a Throwable is another Exception type using type erasure. This
    892     * hides a checked exception from the java compiler, allowing a checked
    893     * exception to be thrown without having the exception in the method's throw
    894     * clause.
    895     */
    896    @SuppressWarnings("unchecked")
    897    private static <R, T extends Throwable> R typeErasure(final Throwable throwable) throws T {
    898        throw (T) throwable;
    899    }
    900
    901    /**
    902     * Throw a checked exception without adding the exception to the throws
    903     * clause of the calling method. For checked exceptions, this method throws
    904     * an UndeclaredThrowableException wrapping the checked exception. For
    905     * Errors and RuntimeExceptions, the original exception is rethrown.
    906     * <p>
    907     * The downside to using this approach is that invoking code which needs to
    908     * handle specific checked exceptions must sniff up the exception chain to
    909     * determine if the caught exception was caused by the checked exception.
    910     *
    911     * @param throwable
    912     *            The throwable to rethrow.
    913     * @param <R> The type of the returned value.
    914     * @return Never actually returned, this generic type matches any type
    915     *         which the calling site requires. "Returning" the results of this
    916     *         method will satisfy the java compiler requirement that all code
    917     *         paths return a value.
    918     * @since 3.5
    919     * @see #rethrow(Throwable)
    920     * @see #hasCause(Throwable, Class)
    921     */
    922    public static <R> R wrapAndThrow(final Throwable throwable) {
    923        if (throwable instanceof RuntimeException) {
    924            throw (RuntimeException) throwable;
    925        }
    926        if (throwable instanceof Error) {
    927            throw (Error) throwable;
    928        }
    929        throw new UndeclaredThrowableException(throwable);
    930    }
    931
    932    /**
    933     * <p>
    934     * Public constructor allows an instance of {@code ExceptionUtils} to be created, although that is not
    935     * normally necessary.
    936     * </p>
    937     */
    938    public ExceptionUtils() {
    939        super();
    940    }
    941}