ExceptionUtils.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.lang3.exception;

  18. import java.io.PrintStream;
  19. import java.io.PrintWriter;
  20. import java.io.StringWriter;
  21. import java.lang.reflect.Method;
  22. import java.lang.reflect.UndeclaredThrowableException;
  23. import java.util.ArrayList;
  24. import java.util.Collections;
  25. import java.util.List;
  26. import java.util.Objects;
  27. import java.util.StringTokenizer;
  28. import java.util.function.Consumer;
  29. import java.util.stream.Stream;

  30. import org.apache.commons.lang3.ArrayUtils;
  31. import org.apache.commons.lang3.ClassUtils;
  32. import org.apache.commons.lang3.StringUtils;
  33. import org.apache.commons.lang3.reflect.MethodUtils;

  34. /**
  35.  * Provides utilities for manipulating and examining
  36.  * {@link Throwable} objects.
  37.  *
  38.  * @since 1.0
  39.  */
  40. public class ExceptionUtils {

  41.     /**
  42.      * The names of methods commonly used to access a wrapped exception.
  43.      */
  44.     // TODO: Remove in Lang 4
  45.     private static final String[] CAUSE_METHOD_NAMES = {
  46.         "getCause",
  47.         "getNextException",
  48.         "getTargetException",
  49.         "getException",
  50.         "getSourceException",
  51.         "getRootCause",
  52.         "getCausedByException",
  53.         "getNested",
  54.         "getLinkedException",
  55.         "getNestedException",
  56.         "getLinkedCause",
  57.         "getThrowable",
  58.     };

  59.     private static final int NOT_FOUND = -1;

  60.     /**
  61.      * Used when printing stack frames to denote the start of a
  62.      * wrapped exception.
  63.      *
  64.      * <p>Package private for accessibility by test suite.</p>
  65.      */
  66.     static final String WRAPPED_MARKER = " [wrapped] ";

  67.     /**
  68.      * Throws the given (usually checked) exception without adding the exception to the throws
  69.      * clause of the calling method. This method prevents throws clause
  70.      * inflation and reduces the clutter of "Caused by" exceptions in the
  71.      * stack trace.
  72.      * <p>
  73.      * The use of this technique may be controversial, but useful.
  74.      * </p>
  75.      * <pre>
  76.      *  // There is no throws clause in the method signature.
  77.      *  public int propagateExample {
  78.      *      try {
  79.      *          // Throws IOException
  80.      *          invocation();
  81.      *      } catch (Exception e) {
  82.      *          // Propagates a checked exception.
  83.      *          throw ExceptionUtils.asRuntimeException(e);
  84.      *      }
  85.      *      // more processing
  86.      *      ...
  87.      *      return value;
  88.      *  }
  89.      * </pre>
  90.      * <p>
  91.      * This is an alternative to the more conservative approach of wrapping the
  92.      * checked exception in a RuntimeException:
  93.      * </p>
  94.      * <pre>
  95.      *  // There is no throws clause in the method signature.
  96.      *  public int wrapExample() {
  97.      *      try {
  98.      *          // throws IOException.
  99.      *          invocation();
  100.      *      } catch (Error e) {
  101.      *          throw e;
  102.      *      } catch (RuntimeException e) {
  103.      *          // Throws an unchecked exception.
  104.      *          throw e;
  105.      *      } catch (Exception e) {
  106.      *          // Wraps a checked exception.
  107.      *          throw new UndeclaredThrowableException(e);
  108.      *      }
  109.      *      // more processing
  110.      *      ...
  111.      *      return value;
  112.      *  }
  113.      * </pre>
  114.      * <p>
  115.      * One downside to using this approach is that the Java compiler will not
  116.      * allow invoking code to specify a checked exception in a catch clause
  117.      * unless there is some code path within the try block that has invoked a
  118.      * method declared with that checked exception. If the invoking site wishes
  119.      * to catch the shaded checked exception, it must either invoke the shaded
  120.      * code through a method re-declaring the desired checked exception, or
  121.      * catch Exception and use the {@code instanceof} operator. Either of these
  122.      * techniques are required when interacting with non-Java JVM code such as
  123.      * Jython, Scala, or Groovy, since these languages do not consider any
  124.      * exceptions as checked.
  125.      * </p>
  126.      *
  127.      * @param throwable
  128.      *            The throwable to rethrow.
  129.      * @param <T> The type of the returned value.
  130.      * @return Never actually returned, this generic type matches any type
  131.      *         which the calling site requires. "Returning" the results of this
  132.      *         method, as done in the propagateExample above, will satisfy the
  133.      *         Java compiler requirement that all code paths return a value.
  134.      * @since 3.14.0
  135.      * @see #wrapAndThrow(Throwable)
  136.      */
  137.     public static <T extends RuntimeException> T asRuntimeException(final Throwable throwable) {
  138.         // claim that the typeErasure invocation throws a RuntimeException
  139.         return ExceptionUtils.<T, RuntimeException>eraseType(throwable);
  140.     }

  141.     /**
  142.      * Claims a Throwable is another Throwable type using type erasure. This
  143.      * hides a checked exception from the Java compiler, allowing a checked
  144.      * exception to be thrown without having the exception in the method's throw
  145.      * clause.
  146.      */
  147.     @SuppressWarnings("unchecked")
  148.     private static <R, T extends Throwable> R eraseType(final Throwable throwable) throws T {
  149.         throw (T) throwable;
  150.     }

  151.     /**
  152.      * Performs an action for each Throwable causes of the given Throwable.
  153.      * <p>
  154.      * A throwable without cause will return a stream containing one element - the input throwable. A throwable with one cause
  155.      * will return a stream containing two elements. - the input throwable and the cause throwable. A {@code null} throwable
  156.      * will return a stream of count zero.
  157.      * </p>
  158.      *
  159.      * <p>
  160.      * This method handles recursive cause structures that might otherwise cause infinite loops. The cause chain is
  161.      * processed until the end is reached, or until the next item in the chain is already in the result set.
  162.      * </p>
  163.      * @param throwable The Throwable to traverse.
  164.      * @param consumer a non-interfering action to perform on the elements.
  165.      * @since 3.13.0
  166.      */
  167.     public static void forEach(final Throwable throwable, final Consumer<Throwable> consumer) {
  168.         stream(throwable).forEach(consumer);
  169.     }

  170.     /**
  171.      * Introspects the {@link Throwable} to obtain the cause.
  172.      *
  173.      * <p>The method searches for methods with specific names that return a
  174.      * {@link Throwable} object. This will pick up most wrapping exceptions,
  175.      * including those from JDK 1.4.
  176.      * </p>
  177.      *
  178.      * <p>The default list searched for are:</p>
  179.      * <ul>
  180.      *  <li>{@code getCause()}</li>
  181.      *  <li>{@code getNextException()}</li>
  182.      *  <li>{@code getTargetException()}</li>
  183.      *  <li>{@code getException()}</li>
  184.      *  <li>{@code getSourceException()}</li>
  185.      *  <li>{@code getRootCause()}</li>
  186.      *  <li>{@code getCausedByException()}</li>
  187.      *  <li>{@code getNested()}</li>
  188.      * </ul>
  189.      *
  190.      * <p>If none of the above is found, returns {@code null}.</p>
  191.      *
  192.      * @param throwable  the throwable to introspect for a cause, may be null
  193.      * @return the cause of the {@link Throwable},
  194.      *  {@code null} if none found or null throwable input
  195.      * @since 1.0
  196.      * @deprecated This feature will be removed in Lang 4, use {@link Throwable#getCause} instead
  197.      */
  198.     @Deprecated
  199.     public static Throwable getCause(final Throwable throwable) {
  200.         return getCause(throwable, null);
  201.     }

  202.     /**
  203.      * Introspects the {@link Throwable} to obtain the cause.
  204.      *
  205.      * <p>A {@code null} set of method names means use the default set.
  206.      * A {@code null} in the set of method names will be ignored.</p>
  207.      *
  208.      * @param throwable  the throwable to introspect for a cause, may be null
  209.      * @param methodNames  the method names, null treated as default set
  210.      * @return the cause of the {@link Throwable},
  211.      *  {@code null} if none found or null throwable input
  212.      * @since 1.0
  213.      * @deprecated This feature will be removed in Lang 4, use {@link Throwable#getCause} instead
  214.      */
  215.     @Deprecated
  216.     public static Throwable getCause(final Throwable throwable, String[] methodNames) {
  217.         if (throwable == null) {
  218.             return null;
  219.         }
  220.         if (methodNames == null) {
  221.             final Throwable cause = throwable.getCause();
  222.             if (cause != null) {
  223.                 return cause;
  224.             }
  225.             methodNames = CAUSE_METHOD_NAMES;
  226.         }
  227.         return Stream.of(methodNames).map(m -> getCauseUsingMethodName(throwable, m)).filter(Objects::nonNull).findFirst().orElse(null);
  228.     }

  229.     /**
  230.      * Gets a {@link Throwable} by method name.
  231.      *
  232.      * @param throwable  the exception to examine
  233.      * @param methodName  the name of the method to find and invoke
  234.      * @return the wrapped exception, or {@code null} if not found
  235.      */
  236.     // TODO: Remove in Lang 4
  237.     private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) {
  238.         if (methodName != null) {
  239.             Method method = MethodUtils.getMethodObject(throwable.getClass(), methodName);
  240.             if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) {
  241.                 try {
  242.                     return (Throwable) method.invoke(throwable);
  243.                 } catch (final ReflectiveOperationException ignored) {
  244.                     // exception ignored
  245.                 }
  246.             }
  247.         }
  248.         return null;
  249.     }

  250.     /**
  251.      * Gets the default names used when searching for the cause of an exception.
  252.      *
  253.      * <p>This may be modified and used in the overloaded getCause(Throwable, String[]) method.</p>
  254.      *
  255.      * @return cloned array of the default method names
  256.      * @since 3.0
  257.      * @deprecated This feature will be removed in Lang 4
  258.      */
  259.     @Deprecated
  260.     public static String[] getDefaultCauseMethodNames() {
  261.         return ArrayUtils.clone(CAUSE_METHOD_NAMES);
  262.     }

  263.     /**
  264.      * Gets a short message summarizing the exception.
  265.      * <p>
  266.      * The message returned is of the form
  267.      * {ClassNameWithoutPackage}: {ThrowableMessage}
  268.      * </p>
  269.      *
  270.      * @param th  the throwable to get a message for, null returns empty string
  271.      * @return the message, non-null
  272.      * @since 2.2
  273.      */
  274.     public static String getMessage(final Throwable th) {
  275.         if (th == null) {
  276.             return StringUtils.EMPTY;
  277.         }
  278.         final String clsName = ClassUtils.getShortClassName(th, null);
  279.         return clsName + ": " + StringUtils.defaultString(th.getMessage());
  280.     }

  281.     /**
  282.      * Walks the {@link Throwable} to obtain its root cause.
  283.      *
  284.      * <p>This method walks through the exception chain until the last element,
  285.      * the root cause of the chain, using {@link Throwable#getCause()}, and
  286.      * returns that exception.</p>
  287.      *
  288.      * <p>This method handles recursive cause chains that might
  289.      * otherwise cause infinite loops. The cause chain is processed until
  290.      * the end, or until the next item in the chain is already
  291.      * processed. If we detect a loop, then return the element before the loop.</p>

  292.      *
  293.      * @param throwable  the throwable to get the root cause for, may be null
  294.      * @return the root cause of the {@link Throwable},
  295.      *  {@code null} if null throwable input
  296.      */
  297.     public static Throwable getRootCause(final Throwable throwable) {
  298.         final List<Throwable> list = getThrowableList(throwable);
  299.         return list.isEmpty() ? null : list.get(list.size() - 1);
  300.     }

  301.     /**
  302.      * Gets a short message summarizing the root cause exception.
  303.      * <p>
  304.      * The message returned is of the form
  305.      * {ClassNameWithoutPackage}: {ThrowableMessage}
  306.      * </p>
  307.      *
  308.      * @param throwable  the throwable to get a message for, null returns empty string
  309.      * @return the message, non-null
  310.      * @since 2.2
  311.      */
  312.     public static String getRootCauseMessage(final Throwable throwable) {
  313.         final Throwable root = getRootCause(throwable);
  314.         return getMessage(root == null ? throwable : root);
  315.     }

  316.     /**
  317.      * Gets a compact stack trace for the root cause of the supplied
  318.      * {@link Throwable}.
  319.      *
  320.      * <p>The output of this method is consistent across JDK versions.
  321.      * It consists of the root exception followed by each of its wrapping
  322.      * exceptions separated by '[wrapped]'. Note that this is the opposite
  323.      * order to the JDK1.4 display.</p>
  324.      *
  325.      * @param throwable  the throwable to examine, may be null
  326.      * @return an array of stack trace frames, never null
  327.      * @since 2.0
  328.      */
  329.     public static String[] getRootCauseStackTrace(final Throwable throwable) {
  330.         return getRootCauseStackTraceList(throwable).toArray(ArrayUtils.EMPTY_STRING_ARRAY);
  331.     }

  332.     /**
  333.      * Gets a compact stack trace for the root cause of the supplied {@link Throwable}.
  334.      *
  335.      * <p>
  336.      * The output of this method is consistent across JDK versions. It consists of the root exception followed by each of
  337.      * its wrapping exceptions separated by '[wrapped]'. Note that this is the opposite order to the JDK1.4 display.
  338.      * </p>
  339.      *
  340.      * @param throwable the throwable to examine, may be null
  341.      * @return a list of stack trace frames, never null
  342.      * @since 3.13.0
  343.      */
  344.     public static List<String> getRootCauseStackTraceList(final Throwable throwable) {
  345.         if (throwable == null) {
  346.             return Collections.emptyList();
  347.         }
  348.         final Throwable[] throwables = getThrowables(throwable);
  349.         final int count = throwables.length;
  350.         final List<String> frames = new ArrayList<>();
  351.         List<String> nextTrace = getStackFrameList(throwables[count - 1]);
  352.         for (int i = count; --i >= 0;) {
  353.             final List<String> trace = nextTrace;
  354.             if (i != 0) {
  355.                 nextTrace = getStackFrameList(throwables[i - 1]);
  356.                 removeCommonFrames(trace, nextTrace);
  357.             }
  358.             if (i == count - 1) {
  359.                 frames.add(throwables[i].toString());
  360.             } else {
  361.                 frames.add(WRAPPED_MARKER + throwables[i].toString());
  362.             }
  363.             frames.addAll(trace);
  364.         }
  365.         return frames;
  366.     }

  367.     /**
  368.      * Gets a {@link List} of stack frames - the message
  369.      * is not included. Only the trace of the specified exception is
  370.      * returned, any caused by trace is stripped.
  371.      *
  372.      * <p>This works in most cases - it will only fail if the exception
  373.      * message contains a line that starts with:
  374.      * {@code &quot;&nbsp;&nbsp;&nbsp;at&quot;.}</p>
  375.      *
  376.      * @param throwable is any throwable
  377.      * @return List of stack frames
  378.      */
  379.     static List<String> getStackFrameList(final Throwable throwable) {
  380.         final String stackTrace = getStackTrace(throwable);
  381.         final String linebreak = System.lineSeparator();
  382.         final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
  383.         final List<String> list = new ArrayList<>();
  384.         boolean traceStarted = false;
  385.         while (frames.hasMoreTokens()) {
  386.             final String token = frames.nextToken();
  387.             // Determine if the line starts with <whitespace>at
  388.             final int at = token.indexOf("at");
  389.             if (at != NOT_FOUND && token.substring(0, at).trim().isEmpty()) {
  390.                 traceStarted = true;
  391.                 list.add(token);
  392.             } else if (traceStarted) {
  393.                 break;
  394.             }
  395.         }
  396.         return list;
  397.     }

  398.     /**
  399.      * Gets an array where each element is a line from the argument.
  400.      *
  401.      * <p>The end of line is determined by the value of {@link System#lineSeparator()}.</p>
  402.      *
  403.      * @param stackTrace  a stack trace String
  404.      * @return an array where each element is a line from the argument
  405.      */
  406.     static String[] getStackFrames(final String stackTrace) {
  407.         final String linebreak = System.lineSeparator();
  408.         final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
  409.         final List<String> list = new ArrayList<>();
  410.         while (frames.hasMoreTokens()) {
  411.             list.add(frames.nextToken());
  412.         }
  413.         return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
  414.     }

  415.     /**
  416.      * Gets the stack trace associated with the specified
  417.      * {@link Throwable} object, decomposing it into a list of
  418.      * stack frames.
  419.      *
  420.      * <p>The result of this method vary by JDK version as this method
  421.      * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
  422.      * On JDK1.3 and earlier, the cause exception will not be shown
  423.      * unless the specified throwable alters printStackTrace.</p>
  424.      *
  425.      * @param throwable  the {@link Throwable} to examine, may be null
  426.      * @return an array of strings describing each stack frame, never null
  427.      */
  428.     public static String[] getStackFrames(final Throwable throwable) {
  429.         if (throwable == null) {
  430.             return ArrayUtils.EMPTY_STRING_ARRAY;
  431.         }
  432.         return getStackFrames(getStackTrace(throwable));
  433.     }

  434.     /**
  435.      * Gets the stack trace from a Throwable as a String.
  436.      *
  437.      * <p>The result of this method vary by JDK version as this method
  438.      * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
  439.      * On JDK1.3 and earlier, the cause exception will not be shown
  440.      * unless the specified throwable alters printStackTrace.</p>
  441.      *
  442.      * @param throwable  the {@link Throwable} to be examined, may be null
  443.      * @return the stack trace as generated by the exception's
  444.      * {@code printStackTrace(PrintWriter)} method, or an empty String if {@code null} input
  445.      */
  446.     public static String getStackTrace(final Throwable throwable) {
  447.         if (throwable == null) {
  448.             return StringUtils.EMPTY;
  449.         }
  450.         final StringWriter sw = new StringWriter();
  451.         throwable.printStackTrace(new PrintWriter(sw, true));
  452.         return sw.toString();
  453.     }

  454.     /**
  455.      * Gets a count of the number of {@link Throwable} objects in the
  456.      * exception chain.
  457.      *
  458.      * <p>A throwable without cause will return {@code 1}.
  459.      * A throwable with one cause will return {@code 2} and so on.
  460.      * A {@code null} throwable will return {@code 0}.</p>
  461.      *
  462.      * <p>This method handles recursive cause chains
  463.      * that might otherwise cause infinite loops. The cause chain is
  464.      * processed until the end, or until the next item in the
  465.      * chain is already in the result.</p>
  466.      *
  467.      * @param throwable  the throwable to inspect, may be null
  468.      * @return the count of throwables, zero on null input
  469.      */
  470.     public static int getThrowableCount(final Throwable throwable) {
  471.         return getThrowableList(throwable).size();
  472.     }

  473.     /**
  474.      * Gets the list of {@link Throwable} objects in the
  475.      * exception chain.
  476.      *
  477.      * <p>A throwable without cause will return a list containing
  478.      * one element - the input throwable.
  479.      * A throwable with one cause will return a list containing
  480.      * two elements. - the input throwable and the cause throwable.
  481.      * A {@code null} throwable will return a list of size zero.</p>
  482.      *
  483.      * <p>This method handles recursive cause chains that might
  484.      * otherwise cause infinite loops. The cause chain is processed until
  485.      * the end, or until the next item in the chain is already
  486.      * in the result list.</p>
  487.      *
  488.      * @param throwable  the throwable to inspect, may be null
  489.      * @return the list of throwables, never null
  490.      * @since 2.2
  491.      */
  492.     public static List<Throwable> getThrowableList(Throwable throwable) {
  493.         final List<Throwable> list = new ArrayList<>();
  494.         while (throwable != null && !list.contains(throwable)) {
  495.             list.add(throwable);
  496.             throwable = throwable.getCause();
  497.         }
  498.         return list;
  499.     }

  500.     /**
  501.      * Gets the list of {@link Throwable} objects in the
  502.      * exception chain.
  503.      *
  504.      * <p>A throwable without cause will return an array containing
  505.      * one element - the input throwable.
  506.      * A throwable with one cause will return an array containing
  507.      * two elements. - the input throwable and the cause throwable.
  508.      * A {@code null} throwable will return an array of size zero.</p>
  509.      *
  510.      * <p>This method handles recursive cause chains
  511.      * that might otherwise cause infinite loops. The cause chain is
  512.      * processed until the end, or until the next item in the
  513.      * chain is already in the result array.</p>
  514.      *
  515.      * @see #getThrowableList(Throwable)
  516.      * @param throwable  the throwable to inspect, may be null
  517.      * @return the array of throwables, never null
  518.      */
  519.     public static Throwable[] getThrowables(final Throwable throwable) {
  520.         return getThrowableList(throwable).toArray(ArrayUtils.EMPTY_THROWABLE_ARRAY);
  521.     }

  522.     /**
  523.      * Tests if the throwable's causal chain have an immediate or wrapped exception
  524.      * of the given type?
  525.      *
  526.      * @param chain
  527.      *            The root of a Throwable causal chain.
  528.      * @param type
  529.      *            The exception type to test.
  530.      * @return true, if chain is an instance of type or is an
  531.      *         UndeclaredThrowableException wrapping a cause.
  532.      * @since 3.5
  533.      * @see #wrapAndThrow(Throwable)
  534.      */
  535.     public static boolean hasCause(Throwable chain,
  536.             final Class<? extends Throwable> type) {
  537.         if (chain instanceof UndeclaredThrowableException) {
  538.             chain = chain.getCause();
  539.         }
  540.         return type.isInstance(chain);
  541.     }

  542.     /**
  543.      * Worker method for the {@code indexOfType} methods.
  544.      *
  545.      * @param throwable  the throwable to inspect, may be null
  546.      * @param type  the type to search for, subclasses match, null returns -1
  547.      * @param fromIndex  the (zero-based) index of the starting position,
  548.      *  negative treated as zero, larger than chain size returns -1
  549.      * @param subclass if {@code true}, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares
  550.      * using references
  551.      * @return index of the {@code type} within throwables nested within the specified {@code throwable}
  552.      */
  553.     private static int indexOf(final Throwable throwable, final Class<? extends Throwable> type, int fromIndex, final boolean subclass) {
  554.         if (throwable == null || type == null) {
  555.             return NOT_FOUND;
  556.         }
  557.         if (fromIndex < 0) {
  558.             fromIndex = 0;
  559.         }
  560.         final Throwable[] throwables = getThrowables(throwable);
  561.         if (fromIndex >= throwables.length) {
  562.             return NOT_FOUND;
  563.         }
  564.         if (subclass) {
  565.             for (int i = fromIndex; i < throwables.length; i++) {
  566.                 if (type.isAssignableFrom(throwables[i].getClass())) {
  567.                     return i;
  568.                 }
  569.             }
  570.         } else {
  571.             for (int i = fromIndex; i < throwables.length; i++) {
  572.                 if (type.equals(throwables[i].getClass())) {
  573.                     return i;
  574.                 }
  575.             }
  576.         }
  577.         return NOT_FOUND;
  578.     }

  579.     /**
  580.      * Returns the (zero-based) index of the first {@link Throwable}
  581.      * that matches the specified class (exactly) in the exception chain.
  582.      * Subclasses of the specified class do not match - see
  583.      * {@link #indexOfType(Throwable, Class)} for the opposite.
  584.      *
  585.      * <p>A {@code null} throwable returns {@code -1}.
  586.      * A {@code null} type returns {@code -1}.
  587.      * No match in the chain returns {@code -1}.</p>
  588.      *
  589.      * @param throwable  the throwable to inspect, may be null
  590.      * @param clazz  the class to search for, subclasses do not match, null returns -1
  591.      * @return the index into the throwable chain, -1 if no match or null input
  592.      */
  593.     public static int indexOfThrowable(final Throwable throwable, final Class<? extends Throwable> clazz) {
  594.         return indexOf(throwable, clazz, 0, false);
  595.     }

  596.     /**
  597.      * Returns the (zero-based) index of the first {@link Throwable}
  598.      * that matches the specified type in the exception chain from
  599.      * a specified index.
  600.      * Subclasses of the specified class do not match - see
  601.      * {@link #indexOfType(Throwable, Class, int)} for the opposite.
  602.      *
  603.      * <p>A {@code null} throwable returns {@code -1}.
  604.      * A {@code null} type returns {@code -1}.
  605.      * No match in the chain returns {@code -1}.
  606.      * A negative start index is treated as zero.
  607.      * A start index greater than the number of throwables returns {@code -1}.</p>
  608.      *
  609.      * @param throwable  the throwable to inspect, may be null
  610.      * @param clazz  the class to search for, subclasses do not match, null returns -1
  611.      * @param fromIndex  the (zero-based) index of the starting position,
  612.      *  negative treated as zero, larger than chain size returns -1
  613.      * @return the index into the throwable chain, -1 if no match or null input
  614.      */
  615.     public static int indexOfThrowable(final Throwable throwable, final Class<? extends Throwable> clazz, final int fromIndex) {
  616.         return indexOf(throwable, clazz, fromIndex, false);
  617.     }

  618.     /**
  619.      * Returns the (zero-based) index of the first {@link Throwable}
  620.      * that matches the specified class or subclass in the exception chain.
  621.      * Subclasses of the specified class do match - see
  622.      * {@link #indexOfThrowable(Throwable, Class)} for the opposite.
  623.      *
  624.      * <p>A {@code null} throwable returns {@code -1}.
  625.      * A {@code null} type returns {@code -1}.
  626.      * No match in the chain returns {@code -1}.</p>
  627.      *
  628.      * @param throwable  the throwable to inspect, may be null
  629.      * @param type  the type to search for, subclasses match, null returns -1
  630.      * @return the index into the throwable chain, -1 if no match or null input
  631.      * @since 2.1
  632.      */
  633.     public static int indexOfType(final Throwable throwable, final Class<? extends Throwable> type) {
  634.         return indexOf(throwable, type, 0, true);
  635.     }

  636.     /**
  637.      * Returns the (zero-based) index of the first {@link Throwable}
  638.      * that matches the specified type in the exception chain from
  639.      * a specified index.
  640.      * Subclasses of the specified class do match - see
  641.      * {@link #indexOfThrowable(Throwable, Class)} for the opposite.
  642.      *
  643.      * <p>A {@code null} throwable returns {@code -1}.
  644.      * A {@code null} type returns {@code -1}.
  645.      * No match in the chain returns {@code -1}.
  646.      * A negative start index is treated as zero.
  647.      * A start index greater than the number of throwables returns {@code -1}.</p>
  648.      *
  649.      * @param throwable  the throwable to inspect, may be null
  650.      * @param type  the type to search for, subclasses match, null returns -1
  651.      * @param fromIndex  the (zero-based) index of the starting position,
  652.      *  negative treated as zero, larger than chain size returns -1
  653.      * @return the index into the throwable chain, -1 if no match or null input
  654.      * @since 2.1
  655.      */
  656.     public static int indexOfType(final Throwable throwable, final Class<? extends Throwable> type, final int fromIndex) {
  657.         return indexOf(throwable, type, fromIndex, true);
  658.     }

  659.     /**
  660.      * Checks if a throwable represents a checked exception
  661.      *
  662.      * @param throwable
  663.      *            The throwable to check.
  664.      * @return True if the given Throwable is a checked exception.
  665.      * @since 3.13.0
  666.      */
  667.     public static boolean isChecked(final Throwable throwable) {
  668.         return throwable != null && !(throwable instanceof Error) && !(throwable instanceof RuntimeException);
  669.     }

  670.     /**
  671.      * Checks if a throwable represents an unchecked exception
  672.      *
  673.      * @param throwable
  674.      *            The throwable to check.
  675.      * @return True if the given Throwable is an unchecked exception.
  676.      * @since 3.13.0
  677.      */
  678.     public static boolean isUnchecked(final Throwable throwable) {
  679.         return throwable != null && (throwable instanceof Error || throwable instanceof RuntimeException);
  680.     }

  681.     /**
  682.      * Prints a compact stack trace for the root cause of a throwable
  683.      * to {@code System.err}.
  684.      *
  685.      * <p>The compact stack trace starts with the root cause and prints
  686.      * stack frames up to the place where it was caught and wrapped.
  687.      * Then it prints the wrapped exception and continues with stack frames
  688.      * until the wrapper exception is caught and wrapped again, etc.</p>
  689.      *
  690.      * <p>The output of this method is consistent across JDK versions.
  691.      * Note that this is the opposite order to the JDK1.4 display.</p>
  692.      *
  693.      * <p>The method is equivalent to {@code printStackTrace} for throwables
  694.      * that don't have nested causes.</p>
  695.      *
  696.      * @param throwable  the throwable to output
  697.      * @since 2.0
  698.      */
  699.     public static void printRootCauseStackTrace(final Throwable throwable) {
  700.         printRootCauseStackTrace(throwable, System.err);
  701.     }

  702.     /**
  703.      * Prints a compact stack trace for the root cause of a throwable.
  704.      *
  705.      * <p>The compact stack trace starts with the root cause and prints
  706.      * stack frames up to the place where it was caught and wrapped.
  707.      * Then it prints the wrapped exception and continues with stack frames
  708.      * until the wrapper exception is caught and wrapped again, etc.</p>
  709.      *
  710.      * <p>The output of this method is consistent across JDK versions.
  711.      * Note that this is the opposite order to the JDK1.4 display.</p>
  712.      *
  713.      * <p>The method is equivalent to {@code printStackTrace} for throwables
  714.      * that don't have nested causes.</p>
  715.      *
  716.      * @param throwable  the throwable to output, may be null
  717.      * @param printStream  the stream to output to, may not be null
  718.      * @throws NullPointerException if the printStream is {@code null}
  719.      * @since 2.0
  720.      */
  721.     @SuppressWarnings("resource")
  722.     public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream printStream) {
  723.         if (throwable == null) {
  724.             return;
  725.         }
  726.         Objects.requireNonNull(printStream, "printStream");
  727.         getRootCauseStackTraceList(throwable).forEach(printStream::println);
  728.         printStream.flush();
  729.     }

  730.     /**
  731.      * Prints a compact stack trace for the root cause of a throwable.
  732.      *
  733.      * <p>The compact stack trace starts with the root cause and prints
  734.      * stack frames up to the place where it was caught and wrapped.
  735.      * Then it prints the wrapped exception and continues with stack frames
  736.      * until the wrapper exception is caught and wrapped again, etc.</p>
  737.      *
  738.      * <p>The output of this method is consistent across JDK versions.
  739.      * Note that this is the opposite order to the JDK1.4 display.</p>
  740.      *
  741.      * <p>The method is equivalent to {@code printStackTrace} for throwables
  742.      * that don't have nested causes.</p>
  743.      *
  744.      * @param throwable  the throwable to output, may be null
  745.      * @param printWriter  the writer to output to, may not be null
  746.      * @throws NullPointerException if the printWriter is {@code null}
  747.      * @since 2.0
  748.      */
  749.     @SuppressWarnings("resource")
  750.     public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter printWriter) {
  751.         if (throwable == null) {
  752.             return;
  753.         }
  754.         Objects.requireNonNull(printWriter, "printWriter");
  755.         getRootCauseStackTraceList(throwable).forEach(printWriter::println);
  756.         printWriter.flush();
  757.     }

  758.     /**
  759.      * Removes common frames from the cause trace given the two stack traces.
  760.      *
  761.      * @param causeFrames  stack trace of a cause throwable
  762.      * @param wrapperFrames  stack trace of a wrapper throwable
  763.      * @throws NullPointerException if either argument is null
  764.      * @since 2.0
  765.      */
  766.     public static void removeCommonFrames(final List<String> causeFrames, final List<String> wrapperFrames) {
  767.         Objects.requireNonNull(causeFrames, "causeFrames");
  768.         Objects.requireNonNull(wrapperFrames, "wrapperFrames");
  769.         int causeFrameIndex = causeFrames.size() - 1;
  770.         int wrapperFrameIndex = wrapperFrames.size() - 1;
  771.         while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) {
  772.             // Remove the frame from the cause trace if it is the same
  773.             // as in the wrapper trace
  774.             final String causeFrame = causeFrames.get(causeFrameIndex);
  775.             final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex);
  776.             if (causeFrame.equals(wrapperFrame)) {
  777.                 causeFrames.remove(causeFrameIndex);
  778.             }
  779.             causeFrameIndex--;
  780.             wrapperFrameIndex--;
  781.         }
  782.     }

  783.     /**
  784.      * Throws the given (usually checked) exception without adding the exception to the throws
  785.      * clause of the calling method. This method prevents throws clause
  786.      * inflation and reduces the clutter of "Caused by" exceptions in the
  787.      * stack trace.
  788.      * <p>
  789.      * The use of this technique may be controversial, but useful.
  790.      * </p>
  791.      * <pre>
  792.      *  // There is no throws clause in the method signature.
  793.      *  public int propagateExample() {
  794.      *      try {
  795.      *          // throws SomeCheckedException.
  796.      *          return invocation();
  797.      *      } catch (SomeCheckedException e) {
  798.      *          // Propagates a checked exception and compiles to return an int.
  799.      *          return ExceptionUtils.rethrow(e);
  800.      *      }
  801.      *  }
  802.      * </pre>
  803.      * <p>
  804.      * This is an alternative to the more conservative approach of wrapping the
  805.      * checked exception in a RuntimeException:
  806.      * </p>
  807.      * <pre>
  808.      *  // There is no throws clause in the method signature.
  809.      *  public int wrapExample() {
  810.      *      try {
  811.      *          // throws IOException.
  812.      *          return invocation();
  813.      *      } catch (Error e) {
  814.      *          throw e;
  815.      *      } catch (RuntimeException e) {
  816.      *          // Throws an unchecked exception.
  817.      *          throw e;
  818.      *      } catch (Exception e) {
  819.      *          // wraps a checked exception.
  820.      *          throw new UndeclaredThrowableException(e);
  821.      *      }
  822.      *  }
  823.      * </pre>
  824.      * <p>
  825.      * One downside to using this approach is that the Java compiler will not
  826.      * allow invoking code to specify a checked exception in a catch clause
  827.      * unless there is some code path within the try block that has invoked a
  828.      * method declared with that checked exception. If the invoking site wishes
  829.      * to catch the shaded checked exception, it must either invoke the shaded
  830.      * code through a method re-declaring the desired checked exception, or
  831.      * catch Exception and use the {@code instanceof} operator. Either of these
  832.      * techniques are required when interacting with non-Java JVM code such as
  833.      * Jython, Scala, or Groovy, since these languages do not consider any
  834.      * exceptions as checked.
  835.      * </p>
  836.      *
  837.      * @param throwable
  838.      *            The throwable to rethrow.
  839.      * @param <T> The type of the return value.
  840.      * @return Never actually returns, this generic type matches any type
  841.      *         which the calling site requires. "Returning" the results of this
  842.      *         method, as done in the propagateExample above, will satisfy the
  843.      *         Java compiler requirement that all code paths return a value.
  844.      * @since 3.5
  845.      * @see #wrapAndThrow(Throwable)
  846.      */
  847.     public static <T> T rethrow(final Throwable throwable) {
  848.         // claim that the typeErasure invocation throws a RuntimeException
  849.         return ExceptionUtils.<T, RuntimeException>eraseType(throwable);
  850.     }

  851.     /**
  852.      * Streams causes of a Throwable.
  853.      * <p>
  854.      * A throwable without cause will return a stream containing one element - the input throwable. A throwable with one cause
  855.      * will return a stream containing two elements. - the input throwable and the cause throwable. A {@code null} throwable
  856.      * will return a stream of count zero.
  857.      * </p>
  858.      *
  859.      * <p>
  860.      * This method handles recursive cause chains that might otherwise cause infinite loops. The cause chain is
  861.      * processed until the end, or until the next item in the chain is already in the result.
  862.      * </p>
  863.      *
  864.      * @param throwable The Throwable to traverse
  865.      * @return A new Stream of Throwable causes.
  866.      * @since 3.13.0
  867.      */
  868.     public static Stream<Throwable> stream(final Throwable throwable) {
  869.         // No point building a custom Iterable as it would keep track of visited elements to avoid infinite loops
  870.         return getThrowableList(throwable).stream();
  871.     }

  872.     /**
  873.      * Worker method for the {@code throwableOfType} methods.
  874.      *
  875.      * @param <T> the type of Throwable you are searching.
  876.      * @param throwable  the throwable to inspect, may be null
  877.      * @param type  the type to search, subclasses match, null returns null
  878.      * @param fromIndex  the (zero-based) index of the starting position,
  879.      *  negative treated as zero, larger than chain size returns null
  880.      * @param subclass if {@code true}, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares
  881.      * using references
  882.      * @return throwable of the {@code type} within throwables nested within the specified {@code throwable}
  883.      */
  884.     private static <T extends Throwable> T throwableOf(final Throwable throwable, final Class<T> type, int fromIndex, final boolean subclass) {
  885.         if (throwable == null || type == null) {
  886.             return null;
  887.         }
  888.         if (fromIndex < 0) {
  889.             fromIndex = 0;
  890.         }
  891.         final Throwable[] throwables = getThrowables(throwable);
  892.         if (fromIndex >= throwables.length) {
  893.             return null;
  894.         }
  895.         if (subclass) {
  896.             for (int i = fromIndex; i < throwables.length; i++) {
  897.                 if (type.isAssignableFrom(throwables[i].getClass())) {
  898.                     return type.cast(throwables[i]);
  899.                 }
  900.             }
  901.         } else {
  902.             for (int i = fromIndex; i < throwables.length; i++) {
  903.                 if (type.equals(throwables[i].getClass())) {
  904.                     return type.cast(throwables[i]);
  905.                 }
  906.             }
  907.         }
  908.         return null;
  909.     }

  910.     /**
  911.      * Returns the first {@link Throwable}
  912.      * that matches the specified class (exactly) in the exception chain.
  913.      * Subclasses of the specified class do not match - see
  914.      * {@link #throwableOfType(Throwable, Class)} for the opposite.
  915.      *
  916.      * <p>A {@code null} throwable returns {@code null}.
  917.      * A {@code null} type returns {@code null}.
  918.      * No match in the chain returns {@code null}.</p>
  919.      *
  920.      * @param <T> the type of Throwable you are searching.
  921.      * @param throwable  the throwable to inspect, may be null
  922.      * @param clazz  the class to search for, subclasses do not match, null returns null
  923.      * @return the first matching throwable from the throwable chain, null if no match or null input
  924.      * @since 3.10
  925.      */
  926.     public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz) {
  927.         return throwableOf(throwable, clazz, 0, false);
  928.     }

  929.     /**
  930.      * Returns the first {@link Throwable}
  931.      * that matches the specified type in the exception chain from
  932.      * a specified index.
  933.      * Subclasses of the specified class do not match - see
  934.      * {@link #throwableOfType(Throwable, Class, int)} for the opposite.
  935.      *
  936.      * <p>A {@code null} throwable returns {@code null}.
  937.      * A {@code null} type returns {@code null}.
  938.      * No match in the chain returns {@code null}.
  939.      * A negative start index is treated as zero.
  940.      * A start index greater than the number of throwables returns {@code null}.</p>
  941.      *
  942.      * @param <T> the type of Throwable you are searching.
  943.      * @param throwable  the throwable to inspect, may be null
  944.      * @param clazz  the class to search for, subclasses do not match, null returns null
  945.      * @param fromIndex  the (zero-based) index of the starting position,
  946.      *  negative treated as zero, larger than chain size returns null
  947.      * @return the first matching throwable from the throwable chain, null if no match or null input
  948.      * @since 3.10
  949.      */
  950.     public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz, final int fromIndex) {
  951.         return throwableOf(throwable, clazz, fromIndex, false);
  952.     }

  953.     /**
  954.      * Returns the throwable of the first {@link Throwable}
  955.      * that matches the specified class or subclass in the exception chain.
  956.      * Subclasses of the specified class do match - see
  957.      * {@link #throwableOfThrowable(Throwable, Class)} for the opposite.
  958.      *
  959.      * <p>A {@code null} throwable returns {@code null}.
  960.      * A {@code null} type returns {@code null}.
  961.      * No match in the chain returns {@code null}.</p>
  962.      *
  963.      * @param <T> the type of Throwable you are searching.
  964.      * @param throwable  the throwable to inspect, may be null
  965.      * @param type  the type to search for, subclasses match, null returns null
  966.      * @return the first matching throwable from the throwable chain, null if no match or null input
  967.      * @since 3.10
  968.      */
  969.     public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type) {
  970.         return throwableOf(throwable, type, 0, true);
  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 match - see
  977.      * {@link #throwableOfThrowable(Throwable, Class)} 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 type  the type to search for, subclasses 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 throwableOfType(final Throwable throwable, final Class<T> type, final int fromIndex) {
  994.         return throwableOf(throwable, type, fromIndex, true);
  995.     }

  996.     /**
  997.      * Tests whether the specified {@link Throwable} is unchecked and throws it if so.
  998.      *
  999.      * @param <T> The Throwable type.
  1000.      * @param throwable the throwable to test and throw or return.
  1001.      * @return the given throwable.
  1002.      * @since 3.13.0
  1003.      * @deprecated Use {@link #throwUnchecked(Throwable)}.
  1004.      */
  1005.     @Deprecated
  1006.     public static <T> T throwUnchecked(final T throwable) {
  1007.         if (throwable instanceof RuntimeException) {
  1008.             throw (RuntimeException) throwable;
  1009.         }
  1010.         if (throwable instanceof Error) {
  1011.             throw (Error) throwable;
  1012.         }
  1013.         return throwable;
  1014.     }

  1015.     /**
  1016.      * Tests whether the specified {@link Throwable} is unchecked and throws it if so.
  1017.      *
  1018.      * @param <T> The Throwable type.
  1019.      * @param throwable the throwable to test and throw or return.
  1020.      * @return the given throwable.
  1021.      * @since 3.14.0
  1022.      */
  1023.     public static <T extends Throwable> T throwUnchecked(final T throwable) {
  1024.         if (isUnchecked(throwable)) {
  1025.             throw asRuntimeException(throwable);
  1026.         }
  1027.         return throwable;
  1028.     }

  1029.     /**
  1030.      * Throws a checked exception without adding the exception to the throws
  1031.      * clause of the calling method. For checked exceptions, this method throws
  1032.      * an UndeclaredThrowableException wrapping the checked exception. For
  1033.      * Errors and RuntimeExceptions, the original exception is rethrown.
  1034.      * <p>
  1035.      * The downside to using this approach is that invoking code which needs to
  1036.      * handle specific checked exceptions must sniff up the exception chain to
  1037.      * determine if the caught exception was caused by the checked exception.
  1038.      * </p>
  1039.      *
  1040.      * @param throwable
  1041.      *            The throwable to rethrow.
  1042.      * @param <R> The type of the returned value.
  1043.      * @return Never actually returned, this generic type matches any type
  1044.      *         which the calling site requires. "Returning" the results of this
  1045.      *         method will satisfy the Java compiler requirement that all code
  1046.      *         paths return a value.
  1047.      * @since 3.5
  1048.      * @see #asRuntimeException(Throwable)
  1049.      * @see #hasCause(Throwable, Class)
  1050.      */
  1051.     public static <R> R wrapAndThrow(final Throwable throwable) {
  1052.         throw new UndeclaredThrowableException(throwUnchecked(throwable));
  1053.     }

  1054.     /**
  1055.      * Public constructor allows an instance of {@link ExceptionUtils} to be created, although that is not
  1056.      * normally necessary.
  1057.      *
  1058.      * @deprecated TODO Make private in 4.0.
  1059.      */
  1060.     @Deprecated
  1061.     public ExceptionUtils() {
  1062.         // empty
  1063.     }
  1064. }