001package Torello.Java; 002 003import static Torello.Java.C.*; 004 005import java.lang.reflect.InvocationTargetException; 006import java.util.Arrays; 007 008/** 009 * <B><CODE>'Exception Cause Chain'</CODE></B> helps convert exception messages whose 010 * <CODE><B>Throwable.cause()</B></CODE> method returns a non-null <CODE>cause</CODE>, thereby 011 * unrolling <I>(and printing)</I> this chain of exceptions into a readable <CODE>String</CODE>. 012 * 013 * <EMBED CLASS=external-html DATA-FILE-ID=EXCC> 014 */ 015@Torello.JavaDoc.Annotations.StaticFunctional 016public class EXCC 017{ 018 private EXCC() { } 019 020 021 // ******************************************************************************************** 022 // ******************************************************************************************** 023 // Print the Cause Chain 024 // ******************************************************************************************** 025 // ******************************************************************************************** 026 027 028 /** 029 * Convenience Method. 030 * <BR />Invokes: {@link #toString(Throwable)} 031 * <BR />And-Then: {@link StrIndent#indentTabs(String, int)} 032 */ 033 public static String toString(Throwable t, int indentation) 034 { return StrIndent.indentTabs(toString(t), indentation); } 035 036 /** 037 * Prints the {@code Exception}-message and {@code Exception}-stack trace to an output 038 * {@code String}, and invokes, recursively, this method with any cause-{@code Throwable's} 039 * that are present in this {@code Throwable}. 040 * 041 * @param t Any Java {@code Throwable}. Java {@code Throwable's} with one or {@code 'cause'} 042 * {@code Throwable's} will utilize more fully the printing-features of this class, 043 * {@code 'EXCC'} - <I>though any {@code Throwable} will be properly printed</I>. 044 * 045 * @return This method doesn't actually print anything to the screen or terminal, it just 046 * returns a {@code String} that you may print yourself - <I>or write to a 047 * {@code class StringBuilder}, or any variation of text-output you wish.</I> 048 */ 049 public static String toString(Throwable t) 050 { 051 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 052 // Initialize the Variables 053 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 054 055 final StackTraceElement[] steArr = t.getStackTrace(); 056 final StringBuilder sb = new StringBuilder(); 057 final Throwable cause = t.getCause(); 058 final Throwable legacyCause = legacyCause(t); 059 final boolean hasCause = (cause != null); 060 final boolean hasLegacyCause = (legacyCause != null) && (legacyCause != cause); 061 062 String msg = t.getMessage(); 063 String localMsg = t.getLocalizedMessage(); 064 String legacyMsg = (hasLegacyCause) ? legacyMsg(t) : null; 065 066 final boolean hasMessage = (msg != null) && (msg.length() > 0); 067 final boolean hasLocalMsg = (localMsg != null) && (localMsg.length() > 0); 068 069 if (hasMessage) msg = StrIndent.indent(msg, 1, false, true); 070 if (hasLocalMsg) localMsg = StrIndent.indent(localMsg, 1, false, true); 071 072 073 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 074 // Print the "Top Part" / "Header Part" / Error-Message to the Return-Output StringBuilder 075 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 076 077 sb.append(BRED + "THROWN: " + t.getClass().getCanonicalName() + RESET + "\n"); 078 079 if (hasMessage) 080 { 081 sb.append(BCYAN + "Throwable.getMessage():\n" + RESET + msg + "\n"); 082 083 if (hasLocalMsg && (! msg.equals(localMsg))) 084 sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + localMsg + "\n"); 085 } 086 087 else if (hasLocalMsg) 088 sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + localMsg + "\n"); 089 090 else 091 sb.append(BYELLOW + "No Exception Messages Provided.\n" + RESET); 092 093 094 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 095 // Print the Stack-Trace 096 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 097 098 sb.append(BCYAN + "StackTrace:\n" + RESET); 099 100 int temp, maxLN = 0; 101 102 for (StackTraceElement ste : steArr) 103 if ((temp = ste.getLineNumber()) > maxLN) 104 maxLN = temp; 105 106 final int base10 = 107 2 + // 2: colon + space 108 ((int) Math.ceil(Math.log10(maxLN))); 109 110 for (int k=0; k < steArr.length; k++) sb.append( 111 '\t' + StringParse.rightSpacePad(steArr[k].getLineNumber() + ":", base10) + 112 steArr[k].getClassName() + "." + 113 steArr[k].getMethodName() + "()\n" 114 ); 115 116 117 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 118 // Build the StringBuilder & Return / Exit 119 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 120 121 return 122 sb.toString() + 123 (hasCause ? StrIndent.indentTabs(toString(cause), 1) : "") + 124 (hasLegacyCause ? (legacyMsg + StrIndent.indentTabs(toString(legacyCause), 1)) : ""); 125 } 126 127 128 // ******************************************************************************************** 129 // ******************************************************************************************** 130 // Variant: Abbreviated Cause-Chain Printing 131 // ******************************************************************************************** 132 // ******************************************************************************************** 133 134 135 /** 136 * Convenience Method. 137 * <BR />Invokes: {@link #toStringMaxTraces(Throwable, int)} 138 * <BR />And-Then: {@link StrIndent#indentTabs(String, int)} 139 */ 140 public static String toStringMaxTraces(Throwable t, int maxNumInvocations, int indentation) 141 { return StrIndent.indent(toStringMaxTraces(t, maxNumInvocations), indentation); } 142 143 /** 144 * Prints the {@code Exception}-message and {@code Exception}-stack trace to an output 145 * {@code String}, and invokes, recursively, this method with any cause-{@code Throwable's} 146 * that are present in this {@code Throwable}. 147 * 148 * @param t Any Java {@code Throwable}. Java {@code Throwable's} with one or {@code 'cause'} 149 * {@code Throwable's} will utilize more fully the printing-features of this class, 150 * {@code 'EXCC'} - <I>though any {@code Throwable} will be properly printed</I>. 151 * 152 * @param maxNumInvocations Each of the {@code Throwable's} printed shall have, at most, 153 * {@code 'maxNumInvocations'} of their Stack-Traces printed into the output {@code String}. 154 * 155 * @return This method doesn't actually print anything to the screen or terminal, it just 156 * returns a {@code String} that you may print yourself - <I>or write to a 157 * {@code class StringBuilder}, or any variation of text-output you wish.</I> 158 */ 159 public static String toStringMaxTraces(Throwable t, int maxNumInvocations) 160 { 161 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 162 // Initialize the Variables 163 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 164 165 StackTraceElement[] steArr = t.getStackTrace(); 166 167 final StringBuilder sb = new StringBuilder(); 168 final Throwable cause = t.getCause(); 169 final Throwable legacyCause = legacyCause(t); 170 final boolean hasCause = (cause != null); 171 final boolean hasLegacyCause = (legacyCause != null) && (legacyCause != cause); 172 173 String msg = t.getMessage(); 174 String localMsg = t.getLocalizedMessage(); 175 String legacyMsg = (hasLegacyCause) ? legacyMsg(t) : null; 176 177 final boolean hasMessage = (msg != null) && (msg.length() > 0); 178 final boolean hasLocalMsg = (localMsg != null) && (localMsg.length() > 0); 179 180 if (hasMessage) msg = StrIndent.indent(msg, 1, false, true); 181 if (hasLocalMsg) localMsg = StrIndent.indent(localMsg, 1, false, true); 182 183 184 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 185 // Print the "Top Part" / "Header Part" / Error-Message to the Return-Output StringBuilder 186 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 187 188 sb.append(BRED + "THROWN: " + t.getClass().getCanonicalName() + RESET + "\n"); 189 190 if (hasMessage) 191 { 192 sb.append(BCYAN + "Throwable.getMessage():\n" + RESET + msg + "\n"); 193 194 if (hasLocalMsg && (! msg.equals(localMsg))) 195 sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + localMsg + "\n"); 196 } 197 198 else if (hasLocalMsg) 199 sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + localMsg + "\n"); 200 201 else 202 sb.append(BYELLOW + "No Exception Messages Provided.\n" + RESET); 203 204 205 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 206 // Print the Stack-Trace 207 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 208 209 sb.append(BCYAN + "StackTrace:\n" + RESET); 210 211 int temp, maxLN = 0, stTruncated = 0; 212 213 if (steArr.length > maxNumInvocations) 214 { 215 stTruncated = steArr.length - maxNumInvocations; 216 steArr = Arrays.copyOf(steArr, maxNumInvocations); 217 } 218 219 for (StackTraceElement ste : steArr) 220 if ((temp = ste.getLineNumber()) > maxLN) 221 maxLN = temp; 222 223 final int base10 = 224 2 + // 2: colon + space 225 ((int) Math.ceil(Math.log10(maxLN))); 226 227 228 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 229 // Build the StringBuilder & Return / Exit 230 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 231 232 for (int k=0; k < steArr.length; k++) sb.append( 233 '\t' + StringParse.rightSpacePad(steArr[k].getLineNumber() + ":", base10) + 234 steArr[k].getClassName() + "." + 235 steArr[k].getMethodName() + "()\n" 236 ); 237 238 if (stTruncated > 0) 239 sb.append("\t... and " + stTruncated + " more invocations.\n"); 240 241 242 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 243 // Build the StringBuilder & Return / Exit 244 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 245 246 return 247 sb.toString() + 248 (hasCause ? StrIndent.indentTabs(toString(cause), 1) : "") + 249 (hasLegacyCause ? (legacyMsg + StrIndent.indentTabs(toString(legacyCause), 1)) : ""); 250 } 251 252 253 // ******************************************************************************************** 254 // ******************************************************************************************** 255 // Variant: No Stack Trace Printing 256 // ******************************************************************************************** 257 // ******************************************************************************************** 258 259 260 /** 261 * Convenience Method. 262 * <BR />Invokes: {@link #toStringNoST(Throwable)} 263 * <BR />And-Then: {@link StrIndent#indentTabs(String, int)} 264 */ 265 public static String toStringNoST(Throwable t, int indentation) 266 { return StrIndent.indentTabs(toStringNoST(t), indentation); } 267 268 /** 269 * Prints the {@code Exception}-message to an output {@code String}. Invokes, recursively, 270 * this method with any cause-{@code Throwable's} that are present in this {@code Throwable}. 271 * 272 * <BR /><BR /><DIV CLASS=JDHint> 273 * This method differs from {@link #toString(Throwable)}, in that it <B>does not print the 274 * {@code StackTrace's} to the output-return {@code String}.</B> 275 * </DIV> 276 * 277 * @param t Any Java {@code Throwable}. Java {@code Throwable's} with one or {@code 'cause'} 278 * {@code Throwable's} will utilize more fully the printing-features of this class, 279 * {@code 'EXCC'} - <I>though any {@code Throwable} will be properly printed</I>. 280 * 281 * @return This method doesn't actually print anything to the screen or terminal, it just 282 * returns a {@code String} that you may print yourself - <I>or write to a 283 * {@code class StorageBuffer,} or any variation of logging you wish.</I> 284 */ 285 public static String toStringNoST(Throwable t) 286 { 287 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 288 // Initialize the Variables 289 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 290 291 final StringBuilder sb = new StringBuilder(); 292 final Throwable cause = t.getCause(); 293 final Throwable legacyCause = legacyCause(t); 294 final boolean hasCause = (cause != null); 295 final boolean hasLegacyCause = (legacyCause != null) && (legacyCause != cause); 296 297 String msg = t.getMessage(); 298 String localMsg = t.getLocalizedMessage(); 299 String legacyMsg = (hasLegacyCause) ? legacyMsg(t) : null; 300 301 final boolean hasMessage = (msg != null) && (msg.length() > 0); 302 final boolean hasLocalMsg = (localMsg != null) && (localMsg.length() > 0); 303 304 if (hasMessage) msg = StrIndent.indent(msg, 1, false, true); 305 if (hasLocalMsg) localMsg = StrIndent.indent(localMsg, 1, false, true); 306 307 308 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 309 // Print the "Top Part" / "Header Part" / Error-Message to the Return-Output StringBuilder 310 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 311 312 sb.append(BRED + "THROWN: " + t.getClass().getCanonicalName() + RESET + "\n"); 313 314 if (hasMessage) 315 { 316 sb.append(BCYAN + "Throwable.getMessage():\n" + RESET + msg + "\n"); 317 318 if (hasLocalMsg && (! msg.equals(localMsg))) 319 sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + localMsg + "\n"); 320 } 321 322 else if (hasLocalMsg) 323 sb.append(BCYAN + "Throwable.getLocalizedMessage():\n" + RESET + localMsg + "\n"); 324 325 else 326 sb.append(BYELLOW + "No Exception Messages Provided.\n" + RESET); 327 328 329 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 330 // Build the StringBuilder & Return / Exit 331 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 332 333 return 334 sb.toString() + 335 (hasCause 336 ? StrIndent.indentTabs(toStringNoST(cause), 1) 337 : "") + 338 (hasLegacyCause 339 ? (legacyMsg + StrIndent.indentTabs(toStringNoST(legacyCause), 1)) 340 : ""); 341 } 342 343 344 // ******************************************************************************************** 345 // ******************************************************************************************** 346 // Two Helpers 347 // ******************************************************************************************** 348 // ******************************************************************************************** 349 350 351 /** 352 * Helper Method for retrieving exceptions that function in a very similar fashion to a 353 * {@code 'cause'} exception. 354 * 355 * @param t Any Java Throwable 356 * 357 * @return For legacy exception classes, this will return the {@code 'cause'}, using the older 358 * method for extracting that cause. 359 */ 360 protected static Throwable legacyCause(final Throwable t) 361 { 362 if (t instanceof InvocationTargetException) 363 return ((InvocationTargetException) t).getTargetException(); 364 365 if (t instanceof ExceptionInInitializerError) 366 return ((ExceptionInInitializerError) t).getException(); 367 368 return null; 369 } 370 371 /** 372 * Generates a simple descriptive {@code String} whenever there is a cause exception present 373 * that has been extracted from one of the older exception classes that can produce such legacy 374 * causes. 375 * 376 * @param t 377 * @return A descriptive {@code String} explaining the legacy cause feature 378 */ 379 protected static String legacyMsg(final Throwable t) 380 { 381 if (t instanceof InvocationTargetException) 382 return "InvocationTargetException.getTargetException():\n"; 383 384 if (t instanceof ExceptionInInitializerError) 385 return "ExceptionInInitializerError.getException():\n"; 386 387 return null; 388 } 389}