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