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}