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    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3.exception;
18  
19  import java.io.PrintStream;
20  import java.io.PrintWriter;
21  import java.io.StringWriter;
22  import java.lang.reflect.InvocationTargetException;
23  import java.lang.reflect.Method;
24  import java.lang.reflect.UndeclaredThrowableException;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.List;
28  import java.util.Objects;
29  import java.util.StringTokenizer;
30  import java.util.function.Consumer;
31  import java.util.stream.Stream;
32  
33  import org.apache.commons.lang3.ArrayUtils;
34  import org.apache.commons.lang3.ClassUtils;
35  import org.apache.commons.lang3.StringUtils;
36  
37  /**
38   * Provides utilities for manipulating and examining
39   * {@link Throwable} objects.
40   *
41   * @since 1.0
42   */
43  public class ExceptionUtils {
44  
45      /**
46       * The names of methods commonly used to access a wrapped exception.
47       */
48      // TODO: Remove in Lang 4
49      private static final String[] CAUSE_METHOD_NAMES = {
50          "getCause",
51          "getNextException",
52          "getTargetException",
53          "getException",
54          "getSourceException",
55          "getRootCause",
56          "getCausedByException",
57          "getNested",
58          "getLinkedException",
59          "getNestedException",
60          "getLinkedCause",
61          "getThrowable",
62      };
63  
64      private static final int NOT_FOUND = -1;
65  
66      /**
67       * Used when printing stack frames to denote the start of a
68       * wrapped exception.
69       *
70       * <p>Package private for accessibility by test suite.</p>
71       */
72      static final String WRAPPED_MARKER = " [wrapped] ";
73  
74      /**
75       * Use to throws a checked exception without adding the exception to the throws
76       * clause of the calling method. This method prevents throws clause
77       * pollution and reduces the clutter of "Caused by" exceptions in the
78       * stack trace.
79       * <p>
80       * The use of this technique may be controversial, but exceedingly useful to
81       * library developers.
82       * </p>
83       * <pre>
84       *  public int propagateExample { // note that there is no throws clause
85       *      try {
86       *          return invocation(); // throws IOException
87       *      } catch (Exception e) {
88       *          return ExceptionUtils.rethrowRuntimeException(e);  // propagates a checked exception
89       *      }
90       *  }
91       * </pre>
92       * <p>
93       * This is an alternative to the more conservative approach of wrapping the
94       * checked exception in a RuntimeException:
95       * </p>
96       * <pre>
97       *  public int wrapExample { // note that there is no throws clause
98       *      try {
99       *          return invocation(); // throws IOException
100      *      } catch (Error e) {
101      *          throw e;
102      *      } catch (RuntimeException e) {
103      *          throw e;  // wraps a checked exception
104      *      } catch (Exception e) {
105      *          throw new UndeclaredThrowableException(e);  // wraps a checked exception
106      *      }
107      *  }
108      * </pre>
109      * <p>
110      * One downside to using this approach is that the java compiler will not
111      * allow invoking code to specify a checked exception in a catch clause
112      * unless there is some code path within the try block that has invoked a
113      * method declared with that checked exception. If the invoking site wishes
114      * to catch the shaded checked exception, it must either invoke the shaded
115      * code through a method re-declaring the desired checked exception, or
116      * catch Exception and use the {@code instanceof} operator. Either of these
117      * techniques are required when interacting with non-java jvm code such as
118      * Jython, Scala, or Groovy, since these languages do not consider any
119      * exceptions as checked.
120      * </p>
121      *
122      * @param throwable
123      *            The throwable to rethrow.
124      * @param <T> The type of the returned value.
125      * @return Never actually returned, this generic type matches any type
126      *         which the calling site requires. "Returning" the results of this
127      *         method, as done in the propagateExample above, will satisfy the
128      *         java compiler requirement that all code paths return a value.
129      * @since 3.14.0
130      * @see #wrapAndThrow(Throwable)
131      */
132     public static <T extends RuntimeException> T asRuntimeException(final Throwable throwable) {
133         // claim that the typeErasure invocation throws a RuntimeException
134         return ExceptionUtils.<T, RuntimeException>eraseType(throwable);
135     }
136 
137     /**
138      * Claims a Throwable is another Throwable type using type erasure. This
139      * hides a checked exception from the Java compiler, allowing a checked
140      * exception to be thrown without having the exception in the method's throw
141      * clause.
142      */
143     @SuppressWarnings("unchecked")
144     private static <R, T extends Throwable> R eraseType(final Throwable throwable) throws T {
145         throw (T) throwable;
146     }
147 
148     /**
149      * Performs an action for each Throwable causes of the given Throwable.
150      * <p>
151      * A throwable without cause will return a stream containing one element - the input throwable. A throwable with one cause
152      * will return a stream containing two elements. - the input throwable and the cause throwable. A {@code null} throwable
153      * will return a stream of count zero.
154      * </p>
155      *
156      * <p>
157      * This method handles recursive cause structures that might otherwise cause infinite loops. The cause chain is
158      * processed until the end is reached, or until the next item in the chain is already in the result set.
159      * </p>
160      * @param throwable The Throwable to traverse.
161      * @param consumer a non-interfering action to perform on the elements.
162      * @since 3.13.0
163      */
164     public static void forEach(final Throwable throwable, final Consumer<Throwable> consumer) {
165         stream(throwable).forEach(consumer);
166     }
167 
168     /**
169      * Introspects the {@link Throwable} to obtain the cause.
170      *
171      * <p>The method searches for methods with specific names that return a
172      * {@link Throwable} object. This will pick up most wrapping exceptions,
173      * including those from JDK 1.4.
174      * </p>
175      *
176      * <p>The default list searched for are:</p>
177      * <ul>
178      *  <li>{@code getCause()}</li>
179      *  <li>{@code getNextException()}</li>
180      *  <li>{@code getTargetException()}</li>
181      *  <li>{@code getException()}</li>
182      *  <li>{@code getSourceException()}</li>
183      *  <li>{@code getRootCause()}</li>
184      *  <li>{@code getCausedByException()}</li>
185      *  <li>{@code getNested()}</li>
186      * </ul>
187      *
188      * <p>If none of the above is found, returns {@code null}.</p>
189      *
190      * @param throwable  the throwable to introspect for a cause, may be null
191      * @return the cause of the {@link Throwable},
192      *  {@code null} if none found or null throwable input
193      * @since 1.0
194      * @deprecated This feature will be removed in Lang 4, use {@link Throwable#getCause} instead
195      */
196     @Deprecated
197     public static Throwable getCause(final Throwable throwable) {
198         return getCause(throwable, null);
199     }
200 
201     /**
202      * Introspects the {@link Throwable} to obtain the cause.
203      *
204      * <p>A {@code null} set of method names means use the default set.
205      * A {@code null} in the set of method names will be ignored.</p>
206      *
207      * @param throwable  the throwable to introspect for a cause, may be null
208      * @param methodNames  the method names, null treated as default set
209      * @return the cause of the {@link Throwable},
210      *  {@code null} if none found or null throwable input
211      * @since 1.0
212      * @deprecated This feature will be removed in Lang 4, use {@link Throwable#getCause} instead
213      */
214     @Deprecated
215     public static Throwable getCause(final Throwable throwable, String[] methodNames) {
216         if (throwable == null) {
217             return null;
218         }
219         if (methodNames == null) {
220             final Throwable cause = throwable.getCause();
221             if (cause != null) {
222                 return cause;
223             }
224             methodNames = CAUSE_METHOD_NAMES;
225         }
226         return Stream.of(methodNames).map(m -> getCauseUsingMethodName(throwable, m)).filter(Objects::nonNull).findFirst().orElse(null);
227     }
228 
229     /**
230      * Gets a {@link Throwable} by method name.
231      *
232      * @param throwable  the exception to examine
233      * @param methodName  the name of the method to find and invoke
234      * @return the wrapped exception, or {@code null} if not found
235      */
236     // TODO: Remove in Lang 4
237     private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) {
238         if (methodName != null) {
239             Method method = null;
240             try {
241                 method = throwable.getClass().getMethod(methodName);
242             } catch (final NoSuchMethodException | SecurityException ignored) {
243                 // exception ignored
244             }
245 
246             if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) {
247                 try {
248                     return (Throwable) method.invoke(throwable);
249                 } catch (final IllegalAccessException | IllegalArgumentException | InvocationTargetException ignored) {
250                     // exception ignored
251                 }
252             }
253         }
254         return null;
255     }
256 
257     /**
258      * Gets the default names used when searching for the cause of an exception.
259      *
260      * <p>This may be modified and used in the overloaded getCause(Throwable, String[]) method.</p>
261      *
262      * @return cloned array of the default method names
263      * @since 3.0
264      * @deprecated This feature will be removed in Lang 4
265      */
266     @Deprecated
267     public static String[] getDefaultCauseMethodNames() {
268         return ArrayUtils.clone(CAUSE_METHOD_NAMES);
269     }
270 
271     /**
272      * Gets a short message summarizing the exception.
273      * <p>
274      * The message returned is of the form
275      * {ClassNameWithoutPackage}: {ThrowableMessage}
276      * </p>
277      *
278      * @param th  the throwable to get a message for, null returns empty string
279      * @return the message, non-null
280      * @since 2.2
281      */
282     public static String getMessage(final Throwable th) {
283         if (th == null) {
284             return StringUtils.EMPTY;
285         }
286         final String clsName = ClassUtils.getShortClassName(th, null);
287         return clsName + ": " + StringUtils.defaultString(th.getMessage());
288     }
289 
290     /**
291      * Introspects the {@link Throwable} to obtain the root cause.
292      *
293      * <p>This method walks through the exception chain to the last element,
294      * "root" of the tree, using {@link Throwable#getCause()}, and
295      * returns that exception.</p>
296      *
297      * <p>From version 2.2, this method handles recursive cause structures
298      * that might otherwise cause infinite loops. If the throwable parameter
299      * has a cause of itself, then null will be returned. If the throwable
300      * parameter cause chain loops, the last element in the chain before the
301      * loop is returned.</p>
302      *
303      * @param throwable  the throwable to get the root cause for, may be null
304      * @return the root cause of the {@link Throwable},
305      *  {@code null} if null throwable input
306      */
307     public static Throwable getRootCause(final Throwable throwable) {
308         final List<Throwable> list = getThrowableList(throwable);
309         return list.isEmpty() ? null : list.get(list.size() - 1);
310     }
311 
312     /**
313      * Gets a short message summarizing the root cause exception.
314      * <p>
315      * The message returned is of the form
316      * {ClassNameWithoutPackage}: {ThrowableMessage}
317      * </p>
318      *
319      * @param throwable  the throwable to get a message for, null returns empty string
320      * @return the message, non-null
321      * @since 2.2
322      */
323     public static String getRootCauseMessage(final Throwable throwable) {
324         final Throwable root = getRootCause(throwable);
325         return getMessage(root == null ? throwable : root);
326     }
327 
328     /**
329      * Gets a compact stack trace for the root cause of the supplied
330      * {@link Throwable}.
331      *
332      * <p>The output of this method is consistent across JDK versions.
333      * It consists of the root exception followed by each of its wrapping
334      * exceptions separated by '[wrapped]'. Note that this is the opposite
335      * order to the JDK1.4 display.</p>
336      *
337      * @param throwable  the throwable to examine, may be null
338      * @return an array of stack trace frames, never null
339      * @since 2.0
340      */
341     public static String[] getRootCauseStackTrace(final Throwable throwable) {
342         return getRootCauseStackTraceList(throwable).toArray(ArrayUtils.EMPTY_STRING_ARRAY);
343     }
344 
345     /**
346      * Gets a compact stack trace for the root cause of the supplied {@link Throwable}.
347      *
348      * <p>
349      * The output of this method is consistent across JDK versions. It consists of the root exception followed by each of
350      * its wrapping exceptions separated by '[wrapped]'. Note that this is the opposite order to the JDK1.4 display.
351      * </p>
352      *
353      * @param throwable the throwable to examine, may be null
354      * @return a list of stack trace frames, never null
355      * @since 3.13.0
356      */
357     public static List<String> getRootCauseStackTraceList(final Throwable throwable) {
358         if (throwable == null) {
359             return Collections.emptyList();
360         }
361         final Throwable[] throwables = getThrowables(throwable);
362         final int count = throwables.length;
363         final List<String> frames = new ArrayList<>();
364         List<String> nextTrace = getStackFrameList(throwables[count - 1]);
365         for (int i = count; --i >= 0;) {
366             final List<String> trace = nextTrace;
367             if (i != 0) {
368                 nextTrace = getStackFrameList(throwables[i - 1]);
369                 removeCommonFrames(trace, nextTrace);
370             }
371             if (i == count - 1) {
372                 frames.add(throwables[i].toString());
373             } else {
374                 frames.add(WRAPPED_MARKER + throwables[i].toString());
375             }
376             frames.addAll(trace);
377         }
378         return frames;
379     }
380 
381     /**
382      * Gets a {@link List} of stack frames - the message
383      * is not included. Only the trace of the specified exception is
384      * returned, any caused by trace is stripped.
385      *
386      * <p>This works in most cases - it will only fail if the exception
387      * message contains a line that starts with:
388      * {@code &quot;&nbsp;&nbsp;&nbsp;at&quot;.}</p>
389      *
390      * @param throwable is any throwable
391      * @return List of stack frames
392      */
393     static List<String> getStackFrameList(final Throwable throwable) {
394         final String stackTrace = getStackTrace(throwable);
395         final String linebreak = System.lineSeparator();
396         final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
397         final List<String> list = new ArrayList<>();
398         boolean traceStarted = false;
399         while (frames.hasMoreTokens()) {
400             final String token = frames.nextToken();
401             // Determine if the line starts with <whitespace>at
402             final int at = token.indexOf("at");
403             if (at != NOT_FOUND && token.substring(0, at).trim().isEmpty()) {
404                 traceStarted = true;
405                 list.add(token);
406             } else if (traceStarted) {
407                 break;
408             }
409         }
410         return list;
411     }
412 
413     /**
414      * Gets an array where each element is a line from the argument.
415      *
416      * <p>The end of line is determined by the value of {@link System#lineSeparator()}.</p>
417      *
418      * @param stackTrace  a stack trace String
419      * @return an array where each element is a line from the argument
420      */
421     static String[] getStackFrames(final String stackTrace) {
422         final String linebreak = System.lineSeparator();
423         final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
424         final List<String> list = new ArrayList<>();
425         while (frames.hasMoreTokens()) {
426             list.add(frames.nextToken());
427         }
428         return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
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>The result of this method vary by JDK version as this method
437      * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
438      * On JDK1.3 and earlier, the cause exception will not be shown
439      * unless the specified throwable alters printStackTrace.</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.
453      *
454      * <p>The result of this method vary by JDK version as this method
455      * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
456      * On JDK1.3 and earlier, the cause exception will not be shown
457      * unless the specified throwable alters printStackTrace.</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>From version 2.2, this method handles recursive cause structures
481      * that might otherwise cause infinite loops. The cause chain is
482      * processed until the end is reached, or until the next item in the
483      * chain is already in the result set.</p>
484      *
485      * @param throwable  the throwable to inspect, may be null
486      * @return the count of throwables, zero if 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 structures that might
503      * otherwise cause infinite loops. The cause chain is processed until
504      * the end is reached, or until the next item in the chain is already
505      * in the result set.</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>From version 2.2, this method handles recursive cause structures
531      * that might otherwise cause infinite loops. The cause chain is
532      * processed until the end is reached, or until the next item in the
533      * chain is already in the result set.</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      *
714      * <p>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.</p>
718      *
719      * <p>The output of this method is consistent across JDK versions.
720      * Note that this is the opposite order to the JDK1.4 display.</p>
721      *
722      * <p>The method is equivalent to {@code printStackTrace} for throwables
723      * that don't have nested causes.</p>
724      *
725      * @param throwable  the throwable to output
726      * @since 2.0
727      */
728     public static void printRootCauseStackTrace(final Throwable throwable) {
729         printRootCauseStackTrace(throwable, System.err);
730     }
731 
732     /**
733      * Prints a compact stack trace for the root cause of a throwable.
734      *
735      * <p>The compact stack trace starts with the root cause and prints
736      * stack frames up to the place where it was caught and wrapped.
737      * Then it prints the wrapped exception and continues with stack frames
738      * until the wrapper exception is caught and wrapped again, etc.</p>
739      *
740      * <p>The output of this method is consistent across JDK versions.
741      * Note that this is the opposite order to the JDK1.4 display.</p>
742      *
743      * <p>The method is equivalent to {@code printStackTrace} for throwables
744      * that don't have nested causes.</p>
745      *
746      * @param throwable  the throwable to output, may be null
747      * @param printStream  the stream to output to, may not be null
748      * @throws NullPointerException if the printStream is {@code null}
749      * @since 2.0
750      */
751     @SuppressWarnings("resource")
752     public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream printStream) {
753         if (throwable == null) {
754             return;
755         }
756         Objects.requireNonNull(printStream, "printStream");
757         getRootCauseStackTraceList(throwable).forEach(printStream::println);
758         printStream.flush();
759     }
760 
761     /**
762      * Prints a compact stack trace for the root cause of a throwable.
763      *
764      * <p>The compact stack trace starts with the root cause and prints
765      * stack frames up to the place where it was caught and wrapped.
766      * Then it prints the wrapped exception and continues with stack frames
767      * until the wrapper exception is caught and wrapped again, etc.</p>
768      *
769      * <p>The output of this method is consistent across JDK versions.
770      * Note that this is the opposite order to the JDK1.4 display.</p>
771      *
772      * <p>The method is equivalent to {@code printStackTrace} for throwables
773      * that don't have nested causes.</p>
774      *
775      * @param throwable  the throwable to output, may be null
776      * @param printWriter  the writer to output to, may not be null
777      * @throws NullPointerException if the printWriter is {@code null}
778      * @since 2.0
779      */
780     @SuppressWarnings("resource")
781     public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter printWriter) {
782         if (throwable == null) {
783             return;
784         }
785         Objects.requireNonNull(printWriter, "printWriter");
786         getRootCauseStackTraceList(throwable).forEach(printWriter::println);
787         printWriter.flush();
788     }
789 
790     /**
791      * Removes common frames from the cause trace given the two stack traces.
792      *
793      * @param causeFrames  stack trace of a cause throwable
794      * @param wrapperFrames  stack trace of a wrapper throwable
795      * @throws NullPointerException if either argument is null
796      * @since 2.0
797      */
798     public static void removeCommonFrames(final List<String> causeFrames, final List<String> wrapperFrames) {
799         Objects.requireNonNull(causeFrames, "causeFrames");
800         Objects.requireNonNull(wrapperFrames, "wrapperFrames");
801         int causeFrameIndex = causeFrames.size() - 1;
802         int wrapperFrameIndex = wrapperFrames.size() - 1;
803         while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) {
804             // Remove the frame from the cause trace if it is the same
805             // as in the wrapper trace
806             final String causeFrame = causeFrames.get(causeFrameIndex);
807             final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex);
808             if (causeFrame.equals(wrapperFrame)) {
809                 causeFrames.remove(causeFrameIndex);
810             }
811             causeFrameIndex--;
812             wrapperFrameIndex--;
813         }
814     }
815 
816     /**
817      * Use to throw a checked exception without adding the exception to the throws
818      * clause of the calling method. This method prevents throws clause
819      * pollution and reduces the clutter of "Caused by" exceptions in the
820      * stack trace.
821      * <p>
822      * The use of this technique may be controversial, but exceedingly useful to
823      * library developers.
824      * </p>
825      * <pre>
826      *  public int propagateExample { // note that there is no throws clause
827      *      try {
828      *          return invocation(); // throws IOException
829      *      } catch (Exception e) {
830      *          return ExceptionUtils.rethrow(e);  // propagates a checked exception
831      *      }
832      *  }
833      * </pre>
834      * <p>
835      * This is an alternative to the more conservative approach of wrapping the
836      * checked exception in a RuntimeException:
837      * </p>
838      * <pre>
839      *  public int wrapExample { // note that there is no throws clause
840      *      try {
841      *          return invocation(); // throws IOException
842      *      } catch (Error e) {
843      *          throw e;
844      *      } catch (RuntimeException e) {
845      *          throw e;  // wraps a checked exception
846      *      } catch (Exception e) {
847      *          throw new UndeclaredThrowableException(e);  // wraps a checked exception
848      *      }
849      *  }
850      * </pre>
851      * <p>
852      * One downside to using this approach is that the java compiler will not
853      * allow invoking code to specify a checked exception in a catch clause
854      * unless there is some code path within the try block that has invoked a
855      * method declared with that checked exception. If the invoking site wishes
856      * to catch the shaded checked exception, it must either invoke the shaded
857      * code through a method re-declaring the desired checked exception, or
858      * catch Exception and use the {@code instanceof} operator. Either of these
859      * techniques are required when interacting with non-java jvm code such as
860      * Jython, Scala, or Groovy, since these languages do not consider any
861      * exceptions as checked.
862      * </p>
863      *
864      * @param throwable
865      *            The throwable to rethrow.
866      * @param <T> The type of the returned value.
867      * @return Never actually returned, this generic type matches any type
868      *         which the calling site requires. "Returning" the results of this
869      *         method, as done in the propagateExample above, will satisfy the
870      *         java compiler requirement that all code paths return a value.
871      * @since 3.5
872      * @see #wrapAndThrow(Throwable)
873      * @deprecated Use {@link #asRuntimeException(Throwable)}.
874      */
875     @Deprecated
876     public static <T> T rethrow(final Throwable throwable) {
877         // claim that the typeErasure invocation throws a RuntimeException
878         return ExceptionUtils.<T, RuntimeException>eraseType(throwable);
879     }
880 
881     /**
882      * Streams causes of a Throwable.
883      * <p>
884      * A throwable without cause will return a stream containing one element - the input throwable. A throwable with one cause
885      * will return a stream containing two elements. - the input throwable and the cause throwable. A {@code null} throwable
886      * will return a stream of count zero.
887      * </p>
888      *
889      * <p>
890      * This method handles recursive cause structures that might otherwise cause infinite loops. The cause chain is
891      * processed until the end is reached, or until the next item in the chain is already in the result set.
892      * </p>
893      *
894      * @param throwable The Throwable to traverse
895      * @return A new Stream of Throwable causes.
896      * @since 3.13.0
897      */
898     public static Stream<Throwable> stream(final Throwable throwable) {
899         // No point building a custom Iterable as it would keep track of visited elements to avoid infinite loops
900         return getThrowableList(throwable).stream();
901     }
902 
903     /**
904      * Worker method for the {@code throwableOfType} methods.
905      *
906      * @param <T> the type of Throwable you are searching.
907      * @param throwable  the throwable to inspect, may be null
908      * @param type  the type to search, subclasses match, null returns null
909      * @param fromIndex  the (zero-based) index of the starting position,
910      *  negative treated as zero, larger than chain size returns null
911      * @param subclass if {@code true}, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares
912      * using references
913      * @return throwable of the {@code type} within throwables nested within the specified {@code throwable}
914      */
915     private static <T extends Throwable> T throwableOf(final Throwable throwable, final Class<T> type, int fromIndex, final boolean subclass) {
916         if (throwable == null || type == null) {
917             return null;
918         }
919         if (fromIndex < 0) {
920             fromIndex = 0;
921         }
922         final Throwable[] throwables = getThrowables(throwable);
923         if (fromIndex >= throwables.length) {
924             return null;
925         }
926         if (subclass) {
927             for (int i = fromIndex; i < throwables.length; i++) {
928                 if (type.isAssignableFrom(throwables[i].getClass())) {
929                     return type.cast(throwables[i]);
930                 }
931             }
932         } else {
933             for (int i = fromIndex; i < throwables.length; i++) {
934                 if (type.equals(throwables[i].getClass())) {
935                     return type.cast(throwables[i]);
936                 }
937             }
938         }
939         return null;
940     }
941 
942     /**
943      * Returns the first {@link Throwable}
944      * that matches the specified class (exactly) in the exception chain.
945      * Subclasses of the specified class do not match - see
946      * {@link #throwableOfType(Throwable, Class)} for the opposite.
947      *
948      * <p>A {@code null} throwable returns {@code null}.
949      * A {@code null} type returns {@code null}.
950      * No match in the chain returns {@code null}.</p>
951      *
952      * @param <T> the type of Throwable you are searching.
953      * @param throwable  the throwable to inspect, may be null
954      * @param clazz  the class to search for, subclasses do not match, null returns null
955      * @return the first matching throwable from the throwable chain, null if no match or null input
956      * @since 3.10
957      */
958     public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz) {
959         return throwableOf(throwable, clazz, 0, false);
960     }
961 
962     /**
963      * Returns the first {@link Throwable}
964      * that matches the specified type in the exception chain from
965      * a specified index.
966      * Subclasses of the specified class do not match - see
967      * {@link #throwableOfType(Throwable, Class, int)} for the opposite.
968      *
969      * <p>A {@code null} throwable returns {@code null}.
970      * A {@code null} type returns {@code null}.
971      * No match in the chain returns {@code null}.
972      * A negative start index is treated as zero.
973      * A start index greater than the number of throwables returns {@code null}.</p>
974      *
975      * @param <T> the type of Throwable you are searching.
976      * @param throwable  the throwable to inspect, may be null
977      * @param clazz  the class to search for, subclasses do not match, null returns null
978      * @param fromIndex  the (zero-based) index of the starting position,
979      *  negative treated as zero, larger than chain size returns null
980      * @return the first matching throwable from the throwable chain, null if no match or null input
981      * @since 3.10
982      */
983     public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz, final int fromIndex) {
984         return throwableOf(throwable, clazz, fromIndex, false);
985     }
986 
987     /**
988      * Returns the throwable of the first {@link Throwable}
989      * that matches the specified class or subclass in the exception chain.
990      * Subclasses of the specified class do match - see
991      * {@link #throwableOfThrowable(Throwable, Class)} for the opposite.
992      *
993      * <p>A {@code null} throwable returns {@code null}.
994      * A {@code null} type returns {@code null}.
995      * No match in the chain returns {@code null}.</p>
996      *
997      * @param <T> the type of Throwable you are searching.
998      * @param throwable  the throwable to inspect, may be null
999      * @param type  the type to search for, subclasses match, null returns null
1000      * @return the first matching throwable from the throwable chain, null if no match or null input
1001      * @since 3.10
1002      */
1003     public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type) {
1004         return throwableOf(throwable, type, 0, true);
1005     }
1006 
1007     /**
1008      * Returns the first {@link Throwable}
1009      * that matches the specified type in the exception chain from
1010      * a specified index.
1011      * Subclasses of the specified class do match - see
1012      * {@link #throwableOfThrowable(Throwable, Class)} for the opposite.
1013      *
1014      * <p>A {@code null} throwable returns {@code null}.
1015      * A {@code null} type returns {@code null}.
1016      * No match in the chain returns {@code null}.
1017      * A negative start index is treated as zero.
1018      * A start index greater than the number of throwables returns {@code null}.</p>
1019      *
1020      * @param <T> the type of Throwable you are searching.
1021      * @param throwable  the throwable to inspect, may be null
1022      * @param type  the type to search for, subclasses match, null returns null
1023      * @param fromIndex  the (zero-based) index of the starting position,
1024      *  negative treated as zero, larger than chain size returns null
1025      * @return the first matching throwable from the throwable chain, null if no match or null input
1026      * @since 3.10
1027      */
1028     public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type, final int fromIndex) {
1029         return throwableOf(throwable, type, fromIndex, true);
1030     }
1031 
1032     /**
1033      * Tests whether the specified {@link Throwable} is unchecked and throws it if so.
1034      *
1035      * @param <T> The Throwable type.
1036      * @param throwable the throwable to test and throw or return.
1037      * @return the given throwable.
1038      * @since 3.13.0
1039      * @deprecated Use {@link #throwUnchecked(Throwable)}.
1040      */
1041     @Deprecated
1042     public static <T> T throwUnchecked(final T throwable) {
1043         if (throwable instanceof RuntimeException) {
1044             throw (RuntimeException) throwable;
1045         }
1046         if (throwable instanceof Error) {
1047             throw (Error) throwable;
1048         }
1049         return throwable;
1050     }
1051 
1052     /**
1053      * Tests whether the specified {@link Throwable} is unchecked and throws it if so.
1054      *
1055      * @param <T> The Throwable type.
1056      * @param throwable the throwable to test and throw or return.
1057      * @return the given throwable.
1058      * @since 3.14.0
1059      */
1060     public static <T extends Throwable> T throwUnchecked(final T throwable) {
1061         if (isUnchecked(throwable)) {
1062             throw asRuntimeException(throwable);
1063         }
1064         return throwable;
1065     }
1066 
1067     /**
1068      * Throws a checked exception without adding the exception to the throws
1069      * clause of the calling method. For checked exceptions, this method throws
1070      * an UndeclaredThrowableException wrapping the checked exception. For
1071      * Errors and RuntimeExceptions, the original exception is rethrown.
1072      * <p>
1073      * The downside to using this approach is that invoking code which needs to
1074      * handle specific checked exceptions must sniff up the exception chain to
1075      * determine if the caught exception was caused by the checked exception.
1076      * </p>
1077      *
1078      * @param throwable
1079      *            The throwable to rethrow.
1080      * @param <R> The type of the returned value.
1081      * @return Never actually returned, this generic type matches any type
1082      *         which the calling site requires. "Returning" the results of this
1083      *         method will satisfy the java compiler requirement that all code
1084      *         paths return a value.
1085      * @since 3.5
1086      * @see #asRuntimeException(Throwable)
1087      * @see #hasCause(Throwable, Class)
1088      */
1089     public static <R> R wrapAndThrow(final Throwable throwable) {
1090         throw new UndeclaredThrowableException(throwUnchecked(throwable));
1091     }
1092 
1093     /**
1094      * Public constructor allows an instance of {@link ExceptionUtils} to be created, although that is not
1095      * normally necessary.
1096      *
1097      * @deprecated Will be private in 3.0.
1098      */
1099     @Deprecated
1100     public ExceptionUtils() {
1101     }
1102 }