001package Torello.JavaDoc;
002
003
004// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
005// Standard-Java Imports
006// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
007
008import java.util.*;
009
010import java.io.IOException;
011import java.util.function.Consumer;
012
013
014// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
015// Java-HTML Imports
016// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
017
018import Torello.Java.*;
019
020import static Torello.Java.C.*;
021import static Torello.JavaDoc.PF.*;
022
023import Torello.Java.ReadOnly.ReadOnlyList;
024import Torello.Java.ReadOnly.ReadOnlyArrayList;
025import Torello.Java.ReadOnly.ROArrayListBuilder;
026import Torello.Java.Additional.Ret2;
027
028
029// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
030// The new Source-Code Parser: com.sun.source.*
031// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
032
033import com.sun.source.util.*;
034import com.sun.source.tree.*;
035import com.sun.source.doctree.*;
036
037
038/**
039 * <B STYLE='color:darkred;'>Reflection Class:</B>
040 * 
041 * Common-Root Ancestor Class of all Bridge Data-Classes.
042 * 
043 * <BR /><BR />
044 * <EMBED CLASS='external-html' DATA-FILE-ID=JPB_DECLARATION>
045 * <EMBED CLASS='external-html' DATA-FILE-ID=JPB_DIAGRAM>
046 */
047@JDHeaderBackgroundImg
048public abstract class Declaration implements java.io.Serializable
049{
050    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */
051    public static final long serialVersionUID = 1;
052
053    /**
054     * For the purposes of passing these around to different parts of the code, every one of these
055     * are given a unique ID.  This id is unique for a method, whether it was parsed from a detail
056     * or a summary section.  This id is (probably) not useful outside of the HTML Processor
057     * Classes.
058     * 
059     * <BR /><BR /><B CLASS=JDDescLabel>ID Clone:</B>
060     * 
061     * <BR />If a subclass of {@code Declaration} is cloned, then this {@code id} field is also
062     * cloned / copied.
063     */
064    public final int id;
065
066    // The id is just created using this counter.
067    private static int idCounter = 1;
068
069    // Used for Modifiers and other Empty ReadOnlyLists's of String
070    static final ReadOnlyList<String> EMPTY_READONLY_LIST = ReadOnlyList.of();
071
072    /**
073     * This returns the {@code String} that is to be sent to the Syntax {@link HiLiter}.  This is
074     * the code inserted into the "HiLited Code" Part (at the end) of a Details Entry on a Java-Doc
075     * Web Page.
076     * 
077     * <BR /><BR />On the part of a Java-Doc Web-Page having a "Method Detail", the Method's
078     * HiLited Source-Code body is obtained by the Upgrader-Logic using this here method.
079     * 
080     * <BR /><BR /><B CLASS=JDDescLabel>Sub-Class Return Values:</B>
081     * 
082     * <BR />This method is overloaded by all sub-classes, and returns values as follows:
083     * 
084     * <BR /><BR /><UL CLASS=JDUL>
085     * <LI>{@link Method} will return its {@link Callable#body} field.</LI>
086     * <LI>{@link Constructor} will return its {@link Callable#body} field.</LI>
087     * <LI>{@link Field} will return the {@link #signature} field</LI>
088     * <LI>{@link EnumConstant} will return the {@link #signature} field</LI>
089     * <LI>{@link AnnotationElem} will return the {@link #signature} field</LI>
090     * <LI>{@link NestedType} will always return null</LI>
091     * </UL>
092     * 
093     * @return The {@code String} that is ultimately sent to the Syntax HiLiter, and inserted
094     * into a Java Doc page.
095     * 
096     * This is package-private, and isn't useful enough to put into the API - this is used
097     * internally, only.
098     */
099    abstract String codeHiLiteString();
100
101
102    // ********************************************************************************************
103    // ********************************************************************************************
104    // Basic String-Fields
105    // ********************************************************************************************
106    // ********************************************************************************************
107
108
109    /**
110     * The <B>Name</B> of the java {@link Field}, {@link Method}, {@code Constructor},
111     * {@link EnumConstant} or {@link AnnotationElem}.  This will be a <B>simple, standard</B>
112     * 'Java Identifier'.
113     * 
114     * <BR /><BR />Note that the name of a {@code Constructor} (for-example) is always just the
115     * name of the class.
116     * 
117     * <BR /><BR />This field will never be null.
118     */
119    public final String name;
120
121    /**
122     * The complete, declared <B>Signature</B> (as a {@code String}) of the {@link Method},
123     * {@link Field}, {@link Constructor}, {@link EnumConstant} or {@link AnnotationElem}.
124     * 
125     * <BR /><BR />This field would never be null.
126     */
127    public final String signature;
128
129    /**
130     * The <B>Java Doc Comment</B> of this <B>'{@link Entity}'</B> ({@link Field}, {@link Method},
131     * {@code Constructor}, {@link EnumConstant}, {@link AnnotationElem} or {@link NestedType}) as
132     * a {@code String} - if one exists.  The Java Doc Comment is the one defined directly above
133     * the {@code Declaration}.
134     * 
135     * <BR /><BR />If this <CODE>Entity</CODE> / Member ({@code Field, Method, Constructor} etc...)
136     * did not have a Java Doc Comment placed on it, <I>then this field {@code 'jdComment'} will be
137     * {@code null}.</I>
138     */
139    public final String jdComment;
140
141    /**
142     * The <B>Body</B> of this <B>'{@link Entity}'</B> ({@link Field}, {@link Method},
143     * {@code Constructor}, {@link EnumConstant}, {@link AnnotationElem} or {@link NestedType}) as
144     * a {@code String} - if one exists.  
145     * 
146     * <BR /><BR />If this <CODE>Entity</CODE> / Member ({@code Field, Method, Constructor} etc...)
147     * did not have a body, <I>then this field {@code 'body'} will be {@code null}.</I>
148     * 
149     * <BR /><BR />The {@code 'body'} of a {@code Method} or {@code Constructor} is exactly the
150     * code that comprises it.  If the method is {@code abstract}, then the method will not have a
151     * body, and in such cases this field will be null.  If this member / entity is a {@link Field}
152     * then the body is the initializer of the {@code Field}.  Again, if there were no initializer
153     * for the field, then {@code 'body'} would also be null.
154     */
155    public final String body;
156
157
158    // ********************************************************************************************
159    // ********************************************************************************************
160    // Non-Basic Fields
161    // ********************************************************************************************
162    // ********************************************************************************************
163
164
165    /**
166     * This just stores the type of {@link Entity} this is.  For sub-classes instances of
167     * {@link Declaration} which are {@link Method}, this field will be equal to
168     * {@link Entity#METHOD}.  For instances of the {@link Field} sub-class, this will equal
169     * {@link Entity#FIELD}, and so on and so forth.
170     * 
171     * <BR /><BR />Mostly, this makes code easier to read when used along-side <B>if-statements</B>
172     * or <B>switch-statements</B>.  This field somewhat akin to {@code Declaration.getClass()}
173     * (when retrieving the specific {@code Declaration} sub-class type).
174     * 
175     * <BR /><BR /><B CLASS=JDDescLabel>Reminder:</B>
176     * 
177     * <BR />Both this class, and sub-class {@code Callable} are declared {@code abstract}, and
178     * only instances of Method, Field, Constructor, etc... can be instantiated.  Only
179     * non-{@code abstract} implementations of this class need to worry about assigning this field
180     * to any real-value.
181     */
182    public final Entity entity;
183
184    /** Location instance that contains character-locations within the original Java Source-File */
185    public final Location location;
186
187
188    // ********************************************************************************************
189    // ********************************************************************************************
190    // ReadOnlyList<String> Fields
191    // ********************************************************************************************
192    // ********************************************************************************************
193
194
195    /**
196     * The {@code 'modifiers'} placed on this {@code Declaration}, including {@code String's}
197     * such as: {@code public, static, final} etc...
198     */
199    public ReadOnlyList<String> modifiers;
200
201    /** <EMBED CLASS='external-html' DATA-FILE-ID=JPB_DECL_ANNOT> */
202    public ReadOnlyList<String> annotations;
203
204
205    // ********************************************************************************************
206    // ********************************************************************************************
207    // Constructor - com.sun.source.tree 
208    // ********************************************************************************************
209    // ********************************************************************************************
210
211
212    // package-private: Only used by subclasses.
213    Declaration(
214            TreeUtils       util,
215            Tree            tree,
216            ModifiersTree   mt,
217            String          name,
218            Entity          entity,
219            Tree            body
220        )
221    {
222        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
223        // Pre-Liminary
224        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
225
226        this.id                 = idCounter++;
227        this.name               = name;
228        this.entity             = entity;
229
230
231        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
232        // The Annotations Placed on this Declaration
233        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
234        //
235        // NOTE: For EnumConstant's, the passed Modifiers-Tree is just null!
236
237        // List<? extends AnnotationTree> annotList = ...
238        @SuppressWarnings("unchecked")
239        List<AnnotationTree> annotList = (mt == null)
240            ? null
241            : (List<AnnotationTree>) mt.getAnnotations();
242
243        if ((annotList == null) || (annotList.size() == 0))
244            this.annotations = EMPTY_READONLY_LIST;
245
246        else this.annotations = new ReadOnlyArrayList<String>
247            (annotList, (AnnotationTree at) -> at.toString().trim(), annotList.size());
248
249
250        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
251        // The Modifiers that Flag this Declaration (private, public, static, final, etc...)
252        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
253        //
254        // NOTE: For EnumConstant's, the passed Modifiers-Tree is just null!
255
256        Set<javax.lang.model.element.Modifier> modSet = (mt == null)
257            ? null
258            : mt.getFlags();
259
260        if ((modSet == null) || (modSet.size() == 0))
261            this.modifiers = EMPTY_READONLY_LIST;
262
263        else this.modifiers = new ReadOnlyArrayList<String>
264            (modSet, (javax.lang.model.element.Modifier m) -> m.toString().trim(), modSet.size());
265
266
267        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
268        // Location
269        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
270
271        this.location = (entity == Entity.INNER_CLASS)
272
273            ? new Location(util, tree, util.getJavaDocCommentTree(tree))
274
275            : new Location(
276                util, tree, util.getJavaDocCommentTree(tree), body,
277                (entity == Entity.FIELD) ? body : null
278            );
279
280
281        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
282        // Now the String's
283        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
284
285        this.signature = util.srcFileAsStr.substring
286            (this.location.signatureStartPos, this.location.signatureEndPos);
287
288        this.jdComment = (this.location.jdcStartPos == -1)
289            ? null
290            : util.srcFileAsStr.substring(this.location.jdcStartPos, this.location.jdcEndPos);
291
292        this.body = (this.location.bodyStartPos == -1)
293            ? null
294            : util.srcFileAsStr.substring(this.location.bodyStartPos, this.location.bodyEndPos);
295    }
296
297
298    // ********************************************************************************************
299    // ********************************************************************************************
300    // Constructor - synthetic methods
301    // ********************************************************************************************
302    // ********************************************************************************************
303
304
305    // Used only by the Callable Subclass, for building instances of derived/synthetic methods
306    // and constructors.  (For instance, an enum has 'valueOf()' and 'values()')
307    // These methods do not have a location or a jdComment or even a body to hilite
308
309    Declaration(String name, Entity entity, String signature)
310    {
311        this.id             = idCounter++;
312        this.name           = name;
313        this.entity         = entity;
314        this.modifiers      = EMPTY_READONLY_LIST;
315        this.annotations    = EMPTY_READONLY_LIST;
316        this.signature      = signature;
317
318        // Since this constructor is only used for "Synthetic Methods" (barely ever used), it is no
319        // big deal to just assign these null;
320
321        this.location   = null;
322        this.jdComment  = null;
323        this.body       = null;
324    }
325
326    // Not private - used by sub-classes
327    Declaration(Declaration d)
328    {
329        this.id                 = d.id;
330        this.annotations        = d.annotations;
331        this.modifiers          = d.modifiers;
332        this.name               = d.name;
333        this.signature          = d.signature;
334        this.jdComment          = d.jdComment;
335        this.body               = d.body;
336        this.entity             = d.entity;
337        this.location           = d.location;
338    }
339
340
341    // ********************************************************************************************
342    // ********************************************************************************************
343    // Package-Private toString-HELPERS: Used by all subclasses 'toString(int flags)' methods
344    // ********************************************************************************************
345    // ********************************************************************************************
346
347
348    Ret2<Boolean, Boolean> jowFlags(int flags)
349    {
350        boolean onlyJOW         = (flags & JOW_INSTEAD) > 0;
351        boolean addJOW          = (flags & JOW_ALSO) > 0;
352
353        // "onlyJOW" has a higher FLAG-PRECEDENCY
354        if (onlyJOW && addJOW) addJOW = false;
355
356        return new Ret2<>(addJOW, onlyJOW);
357    }
358
359    String printedName(String entity, int numSpaces, boolean color)
360    {
361        return
362            StringParse.rightSpacePad(entity + " Name:", numSpaces) +
363            "[" + (color ? BCYAN : "") + name + (color ? RESET : "") + "]\n";
364    }
365
366    String printedSignature(int numSpaces, boolean color)
367    {
368        return
369            StringParse.rightSpacePad("Signature:", numSpaces) +
370            "[" + (color ? BYELLOW : "") +
371            StrPrint.abbrevEndRDSF(signature, MAX_STR_LEN, true) +
372            (color ? RESET : "") + "]\n";
373    }
374
375    String printedDeclaration(int numSpaces, boolean color)
376    {
377        return
378            StringParse.rightSpacePad("Declaration:", numSpaces) +
379            "[" + (color ? BYELLOW : "") +
380            StrPrint.abbrevEndRDSF(signature, MAX_STR_LEN, true) +
381            (color ? RESET : "") + "]\n";
382    }
383
384    String printedModifiers(int numSpaces)
385    {
386        return
387            StringParse.rightSpacePad("Modifiers:", numSpaces) +
388            "[" + StrCSV.toCSV(modifiers, true, true, null) + "]\n";
389    }
390
391    String printedComments(int numSpaces, boolean color, boolean comments)
392    {
393        if (! comments)
394            return "";
395
396        else if (jdComment == null) return
397            "\n" +
398            StringParse.rightSpacePad("JD Comments:", numSpaces) +
399            (color ? BRED : "") + "None Available / Not Included" + (color ? RESET : "");
400
401        else return
402            "\n" +
403            StringParse.rightSpacePad("JD Comments:", numSpaces) +
404            "[" +
405            (color ? BGREEN : "") +
406            StrPrint.abbrevEndRDSF(jdComment, MAX_STR_LEN, true) +
407            (color ? RESET : "") +
408            "]";
409    }
410
411    // NOTE: This is always the last item printed to the output-string.  This line *DOES NOT* end
412    //       with a new-line '\n' character.
413
414    String printedLocation(int numSpaces, boolean color, boolean briefLocation)
415    {
416        if (location == null) return StringParse.rightSpacePad("Location:", numSpaces) + "null";
417
418        if (briefLocation) return color
419            ?
420                (StringParse.rightSpacePad("Location:", numSpaces) +
421                "[" +
422                "signature-line=" + BRED + this.location.signatureStartLine + RESET + ", " +
423                "javadoc-line=" + BRED + this.location.jdcStartLine + RESET + ", " +
424                "body-line=" + BRED + this.location.bodyStartLine + RESET +
425                "]")
426            :
427                (StringParse.rightSpacePad("Location:", numSpaces) +
428                "[" +
429                "signature-line=" + this.location.signatureStartLine + ", " +
430                "javadoc-line=" + this.location.jdcStartLine + ", " +
431                "body-line=" + this.location.bodyStartLine +
432                ']');
433
434        String spaces = StringParse.nChars(' ', numSpaces);
435
436        if (color) return
437            StringParse.rightSpacePad("Location:", numSpaces) +
438            "JavaDocComment: [" +
439                "startPos=" + BRED + location.jdcStartPos + RESET + ", " +
440                "endPos=" + BRED + location.jdcEndPos + RESET + ", " +
441                "startLine=" + BRED + location.jdcStartLine + RESET + ", " +
442                "endLine=" + BRED + location.jdcEndLine + RESET + ", " +
443                "startCol=" + BRED + location.jdcStartCol + RESET + ", " +
444                "endCol=" + BRED + location.jdcEndCol + RESET + "]\n" +
445
446            spaces + "Signature:      [" +
447                "startPos=" + BRED + location.signatureStartPos + RESET + ", " +
448                "endPos=" + BRED + location.signatureEndPos + RESET + ", " +
449                "startLine=" + BRED + location.signatureStartLine + RESET + ", " +
450                "endLine=" + BRED + location.signatureEndLine + RESET + ", " +
451                "startCol=" + BRED + location.signatureStartCol + RESET + ", " +
452                "endCol=" + BRED + location.signatureEndCol + RESET + "]\n" +
453
454            spaces + "Body:           [" +
455                "startPos=" + BRED + location.bodyStartPos + RESET + ", " +
456                "endPos=" + BRED + location.bodyEndPos + RESET + ", " +
457                "startLine=" + BRED + location.bodyStartLine + RESET + ", " +
458                "endLine=" + BRED + location.bodyEndLine + RESET + ", " +
459                "startCol=" + BRED + location.bodyStartCol + RESET + ", " +
460                "endCol=" + BRED + location.bodyEndCol + RESET + "]";
461
462        else return
463            StringParse.rightSpacePad("Location:", numSpaces) +
464            "JavaDocComment: [" +
465                "startPos=" + location.jdcStartPos + ", " +
466                "endPos=" + location.jdcEndPos + ", " +
467                "startLine=" + location.jdcStartLine + ", " +
468                "endLine=" + location.jdcEndLine + ", " +
469                "startCol=" + location.jdcStartCol + ", " +
470                "endCol=" + location.jdcEndCol + "]\n" +
471
472            spaces + "Signature:      [" +
473                "startPos=" + location.signatureStartPos + ", " +
474                "endPos=" + location.signatureEndPos + ", " +
475                "startLine=" + location.signatureStartLine + ", " +
476                "endLine=" + location.signatureEndLine + ", " +
477                "startCol=" + location.signatureStartCol + ", " +
478                "endCol=" + location.signatureEndCol + "]\n" +
479
480            spaces + "Body:           [" +
481                "startPos=" + location.bodyStartPos + ", " +
482                "endPos=" + location.bodyEndPos + ", " +
483                "startLine=" + location.bodyStartLine + ", " +
484                "endLine=" + location.bodyEndLine + ", " +
485                "startCol=" + location.bodyStartCol + ", " +
486                "endCol=" + location.bodyEndCol + "]";
487    }
488
489
490    // ********************************************************************************************
491    // ********************************************************************************************
492    // Abstract-Class ToString
493    // ********************************************************************************************
494    // ********************************************************************************************
495
496
497    /**
498     * Dummy Method.  Overriden by Concrete Sub-Classes.
499     * @see Method#toString()
500     * @see Field#toString()
501     * @see Constructor#toString()
502     */
503    public String toString()
504    { return "Declaration is Abstract, all Concrete Sub-Classes Override this method."; }
505
506    /**
507     * Dummy Method.  Overriden by Concrete Sub-Classes.
508     * @see Method#toString(int)
509     * @see Field#toString(int)
510     * @see Constructor#toString(int)
511     */
512    public String toString(int flags)
513    { return "Declaration is Abstract, all Concrete Sub-Classes Override this method."; }
514}