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