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}