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