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}