1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
package Torello.JDUInternal.Features.STATS;

import Torello.JavaDoc.*;

import java.util.*;
import java.util.stream.*;

import Torello.HTML.*;
import Torello.HTML.NodeSearch.*;
import Torello.Java.*;

import static Torello.Java.C.*;

import Torello.Java.ReadOnly.ReadOnlyList;

import Torello.JDUInternal.Parse.Java.JSCF.JavaSourceCodeFile;

import Torello.JDUInternal.Messager.Where.JDUFeatures;
import Torello.JDUInternal.Messager.Messager;
import Torello.JDUInternal.Messager.MsgControl;
import Torello.JDUInternal.Messager.MsgVerbose;
import Torello.JDUInternal.Messager.Where.Where_Am_I;

import Torello.Java.ReadOnly.ReadOnlyMap;
import Torello.Java.ReadOnly.ReadOnlyTreeMap;

// Maintains a suite of statistics about all Java project-wide source-code files.
// 
// <BR />As the Upgrade Processors are executed, this class maintains a few statistics about the
// build, and produces the {@code Stats} HTML instance, which is subsequently linked to a
// {@code 'Stats'} button on output Java Doc Web-Pages - and also returned to the user after
// calling the ugrader

public class StatsInternal
{
    private static final Where_Am_I WHERE_AM_I = JDUFeatures.STATS.asSubSection(
        StatsInternal.class,
        "Compute Statistics about the User's Project"
    );


    // ********************************************************************************************
    // ********************************************************************************************
    // ***Optimization Fields***
    // ********************************************************************************************
    // ********************************************************************************************


    // The same package will be referenced, over and over, until the package has completed
    // These tags are used hundreds of times in some of the packages.

    String          LAST_USED_packageName   = null;
    StatsInternal   LAST_USED_packageStats  = null;


    // Package-Level instances of 'Stats' will have their package-names saved here.
    // For the "Complete-Project" Stats instance, this will be null.

    public final String packageName;


    // Each Package has it's own / recursive instance of 'Stats'
    // For the Package-Level instances of 'Stats' - this field will be null.

    public final Map<String, StatsInternal> packageStatsMap;

    // A Pointer to the "Project-Global Embed-Tags Map"
    public final ReadOnlyMap<String, String> globalTagsMap;

    // A Count-Total for the "Project-Global Embed-Tags Map"
    public final Map<String, Integer> globalTagsCount;


    // A reference pointer to the "Package-Local Embed-Tag Map", (ID's ==> FileNames)
    // NOTE: For the top-level main 'Stats' instance, this will remain null

    public final ReadOnlyMap<String, String> packageTagsMap;


    // Keeps a count of the use of all "Package-Local Embed-Tags"
    // NOTE: For the top-level main 'Stats' instance, this will remain null

    public final Map<String, Integer> packageTagsCount;


    // ********************************************************************************************
    // ********************************************************************************************
    // 3 Constructors:  1) Top-Level (First / Empty),  2) Top-Level w/Map,  3) Java-Package
    // ********************************************************************************************
    // ********************************************************************************************


    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    // Constructor: Initial-Starting Instance (usually clobbered & Discarded) - Empty, Top-Level!
    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    // 
    // This builds a "Global Stats" that doesn't have any Global Embed-Tags.  This is the
    // constructor used by configuration-class "Upgrade" in the "fields initializers" section.
    // When the instance that is created by this constructor IS NOT CLOBBERED NOR DISCARDED, it
    // means that the user DID NOT register a Global Embed-Tags Map.
    //
    // SPECIFICALLY: The following line is the "first initialization used" (inside Class-Upgrade)
    // private StatsInternal stats = new StatsInternal();
    //
    // If the User registers a global Embed-Tags Map, then this instance is SIMPLY-DISCARDED 
    // (Garbage Collected), and a new instance assigned to the "StatsInternal" Upgrade Field
    // via the "setProjectGlobalEmbedTagsMap()" initialization-configuration method inside class
    // "Upgrade".
    //
    // AGAIN: Usually this instance is built, **BUT** then replaced if the user configures the
    //        upgrade class with a Global Embed Tags map.  In the Java HTML JAR an instance is
    //        built using this constructor, but then destoyed immediately after registering.
    //        This is not a big enough waste - when compared to the alternative (which is writing
    //        another "register map" method).
    // 
    // FOR USERS WHO DO NOT REGISTER A GLOBAL EMBED TAGS MAP, THE INSTANCE CREATED BY THIS
    // CONSTRFUCTOR - OBVIOUSLY - WON'T BE GARBAGE COLLECTED.

    public StatsInternal()
    {
        // This Constructor is used to build a "Global-Stats" instance, so therefore it doesn't
        // have a Java Package-Name with which it may be associated.

        this.packageName = null;


        // Hopefully, the user will provide some StatsInternal instances for Java-Packages within
        // his or her project...  And when they do, StatsInternal will be ready!

        this.packageStatsMap = new HashMap<>();


        // As an aside - if this constructor were used to construct "Package Stats Instances", then
        // these would be Non-Null-Empty Map & ReadOnlyMap instances.  However, this constructor 
        // is designed for being used to build the lone/singleton "Top Level Stats" instance.
        // 
        // The Top-Level Stats instance isn't about a single Java-Package at all.  It isn't an
        // "EMPTY" Java-Package (one in which the End-User simply didn't provide Embed-Tags, or an
        // Embed-Tags Map), *BUT RATHER* an instance which is counting the Global-Tags only.
        //
        // Here (and this *IS* the "complexity"), these *SHOULD* be null, rather than "Empty-Maps"

        this.packageTagsMap     = null; // This is, indeed, a "Global-Tags" Instance
        this.packageTagsCount   = null; // It is not a "Package-Tags" Instance - these are null!


        // Note this constructor is invoked, once, at the top of Class-Upgrade, and it is built 
        // with the presumption that the user isn't planning on providing a "Global-Tags Map".
        // The decision isn't final until the Upgrade.upgrade() method is invoked, and the 
        // JDU actually starts running...
        // 
        // Until then, the user may further configure the Upgrade-Class, and does indeed have the
        // ability provide a Global-Tags Map.  If he does, the instance that has been generated
        // by this constructor would be *DISCARDED* and Garbage-Collected....

        this.globalTagsMap      = ReadOnlyTreeMap.emptyROTM();
        this.globalTagsCount    = Map.of();
    }


    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    // Constructor: The "Top-Level" or the "Project-Global" StatsInternal instance 
    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

    public StatsInternal(ReadOnlyMap<String, String> globalTagsMap)
    {
        // This is a 'JavaDocError' rather than a message to the messager because this should
        // *NEVER* occur.  At the time of the writing of this exception-check, this constructor is
        // invoked from the 'Upgrade' Confguration class.  A null-check is done there on th euser
        // input.  If that changes or moves, it should be thought of as "My Fault" not
        // "Their Fault" - so this has to be a 'THROW' rather than a 'MESSAGER' error.
        //
        // Keep these here because this class seems Ultra-Simple at first glance, but because it
        // is ALL ABOUT DATA, it is sort of more DIFFICULT than one might expect.
        //
        // The Exception/Errors in this class ARE ALL FOR MISTAKES that should NEVER happen, NO
        // MATTER WHAT the user has entered or passed to the Upgrade-Configuration class.

        if (globalTagsMap == null) Messager.assertFailCheckup(
            "A null 'globalTagsMap' was passed to the Stats Constructor.  If there are no " +
            "Global Embed Tags being used with this Project, the Upgrade-Configuration class " +
            "is supposed to be using the Stats-class zero-argument constructor",
            WHERE_AM_I
        );


        // This is the "Top-Level" StatsInternal Instance.  It is for the Project-Wide Stats, and
        // it therefore DOES NOT have a Package-Name.  It is not affiliated with any single Java
        // Package.

        this.packageName = null;

        // There will (extemely likely) be User-Packages added.
        this.packageStatsMap = new HashMap<>();


        // The "Global Stats" (a.k.a. the "Top-Level" StatsInternal Instance) doesn't have any
        //  Package-Tags
        // 
        // AGAIN: This constructor is only used for building the "Global Stats" instance.

        this.packageTagsMap     = null; // This is "null", rather than an Empty ReadOnlyMap
        this.packageTagsCount   = null; // This is also "null", rather than an empty java.util.Map

        this.globalTagsMap      = globalTagsMap;
        this.globalTagsCount    = new HashMap<>();


        // This must be initialized here.  Even if a Tag is not used, knowing that it's count
        // is ZERO is helpful to the user.  If we leave it null here, yes, that could be
        // interpreted as a ZERO, but it looks nicer here, and at the end to put a zero, rather
        // than leaving it null and reverse-checking the global-map.

        final Integer ZERO = Integer.valueOf(0);

        // Initialize the PROJECT-GLOBAL tag-count map.
        for (String embedTag : globalTagsMap.keySet()) this.globalTagsCount.put(embedTag, ZERO);
    }


    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    // Constructor: User-Package, StatsInternal instances
    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

    private StatsInternal(
            final String                        packageName,
            final ReadOnlyMap<String, String>   packageTagsMap,
            final ReadOnlyMap<String, String>   globalTagsMap
        )
    {
        this.packageName = packageName;

        if (packageTagsMap == null) Messager.assertFailCheckup(
            "A null 'packageTagsMap' was passed to the StatsInternal Constructor.  If there are " +
            "no Package-Local Embed Tags being used an empty instance is supposed to be passed.",
            WHERE_AM_I
        );


        // The "Top-Level" StatsInternal instance is a container.  (Side-Note, it is not a tree, 
        // but rather, just a container of "Package Instances" of StatsInternal).
        // 
        // This constructor is assigned the modifier "private" - it is used, below, to register a
        // a User-Provided Java-Package instance.
        // 
        // This isn't a "Tree-Like" Data-Structure, the Top-Level StatsInternal instance has a list
        // of StatsInternal instances - one for each Java-Package in the Users overall Java-Project
        // (a.k.a. the User's "Build").

        this.packageStatsMap = null; // "Package" instances don't have "Sub-Packages"


        // "Package" instance don't need the "Global Tag Map" ref, but rather than leaving it null,
        // there is no harm in passing it to *this* constructor from the "registerPackage" method.
        // Remember, this constructor is private, meaning it can only be invoked from within this
        // class.  The only place that this constructor is invoked is from the "registerPackage"
        // method, directly below the body of this constructor.
        // 
        // this.globalTagsMap is just a simple, direct-copy, of the Map from the owner-stats 
        // instance that "contains" this Package-Level StatsInternal instance...

        this.globalTagsMap      = globalTagsMap;
        this.globalTagsCount    = new HashMap<>();


        // If there is a User-Provided Java-Project in which the user has not defined any <EMBED>
        // tags, and no "[pkg-dir]/upgrade-files/external-html-ids.properties" file was available,
        // then the "packageTagsMap" instance which is provided WILL NOT BE NULL, but rather it 
        // will be a Non-Null-Empty-Map.

        this.packageTagsMap     = packageTagsMap;
        this.packageTagsCount   = new HashMap<>();

        final Integer ZERO = Integer.valueOf(0);

        for (final String embedTag : this.packageTagsMap.keySet())
            this.packageTagsCount.put(embedTag, ZERO);
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Register a new Java-Package
    // ********************************************************************************************
    // ********************************************************************************************


    // This is called by the "External HTML Initialization" Code.  When this is invoked, a t this
    // point in that code, the 'packageTagsMap' has just been read in from the
    // 'upgrade-files/external-html/external-html-ids.properties' file.

    public void registerPackage(
            String                      packageName,
            ReadOnlyMap<String, String> packageTagsMap
        )
    {
        this.packageStatsMap.put
            (packageName, new StatsInternal(packageName, packageTagsMap, this.globalTagsMap));
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Stats Fields: BASIC - NOTE: ALL OF THESE ARE PACKAGE-PRIVATE
    // ********************************************************************************************
    // ********************************************************************************************


    // A count of the total number of lines of {@code '.java'} files
    int numLines;

    // A count of the total number of bytes of {@code '.java'} files.
    int numBytes;

    // A count of the total number of HiLited HTML {@code <DIV>} Elements.
    int numHiLitedDivs = 0;


    // A count of the total number of methods, ctors, fields, ae's and ec's found during upgrade
    int
        numMethods          = 0,
        numConstructors     = 0,
        numFields           = 0,
        numAnnotationElems  = 0,
        numEnumConstants    = 0;

    // A count of the total number of method, ctor, field, ae and ec bodies hilited by the upgrade
    int
        numHiLitedMethods           = 0,
        numHiLitedConstructors      = 0,
        numHiLitedFields            = 0,
        numHiLitedAnnotationElems   = 0,
        numHiLitedEnumConstants     = 0;

    // A count of the total number of entities that were documented by Java Doc.
    int
        numDocumentedMethods            = 0,
        numDocumentedConstructors       = 0,
        numDocumentedFields             = 0,
        numDocumentedAnnotationElems    = 0,
        numDocumentedEnumConstants      = 0;


    // A count of the total number of static methods/fiedls found during the upgrade.
    int
        numStaticMethods    = 0,
        numStaticFields     = 0;


    // A count of the total number of final FCM found by the upgrade.
    int
        numFinalMethods         = 0,
        numFinalConstructors    = 0,
        numFinalFields          = 0;


    // A count of the total number of public FCM found by the upgrader.
    int
        numPublicMethods        = 0,
        numPublicConstructors   = 0,
        numPublicFields         = 0;

    // The total number of protected FCM found during the upgrade. 
    int
        numProtectedMethods         = 0,
        numProtectedConstructors    = 0,
        numProtectedFields          = 0;

    // The total number of private FCM found during the upgrade. 
    int
        numPrivateMethods       = 0,
        numPrivateConstructors  = 0,
        numPrivateFields        = 0;

    // A count of the total number of {@code transient} fields found during the upgrade.
    int numTransientFields = 0;

    // A count of the total number of {@code volatile} fields found during the upgrade. 
    int numVolatileFields = 0;


    // ********************************************************************************************
    // ********************************************************************************************
    // If I can make the above fields be **PACKAGE-PRIVATE** - I SHALL DO SO !!
    // ********************************************************************************************
    // ********************************************************************************************


    // *** NOTE *** These methods are ONLY USED IN ONE LOCATION - THE CONSTRUCTOR FOR
    //              Torello.JavaDoc.Stats
    // 
    // ALSO: These are all **PUBLIC** Getters for **PACKAGE-PRIVATE** Fields !

    public int numLines()                       { return this.numLines;                     }
    public int numBytes()                       { return this.numBytes;                     }
    public int numHiLitedDivs()                 { return this.numHiLitedDivs;               }

    // A count of the total number of methods, ctors, fields, ae's and ec's found during upgrade
    public int numMethods()                     { return this.numMethods;                   }
    public int numConstructors()                { return this.numConstructors;              }
    public int numFields()                      { return this.numFields;                    }
    public int numAnnotationElems()             { return this.numAnnotationElems;           }
    public int numEnumConstants()               { return this.numEnumConstants;             }

    // A count of the total number of method, ctor, field, ae and ec bodies hilited by the upgrade
    public int numHiLitedMethods()              { return this.numHiLitedMethods;            }
    public int numHiLitedConstructors()         { return this.numHiLitedConstructors;       }
    public int numHiLitedFields()               { return this.numHiLitedFields;             }
    public int numHiLitedAnnotationElems()      { return this.numHiLitedAnnotationElems;    }
    public int numHiLitedEnumConstants()        { return this.numHiLitedEnumConstants;      }

    // A count of the total number of entities that were documented by Java Doc.
    public int numDocumentedMethods()           { return this.numDocumentedMethods;         }
    public int numDocumentedConstructors()      { return this.numDocumentedConstructors;    }
    public int numDocumentedFields()            { return this.numDocumentedFields;          }
    public int numDocumentedAnnotationElems()   { return this.numDocumentedAnnotationElems; }
    public int numDocumentedEnumConstants()     { return this.numDocumentedEnumConstants;   }

    // A count of the total number of static methods/fiedls found during the upgrade.
    public int numStaticMethods()               { return this.numStaticMethods;             }
    public int numStaticFields()                { return this.numStaticFields;              }

    // A count of the total number of final FCM found by the upgrade.
    public int numFinalMethods()                { return this.numFinalMethods;              }
    public int numFinalConstructors()           { return this.numFinalConstructors;         }
    public int numFinalFields()                 { return this.numFinalFields;               }

    // A count of the total number of public FCM found by the upgrader.
    public int numPublicMethods()               { return this.numPublicMethods;             }
    public int numPublicConstructors()          { return this.numPublicConstructors;        }
    public int numPublicFields()                { return this.numPublicFields;              }

    // The total number of protected FCM found during the upgrade. 
    public int numProtectedMethods()            { return this.numProtectedMethods;          }
    public int numProtectedConstructors()       { return this.numProtectedConstructors;     }
    public int numProtectedFields()             { return this.numProtectedFields;           }

    // The total number of private FCM found during the upgrade. 
    public int numPrivateMethods()              { return this.numPrivateMethods;            }
    public int numPrivateConstructors()         { return this.numPrivateConstructors;       }
    public int numPrivateFields()               { return this.numPrivateFields;             }

    // A count of the total number of {@code transient} fields found during the upgrade.
    public int numTransientFields()             { return this.numTransientFields;           }

    // A count of the total number of {@code volatile} fields found during the upgrade. 
    public int numVolatileFields()              { return this.numVolatileFields;            }

}