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