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