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