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
450
451
452
453
454
455
456
457
458
459
460
461
462
package Torello.HTML.Tools.JavaDoc;

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

import Torello.Java.Shell.C;

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

import java.util.function.ToIntFunction;

/**
 * <CODE>RearrangeSummaries - Documentation.</CODE><BR /><BR />
 * <EMBED CLASS="external-html" DATA-FILE-ID="REARSUMM">
 */
@StaticFunctional
public class RearrangeSummaries
{
    private RearrangeSummaries() { }

    /**
     * <CODE>SummaryItems - Documentation.</CODE><BR /><BR />
     * <EMBED CLASS="external-html" DATA-FILE-ID="RSITEMS">
     */
    public static class SummaryItems
    {
        /**
         * This is the list of classes, interfaces or enums that shall have at least one of 
         * their summary sections rearranged.  The names passed to this field must contain full
         * and complete package-names.  <I>The 'Simple Class Name' is insufficient!</I>  This array
         * shall be parallel (and, therefore, of equal length) to the other arrays in this class.
         */
        public final String[] ciets;

        /**
         * This should contain the names of the sections in which the fields, constructors or
         * methods will be divided.  Each sub-array of this two dimensional array shall contain
         * a list for one CIET.  This array must be parallel to the other arrays in this class.
         */
        public final String[][] sectionNames;

        /**
         * This array should identify which of the summary sections are to be rearranged.  The
         * Summary Sections in a JavaDoc web-page that can be re-arranged include fields, methods
         * and constructors.
         */
        public final FCM[] sections;

        /**
         * This is function-pointer that you must provide to sort the methods, fields or 
         * constructors in a summary-section.  Each of these
         * {@code java.util.function.IntFuction's} must have an {@code int apply(T t)} method
         * that will return the array-index of the section-name where the method, field or
         * constructor will be placed.
         * 
         * <BR /><BR /><B STYLE='color:red;'>IMPORTANT:</B> The {@code IntFunction<T>} that are
         * members of this list must have a type that is one of {@code Method, Field} or
         * {@code Constructor}.
         */
        @SuppressWarnings("rawtypes") 
        public final ToIntFunction[] sorters;

        /** Internally used constructor for this class. */
        public SummaryItems(
                String[] ciets, String[][] sectionNames, FCM[] sections,
                @SuppressWarnings("rawtypes") ToIntFunction[] sorters,
                String rootSourceFileDirectory
            )
        {
            this.ciets          = ciets;
            this.sectionNames   = sectionNames;
            this.sections       = sections;
            this.sorters        = sorters;

            try
                { checkArrays(rootSourceFileDirectory); }
            catch (Throwable t)
            {
                SummaryRearrangeException sre = new SummaryRearrangeException(
                    "There was an error checking the input arrays for the Summary Rearrangement " +
                    "that was requested.  Please see the Throwable.getCause() for details.",
                    t
                );
                Upgrade.ERROR
                    (sre, "Failed setting SummaryItems Input-Arrays (RearrangeSummaries)");
            }
        }

        private void checkArrays(String rootSrcDir) throws IOException
        {
            // This whole thing just checks that these arrays have equal lengths, and throws a
            // consistently formatted error message if they are not.  It also checks for null, and
            // throws a NullPointerException in such cases.
            ParallelArrayException.check
                (ciets, "ciets", true, sectionNames, "sectionNames", true);

            ParallelArrayException.check
                (sections, "sections", true, sectionNames, "sectionNames");

            ParallelArrayException.check
                (sorters, "sorters", true, sections, "sections");

            // Just does another null-pointer check.
            for (String[] cietSectionNames : sectionNames)

                for (String sectionName : cietSectionNames)

                    if (sectionName == null) throw new NullPointerException(
                        "The sectionNames[][] array passed to this method contains a null " +
                        "reference."
                    );

            // Check that the CIET's in the ciet-array actually exist on disk.
            //
            // This is not really necessary, because it if the user provided a CIET that was
            // 'misspelled' or had been 'deprecated', the Upgrader Logic would just skip that CIET
            // since it would never encounter it in the JavaDog pages in the first place!
            //
            // This seems 'smarter' because it lets them know when they have erroneous stuff in
            // their configuration files.

            TOP:
            for (String ciet : ciets)
            {
                String fName = rootSrcDir + ciet.replace(".", File.separator) + ".java";

                if (new File(fName).exists()) continue;

                StringBuilder   sb  = new StringBuilder();
                int             pos = fName.length();

                while ((pos = fName.lastIndexOf(Upgrade.FSEP)) != -1)
                {
                    fName = fName.substring(0, pos) + ".java";

                    if (new File(fName).exists()) continue TOP;

                    sb.append("      " + fName + '\n');
                }

                throw new FileNotFoundException(
                    "One of the CIET - Classes, Interfaces or Enumerated-Types provided to " +
                    "the RearrangeSummaries.SummaryItems Constructor points to a '.java' " +
                    "file that could not be located on disk.\n" +
                    "CIET: " + ciet + "\n" +
                    "DISK: " + rootSrcDir + ciet.replace(".", File.separator) + ".java" +
                    ((sb.length() > 0)
                        ?
                        "NOTE: Attempts to check if this is actually a (static) inner-class were " +
                        "made, but they also failed.\n" +
                        "Also Checked:\n" +
                        sb.toString()
                        : ""
                    )
                );
            }
        }
    }

    // retrieves the appropriate summary-pointer
    private static DotPair summaryDP(Vector<HTMLNode> fileVec, FCM fcm)
    {
        switch (fcm)
        {
            case Field:         return Summaries.fieldSummaries(fileVec);
            case Method:        return Summaries.methodSummaries(fileVec);
            case Constructor:   return Summaries.constructorSummaries(fileVec);
            default: throw new UnreachableError();
        }
    }

    /**
     * Runs it.
     * 
     * @param pr This contains all of the needed parameters for this method, encapsulated into a
     * single record-class.  The list is somewhat lengthy, so this makes the code "look cleaner"
     * 
     * @param sort The sort information requested by the user.
     */
    public static void run(CommonParamRecord pr, SummaryItems sort)
    {
        // This minor detail just makes sure we don't run a sort on any Summary-Section twice.
        // This is only for potential user-errror.  If they have provided two different sorting
        // functions for the same Summary-Section, an exception has to be thrown (or else a real
        // bad NullPointerException will eventually be thrown)
        TreeSet<FCM> alreadyRan = new TreeSet<>();

        // See if there is a match between the Class, Interface or Enum that was passed with one
        // of the user-requets for Summary-Rearrangement.
        //
        // NOTE: There may be more than one type of sort provided for the same class!

        for (int arrPos=0; arrPos < sort.ciets.length; arrPos++)

            if (sort.ciets[arrPos].equals(pr.cietFullName))
            {
                FCM fcm = sort.sections[arrPos];

                if (alreadyRan.contains(fcm)) throw new SummaryRearrangeException(
                    "While parsing the " + fcm + " list in the Summaries Section for File:\n" +
                    pr.jdFileName + "\nOne of the sorters provided is actually a second-sorter.  " +
                    "The " + fcm + " Summary Section, has already been sorted.  You have " +
                    "provided two sorts for the " + fcm + " Summary Section."
                );

                run(pr, fcm, sort.sectionNames[arrPos], sort.sorters[arrPos]);

                alreadyRan.add(fcm);
            }
    }

    @SuppressWarnings("unchecked")
    private static void run(
            CommonParamRecord pr, FCM fcm, String[] sectionNames,
            @SuppressWarnings("rawtypes") ToIntFunction sorter
        )
    {
        // This is used at the **VERY END** of the main while-loop.  This is the "title-row"
        // for each of the "sections."  Since this method may be rearranging Methods, Fields or
        // Constructors - there must be a different <TR>...</TR> depending on which it is.
        // The 'titleRow' Vectors are private fields defined near the bottom of this class.
        Vector<HTMLNode> titleRow = null;

        switch (fcm)
        {
            case Field:         titleRow = titleRowMethodOrField;
                                titleRow.setElementAt(new TextNode("Field"), FIELD_METH_NAME_POS);
                                break;
            case Method:        titleRow = titleRowMethodOrField;
                                titleRow.setElementAt(new TextNode("Method"), FIELD_METH_NAME_POS);
                                break;
            case Constructor:   titleRow = titleRowConstructor;
                                break;
        }

        // Build the table row iterator.  Make sure to restrict the cursor to just the summary
        // table that is being re-arranged.
        DotPair         summaryDP   = summaryDP(pr.fileVec, fcm);
        HNLIInclusive   iter        = TagNodeInclusiveIterator.iter(pr.fileVec, "tr");

        iter.restrictCursor(summaryDP);

        // This is just an "overly complicated" way to sort the table-rows into their appropriate
        // sections.  Once you are used to "Java Streams", then sorting things into groups always
        // looks like this with (complex-looking) Stream.Builders.  However, this is actually very
        // simple.  This is just a "list of lists".  Streams always make it easier since the length
        // of each sub-array is unknown until the sort has been complicated.
        //
        // There is one Stream.Builder for each of the sections in the particular summary.
        // This is why this is an 'array' with the '[]' notation.
        //
        // The contents that are placed into each Stream are an HTML Table Row, as a Vector of
        // HTMLNode.  (This is the "generic-type" of the Stream.Builder)
        //
        // FINALLY: Java Generics don't actually work as well as you think.  The following array
        //          of Stream.Builder is NEITHER a raw-type, nor is there an unchecked cast...
        //          It's the "Array" of "Generic Classes" that makes the compiler-break.  This is
        //          IMHO, a Java-Bug of sorts.
        //
        // SIMPLY-PUT: If you create an "Array of Generics" the whole compile-time type-checking
        //             thing (which is all a generic is) just gives up...
        @SuppressWarnings({"unchecked", "rawtypes"})
        Stream.Builder<Vector<HTMLNode>>[] bArr = new Stream.Builder[sectionNames.length];

        for (int i=0; i < bArr.length; i++) bArr[i] = Stream.builder();

        // Always skip the first row in the HTML table, it is just the header row.
        if (iter.hasNext()) iter.next();

        int numRearranged = 0;

        // Just iterate the rows of the table, and sort them into the sections/buckets requested
        while (iter.hasNext())
        {
            // get the next table-row from the Iterator.  The Iterator has already had its cursor
            // restricted to just the summary-section requested by the user.  The first row (title)
            // was skipped before starting the loop.
            Vector<HTMLNode> tableRow = iter.next();

            // The signature as a String.  The "Util" method just removes all HTML TagNode's that
            // (and any CommentNode's) in the Vector, and then concatenates the remaining nodes into
            // a String.
            String rowStr = Util.textNodesString(tableRow);

            // The 'placement' integer is what the user returns.  It tells which of the arrays
            // to save the table-row.
            int placement = 0;

            // Dummy-variable for "Generic parameters"  The 
            Vector<String> GP = new Vector<>();

            // Use the user-provided lambda function (the sorter[arrPos]), to get the section-number
            // where this table-row should be placed.
            // 
            // NOTE: This whole thing should actually be just a few lines, but it is the error
            //       reporting that is making this much longer.
            Field f=null;   Method m=null;  Constructor c=null;
            try
            {
                switch (fcm)
                {
                    case Field:
                        f = JavaDocHTMLFile.parseField(rowStr, pr.srcCodeFileName, GP);
                        break;
                    case Constructor:
                        c = JavaDocHTMLFile.parseConstructor(rowStr, pr.srcCodeFileName, GP);
                        break;
                    case Method:
                        m = JavaDocHTMLFile.parseMethod(rowStr, pr.srcCodeFileName, GP);
                        break;

                    default: throw new UnreachableError();
                }
            }
            catch (JavaDocHTMLParseException e)
            {
                throw new SummaryRearrangeException(
                    "While parsing the " + fcm + " list in the Summaries Section for File:\n" +
                    pr.jdFileName + "\nOne of the HTML Summary Signatures failed to parse.  " +
                    "See the Throwable.getCause() for more details.", e
                );
            }

            // Now invoke the user provided sorter-function (it returns an integer)
            try
            {
                switch (fcm)
                {
                    case Field:         placement = sorter.applyAsInt(f); break;
                    case Constructor:   placement = sorter.applyAsInt(c); break;
                    case Method:        placement = sorter.applyAsInt(m); break;
                    default: throw new UnreachableError();
                }
            }
            catch (ClassCastException e)
            {
                throw new SummaryRearrangeException(
                    "While parsing the " + fcm + " list in the Summaries Section for File:\n" +
                    pr.jdFileName + "\nThere was a ClassCastException.  Likely the sorting " +
                    "function provided during configuration was not designed to accept the " +
                    "right class.  You must provided a ToIntFunction<" + fcm + ">.  Please " +
                    "see the Throwable.getCause() for more details."
                );
            }
            catch (Exception e)
            {
                throw new SummaryRearrangeException(
                    "While parsing the " + fcm + " list in the Summaries Section for File:\n" +
                    pr.jdFileName + "\nThere was an exception.  The user-provided " +
                    "ToIntFunction<" + fcm + "> method 'applyAsInt(" + fcm + ") did not complete.  "+
                    "Please see the Throwable.getCause() for more details."
                );
            }

            // The user (lambda-function) may have made a mistake (next 2 exception checks)
            if (placement < 0) throw new SummaryRearrangeException(
                "While parsing the " + fcm + " list in the Summaries Section for File:\n" +
                pr.jdFileName + "\nOne of the values returned by the user-provided " +
                "sorting-function was negative.  The value returned was [" + placement + "].  " +
                "The sorter is used to place HTML table-rows into the appropriate sections of " +
                "the " + fcm + " Summaries, and cannot be negative."
            );

            if (placement >= bArr.length) throw new SummaryRearrangeException(
                "While parsing the " + fcm + " list in the Summaries Section for File:\n" +
                pr.jdFileName + "\nOne of the values returned by the user-provided " +
                "sorting-function was greater than the number of categories that were provided.  " +
                "The value returned was [" + placement + "], but the number of categories is " +
                bArr.length + ".  The sorter is used to place HTML table-rows into the appropriate " +
                "sections of the " + fcm + " Summaries, and must be less than " + bArr.length
            );

            // Now place the table-row Vector into the "bucket" or "section" that was just returned
            // by the user provided sorting-function / lambda.
            bArr[placement].accept(tableRow);

            // Keep a count of how many of these happened.  This count is printed at the end of
            // this method.
            numRearranged++;
        }
    
        // The new HTML table rows will be placed here.
        Vector<HTMLNode> tableBody = new Vector<>();

        // Now build the new table-body.
        for (int i=0; i < bArr.length; i++)
            tableBody.addAll(
                makeRows(
                    (Vector<HTMLNode>[]) bArr[i].build().toArray(Vector[]::new),
                    sectionNames[i], titleRow
                ));

        // Remove the table-caption
        int numRemoved = TagNodeRemoveInclusive.first(pr.fileVec, summaryDP.start, -1, "caption");

        // Replace the old table body with the new one that was just computed
        Util.replaceRange(pr.fileVec, summaryDP.start+1, summaryDP.end-numRemoved, tableBody);

        if (pr.sw != null) pr.sw.println(
            "\tRearranged " + C.BBLUE + StringParse.zeroPad(numRearranged) + C.RESET + " " +
            fcm.toString() + "(s) in the " +
            C.BCYAN + fcm.toString() + " Summary Section." + C.RESET
        );
    }

    // ********************************************************************************************
    // ********************************************************************************************
    // HTML Generation Part
    // ********************************************************************************************
    // ********************************************************************************************

    private static final int FIELD_METH_NAME_POS = 21;

    private static final Vector<HTMLNode> titleRowMethodOrField = HTMLPage.getPageTokens(
        "<TR><TD COLSPAN=2>&nbsp;</TD></TR>\n" +
        "<TR><TH CLASS=RSUMM COLSPAN=2><SPAN>TEXT</SPAN></TH></TR>\n" +
        "<TR>\n" +
        "\t<TH class=\"colFirst\" scope=\"col\" STYLE=\"white-space: nowrap;\">Modifier and Type</TH>\n" +
        "\t<TH class=\"colSecond\" scope=\"col\">F_OR_M</TH>\n" +
        "</TR>\n",
        false
    );

    private static final Vector<HTMLNode> titleRowConstructor = HTMLPage.getPageTokens(
        "<TR><TD>&nbsp;</TD></TR>\n" +
        "<TR><TH CLASS=RSUMM><SPAN>TEXT</SPAN></TH></TR>\n" +
        "<TR><TH class=\"colFirst\" scope=\"col\">Constructor</TH></TR>\n",
        false
    );

    private static final TextNode NEWLINE = new TextNode("\n");

    private static final Vector<HTMLNode> makeRows
        (Vector<HTMLNode>[] rows, String sectionName, Vector<HTMLNode> titleRow)
    {
        Vector<HTMLNode>    ret         = new Vector<>();
        boolean             altOrRow    = true;

        titleRow.setElementAt(new TextNode(sectionName), 9);
        ret.addAll(titleRow);

        for (Vector<HTMLNode> row : rows)
        {
            int pos = InnerTagFind.first
                (row, "tr", "class", TextComparitor.EQ, "altColor", "rowColor");

            TagNode tn = (TagNode) row.elementAt(pos);

            tn = tn.setCSSClasses(null, false, altOrRow ? "altColor" : "rowColor");
            row.setElementAt(tn, pos);
            altOrRow = ! altOrRow;

            ret.addAll(row);
            ret.add(NEWLINE);
        }

        return ret;
    }
}