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}