1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
package Torello.JavaDoc.Annotations;

import static Torello.Java.C.BGREEN;
import static Torello.Java.C.BCYAN;
import static Torello.Java.C.BRED;
import static Torello.Java.C.RESET;

import Torello.Java.StrCSV;
import Torello.Java.StrSource;
import Torello.Java.ReadOnly.ReadOnlySet;

import Torello.JavaDoc.Entity;

import Torello.JavaDoc.Messager.Messager;
import Torello.JavaDoc.Messager.Where_Am_I;
import Torello.JDUInternal.Miscellaneous.Where.JDUAnnotations;

import java.util.ArrayList;
import java.util.function.Function;

class LJSErrorCheck
{
    private static final Where_Am_I WHERE_AM_I = JDUAnnotations.LJSErrorCheck;

    // class LJSMirror Fields:
    // 
    // public final String                 annotationAsStr;
    // public final String                 handle;
    // public final String                 typeName;
    // public final Entity                 entity;
    // public final String                 name;
    // public final byte                   paramCount;
    // public final ReadOnlyList<String>   paramNames;
    // public final ReadOnlyList<String>   paramTypesJOW;
    // public final boolean                hrefOnlyNoLineNum;

    static void check(
            final LJSMirror             m,        
            final ReadOnlySet<String>   unParsedLJSFilesList,
            final ReadOnlySet<String>   allPkgLJSHandles,
            final String                signature
        )
    {
        // The only way to link a Syntax-HiLited '.java' HTML-File onto a Java-Doc Web-Page using
        // the @LinkJavaSource Annotation is to provide a valid '.java' File-Handle to the 
        // Annotation-Element named 'handle'
        // 
        // If no handle was provided, an error has occured.  This should have been caught by the
        // user's Annotation-Processor, but if that has been disabled, it has to be checked here
        // as well.

        if (m.handle == null)
        {
            Messager.userErrorContinue(
                "Annotation Placed:\n" +
                "    " + BCYAN + m.getAnnotationAsString() + RESET + '\n' +
                "You have placed a @LinkJavaSource Annotation on one of your entities inside of " +
                "a Java-File.  It is mandatory that you provide a value for the 'handle' " +
                "Annotation-Element.\n" +
                '\n' +
                "This error ought to have been caught during the Compilation-Phase of your " +
                "Build by the Standard 'javac' Annotation-Processor.\n" +
                "Have you disabled Annotation-Processing?",
                WHERE_AM_I
            );

            return;
        }

        if (! StrSource.isValidJavaIdentifier(m.handle))
        {
            Messager.userErrorContinue(
                "Annotation Placed:\n" +
                "    " + BCYAN + m.getAnnotationAsString() + RESET + '\n' +
                "The value you have provided to the Annotation-Element " +
                    BGREEN + "handle" + RESET + ":\n" +
                "    [" + BRED + m.handle + RESET + "]\n" +
                "is not a valid Java-Identifier.",
                WHERE_AM_I
            );

            return;
        }

        // I forgot to add this until today!  Nov. 8th, 2024
        // This is eventually found when trying to get the link into the page.
        // However, when the D4 record finds this error, it eeks its way out as an
        // assertFailCheckup, and is stated as a "Developer Fault Internal Error"
        // 
        // Don't forget to include this check!  Adding it required bubbling up this 
        // "allPkgLJSHandles" into the EntityAnnotationsData record.  Which I hve now officially
        // accomplished.  This error message looks much better than an "Assert-Fail"

        if (! allPkgLJSHandles.contains(m.handle))
        {
            Messager.userErrorContinue(
                "Annotation Placed:\n" +
                "    " + BCYAN + m.getAnnotationAsString() + RESET + '\n' +
                "The value you have provided to the Annotation-Element " +
                    BGREEN + "handle" + RESET + ":\n" +
                "    [" + BRED + m.handle + RESET + "]\n" +
                "was not listed or located amongst the specified File-Handles that were found " +
                "within your LJS User-Configuration '.json' Files",
                WHERE_AM_I
            );

            return;
        }

        // This is only true if all Annotation-Elements are empty / null / unused
        // The above check guarantees that there is a handle, or that an error message about a
        // missing handle is printed.  If this is true, THERE IS NOTHING LEFT TO CHECK !

        if (m.hrefOnlyNoLineNum) return;


        // Here, we KNOW-FOR-CERTAIN that there is at least one non-null, non-empty 
        // Annotation-Element / Annotation-Parameter.  If the handle that was used is actually
        // specifying one of the files that had the "Parse": false JSON-Setting in the User's
        // LinkJavaSource.json file, then it must be flagged as an error.
        // 
        // A user can only provide Annotation-Values to the Elements inside of @LinkJavaSource
        // IF HE IS WILLING TO PARSE THAT FILE INTO AN AST...
        // 
        // Parsing a file into an AST is kind of expensive, so it isn't the greatest thing to do.
        // If you can get away with leaving off the Line-Number Information inside the HTML-Anchor
        // that is inserted into your '.java' File, then also make sure to LEAVE-OFF all of the 
        // extra Annotation-Element fields when using @LinkJavaSource.
        //
        // In that case, the HTML <A HREF=URL> will have a URL that just points to the top of the 
        // Syntax-HiLited '.java' File, and won't include the "#Line-Number" in the link!  It is a
        // lot faster, but (obviously) has some draw-backs.  If you are linking a giant '.java'
        // File, the user is going to have to laboriously scroll through that file to find what you
        // are trying to show.

        if (unParsedLJSFilesList.contains(m.handle))
        {
            Messager.userErrorContinue(
                "Annotation Placed:\n" +
                "    " + BCYAN + m.getAnnotationAsString() + RESET + '\n' +
                "Your Annotation provides values for the following fields:\n" +
                "    [" + fields(m) + "]\n" +
                "Unfortunately, the '.java' File that is named by your File-Hande:\n" +
                "    [" + BGREEN + m.handle + RESET + "],\n" +
                "received the explicit request that it remain unparsed.\n" +
                '\n' +
                "When your @LinkJavaSource '.java' Files are not parsed into AST's, the " +
                "HTML-Anchor HREF-URL's which are inserted into your pages simply may not " +
                "contain any Line-Number or File-Location information.\n" +
                '\n' +
                "These HiLited '.java' Files may still be linked using an HTML Anchor, but the " +
                "HREF-URL may only point to the top of the page.  The URL cannot link to a " +
                "particular or 'relative' location on the HiLited Source-Code Web-Page.",
                WHERE_AM_I
            );

            return;
        }


        // So at this point we are certain that the user has provided a reasonable File-Handle,
        // and that he has also provided AT LEAST ONE kind of Member-Specifier because he wants the
        // Line-Number of the Entity to be included in the <A HREF="URL.html#LineNum"> that is
        // going to be inserted onto his page.
        // 
        // Now it is time to validate the rest of the information.
        // First validate that there is a name, it is non-null, and it is a valid Java Identifier
        //
        // If the name **IS** null, it must be for a link to a constructor, otherwise, this is a 
        // User-Error.

        if (m.name == null)
        {
            if (m.entity != Entity.CONSTRUCTOR)
            {
                Messager.userErrorContinue(
                    "Annotation Placed:\n" +
                    "    " + BCYAN + m.getAnnotationAsString() + RESET + '\n' +
                    "You have requested that a specific " + entityStr(m) + " linked to Java-Doc " +
                    "Web-Page Detail-Entry.  However, you have not provided a " +
                    BGREEN + "name" + RESET + " Element-Value to your @LinkJavaSource " +
                    "Annotation-Placement.\n",
                    WHERE_AM_I
                );

                return;
            }
        }

        // In this 'else', we know that 'name' is non-null
        else
        {
            if (m.entity == Entity.CONSTRUCTOR)
            {
                Messager.userErrorContinue(
                    "Annotation Placed:\n" +
                    "    " + BCYAN + m.getAnnotationAsString() + RESET + '\n' +
                    "You have provided a value to the '" + BGREEN + "name" + RESET + "' element " +
                    "in your Annotation-Placement.  However, you have always specified that the " +
                    "entity you would like linked is a " + entityStr(m) + ".\n" +
                    "Constructors are not actually, official, Java-Type Members, and they do " +
                    "have names.  Therefore, when requesting a link to a constructor, you may " +
                    "not provide a value to the '" + BGREEN + "name" + RESET + "' element.",
                    WHERE_AM_I
                );

                return;
            }

            if (! StrSource.isValidJavaIdentifier(m.name))
            {
                Messager.userErrorContinue(
                    "Annotation Placed:\n" +
                    "    " + BCYAN + m.getAnnotationAsString() + RESET + '\n' +
                    "You have requested that a specific " + entityStr(m) + " linked to Java-Doc " +
                    "Web-Page Detail-Entry.  However, the " + BGREEN + "name" + RESET + " you " +
                    "provided:\n" +
                    "    [" + BRED + m.name + RESET + "]\n" +
                    "is not a value Java-Identifier, and therefore couldn't possibly specify a " +
                    "valid " + entityStr(m),
                    WHERE_AM_I
                );

                return;
            }
        }


        // Now it is time to validate whether the rest of the elements are used.
        // The 'name' is the only element allowed for all of the Entities,
        // other than Constructors or Methods.

        final boolean requestsCallable = 
                (m.entity == null)
            ||  (m.entity == Entity.METHOD)
            ||  (m.entity == Entity.CONSTRUCTOR);

        final int count =
            ((m.paramNames    != null)    ? 1 : 0) +
            ((m.paramTypesJOW != null)    ? 1 : 0) +
            ((m.paramCount    != null)    ? 1 : 0);

        if (requestsCallable && (count > 1))
        {
            Messager.userErrorContinue(
                "Annotation Placed:\n" +
                "    " + BCYAN + m.getAnnotationAsString() + RESET + '\n' +
                "An unexpected error has occured, which should have been caught during the " +
                "'javac' Compilation-Phase.  When annotating a " + entityStr(m) + " with the " +
                "@LinkJavaSource annotation, you must provide a value for exactly one of the " +
                "following Elements / Arguments:\n" +
                "    [" +
                    BGREEN + "paramNames" + RESET + ',' + 
                    BGREEN + "paramTypesJOW" + RESET + ',' +
                    BGREEN + "paramCount" + RESET + 
                    "]\n" +
                "You have provided Argument-Values for more than one of these arguments.\n" +
                "In your Annotation-Use, the following Annotation-Elements were provided " +
                "values:\n" +
                fieldsForCallablesOnly(m),
                WHERE_AM_I
            );

            return;
        }

        if ((! requestsCallable) && (count > 0))
        {
            Messager.userErrorContinue(
                "Annotation Placed:\n" +
                "    " + BCYAN + m.getAnnotationAsString() + RESET + '\n' +
                "You requested that a(n) " + m.entity + " have it's Java-Source Linked, " +
                "using the @LinkJavaSource Annotation.  However, you have provided values for " +
                "the following Annotation-Elements, listed below:\n" +
                fieldsForCallablesOnly(m) +
                "These Annotation-Elements may only be used when requesting an <A HREF=URL> " +
                "direct-link onto your page for a Method or Constructor (which actually have " +
                "parameters).\n" +
                '\n' +
                "Since a(n) " + m.entity + " does not actually accept parameters, the elements " +
                "listed above are not allowed in your @LinkJavaSource Annotation-Use." +
                '\n' +
                "Alternatively, if you have intended to name a Method or Constructor, please " +
                "modify the 'Entity' Enum-Constant passed to your '" + BGREEN + "entity" + RESET +
                "' parameter to either Entity.METHOD or Entity.CONSTRUCTOR.  Note that leaving " +
                "the '" + BGREEN + "entity" + RESET + "' element blank will force its value to " +
                "the default, which is Entity.METHOD.",
                WHERE_AM_I
            );

            return;
        }

        if ((m.entity == Entity.CONSTRUCTOR) && (count == 0))
        {
            Messager.userErrorContinue(
                "Annotation Placed:\n" +
                "    " + BCYAN + m.getAnnotationAsString() + RESET + '\n' +
                "You have requested that a " + entityStr(m) + " be hilited.\n" +
                "In order to locate this constructor within the '.java' Source-File (and find " +
                "its Line-Number), you must provide a value to exactly one of the following " +
                "Elements / Arguments:\n" +
                "    [" +
                    BGREEN + "paramNames" + RESET + ',' + 
                    BGREEN + "paramTypesJOW" + RESET + ',' +
                    BGREEN + "paramCount" + RESET + 
                    "]\n",
                WHERE_AM_I
            );

            return;
        }


        if (    (m.paramCount != null)
            &&  (m.paramCount < 0)
        )
        {
            Messager.userErrorContinue(
                "Annotation Placed:\n" +
                "    " + BCYAN + m.getAnnotationAsString() + RESET + '\n' +
                "The value you have provided to parameter '" + BGREEN + "paramCount" + RESET +
                "' is negative.",
                WHERE_AM_I
            );

            return;
        }
    }

    private static String entityStr(LJSMirror m)
    {
        return (m.entity != null)
            ? (BCYAN + m.entity.toString() + RESET)
            : (BCYAN + "method" + RESET);
    }

    private static final Function<String, String> F1 =
        (String annotationElementName) -> BRED + annotationElementName + RESET;

    private static String fields(final LJSMirror m)
    {
        ArrayList<String> al = new ArrayList<>();

        if (m.name != null)             al.add("name");
        if (m.entity != null)           al.add("entity");
        if (m.paramCount != null)       al.add("paramCount");
        if (m.paramNames != null)       al.add("paramNames");
        if (m.paramTypesJOW != null)    al.add("paramTypesJOW");
        if (m.typeName != null)         al.add("typeName");

        return StrCSV.toCSV(al, F1, true, null);
    }

    private static String fieldsForCallablesOnly(final LJSMirror m)
    {
        // "    paramNames=".length() => 15
        // "    paramTypesJOW=".length() => 17
        return
            ((m.paramCount == null)
            ? ""
            : ("    " + BGREEN + "paramCount" + RESET + '=' + m.paramCount + '\n')) +

        ((m.paramNames == null)
            ? ""
            : ("    " + BGREEN + "paramNames" + RESET + '=' +
                HELPER.TOSTR(m.paramNames, 15) + '\n')) +

        ((m.paramTypesJOW == null)
            ? ""
            : ("    " + BGREEN + "paramTypesJOW" + RESET + '=' +
                HELPER.TOSTR(m.paramTypesJOW, 17) + '\n'));

    }
}