001package Torello.Java;
002
003import static Torello.Java.C.*;
004
005import java.util.Arrays;
006
007/**
008 * <B><CODE>'Exception Cause Chain'</CODE></B> helps convert exception messages whose
009 * <CODE><B>Throwable.cause()</B></CODE> method returns a non-null <CODE>cause</CODE>, thereby
010 * unrolling <I>(and printing)</I> this chain of exceptions into a readable <CODE>String</CODE>.
011 * 
012 * <EMBED CLASS=external-html DATA-FILE-ID=EXCC>
013 */
014@Torello.JavaDoc.StaticFunctional
015public class EXCC
016{
017    private EXCC() { }
018
019    /**
020     * Convenience Method.
021     * <BR />Invokes: {@link #toString(Throwable)}
022     * <BR />And-Then: {@link StrIndent#indentTabs(String, int)}
023     */
024    public static String toString(Throwable t, int indentation)
025    { return StrIndent.indentTabs(toString(t), indentation); }
026
027    /**
028     * Prints the {@code Exception}-message and {@code Exception}-stack trace to an output
029     * {@code String}, and invokes, recursively, this method with any cause-{@code Throwable's}
030     * that are present in this {@code Throwable}.
031     *
032     * @param t Any Java {@code Throwable}.  Java {@code Throwable's} with one or {@code 'cause'}
033     * {@code Throwable's} will utilize more fully the printing-features of this class,
034     * {@code 'EXCC'} - <I>though any {@code Throwable} will be properly printed</I>.
035     * 
036     * @return This method doesn't actually print anything to the screen or terminal, it just
037     * returns a {@code String} that you may print yourself - <I>or write to a
038     * {@code class StringBuilder}, or any variation of text-output you wish.</I>
039     */
040    public static String toString(Throwable t)
041    {
042        StackTraceElement[] steArr      = t.getStackTrace();
043        StringBuilder       sb          = new StringBuilder();
044        Throwable           cause       = t.getCause();
045        String              m           = t.getMessage();
046        String              lm          = t.getLocalizedMessage();
047        boolean             hasMessage  = (m != null) && (m.length() > 0);
048        boolean             hasLMess    = (lm != null) && (lm.length() > 0);
049
050        if (hasMessage) m   = StrIndent.indent(m, 1, false, true);
051        if (hasLMess)   lm  = StrIndent.indent(lm, 1, false, true);
052
053        sb.append(BRED + "THROWN: " + t.getClass().getCanonicalName() + RESET + "\n");
054
055        if (hasMessage)
056        {
057            sb.append(BCYAN + "Throwable.getMessage():\n" + RESET + m + "\n");
058
059            if (hasLMess && (! m.equals(lm)))
060                sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + lm + "\n");
061        }
062
063        else if (hasLMess)
064            sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + lm + "\n");
065
066        else
067            sb.append(BYELLOW + "No Exception Messages Provided.\n" + RESET);
068
069        sb.append(BCYAN + "StackTrace:\n" + RESET);
070
071        int temp, maxLN = 0;
072
073        for (StackTraceElement ste : steArr)
074
075            if ((temp = ste.getLineNumber()) > maxLN)
076                maxLN = temp;
077
078        int base10 = 2 /* 2: colon + space */ + ((int) Math.ceil(Math.log10(maxLN)));
079
080        for (int k=0; k < steArr.length; k++)
081
082            sb.append(
083                '\t' + StringParse.rightSpacePad(steArr[k].getLineNumber() + ":", base10) +
084                steArr[k].getClassName() + "." +
085                steArr[k].getMethodName() + "()\n"
086            );
087
088        return (cause == null)
089            ? sb.toString()
090            : sb.toString() + StrIndent.indentTabs(toString(cause), 1);
091    }
092
093    /**
094     * Convenience Method.
095     * <BR />Invokes: {@link #toStringMaxTraces(Throwable, int)}
096     * <BR />And-Then: {@link StrIndent#indentTabs(String, int)}
097     */
098    public static String toStringMaxTraces(Throwable t, int maxNumInvocations, int indentation)
099    { return StrIndent.indent(toStringMaxTraces(t, maxNumInvocations), indentation); }
100
101    /**
102     * Prints the {@code Exception}-message and {@code Exception}-stack trace to an output
103     * {@code String}, and invokes, recursively, this method with any cause-{@code Throwable's}
104     * that are present in this {@code Throwable}.
105     *
106     * @param t Any Java {@code Throwable}.  Java {@code Throwable's} with one or {@code 'cause'}
107     * {@code Throwable's} will utilize more fully the printing-features of this class,
108     * {@code 'EXCC'} - <I>though any {@code Throwable} will be properly printed</I>.
109     * 
110     * @param maxNumInvocations Each of the {@code Throwable's} printed shall have, at most,
111     * {@code 'maxNumInvocations'} of their Stack-Traces printed into the output {@code String}.
112     * 
113     * @return This method doesn't actually print anything to the screen or terminal, it just
114     * returns a {@code String} that you may print yourself - <I>or write to a
115     * {@code class StringBuilder}, or any variation of text-output you wish.</I>
116     */
117    public static String toStringMaxTraces(Throwable t, int maxNumInvocations)
118    {
119        StackTraceElement[] steArr      = t.getStackTrace();
120        StringBuilder       sb          = new StringBuilder();
121        Throwable           cause       = t.getCause();
122        String              m           = t.getMessage();
123        String              lm          = t.getLocalizedMessage();
124        boolean             hasMessage  = (m != null) && (m.length() > 0);
125        boolean             hasLMess    = (lm != null) && (lm.length() > 0);
126
127        if (hasMessage) m   = StrIndent.indent(m, 1, false, true);
128        if (hasLMess)   lm  = StrIndent.indent(lm, 1, false, true);
129
130        sb.append(BRED + "THROWN: " + t.getClass().getCanonicalName() + RESET + "\n");
131
132        if (hasMessage)
133        {
134            sb.append(BCYAN + "Throwable.getMessage():\n" + RESET + m + "\n");
135
136            if (hasLMess && (! m.equals(lm)))
137                sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + lm + "\n");
138        }
139
140        else if (hasLMess)
141            sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + lm + "\n");
142
143        else
144            sb.append(BYELLOW + "No Exception Messages Provided.\n" + RESET);
145
146        sb.append(BCYAN + "StackTrace:\n" + RESET);
147
148        int temp, maxLN = 0;
149
150        int stTruncated = 0;
151
152        if (steArr.length > maxNumInvocations)
153        {
154            stTruncated = steArr.length - maxNumInvocations;
155            steArr = Arrays.copyOf(steArr, maxNumInvocations);
156        }
157
158        for (StackTraceElement ste : steArr)
159
160            if ((temp = ste.getLineNumber()) > maxLN)
161                maxLN = temp;
162
163        int base10 = 2 /* 2: colon + space */ + ((int) Math.ceil(Math.log10(maxLN)));
164
165        for (int k=0; k < steArr.length; k++)
166
167            sb.append(
168                '\t' + StringParse.rightSpacePad(steArr[k].getLineNumber() + ":", base10) +
169                steArr[k].getClassName() + "." +
170                steArr[k].getMethodName() + "()\n"
171            );
172
173        if (stTruncated > 0) sb.append("\t... and " + stTruncated + " more invocations.\n");
174
175        return (cause == null)
176            ? sb.toString()
177            : sb.toString() + StrIndent.indentTabs(toString(cause), 1);
178    }
179
180    /**
181     * Convenience Method.
182     * <BR />Invokes: {@link #toStringNoST(Throwable)}
183     * <BR />And-Then: {@link StrIndent#indentTabs(String, int)}
184     */
185    public static String toStringNoST(Throwable t, int indentation)
186    { return StrIndent.indentTabs(toStringNoST(t), indentation); }
187
188    /**
189     * Prints the {@code Exception}-message to an output {@code String}.  Invokes, recursively,
190     * this method with any cause-{@code Throwable's} that are present in this {@code Throwable}.
191     *
192     * <BR /><BR />This method differs from {@link #toString(Throwable)}, in that it <I>does not
193     * print the {@code StackTrace's} to the output {@code String}.</I>
194     * 
195     * @param t Any Java {@code Throwable}.  Java {@code Throwable's} with one or {@code 'cause'}
196     * {@code Throwable's} will utilize more fully the printing-features of this class,
197     * {@code 'EXCC'} - <I>though any {@code Throwable} will be properly printed</I>.
198     * 
199     * @return This method doesn't actually print anything to the screen or terminal, it just
200     * returns a {@code String} that you may print yourself - <I>or write to a
201     * {@code class StorageBuffer,} or any variation of logging you wish.</I>
202     */
203    public static String toStringNoST(Throwable t)
204    {
205        StringBuilder       sb          = new StringBuilder();
206        Throwable           cause       = t.getCause();
207        String              m           = t.getMessage();
208        String              lm          = t.getLocalizedMessage();
209        boolean             hasMessage  = (m != null) && (m.length() > 0);
210        boolean             hasLMess    = (lm != null) && (lm.length() > 0);
211
212        if (hasMessage) m   = StrIndent.indent(m, 1, false, true);
213        if (hasLMess)   lm  = StrIndent.indent(lm, 1, false, true);
214
215        sb.append(BRED + "THROWN: " + t.getClass().getCanonicalName() + RESET + "\n");
216
217        if (hasMessage)
218        {
219            sb.append(BCYAN + "Throwable.getMessage():\n" + RESET + m + "\n");
220
221            if (hasLMess && (! m.equals(lm)))
222                sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + lm + "\n");
223        }
224
225        else if (hasLMess)
226            sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + lm + "\n");
227
228        else
229            sb.append(BYELLOW + "No Exception Messages Provided.\n" + RESET);
230
231        return (cause == null)
232            ? sb.toString()
233            : sb.toString() + StrIndent.indentTabs(toStringNoST(cause), 1);
234    }
235
236}