1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
package Torello.JavaDoc.Annotations;

import Torello.Java.StrCSV;
import Torello.Java.StrPrint;

import Torello.Java.ReadOnly.ReadOnlyList;
import Torello.Java.ReadOnly.ReadOnlyArrayList;
import Torello.Java.ReadOnly.ROArrayListBuilder;

import Torello.JavaDoc.Messager.Messager;
import Torello.JavaDoc.Messager.MsgVerbose;

import Torello.JDUInternal.Miscellaneous.Where.JDUAnnotations;

import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.NewArrayTree;

import static com.sun.source.tree.Tree.Kind.MEMBER_SELECT;
import static com.sun.source.tree.Tree.Kind.IDENTIFIER;
import static com.sun.source.tree.Tree.Kind.NEW_ARRAY;
import static com.sun.source.tree.Tree.Kind.ASSIGNMENT;

import java.util.List;

// EXPORTS:
//      public Excuse[] excuses() default {};
//      public String[] excused() default {};
// 
// Torello.JDUInternal.SimpleFeatures.StatelessClasses
//      This class is the "Work-Horse" for the Static-Function JDU-API User-Annotation.  This class
//      does the actual work of inserting an HTML-Message into a Java-Doc '.html'-File which 
//      explains that the User wants to inform his/her user's that a particular class/type does not
//      maintain any state.

/**
 * An internally used data-record class used for storing all user-supplied annotation data 
 * associated with a single use / application of the {@link StaticFunctional} annotation.
 * 
 * @see Torello.JavaDoc.Annotations.StaticFunctional
 */
@Torello.JavaDoc.Annotations.JDHeaderBackgroundImg(EmbedTagFileID="MIRROR_JDHBI")
public class SFMirror implements AnnotationMirror
{
    // ********************************************************************************************
    // ********************************************************************************************
    // Public, Final, ReadOnly Mirrored, Fields
    // ********************************************************************************************
    // ********************************************************************************************


    /** Holds data extracted from {@link StaticFunctional#Excuses()} */
    public final ReadOnlyList<StaticFunctional.Excuse> excuses;

    /** Holds data extracted from {@link StaticFunctional#Excused()} */
    public final ReadOnlyList<String> excused;


    // ********************************************************************************************
    // ********************************************************************************************
    // interface AnnotationMirror
    // ********************************************************************************************
    // ********************************************************************************************


    // Stores the actual text of the @StaticFunctional annotation which was placed.
    // This is nothing more than the exact line that was originally inserted into the class.
    // The only reason this is even needed (in case you are wondering) is because the error 
    // checking & error printing code outputs this text when printing an error message.

    private final String annotationAsStr;

    /** {@inheritDoc} */ @Override
    public String getAnnotationAsString() { return annotationAsStr; }

    /** {@inheritDoc} */ @Override
    public String getAnnotationName() { return "StaticFunctional"; }


    // ********************************************************************************************
    // ********************************************************************************************
    // Package-Private Constructor, Invoked by class "TypeAnnotationMirrors"
    // ********************************************************************************************
    // ********************************************************************************************


    SFMirror(
            final List<? extends ExpressionTree>    arguments,
            final String                            annotationAsStr
        )
    {
        this.annotationAsStr = annotationAsStr;

        ReadOnlyList<StaticFunctional.Excuse>   excuses = ReadOnlyArrayList.emptyROAL();
        ReadOnlyList<String>                    excused = ReadOnlyArrayList.emptyROAL();

        for (ExpressionTree argument : arguments)
        {
            // System.out.println(argument);

            if (argument.getKind() != ASSIGNMENT) Messager.assertFail(
                "The ExpressionTree returned by the @StaticFunctional arguments list was not of " +
                "type ASSIGNMENT, but rather " + argument.getKind() + '\n' +
                "ExpressionTree.toString(): " + argument.toString() + '\n' +
                "All Annotation Expressions: " + StrCSV.toCSV(arguments, true, true, null),
                null,
                JDUAnnotations.SFMirror
            );

            String          elemName    = ((AssignmentTree) argument).getVariable().toString();
            ExpressionTree  elemValue   = ((AssignmentTree) argument).getExpression();

            /*
            System.out.println(
                "    elemName:             " + elemName + '\n' +
                "    elemValue.toString(): " + elemValue.toString() + '\n' +
                "    elemValue.getKind():  " + elemValue.getKind().toString() + '\n' +
                "    elemValue.getClass(): " + elemValue.getClass().getSimpleName() + '\n'
            );
            */

            if (elemName.equals("Excuses"))
                excuses = handleExcuses(elemValue);

            else if (elemName.equals("Excused"))
                excused = HELPER.handleStringArray(elemValue, "Excused", JDUAnnotations.SFMirror);

            else Messager.assertFail(
                "There was an Expression Variable-Name that was neither 'Excuses' nor " +
                "'Excused', but rather: " + elemName,
                null,
                JDUAnnotations.SFMirror
            );
        }

        if (MsgVerbose.isVerbose()) MsgVerbose.println(
            "@StaticFunctional Annotation-Processing Mirror Class:\n" +
            "    excused: " + StrCSV.toCSV(excused, true, false, null) + '\n' +
            "    excuses: " + StrCSV.toCSV(excuses, true, false, null)
        );

        this.excuses = excuses;
        this.excused = excused;
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // toString
    // ********************************************************************************************
    // ********************************************************************************************


    /** {@inheritDoc} */ @Override
    public String toString()
    {
        // public final ReadOnlyList<Excuse> excuses;
        // public final ReadOnlyList<String> excused;
        // 
        // "    excuses: ".length() => 13

        return
            this.annotationAsStr + '\n' +
            "{\n" +
            "    excuses: " + HELPER.TOSTR(this.excuses, 13) + '\n' +
            "    excused: " + HELPER.TOSTR(this.excused, 13) + '\n' +
            "}";
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Actual, Reflection-Based, Data Extraction Methods, Used & Invoked by the Constructor
    // ********************************************************************************************
    // ********************************************************************************************


    // This is declared 'static' just to help keep in mind that this method doesn't actually access
    // any of this class' field-data.  This method parsed the 'ExpressionTree', and returns the
    // completed / built ReadOnlyList of Excuses back to the method (directly above).  Note the 
    // method above is not static.  It assigs this list to this' instance field 'excuses'

    private static ReadOnlyList<StaticFunctional.Excuse> handleExcuses(ExpressionTree t)
    {
        switch (t.getKind())
        {
            case MEMBER_SELECT: return ReadOnlyList.of(getExcuse((MemberSelectTree) t));
            case IDENTIFIER:    return ReadOnlyList.of(getExcuse((IdentifierTree) t));

            case NEW_ARRAY:

                ROArrayListBuilder<StaticFunctional.Excuse> b = new ROArrayListBuilder<>();

                for (ExpressionTree excuse : ((NewArrayTree) t).getInitializers())

                    switch (excuse.getKind())
                    {
                        case MEMBER_SELECT: b.add(getExcuse((MemberSelectTree) excuse)); break;
                        case IDENTIFIER:    b.add(getExcuse((IdentifierTree) excuse));   break;

                        default: Messager.assertFail(
                            "There is an initializer-array that contains the element: " +
                                excuse.toString() + "\n" +
                            "Unfortunately, this element's 'kind' value is neither " +
                            "MEMBER_SELECT nor IDENTIFIER, but rather [" + excuse.getKind() + "]",
                            null,
                            JDUAnnotations.SFMirror                            
                        );
                    }

                return b.build();

            default: throw Messager.assertFail(
                "There is an initializer with a 'kind' value that is not MEMBER_SELECT nor " +
                "IDENTIFIER nor NEW_ARRAY, but rather a [" + t.getKind() + "]",
                null,
                JDUAnnotations.SFMirror
            );
        }
    }


    // This method implies that the user has said something like: Excuses=Excuse.FLAG
    // Notice that the actual enum constant is after the enum-name 'Excuse'
    //
    // Also, this method is declared static, and is therefore just a helper

    private static StaticFunctional.Excuse getExcuse(MemberSelectTree excuse)
    {
        try
            { return StaticFunctional.Excuse.valueOf(excuse.getIdentifier().toString()); }

        catch (Exception e)
        {
            throw Messager.assertFail(
                e,
                "Exception thrown while building an AnnotationsMirror.  The 'Excuses' Array " +
                "contained the following ExpressionTree: " + excuse.toString() + ", but that " +
                "has failed to load to a constant of the enum Excuse.\n\n" +
                "Did you actually compile your source-code with the Annotation-Processor " +
                "enabled?  If there were an error, it should have been caught at compile time.",
                null,
                JDUAnnotations.SFMirror
            );
        }        
    }


    // This method implies that the user has typed something like: Excuses=FLAG
    // This is allowed as long as the following line is also within / inside their source-code
    //      ==> import static Torello.JavaDoc.Excuse.*;  (A 'static important')
    //
    // Also, this method is declared static, and is therefore just a helper

    private static StaticFunctional.Excuse getExcuse(IdentifierTree excuse)
    {
        try
            { return StaticFunctional.Excuse.valueOf(excuse.getName().toString()); }

        catch (Exception e)
        {
            throw Messager.assertFail(
                e,
                "Exception thrown while building an AnnotationsMirror.  The 'Excuses' Array " +
                "contained the following ExpressionTree: " + excuse.toString() + ", but that " +
                "has failed to load to a constant of the enum Excuse.\n\n" +
                "Did you actually compile your source-code with the Annotation-Processor " +
                "enabled?  If there were an error, it should have been caught at compile time.",
                null,
                JDUAnnotations.SFMirror
            );
        }        
    }
}