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