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
package Torello.JDUInternal.Annotations.TypeAnnotations.Mirror;

import Torello.JavaDoc.Excuse;

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

import Torello.JDUInternal.Annotations.HELPER;

import Torello.JDUInternal.Messager.Messager;
import Torello.JDUInternal.Messager.MsgVerbose;
import Torello.JDUInternal.Messager.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;

// @StaticFunctional Annotation
// 
// EXPORTS:
//      public Excuse[] excuses() default {};
//      public String[] excused() default {};
// 
// 
// * Torello.JavaDoc.StaticFunctional
//      This is the actual User-Annotation that is part of the API.  A user may place the
//      @StaticFunctional Annotation on a Type / CIET (Class, Interface, Enum, Annotation or Rec)
//      And the "StatelessClass" Simple-Feature will run.  
// 
// * 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.
// 
// * Torello.JDUInternal.Annotations.TypeAnnotations.Processor.StaticFunctionalProcessor
//      This is Annotation-Processsor that is / can-be invoked by the 'javac' (Java-Compiler) at
//      Compile-Time.  Of all of the Annotation-Processors that are used by the Java-Doc Upgrader,
//      this one does THE MOST Error-Checking.  Most of them do nothing.
// 
// * Torello.JDUInternal.Annotations.TypeAnnotations.Mirror.SFMirror
//      After the JDU Detects that a CIET / Type has had the @StaticFunctional Annotation placed
//      upon it, this class extracts the relevant information out of the Annotation using the AST
//      Library in 'com.sun.source.tree' and all of it's helper classes.  It stores the data into
//      two lists.

public class SFMirror 
{
    public final ReadOnlyList<Excuse> excuses;
    public final ReadOnlyList<String> excused;

    public SFMirror(List<? extends ExpressionTree> arguments)
    {
        ReadOnlyList<Excuse> excuses = ReadOnlyArrayList.emptyROAL();
        ReadOnlyList<String> excused = ReadOnlyArrayList.emptyROAL();

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

            if (argument.getKind() != ASSIGNMENT) Messager.assertFailOracleParser(
                "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.assertFailOracleParser(
                "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;
    }


    // 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<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<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.assertFailOracleParser(
                            "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: return Messager.assertFailOracleParser(
                "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 Excuse getExcuse(MemberSelectTree excuse)
    {
        try
            { return Excuse.valueOf(excuse.getIdentifier().toString()); }

        catch (Exception e)
        {
            return Messager.assertFailGeneralPurpose(
                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 Excuse getExcuse(IdentifierTree excuse)
    {
        try
            { return Excuse.valueOf(excuse.getName().toString()); }

        catch (Exception e)
        {
            return Messager.assertFailGeneralPurpose(
                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
            );
        }        
    }
}