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