001package Torello.JavaDoc;
002
003import java.io.Serializable;
004
005import java.util.*;
006import java.util.stream.*;
007
008import Torello.HTML.*;
009import Torello.HTML.NodeSearch.*;
010import Torello.Java.*;
011
012import static Torello.Java.C.*;
013
014import Torello.JDUInternal.GeneralPurpose.JDUProcessor;
015import Torello.JDUInternal.HTMLProcessors.EmbedTag.EmbedTagTopDescription;
016import Torello.JDUInternal.ParseJavaSource.JavaSourceCodeFile;
017
018import Torello.JDUInternal.GeneralPurpose.Messager;
019import Torello.JDUInternal.GeneralPurpose.MessagerVerbose;
020
021import Torello.Java.ReadOnly.ReadOnlyMap;
022
023/**
024 * Maintains a suite of statistics about all Java project-wide source-code files.
025 * 
026 * <BR />As the Upgrade Processors are executed, this class maintains a few statistics about the
027 * build, and produces the {@code Stats} HTML instance, which is subsequently linked to a
028 * {@code 'Stats'} button on output Java Doc Web-Pages - <I>and also returned to the user after
029 * calling the ugrader</I>.
030 * 
031 * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=UPSTATS>
032 */
033public class Stats implements java.io.Serializable
034{
035    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */
036    protected static final long serialVersionUID = 1;
037
038    // Package-Level instances of 'Stats' will have their package-names saved here.
039    // For the "Complete-Project" Stats instance, this will be null.
040    private final String packageName;
041
042    // Each Package has it's own / recursive instance of 'Stats'
043    // For the Package-Level instances of 'Stats' - this field will be null.
044    private final Map<String, Stats> packageStatsMap;
045
046    /**
047     * Global Stats are maintained, but so are package-local statistics.  This reetrieves the
048     * package-local statistics instance.
049     * @param packageName The name of one of the upgraded Java Packages, as a {@code String}.
050     * @return The package-level instance of {@code 'Stats'} for that package.
051     */
052    public Stats getPackageStats(String packageName)
053    { return packageStatsMap.get(packageName); }
054
055    // A Pointer to the "Project-Global Embed-Tags Map"
056    private final ReadOnlyMap<String, String> globalTagsMap;
057
058    // A Count-Total for the "Project-Global Embed-Tags Map"
059    private final Map<String, Integer> globalTagsCount;
060
061    /**
062     * Retrieving a count of all Project-Global Tags is done with this getter.  <I>It is important
063     * to remember that an instance of {@code 'Stats'} may be a <B>Project-Global</B> instance, or a
064     * <B>Package-Local</B> instance</I>.  If {@code 'this'} instance of {@code Stats} is the 
065     * 'Project-Global' instance, the integer count-values inside the returned-{@code Map} will be
066     * aggregate totals (for the whole project) of the number of times your External-HTML
067     * {@code <EMBED>} tags were used.
068     * 
069     * <BR /><BR />If this method is invoked on one of the Package-Local {@code 'Stats'} instances,
070     * the returned-{@code Map} will hold integer count-values for the Project-Global
071     * {@code <EMBED>} tags were used <I>inside the particular package for which {@code 'this'}
072     * statistics-instance was built.</I>
073     * 
074     * <BR /><BR /><B STYLE='color:red'>NOTE:</B> The complete dump of the statistics-tables for
075     * the Java HTML Jar Library may be viewed by clicking on the {@code 'Stats'} link in the top
076     * right corner of this page.  The data-tables there show the exact data that's inside the
077     * {@code Map's}-tables of this class.
078     * 
079     * @return If {@code 'this'} instance of {@code 'Stats'} is the 'Project-Global' instance, the
080     * returned-{@code Map} will be the 'Project-Totals' use-count for all Globally-Defined
081     * {@code <EMBED class='external-html ..>'} Tags.
082     * 
083     * <BR /><BR />If {@code 'this'} instance of {@code 'Stats'} is a 'Package-Local' instance,
084     * the returned-{@code Map} will have <B>Package-Local use-counts</B> for all of the
085     * <B>Globallly-Defined Tags.</B>
086     */
087    @SuppressWarnings("unchecked")
088    public Map<String, Integer> getGlobalTagsCount()
089    { return (HashMap<String, Integer>) ((HashMap<String, Integer>) globalTagsCount).clone(); }
090
091    // A reference pointer to the "Package-Local Embed-Tag Map", (ID's ==> FileNames)
092    // NOTE: For the top-level main 'Stats' instance, this will remain null
093
094    private final ReadOnlyMap<String, String> packageTagsMap;
095
096    // Keeps a count of the use of all "Package-Local Embed-Tags"
097    // NOTE: For the top-level main 'Stats' instance, this will remain null
098
099    private final Map<String, Integer> packageTagsCount;
100
101    /**
102     * This retrieves an integer count-{@code Map} of all Package-Local Embedded HTML
103     * (External-HTML) Tags.  If {@code 'this'} instance of {@code 'Stats'} is the Top-Level,
104     * Project-Global instance of {@code 'Stats'}, this method returns null.
105     * 
106     * @return A {@code Map} containing counts of the number of times a Package-Local
107     * {@code <EMBED CLASS='external-html'>} Tag has been used on web-pages that are inside
108     * that particular package.
109     *
110     * <BR /><BR />This method returns null if {@code 'this'} instance of {@code 'Stats'} is
111     * the Global-Instance, rather than one of the Package-Local Instances.
112     */
113    @SuppressWarnings("unchecked")
114    public Map<String, Integer> getPackageTagsCount()
115    {
116        return (packageTagsCount == null)
117            ? null
118            : (HashMap<String, Integer>) ((HashMap<String, Integer>) packageTagsCount).clone();
119    }
120
121    // This builds a "Global Stats" that doesn't have any Global Embed-Tags.  This is the constructor
122    // used by configuration-class "Upgrade" in the "fields initializers" section.
123    //
124    // SPECIFICALLY: The following line is the "first initialization used".
125    //              protected Stats stats = new Stats();
126    //
127    // If the User registers a global Embed-Tags Map, then this instance is SIMPLY-DISCARDED 
128    // (Garbage Collected), and a new instance assigned to that Upgrade Field.
129    //
130    // AGAIN: Usually this instance is built, **BUT** then replaced if the user configures the
131    //        upgrade class with a Global Embed Tags map.  In the Java HTML JAR an instance is
132    //        built using this constructor, but then destoyed immediately after registering.
133    //        This is not a big enough waste - when compared to the alternative (which is writing
134    //        another "register map" method).
135    //
136    // FOR USERS WHO DO NOT REGISTER A GLOBAL EMBED TAGS MAP, THE INSTANCE CREATED BY THIS
137    // CONSTRFUCTOR - OBVIOUSLY - WON'T BE GARBAGE COLLECTED.
138
139    Stats()
140    {
141        this.packageName        = null;
142        this.packageStatsMap    = new HashMap<>();
143        this.packageTagsMap     = null;
144        this.packageTagsCount   = null;
145        this.globalTagsMap      = null;
146        this.globalTagsCount    = null;
147    }
148
149    // Constructor for global-stats instance 
150    // NO MESSGER USE
151
152    Stats(ReadOnlyMap<String, String> globalTagsMap)
153    {
154        // This is a 'JavaDocError' rather than a message to the messager because this should
155        // *NEVER* occur.  At the time of the writing of this exception-check, this constructor is
156        // invoked from the 'Upgrade' Confguration class.  A null-check is done there on th euser
157        // input.  If that changes or moves, it should be thought of as "My Fault" not
158        // "Their Fault" - so this has to be a 'THROW' rather than a 'MESSAGER' error.
159        //
160        // Keep these here because this class seems Ultra-Simple at first glance, but because it
161        // is ALL ABOUT DATA, it is sort of more DIFFICULT than one might expect.
162        //
163        // The Exception/Errors in this class ARE ALL FOR MISTAKES that should NEVER happen, NO
164        // MATTER WHAT the user has entered or passed to the Upgrade-Configuration class.
165
166        if (globalTagsMap == null) Messager.assertFailCheckup(
167            "A null 'globalTagsMap' was passed to the Stats Constructor.  If there are no " +
168            "Global Embed Tags being used with this Project, the Upgrade-Configuration class " +
169            "is supposed to be using the Stats-class zero-argument constructor"
170        );
171
172        this.packageName        = null;
173        this.packageStatsMap    = new HashMap<>();
174
175        // The "Global Stats" instance doesn't have PackageTags
176        // AGAIN: This constructor is the Constructor for the "Global Stats" instance.
177
178        this.packageTagsMap     = null;
179        this.packageTagsCount   = null;
180
181        this.globalTagsMap      = globalTagsMap;
182        this.globalTagsCount    = new HashMap<>();
183
184        // This must be initialized here.  Even if a Tag is not used, knowing that it's count
185        // is ZERO is helpful to the user.  If we leave it null here, yes, that could be
186        // interpreted as a ZERO, but it looks nicer here, and at the end to put a zero, rather
187        // than leaving it null and reverse-checking the global-map.
188
189        Integer ZERO = Integer.valueOf(0);
190
191        // Initialize the PROJECT-GLOBAL tag-count map.
192        for (String embedTag : globalTagsMap.keySet()) this.globalTagsCount.put(embedTag, ZERO);
193    }
194
195    // Constructor for package-stats instances
196    private Stats(String packageName, ReadOnlyMap<String, String> packageTagsMap)
197    {
198        this.packageName        = packageName;
199        this.packageStatsMap    = null;         // This instance **IS** a "Package Stats"
200
201        this.globalTagsMap      = null;         // This is not needed
202        this.globalTagsCount    = new HashMap<>();
203
204        this.packageTagsMap     = packageTagsMap;
205        this.packageTagsCount   = (packageTagsMap == null) ? null : new HashMap<>();
206
207        Integer ZERO = Integer.valueOf(0);
208
209        if (packageTagsCount != null)
210
211            for (String embedTag : this.packageTagsMap.keySet())
212                this.packageTagsCount.put(embedTag, ZERO);
213    }
214
215    // This is called by "MainFilesProcessor" right before processing the Java Doc Web-Pages
216    // for a particular Java Package.  At this point in that code, the 'packageTagsMap' has just
217    // been read in from the 'upgrade-files/external-html/external-html-ids.properties' file.
218    //
219    // If the user didn't write any External-HTML Files, then the 'packageTagsMap' instance will
220    // be null.  That is OK, that is only one of the many statistics that are maintained by this
221    // class.
222    //
223    // MESSAGER:
224    //  1) USES:        assertFailGeneralPurpose
225    //  2) INVOKED-BY:  RetrieveEmbedTagMapPropFiles - only once
226    //  3) RETURNS:     NOTHING
227    //  4) THROWS:      UpgradeException (assertFailGeneralPurpose)
228    //
229    // ONE ASSERT STATEMENT WHICH THEORETICALLY SHOULD BE UNREACHABLE
230    // ONLY SETS FileName if unreachable-code is actually reached.
231    //
232    // EXPORT_PORTAL METHOD
233    // This method is used by Package UserConfigReaders, and doesn't need to be exported to the user
234    // Stats.run is NOT STATIC!!!
235
236    void registerPackage(
237            String                      packageName,
238            ReadOnlyMap<String, String> packageTagsMap,
239            Stats                       topLevelStatsInstance
240        )
241    {
242        // AGAIN: This should never be null, no matter what input the user has provided.  If
243        //        this is null, it means (mostly) that something else was changed (probably in
244        //        class 'Upgrade' or 'MainFilesProcessor').  Throw this to signify that it is
245        //        MY FAULT - NOT THEIR FAULT.
246
247        if (topLevelStatsInstance == null)
248        {
249            Messager.setCurrentFileName("No-File Processing", "Unreachable Code Reached");
250
251            Messager.assertFailGeneralPurpose(
252                "The instance of Stats passed to topLevelStatsInstance is null.\n" +
253                "Niether the top-level 'Stats' instance, nor the Package-Local instances " +
254                "should ever be null.\n" +
255                "Currently attempting to build Package-Local 'Stats' instance for package: " +
256                "    [" + BGREEN + packageName + RESET + "]\n" +
257                BRED + "NOTE:" + RESET + " This is the developers fault.",
258                null
259            );
260        }
261
262        topLevelStatsInstance.packageStatsMap.put
263            (packageName, new Stats(packageName, packageTagsMap));
264    }
265
266    /** The name of the Statistics HTML file saved to the root JavaDoc Directory. */
267    protected static final String STATS_HTML_FILENAME = "Stats.html";
268
269
270    // ********************************************************************************************
271    // ********************************************************************************************
272    // Stats Fields: BASIC
273    // ********************************************************************************************
274    // ********************************************************************************************
275
276
277    /** @return A count of the total number of lines of {@code '.java'} files. */
278    public int numLines() { return numLines; }
279    private int numLines;
280
281    /** @return A count of the total number of bytes of {@code '.java'} files. */
282    public int numBytes() { return numBytes; }
283    private int numBytes;
284
285    /** @return A count of the total number of HiLited HTML {@code <DIV>} Elements. */
286    public int numHiLitedDivs() { return numHiLitedDivs; }
287    private int numHiLitedDivs = 0;
288
289
290    // ********************************************************************************************
291    // ********************************************************************************************
292    // Stats: Entity (METHOD, FIELD, CONSTRUCTOR, ANNOTATION_ELEM, ENUM_CONSTANT)
293    // ********************************************************************************************
294    // ********************************************************************************************
295
296
297    /** @return A count of the total number of methods found during the upgrade. */
298    public int numMethods() { return numMethods; }
299    private int numMethods = 0;
300
301    /** @return A count of the total number of constructors found during the upgrade. */
302    public int numConstructors() { return numConstructors; }
303    private int numConstructors = 0;
304
305    /** @return A count of the total number of fields found during the upgrade. */
306    public int numFields() { return numFields; }
307    private int numFields = 0;
308
309    /** @return A count of the total number of annotation-elements found during the upgrade. */
310    public int numAnnotationElems() { return numAnnotationElems; }
311    private int numAnnotationElems = 0;
312
313    /** @return A count of the total number of enum-constants found during the upgrade. */
314    public int numEnumConstants() { return numEnumConstants; }
315    private int numEnumConstants = 0;
316
317
318    // ********************************************************************************************
319    // ********************************************************************************************
320    // Stats: HILITED-Entity (METHOD, FIELD, CONSTRUCTOR, ANNOTATION_ELEM, ENUM_CONSTANT)
321    // ********************************************************************************************
322    // ********************************************************************************************
323
324
325    /** @return A count of the total number of method bodies hilited by the upgrader. */
326    public int numHiLitedMethods() { return numHiLitedMethods; }
327    private int numHiLitedMethods = 0;
328
329    /** @return A count of the total number of constructor bodies hilited by the upgrader. */
330    public int numHiLitedConstructors() { return numHiLitedConstructors; }
331    private int numHiLitedConstructors = 0;
332
333    /** @return A count of the total number of field declarations hilited by the upgrader. */
334    public int numHiLitedFields() { return numHiLitedFields; }
335    private int numHiLitedFields = 0;
336
337    /**
338     * @return A count of the total number of annotation-element declarations hilited by the
339     * upgrader.
340     */
341    public int numHiLitedAnnotationElems() { return numHiLitedAnnotationElems; }
342    private int numHiLitedAnnotationElems = 0;
343
344    /**
345     * @return A count of the total number of enumeration-constant declarations hilited by the
346     * upgrader.
347     */
348    public int numHiLitedEnumConstants() { return numHiLitedEnumConstants; }
349    private int numHiLitedEnumConstants = 0;
350
351
352    // ********************************************************************************************
353    // ********************************************************************************************
354    // Stats: DOCUMENTED-Entity (METHOD, FIELD, CONSTRUCTOR, ANNOTATION_ELEM, ENUM_CONSTANT)
355    // ********************************************************************************************
356    // ********************************************************************************************
357
358
359    /** @return A count of the total number of methods that were documented by Java Doc. */
360    public int numDocumentedMethods() { return numDocumentedMethods; }
361    private int numDocumentedMethods = 0;
362
363    /** @return A count of the total number of constructors that were documented by Java Doc. */
364    public int numDocumentedConstructors() { return numDocumentedConstructors; }
365    private int numDocumentedConstructors = 0;
366
367    /** @return A count of the total number of fields that were documented by Java Doc. */
368    public int numDocumentedFields() { return numDocumentedFields; }
369    private int numDocumentedFields = 0;
370
371    /**
372     * @return A count of the total number of annotation-elements that were documented by
373     * Java Doc.
374     */
375    public int numDocumentedAnnotationElems() { return numDocumentedAnnotationElems; }
376    private int numDocumentedAnnotationElems = 0;
377
378    /**
379     * @return A count of the total number of annotation-elements that were documented by
380     * Java Doc.
381     */
382    public int numDocumentedEnumConstants() { return numDocumentedEnumConstants; }
383    private int numDocumentedEnumConstants = 0;
384
385
386    // ********************************************************************************************
387    // ********************************************************************************************
388    // Stats: STATIC-Entity (METHOD, FIELD)
389    // ********************************************************************************************
390    // ********************************************************************************************
391
392
393    /** @return A count of the total number of {@code static} methods found during the upgrade. */
394    public int numStaticMethods() { return numStaticMethods; }
395    private int numStaticMethods = 0;
396
397    /** @return A count of the total number of {@code static} fields found during the upgrade. */
398    public int numStaticFields() { return numStaticFields; }
399    private int numStaticFields = 0;
400
401
402    // ********************************************************************************************
403    // ********************************************************************************************
404    // Stats: FINAL-Entity (METHOD, FIELD, CONSTRUCTOR)
405    // ********************************************************************************************
406    // ********************************************************************************************
407
408
409    /** @return A count of the total number of {@code final} methods found by the upgrade. */
410    public int numFinalMethods() { return numFinalMethods; }
411    private int numFinalMethods = 0;
412
413    /** @return A count of the total number of {@code final} constructors found by the upgrader. */
414    public int numFinalConstructors() { return numFinalConstructors; }
415    private int numFinalConstructors = 0;
416
417    /** @return A count of the total number of {@code final} fields found by the upgrader. */
418    public int numFinalFields() { return numFinalFields; }
419    private int numFinalFields = 0;
420
421
422    // ********************************************************************************************
423    // ********************************************************************************************
424    // Stats: PUBLIC-Entity (METHOD, FIELD, CONSTRUCTOR)
425    // ********************************************************************************************
426    // ********************************************************************************************
427
428
429    /** @return A count of the total number of {@code public} methods found by the upgrader. */
430    public int numPublicMethods() { return numPublicMethods; }
431    private int numPublicMethods = 0;
432
433    /** @return A count of the total number of {@code public} constructors found by the upgrader.*/
434    public int numPublicConstructors() { return numPublicConstructors; }
435    private int numPublicConstructors = 0;
436
437    /** @return A count of the total number of {@code public} fields found by the upgrader. */
438    public int numPublicFields() { return numPublicFields; }
439    private int numPublicFields = 0;
440
441
442    // ********************************************************************************************
443    // ********************************************************************************************
444    // Stats: PROTECTED-Entity (METHOD, FIELD, CONSTRUCTOR)
445    // ********************************************************************************************
446    // ********************************************************************************************
447
448
449    /** @return The total number of {@code protected} methods found during the upgrade. */
450    public int numProtectedMethods() { return numProtectedMethods; }
451    private int numProtectedMethods = 0;
452
453    /** @return The total number of {@code protected} constructors found during the upgrade. */
454    public int numProtectedConstructors() { return numProtectedConstructors; }
455    private int numProtectedConstructors = 0;
456
457    /** @return The total number of {@code protected} fields found during the upgrade. */
458    public int numProtectedFields() { return numProtectedFields; }
459    private int numProtectedFields = 0;
460
461
462    // ********************************************************************************************
463    // ********************************************************************************************
464    // Stats: PRIVATE-Entity (METHOD, FIELD, CONSTRUCTOR)
465    // ********************************************************************************************
466    // ********************************************************************************************
467
468
469    /** @return The total number of {@code private} methods found during the upgrade. */
470    public int numPrivateMethods() { return numPrivateMethods; }
471    private int numPrivateMethods = 0;
472
473    /** @return The total number of {@code private} constructors found during the upgrade. */
474    public int numPrivateConstructors() { return numPrivateConstructors; }
475    private int numPrivateConstructors = 0;
476
477    /** @return The total number of {@code private} fields found during the upgrade. */
478    public int numPrivateFields() { return numPrivateFields; }
479    private int numPrivateFields = 0;
480
481
482    // ********************************************************************************************
483    // ********************************************************************************************
484    // Stats: OTHER-Entity (FIELD only)
485    // ********************************************************************************************
486    // ********************************************************************************************
487
488
489    /** @return A count of the total number of {@code transient} fields found during the upgrade.*/
490    public int numTransientFields() { return numTransientFields; }
491    private int numTransientFields = 0;
492
493    /** @return A count of the total number of {@code volatile} fields found during the upgrade. */
494    public int numVolatileFields() { return numVolatileFields; }
495    private int numVolatileFields = 0;
496
497
498
499    // ********************************************************************************************
500    // ********************************************************************************************
501    // Stats Per Package, **AND** Embed-Tags Stats
502    // ********************************************************************************************
503    // ********************************************************************************************
504
505
506    // ***Optimization Fields***
507    // The same package will be referenced, over and over, until the package has completed
508    // These tags are used hundreds of times in some of the packages.
509
510    private String  LAST_USED_packageName   = null;
511    private Stats   LAST_USED_packageStats  = null;
512
513
514    // Increments the total-count by 1 for a particular embed-tag
515    //
516    // NOTE: The whole 'LAST_USED_' PHENOMENON is an optimization, so that these tables are not
517    //       looked up in the Hashtable every single time they are used.
518    //
519    // MESSAGER:
520    //  1) INVOKES:     assertFailGeneralPurpose (100% my fault, not there fault)
521    //  2) INVOKED-BY:  **ONLY ONCE**, from 'EmbedTag', (inside a loop)
522    //  3) RETURN:      NOTHING
523    //  4) THROWS:      UpgradeException (assertFailGeneralPurpose)
524
525    void incrementTag(String packageName, String embedTag, boolean globalTagOrPackageTag)
526    {
527        if (    (this.LAST_USED_packageName == null)
528            ||  (! this.LAST_USED_packageName.equals(packageName)))
529        {
530            this.LAST_USED_packageName  = packageName;
531            this.LAST_USED_packageStats = this.packageStatsMap.get(packageName);
532
533            // This should never happen, but if it does, this exception is more meaningful than
534            // NullPointerException.  It isn't any better though!
535            //
536            // (IF NULL, THIS IS MY FAULT, NOT THEIR FAULT, and at the time of the writing of this
537            // code, THIS CANNOT HAPPEN.  HOWEVER, if something is changed, then this exception
538            // throw is more intelligent than NPE)
539
540            if (this.LAST_USED_packageStats == null)
541
542                Messager.assertFailGeneralPurpose(
543                    "The Package-Stats instance for Package [" + packageName + "] returned null " +
544                    "from the Global-Stats field 'packageStatsMap'\n" +
545                    "Processing Embed-Tag [" + BCYAN + embedTag + RESET + "], a " +
546                    (globalTagOrPackageTag ? "Global" : "Package-Local") + " Tag.",
547                    null
548                );
549        }
550
551        if (globalTagOrPackageTag)
552        {
553            Integer globalCount = this.globalTagsCount.get(embedTag);
554
555            // This should never happen, but if it does, this exception is more meaningful than
556            // NullPointerException.  It isn't any better though!
557            //
558            // SEE ABOVE COMMENT
559
560            if (globalCount == null)
561
562                Messager.assertFailGeneralPurpose(
563                    "The Global-Stats instance returned a null count, but these are all " +
564                    "supposed to have been initialized to zero.\n" +
565                    "Processing Embed-Tag [" + BCYAN + embedTag + RESET + "], a Global Tag.",
566                    null
567                );
568
569            this.globalTagsCount.put(embedTag, globalCount + 1);
570
571            // These are **ALWAYS NULL** the first time they are used.
572            Integer i = this.LAST_USED_packageStats.globalTagsCount.get(embedTag);
573
574            this.LAST_USED_packageStats.globalTagsCount.put(embedTag, (i == null) ? 1 : (i+1));
575        }
576        else
577        {
578            // These exception throws are being used more like Java 'assert' statements.  If an
579            // error occurs, it means that I changed something else, somplace else, and forgot
580            // about this part.  As of the time of the writing of this exception check, this
581            // field CAN NEVER BE NULL WHEN THIS LINE IS EXECUTED.
582
583            if (this.LAST_USED_packageStats.packageTagsCount == null)
584
585                Messager.assertFailGeneralPurpose(
586                    "While processing Embed-Tags for package [" + packageName + "]\n" +
587                    "The Package-Local Stats-Instance had a NULL-MAP reference for map " +
588                    "'packageTagsCount'.\n" +
589                    "Currently attempting to increment counter for (Package-Local) Embed-Tag " +
590                    "[" + BCYAN + embedTag + RESET + "].",
591                    null
592                );
593
594            Integer packageCount = this.LAST_USED_packageStats.packageTagsCount.get(embedTag);
595
596            // EXCEPTION-THROW COMMENT: SAME AS ABOVE.  This code is theoretically unreachable, 
597            // but every developer has been known to change something else, somewhere else, and
598            // at the same time forgetting how that change might affect THIS CODE (below)
599            //
600            // HERE, if 'packageCount' were null, this would BY MY FAULT, NOT THE USERS FAULT.
601
602            if (packageCount == null)
603            {
604                if (embedTag.equals(EmbedTagTopDescription.JDHBI_RESERVED_EMBED_TAG_FILE_ID))
605                    this.LAST_USED_packageStats.packageTagsCount.put(embedTag, 1);
606
607                else Messager.assertFailGeneralPurpose(
608                    "While retrieving Embed-Tag Count Data for Package-Local Embed-Tag " +
609                    "[" + embedTag + "]\n" +
610                    "Located in Package [" + packageName + "]\n" +
611                    "The Embed-Tag-Count found in the packageTagsCount-Map had a null-Integer.  " +
612                    "But these are all initialized to zero.",
613                    null
614                );
615            }
616
617            else this.LAST_USED_packageStats.packageTagsCount.put(embedTag, packageCount + 1);
618        }
619    }
620
621
622    // ********************************************************************************************
623    // ********************************************************************************************
624    // Update Stats for a single File
625    // ********************************************************************************************
626    // ********************************************************************************************
627
628
629    // Increments all the counters by the value in the given parameters
630    // This is package-visible, because it really serves little purpose outside of this
631    // package.
632    //
633    // NOTE: 'jdhf' is only used for the "getMethods", "geFields" things.  It is not asked for
634    //       'fileVec'
635    //
636    // MESSAGER:
637    //  1) INVOKES:     assertFailGeneralPurpose
638    //  2) INVOKED-BY:  THIS METHOD IS ONLY INVOKED ONCE, FROM DetailsFilesProcesor
639    //  3) RETURNS:     void - NOTHING
640    //  4) THROWS:      UpgradeException (assertFailGeneralPurpose)
641    //
642    // EXPORT_PORTAL METHOD
643    // This method is used by Package HTMLProcessors, and doesn't need to be exported to the user
644    // Stats.run is NOT STATIC!!!
645
646    void run(
647            JavaSourceCodeFile jscf, JavaDocHTMLFile jdhf,
648            int numHLM, int numHLC, int numHLF, int numHLAE, int numHLEC,
649            int numHLD
650        )
651    {
652        Method[]            mArr = jscf.getMethods();
653        Constructor[]       cArr = jscf.getConstructors();
654        Field[]             fArr = jscf.getFields();
655        AnnotationElem[]    eArr = jscf.getAnnotationElems();
656        EnumConstant[]      xArr = jscf.getEnumConstants();
657
658        final Stats ps = this.packageStatsMap.get(jdhf.packageName);
659
660        // Currently, this has been verified to be impossible.  If something changes, a
661        // 'JavaDocError' throw is the best way to handle it.  Note that the 'cost' of doing
662        // this null-check is absolutely minimal.
663
664        if (ps == null) Messager.assertFailGeneralPurpose(
665            "While attempting to update the both the Global and the Package-Local " +
666            "Stats-Instance for JavaSourceCodeFile:\n    " +
667            '[' + BYELLOW + jscf.fileName + RESET + '\n' +
668            "The Package-Local Stats-Instance that was returned from the Package-Stats Map " +
669            "was null.",
670            null
671        );
672
673        // If this is an inner-class, then the number of lines in the class (and the number of
674        // bytes) will already have been counted when the Enclosing-Class was tallied.
675        // If this is an inner-class, just skip the counting of the lines/bytes for the class
676
677        if (! jdhf.isInner)
678        {
679            ps.numLines                     += jdhf.typeLineCount;
680            this.numLines                   += jdhf.typeLineCount;
681            ps.numBytes                     += jdhf.typeSizeChars;
682            this.numBytes                   += jdhf.typeSizeChars;
683        }
684
685        ps.numHiLitedDivs                   += numHLD;
686        this.numHiLitedDivs                 += numHLD;
687
688        ps.numMethods                       += mArr.length;
689        this.numMethods                     += mArr.length;
690        ps.numHiLitedMethods                += numHLM;
691        this.numHiLitedMethods              += numHLM;
692        ps.numDocumentedMethods             += jdhf.numMethods();
693        this.numDocumentedMethods           += jdhf.numMethods();
694
695        ps.numConstructors                  += cArr.length;
696        this.numConstructors                += cArr.length;
697        ps.numHiLitedConstructors           += numHLC;
698        this.numHiLitedConstructors         += numHLC;
699        ps.numDocumentedConstructors        += jdhf.numConstructors();
700        this.numDocumentedConstructors      += jdhf.numConstructors();
701
702        ps.numFields                        += fArr.length;
703        this.numFields                      += fArr.length;
704        ps.numHiLitedFields                 += numHLF;
705        this.numHiLitedFields               += numHLF;
706        ps.numDocumentedFields              += jdhf.numFields();
707        this.numDocumentedFields            += jdhf.numFields();
708
709        ps.numAnnotationElems               += eArr.length;
710        this.numAnnotationElems             += eArr.length;
711        ps.numHiLitedAnnotationElems        += numHLAE;
712        this.numHiLitedAnnotationElems      += numHLAE;
713        ps.numDocumentedAnnotationElems     += jdhf.numAnnotationElems();
714        this.numDocumentedAnnotationElems   += jdhf.numAnnotationElems();
715
716        ps.numEnumConstants                 += xArr.length;
717        this.numEnumConstants               += xArr.length;
718        ps.numHiLitedEnumConstants          += numHLEC;
719        this.numHiLitedEnumConstants        += numHLEC;
720        ps.numDocumentedEnumConstants       += jdhf.numEnumConstants();
721        this.numDocumentedEnumConstants     += jdhf.numEnumConstants();
722
723        for (Method m : mArr) m.modifiers.forEach((String modifier) ->
724        {
725            switch (modifier)
726            {
727                case "static"       :   this.numStaticMethods++;
728                                        ps.numStaticMethods++;
729                                        break;
730                case "public"       :   this.numPublicMethods++;
731                                        ps.numPublicMethods++;
732                                        break;
733                case "protected"    :   this.numProtectedMethods++;
734                                        ps.numProtectedMethods++;
735                                        break;
736                case "private"      :   this.numPrivateMethods++;
737                                        ps.numPrivateMethods++;
738                                        break;
739                case "final"        :   this.numFinalMethods++;
740                                        ps.numFinalMethods++;
741                                        break;
742            }
743        });
744
745        for (Constructor c : cArr) c.modifiers.forEach((String modifier) ->
746        {
747            switch (modifier)
748            {
749                case "public"       :   this.numPublicConstructors++;
750                                        ps.numPublicConstructors++;
751                                        break;
752                case "protected"    :   this.numProtectedConstructors++;
753                                        ps.numProtectedConstructors++;
754                                        break;
755                case "private"      :   this.numPrivateConstructors++;
756                                        ps.numPrivateConstructors++;
757                                        break;
758                case "final"        :   this.numFinalConstructors++;
759                                        ps.numFinalConstructors++;
760                                        break;
761            }
762        });
763
764        for (Field f : fArr) f.modifiers.forEach((String modifier) ->
765        {
766            switch (modifier)
767            {
768                case "static"       :   this.numStaticFields++;
769                                        ps.numStaticFields++;
770                                        break;
771                case "public"       :   this.numPublicFields++;
772                                        ps.numPublicFields++;
773                                        break;
774                case "protected"    :   this.numProtectedFields++;
775                                        ps.numProtectedFields++;
776                                        break;
777                case "private"      :   this.numPrivateFields++;
778                                        ps.numPrivateFields++;
779                                        break;
780                case "final"        :   this.numFinalFields++;
781                                        ps.numFinalFields++;
782                                        break;
783                case "transient"    :   this.numTransientFields++;
784                                        ps.numTransientFields++;
785                                        break;
786                case "volatile"     :   this.numVolatileFields++;
787                                        ps.numVolatileFields++;
788                                        break;
789            }
790        });
791    }
792
793
794    // ********************************************************************************************
795    // ********************************************************************************************
796    // To String
797    // ********************************************************************************************
798    // ********************************************************************************************
799
800
801    /**
802     * Generates a {@code String} that enapsulates all of the counters / running-totals inside
803     * this data statistic class.
804     * 
805     * @return A {@code String} representation of this class.  Only includes statistics about
806     * use of methods, constructors, fields etc...  <B>DOES NOT INCLUDE</B> statistics regarding
807     * the use of the {@code <EMBED CLASS='external-html' ...>} tags.
808     */
809    public String toString()
810    {
811        StringBuilder sb = new StringBuilder();
812
813        for (String packageName : packageStatsMap.keySet()) sb.append(
814            "Stats for Package: [" + BCYAN + packageName + RESET + "]:\n\n" +
815            StrIndent.indent(toStringINTERNAL(packageStatsMap.get(packageName)), 4) + '\n'
816        );
817
818        sb.append("UPGADE / BUILD TOTAL:\n\n" + toStringINTERNAL(this));
819
820        return sb.toString();
821    }
822
823    private static String toStringINTERNAL(Stats s)
824    {
825        return
826            StringParse.rightSpacePad("" + StringParse.commas(s.numLines), 12)                       + "Total Lines of Source Code\n" +
827            StringParse.rightSpacePad("" + StringParse.commas(s.numBytes), 12)                       + "Total Bytes of Source Code\n" +
828            '\n' +
829            StringParse.rightSpacePad("" + StringParse.commas(s.numHiLitedDivs), 12)                 + "HiLited DIV Tags\n" +
830            '\n' +
831            StringParse.rightSpacePad("" + StringParse.commas(s.numMethods), 12)                     + "Source Total Methods\n" +
832            StringParse.rightSpacePad("" + StringParse.commas(s.numFields), 12)                      + "Source Total Fields\n" +
833            StringParse.rightSpacePad("" + StringParse.commas(s.numConstructors), 12)                + "Source Total Constructors\n" +
834            StringParse.rightSpacePad("" + StringParse.commas(s.numAnnotationElems), 12)             + "Source Total Annotation Elements\n" +
835            StringParse.rightSpacePad("" + StringParse.commas(s.numEnumConstants), 12)               + "Source Total Enumeration Constants\n" +
836            '\n' +
837            StringParse.rightSpacePad("" + StringParse.commas(s.numHiLitedMethods), 12)              + "Method Bodies HiLited\n" +
838            StringParse.rightSpacePad("" + StringParse.commas(s.numHiLitedFields), 12)               + "Fields Declarations HiLited\n" +
839            StringParse.rightSpacePad("" + StringParse.commas(s.numHiLitedConstructors), 12)         + "Constructors Bodies HiLited\n" +
840            StringParse.rightSpacePad("" + StringParse.commas(s.numHiLitedAnnotationElems), 12)      + "Annotation Elements HiLited\n" +
841            StringParse.rightSpacePad("" + StringParse.commas(s.numHiLitedEnumConstants), 12)        + "Enumeration Constants HiLited\n" +
842            '\n' +
843            StringParse.rightSpacePad("" + StringParse.commas(s.numDocumentedMethods), 12)           + "JavaDoc Documented Methods\n" +
844            StringParse.rightSpacePad("" + StringParse.commas(s.numDocumentedFields), 12)            + "JavaDoc Documented Fields\n" +
845            StringParse.rightSpacePad("" + StringParse.commas(s.numDocumentedConstructors), 12)      + "JavaDoc Documented Constructors\n" +
846            StringParse.rightSpacePad("" + StringParse.commas(s.numDocumentedAnnotationElems), 12)   + "JavaDoc Documented Annotation Elements\n" +
847            StringParse.rightSpacePad("" + StringParse.commas(s.numDocumentedEnumConstants), 12)     + "JavaDoc Documented Enumeration Constants\n" +
848            '\n' +
849            StringParse.rightSpacePad("" + StringParse.commas(s.numStaticMethods), 12)               + "Static Methods\n" +
850            StringParse.rightSpacePad("" + StringParse.commas(s.numStaticFields), 12)                + "Static Fields\n" +
851            '\n' +
852            StringParse.rightSpacePad("" + StringParse.commas(s.numFinalMethods), 12)                + "Final Methods\n" +
853            StringParse.rightSpacePad("" + StringParse.commas(s.numFinalFields), 12)                 + "Final Fields\n" +
854            StringParse.rightSpacePad("" + StringParse.commas(s.numFinalConstructors), 12)           + "Final Constructors\n" +
855            '\n' +
856            StringParse.rightSpacePad("" + StringParse.commas(s.numPublicMethods), 12)               + "Public Methods\n" +
857            StringParse.rightSpacePad("" + StringParse.commas(s.numPublicFields), 12)                + "Public Fields\n" +
858            StringParse.rightSpacePad("" + StringParse.commas(s.numPublicConstructors), 12)          + "Public Constructors\n" +
859            '\n' +
860            StringParse.rightSpacePad("" + StringParse.commas(s.numProtectedMethods), 12)            + "Protected Methods\n" +
861            StringParse.rightSpacePad("" + StringParse.commas(s.numProtectedFields), 12)             + "Protected Fields\n" +
862            StringParse.rightSpacePad("" + StringParse.commas(s.numProtectedConstructors), 12)       + "Protected Constructors\n" +
863            '\n' +
864            StringParse.rightSpacePad("" + StringParse.commas(s.numPrivateMethods), 12)              + "Private Methods\n" +
865            StringParse.rightSpacePad("" + StringParse.commas(s.numPrivateFields), 12)               + "Private Fields\n" +
866            StringParse.rightSpacePad("" + StringParse.commas(s.numPrivateConstructors), 12)         + "Private Constructors\n" +
867            '\n' +
868            StringParse.rightSpacePad("" + StringParse.commas(s.numTransientFields), 12)             + "Transient Fields\n" +
869            StringParse.rightSpacePad("" + StringParse.commas(s.numVolatileFields), 12)              + "Volatile Fields\n";
870    }
871
872
873    // ********************************************************************************************
874    // ********************************************************************************************
875    // Inserting Link into JavaDoc Directory MAIN-OVERVIEW FRAMES
876    // ********************************************************************************************
877    // ********************************************************************************************
878
879
880    private static final String HEADER =
881        "<HTML>\n<HEAD>\n" +
882        "<TITLE>Statistics</TITLE>\n" +
883        "<LINK rel='icon' type='image/jpg' href='favicon.jpg' />\n" +
884        "<LINK rel='stylesheet' type='text/css' href='stylesheets/Colors.css' />\n" +
885        "<LINK rel='stylesheet' type='text/css' href='stylesheets/Upgrader-Widgets.css' />\n" +
886        "</HEAD>\n" +
887        "<BODY CLASS=STATS>\n";
888
889    private static final String FOOTER = "\n</BODY>\n</HTML>\n";
890
891    // Writes a Java-Doc Viewable HTML File explaining the Statistics
892    // RETURN This shall return TRUE when the file was successfully written
893    //
894    // MESSAGER:
895    //  1) INVOKES:     println, assertFailGeneralPurpose
896    //  2) INVOKED-BY:  Only called once in Upgrade.upgrade, No Where Else
897    //  3) RETURNS:     void - NOTHING
898    //  4) THROWS:      UpgradeException (assertFailGeneralPurpose)
899
900    void saveStatsHTMLFile(String saveFileDirectory)
901    {
902        Messager.println("\nWriting Statistics to Root Java Doc Directory.");
903
904        StringBuilder html = new StringBuilder();
905
906        html.append(HEADER);
907
908        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
909        // Regular Stats - Methods, Fields, etc... - (almost identical to: 'toString')
910        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
911
912        html.append("<TABLE CLASS=STATS>\n");
913
914        for (Map.Entry<String, Stats> packageStats : packageStatsMap.entrySet())
915        {
916            // Method, Constructor, Field, Counts  etc... - COUNTS FOR EACH PACKAGE
917            html.append(
918                "\n\n\t<TR><TD COLSPAN=3>&nbsp;<BR /><BR /><BR /></TD></TR>\n" +
919                "\t<TR><TH ID='" +
920                packageStats.getKey() +  // Using the Package-Name as the HTML-ID for finding it
921                "' COLSPAN=3>\n\t\tStats for Package: <SPAN CLASS=PNSTATS>" +
922                packageStats.getKey() +  // Package-Name (as String)
923                "</SPAN></TH></TR>\n" +
924                toHTML_INTERNAL(packageStats.getValue()) + "\n\n" // Package-Stats (as Stats)
925            );
926
927            // This was broken up into a separate method becuse it is so long
928            //
929            // NO MESSAGER, NO THROWS - EXCEPT ONE LINE OF Messager.println(...)
930
931            append_one_packages_embed_tag_statistics(packageStats, html);
932        }
933
934
935        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
936        // Method, Constructor, Field etc... - COUNT-TOTALS FOR COMPLETE PROJECT
937        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
938
939        html.append(
940            "\n\n\n\t<TR><TD COLSPAN=3>&nbsp;<BR /><BR /><BR /></TD></TR>\n" +
941            "\n\t<TR><TH COLSPAN=3><SPAN CLASS=PNTSTATS>UPGADE / BUILD TOTAL:</SPAN></TH></TR>" +
942            toHTML_INTERNAL(this)
943        );
944
945    
946        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
947        // Embed-Tag Count Totals ... - FOR COMPLETE PROJECT
948        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
949
950        html.append(
951            "\n\t<TR><TD COLSPAN=3>&nbsp;</TD></TR>\n" +
952            "\n\n\t<TR><TH COLSPAN=3><SPAN CLASS=PNTSTATS>Complete Upgrade Totals:" +
953            "</SPAN></TH></TR>\n"
954        );
955
956        if (this.globalTagsMap != null)
957
958            // NO MESSAGER, NO THROWS
959            tagCountToHTML(
960                globalTagsCount, this.globalTagsMap, html,
961                true,   // YES, Print the Zero-Count
962                true    // This is the GLOBAL TagsMap Count
963            );
964
965        html.append("</TABLE>\n");
966
967
968        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
969        // Write File, Exit
970        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
971
972        html.append(FOOTER);
973
974        String saveFileName = saveFileDirectory + STATS_HTML_FILENAME;
975
976        try
977            { FileRW.writeFile(html, saveFileName); }
978
979        catch (Exception ioe)
980        {
981            Messager.setCurrentFileName
982                (saveFileName, "Computed Project-Statistics HTML-Page");
983
984            Messager.assertFailGeneralPurpose
985                (ioe, "Exception Writing Stats.html - <A> Anchor Links will be broken.", null);
986        }
987
988        if (MessagerVerbose.isVerbose())
989            MessagerVerbose.println("Wrote File: " + BYELLOW + saveFileName + RESET);
990    }
991
992    // This method was extracted out of the loop and turned into an independent method.
993    // It looks better this way.  That's all.
994    //
995    // NO MESSAGER, NO THROWS - EXCEPT ONE LINE OF Messager.println(...)
996
997    private void append_one_packages_embed_tag_statistics
998        (Map.Entry<String, Stats> packageStats, StringBuilder html)
999    {
1000        Stats s = packageStats.getValue();
1001
1002        if (MessagerVerbose.isVerbose()) MessagerVerbose.println
1003            ("Printing Stats to HTML for Package: " + packageStats.getKey());
1004
1005        // Absolute number of "Package-Local Embed Tags" are used - regardless of whether there
1006        // were any "Package-Local Embed Tags" even defined.
1007        int nPkgTags = (s.packageTagsCount == null) ? 0 : s.packageTagsCount.size();
1008
1009        // Absolute number for "Package-Local Use of Global Embed Tags"
1010        int nGblTags = (s.globalTagsCount == null) ? 0 : s.globalTagsCount.size();
1011
1012        // IF A SPECIFIC PACKAGE DOES NOT USE <EMBED CLASS='external-html'> TAGS, SKIP IT.
1013        if ((nPkgTags == 0) && (nGblTags == 0)) return;
1014
1015        html.append(
1016            "\n\n\t<TR><TH COLSPAN=3>Embed Tag FILE-ID Counts for Package: " +
1017            "<SPAN CLASS=PNSTATS>" + packageStats.getKey() + "</SPAN></TH></TR>\n"
1018        );
1019
1020        if (nGblTags > 0) // Package-Local USE OF *GLOBAL Embed-Tags
1021        {
1022            html.append(
1023                "\n\t<TR><TD COLSPAN=3>&nbsp;</TD></TR>\n" +
1024                "\n\t<TR><TD COLSPAN=3><B>Global &lt;EMBED&gt; Tags Used:</B></TD></TR>\n"
1025            );
1026
1027            // NO MESSAGER, NO THROWS
1028            tagCountToHTML(
1029                s.globalTagsCount, this.globalTagsMap, html,
1030                false,  // Don't mention zero-counts (there cannot be any, anyway)
1031                        // Since this is "Package Use of Global-Tags", it's not important
1032                        //
1033                true    // This tells it that these are global-counts, so it prints it properly
1034            );
1035        }
1036
1037        if (nPkgTags > 0) // Package-Local Tags
1038        {
1039            html.append(
1040                "\n\t<TR><TD COLSPAN=3>&nbsp;</TD></TR>\n" +
1041                "\n\t<TR><TD COLSPAN=3><B>Package-Local &lt;EMBED&gt; Tags Used:</B></TD></TR>\n"
1042            );
1043
1044            // NO MESSAGER, NO THROWS
1045            tagCountToHTML(
1046                s.packageTagsCount, s.packageTagsMap, html,
1047                true,   // Print the zero-counts, These are Package-Tags, so if the user didn't
1048                        // did not use some tags, put a notice about it.
1049                        //
1050                false   // This tells it that these are local-counts, so it prints it properly
1051            );
1052        }
1053    }
1054
1055
1056    // ********************************************************************************************
1057    // ********************************************************************************************
1058    // Generating Stats HTML Files
1059    // ********************************************************************************************
1060    // ********************************************************************************************
1061
1062
1063    // This merely builds an HTML table out of an instance of Stats
1064    // NOTE: It **DOES NOT** include the "Package-Level" stats Tables.
1065    //       That is done recursively.
1066    //
1067    // NO MESSAGER, NO THROWS
1068
1069    private static String toHTML_INTERNAL(Stats s)
1070    {
1071        return
1072            "\t<TR CLASS=BLANK><TD COLSPAN=3>&nbsp;</TD>\n" +
1073
1074            "\t<TR><TD>" + StringParse.commas(s.numLines)                       + "</TD><TD COLSPAN=2>Total Lines of Source Code</TD></TR>\n" +
1075            "\t<TR><TD>" + StringParse.commas(s.numBytes)                       + "</TD><TD COLSPAN=2>Total Bytes of Source Code</TD></TR>\n" +
1076            "\t<TR CLASS=BLANK><TD COLSPAN=3>&nbsp;</TD>\n" +
1077
1078            "\t<TR><TD>" + StringParse.commas(s.numHiLitedDivs)                 + "</TD><TD COLSPAN=2>HiLited DIV Tags</TD></TR>\n" +
1079            "\t<TR CLASS=BLANK><TD COLSPAN=3>&nbsp;</TD>\n" +
1080
1081            "\t<TR><TD>" + StringParse.commas(s.numMethods)                     + "</TD><TD COLSPAN=2>Source Total Methods</TD></TR>\n" +
1082            "\t<TR><TD>" + StringParse.commas(s.numFields)                      + "</TD><TD COLSPAN=2>Source Total Fields</TD></TR>\n" +
1083            "\t<TR><TD>" + StringParse.commas(s.numConstructors)                + "</TD><TD COLSPAN=2>Source Total Constructors</TD></TR>\n" +
1084            "\t<TR><TD>" + StringParse.commas(s.numAnnotationElems)             + "</TD><TD COLSPAN=2>Source Total Annotation Elements</TD></TR>\n" +
1085            "\t<TR><TD>" + StringParse.commas(s.numEnumConstants)               + "</TD><TD COLSPAN=2>Source Total Enumeration Constants</TD></TR>\n" +
1086            "\t<TR><TD COLSPAN=3>&nbsp;</TD>\n" +
1087
1088            "\t<TR><TD>" + StringParse.commas(s.numHiLitedMethods)              + "</TD><TD COLSPAN=2>Method Bodies HiLited</TD></TR>\n" +
1089            "\t<TR><TD>" + StringParse.commas(s.numHiLitedFields)               + "</TD><TD COLSPAN=2>Fields Declarations HiLited</TD></TR>\n" +
1090            "\t<TR><TD>" + StringParse.commas(s.numHiLitedConstructors)         + "</TD><TD COLSPAN=2>Constructors Bodies HiLited</TD></TR>\n" +
1091            "\t<TR><TD>" + StringParse.commas(s.numHiLitedAnnotationElems)      + "</TD><TD COLSPAN=2>Annotation Elements HiLited</TD></TR>\n" +
1092            "\t<TR><TD>" + StringParse.commas(s.numHiLitedEnumConstants)        + "</TD><TD COLSPAN=2>Enumeration Constants HiLited</TD></TR>\n" +
1093            "\t<TR CLASS=BLANK><TD COLSPAN=3>&nbsp;</TD>\n" +
1094
1095            "\t<TR><TD>" + StringParse.commas(s.numDocumentedMethods)           + "</TD><TD COLSPAN=2>JavaDoc Documented Methods</TD></TR>\n" +
1096            "\t<TR><TD>" + StringParse.commas(s.numDocumentedFields)            + "</TD><TD COLSPAN=2>JavaDoc Documented Fields</TD></TR>\n" +
1097            "\t<TR><TD>" + StringParse.commas(s.numDocumentedConstructors)      + "</TD><TD COLSPAN=2>JavaDoc Documented Constructors</TD></TR>\n" +
1098            "\t<TR><TD>" + StringParse.commas(s.numDocumentedAnnotationElems)   + "</TD><TD COLSPAN=2>JavaDoc Documented Annotation Elements</TD></TR>\n" +
1099            "\t<TR><TD>" + StringParse.commas(s.numDocumentedEnumConstants)     + "</TD><TD COLSPAN=2>JavaDoc Documented Enumeration Constants</TD></TR>\n" +
1100            "\t<TR CLASS=BLANK><TD COLSPAN=3>&nbsp;</TD>\n" +
1101
1102            "\t<TR><TD>" + StringParse.commas(s.numStaticMethods)               + "</TD><TD COLSPAN=2>Static Methods</TD></TR>\n" +
1103            "\t<TR><TD>" + StringParse.commas(s.numStaticFields)                + "</TD><TD COLSPAN=2>Static Fields</TD></TR>\n" +
1104            "\t<TR CLASS=BLANK><TD COLSPAN=3>&nbsp;</TD>\n" +
1105
1106            "\t<TR><TD>" + StringParse.commas(s.numFinalMethods)                + "</TD><TD COLSPAN=2>Final Methods</TD></TR>\n" +
1107            "\t<TR><TD>" + StringParse.commas(s.numFinalFields)                 + "</TD><TD COLSPAN=2>Final Fields</TD></TR>\n" +
1108            "\t<TR><TD>" + StringParse.commas(s.numFinalConstructors)           + "</TD><TD COLSPAN=2>Final Constructors</TD></TR>\n" +
1109            "\t<TR CLASS=BLANK><TD COLSPAN=3>&nbsp;</TD>\n" +
1110
1111            "\t<TR><TD>" + StringParse.commas(s.numPublicMethods)               + "</TD><TD COLSPAN=2>Public Methods</TD></TR>\n" +
1112            "\t<TR><TD>" + StringParse.commas(s.numPublicFields)                + "</TD><TD COLSPAN=2>Public Fields</TD></TR>\n" +
1113            "\t<TR><TD>" + StringParse.commas(s.numPublicConstructors)          + "</TD><TD COLSPAN=2>Public Constructors</TD></TR>\n" +
1114            "\t<TR CLASS=BLANK><TD COLSPAN=3>&nbsp;</TD>\n" +
1115
1116            "\t<TR><TD>" + StringParse.commas(s.numProtectedMethods)            + "</TD><TD COLSPAN=2>Protected Methods</TD></TR>\n" +
1117            "\t<TR><TD>" + StringParse.commas(s.numProtectedFields)             + "</TD><TD COLSPAN=2>Protected Fields</TD></TR>\n" +
1118            "\t<TR><TD>" + StringParse.commas(s.numProtectedConstructors)       + "</TD><TD COLSPAN=2>Protected Constructors</TD></TR>\n" +
1119            "\t<TR CLASS=BLANK><TD COLSPAN=3>&nbsp;</TD>\n" +
1120
1121            "\t<TR><TD>" + StringParse.commas(s.numPrivateMethods)              + "</TD><TD COLSPAN=2>Private Methods</TD></TR>\n" +
1122            "\t<TR><TD>" + StringParse.commas(s.numPrivateFields)               + "</TD><TD COLSPAN=2>Private Fields</TD></TR>\n" +
1123            "\t<TR><TD>" + StringParse.commas(s.numPrivateConstructors)         + "</TD><TD COLSPAN=2>Private Constructors</TD></TR>\n" +
1124            "\t<TR CLASS=BLANK><TD COLSPAN=3>&nbsp;</TD>\n" +
1125
1126            "\t<TR><TD>" + StringParse.commas(s.numTransientFields)             + "</TD><TD COLSPAN=2>Transient Fields</TD></TR>\n" +
1127            "\t<TR><TD>" + StringParse.commas(s.numVolatileFields)              + "</TD><TD COLSPAN=2>Volatile Fields</TD></TR>\n" +
1128            "\t<TR CLASS=BLANK><TD COLSPAN=3>&nbsp;</TD>\n";
1129    }
1130
1131    // This one works slightly differently than the previous "toHTML" function
1132    // It places the appended text inside the passed StringBuilder
1133    //
1134    // NO MESSAGER, NO THROWS
1135
1136    private static void tagCountToHTML(
1137            Map<String, Integer>        embedTagsCount,
1138            ReadOnlyMap<String, String> embedTagsMap,
1139            StringBuilder               sb,
1140            boolean                     printZeros,
1141            boolean                     globalOrLocal
1142        )
1143    {
1144        StringBuilder zerosCountSB = new StringBuilder();
1145
1146        sb.append("\t<TR><TD COLSPAN=3>&nbsp;</TD></TR>\n");
1147
1148        for (ReadOnlyMap.Entry<String, String> embedTag : embedTagsMap.entrySet())
1149        {
1150            Integer count = embedTagsCount.get(embedTag.getKey());
1151
1152            if (count == null) continue;
1153
1154            String s =
1155                "\t<TR>" +
1156                "<TD>" + StringParse.commas(count) + "</TD>" +
1157                "<TD>" + embedTag.getKey() + "</TD>" +
1158                "<TD>" + embedTag.getValue() + "</TD>" +
1159                "</TR>\n";
1160
1161            if (count > 0) sb.append(s);
1162
1163            if (printZeros && (count == 0)) zerosCountSB.append(s);
1164        }
1165
1166        sb.append("\t<TR><TD COLSPAN=3>&nbsp;</TD></TR>\n");
1167
1168        if (printZeros && (zerosCountSB.length() > 0))
1169
1170            sb.append(
1171                "\n\t<TR><TD COLSPAN=3>&nbsp;</TD></TR>\n" +
1172                "\n\t<TR><TD COLSPAN=3><B>Unused " +
1173                    (globalOrLocal ? "Global" : "Package-Local ") +
1174                    "&lt;EMBED&gt; Tags:</B></TD></TR>\n" +
1175                "\n\t<TR><TD COLSPAN=3>&nbsp;</TD></TR>\n" +
1176                zerosCountSB.toString() +
1177                "\n\t<TR><TD COLSPAN=3>&nbsp;</TD></TR>\n"
1178            );
1179    }
1180
1181
1182    // ********************************************************************************************
1183    // ********************************************************************************************
1184    // Generating Stats Buttons
1185    // ********************************************************************************************
1186    // ********************************************************************************************
1187
1188
1189    // This is the "Stats" button that's inserted into the pages
1190    private static final Vector<HTMLNode> nodesToInsert =
1191        HTMLPage.getPageTokens("<LI><A>Stats</A></LI>\n", false);
1192
1193    // In the above defined Vector, the <LI> has an HTML Anchor <A> at Vector-index 1.
1194    private static final int ANCHOR_POS = 1;
1195
1196    // Used by: "ExtraFilesProcessor"
1197    //
1198    // MESSAGER:
1199    //  1) INVOKES:     warning *ONLY*
1200    //  2) INVOKED-BY:  *ONCE* ExtraFilesProcessor
1201    //  3) RETURNS:     NOTHING
1202    //  4) THROWS:      NOTHING INTENTIONAL
1203    //
1204    // EXPORT_PORTAL METHOD
1205    // This method is used by Package HTMLProcessors, and doesn't need to be exported to the user.
1206
1207    static void addStatsButton(Vector<HTMLNode> page, String dotDots, String packageName)
1208    {
1209        AVT avt =
1210            AVT.cmp("class", TextComparitor.C, "navList").and(
1211            AVT.cmp("title", TextComparitor.EQ, "Navigation"));
1212
1213        Vector<SubSection> navMenus = InnerTagPeekInclusive.all(page, avt, "ul");
1214
1215        if (navMenus.size() < 2)
1216        {
1217            Messager.warning
1218                ("No Navigtion Menus Found, not adding a 'Stats.html' Link.", JDUProcessor.Stats);
1219            return;
1220        }
1221
1222        SubSection topNav = navMenus.elementAt(0);
1223        SubSection tailNav = navMenus.elementAt(1);
1224
1225        // MESSAGER:
1226        //  1) INVOKES:     Messager.warning
1227        //  2) INVOKED-BY:  HERE
1228        //  3) RETURN:      'false' if the Messager *WAS* used on warning
1229        //  4) THROWS:      NO THROW STATEMENTS
1230
1231        if (! addStatsButton(topNav.html, tailNav.html, dotDots, packageName)) return;
1232
1233        ReplaceNodes.r(page, topNav);
1234        ReplaceNodes.r(page, tailNav);
1235    }
1236
1237    // Used by: "MainFilesProcessor"
1238    // Inserts a button into the Navigation Bar on a Java Doc HTML Page.
1239    //
1240    // MESSAGER:
1241    //  1) INVOKES:     Messager.warning
1242    //  2) INVOKED-BY:  Twice - MainFilesProcessor (once), addStatsButton (above)
1243    //  3) RETURN:      'false' if the Messager *WAS* used on warning
1244    //  4) THROWS:      NO THROW STATEMENTS
1245    //
1246    // EXPORT_PORTAL METHOD
1247    // This method is used by Package HTMLProcessors, and doesn't need to be exported to the user.
1248
1249    @SuppressWarnings("unchecked") // The clone invocation
1250    static boolean addStatsButton(
1251            Vector<HTMLNode> navBarTop, Vector<HTMLNode> navBarBottom,
1252            String dotDots, String packageName
1253        )
1254    {
1255        // Find: <ul class="navList" title="Navigation">
1256        AVT avt =
1257            AVT.cmp("class", TextComparitor.C, "navList").and(
1258            AVT.cmp("title", TextComparitor.EQ, "Navigation"));
1259
1260        // Get the Pertinent Navigator-Menu.  It is a sub-part of the overall Navigation-Bar
1261        // The Stats Button is added to the first Navigation-List <UL> of both the bottom
1262        // and top Navigation-Bar.
1263
1264        DotPair topDP       = InnerTagFindInclusive.first(navBarTop, avt, "ul");
1265        DotPair bottomDP    = InnerTagFindInclusive.first(navBarBottom, avt, "ul");
1266
1267        // Print some pointless warnings.  This will one day break.  Warning messages are better
1268        // than Unknown-Exceptions.  Not everybody uses the Navigation-Menus, and what will
1269        // happen once a user runs this without them is currently unknown.
1270
1271        boolean topNull     = topDP == null;
1272        boolean tailNull    = bottomDP == null;
1273
1274        if (topNull && tailNull)
1275        {
1276            Messager.warning("This page does not have any Navigatin Menus", JDUProcessor.Stats);
1277            return false;
1278        }
1279
1280        else if (topNull || tailNull)
1281        {
1282            if (topNull) Messager.warning
1283                ("Page does not have a Top Navigation Menu.", JDUProcessor.Stats);
1284
1285            if (tailNull) Messager.warning
1286                ("Page does not have a Tail Navigation Menu.", JDUProcessor.Stats);
1287
1288            return false;
1289        }
1290
1291        // Clone the STATIC-FINAL 'nodesToInsert' Vector.
1292        Vector<HTMLNode> v = (Vector<HTMLNode>) nodesToInsert.clone();
1293
1294        // The HTML <A HREF=...> elemet that needs the RELATIVE-PATH URL w/ 'dotDots'
1295        TagNode anchor = (TagNode) nodesToInsert.elementAt(ANCHOR_POS);
1296
1297        // Set the HREF for the anchor-url link
1298        anchor = anchor.setAV("HREF", dotDots + "Stats.html#" + packageName, SD.SingleQuotes);
1299
1300        // Replace the old HTML Anchor Element with the updated one having the
1301        // relative path String to the base Java Doc directory.
1302
1303        v.setElementAt(anchor, ANCHOR_POS);
1304
1305        // Insert the updated navigator-menu item into the page, AS THE LAST ELEMENT 
1306        // of the menu (beginning at the node JUST-BEFORE the one located at navMenu.end)
1307
1308        /*if (! topNull)  */ navBarTop.addAll(topDP.end, v);
1309        /*if (! tailNull) */ navBarBottom.addAll(bottomDP.end, v);
1310
1311        return true;
1312    }
1313}