001package Torello.Java.JSON;
002
003import Torello.Java.StringParse;
004
005import java.util.Vector;
006import java.lang.reflect.Parameter;
007import java.io.StringWriter;
008import javax.json.Json;
009import javax.json.stream.JsonGenerator;
010
011/**
012 * Generates JSON Requests from lists of Parameter-Names, Parameter-Types and Parameter-Values.
013 * 
014 * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=WRITE_JSON>
015 */
016@Torello.JavaDoc.StaticFunctional
017@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="JSON_SERIALIZER_JDHBI")
018public class WriteJSON
019{
020    // This is a "Static" class (static functional)
021    private WriteJSON() { }
022
023    /**
024     * Converts a list of class types, names &amp; values into a Json Request, as a {@code String}
025     * 
026     * @param paramTypes These are the types being passed to the JSON Request.
027     * 
028     * @param paramNames These are the names of the parameters, and these are used for the names
029     * of the JSON parameters passed.
030     * 
031     * @param optionals This is a boolean array that indicates which parameter-values are allowed 
032     * to be null.
033     * 
034     * @param methodName The name of the command being passed over the Web-Socket Connection.
035     * 
036     * @return Returns the JSON Web-Socket Request as a {@code java.lang.String}
037     */
038    public static String get(
039            Vector<Class<?>> paramTypes, Vector<String> paramNames, boolean[] optionals,
040            int webSocketID, String methodName, Object... paramValues
041        )
042    {
043        StringWriter   strW        = new StringWriter();
044        JsonGenerator  jGen        = Json.createGenerator(strW);
045
046        jGen.writeStartObject()
047            .write("id", webSocketID)
048            .write("method", methodName);
049
050        if (paramTypes.size() > 0) 
051        {
052            Vector<Object> oVec = new Vector<>();
053            for (Object o : paramValues) oVec.add(o);
054
055            jGen.writeStartObject("params");
056            get(jGen, paramTypes, paramNames, oVec, optionals);
057            jGen.writeEnd();
058        }
059        // else jGen.writeNull("params"); // Is this the right idea?
060
061        jGen.writeEnd();
062        jGen.close();
063
064        return strW.toString();
065    }
066
067    /**
068     * Converts a list of class types, names &amp; values into a series of Json-Generator commands.
069     * 
070     * @param jGen Because this method may be used recursively, the current generator that is being
071     * used must be passed to this method.
072     * 
073     * @param cVec The list of types for each entity being converted to a Json Request.
074     * @param nVec The list of names for each entity being converted to a Json Request.
075     * @param oVec The list of values for each entity being converted to a Json Request.
076     * @param optArr A {@code boolean[]} array indicating which parameters may be null.
077     */
078    public static void get(
079            JsonGenerator jGen, Vector<Class<?>> cVec, Vector<String> nVec, Vector<Object> oVec,
080            boolean[] optArr
081        )
082    {
083        for (int k=0; k < nVec.size(); k++)
084        {
085            boolean     opt = optArr[k];
086            Class<?>    c   = cVec.elementAt(k);
087            String      n   = nVec.elementAt(k);
088            Object      o   = oVec.elementAt(k);
089
090            if (o == null)
091            {
092                if (! opt) throw new Error(
093                    "The Operating Assertion is that if a Field is null in this class, it must " +
094                    "have been declared 'optional.'  Field [" + n + "] is null, but not optional."
095                );
096
097                else continue; // Just leave off of the JSON Completely!
098            }
099
100            switch (c.getSimpleName())
101            {
102                case "int"      : 
103                case "Integer"  :   jGen.write(n, ((Integer) o).intValue());        break;
104                case "boolean"  :
105                case "Boolean"  :   jGen.write(n, ((Boolean) o).booleanValue());    break;
106                case "String"   :   jGen.write(n, (String) o);                      break;
107
108                case "Number" :
109                    if (o instanceof Long)  jGen.write(n, ((Long) o).longValue());
110                    else                    jGen.write(n, ((Double) o).doubleValue());
111                    break;
112
113                case "int[]" :
114                    jGen.writeStartArray(n);
115                    for (int i : (int[]) o) jGen.write(i);
116                    jGen.writeEnd();
117                    break;
118
119                case "boolean[]" :
120                    jGen.writeStartArray(n);
121                    for (boolean b : (boolean[]) o) jGen.write(b);
122                    jGen.writeEnd();
123                    break;
124
125                case "String[]" :
126                    jGen.writeStartArray(n);
127                    for (String s : (String[]) o)
128                        if (s == null)  jGen.writeNull();
129                        else            jGen.write(s);
130                    jGen.writeEnd();
131                    break;
132
133                case "Number[]" :
134                    jGen.writeStartArray(n);
135                    for (Number num : (Number[]) o)
136                        if (num == null)                jGen.writeNull();
137                        else if (num instanceof Long)   jGen.write(n, ((Long) num).longValue());
138                        else                            jGen.write(n, ((Double) num).doubleValue());
139                    jGen.writeEnd();
140                    break;
141
142                case "int[][]" :
143                    jGen.writeStartArray(n);
144                    for (int[] iArr : (int[][]) o)
145                    {
146                        jGen.writeStartArray();
147                        for (boolean b : (boolean[]) o) jGen.write(b);
148                        jGen.writeEnd();
149                    }
150                    jGen.writeEnd();
151                    break;
152
153                case "boolean[][]" :
154                    jGen.writeStartArray(n);
155                    for (int[] iArr : (int[][]) o)
156                    {
157                        jGen.writeStartArray();
158                        for (boolean b : (boolean[]) o) jGen.write(b);
159                        jGen.writeEnd();
160                    }
161                    jGen.writeEnd();
162                    break;
163
164                case "String[][]" :
165                    jGen.writeStartArray(n);
166                    for (String[] sArr : (String[][]) o)
167                    {
168                        jGen.writeStartArray();
169                        for (String s : (String[]) o)
170                            if (s == null)  jGen.writeNull();
171                            else            jGen.write(s);
172                        jGen.writeEnd();
173                    }
174                    jGen.writeEnd();
175                    break;
176
177
178                case "Number[][]" :
179                    jGen.writeStartArray(n);
180                    NUM_ARR:
181                    for (Number[] sArr : (Number[][]) o)
182                    {
183                        if (sArr == null)
184                        { jGen.writeNull(); continue NUM_ARR; }
185
186                        jGen.writeStartArray();
187                        for (Number num : (Number[]) o)
188                            if (num == null)                jGen.writeNull();
189                            else if (num instanceof Long)   jGen.write(n, ((Long) num).longValue());
190                            else                            jGen.write(n, ((Double) num).doubleValue());
191                        jGen.writeEnd();
192                    }
193                    jGen.writeEnd();
194                    break;
195
196                default:
197                    if (c.isArray())
198                    {
199                        if (! BaseType.class.isAssignableFrom(c.getComponentType()))
200                            throw new Error(
201                                "One of the Fields [" + n + "] is declared an array, " +
202                                "(type " + c.getSimpleName() + "), but the Array Elements do " +
203                                "not implement the interface 'BaseType'"
204                            );
205                        
206                        if (StringParse.countCharacters(c.getSimpleName(), '[') > 1)
207                            throw new Error(
208                                "One of the Fields [" + n + "] is declared an array, " +
209                                "(type " + c.getSimpleName() + "), but the Array has a dimension " +
210                                "greater than 1.  This was agreed, impossible."
211                            );
212
213                        jGen.writeStartArray(n);
214                        for (BaseType bt : (BaseType[]) o)
215                            if (bt == null) jGen.writeNull();
216                            else            bt.toJSON(null, jGen);
217                        jGen.writeEnd();
218                    }
219                    else
220                    {
221                        if (! BaseType.class.isAssignableFrom(c)) throw new Error(
222                            "Field [" + n +"] has a class / type [" + c.getSimpleName() + "], " +
223                            "but this class does not implement the interface BaseType!"
224                        );
225
226                        else ((BaseType) o).toJSON(n, jGen);
227                    }
228            }
229        }
230    }
231}