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
package Torello.JDUInternal.Annotations.EntityAnnotations.Processor;

import Torello.Java.StrSource;
import Torello.Java.StringParse;

import Torello.Java.Additional.EffectivelyFinal;

import Torello.JavaDoc.LinkJavaSource;
import Torello.JavaDoc.hidden.LJSRepeatable;

import Torello.JDUInternal.Annotations.HELPER;

import javax.annotation.processing.Messager;

import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;

import javax.tools.Diagnostic;

import java.util.List;

// @LinkJavaSource Annotation:
//
// EXPORTS:
// 
//     public String    handle();
//     public String    typeName()      default "";
//     public Entity    entity();
//     public String    name();
//     public byte      paramCount()    default -1;
//     public String[]  paramNames()    default { };
//     public String[]  paramTypesJOW() default { };
// 
// 
// The following files are relatedf to this Annotation-Mirror Data-Class:
// 
// * Torello.JavaDoc.LinkJavaSource
//      For the actual annotation definition.
//      This is the actual @interface for the @LinkJavaSource Annotation
// 
// * package Torello.JDUInternal.Features.LINK_JAVA_SOURCE
//      This package does the "vast majority" of the work that is needed process a User's
//      Annotation-Placement.  The classes in the packages in this class look for, and load, all 
//      of the External '.java'-Files which have been specified by the programmer's annotation
//      uses.  These classes also perform the Source-Code HiLiting, and save the output to the
//      appropriate packge's '[pkg-javadoc]/ljs-hilite-files/' directory.  They finally, also,
//      generate the appropriate "HREF=..." so that an appropriate '<A HREF...>' link may be
//      inserted
// 
// * Torello.JDUInternal.Annotations.EntityAnnotations.Mirror.LJSMirror
//      The Data-Contents of Annotation that has been placed by the User on the Entity
//      (Entity: Method, Field, Constructor, Enum-Constant, Annotation-Element)
// 
// * Torello.JDUInternal.Annotations.EntityAnnotations.Processor.LinkJSourceProcessor
//      The Annotation-Processor that is invoked by 'javac' when compiling a class that uses the
//      @LinkJavaSource Annotation.  

public class LinkJSourceProcessor
{
    // This is used by the Error-Message Printer
    private static final String LJS_NAME = LinkJavaSource.class.getSimpleName();

    public static boolean process(
            Element             ciet,
            AnnotationMirror    am,
            javax.annotation.processing.Messager messager
        )
    {
        EffectivelyFinal<Boolean> errors = new EffectivelyFinal<>(false);

        EffectivelyFinal<Boolean>
            name            = new EffectivelyFinal<>(false),
            paramNames      = new EffectivelyFinal<>(false),
            paramTypesJOW   = new EffectivelyFinal<>(false),
            paramCount      = new EffectivelyFinal<>(false);

        am.getElementValues().forEach((ExecutableElement ee, AnnotationValue av) ->
        {
            // Understanding the 'terminology' in Annotations and Annotation-Processor's is half of
            // the work in using it.

            final Object val  = av.getValue();

            switch (ee.toString())
            {
                case "handle()" :
                    errors.f |= checkIdentifierAE(messager, ciet, "handle()", (String) val);
                    break;

                case "typeName()" :
                    errors.f |= checkTypeStr(messager, ciet, (String) val);
                    break;

                case "entity()" :
                    break;

                case "name()" :
                    name.f = true;
                    errors.f |= checkIdentifierAE(messager, ciet, "name()", (String) val);
                    break;

                case "paramCount()":
                    paramCount.f = true;
                    errors.f |= checkParamCount(messager, ciet, (Byte) val);
                    break;

                case "paramNames()":
                    paramNames.f = true;
                    errors.f |= checkParamList(messager, ciet, "paramNames()", (List) val);
                    break;

                case "paramTypesJOW()":
                    paramTypesJOW.f = true;
                    errors.f |= checkParamList(messager, ciet, "paramTypesJOW()", (List) val);
                    break;

                default: 

                    // This should be UNREACHABLE-CODE.  The Java-Compiler, itself, should do the
                    // complaining that the user supplied an Annotation-Element name that is not
                    // among those listed above.  Unless 'javac' changes, this cannot execute.

                    throw new InternalError(
                        "There was an annotation parameter whose name wasn't recognized: " +
                        ee.toString() + "\n" +
                        "The only parameter's that may be passed to @LinkJavaSource:\n" +
                        "'handle()', 'typeName()', 'entity()', 'name()', 'paramCount()', " +
                        "'paramNames()' and 'paramTypesJOW()'"
                    );
            }
        });        

        return errors.f;
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Checks both the "name()" and "handle()" Annotation-Elements
    // ********************************************************************************************
    // ********************************************************************************************


    private static boolean checkIdentifierAE(
            javax.annotation.processing.Messager messager,
            Element ciet,
            String  aeName,
            String  aeValAsStr
        )
    {
        if (StrSource.isValidJavaIdentifier(aeValAsStr)) return true;

        // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
        messager.printMessage(
            Diagnostic.Kind.ERROR,
            HELPER.LOCATION(ciet, LJS_NAME) +
            "\tString Annotation-Parameter '" + aeName + "' was passed " +
                "[" + aeValAsStr + "]\n" +
            "\tHowever, this is not a valid Java-Identifier."
        );

        return false;
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Checks the "typeName()" Annotation-Elements
    // ********************************************************************************************
    // ********************************************************************************************


    private static boolean checkTypeStr
        (javax.annotation.processing.Messager messager, Element ciet, String aeValAsStr)
    {
        if (! StrSource.isJavaTypeStr(aeValAsStr))
        {
            messager.printMessage(
                Diagnostic.Kind.ERROR,
                HELPER.LOCATION(ciet, LJS_NAME) +

                // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
                "\tString Annotation-Parameter 'typeName()' was passed " +
                    "[" + aeValAsStr + "]\n" +
                "\tHowever, this is not a valid Java Type-Name."
            );

            return false;
        }

        return true;
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Check "paramCount()"
    // ********************************************************************************************
    // ********************************************************************************************


    private static boolean checkParamCount
        (javax.annotation.processing.Messager messager, Element ciet, byte paramCount)
    {
        if (paramCount < 0)
        {
            // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
            messager.printMessage(
                Diagnostic.Kind.ERROR,
                HELPER.LOCATION(ciet, LJS_NAME) +
                "\t'paramCount' was passed a negative number: [" + paramCount + "]"
            );

            return false;
        }

        return true;
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Check the "paramNames()" and "paramTypes()" Annotation-Elements
    // ********************************************************************************************
    // ********************************************************************************************


    @SuppressWarnings("rawtypes")
    private static boolean checkParamList(
            javax.annotation.processing.Messager messager,
            Element ciet,
            String  aeName,
            List    aeValList
        )
    {
        String aeValAsStr;

        for (Object val : aeValList)

            if (! StrSource.isValidJavaIdentifier
                (aeValAsStr = StringParse.ifQuotesStripQuotes(val.toString()))
            )
            {
                messager.printMessage(
                    Diagnostic.Kind.ERROR,
                    HELPER.LOCATION(ciet, LJS_NAME) +

                    // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
                    "\tString Annotation-Parameter '" + aeName + "' was passed " +
                        "String[]-Array Element [" + aeValAsStr + "]\n" +
                    "\tHowever, this is not a valid Java-Identifier."
                );

                return false;
            }

        return true;
    }
}