001package Torello.Java;
002
003import Torello.Java.Shell.C;
004
005/**
006 * <B><CODE>'Exception Cause Chain'</CODE></B> helps convert exception messages whose
007 * <CODE><B>Throwable.cause()</B></CODE> method returns a non-null <CODE>cause</CODE>, thereby
008 * unrolling <I>(and printing)</I> this chain of exceptions into a readable <CODE>String</CODE>.
009 * 
010 * <BR /><BR /><EMBED CLASS="external-html" DATA-FILE-ID="EXCC">
011 */
012@Torello.HTML.Tools.JavaDoc.StaticFunctional
013public class EXCC
014{
015    private EXCC() { }
016
017    /**
018     * Convenience Method.
019     * <BR />Invokes: {@link #toString(Throwable)}
020     * <BR />And-Then: {@link StrIndent#indentTabs(String, int)}
021     */
022    public static String toString(Throwable t, int indentation)
023    { return StrIndent.indentTabs(toString(t), indentation); }
024
025    /**
026     * Prints the {@code Exception}-message and {@code Exception}-stack trace to an output
027     * {@code String}, and invokes, recursively, this method with any cause-{@code Throwable's}
028     * that are present in this {@code Throwable}.
029     * 
030     * @param t Any Java {@code Throwable}
031     * 
032     * @return This method doesn't actually print anything to the screen or terminal, it just
033     * returns a {@code String} that you may print yourself - <I>or write to a
034     * {@code class StringBuilder}, or any variation of text-output you wish.</I>
035     */
036    public static String toString(Throwable t)
037    {
038        StackTraceElement[] steArr      = t.getStackTrace();
039        StringBuilder       sb          = new StringBuilder();
040        Throwable           cause       = t.getCause();
041        String              m           = t.getMessage();
042        String              lm          = t.getLocalizedMessage();
043        boolean             hasMessage  = (m != null) && (m.length() > 0);
044        boolean             hasLMess    = (lm != null) && (lm.length() > 0);
045
046        if (hasMessage) m   = StrIndent.indent(m, 1, false, true);
047        if (hasLMess)   lm  = StrIndent.indent(lm, 1, false, true);
048
049        sb.append(C.BRED + "THROWN: " + t.getClass().getCanonicalName() + C.RESET + "\n");
050
051        if (hasMessage)
052        {
053            sb.append(C.BCYAN + "Throwable.getMessage():\n" + C.RESET + m + "\n");
054
055            if (hasLMess && (! m.equals(lm)))
056                sb.append(C.BCYAN + "Throwable.getLocalizedMessage():\n" + C.RESET + lm + "\n");
057        }
058
059        else if (hasLMess)
060            sb.append(C.BCYAN + "Throwable.getLocalizedMessage():\n" + C.RESET + lm + "\n");
061
062        else
063            sb.append(C.BYELLOW + "No Exception Messages Provided.\n" + C.RESET);
064
065        sb.append(C.BCYAN + "StackTrace:\n" + C.RESET);
066
067        int temp, maxLN = 0;
068
069        for (StackTraceElement ste : steArr)
070
071            if ((temp = ste.getLineNumber()) > maxLN)
072                maxLN = temp;
073
074        int base10 = 2 /* 2: colon + space */ + ((int) Math.ceil(Math.log10(maxLN)));
075
076        for (int k=0; k < steArr.length; k++)
077
078            sb.append(
079                '\t' + StringParse.rightSpacePad(steArr[k].getLineNumber() + ":", base10) +
080                steArr[k].getClassName() + "." +
081                steArr[k].getMethodName() + "()\n"
082            );
083
084        return (cause == null)
085            ? sb.toString()
086            : sb.toString() + StrIndent.indentTabs(toString(cause), 1);
087    }
088
089
090    /**
091     * Prints the {@code Exception}-message to an output {@code String}.  Invokes, recursively,
092     * this method with any cause-{@code Throwable's} that are present in this {@code Throwable}.
093     *
094     * <BR /><BR />This method differs from {@link #toString(Throwable)}, in that it <I>does not
095     * print the {@code StackTrace's} to the output {@code String}.</I>
096     * 
097     * @param t Any Java {@code Throwable}
098     * 
099     * @return This method doesn't actually print anything to the screen or terminal, it just
100     * returns a {@code String} that you may print yourself - <I>or write to a
101     * {@code class StorageBuffer,} or any variation of logging you wish.</I>
102     */
103    public static String toStringNoST(Throwable t)
104    {
105        StringBuilder       sb          = new StringBuilder();
106        Throwable           cause       = t.getCause();
107        String              m           = t.getMessage();
108        String              lm          = t.getLocalizedMessage();
109        boolean             hasMessage  = (m != null) && (m.length() > 0);
110        boolean             hasLMess    = (lm != null) && (lm.length() > 0);
111
112        if (hasMessage) m   = StrIndent.indent(m, 1, false, true);
113        if (hasLMess)   lm  = StrIndent.indent(lm, 1, false, true);
114
115        sb.append(C.BRED + "THROWN: " + t.getClass().getCanonicalName() + C.RESET + "\n");
116
117        if (hasMessage)
118        {
119            sb.append(C.BCYAN + "Throwable.getMessage():\n" + C.RESET + m + "\n");
120
121            if (hasLMess && (! m.equals(lm)))
122                sb.append(C.BCYAN + "Throwable.getLocalizedMessage():\n" + C.RESET + lm + "\n");
123        }
124
125        else if (hasLMess)
126            sb.append(C.BCYAN + "Throwable.getLocalizedMessage():\n" + C.RESET + lm + "\n");
127
128        else
129            sb.append(C.BYELLOW + "No Exception Messages Provided.\n" + C.RESET);
130
131        return (cause == null)
132            ? sb.toString()
133            : sb.toString() + StrIndent.indentTabs(toStringNoST(cause), 1);
134    }
135}