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
package Torello.Java.JSON;

import Torello.Java.StringParse;

import java.util.Vector;
import java.lang.reflect.Parameter;
import java.io.StringWriter;
import javax.json.Json;
import javax.json.stream.JsonGenerator;

/**
 * Generates JSON Requests from lists of Parameter-Names, Parameter-Types and Parameter-Values.
 * 
 * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=WRITE_JSON>
 */
@Torello.JavaDoc.StaticFunctional
@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="JSON_SERIALIZER_JDHBI")
public class WriteJSON
{
    // This is a "Static" class (static functional)
    private WriteJSON() { }

    /**
     * Converts a list of class types, names &amp; values into a Json Request, as a {@code String}
     * 
     * @param paramTypes These are the types being passed to the JSON Request.
     * 
     * @param paramNames These are the names of the parameters, and these are used for the names
     * of the JSON parameters passed.
     * 
     * @param optionals This is a boolean array that indicates which parameter-values are allowed 
     * to be null.
     * 
     * @param methodName The name of the command being passed over the Web-Socket Connection.
     * 
     * @return Returns the JSON Web-Socket Request as a {@code java.lang.String}
     */
    public static String get(
            Vector<Class<?>> paramTypes, Vector<String> paramNames, boolean[] optionals,
            int webSocketID, String methodName, Object... paramValues
        )
    {
        StringWriter   strW        = new StringWriter();
        JsonGenerator  jGen        = Json.createGenerator(strW);

        jGen.writeStartObject()
            .write("id", webSocketID)
            .write("method", methodName);

        if (paramTypes.size() > 0) 
        {
            Vector<Object> oVec = new Vector<>();
            for (Object o : paramValues) oVec.add(o);

            jGen.writeStartObject("params");
            get(jGen, paramTypes, paramNames, oVec, optionals);
            jGen.writeEnd();
        }
        // else jGen.writeNull("params"); // Is this the right idea?

        jGen.writeEnd();
        jGen.close();

        return strW.toString();
    }

    /**
     * Converts a list of class types, names &amp; values into a series of Json-Generator commands.
     * 
     * @param jGen Because this method may be used recursively, the current generator that is being
     * used must be passed to this method.
     * 
     * @param cVec The list of types for each entity being converted to a Json Request.
     * @param nVec The list of names for each entity being converted to a Json Request.
     * @param oVec The list of values for each entity being converted to a Json Request.
     * @param optArr A {@code boolean[]} array indicating which parameters may be null.
     */
    public static void get(
            JsonGenerator jGen, Vector<Class<?>> cVec, Vector<String> nVec, Vector<Object> oVec,
            boolean[] optArr
        )
    {
        for (int k=0; k < nVec.size(); k++)
        {
            boolean     opt = optArr[k];
            Class<?>    c   = cVec.elementAt(k);
            String      n   = nVec.elementAt(k);
            Object      o   = oVec.elementAt(k);

            if (o == null)
            {
                if (! opt) throw new Error(
                    "The Operating Assertion is that if a Field is null in this class, it must " +
                    "have been declared 'optional.'  Field [" + n + "] is null, but not optional."
                );

                else continue; // Just leave off of the JSON Completely!
            }

            switch (c.getSimpleName())
            {
                case "int"      : 
                case "Integer"  :   jGen.write(n, ((Integer) o).intValue());        break;
                case "boolean"  :
                case "Boolean"  :   jGen.write(n, ((Boolean) o).booleanValue());    break;
                case "String"   :   jGen.write(n, (String) o);                      break;

                case "Number" :
                    if (o instanceof Long)  jGen.write(n, ((Long) o).longValue());
                    else                    jGen.write(n, ((Double) o).doubleValue());
                    break;

                case "int[]" :
                    jGen.writeStartArray(n);
                    for (int i : (int[]) o) jGen.write(i);
                    jGen.writeEnd();
                    break;

                case "boolean[]" :
                    jGen.writeStartArray(n);
                    for (boolean b : (boolean[]) o) jGen.write(b);
                    jGen.writeEnd();
                    break;

                case "String[]" :
                    jGen.writeStartArray(n);
                    for (String s : (String[]) o)
                        if (s == null)  jGen.writeNull();
                        else            jGen.write(s);
                    jGen.writeEnd();
                    break;

                case "Number[]" :
                    jGen.writeStartArray(n);
                    for (Number num : (Number[]) o)
                        if (num == null)                jGen.writeNull();
                        else if (num instanceof Long)   jGen.write(n, ((Long) num).longValue());
                        else                            jGen.write(n, ((Double) num).doubleValue());
                    jGen.writeEnd();
                    break;

                case "int[][]" :
                    jGen.writeStartArray(n);
                    for (int[] iArr : (int[][]) o)
                    {
                        jGen.writeStartArray();
                        for (boolean b : (boolean[]) o) jGen.write(b);
                        jGen.writeEnd();
                    }
                    jGen.writeEnd();
                    break;

                case "boolean[][]" :
                    jGen.writeStartArray(n);
                    for (int[] iArr : (int[][]) o)
                    {
                        jGen.writeStartArray();
                        for (boolean b : (boolean[]) o) jGen.write(b);
                        jGen.writeEnd();
                    }
                    jGen.writeEnd();
                    break;

                case "String[][]" :
                    jGen.writeStartArray(n);
                    for (String[] sArr : (String[][]) o)
                    {
                        jGen.writeStartArray();
                        for (String s : (String[]) o)
                            if (s == null)  jGen.writeNull();
                            else            jGen.write(s);
                        jGen.writeEnd();
                    }
                    jGen.writeEnd();
                    break;


                case "Number[][]" :
                    jGen.writeStartArray(n);
                    NUM_ARR:
                    for (Number[] sArr : (Number[][]) o)
                    {
                        if (sArr == null)
                        { jGen.writeNull(); continue NUM_ARR; }

                        jGen.writeStartArray();
                        for (Number num : (Number[]) o)
                            if (num == null)                jGen.writeNull();
                            else if (num instanceof Long)   jGen.write(n, ((Long) num).longValue());
                            else                            jGen.write(n, ((Double) num).doubleValue());
                        jGen.writeEnd();
                    }
                    jGen.writeEnd();
                    break;

                default:
                    if (c.isArray())
                    {
                        if (! BaseType.class.isAssignableFrom(c.getComponentType()))
                            throw new Error(
                                "One of the Fields [" + n + "] is declared an array, " +
                                "(type " + c.getSimpleName() + "), but the Array Elements do " +
                                "not implement the interface 'BaseType'"
                            );
                        
                        if (StringParse.countCharacters(c.getSimpleName(), '[') > 1)
                            throw new Error(
                                "One of the Fields [" + n + "] is declared an array, " +
                                "(type " + c.getSimpleName() + "), but the Array has a dimension " +
                                "greater than 1.  This was agreed, impossible."
                            );

                        jGen.writeStartArray(n);
                        for (BaseType bt : (BaseType[]) o)
                            if (bt == null) jGen.writeNull();
                            else            bt.toJSON(null, jGen);
                        jGen.writeEnd();
                    }
                    else
                    {
                        if (! BaseType.class.isAssignableFrom(c)) throw new Error(
                            "Field [" + n +"] has a class / type [" + c.getSimpleName() + "], " +
                            "but this class does not implement the interface BaseType!"
                        );

                        else ((BaseType) o).toJSON(n, jGen);
                    }
            }
        }
    }
}