001package Torello.JavaDoc.Annotations;
002
003import static Torello.Java.C.BCYAN;
004import static Torello.Java.C.BRED;
005import static Torello.Java.C.RESET;
006
007import Torello.Java.ReadOnly.ReadOnlyList;
008import Torello.Java.ReadOnly.ReadOnlySet;
009
010import Torello.Java.StringParse;
011
012import Torello.JavaDoc.Entity;
013
014import Torello.JavaDoc.Messager.Messager;
015import Torello.JavaDoc.Messager.MsgPrintTools;
016import Torello.JavaDoc.Messager.Where_Am_I;
017import Torello.JDUInternal.Miscellaneous.Where.JDUAnnotations;
018
019import com.sun.source.tree.AnnotationTree;
020import com.sun.source.tree.ExpressionTree;
021import com.sun.source.tree.AssignmentTree;
022import com.sun.source.tree.LiteralTree;
023
024import java.util.List;
025
026//     public String    handle();
027//     public String    typeName()      default "";
028//     public Entity    entity();
029//     public String    name();
030//     public byte      paramCount()    default -1;
031//     public String[]  paramNames()    default { };
032//     public String[]  paramTypesJOW() default { };
033// 
034// The following files are relatedf to this Annotation-Mirror Data-Class:
035// 
036// package Torello.JDUInternal.Features.LINK_JAVA_SOURCE
037//      This package does the "vast majority" of the work that is needed process a User's
038//      Annotation-Placement.  The classes in the packages in this class look for, and load, all 
039//      of the External '.java'-Files which have been specified by the programmer's annotation
040//      uses.  These classes also perform the Source-Code HiLiting, and save the output to the
041//      appropriate packge's '[pkg-javadoc]/ljs-hilite-files/' directory.  They finally, also,
042//      generate the appropriate "HREF=..." so that an appropriate '<A HREF...>' link may be
043//      inserted
044
045/**
046 * An internally used data-record class used for storing all user-supplied annotation data 
047 * associated with a single use / application of the {@link LinkJavaSource} annotation.
048 * 
049 * @see Torello.JavaDoc.Annotations.LinkJavaSource
050 */
051@Torello.JavaDoc.Annotations.JDHeaderBackgroundImg(EmbedTagFileID="MIRROR_JDHBI")
052public class LJSMirror implements AnnotationMirror
053{
054    // ********************************************************************************************
055    // ********************************************************************************************
056    // Static-Constant Fields
057    // ********************************************************************************************
058    // ********************************************************************************************
059
060
061    private static final Where_Am_I WHERE_AM_I = JDUAnnotations.LJSMirror;
062
063    private static final ECData<Torello.JavaDoc.Entity> ecParser = new ECData<>(
064        Torello.JavaDoc.Entity.class,
065    
066        // These are strictly needed for Error message which are being passed to the messager
067        JDUAnnotations.LJSMirror,               // Where_Am_I 
068        LinkJavaSource.class.getSimpleName(),   // The name of the Annoation
069        "entity"                                // The Annotation-Element being processed
070    );
071
072
073    // ********************************************************************************************
074    // ********************************************************************************************
075    // interface AnnotationMirror
076    // ********************************************************************************************
077    // ********************************************************************************************
078
079
080    // Stores the actual text of the @StaticFunctional annotation which was placed.
081    // This is nothing more than the exact line that was originally inserted into the class.
082    // The only reason this is even needed (in case you are wondering) is because the error 
083    // checking & error printing code outputs this text when printing an error message.
084
085    private final String annotationAsStr;
086
087    /** {@inheritDoc} */ @Override
088    public String getAnnotationAsString() { return annotationAsStr; }
089
090    /** {@inheritDoc} */ @Override
091    public String getAnnotationName() { return "LJSMirror"; }
092
093
094    // ********************************************************************************************
095    // ********************************************************************************************
096    // Public, Final, ReadOnly, Mirrored Fields
097    // ********************************************************************************************
098    // ********************************************************************************************
099
100
101    /** Holds data extracted from {@link LinkJavaSource#handle()} */
102    public final String handle;
103
104    /** Holds data extracted from {@link LinkJavaSource#typeName()} */
105    public final String typeName;
106
107    /** Holds data extracted from {@link LinkJavaSource#entity()} */
108    public final Entity entity;
109
110    /** Holds data extracted from {@link LinkJavaSource#name()} */
111    public final String name;
112
113    /** Holds data extracted from {@link LinkJavaSource#paramCount()} */
114    public final Byte paramCount;
115
116    /** Holds data extracted from {@link LinkJavaSource#paramNames()} */
117    public final ReadOnlyList<String> paramNames;
118
119    /** Holds data extracted from {@link LinkJavaSource#paramTypesJOW()} */
120    public final ReadOnlyList<String> paramTypesJOW;
121
122    /**
123     * Assigned {@code TRUE} if and only if {@link #handle} is the only annotation element that
124     * has been assigned a value.
125     */
126    public final boolean hrefOnlyNoLineNum;
127
128
129    // ********************************************************************************************
130    // ********************************************************************************************
131    // Package-Private Constructor, Invoked by class "EntityAnnotationMirrors"
132    // ********************************************************************************************
133    // ********************************************************************************************
134
135
136    // This is invoked by the top-level mirror-class: EntityAnnotationMirrors
137    // ReflAnnotationMirros is in this package, and therefore, this constructor is
138    // package private.
139    // 
140    // This constructor was entirely created by Chat-GPT.
141    // Yes, it has been thoroughly tested!
142    // Yes, it's the fun of it all that makes ChatGPT worth it.
143    // 
144    // I have changed it many times since writing that comment.  Not a lot though!
145    // I don't bother messing with the A.I. for the "Error-Checking" code.  The truth of it all is
146    // that if you approach Chat-GPT like a puzzle, just like programming usually feels like
147    // EXCEPT that the challenge is thinking how to describe what you want to do in English, rather
148    // than in Java-Code - watching it output your code is sort of enjoyable...
149    // 
150    // Anything that involves the Messager, I simply cannot think of the English words to explain
151    // what I want it to do.  I wouldn't bother asking Mr. GPT about it at all, because it wouldn't
152    // even be fun...  The Messager / Error-Checking stuff truly is some of the toughest stuff to
153    // write of all.
154    //
155    // ********************************************************************************************
156    // ********************************************************************************************
157    //
158    // Data-Flow I think I have mastered.  Generally, with data-flow, there are two rules:
159    // 
160    //      a) 'final' and ReadOnly answer many of the problems you face...  It doesn't change!
161    //          No matter what you do with it, or where you send, it cannot change, it was declared
162    //          **BOTH** final **AND** ReadOnly - it is just a "Lookup Operation"
163    // 
164    //      b) Consistent VARIABLE / FIELD NAMES ... answer the rest of your problems
165    //         It is **ALWAYS** named "jdhf" (all-lower-case), no matter where in the code you are!
166    // 
167    //         A JavaSourceCodeFile is **ALWAYS** named 'jscf'
168    //         A 'jdhf' can change, a 'jscf' is 100% final-Read-Only...
169    //
170    // And that is literally it!  Pick a name for your class, and no matter where in the code you
171    // need or would like to reference that class, alway use the same field or variable name for
172    // that class ... And that is all you need to say about "Data-Flow"...  
173    //
174    // Again: It's constant / unchanging... It's all just "table lookup"
175    //        It always has the same exact name, no matter where it is...  And nothing about the
176    //        data is being modified, so it doesn't really matter what you do with the reference,
177    //        you cannot change it's values since they are final-read-only!
178    // 
179    //        I don't have to care what happens to the contents of an object when I pass off a 
180    //        reference in my code...
181    //
182    // ********************************************************************************************
183    // ********************************************************************************************
184    // 
185    // With "Program Flow" or "Control Flow" ...  A.K.A. The messager throws exceptions and returns
186    // true/false/null - I'm still confused as to what the right way to think about it even is.
187    // 
188    // It has something to do with **NOT** catching exceptions...
189    // But it also has something to do with "Messager.checkErrors"
190    // 
191    // I really don't completely get what I'm doing at the moment with "Control-Flow"
192    // 
193    // ********************************************************************************************
194    // ********************************************************************************************
195
196
197    LJSMirror(
198            // This is the Annotation, as a simple Java-String.  This is very useful for
199            // Error-Printing with the Messager.  That's the only purpose of it
200
201            final String annotationAsStr,
202
203            // The parsed arguments to to the Annotation
204            final List<? extends ExpressionTree> arguments,
205
206
207            // The Signature of the Detail-Entity onto which this "@LinkJavaSource" Annotation
208            // was placed.
209
210            final String signature,
211
212            // The set of all acceptable handles for a package
213            final ReadOnlySet<String> allPkgLJSHandles,
214
215
216            // This is a list of all of the LJS Files that were loaded as a result of the '.json'
217            // Files list of External LJS Files AND ARE UN-PARSED.  The user has the ability to
218            // ask that a file not be parsed.  The value here is that many external '.java' Files
219            // are very short classes that contain only one method.  In such cases the external
220            // HTML-Anchor element DOESN'T NEED A LINE-NUMBER AT ALL !  It just needs the
221            // '.java.html' File-Name.
222            // 
223            // This is a list of such classes.
224
225            final ReadOnlySet<String> unParsedLJSFilesList
226        )
227    {
228        String  handle          = null;
229        String  typeName        = null;
230        Entity  entity          = null;
231        String  name            = null;
232        Byte    paramCount      = null;
233        boolean notJustHandle   = false;
234
235        ReadOnlyList<String> paramNames     = null;
236        ReadOnlyList<String> paramTypesJOW  = null;
237
238        for (int i = 0; i < arguments.size(); i++)
239        {
240            final ExpressionTree expr = arguments.get(i);
241
242            if (! (expr instanceof AssignmentTree)) Messager.assertFail(
243                "Annotation Placed:\n" +
244                "    " + BCYAN + annotationAsStr + RESET + '\n' +
245                "While parsing the Arguments / Elements of an LJSMirror Annotation, the " +
246                "Oracle-Parser (com.sun.source.tree) returned an instance of `ExpressionTree` " +
247                "that was not an instance of `AssignmentTree`.  This error should have been " +
248                "caught by `javac` during the Compilation of this Source-File.",
249                WHERE_AM_I
250            );
251
252            final AssignmentTree assignExpr = (AssignmentTree) expr;
253
254            // Extracting the left-hand side (LHS) and right-hand side (RHS)
255            ExpressionTree LHS = assignExpr.getVariable();
256            ExpressionTree RHS = assignExpr.getExpression();
257
258            final String lhsStr = LHS.toString();
259            // final String rhsStr = RHS.toString().replace("\\'", "'");
260
261            final String rhsStr = (RHS instanceof LiteralTree) 
262                ? ((LiteralTree) RHS).getValue().toString()
263                : RHS.toString();
264
265            String temp;
266
267            final Where_Am_I WHERE_AM_I = JDUAnnotations.LJSMirror;
268
269            switch (lhsStr)
270            {
271                case "handle":
272
273                    if (handle != null) HELPER.dupErrorAE
274                        (annotationAsStr, handle, "handle", rhsStr, WHERE_AM_I);
275                    else
276                        handle = rhsStr;
277
278                    break;
279
280
281                case "typeName":
282
283                    if (typeName != null) HELPER.dupErrorAE
284                        (annotationAsStr, typeName, "typeName", rhsStr, WHERE_AM_I);
285                    else
286                        typeName = rhsStr;
287
288                    notJustHandle = true;
289                    break;
290
291
292                case "entity":
293
294                    if (entity != null) HELPER.dupErrorAE
295                        (annotationAsStr, entity, "entity", rhsStr, WHERE_AM_I);
296                    else 
297                        entity = ecParser.valueOf(rhsStr, signature);
298
299                    notJustHandle = true;
300                    break;
301
302
303                case "name":
304
305                    if (name != null) HELPER.dupErrorAE
306                        (annotationAsStr, name, "name", rhsStr, WHERE_AM_I);
307                    else 
308                        name = rhsStr;
309
310                    notJustHandle = true;
311                    break;
312
313
314                case "paramCount":
315
316                    if (paramCount != null) HELPER.dupErrorAE
317                        (annotationAsStr, paramCount, "paramCount", rhsStr, WHERE_AM_I);
318
319                    else if (! StringParse.isByte(rhsStr)) Messager.userErrorContinue(
320                        "Annotation Placed:\n" +
321                        "    " + BCYAN + annotationAsStr + RESET + '\n' +
322                        "The value you have provided to element 'paramCount' is not a valid Byte:\n" +
323                        "    [" + BRED + rhsStr + RESET + "]\n" +
324                        MsgPrintTools.annotationProcessingError(),
325                        WHERE_AM_I
326                    );
327
328                    else paramCount = Byte.parseByte(rhsStr);
329
330                    notJustHandle = true;
331                    break;
332
333
334                case "paramNames":
335
336                    if (paramNames != null) HELPER.dupErrorAE
337                        (annotationAsStr, paramNames, "paramNames", rhsStr, WHERE_AM_I);
338                    else
339                        paramNames = extractStringArray(rhsStr);
340
341                    notJustHandle = true;
342                    break;
343
344
345                case "paramTypesJOW":
346
347                    if (paramTypesJOW != null) HELPER.dupErrorAE
348                        (annotationAsStr, paramTypesJOW, "paramTypesJOW", rhsStr, WHERE_AM_I);
349                    else
350                        paramTypesJOW = extractStringArray(rhsStr);
351
352                    notJustHandle = true;
353                    break;
354
355
356                default: Messager.userErrorContinue(
357                    "While proessing a @LinkJavaSource Annotation, the following " +
358                    "Annotation-Element Name was found:\n" +
359                    "    [" + lhsStr + "]\n" +
360                    "This is not a valid Annotation-Argument, and this error should have been " +
361                    "caught by `javac` during its Compilation-Phase.  This is not one of " +
362                    "@LinkJavaSource' Elements (sometimes called \"Arguements\")\n" +
363                    "Valid Element-Names for this Annotation Include:\n" +
364                    "    handle, typeName, entity, name, paramCount, paramNames " +
365                    "and paramTypesJOW\n" +
366                    "Have you disabled Annotation-Processing?",
367                    WHERE_AM_I
368                );
369            }
370        }
371
372        // "entity" shall remain null if it wasn't passed a value
373        // I used to do the following:
374        // 
375        // this.entity = (entity == null) ? Entity.METHOD : entity;
376
377        // Assigning values to final fields
378        this.annotationAsStr    = annotationAsStr;
379        this.handle             = handle;
380        this.typeName           = typeName;
381        this.name               = name;
382        this.entity             = entity;
383        this.paramCount         = paramCount;
384        this.paramNames         = paramNames;
385        this.paramTypesJOW      = paramTypesJOW;
386
387        // This is not an actual Annotation-Element, it is a 
388        this.hrefOnlyNoLineNum = notJustHandle == false;
389
390        LJSErrorCheck.check(this, unParsedLJSFilesList, allPkgLJSHandles, signature);
391    }
392
393
394    // ********************************************************************************************
395    // ********************************************************************************************
396    // Helpers
397    // ********************************************************************************************
398    // ********************************************************************************************
399
400
401    // Helper method to extract string arrays from ExpressionTree
402    private ReadOnlyList<String> extractStringArray(String rhsStr)
403    {
404        // The user may pass a String-Array of Length=1, and omit the Squiggly-Braces
405        // If he has done so, there will not Squiggly-Braces!
406
407        // if (rhsStr.charAt(0) != '{')
408        //    return ReadOnlyList.of(StringParse.ifQuotesStripQuotes(rhsStr));
409
410        // Assuming format: "fieldName={value1, value2, value3}"
411        rhsStr = rhsStr.substring(1, rhsStr.length() - 1);
412
413        String[] sArr = rhsStr.split(",");
414
415        for (int i=0; i < sArr.length; i++)
416            sArr[i] = StringParse.ifQuotesStripQuotes(sArr[i].trim());
417
418        return ReadOnlyList.of(sArr);
419    }
420
421
422    // ********************************************************************************************
423    // ********************************************************************************************
424    // toString
425    // ********************************************************************************************
426    // ********************************************************************************************
427
428
429    /** {@inheritDoc} */ @Override
430    public String toString()
431    {
432        // public final String                 handle;
433        // public final String                 typeName;
434        // public final Entity                 entity;
435        // public final String                 name;
436        // public final Byte                   paramCount;
437        // public final ReadOnlyList<String>   paramNames;
438        // public final ReadOnlyList<String>   paramTypesJOW;
439        // public final boolean                hrefOnlyNoLineNum;
440        // 
441        // 19 ==> Width of the Title Strings....  They are all 19 characters wide
442
443        return 
444            annotationAsStr + '\n' +
445            "{\n" +
446            "// Category: User Specifications for Hi-Liting and Linking onto Java-Doc Page\n" +
447            "    entity:        " + HELPER.TOSTR(this.entity)               + '\n' +
448            "    name:          " + HELPER.TOSTR(this.name, 19)             + '\n' +
449            "    paramCount:    " + paramCount                              + '\n' +
450            "    paramNames:    " + HELPER.TOSTR(this.paramNames, 19)       + '\n' +
451            "    paramTypesJOW: " + HELPER.TOSTR(this.paramTypesJOW, 19)    + '\n' +
452            '\n' +
453            "// Category: Source-Code File and Class Specification\n" +
454            "    handle:        " + HELPER.TOSTR(this.handle, 19)           + '\n' +
455            "    typeName:      " + HELPER.TOSTR(this.typeName, 19)         + '\n' +
456            '}';
457    }
458}