001package Torello.HTML.Tools.JavaDoc;
002
003import Torello.HTML.*;
004import Torello.HTML.NodeSearch.*;
005import Torello.Java.*;
006
007import Torello.Java.Shell.C;
008import Torello.HTML.Tools.JavaDoc.RetrieveSummarySorters.SorterAndTitles;
009import Torello.Java.Function.IntTFunction;
010
011import java.util.*;
012import java.util.stream.*;
013
014import java.util.function.Function;
015import java.lang.reflect.InvocationTargetException;
016
017/**
018 * <B STYLE='color:darkred;'>Process Java Doc Web-Page:</B>
019 * 
020 * Organizes {@link Entity} items and generates sub-section labels for Java Doc Summary Sections
021 * by way of user-provided sorters and title-categories
022 * 
023 * <EMBED CLASS="external-html" DATA-FILE-ID=PKG_PRIVATE_MSG>
024 * <EMBED CLASS="external-html" DATA-FILE-ID=REARRANGE_SUMM>
025 */
026@StaticFunctional
027public class RearrangeEntitySummaries
028{
029    private RearrangeEntitySummaries() { }
030
031
032    // ********************************************************************************************
033    // ********************************************************************************************
034    // HTML Generation Part - New, sorted, Summary-Section HTML Table Rows
035    // ********************************************************************************************
036    // ********************************************************************************************
037
038
039    private static final int TEXT_POS = 9;
040    private static final int NamePos$M$F$IC = 21;
041    private static final int NamePos$C$EC = 16;
042
043    // NOTE: The "colspan" attribute is set to '3' - even though these tables only have two columns
044    //       whenever the "Summary Descriptions" are removed.
045    //       According to the Internet, this is OK, as long as the <TABLE> does not have it's
046    //       'table-layout' CSS set to "fixed".  If there are fewer columns than the number of
047    //       columns in the "COLSPAN" attribute, then the COLSPAN is set to "ALL"
048    //       (AGAIN: UNLESS THE THERE IS A CSS-STYLE DECLARATION SAYING: table-layout: fixed;)
049    //
050    // ALSO: There is pseudo-hack, which isn't being fixed today where it says "COLSPAN=2" for
051    //       2nd <TH> here.  If it is the case that "Summary Descriptions" were NOT-REMOVED, and
052    //       the summaries are being re-arranged, then this should be <TH>Description</TH>
053    //       Unfortunately, that's going to be missing for the time being.
054
055    private static final Vector<HTMLNode> titleRow$M$F$IC = HTMLPage.getPageTokens(
056        "<TR><TD COLSPAN=3>&nbsp;</TD></TR>\n" + 
057        "<TR><TH CLASS=RSUMM COLSPAN=3><SPAN>TEXT</SPAN></TH></TR>\n" +
058        "<TR>\n" +
059        "\t<TH class=\"colFirst\" scope=\"col\" STYLE=\"white-space: nowrap;\">Modifier and Type</TH>\n" +
060        "\t<TH class=\"colSecond\" scope=\"col\" COLSPAN=2>FIELD_OR_METHOD_OR_IC_NAME</TH>\n" +
061        "</TR>\n",
062        false
063    );
064
065    private static final Vector<HTMLNode> titleRow$C$EC = HTMLPage.getPageTokens(
066        "<TR><TD COLSPAN=2>&nbsp;</TD></TR>\n" +
067        "<TR><TH CLASS=RSUMM COLSPAN=2><SPAN>TEXT</SPAN></TH></TR>\n" +
068        "<TR><TH class=\"colFirst\" scope=\"col\" COLSPAN=2>CONSTRUCTOR_OR_EC</TH></TR>\n",
069        false
070    );
071
072
073    // ********************************************************************************************
074    // ********************************************************************************************
075    // Sort *ALL* Entity-Summary Sections for which an Entity-Summary Sort has been requested.
076    // ********************************************************************************************
077    // ********************************************************************************************
078
079    // (A.K.A. Iterate the ArrayList of Sorters/Titles)
080    // 
081    // Sort any / all requested Summary-Sections for a single CIET/Class in a single Package.
082    // The Type/CIET being sorted is the "jdhf"
083    //
084    // THIS METHOD IS NOT PRIVATE, THE OTHERS ARE!
085    //
086    // MESSAGER:
087    //  1) SETS:        processorName
088    //  2) EXPECTS:     fileName to be set already
089    //  3) USES:        errorExitingNow *ONLY* (and also println)
090    //  4) INVOKED BY:  MainFilesProcessor, only once, main-loop
091    //  5) RETURNS:     'false' when an error happens
092    //  6) THROWS:      File does not have throw statements
093
094    static boolean run
095        (JavaDocHTMLFile jdhf, ArrayList<SorterAndTitles> cietSorters)
096    {
097        Messager.setProcessorName("RearrangeEntitySummaries");
098
099        for (SorterAndTitles sorterAndTitles : cietSorters)
100        {
101            int numRearranged = run(jdhf, sorterAndTitles);
102
103            if (numRearranged == -1) return false;
104
105            if (Messager.isVerbose()) Messager.println(
106                "    Rearranged " + C.BBLUE + StringParse.zeroPad(numRearranged) + C.RESET + " " +
107                sorterAndTitles.entity.toString() + "(s) in the " +
108                C.BCYAN + sorterAndTitles.entity.toString() + " Summary Section." + C.RESET
109            );
110        }
111
112        return true;
113    }
114
115
116    // ********************************************************************************************
117    // ********************************************************************************************
118    // HELPERS / UTIL
119    // ********************************************************************************************
120    // ********************************************************************************************
121
122
123    // Helper method for messager-error Reporting
124    private static String OPENING_MSG(Entity entityType)
125    {
126        return 
127            "Arranging the " + C.BBLUE + entityType.toString() + C.RESET + " Summaries:\n";
128    }
129
130    private static final IntTFunction<String, String> PRINTER = (int i, String s) ->
131        "\n#" + StringParse.zeroPad10e2(i) + ", " + ('\"' + s + '\"');
132
133    // Helper method for messager-error reporting
134    private static final String CLOSING_MSG
135        (java.lang.reflect.Method sorter, Declaration d, String[] titles, Entity entityType)
136    {
137        return
138            "Currently attempting to use User-Provided " +
139            C.BBLUE + entityType.toString() + C.RESET + " Summary Sorting Method:\n\t" +
140            C.BGREEN + sorter.toString() + "\n" + C.RESET +
141            "Currently attempting to sort:\n\t" +
142            "[" + C.BCYAN + StringParse.newLinesAsText(d.signature) + C.RESET + "]\n" +
143            "User-Provided Section-Titles for " + C.BBLUE + entityType.toString() + C.RESET + ' ' +
144            "Summaries:" + StrIndent.indent(StrCSV.toCSV(titles, PRINTER, true, null), 4);
145    }
146
147
148    // ********************************************************************************************
149    // ********************************************************************************************
150    // Sort *ONE* *SINGLE* Summary Section (There may be multiple Sections to be sorted aftewards)
151    // ********************************************************************************************
152    // ********************************************************************************************
153
154
155    @SuppressWarnings("unchecked")
156    private static <ENTITY extends Declaration> int run(JavaDocHTMLFile jdhf, SorterAndTitles sat)
157    {
158        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
159        // Main Stuff
160        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
161
162        // This is used at the **VERY END** of the main while-loop.  This is the "title-row"
163        // for each of the "sections."  Since this method may be rearranging Methods, Fields or
164        // Constructors - there must be a different <TR>...</TR> depending on which it is.
165        // The 'titleRow' Vectors are private fields (in this class) defined near the bottom.
166
167        Vector<HTMLNode> titleRow = null;
168
169        // This is the full-and-complete parse-and-extraction of the Summary-Table Rows.  Note
170        // that the Java-Compiler is *NOT* smart-enough to recognized that the jdhf Summary
171        // Table field has the same type as "SummaryTableHTML<ENTITY>" - so there is a cast
172        // that is needed below, in the assignment of this variable.
173
174        SummaryTableHTML<ENTITY> table = null;
175
176        switch (sat.entity)
177        {    
178            case FIELD:
179                table       = (SummaryTableHTML<ENTITY>) jdhf.fieldSummaryTable;
180                titleRow    = titleRow$M$F$IC;
181                titleRow.setElementAt(new TextNode("Field"), NamePos$M$F$IC);
182                break;
183
184            case METHOD:
185                table       = (SummaryTableHTML<ENTITY>) jdhf.methodSummaryTable;
186                titleRow    = titleRow$M$F$IC;
187                titleRow.setElementAt(new TextNode("Method"), NamePos$M$F$IC);
188                break;
189
190            case CONSTRUCTOR:
191                table       = (SummaryTableHTML<ENTITY>) jdhf.constructorSummaryTable;
192                titleRow    = titleRow$C$EC;
193                titleRow.setElementAt(new TextNode("Constructor"), NamePos$C$EC);
194                break;
195
196            case ENUM_CONSTANT:
197                table       = (SummaryTableHTML<ENTITY>) jdhf.ecSummaryTable;
198                titleRow    = titleRow$C$EC;
199                titleRow.setElementAt(new TextNode("Enumerated Constant"), NamePos$C$EC);
200                break;
201
202            case INNER_CLASS:
203                table       = (SummaryTableHTML<ENTITY>) jdhf.ntSummaryTable;
204                titleRow    = titleRow$M$F$IC;
205                titleRow.setElementAt(new TextNode("Inner-Class"), NamePos$M$F$IC);
206                break;
207
208            // There aren't any "Annotation Elements" in Java HTML - except for 1.
209            // This isn't implemented because there are **BOTH** Required Annotation Elements
210            // and also Optional Annotation Elements.  This makes sorting them more difficult, and
211            // largely completely unnecessary
212
213            case ANNOTATION_ELEM:
214
215            default:
216                throw new UnreachableError();
217        }
218
219        // There is one IntStream.Builder for each of the section-titles in the particular summary.
220        // section.  Notice that this is an 'array' with the '[]' notation.
221
222        IntStream.Builder[] bArr = new IntStream.Builder[sat.sectionTitles.length];
223
224        // Instantiate each element of the Stream-Builder array be calling the static
225        // Stream-Builder factory method.  That method is "Stream.builder()".  That factory method
226        // has a <T> type parameter, which is Vector<HTMLNode>, *AS PER THE ARRAY-DEFINITION ABOVE*
227
228        for (int i=0; i < bArr.length; i++) bArr[i] = IntStream.builder();
229
230        int numRearranged = 0;
231
232        // These are usually a 'Declaration' sub-class / inherited-class... Except when sorting an
233        // Inner-Class / Nested-Class Summary-Table.  Then they are String's.  Otherwise, this would
234        // be a Vector<ENTITY> and ENTITY would be a type-parameter from the beginning.
235
236        // Vector<ENTITY> entityInstances = table.rowEntities;
237
238        Iterator<ENTITY> entityIter = table.rowEntityIterator();
239        int i=-1;
240
241        // Just iterate the rows of the table, and sort them into the sections/buckets requested
242        // for (int i=0; i < summRows.size(); i++)
243        while (entityIter.hasNext())
244        {
245            // ENTITY declaration = entityInstances.elementAt(i);
246            ENTITY declaration = entityIter.next();
247            i++; // starts at -1
248
249            // This is the result of the user-provided sorting-function.  This result is used to
250            // pick one of the IntStream.Builder instances from the IntStream.Builder[] array.
251            // The builder will simply hold 'i'
252
253            int placement = -1;
254
255            try
256                { placement = (Integer) sat.sorter.invoke(null, declaration); }
257
258            catch (InvocationTargetException e)
259            {
260                String MSG = (e.getMessage() != null)
261                    ? (StrIndent.indent(e.getMessage(), 4) + '\n')
262                    : "";
263
264                Messager.userErrorHalt(
265                    e,
266                    OPENING_MSG(sat.entity) + 
267                    "The user-provided sorting-method seems to have thrown an exception or " +
268                    "throwable:\n\t" +
269                    "[" + e.getTargetException().getClass().getSimpleName() + "]\n" +
270                    MSG + CLOSING_MSG
271                        (sat.sorter, declaration, sat.sectionTitles, sat.entity) +
272                    "\nNot Summary Sorting Section..."
273                );
274            }
275
276            catch (IllegalAccessException e)
277            {
278                Messager.userErrorHalt(
279                    e,
280                    OPENING_MSG(sat.entity) + 
281                    "The user-provided sorting-method seems to have caused an " +
282                    "IllegalAccessException to throw while invoking it.\n" +
283                    CLOSING_MSG
284                        (sat.sorter, declaration, sat.sectionTitles, sat.entity) +
285                    "\nNot Sorting Summary Section..."
286                    // The Class.getMethods() only returns Methods that have been declared public,
287                    // and therefore this should also be a public method.
288                );
289            }
290
291            // Their sorter class could not be initialized / loaded by the class-loader
292            catch (ExceptionInInitializerError e)
293            {
294                String MSG = (e.getMessage() != null)
295                    ? (StrIndent.indentTabs(e.getMessage(), 1) + '\n')
296                    : "";
297
298                Messager.userErrorHalt(
299                    OPENING_MSG(sat.entity) + 
300                    "The user-provided sorting-method could not be called because one of the " +
301                    "static initializer's inside your class has thrown an exception during " +
302                    "class-loading:\n\t" +
303                    "[" + e.getException().getClass().getSimpleName() + "]\n" +
304                    MSG + CLOSING_MSG
305                        (sat.sorter, declaration, sat.sectionTitles, sat.entity) +
306                    "\nNot Sorting Summary Section..."
307                );
308            }
309
310            // There sorter method threw an exception
311            catch (Exception e)
312            {
313                Messager.userErrorHalt(
314                    e,
315                    OPENING_MSG(sat.entity) + 
316                    "The user-provided sorting-method seems to have caused an " +
317                    C.BRED + e.getClass().getSimpleName() + C.RESET + " to throw\n" +
318                    CLOSING_MSG
319                        (sat.sorter, declaration, sat.sectionTitles, sat.entity) +
320                    "\nNot Sorting Summary Section..."
321                );
322            }
323
324            // The user (lambda-function) may have made a mistake
325            if ((placement < 0) ||  (placement >= sat.sectionTitles.length)) 
326            {
327                Messager.userErrorHalt(
328                    OPENING_MSG(sat.entity) + 
329                    "Has returned the value [" + placement + "].  The sorter is used to " +
330                    "organize HTML " + C.BBLUE + sat.entity.toString() + C.RESET + " Summary " +
331                    "Table-Rows into a User-Chosen " + C.BBLUE + sat.entity.toString() + C.RESET +
332                    " Section-Titles Category.  These categories are listed in a String-Array.  " +
333                    "Therefore this User-Provided sorter may not return negative-numbers, " +
334                    "nor may they be numbers that are larger than the number of Titles.\n" +
335                    "This sorter method must return values between " + C.BRED + "0" + C.RESET +
336                    " and " + C.BRED + (sat.sectionTitles.length-1) + C.RESET + "\n" +
337                    CLOSING_MSG
338                        (sat.sorter, declaration, sat.sectionTitles, sat.entity) +
339                    "\nNot Sorting Summary Section..."
340                );
341            }
342
343            // Now place the table-row number (which is 'i') into the "bucket" or "section" (which
344            // is identified by 'placement') that was just returned by the user provided sorter.
345
346            bArr[placement].accept(i);
347
348            // Keep a count of how many of these happened.  This count is printed at the end of
349            // this method.
350
351            numRearranged++;
352        }
353
354
355        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
356        // Use the computed-placements of each row to build a new rows-vector for SummaryTableHTML
357        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
358
359        // The new HTML table rows (and their associated Declarations) will be placed here.
360        Vector<Vector<HTMLNode>>    newTableBodyRows    = new Vector<>();
361        Vector<ENTITY>              newTableRowEntities = new Vector<>();
362
363        // Used as the first two characters of the CSS-ID that is added to each row-<TR>
364        // This has been moved to SummaryTableHTML
365        // final String idChars = sat.entity.abbrev;
366
367        // The outer-loop interates through each of the "placement-buckets".  These placement
368        // buckets were all instances of IntStream.Builder.  The outer-loop iterates each of the
369        // IntStream.Builder's from the array of them - and builds the IntStream, and then starts
370        // an inner-loop that iterates the contents of the IntStream.
371
372        for (int section=0; section < bArr.length; section++)
373        {
374            int[] acceptedRowNumbers = bArr[section].build().toArray();
375
376            if (acceptedRowNumbers.length == 0)
377            {
378                Messager.userErrorHalt(
379                    "After performing the " + C.BBLUE + sat.entity.toString() + C.RESET + " " +
380                    "Summary Rearrange, the section:\n" +
381                    "[\"" + C.BBLUE + sat.sectionTitles[section] + C.RESET + "\"]\n" +
382                    "Contained zero elements after the sort.  Sorted Summary Sections may not " +
383                    "be not be empty\n" +
384                    "Not Sorting the " + C.BBLUE + sat.entity.toString() + C.RESET +
385                    " Summary Section"
386                );
387            }
388
389            // Each of the IntStream buckets has an HTML <TR> Title-Row.  Those title rows
390            // are added to the output <TR> rows vector.  The entity that is associated with
391            // Title-Row <TR>'s is just set to null.
392
393            titleRow.setElementAt(new TextNode(sat.sectionTitles[section]), TEXT_POS);
394
395            // Now that the table-rows are individual vectors, and the <TABLE> is not built until
396            // the very end when the whole JDHF is re-built, the call to CLONE is needed.
397            // Otherwise, each time this loop iterates, the Vector will be changed, and at the end,
398            // all of the rows will have the same title!
399
400            newTableBodyRows.add((Vector<HTMLNode>) titleRow.clone());
401            newTableRowEntities.add(null);
402
403            // The colors of the <TR>'s have to alternate
404            boolean altOrRow = true;
405
406            // Now, iterate the contents of the IntStream - which are nothing more than pointers
407            // (Vector-indices) to each row-<TR> that was placed inside the section/bucket for
408            // the section that we are iterating (from the outer-loop)
409
410            for (int rowNumber : acceptedRowNumbers)
411            {
412                Vector<HTMLNode>    row     = table.getTableRow(rowNumber);
413                ENTITY              entity  = table.getRowEntity(rowNumber);
414
415                /*
416                int pos = InnerTagFind.first
417                    (row, "tr", "class", TextComparitor.EQ, "altColor", "rowColor");
418                */
419
420                // This *HAS* to be an openng <TR> element - it was retrieved in the
421                // SummaryTableHTML constructor, using InnerTagGet - which is not broken.  It
422                // has been tested for 2 years.
423
424                TagNode tn = row.elementAt(0).asTagNode();
425
426                // Set the "altColor" and "rowColor" class
427                tn = tn.setCSSClasses(null, false, altOrRow ? "altColor" : "rowColor");
428
429                // Give the <TR> an "ID=..." attribute so that links may be added to it in
430                // NavButtons
431                //
432                // This has been moved to SummaryTableHTML
433                // tn = tn.setID(idChars + entity.id, null);
434
435                // Replace the <TR> with the new <TR>, and this is finished.
436                row.setElementAt(tn, 0);
437                altOrRow = ! altOrRow;
438
439                // Put these in the new rows vector, so that this new <TR>-rows Vector may be
440                // assigned to the SummaryTableHTML rows Vector before exiting this method.
441                //
442                // NOTE: If you are wondering about "NEW_LINE", that is added at the very end inside
443                //       SummaryTableHTML when a big Replaceable is built for the entire <TABLE>
444
445                newTableBodyRows.add(row);
446                newTableRowEntities.add(entity);
447            }
448        }
449
450        // Now, this sets the actual Table-Row Vector-Fields in class SummaryTableHTML<ENTITY>.
451        // They are listed is private, but there is a Package-Private setter (setTableRows(...))
452        //
453        // SummaryTableHTML.tableRows:      typed as Vector<Vector<HTMLNode>>
454        // SummaryTableHTML.rowEntities:    typed as Vector<ENTITY>
455
456        table.setTableRows(newTableBodyRows, newTableRowEntities);
457
458
459        // REMEMBER: The first line of the table has "Name, Modifiers, Description" - but this is
460        //           all clobbered and replaced by the Section-Titles[] Rows.  The field
461        //           'headerRow' inside SummaryTableHTML is final, and package-private
462        //
463        // SummaryTableHTML.headerRow has type Vector<HTMLNode>
464
465        table.headerRow.clear();
466
467        return numRearranged;
468    }
469}