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