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