    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}