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