001package Torello.JavaDoc.Annotations; 002 003import Torello.Java.StrCSV; 004import Torello.Java.StrPrint; 005 006import Torello.Java.ReadOnly.ReadOnlyList; 007import Torello.Java.ReadOnly.ReadOnlyArrayList; 008import Torello.Java.ReadOnly.ROArrayListBuilder; 009 010import Torello.JavaDoc.Messager.Messager; 011import Torello.JavaDoc.Messager.MsgVerbose; 012 013import Torello.JDUInternal.Miscellaneous.Where.JDUAnnotations; 014 015import com.sun.source.tree.ExpressionTree; 016import com.sun.source.tree.AssignmentTree; 017import com.sun.source.tree.MemberSelectTree; 018import com.sun.source.tree.IdentifierTree; 019import com.sun.source.tree.NewArrayTree; 020 021import static com.sun.source.tree.Tree.Kind.MEMBER_SELECT; 022import static com.sun.source.tree.Tree.Kind.IDENTIFIER; 023import static com.sun.source.tree.Tree.Kind.NEW_ARRAY; 024import static com.sun.source.tree.Tree.Kind.ASSIGNMENT; 025 026import java.util.List; 027 028// EXPORTS: 029// public Excuse[] excuses() default {}; 030// public String[] excused() default {}; 031// 032// Torello.JDUInternal.SimpleFeatures.StatelessClasses 033// This class is the "Work-Horse" for the Static-Function JDU-API User-Annotation. This class 034// does the actual work of inserting an HTML-Message into a Java-Doc '.html'-File which 035// explains that the User wants to inform his/her user's that a particular class/type does not 036// maintain any state. 037 038/** 039 * An internally used data-record class used for storing all user-supplied annotation data 040 * associated with a single use / application of the {@link StaticFunctional} annotation. 041 * 042 * @see Torello.JavaDoc.Annotations.StaticFunctional 043 */ 044@Torello.JavaDoc.Annotations.JDHeaderBackgroundImg(EmbedTagFileID="MIRROR_JDHBI") 045public class SFMirror implements AnnotationMirror 046{ 047 // ******************************************************************************************** 048 // ******************************************************************************************** 049 // Public, Final, ReadOnly Mirrored, Fields 050 // ******************************************************************************************** 051 // ******************************************************************************************** 052 053 054 /** Holds data extracted from {@link StaticFunctional#Excuses()} */ 055 public final ReadOnlyList<StaticFunctional.Excuse> excuses; 056 057 /** Holds data extracted from {@link StaticFunctional#Excused()} */ 058 public final ReadOnlyList<String> excused; 059 060 061 // ******************************************************************************************** 062 // ******************************************************************************************** 063 // interface AnnotationMirror 064 // ******************************************************************************************** 065 // ******************************************************************************************** 066 067 068 // Stores the actual text of the @StaticFunctional annotation which was placed. 069 // This is nothing more than the exact line that was originally inserted into the class. 070 // The only reason this is even needed (in case you are wondering) is because the error 071 // checking & error printing code outputs this text when printing an error message. 072 073 private final String annotationAsStr; 074 075 /** {@inheritDoc} */ @Override 076 public String getAnnotationAsString() { return annotationAsStr; } 077 078 /** {@inheritDoc} */ @Override 079 public String getAnnotationName() { return "StaticFunctional"; } 080 081 082 // ******************************************************************************************** 083 // ******************************************************************************************** 084 // Package-Private Constructor, Invoked by class "TypeAnnotationMirrors" 085 // ******************************************************************************************** 086 // ******************************************************************************************** 087 088 089 SFMirror( 090 final List<? extends ExpressionTree> arguments, 091 final String annotationAsStr 092 ) 093 { 094 this.annotationAsStr = annotationAsStr; 095 096 ReadOnlyList<StaticFunctional.Excuse> excuses = ReadOnlyArrayList.emptyROAL(); 097 ReadOnlyList<String> excused = ReadOnlyArrayList.emptyROAL(); 098 099 for (ExpressionTree argument : arguments) 100 { 101 // System.out.println(argument); 102 103 if (argument.getKind() != ASSIGNMENT) Messager.assertFail( 104 "The ExpressionTree returned by the @StaticFunctional arguments list was not of " + 105 "type ASSIGNMENT, but rather " + argument.getKind() + '\n' + 106 "ExpressionTree.toString(): " + argument.toString() + '\n' + 107 "All Annotation Expressions: " + StrCSV.toCSV(arguments, true, true, null), 108 null, 109 JDUAnnotations.SFMirror 110 ); 111 112 String elemName = ((AssignmentTree) argument).getVariable().toString(); 113 ExpressionTree elemValue = ((AssignmentTree) argument).getExpression(); 114 115 /* 116 System.out.println( 117 " elemName: " + elemName + '\n' + 118 " elemValue.toString(): " + elemValue.toString() + '\n' + 119 " elemValue.getKind(): " + elemValue.getKind().toString() + '\n' + 120 " elemValue.getClass(): " + elemValue.getClass().getSimpleName() + '\n' 121 ); 122 */ 123 124 if (elemName.equals("Excuses")) 125 excuses = handleExcuses(elemValue); 126 127 else if (elemName.equals("Excused")) 128 excused = HELPER.handleStringArray(elemValue, "Excused", JDUAnnotations.SFMirror); 129 130 else Messager.assertFail( 131 "There was an Expression Variable-Name that was neither 'Excuses' nor " + 132 "'Excused', but rather: " + elemName, 133 null, 134 JDUAnnotations.SFMirror 135 ); 136 } 137 138 if (MsgVerbose.isVerbose()) MsgVerbose.println( 139 "@StaticFunctional Annotation-Processing Mirror Class:\n" + 140 " excused: " + StrCSV.toCSV(excused, true, false, null) + '\n' + 141 " excuses: " + StrCSV.toCSV(excuses, true, false, null) 142 ); 143 144 this.excuses = excuses; 145 this.excused = excused; 146 } 147 148 149 // ******************************************************************************************** 150 // ******************************************************************************************** 151 // toString 152 // ******************************************************************************************** 153 // ******************************************************************************************** 154 155 156 /** {@inheritDoc} */ @Override 157 public String toString() 158 { 159 // public final ReadOnlyList<Excuse> excuses; 160 // public final ReadOnlyList<String> excused; 161 // 162 // " excuses: ".length() => 13 163 164 return 165 this.annotationAsStr + '\n' + 166 "{\n" + 167 " excuses: " + HELPER.TOSTR(this.excuses, 13) + '\n' + 168 " excused: " + HELPER.TOSTR(this.excused, 13) + '\n' + 169 "}"; 170 } 171 172 173 // ******************************************************************************************** 174 // ******************************************************************************************** 175 // Actual, Reflection-Based, Data Extraction Methods, Used & Invoked by the Constructor 176 // ******************************************************************************************** 177 // ******************************************************************************************** 178 179 180 // This is declared 'static' just to help keep in mind that this method doesn't actually access 181 // any of this class' field-data. This method parsed the 'ExpressionTree', and returns the 182 // completed / built ReadOnlyList of Excuses back to the method (directly above). Note the 183 // method above is not static. It assigs this list to this' instance field 'excuses' 184 185 private static ReadOnlyList<StaticFunctional.Excuse> handleExcuses(ExpressionTree t) 186 { 187 switch (t.getKind()) 188 { 189 case MEMBER_SELECT: return ReadOnlyList.of(getExcuse((MemberSelectTree) t)); 190 case IDENTIFIER: return ReadOnlyList.of(getExcuse((IdentifierTree) t)); 191 192 case NEW_ARRAY: 193 194 ROArrayListBuilder<StaticFunctional.Excuse> b = new ROArrayListBuilder<>(); 195 196 for (ExpressionTree excuse : ((NewArrayTree) t).getInitializers()) 197 198 switch (excuse.getKind()) 199 { 200 case MEMBER_SELECT: b.add(getExcuse((MemberSelectTree) excuse)); break; 201 case IDENTIFIER: b.add(getExcuse((IdentifierTree) excuse)); break; 202 203 default: Messager.assertFail( 204 "There is an initializer-array that contains the element: " + 205 excuse.toString() + "\n" + 206 "Unfortunately, this element's 'kind' value is neither " + 207 "MEMBER_SELECT nor IDENTIFIER, but rather [" + excuse.getKind() + "]", 208 null, 209 JDUAnnotations.SFMirror 210 ); 211 } 212 213 return b.build(); 214 215 default: throw Messager.assertFail( 216 "There is an initializer with a 'kind' value that is not MEMBER_SELECT nor " + 217 "IDENTIFIER nor NEW_ARRAY, but rather a [" + t.getKind() + "]", 218 null, 219 JDUAnnotations.SFMirror 220 ); 221 } 222 } 223 224 225 // This method implies that the user has said something like: Excuses=Excuse.FLAG 226 // Notice that the actual enum constant is after the enum-name 'Excuse' 227 // 228 // Also, this method is declared static, and is therefore just a helper 229 230 private static StaticFunctional.Excuse getExcuse(MemberSelectTree excuse) 231 { 232 try 233 { return StaticFunctional.Excuse.valueOf(excuse.getIdentifier().toString()); } 234 235 catch (Exception e) 236 { 237 throw Messager.assertFail( 238 e, 239 "Exception thrown while building an AnnotationsMirror. The 'Excuses' Array " + 240 "contained the following ExpressionTree: " + excuse.toString() + ", but that " + 241 "has failed to load to a constant of the enum Excuse.\n\n" + 242 "Did you actually compile your source-code with the Annotation-Processor " + 243 "enabled? If there were an error, it should have been caught at compile time.", 244 null, 245 JDUAnnotations.SFMirror 246 ); 247 } 248 } 249 250 251 // This method implies that the user has typed something like: Excuses=FLAG 252 // This is allowed as long as the following line is also within / inside their source-code 253 // ==> import static Torello.JavaDoc.Excuse.*; (A 'static important') 254 // 255 // Also, this method is declared static, and is therefore just a helper 256 257 private static StaticFunctional.Excuse getExcuse(IdentifierTree excuse) 258 { 259 try 260 { return StaticFunctional.Excuse.valueOf(excuse.getName().toString()); } 261 262 catch (Exception e) 263 { 264 throw Messager.assertFail( 265 e, 266 "Exception thrown while building an AnnotationsMirror. The 'Excuses' Array " + 267 "contained the following ExpressionTree: " + excuse.toString() + ", but that " + 268 "has failed to load to a constant of the enum Excuse.\n\n" + 269 "Did you actually compile your source-code with the Annotation-Processor " + 270 "enabled? If there were an error, it should have been caught at compile time.", 271 null, 272 JDUAnnotations.SFMirror 273 ); 274 } 275 } 276}