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        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
043        // Initialize the Variables
044        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
045
046        final StackTraceElement[]   steArr  = t.getStackTrace();
047        final StringBuilder         sb      = new StringBuilder();
048        final Throwable             cause   = t.getCause();
049
050        String m    = t.getMessage();
051        String lm   = t.getLocalizedMessage();
052
053        final boolean hasMessage    = (m != null) && (m.length() > 0);
054        final boolean hasLMsg       = (lm != null) && (lm.length() > 0);
055
056        if (hasMessage) m   = StrIndent.indent(m, 1, false, true);
057        if (hasLMsg)    lm  = StrIndent.indent(lm, 1, false, true);
058
059
060        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
061        // Print the "Top Part" / "Header Part" / Error-Message to the Return-Output StringBuilder
062        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
063
064        sb.append(BRED + "THROWN: " + t.getClass().getCanonicalName() + RESET + "\n");
065
066        if (hasMessage)
067        {
068            sb.append(BCYAN + "Throwable.getMessage():\n" + RESET + m + "\n");
069
070            if (hasLMsg && (! m.equals(lm)))
071                sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + lm + "\n");
072        }
073
074        else if (hasLMsg)
075            sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + lm + "\n");
076
077        else
078            sb.append(BYELLOW + "No Exception Messages Provided.\n" + RESET);
079
080
081        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
082        // Print the Stack-Trace
083        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
084
085        sb.append(BCYAN + "StackTrace:\n" + RESET);
086
087        int temp, maxLN = 0;
088
089        for (StackTraceElement ste : steArr)
090            if ((temp = ste.getLineNumber()) > maxLN)
091                maxLN = temp;
092
093        final int base10 =
094            2 + // 2: colon + space
095            ((int) Math.ceil(Math.log10(maxLN)));
096
097        for (int k=0; k < steArr.length; k++) sb.append(
098            '\t' + StringParse.rightSpacePad(steArr[k].getLineNumber() + ":", base10) +
099            steArr[k].getClassName() + "." +
100            steArr[k].getMethodName() + "()\n"
101        );
102
103
104        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
105        // Build the StringBuilder & Return / Exit
106        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
107
108        return (cause == null)
109            ? sb.toString()
110            : sb.toString() + StrIndent.indentTabs(toString(cause), 1);
111    }
112
113    /**
114     * Convenience Method.
115     * <BR />Invokes: {@link #toStringMaxTraces(Throwable, int)}
116     * <BR />And-Then: {@link StrIndent#indentTabs(String, int)}
117     */
118    public static String toStringMaxTraces(Throwable t, int maxNumInvocations, int indentation)
119    { return StrIndent.indent(toStringMaxTraces(t, maxNumInvocations), indentation); }
120
121    /**
122     * Prints the {@code Exception}-message and {@code Exception}-stack trace to an output
123     * {@code String}, and invokes, recursively, this method with any cause-{@code Throwable's}
124     * that are present in this {@code Throwable}.
125     *
126     * @param t Any Java {@code Throwable}.  Java {@code Throwable's} with one or {@code 'cause'}
127     * {@code Throwable's} will utilize more fully the printing-features of this class,
128     * {@code 'EXCC'} - <I>though any {@code Throwable} will be properly printed</I>.
129     * 
130     * @param maxNumInvocations Each of the {@code Throwable's} printed shall have, at most,
131     * {@code 'maxNumInvocations'} of their Stack-Traces printed into the output {@code String}.
132     * 
133     * @return This method doesn't actually print anything to the screen or terminal, it just
134     * returns a {@code String} that you may print yourself - <I>or write to a
135     * {@code class StringBuilder}, or any variation of text-output you wish.</I>
136     */
137    public static String toStringMaxTraces(Throwable t, int maxNumInvocations)
138    {
139        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
140        // Initialize the Variables
141        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
142
143        StackTraceElement[] steArr = t.getStackTrace();
144
145        final StringBuilder sb      = new StringBuilder();
146        final Throwable     cause   = t.getCause();
147
148        String m    = t.getMessage();
149        String lm   = t.getLocalizedMessage();
150
151        final boolean hasMessage    = (m != null) && (m.length() > 0);
152        final boolean hasLMsg       = (lm != null) && (lm.length() > 0);
153
154        if (hasMessage) m   = StrIndent.indent(m, 1, false, true);
155        if (hasLMsg)    lm  = StrIndent.indent(lm, 1, false, true);
156
157
158        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
159        // Print the "Top Part" / "Header Part" / Error-Message to the Return-Output StringBuilder
160        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
161
162        sb.append(BRED + "THROWN: " + t.getClass().getCanonicalName() + RESET + "\n");
163
164        if (hasMessage)
165        {
166            sb.append(BCYAN + "Throwable.getMessage():\n" + RESET + m + "\n");
167
168            if (hasLMsg && (! m.equals(lm)))
169                sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + lm + "\n");
170        }
171
172        else if (hasLMsg)
173            sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + lm + "\n");
174
175        else
176            sb.append(BYELLOW + "No Exception Messages Provided.\n" + RESET);
177
178
179        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
180        // Print the Stack-Trace
181        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
182
183        sb.append(BCYAN + "StackTrace:\n" + RESET);
184
185        int temp, maxLN = 0, stTruncated = 0;
186
187        if (steArr.length > maxNumInvocations)
188        {
189            stTruncated = steArr.length - maxNumInvocations;
190            steArr      = Arrays.copyOf(steArr, maxNumInvocations);
191        }
192
193        for (StackTraceElement ste : steArr)
194            if ((temp = ste.getLineNumber()) > maxLN)
195                maxLN = temp;
196
197        final int base10 =
198            2 + // 2: colon + space
199            ((int) Math.ceil(Math.log10(maxLN)));
200
201
202        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
203        // Build the StringBuilder & Return / Exit
204        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
205
206        for (int k=0; k < steArr.length; k++) sb.append(
207            '\t' + StringParse.rightSpacePad(steArr[k].getLineNumber() + ":", base10) +
208            steArr[k].getClassName() + "." +
209            steArr[k].getMethodName() + "()\n"
210        );
211
212        if (stTruncated > 0)
213            sb.append("\t... and " + stTruncated + " more invocations.\n");
214
215
216        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
217        // Build the StringBuilder & Return / Exit
218        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
219
220        return (cause == null)
221            ? sb.toString()
222            : sb.toString() + StrIndent.indentTabs(toString(cause), 1);
223    }
224
225    /**
226     * Convenience Method.
227     * <BR />Invokes: {@link #toStringNoST(Throwable)}
228     * <BR />And-Then: {@link StrIndent#indentTabs(String, int)}
229     */
230    public static String toStringNoST(Throwable t, int indentation)
231    { return StrIndent.indentTabs(toStringNoST(t), indentation); }
232
233    /**
234     * Prints the {@code Exception}-message to an output {@code String}.  Invokes, recursively,
235     * this method with any cause-{@code Throwable's} that are present in this {@code Throwable}.
236     *
237     * <BR /><BR /><DIV CLASS=JDHint>
238     * This method differs from {@link #toString(Throwable)}, in that it <B>does not print the
239     * {@code StackTrace's} to the output-return {@code String}.</B>
240     * </DIV>
241     * 
242     * @param t Any Java {@code Throwable}.  Java {@code Throwable's} with one or {@code 'cause'}
243     * {@code Throwable's} will utilize more fully the printing-features of this class,
244     * {@code 'EXCC'} - <I>though any {@code Throwable} will be properly printed</I>.
245     * 
246     * @return This method doesn't actually print anything to the screen or terminal, it just
247     * returns a {@code String} that you may print yourself - <I>or write to a
248     * {@code class StorageBuffer,} or any variation of logging you wish.</I>
249     */
250    public static String toStringNoST(Throwable t)
251    {
252        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
253        // Initialize the Variables
254        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
255
256        final StringBuilder sb      = new StringBuilder();
257        final Throwable     cause   = t.getCause();
258
259        String m    = t.getMessage();
260        String lm   = t.getLocalizedMessage();
261
262        final boolean hasMessage    = (m != null) && (m.length() > 0);
263        final boolean hasLMsg       = (lm != null) && (lm.length() > 0);
264
265        if (hasMessage) m   = StrIndent.indent(m, 1, false, true);
266        if (hasLMsg)    lm  = StrIndent.indent(lm, 1, false, true);
267
268
269        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
270        // Print the "Top Part" / "Header Part" / Error-Message to the Return-Output StringBuilder
271        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
272
273        sb.append(BRED + "THROWN: " + t.getClass().getCanonicalName() + RESET + "\n");
274
275        if (hasMessage)
276        {
277            sb.append(BCYAN + "Throwable.getMessage():\n" + RESET + m + "\n");
278
279            if (hasLMsg && (! m.equals(lm)))
280                sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + lm + "\n");
281        }
282
283        else if (hasLMsg)
284            sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + lm + "\n");
285
286        else
287            sb.append(BYELLOW + "No Exception Messages Provided.\n" + RESET);
288
289
290        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
291        // Build the StringBuilder & Return / Exit
292        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
293
294        return (cause == null)
295            ? sb.toString()
296            : sb.toString() + StrIndent.indentTabs(toStringNoST(cause), 1);
297    }
298
299}