001package Torello.JavaDoc.Messager; 002 003import java.io.File; 004 005import Torello.Java.StringParse; 006import Torello.Java.UnreachableError; 007import Torello.Java.C; 008import Torello.Java.StorageWriter; 009 010import Torello.JavaDoc.Declaration; 011 012/** 013 * This is an internally used class that serves as a "Data-Record" type which maintains all of the 014 * internally needed "state" that is required to run the Messager Package. 015 * 016 * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=PRINT_RECORD> 017 */ 018public class PrintRecord 019{ 020 // ******************************************************************************************** 021 // ******************************************************************************************** 022 // Constructor & Immutable-Fields ==> Fields set in stone at time of construction 023 // ******************************************************************************************** 024 // ******************************************************************************************** 025 026 027 // This Appendable allows the user to pick a place to store the log-contents to disk, or just 028 // about any output-Appendable. This java.lang.Appendable may be specified/configured with the 029 // class Upgrade - using the method: setLogFile 030 // 031 // NOTE: There are two methods for setting this field. One allows the user to provide a file 032 // name, and the other allows them to provide any free-formed java.lang.Appendable. 033 // 034 // FINAL-IMMUTABLE & **PRIVATE** Instance-Field - Only Used inside this class 035 036 private final Appendable logAppendable; 037 038 039 // Here is the level of the Verbosity. The actual printing is done inside class MsgPkgPrivate, 040 // and in that method, messages are only printed if they are above or equal to the current 041 // Verbosity-Level 042 043 final int verbosityLevel; 044 045 046 // This just tells whether the above field is null. 047 // TRUE => NON-NULL 048 // FALSE => NULL 049 // NOTE: This one is null 050 051 final boolean hasLogAppendable; 052 053 054 // Some stupid little thing, which I totally don't completely remember right now. 055 // It is used inside the PrintHeading class. 056 // Certain Verbosity-Levels necessitate a newline character 057 058 final String VERBOSITY_LEVEL_NEW_LINE; 059 060 061 // The "StorageWriter" Prints to the Screen. The String's that it saves inside of it's 062 // "Storage", are then send to the 'logAppendable' - if-and-only-if the logAppendable is non 063 // null! 064 065 final StorageWriter screenWriterSW = new StorageWriter(); 066 067 068 // This is used OVER-AND-OVER through out the Print-Messager & Print-Heading classes 069 // It is "reset" whenever it is finished, rather than destroyed / freed. 070 // 071 // This is currently important because it allows the logic to avoid creating and destroying a 072 // million little StringBuilder's. The only other point to "know about" is that StringBuilder 073 // wasn't used at all a few months back (except "implicity", since the JDK, internally, uses a 074 // StringBuilder when doing its concatenation routines). 075 // 076 // Now that some of the Parse Code is being turned into an external thing, there is the 077 // potential to re-use the messager code. If that happens, a StringBuilder is used to retrieve 078 // the Error-Message Strings, and then shove them into an exception instead! (Thereby 079 // revering the original premise for having a "Messager" at all, namely avoid exception throws) 080 081 final StringBuilder sb = new StringBuilder(); 082 083 // The actual constructor 084 PrintRecord(final int verbosityLevel, final Appendable logAppendable) 085 { 086 this.verbosityLevel = verbosityLevel; 087 this.VERBOSITY_LEVEL_NEW_LINE = (verbosityLevel < 2) ? "\n" : ""; 088 this.logAppendable = logAppendable; 089 this.hasLogAppendable = (logAppendable != null); 090 } 091 092 093 // ******************************************************************************************** 094 // ******************************************************************************************** 095 // Static-Constants 096 // ******************************************************************************************** 097 // ******************************************************************************************** 098 099 100 private static final byte mode_FILE_AND_PROC_ONLY = 1; 101 private static final byte mode_FILE_PROC_AND_DECL = 2; 102 private static final int indentation_FILE_AND_PROC_ONLY = 8; 103 private static final int indentation_FILE_PROC_AND_DECL = 12; 104 105 // These are **ONLY** used in the getter below 106 private static final String I8 = StringParse.nChars(' ', 8); 107 private static final String I12 = StringParse.nChars(' ', 12); 108 109 110 // There is a very detail explanation for why "19" and "23" have been chosen, below! 111 // 112 // final String fodIndent = " Processing ".length() + fileOrDir.length() + 2; 113 // 114 // ==> " Directory " is 11 (9 + 2), " File " is 6 (4 + 2) 115 // ==> " Processing " is 12 spaces long 116 // ==> 12 + (11 or 6) 117 // ==> 23 or 18 118 119 private static final String I18 = StringParse.nChars(' ', 18); 120 private static final String I23 = StringParse.nChars(' ', 23); 121 122 123 // ******************************************************************************************** 124 // ******************************************************************************************** 125 // PRIVATE & **MUTABLE** Instance-Fields 126 // ******************************************************************************************** 127 // ******************************************************************************************** 128 129 130 private int errorCount = 0; 131 private int warningCount = 0; 132 133 // The mode it's using 134 private byte mode = mode_FILE_AND_PROC_ONLY; 135 136 // This sets the indentation level - as a number of space ' ' characters 137 private Integer indentation = indentation_FILE_AND_PROC_ONLY; 138 139 140 // The "current" Java Doc HTML File being analyzed. Whenever an error occurs, presuming this 141 // field is non-null, it will be printed at the top of the error message. 142 143 private String fileName = null; 144 145 146 // In this class, there is a method which "sets" the file-name. This is done many times insde 147 // the JDU-Internal classes. Whenever the file-name is set, there is a second parameter 148 // included in the "set" method that mandates / requires a brief-small description of what the 149 // file is also be provided. 150 151 private String fileNameKind = " --- "; 152 153 // This shall be set to ==> ? " Directory " : " File "; 154 private String fileOrDir = " "; 155 156 157 // There is a very detail explanation for why "19" and "23" have been chosen, ABOVE! 158 // final String fodIndent = " Processing ".length() + fileOrDir.length() + 2; 159 160 private String fodIndentation = null; 161 162 163 // The first time that an error or warning is printed, the file-name being "analyzed" must be 164 // printed at the top of the message. All subsequent messges do not need this error 165 // header message, AS LONG AS THE SAME FILE IS STILL BEING ANALYZED. 166 // 167 // When the Program Control-Flow sends a message that a new File-Name is being analzyed, then 168 // the boolean is reset to FALSE immediately! 169 // 170 // Whenever this boolean is FALSE, the File-Name being anaylzed has to be printed to any output 171 // error-message or warning-messages that are printed to the user's terminal. 172 173 private boolean printedFileNameAlready = false; 174 175 176 // The "current" declaration about which error messages will be printed (if there are any) 177 private Declaration declaration = null; 178 179 180 // The "EmbedTag" processor treats the "TopDescription" as one of the details section. It 181 // really isn't "a hack" - because the code is so long and the "Top Description" Embed Tag's 182 // are the exact same as the "Details" EmbedTag's. This is also true for the "HiLiteDividers" 183 // 184 // This, sort of, "overrides" the "detailSection" boolean. Since the "Top Description" part of 185 // any Java-Doc Web-Page also needs to be analyzed-and-updated, it is altogether possible that 186 // that an error-or-warning message for the Top-Description HTML might be generated. 187 // 188 // This needs to be handled IN THE EXACT SAME MANNER as error-or-warning messages are handled 189 // for the Detail-Sections. It needes to be printed to the user's terminal before the actual 190 // error-message itself! 191 // 192 // This boolean just says: JDU isn't currently processing **ANY** Detail-Sections, so don't 193 // print the Detail-Section's name, instead please print a friendly message saying 194 // that the "Top-Description Segment" of the Java-Doc Web-Page is being processed and analyzed 195 // at the moment. 196 197 private boolean topDescriptionAsDecl = false; 198 199 200 // The first time that an error or warning is printed, the detail-section location must be 201 // printed at the top of the message. All subsequent messges do not need this error 202 // header message. 203 204 private boolean printedDeclAlready = false; 205 206 207 // The purpose of is to - sort of - "Prevent" reprinting the Processor-Name and / or the 208 // Sub-Processor Name (over and over) when the Messager Receives requests to print errors over 209 // and over that are from the exact same Messager-Processor Location. 210 // 211 // It looks **REALLY UGLY** to see the exact same, long-winded, Processor Location over and 212 // over for the exact same type of error. 213 214 private Where_Am_I prev_WHERE_AM_I = null; 215 216 217 218 // ******************************************************************************************** 219 // ******************************************************************************************** 220 // PACKAGE-PRIVATE **GETTERS** - Used in "PrintHeading" 221 // ******************************************************************************************** 222 // ******************************************************************************************** 223 224 225 boolean hadErrors() { return this.errorCount > 0; } 226 boolean hadWarnings() { return this.warningCount > 0; } 227 int getErrorCount() { return this.errorCount; } 228 int getWarningCount() { return this.warningCount; } 229 int getIndentation() { return this.indentation; } 230 byte getMode() { return this.mode; } 231 String getFileName() { return this.fileName; } 232 String getFileNameKind() { return this.fileNameKind; } 233 boolean alreadyPrintedFileName() { return this.printedFileNameAlready; } 234 Declaration getDeclaration() { return this.declaration; } 235 boolean topDescriptionAsDecl() { return this.topDescriptionAsDecl; } 236 boolean alreadyPrintedDecl() { return this.printedDeclAlready; } 237 boolean modeFileAndProc() { return this.mode == mode_FILE_AND_PROC_ONLY; } 238 boolean modeFileProcAndDecl() { return this.mode == mode_FILE_PROC_AND_DECL; } 239 String isFileOrDir() { return this.fileOrDir; } 240 String fodIndentation() { return this.fodIndentation; } 241 242 boolean whereAmIChanged(final Where_Am_I WHERE_AM_I) 243 { return this.prev_WHERE_AM_I == WHERE_AM_I; } 244 245 String getIndentationStr() 246 { 247 if (this.indentation == 8) return I8; 248 if (this.indentation == 12) return I12; 249 250 throw new UnreachableError(); 251 } 252 253 boolean checkEqualsLogAppendable(final Appendable logAppendable) 254 { return this.logAppendable == logAppendable; } 255 256 257 258 // ******************************************************************************************** 259 // ******************************************************************************************** 260 // Some simple setters 261 // ******************************************************************************************** 262 // ******************************************************************************************** 263 264 265 void printingFileNameRightNow() 266 { this.printedFileNameAlready = true; } 267 268 void printingFirstDeclRightNow() 269 { this.printedDeclAlready = true; } 270 271 int incErrorCountAndCheck() 272 { 273 if (++this.errorCount > 25) throw new ReachedMaxErrors(); 274 else return this.errorCount; 275 } 276 277 int incWarningCount() 278 { return ++this.warningCount; } 279 280 void setPreviouseWhereAmI(final Where_Am_I WHERE_AM_I) 281 { this.prev_WHERE_AM_I = WHERE_AM_I; } 282 283 284 // ******************************************************************************************** 285 // ******************************************************************************************** 286 // Messager Control Setters 287 // ******************************************************************************************** 288 // ******************************************************************************************** 289 290 291 void setCurrentFileName(final String fName, final String fNameKind) 292 { 293 final boolean b = fName.endsWith(File.separator); 294 295 this.fileNameKind = fNameKind; 296 this.fileName = fName; 297 this.fileOrDir = b ? " Directory " : " File "; 298 this.fodIndentation = b ? I18 : I23; 299 this.printedFileNameAlready = false; 300 this.indentation = indentation_FILE_AND_PROC_ONLY; 301 this.mode = mode_FILE_AND_PROC_ONLY; 302 303 // Also clear the detail section, and the entity 304 this.declaration = null; 305 this.printedDeclAlready = false; 306 } 307 308 void setTopDescriptionSection() 309 { 310 this.declaration = null; 311 this.printedDeclAlready = false; 312 this.topDescriptionAsDecl = true; 313 this.indentation = indentation_FILE_PROC_AND_DECL; 314 this.mode = mode_FILE_PROC_AND_DECL; 315 } 316 317 void setDeclaration(final Declaration declaration) 318 { 319 this.declaration = declaration; 320 this.printedDeclAlready = false; 321 this.topDescriptionAsDecl = false; 322 this.indentation = indentation_FILE_PROC_AND_DECL; 323 this.mode = mode_FILE_PROC_AND_DECL; 324 } 325 326 327 // ******************************************************************************************** 328 // ******************************************************************************************** 329 // Do Something 330 // ******************************************************************************************** 331 // ******************************************************************************************** 332 333 334 void writeSBToScreen() 335 { 336 this.screenWriterSW.print(this.sb.toString()); 337 this.sb.setLength(0); 338 } 339 340 void checkPointLog() 341 { 342 final String textAlreadyWrittenToScreen = C 343 .toHTML(this.screenWriterSW.getString(), true, true, true) 344 .replace("\t", " "); 345 346 this.screenWriterSW.erase(); 347 348 try 349 { this.logAppendable.append(textAlreadyWrittenToScreen); } 350 351 catch (Exception e) 352 { 353 this.setCurrentFileName("User Provided Log", null); 354 355 Messager.assertFail( 356 e, 357 "Exception thrown while attempting to write to User-Provided Appendable-Log\n" + 358 "The Appendable which was provided to the class 'Torello.JavaDoc.Upgrade' has " + 359 "thrown an Exception while attempting to write to it.", 360 JDUMessager.PrintRecord 361 ); 362 } 363 } 364}