001package Torello.HTML.Tools.JavaDoc;
002
003import java.util.*;
004import java.io.*;
005import java.util.regex.*;
006
007import java.util.function.*;
008
009import Torello.HTML.*;
010import Torello.HTML.NodeSearch.*;
011import Torello.HTML.Tools.JavaDoc.*;
012import Torello.Java.*;
013
014import Torello.Java.Shell.C;
015
016/**
017 * <CODE>HiLiteConstructors - Documentation.</CODE><BR /><BR />
018 * <EMBED CLASS="external-html" DATA-FILE-ID="HLCONST">
019 */
020@StaticFunctional
021public class HiLiteConstructors
022{
023    private HiLiteConstructors() { }
024
025    private static final HTMLNode[] SURROUNDING_MESSAGE = HTMLPage
026        .getPageTokens(
027            "<DT><span class='" + Colorize.CSS_CLASS_FOR_CODE_METHOD_LABEL + "'>" +
028            "Code:</span></dt>\n" +
029            "<DD CLASS='" + Colorize.CSS_CLASS_FOR_CODE_METHOD_DEFINITION + "'>" +
030            "<B>Exact Constructor Body:</B><BR />\n",
031            false
032        )
033        .stream()
034        .toArray(HTMLNode[]::new);
035
036    /**
037     * This method will hilite all {@code Constructor's} that are scraped from a Java Doc Generated
038     * HTML Documentation Page for a Class, Enumerated Type, or Interface.  Hilited source-code for
039     * constructor-bodies (as {@code String's}) are inserted into the Java-Doc HTML Web-Page in the
040     * <B>{@code 'Constructor Details'}</B> section for any / all {@code Constructor's} found on
041     * the page..
042     * 
043     * @param pr This contains all of the needed parameters for this method, encapsulated into a
044     * single record-class.  The list is somewhat lengthy, so this makes the code "look cleaner"
045     * 
046     * @return This will return a count on exactly how many {@code Constructor's} have had their 
047     * code-hilited {@code Constructor}-bodies inserted into the JavaDoc Documentation File for
048     * this class.
049     */
050    public static int run(CommonParamRecord pr)
051    {
052        if (pr.sw != null) pr.sw.println
053            ("HiLiteConstructors.run(...) " + C.BRED + "[REQUESTED] " + C.RESET);
054
055        int countNumConstructorsHiLited = 0;
056        int numConstructorsSkipped      = 0;
057
058        // ********** Check whether the fileVec has any Constructors in the first place  **********
059        if (Details.hasConstructorDetails(pr.fileVec) == null)
060        {
061            if (pr.sw != null) pr.sw.println(
062                "\tJavaDoc File: " + C.BYELLOW + pr.jdFileName + C.RESET + " does not have a " +
063                "Constructor-Details-Section, Skipping..."
064            );
065            return 0; // no constructors to hilite, skip this file
066        }
067        // ********** Check whether the fileVec has any Constructors in the first place  **********
068
069
070        // ********** Iterate through every method found in the '.html' file  *****************
071        HNLIInclusive   constructorsIter    = Details.constructorDetailsIterator(pr.fileVec);
072        boolean         firstIteration      = true;
073        int             cur                 = 0;
074
075        while (constructorsIter.hasNext())
076        {
077            // Retrieves the next constructor-details section defined in the HTML file using the
078            // "Constructor Details Iterator"
079            DotPair constructorDP = constructorsIter.nextDotPair();
080
081            // The instance of JavaDocHTMLFile (jdhf) was built using the same '.html' file, and
082            // the same iterator.  Using the 'getConstructor(counter)' should retrieve the parsed
083            // version of this HTML-defined java constructor from the javadoc documentation HTML
084            // page.
085            Constructor fromHTMLFileConstructor = pr.jdhf.getConstructor(cur++);
086
087            // Now we need to search through all of the constructors in the JavaSourceCodeFile for
088            // a constructor with the EXACT SAME PARAMETER LIST.
089            Constructor fromSrcFileConstructor = (fromHTMLFileConstructor != null)
090                ? pr.jscf.findConstructor(fromHTMLFileConstructor)
091                : null;
092
093            // Since the instance of "Constructor" that is retrieved from the Source-Code '.java'
094            // file is guaranteed to have the "body text" filled out (as long as their was a body
095            // defined for that constructor, i.e. - it wasn't 'abstract') - we can get that string
096            // and save it and hilite it.
097            String  constructorBodyStr = (fromSrcFileConstructor != null)
098                ? fromSrcFileConstructor.body
099                : null;
100
101            // Output a tab or else it will look "lopsided"
102            if (pr.sw != null) if (firstIteration) { firstIteration = false; pr.sw.print('\t'); }
103
104            // If there was no constructor body found ... skip to the next constructor.
105            if (constructorBodyStr == null)
106            {
107                if (pr.sw != null)
108                    pr.sw.print
109                        ("[NOT-FOUND]: " + C.BRED + fromHTMLFileConstructor.name + C.RESET + "  ");
110
111                numConstructorsSkipped++;
112                continue;
113            }
114
115            // PRINT OUT THE CONSTRUCTOR
116            if (pr.sw != null) pr.sw.print(C.BGREEN + fromHTMLFileConstructor.name + C.RESET + "  ");
117
118
119            // ********** Compute the Insertion Point *****************
120            // The "<DL>  <DT></DT><DD></DD>  </DL>" lists are for the throws, see-also, returns,
121            // etc....  The Code HiLited Constructor-Body HTML needs to be inserted into its own:
122            // <DT>Code:</DT>\n
123            // <DD> Hi-Lited Constructor-Body DIV created by HiLite.ME Server</DD>
124            //
125            // The Insertion point shall be right before the closing </DL> HTML Element.  This
126            // means HiLited Code will *always* be the last 'thing' (for lack of a technical 
127            // term) that you can see in a Constructor-Details Section.
128            //
129            // ALSO: There are some Constructor for which no Javadoc Comments have been written,
130            //       therefore, there is no HTML "Definition List" already present in that
131            //       particular constructor's "Constructor Details" Section.  In this case, an HTML
132            //       <DL> ... </DL> surrounding element needs to be created.
133            // ********** Compute the Insertion Point *****************
134
135            int insertionPoint = TagNodeFind.last
136                (pr.fileVec, constructorDP.start, constructorDP.end, TC.ClosingTags, "dl");
137
138            boolean needToBuildDefList  = insertionPoint == -1;
139
140            // ********** Compute the Insertion Point *****************
141            // If we need to build the <DL> (Definition List), then we will also have to find a
142            // new insertion point.  The constructor-details section ends with a closing '</LI>' 
143            // element, and then a closing '</UL>' Element.  Right before the '</LI>' is where 
144            // the insertion should occur.
145            // ********** Compute the Insertion Point *****************
146
147            if (needToBuildDefList) insertionPoint = TagNodeFind.last
148                (pr.fileVec, constructorDP.start, constructorDP.end, TC.ClosingTags, "li");
149
150            if (insertionPoint == -1)
151                // This case should never happen, but keep it is here, just in case.
152                // (Kind of the point of exceptions).  Guarantees no "Index Out of Bounds" 
153                // Exceptions, and a friendly message.
154            {
155                if (pr.sw != null) pr.sw.println(
156                    C.BRED + "Could not determine a place to put the hilited code in the JD Page. " +
157                    "NOT INSERTING CODE." + C.RESET
158                );
159
160                continue;
161            }
162
163            // ********** Call the Code Hilite Server *****************
164            constructorBodyStr = StrIndent.chompCallableBraces(constructorBodyStr);
165
166            try
167            { 
168                constructorsIter.insertAt(
169                    hiLiteConstructorBody(constructorBodyStr, needToBuildDefList, pr.hiLiter),
170                    insertionPoint
171                );
172            }
173            catch (IOException | HiLiteException e)
174            {
175                throw new HiLiteError(
176                    "Failed to HiLite Constructor-Body (first three lines): " +
177                    "[\n" + StringParse.firstNLines(constructorBodyStr, 3) + "\n].\n" +
178                    "Currently Processing Java Source Code File: [" + pr.srcCodeFileName + "].\n" +
179                    "Currently Processing JavaDoc HTML Documentation Page: [" + pr.jdFileName + "].\n" +
180                    "Please see getCause() throwable for details.\n" + EXCC.toString(e),
181                    e
182                );
183            }
184            // ********** Call the Code Hilite Server *****************
185
186            // Loop to the next constructor.
187            countNumConstructorsHiLited++;
188        }
189
190        if ((countNumConstructorsHiLited > 0) || (numConstructorsSkipped > 0)) 
191            if (pr.sw != null) pr.sw.println();
192
193        if (numConstructorsSkipped > 0) 
194            if (pr.sw != null) pr.sw.println(
195                "\t" + C.BRED + "Skipped  " + C.RESET + 
196                C.BBLUE + StringParse.zeroPad(numConstructorsSkipped, 5) + C.RESET + " Constructors."
197            );
198
199        if (pr.sw != null) pr.sw.println(
200            "\tHilited  " + C.BBLUE + StringParse.zeroPad(countNumConstructorsHiLited, 5) + C.RESET +
201            " Constructors."
202        );
203
204        return countNumConstructorsHiLited;
205    }
206
207    /**
208     * This method is used to hilited the code of a Java <B>{@code Constructor Body}</B>
209     * 
210     * @param constructorBody This is the Java Source Code <B>{@code Constructor Body}</B>
211     * Definition that was retrieved using the <B>{@code 'JavaParser Library'}</B>
212     * 
213     * @param needToBuildDefList This happens when there is no {@code <DL> DD/DT ... </DL>} list
214     * already available on the page. In this case, and extra {@code '<DL>'} and {@code '</DL>'}
215     * must attached to the HTML-{@code Vector} that is produced here.
216     * 
217     * @param hiLiter The {@code Functional Interface} module that performs <B>Code Hiliting</B>.
218     * 
219     * @return This will return the HiLited Source Code.  It is vectorized into a
220     * {@code Vector<HTMLNode>}.
221     * 
222     * @throws IOException If there are I/O errors when calling the {@code HiLiter}
223     * @throws HiLiteException If there are other errors when invoking the {@code HiLiter}
224     * 
225     * @see HiLiter#hiLite(String, String, boolean)     *
226     * @see TagNodePeek
227     * @see TagNodeIndex
228     * @see HTMLTags#hasTag(String, TC)
229     */
230    private static Vector<HTMLNode> hiLiteConstructorBody
231        (String constructorBody, boolean needToBuildDefList, HiLiter hiLiter)
232        throws IOException, HiLiteException
233    {
234        // Call the "Pretty Print HiLiter"
235        Vector<HTMLNode> newSubSection = hiLiter.hiLite(constructorBody, "java", true);
236
237        for (int j = (SURROUNDING_MESSAGE.length - 1); j >= 0; j--) 
238            newSubSection.add(0, SURROUNDING_MESSAGE[j]);
239
240        // ***  The SURROUNDING_MESSAGE array has the opening <DT><SPAN ...>Code:</SPAN><DD>
241        //      nodes that are needed before the hilited code.  HOWEVER, the "Closing </DD>" 
242        //      HTML Element needs to be added *AFTER* (at the end of) the hilited code.
243
244        newSubSection.add(HTMLTags.hasTag("dd", TC.ClosingTags));
245            // adds </DD> to the end of this vector
246
247        // *** If we have to create the surrounding <DL>...</DL> list, because the actual
248        //     constructor-details section did not have one in the first place, add an opening
249        //     <DL> and also a closing </DL> element at the beginning, and the end, respectively.
250
251        if (needToBuildDefList)
252        {
253            newSubSection.add(0, HTMLTags.hasTag("dl", TC.OpeningTags));
254                // adds <DL> the beginning of this vector
255
256            newSubSection.add(HTMLTags.hasTag("dl", TC.ClosingTags));
257                // adds </DL> to the end of this vector.
258        }
259
260        // *** Make all of them scrollable
261        TagNodeIndex tni = TagNodePeek.first(newSubSection, TC.OpeningTags, "div");
262
263        newSubSection.setElementAt(
264            tni.n.appendToAV
265                ("style", " max-height: 20em; overflow: auto; ", true, SD.SingleQuotes),
266            tni.index
267        );
268
269        return newSubSection;
270    }
271}