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