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}