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