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