001package Torello.JavaDoc.Annotations;
002
003import Torello.Java.Additional.Counter;
004import Torello.Java.Additional.EffectivelyFinal;
005
006import Torello.Java.StringParse;
007import Torello.Java.StrCSV;
008
009import java.util.Vector;
010import java.util.Set;
011import java.util.List;
012import java.util.Iterator;
013
014import javax.lang.model.element.Modifier;
015import javax.lang.model.element.ElementKind;
016
017import javax.lang.model.element.Element;
018import javax.lang.model.element.ExecutableElement;
019import javax.lang.model.element.AnnotationMirror;
020import javax.lang.model.element.AnnotationValue;
021
022import javax.tools.Diagnostic;
023import javax.lang.model.util.SimpleElementVisitor8;
024
025// EXPORTS:
026//      public Excuse[] excuses() default {};
027//      public String[] excused() default {};
028// 
029// Torello.JDUInternal.SimpleFeatures.StatelessClasses
030//      This class is the "Work-Horse" for the Static-Function JDU-API User-Annotation.  This class
031//      does the actual work of inserting an HTML-Message into a Java-Doc '.html'-File which 
032//      explains that the User wants to inform his/her user's that a particular class/type does not
033//      maintain any state.
034
035
036@Torello.JavaDoc.Annotations.JDHeaderBackgroundImg(EmbedTagFileID="ANNOT_PROC_JDHBI")
037public class SFProcessor
038{
039    private SFProcessor() { }
040
041    // This is used by the Error-Message Printer
042    private static final String SF_NAME = StaticFunctional.class.getSimpleName();
043
044    // Some new "Multi-Threaded Java-Compiler" might break this class...
045    // If there were multiple messagers in this imaginary-new multi-threaded compiler...
046    // global-static non-final variable...
047
048    private static javax.annotation.processing.Messager messager = null;
049
050
051    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
052    // Original Version used Reg-Ex's to parse Annotation-Elements.  These are all "Legacy" now...
053    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
054    //
055    // Getting to know These Classes, and how to avoid importing them is what needed to happen.
056    //      com.sun.tools.javac.code.Attribute$Constant
057    //      com.sun.tools.javac.util.List
058    //
059    // When you ask for the Annotation-Element's, the above classes is what you will receive.
060    // You have to take the good with the bad.  These classes aren't "that difficult" to deal with.
061    // Since everything involving annotations is really ugly code, it needs "profuse commenting".
062    // This was, for me, the straw that will break the back.
063
064
065    /**
066     * This implements the processing for the Annotation @{@link StaticFunctional}.  It does a lot
067     * of validity checks on the parameter input.
068     */
069    static boolean process
070        (Element ciet, AnnotationMirror am, javax.annotation.processing.Messager messager)
071    {
072        SFProcessor.messager = messager;
073
074        boolean errors = false;
075
076        ElementKind k = ciet.getKind();
077
078        // @StaticFunctional may not be placed on an CIET.ENUM, nor a CIET.ANNOTATION nor a
079        // CIET.RECORD.  In the actual annotation '.java' file the @StaticFunctional "thingy"
080        // (where 'thingy' means StaticFunction.java - the definition, not the mirror nor the
081        // the processor)... The file 'StaticFunctional.java' ... itself, has an annotation placed
082        // on it that says:  @Target(ElementType.TYPE).  This means the javac compiler will allows
083        // any '.java' file to have the @StaticFunctional Annotation placed on it.
084        //
085        // However, this annotation is only intended for Java-Classes and Java-Interfaces - and it
086        // says so right there in the docs for the @StaticFunctional Annotation.  So if the
087        // Annotation-Mirror does not report that this Annotation was placed on a CIET.CLASS or
088        // a CIET.INTERFACE (but rather an enum, record or other annotation) - then tell the user
089        // this is an error.
090        //
091        // Again, this error message is printed if the ElementKind is an ENUM, ANNOTATION or RECORD
092
093        if ((k != ElementKind.CLASS) && (k != ElementKind.INTERFACE))
094        {
095            // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
096            messager.printMessage(
097                Diagnostic.Kind.ERROR,     
098                HELPER.LOCATION(ciet, SF_NAME) +
099                "\tOnly classes and interfaces can be annotated with @StaticFunctional\n" +
100                ciet.toString() + " is neither of these, it is of kind: " + k.toString()
101            );
102
103            return true; // return errors = true;
104        }
105
106        // The "Excused()" String[]-Array that was passed by the user lists the names of the
107        // fields that have been excused.  This is needed information during the processing-stage.
108        // These will be saved inside a Vector.
109
110        Vector<String> excused = new Vector<>();
111
112        // The "Excuses()" Excuse[]-Array that the user may or may not pass are not needed right
113        // now.  The JavaDoc Upgrader will use them when it upgrades the Java-Doc Web-Pages.  
114        // Instead, all that is needed is the length of the array.  These two Annotation-Elements
115        // (here called 'Members') must be parallel arrays with the same length; and if they are
116        // not an error-message must be printed.
117        //
118        // NOTE: How cool is the "EffectivelyFinal" class from Torello.Java.Additional?
119        //       (The 'for-each' loop body below is actually a "lambda-expression", and therefore
120        //       requires that any variables which are used from outside the lambda-expression to
121        //       be declared final or effectively-final)
122
123        EffectivelyFinal<Integer> excusesCount = new EffectivelyFinal<>(0);
124
125        am.getElementValues().forEach((ExecutableElement ee, AnnotationValue av) ->
126        {
127            // In the JavaDoc Web-Page Summaries Section, a "thingy" inside of an Annotation is
128            // referred to as an Annotation-Element.  There are *BOTH* Optional *AND* Required
129            // Annotation-Elements.  (In the JavaDoc-Upgrader these Elements are actually called
130            // "Entities", and there is an enum-constant called Entity.ANNOTATION_ELEM)
131            // 
132            // In this part of "Java Annotations" (the actual Annotation-Processors), these are
133            // referred to as "Members" - not "Elements", but they are the same thing.  These are
134            // just the values that the user may provide, if he or she so chooses, when placing an
135            // Annotation on a type/CIET.
136            //
137            // In the @StaticFunctional Annotation, the two "Member Names" are "Excuses" and
138            // "Excused".  Both of their Values are Arrays... (One String[]-Array and one
139            // Excuse[]-Array)
140
141            String memberName   = ee.toString();
142            Object memberValue  = av.getValue();
143
144
145            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
146            // AND HERE-IN LIES THE TRUE-MADNESS.
147            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
148            //
149            // "av.getValue()" returns an instance of: com.sun.tools.javac.util.List
150            // And I have never heard of such a thing...
151            //
152            // Java Assertion.  This Really **SHOULD** Hold, but just in case....  There will be
153            // a nice, warm & friendly reminder of how preposterous Java Annotation's really are.
154            // They really could have done more work to 'sort-of' put-it all together.  The classes
155            // we are supposed to use to actually do Annotation-Processing seem to be distributed
156            // among HALF-A-DOZEN different Java-Packages...
157            //
158            // java.lang.annotation, javax.annotation.processing, javax.lang.model,
159            // javax.lang.model.element, javax.tools, javax.lang.model.util,
160            // 
161            // AND-EVEN: com.sun.tools.javac.util.List  ... Which is not even in the JDK!!            
162
163            if (! java.util.List.class.isAssignableFrom(memberValue.getClass()))
164
165                // Don't use the Messager, this is a "Java-Error" (or else I'm just not quite
166                // understanding Annotations-Processors yet)
167
168                throw new InternalError(
169                    memberName + " Annotation-Element-Value Type is not a java.util.List, " +
170                    '[' + memberValue.getClass().getName() + ']'
171                );
172
173
174            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
175            // Check if the "Entity/Member" is the Excused-Field Names List (A String[]-Array)
176            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
177            //
178            // Retrieve the names of the Excused Fields (they are String's inside of a
179            // String[]-Array).  Save them to the Vector<String> that is/was declared at the very
180            // top of this method.
181
182            else if (memberName.equals("Excused()"))
183
184                for (Object fieldName : (List) memberValue)
185
186                    // The 'toString()' actually does include the quotation-marks in Java 11
187                    // But in Java 17, the quotation-marks are gone!  The 'ifQuotesStripQuotes'
188                    // method removes surrounding quotation-marks only if they are present.
189
190                    excused.add(StringParse.ifQuotesStripQuotes(fieldName.toString()));
191
192
193            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
194            // Check if the "Entity/Member" is the Excuses List (An enum Excuse[]-Array)
195            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
196            //
197            // Just retrieve how many Excuses there are, it is not important right now what the
198            // excuses actually are, so long as the number provided is the same as the number of
199            // fields for the 'Excused[]' Array.
200
201            else if (memberName.equals("Excuses()"))
202                excusesCount.f = ((List) memberValue).size();
203
204
205            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
206            // Unreachable Code.  javac will not compile extranneous Annotation-Elems
207            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
208            //
209            // javac should flag this error before it ever reaches the Annotation-Processor.  Just
210            // in case I missed something, throw an "InternalError" (don't use the messager)
211            // 
212            // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
213
214            else throw new InternalError(
215                "\tThere was an annotation parameter whose name wasn't recognized: " +
216                memberName.toString() + "\n" +
217                "\tThe only parameter's that may be passed to @StaticFunctional " +
218                    "are 'Excuses' and 'Excused'\n"
219            );
220        });
221
222
223        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
224        // Do the "Parallel Array" Check, first.
225        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
226        //
227        // This first checks that the same number of excuses and excused-fields were provided
228        // (if any).  The "Excuses()" and "Excused()" Annotation-Elements (Entities) are supposed
229        // to be parallel-arrays, whose lengths are equal.  If they are not, print an error
230        // messsage.
231
232        if (excusesCount.f != excused.size())
233        {
234            // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
235            messager.printMessage(
236                Diagnostic.Kind.ERROR,     
237                HELPER.LOCATION(ciet, SF_NAME) +
238                "\tYou have passed " + excused.size() + " excused field name" +
239                ((excused.size() == 1) ? "" : "s") + ", " +
240                "and " + excusesCount.f + " explanation" +
241                ((excusesCount.f == 1) ? "" : "s") + " for " +
242                ((excused.size() == 1) ? "that field" : "those fields") + ".\n" +
243                "\tThe two optional array-parameters to the @StaticFunctional annotation must " +
244                "be parallel-arrays, and when used, their lengths must be equal."
245            );
246
247            errors = true;
248        }
249
250
251        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
252        // Check, first, whether or not "Excused Field Names" are all valid Java Identifiers
253        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
254        //
255        // This whole iterator-thing is needed to prevent multiple error messages about the same
256        // mistaken field-name.  If a name is not a valid Java-Identifier, then an error message is
257        // printed here.  There should be no reason to print a second error message later 
258        // mentioning that that field name could not be found...
259        //
260        // Therefore, when not a valid Java-Identifier, the name of the Field should just be
261        // removed, immediately, right here.  (The Iterator.remove() method removes it from the
262        // underlying Vector<String>).
263        //
264        // NOTE: if it the field-name is not a valid Java-Identifier, there is no way it will be
265        //       found as a "Member" of the Class or Interface...  And a second error message would
266        //       be printed saying that the field-name wasn't found in the class (avoid this, by
267        //       removing the field-name from the Vector)
268
269        Iterator<String> excusedIter = excused.iterator();
270
271        while (excusedIter.hasNext())
272
273            if (! checkJavaIdentifier(ciet, excusedIter.next()))
274            {
275                errors = true;
276                excusedIter.remove();
277            }
278
279
280        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
281        // NOW: Iterate all the "Entities" (which Java calls "Elements") inside this CIET/Type
282        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
283        //
284        // At this point, there may have already been errors.  The field names could be invalid
285        // Java Identifiers and the arrays might not have been parallel.  However, there isn't
286        // a reason to stop to printing more error messages.  Getting as many of these messages
287        // out as possible is faster for the programmer.
288        //
289        // IMPORTANT: Any "additional" information passed to @StaticFunctional through the
290        //            "Annotation-Elements" (Excused & Excuses), **CAN ONLY BE** for **FIELDS**
291        //
292        // This loop checks constructors, methods, and fields, and for the above mentioned reason,
293        // this should should continue rather than quitting now - only to have a developer find
294        // more errors the next time he tries to compile.
295
296        int constructorCount = 0;
297
298        // Again, here an "Element" is the "stuff" declared (at the 'top-level' so-to-speak) inside
299        // of a Type (Class or Interface).  For the second time, I will repeat that in this JD
300        // Upgrader Package, I actually call these "Entities" rather than "Elements", and the
301        // "Entity" enum has FIELD'S, METHOD'S, CONSTRUCTOR'S, ANNOTATION_ELEM'S, ENUM_CONSTANT'S
302        // and NESTED_TYPE'S
303        //
304        // Since this Type could only possibly be a Class or Interface, that rules out -
305        // immediately, iterating ANNOTATION_ELEM's and ENUM_CONSTANTS...  (And Inner-Classes are
306        // just irrelevant to the @StaticFunctional Annotation).  We only iterate Methods, Fields
307        // and Constructors.
308    
309        for (Element e : ciet.getEnclosedElements())
310        {
311            // The Modifers are the words: public, private, protected, static, final, transient,
312            // volatile etc...
313
314            Set<Modifier> modifiers = e.getModifiers();
315
316            switch (e.getKind())
317            {
318                // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
319                // Inspects a Method, and makes sure it has been declared static
320                // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
321
322                case METHOD :
323
324                    // If it is not 'static' - print an the method 'modifierError' will print an
325                    // error-message to the messager.  Make sure to update the 'errors' boolean
326                    // flag.
327
328                    if (! modifiers.contains(Modifier.STATIC))
329
330                        errors |= modifierError(ElementKind.METHOD, Modifier.STATIC, ciet, e);
331
332                    break;
333
334
335                // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
336                // Inspects a field, and ensure that it is both static & final -- or excused.
337                // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
338
339                case FIELD :
340
341                    boolean isExcused = excused.contains(e.getSimpleName().toString());
342
343                    // NOTE: non-static fields **CANNOT** be excused, only non-final fields
344                    if (! modifiers.contains(Modifier.STATIC))
345
346                        errors |= modifierError(ElementKind.FIELD, Modifier.STATIC, ciet, e);
347
348                    // If the field doesn't have the 'final' modifier, and it isn't excused, this
349                    // is an error, and this is "kind-of" the **CORE** of @StaticFunctional
350                    // This line right here, below, was (sort-of) the WHOLE-POINT of the Annotation
351                    //
352                    // ALSO: Making sure that all methods are declared static is, too, the
353                    //       'WHOLE-POINT' (previous switch-case)
354
355                    if (! modifiers.contains(Modifier.FINAL))
356                    {
357                        if (! isExcused)
358                            errors |= fieldIsNotFinalButIsAlsoNotExcused(ciet, e);
359                    }
360
361                    // NOTE: This is a WARNING, not an ERROR.  This happens if they passed a field
362                    //       name to "Excused()", that is already declared 'final' (so it doesn't
363                    //       need to be listed as 'excused')
364                    // 
365                    // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
366
367                    else if (isExcused) messager.printMessage(
368                        Diagnostic.Kind.WARNING,     
369                        HELPER.LOCATION(ciet, SF_NAME) +
370                        "\tField [" + e.getSimpleName().toString() + "] is declared excused; but" +
371                        "this field is declared final, and therefore does not need to be excused."
372                    );
373
374                    // Remove this from the list... There is another error-check after this
375                    // 'forEach' loop ends that checks if the user provided any field-names that
376                    // weren't found as members / entities of the class.  That check is done by
377                    // inspecting the 'excused' list (and if it isn't completely empty), then it
378                    // prints the error-message that the field wasn't found.
379
380                    if (isExcused) excused.remove(e.getSimpleName().toString());
381
382                    break;
383
384
385                // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
386                // Checks a constructor, and make sure it has zero-arguments, and is private
387                // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
388
389                case CONSTRUCTOR :
390
391                    constructorCount++;
392
393                    // NOTE: The static inner-class / nested-type "ZeeroArgConstructor" has a big
394                    //       explanation for why it is needed - and this needs to be a separate
395                    //       inner-class rather than a simple-check that is done right here inside
396                    //       the switch-statement.
397                    //
398                    // The check requires counting the number of parameters to the constructor, but
399                    // that information is completely-unavailable without using this "visitor".
400                    //
401                    // The method 'Element.accept(Visitor())' walks the Parse-Tree, and sends all
402                    // of the Elements in the Parse-Tree to the Visitor-Handler's, User-Defined,
403                    // Visit-Methods.  See the inner-class (defined below) for 'ZeroArgConstructor'
404                    // to view the handler of a constructor-definition.
405                    //
406                    // NOTE: Building a 'Visitor' from an 'Adapter' in order to walk a Parse-Tree
407                    //       using a 'walk' method (in this case, an 'accept' method) is the exact
408                    //       same thing that the Java-Parser library does vis-a-vis AST Parse-Trees
409                    //       and Tree-Walks with it's Visitor's and Visitor-Adapter classes.  Here
410                    //       we are using the java.lang.model stuff-ola, instead.
411
412                    if (e.accept(new ZeroArgContructor(messager, ciet), null)) errors = true;
413
414                    // This 'if-branch' can only execute if this particular constructor has
415                    // zero-args, and, yet, has not been declared private.  This is important for
416                    // the "sinister" reason that the Java Auto-Generated Zero-Arg Constructor is
417                    // **USUALLY** never actually something that the programmer typed into his 
418                    // '.java' file.  
419                    //
420                    // This Error Messages reminds them of this tidbit of information... (auto
421                    // added constructors that are placed into a class by 'javac' - The Java
422                    // Compiler)
423
424                    else if (! modifiers.contains(Modifier.PRIVATE))
425
426                        errors |= modifierError
427                            (ElementKind.CONSTRUCTOR, Modifier.PRIVATE, ciet, e);
428
429                    break;
430
431                default: break;
432
433            }   // switch-statement: of the "Elements" inside this CIET/Type
434                // which in JavaDoc-Upgrader-Speak are unfortunately called "Entities" instead
435
436        } // For-Loop: Iterating the "Elements" of this CIET/Type
437
438
439        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
440        // This make sure there aren't any 'extranneous' excused field-names
441        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
442        //
443        // NOTE: In the loop above, whenever an "Element" (Java-Doc-Upgrader-Speak "Entities") is
444        //       found by the switch-statement, above, it is removed from the Vector<String> that
445        //       lists all of the "Excused" Fields (which aren't required to be 'final')
446        //
447        // So... If there are still field-names listed in the 'excused' list, then they are still
448        // there because they were never found in the above for-loop-switch-statement, and that 
449        // would be because they aren't actually fields inside the TYPE/CIET.
450
451        if (excused.size() > 0)
452        {
453            // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
454            messager.printMessage(
455                Diagnostic.Kind.ERROR,     
456                HELPER.LOCATION(ciet, SF_NAME) +
457                "\tThere were fields listed as excused and non-final, but those fields were not " +
458                "actually members of the " + ciet.getKind().toString().toLowerCase() + "." +
459                "\n\tUn-Resolved Excused Feild-Names: " +
460                StrCSV.toCSV(excused, s -> "[" + s + "]", false, null)
461            );
462
463            errors = true;
464        }
465
466
467        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
468        // Make sure there is only 1 constructor. 
469        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
470        //
471        // This test is a little SUPERFLUOUS, otherwise one of the constructors would not have had
472        // zero-arguments, and therefore would have been flagged (and mentioned) in the previous
473        // loop that iterates all of the CIET/Type "Elements" ("Entities").  It  is a little
474        // friendly reminder to myself (and them, I guess) - no more no less.
475
476        if (constructorCount > 1)
477        {
478            // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
479            messager.printMessage(
480                Diagnostic.Kind.ERROR,     
481                HELPER.LOCATION(ciet, SF_NAME) +
482                "\tThere are " + constructorCount + " constructors.  There must be precisely " +
483                "one private, zero-argument, constructor."
484            );
485
486            errors = true;
487        }
488
489        return errors;
490    }
491
492
493    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
494    // This implements a "Visitor" for a Constructor
495    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
496    //
497    // The only purpose of this class is to check that any constructors which are found, indeed
498    // have precisely zero-arguments.  The primary need to make this a class is that it extends
499    // the "SimpleElementVisitor8", which has a method that accept the "ExecutableElement."
500    //
501    // Having access to the "ExecutableElement" is actually the **ONLY WAY** to get the **NUMBER**
502    // of parameters passed to a constructor.
503    //
504    // In the for-loop (above) that iterates the "Elements" ("Entities" - in JDU-Speak), none of 
505    // the variables there actually have the ability to retrieve this information...  Again... this
506    // "Visitor" Nested-Class is **ONLY** used on Elements which are Constructors.
507    //
508    // This is the "Inheritance Tree" for the Visitor:
509    //  java.lang.Object
510    //      javax.lang.model.util.AbstractElementVisitor6<R,P>
511    //          javax.lang.model.util.SimpleElementVisitor6<R,P>
512    //              javax.lang.model.util.SimpleElementVisitor7<R,P>
513    //                  javax.lang.model.util.SimpleElementVisitor8<R,P>
514
515    private static class ZeroArgContructor extends SimpleElementVisitor8<Boolean, Void>
516    {
517        private final javax.annotation.processing.Messager messager;
518        private final Element ciet;
519
520        ZeroArgContructor(javax.annotation.processing.Messager messager, Element ciet)
521        { this.messager=messager;  this.ciet=ciet; }
522
523        // This javax/lang/model/element/ElementVisitor has quite a few methods for visiting 
524        // "stuff."  This is just one of several of them.  Again, this is only called when a
525        // contructor is found, but it is also used on methods.  The "ExecutableElement" is the
526        // parameter that makes this whole inner-class / Nested-Type useful-necessary.
527
528        public Boolean visitExecutable(ExecutableElement exEl, Void v)
529        {
530            int paramCount = exEl.getParameters().size();
531
532            if (paramCount > 0)
533            {
534                // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
535                messager.printMessage(
536                    Diagnostic.Kind.ERROR,     
537                    HELPER.LOCATION(ciet, SF_NAME) +
538                    "\tThere is a constructor with " + paramCount + " parameter" +
539                    ((paramCount == 1) ? "" : "s") + ".  " +
540                    "There must be precisely one private, zero-argument, constructor."
541                );
542
543                return true;
544            }
545
546            return false;
547        }
548    }
549
550
551    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
552    // Checks that only valid Java Variable-Names are passed to the "Excused" Array-Parameter
553    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
554
555    private static boolean checkJavaIdentifier(Element ciet, String fieldName)
556    {
557        // System.out.println(fieldName);
558
559        if (fieldName.length() == 0)
560        {
561            // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
562            messager.printMessage(
563                Diagnostic.Kind.ERROR,
564                HELPER.LOCATION(ciet, SF_NAME) +
565                "\tString[]-Array Annotation-Parameter 'Excused' contains at least one " +
566                "Zero-Length-String Element."
567            );
568
569            return false;
570        }
571
572        boolean passedTheTest = Character.isJavaIdentifierStart(fieldName.charAt(0));
573
574        for (int i=1; passedTheTest && (i < fieldName.length()); i++)
575            passedTheTest &= Character.isJavaIdentifierPart(fieldName.charAt(i));
576
577        // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
578        if (! passedTheTest) messager.printMessage(
579            Diagnostic.Kind.ERROR,
580            HELPER.LOCATION(ciet, SF_NAME) +
581            "\tString[]-Array Paramter 'Excused()' indicates that a field named "+
582                '[' + fieldName + "] is excused.\n" +
583            "\tThis is not a valid Java Identifier, and therefore could not be a field."
584        );
585
586        return passedTheTest;
587    }
588
589
590    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
591    // Error Message for problems with the modifiers
592    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
593    //
594    // Used on 'Constructors' if they don't have the 'private' modifier
595    // Used on 'Fields' if they don't have the 'static' modifier
596    // Used on 'Methods' if they don't have the 'static' modifier
597    //
598    // NOTE: There is no such thing as a 'static' contructor (hopefully that is obvious).
599    //       Fields which lack the 'final' modifier, need a specialized error message. (below)
600
601    private static boolean modifierError(ElementKind k, Modifier m, Element ciet, Element fcm)
602    {
603        if (k == ElementKind.CONSTRUCTOR)
604
605            // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
606            messager.printMessage(
607                Diagnostic.Kind.ERROR,     
608                HELPER.LOCATION(ciet, SF_NAME) +
609                "\tThere is a constructor which was not declared private.  " +
610                "Is it Java's zero-argument, auto-generated (synthetic) constructor?"
611            );
612
613        // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
614        else messager.printMessage(
615            Diagnostic.Kind.ERROR,     
616            HELPER.LOCATION(ciet, SF_NAME) +
617            "\tThere is a " + k.toString().toLowerCase() + " named " +
618            '[' + fcm.getSimpleName().toString() + "] " +
619            "which was not declared " + m.toString()
620        );
621
622        return true;
623    }
624
625
626    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
627    // Saving the best for last...
628    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
629    //
630    // The primary point about this whole annotation, is it (kind-of) helps prevent "Spaghetti
631    // Code".  When used, it is intended to mean that a "Class" or "Type" (CIET) **DOES NOT** have
632    // any "Global Variables"  (That's really the whole shebang).  If there are declared fields,
633    // they must be declared as constants using the 'final' modifier.
634
635    private static boolean fieldIsNotFinalButIsAlsoNotExcused(Element ciet, Element fcm)
636    {
637        // NOTE: Torello.JDUInternal.Messager **DOES NOT** ALLOW '\t'
638        messager.printMessage(
639            Diagnostic.Kind.ERROR,     
640            HELPER.LOCATION(ciet, SF_NAME) +
641            "\tThere is a field named [" + fcm.getSimpleName().toString() + "] " +
642            "which was not declared final, nor was it listed excused."
643        );
644
645        return true;
646    }
647}