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