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