001package Torello.JavaDoc.Messager; 002 003import Torello.Java.StrIndent; 004import Torello.JavaDoc.Declaration; 005import Torello.JavaDoc.ReflHTML; 006import Torello.Java.C; 007 008import static Torello.Java.C.RESET; 009import static Torello.Java.C.BWHITE; 010import static Torello.Java.C.RED_BKGND; 011 012 013import java.util.Vector; 014 015// A replacement for <CODE>System.out, System.err</CODE> and even the StorageWriter 016// class used to send error messages to the terminal about upgrade progress. 017 018/** 019 * The Messager class is the top-level interface for emitting structured messages, warnings, and 020 * assertion failures to the user terminal. It exposes a static API (e.g., 021 * {@code Messager.userError(...)}) that can be called from anywhere in the application without 022 * requiring an explicit instance. 023 * 024 * <EMBED CLASS='external-html' DATA-FILE-ID=MSGR_THREAD_LOCAL> 025 * <EMBED CLASS='external-html' DATA-FILE-ID=MSGR_PARAM_TABLE> 026 */ 027public class Messager 028{ 029 /** 030 * When {@code TRUE}, requests that the stack trace be printed to terminl output for any 031 * Assertion-Failure Error-Message which contains an Exception-Throw 032 */ 033 public static boolean DEFAULT_SHOW_STACK_TRACE_ON_ASSERT_FAIL = true; 034 035 036 // ******************************************************************************************** 037 // ******************************************************************************************** 038 // Scoped-Values / ThreadLocal-Instance & Constructor 039 // ******************************************************************************************** 040 // ******************************************************************************************** 041 042 043 private static final java.lang.ThreadLocal<Messager> tlInstance = 044 new java.lang.ThreadLocal<>(); 045 046 /** 047 * Initializes the {@code ThreadLocal<Messager>} instance for the current thread. 048 * 049 * <BR /><BR /> 050 * This method must be called exactly once per thread, before any of the static 051 * {@link Messager} printing methods (such as {@code userError}, 052 * {@code assertionFailure}, etc.) are invoked. It constructs and wires together the internal 053 * helper classes {@link PrintRecord}, {@link PrintHeading}, and {@link PrintMessage}, and 054 * binds the resulting {@code Messager} object to the current thread using 055 * {@code ThreadLocal.set()}. 056 * 057 * <BR /><BR /> 058 * This design avoids the need to explicitly pass around {@code Messager} instances 059 * across the application and enables safe, isolated message formatting per thread. 060 * This is particularly useful for tools that may one day process multiple source files 061 * or build artifacts in parallel (e.g., future parallelization of the JDU tool). 062 * 063 * <BR /><BR /> 064 * This method must not be called more than once on the same thread. Doing so will result 065 * in an {@code InternalError}, as re-initialization would overwrite the existing 066 * {@code Messager} instance and compromise internal state. 067 * 068 * @param verbosityLevel The verbosity level for message output (must be between 0 and 3 069 * inclusive). Higher levels enable more verbose diagnostic output. 070 * 071 * @param logAppendable The {@code Appendable} to which log output should be written. May be 072 * {@code null} if no log file output is needed. If so, Output-Text is not saved to disk. 073 * 074 * @throws InternalError if this method is called more than once per thread and different 075 * assigned values to {@code 'verbosityLevel'} or {@code 'logAppendable'} are provided on the 076 * separate invocations. 077 * 078 * <BR /><BR />This {@code Error} will also throw if the {@code verbosityLevel} is out of range 079 */ 080 public static void initializeThreadLocalInstance 081 (final int verbosityLevel, final Appendable logAppendable) 082 { 083 Messager m = tlInstance.get(); 084 085 086 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 087 // Newer Addition: This method may be invoked multiple times 088 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 089 // 090 // In order to facilitate calling some of the JDU's Features before the Upgrader has 091 // started running - which in the partiular case that forced this new addition was 092 // handling an Error by the File-HiLiter inside the "MoreHiliting" Class - the Messager 093 // has to be initialized before the Upgrader even starts it's primary "upgrade()" Method. 094 // 095 // The default solution is to allow this method to be called as many times as are needed, 096 // with the minor caveat that if the input values change in any way shape or form, then 097 // an InternalError shall throw. 098 099 if (m != null) 100 { 101 final boolean eqV = (m.printRecord.verbosityLevel == verbosityLevel); 102 final boolean eqL = m.printRecord.checkEqualsLogAppendable(logAppendable); 103 104 if (eqV && eqL) return; 105 106 final String vStr = eqV 107 ? "" 108 : "The input Verbosity-Level [" + verbosityLevel + "], does not equal the " + 109 "previously assigned Verbosity-Level [" + m.printRecord.verbosityLevel + "]\n"; 110 111 final String lStr = eqL 112 ? "" 113 : "The input Log-Appendable is a different instance / reference Object than " + 114 "the previously assigned Log-Appendable.\n"; 115 116 throw new InternalError( 117 "Messager.initializeThreadLocalInstance(int, Appendable) has been called " + 118 "multiple times. On this invocation:\n" + vStr + lStr 119 ); 120 } 121 122 if ((verbosityLevel < 0) || (verbosityLevel > 3)) throw new InternalError( 123 "verbosityLevel=" + verbosityLevel + ", " + 124 "but it must be between 0 and 3." 125 ); 126 127 final PrintRecord printRecord = new PrintRecord(verbosityLevel, logAppendable); 128 final PrintHeading printHeading = new PrintHeading(printRecord); 129 final PrintMessage printMessage = new PrintMessage(printRecord, printHeading); 130 final Messager messager = new Messager(printRecord, printMessage); 131 132 tlInstance.set(messager); 133 } 134 135 136 // ******************************************************************************************** 137 // ******************************************************************************************** 138 // Constructor & Fields 139 // ******************************************************************************************** 140 // ******************************************************************************************** 141 142 143 private final PrintRecord printRecord; 144 private final PrintMessage printMessage; 145 146 147 private Messager(final PrintRecord printRecord, final PrintMessage printMessage) 148 { 149 this.printRecord = printRecord; 150 this.printMessage = printMessage; 151 } 152 153 154 // ******************************************************************************************** 155 // ******************************************************************************************** 156 // Simple print / println ... and one "warning" Printer-Method 157 // ******************************************************************************************** 158 // ******************************************************************************************** 159 160 161 public static void print(final String s) 162 { 163 final Messager m = tlInstance.get(); 164 if (m.printRecord.verbosityLevel >= 2) m.printRecord.screenWriterSW.print(s); 165 } 166 167 public static void println(final String s) 168 { 169 final Messager m = tlInstance.get(); 170 if (m.printRecord.verbosityLevel >= 2) m.printRecord.screenWriterSW.println(s); 171 } 172 173 public static void println() 174 { 175 final Messager m = tlInstance.get(); 176 if (m.printRecord.verbosityLevel >= 2) m.printRecord.screenWriterSW.println(); 177 } 178 179 public static void printQuiet(final String s) 180 { 181 final Messager m = tlInstance.get(); 182 if (m.printRecord.verbosityLevel == 1) m.printRecord.screenWriterSW.print(s); 183 } 184 185 public static void printSilent(final boolean dotOrNewLine) 186 { 187 final Messager m = tlInstance.get(); 188 189 if (m.printRecord.verbosityLevel == 0) 190 m.printRecord.screenWriterSW.print(dotOrNewLine ? '.' : '\n'); 191 } 192 193 public static void ifVerboseAppendVerbose() 194 { 195 final Messager m = tlInstance.get(); 196 197 if (m.printRecord.verbosityLevel == 3) 198 m.printRecord.screenWriterSW.print(MsgVerbose.getAndClear()); 199 } 200 201 public static void warning(final String message, final Where_Am_I WHERE_AM_I) 202 { 203 final Messager m = tlInstance.get(); 204 205 m.printMessage.WARNING(message, WHERE_AM_I); 206 m.printRecord.writeSBToScreen(); 207 } 208 209 210 // ******************************************************************************************** 211 // ******************************************************************************************** 212 // Fatal-Assert: Throws to Stop Program-Execution Immediatley 213 // ******************************************************************************************** 214 // ******************************************************************************************** 215 216 217 public static Error assertFail(final String message, final Where_Am_I WHERE_AM_I) 218 { 219 final Messager m = tlInstance.get(); 220 221 m.printMessage.ERROR( 222 message, 223 null, // signature 224 true, // FATAL 225 WHERE_AM_I 226 ); 227 228 m.printRecord.writeSBToScreen(); 229 throw new AssertFail(); 230 } 231 232 public static Error assertFail 233 (final String message, final String signature, final Where_Am_I WHERE_AM_I) 234 { 235 final Messager m = tlInstance.get(); 236 237 m.printMessage.ERROR( 238 message, 239 signature, 240 true, // FATAL 241 WHERE_AM_I 242 ); 243 244 m.printRecord.writeSBToScreen(); 245 throw new AssertFail(); 246 } 247 248 public static Error assertFail 249 (final Throwable t, final String message, final Where_Am_I WHERE_AM_I) 250 { 251 final Messager m = tlInstance.get(); 252 253 m.printMessage.ERROR( 254 t, 255 message, 256 null, // signature 257 true, // FATAL 258 DEFAULT_SHOW_STACK_TRACE_ON_ASSERT_FAIL, 259 WHERE_AM_I 260 ); 261 262 m.printRecord.writeSBToScreen(); 263 throw new AssertFail(); 264 } 265 266 public static Error assertFail( 267 final Throwable t, 268 final String message, 269 final String signature, 270 final Where_Am_I WHERE_AM_I 271 ) 272 { 273 final Messager m = tlInstance.get(); 274 275 m.printMessage.ERROR( 276 t, 277 message, 278 signature, 279 true, // FATAL 280 DEFAULT_SHOW_STACK_TRACE_ON_ASSERT_FAIL, 281 WHERE_AM_I 282 ); 283 284 m.printRecord.writeSBToScreen(); 285 throw new AssertFail(); 286 } 287 288 289 // ******************************************************************************************** 290 // ******************************************************************************************** 291 // Non-Fatal Assert: Error, but it doesn't need to stop immediately 292 // ******************************************************************************************** 293 // ******************************************************************************************** 294 295 296 public static void assertFailContinue(final String message, final Where_Am_I WHERE_AM_I) 297 { 298 final Messager m = tlInstance.get(); 299 300 m.printMessage.ERROR( 301 message, 302 null, // signature 303 false, // NON-FATAL 304 WHERE_AM_I 305 ); 306 307 m.printRecord.writeSBToScreen(); 308 } 309 310 public static void assertFailContinue 311 (final Throwable t, final String message, final Where_Am_I WHERE_AM_I) 312 { 313 final Messager m = tlInstance.get(); 314 315 m.printMessage.ERROR( 316 t, 317 message, 318 null, // signature 319 false, // NON-FATAL 320 false, // Don't Show Stack-Trace 321 WHERE_AM_I 322 ); 323 324 m.printRecord.writeSBToScreen(); 325 } 326 327 328 // ******************************************************************************************** 329 // ******************************************************************************************** 330 // Fatal-User-Error: Throws to Stop Program-Execution Immediatley 331 // ******************************************************************************************** 332 // ******************************************************************************************** 333 334 335 public static Error userErrorHalt(final String message, final Where_Am_I WHERE_AM_I) 336 { 337 final Messager m = tlInstance.get(); 338 339 m.printMessage.ERROR( 340 message, 341 null, // signature 342 true, // FATAL 343 WHERE_AM_I 344 ); 345 346 m.printRecord.writeSBToScreen(); 347 throw new MessagerGeneratedError(); 348 } 349 350 public static Error userErrorHalt 351 (final Throwable t, final String message, final Where_Am_I WHERE_AM_I) 352 { 353 final Messager m = tlInstance.get(); 354 355 m.printMessage.ERROR( 356 t, // Exception that was thrown 357 message, // THE MESSAGE 358 null, // signature 359 true, // FATAL 360 true, // Show Stack-Trace 361 WHERE_AM_I 362 ); 363 364 m.printRecord.writeSBToScreen(); 365 throw new MessagerGeneratedError(); 366 } 367 368 369 // ******************************************************************************************** 370 // ******************************************************************************************** 371 // User-Error: Program-Flow Execution Continues 372 // ******************************************************************************************** 373 // ******************************************************************************************** 374 375 376 public static void userErrorContinue(final String message, final Where_Am_I WHERE_AM_I) 377 { 378 final Messager m = tlInstance.get(); 379 380 m.printMessage.ERROR( 381 message, 382 null, // signature 383 false, // NON-FATAL 384 WHERE_AM_I 385 ); 386 387 m.printRecord.writeSBToScreen(); 388 } 389 390 public static void userErrorContinue 391 (final String message, final String signature, final Where_Am_I WHERE_AM_I) 392 { 393 final Messager m = tlInstance.get(); 394 395 m.printMessage.ERROR( 396 message, 397 signature, 398 false, // NON-FATAL 399 WHERE_AM_I 400 ); 401 402 m.printRecord.writeSBToScreen(); 403 } 404 405 public static void userErrorContinue 406 (final Throwable t, final String message, final Where_Am_I WHERE_AM_I) 407 { 408 final Messager m = tlInstance.get(); 409 410 m.printMessage.ERROR( 411 t, 412 message, 413 null, // signature 414 false, // NON-FATAL 415 false, // Don't Show Stack-Trace 416 WHERE_AM_I 417 ); 418 419 m.printRecord.writeSBToScreen(); 420 } 421 422 public static void userErrorContinueST 423 (final Throwable t, final String message, final Where_Am_I WHERE_AM_I) 424 { 425 final Messager m = tlInstance.get(); 426 427 m.printMessage.ERROR( 428 t, 429 message, 430 null, // signature 431 false, // NON-FATAL 432 true, // SHOW STACK TRACE 433 WHERE_AM_I 434 ); 435 436 m.printRecord.writeSBToScreen(); 437 } 438 439 440 // ******************************************************************************************** 441 // ******************************************************************************************** 442 // The Old Class "MsgControl" 443 // ******************************************************************************************** 444 // ******************************************************************************************** 445 446 447 public static void setCurrentFileName(final String fName, final String fNameKind) 448 { 449 final Messager m = tlInstance.get(); 450 m.printRecord.setCurrentFileName(fName, fNameKind); 451 } 452 453 public static void setTopDescriptionSection() 454 { 455 final Messager m = tlInstance.get(); 456 m.printRecord.setTopDescriptionSection(); 457 } 458 459 public static void setDeclaration(final Declaration declaration) 460 { 461 final Messager m = tlInstance.get(); 462 m.printRecord.setDeclaration(declaration); 463 } 464 465 public static boolean hadErrors() 466 { 467 final Messager m = tlInstance.get(); 468 return m.printRecord.hadErrors(); 469 } 470 471 public static void ifLogCheckPointLog() 472 { 473 final Messager m = tlInstance.get(); 474 if (! m.printRecord.hasLogAppendable) return; 475 m.printRecord.checkPointLog(); 476 } 477 478}