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
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
package Torello.Java.JSON;

import Torello.JavaDoc.IntoHTMLTable;
import static Torello.JavaDoc.IntoHTMLTable.Background.*;

import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonValue;
import javax.json.JsonNumber;
import javax.json.JsonString;

import static javax.json.JsonValue.ValueType.*;

import java.math.BigDecimal;
import java.util.function.Function;
import java.util.function.ObjIntConsumer;

/**
 * This class is the "Central Artery" for all Json-Array Processing done in this package.
 * These four FOR-LOOPS handle 100% of the array processors done by all of the "RJArr"
 * classes offered.
 *  
 * These four methods are extremely similar, but have a few minor subtleties that prevent 
 * them from being unified into a single handler for all types.
 */
public class ProcessJsonArray
{
    private ProcessJsonArray() { }


    // ********************************************************************************************
    // ********************************************************************************************
    // Un-Synchronized Method Variants
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * Any and all Json-Array Processors that are intended to read an array of {@link JsonNumber}
     * will invoke this method to do their Type-Conversions.
     * 
     * @param <NUMERIC_DATA_TYPE> <EMBED CLASS='external-html' DATA-FILE-ID=PJA_NUM_DATA_TYPE>
     * @param <RETURN_TYPE> <EMBED CLASS='external-html' DATA-FILE-ID=PJA_NUM_RETURN_TYPE>
     * 
     * @param ja This should be any instance of {@link JsonArray}.  It is expected that if this
     * array does not contain explicit {@link JsonNumber} values, that is should at least contain
     * values that are to converted or properly parsed into Java-Numbers.
     * 
     * @param rec <EMBED CLASS='external-html' DATA-FILE-ID=PJA_REC_PARAM>
     * 
     * @return An instance of the type specified by Type-Parameter {@code 'RETURN_TYPE'}
     */
    public static <NUMERIC_DATA_TYPE extends Number, RETURN_TYPE> RETURN_TYPE numericToJava(
            final JsonArray                                     ja,
            final SettingsRec<NUMERIC_DATA_TYPE, RETURN_TYPE>   rec
        )
    {
        rec.ja = ja;

        final int SIZE = ja.size();

        JsonValue jv;


        // If 'RETURN_TYPE' is a Stream, this builds a new Stream, and saves it to the
        // internally-used "EffectivelyFinal" instance.  It's quite simple, actually.
        //
        // If 'RETURN_TYPE' is a Consumer, this is a simple "No-Op"

        rec.constructNewBuilder.run();

        for (int i=0; i < SIZE; i++)

            switch ((jv = ja.get(i)).getValueType())
            {
                // javax.json.JsonValue.ValueType.NULL
                case NULL: rec.handlerNull.accept(i); break;

                // javax.json.JsonValue.ValueType.NUMBER
                case NUMBER: rec.handlerNumber.accept((JsonNumber) jv, i); break;

                // javax.json.JsonValue.ValueType.STRING
                case STRING: rec.handlerJsonString.accept((JsonString) jv, i); break;

                // OBJECT, ARRAY, TRUE, FALSE
                default:

                    if (rec.handlerWrongType != null)
                        rec.handlerWrongType.accept(i);
                    else
                        throw new JsonTypeArrException(ja, i, NUMBER, jv, rec.CLASS);
            }

        // Run Stream.Builder.build()
        return rec.runBuilderDotBuild.get();
    }

    /**
     * Any and all Json-Array Processors that are intended to read an array of Json-Boolean Values
     * will invoke this method to do their Type-Conversions.
     * 
     * @param <RETURN_TYPE> <EMBED CLASS='external-html' DATA-FILE-ID=PJA_BL_RETURN_TYPE>
     * 
     * @param ja This should be any instance of {@link JsonArray}.  It is expected that if this
     * array does not contain explicit Json-Boolean values, that is should at least contain values
     * that are to converted or properly parsed into Java-Booleans.
     * 
     * @param rec <EMBED CLASS='external-html' DATA-FILE-ID=PJA_REC_PARAM>
     * 
     * @return An instance of {@code Stream<Boolean>}.  If the provided instance of 
     * {@code SettingsRec} has been constructed for a {@code Consumer}, then this method will  
     * return null.
     */
    public static <RETURN_TYPE> RETURN_TYPE booleanToJava(
            final JsonArray                         ja,
            final SettingsRec<Boolean, RETURN_TYPE> rec
        )
    {
        final int SIZE = ja.size();

        JsonValue jv = null;

        rec.ja = ja;

        // If 'RETURN_TYPE' is a Consumer, this is a simple "No-Op"
        rec.constructNewBuilder.run();

        for (int i=0; i < SIZE; i++)

            switch ((jv = ja.get(i)).getValueType())
            {
                // javax.json.JsonValue.ValueType.NULL, TRUE, FALSE & STRING
                case NULL:      rec.handlerNull.accept(i); break;
                case TRUE:      rec.ACCEPTOR.accept(true, i); break;
                case FALSE:     rec.ACCEPTOR.accept(false, i); break;
                case STRING:    rec.handlerJsonString.accept((JsonString) jv, i); break;

                // javax.json.JsonValue.ValueType.NUMBER, OBJECT, ARRAY
                default:
                    if (rec.handlerWrongType != null) rec.handlerWrongType.accept(i);
                    else throw new JsonTypeArrException(ja, i, TRUE, jv, Boolean.class);
            }


        // Run Stream.Builder.build() - IF NEEDED
        // 'get()' simply returns null if the SettingsRec<T, U> had a "consumer"
        // for it's 'U' Type, rather than a Stream.

        return rec.runBuilderDotBuild.get();
    }

    /**
     * Used to convert {@code JsonObject}'s into Java-Objects
     * 
     * @param <T> The Class / Type of the Objects to be extracted from the {@link JsonArray}
     * @param <RETURN_TYPE> <EMBED CLASS='external-html' DATA-FILE-ID=PJA_T_RETURN_TYPE>
     * 
     * @param ja This should be any instance of {@link JsonArray}.  It is expected that this array
     * contain explicit {@link JsonObject} values, that can be converted in.to {@code 'T'}
     * 
     * @param rec <EMBED CLASS='external-html' DATA-FILE-ID=PJA_REC_PARAM>
     * 
     * @return An instance of the type specified by Type-Parameter {@code 'RETURN_TYPE'}.
     */
    public static <T, RETURN_TYPE> RETURN_TYPE objToJava(
            final JsonArray                     ja,
            final SettingsRec<T, RETURN_TYPE>   rec
        )
    {
        final int SIZE = ja.size();

        JsonValue jv = null;

        rec.ja = ja;


        // For Stream<T>, this builds a new Stream.Builder
        // for Consumer<T>, this is a "No-Op"

        rec.constructNewBuilder.run();

        for (int i=0; i < SIZE; i++)

            switch ((jv = ja.get(i)).getValueType())
            {
                // javax.json.JsonValue.ValueType.NULL
                case NULL:
                    rec.handlerNull.accept(i);
                    break;

                // javax.json.JsonValue.ValueType.OBJECT
                case OBJECT:
                    T obj = rec.builder1Or2
                        ? rec.objBuilder.apply((JsonObject) jv)
                        : rec.objBuilder2.apply(i, (JsonObject) jv);

                    rec.ACCEPTOR.accept(obj, i);
                    break;

                // javax.json.JsonValue.ValueType.NUMBER, STRING, TRUE, FALSE, ARRAY
                default:
                    if (rec.handlerWrongType != null) rec.handlerWrongType.accept(i);
                    else throw new JsonTypeArrException(ja, i, TRUE, jv, rec.CLASS);
            }


        // For Stream<T>, this runs Stream.Builder.build()
        // For Consumer<T>, this automatically returns null

        return rec.runBuilderDotBuild.get();
    }

    /**
     * Any and all Json-Array Processors that are intended to read an array of {@link JsonString}
     * Values will invoke this method to do their Type-Conversions.
     * 
     * @param <RETURN_TYPE> <EMBED CLASS='external-html' DATA-FILE-ID=PJA_STR_RETURN_TYPE>
     * 
     * @param ja This should be any instance of {@link JsonArray}.
     * 
     * @param rec <EMBED CLASS='external-html' DATA-FILE-ID=PJA_REC_PARAM>
     * 
     * @return An instance of {@code Stream<String>}.  If the provided instance of
     * {@code SettingsRec} has been constructed for a {@code Consumer}, then this method will
     * return null.
     */
    public static <RETURN_TYPE> RETURN_TYPE strToJava(
            final JsonArray                         ja,
            final SettingsRec<String, RETURN_TYPE>  rec
        )
    {
        final int SIZE = ja.size();

        JsonValue jv = null;

        rec.ja = ja;

        if (rec.constructNewBuilder != null) rec.constructNewBuilder.run();

        for (int i=0; i < SIZE; i++)

            switch ((jv = ja.get(i)).getValueType())
            {
                // javax.json.JsonValue.ValueType.NULL
                case NULL:
                    rec.handlerNull.accept(i);
                    break;

                // javax.json.JsonValue.ValueType.STRING
                case STRING:
                    rec.ACCEPTOR.accept(((JsonString) jv).getString(), i);
                    break;

                // OBJECT, ARRAY, TRUE, FALSE, NUMBER
                default:
                    rec.jsonStringWrongTypeHandler.accept(jv, i);
            }


        // Returns the Built-Stream, for Streams.
        // Returns 'null' for Consumer's

        return rec.runBuilderDotBuild.get();
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Synchronized Method Variants
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * <BR>Synchronized: Nothing More than a "Synchronized Wrapper" which can be used in a
     *                   Multi-Threaded Programming-Environment.
     * <BR>Wraps: Method {@link #numericToJava(JsonArray, SettingsRec)}
     * <BR>Locking-Object: Input-Parameter {@code 'rec'}, the {@code SettingsRec} instance 
     * <BR>IMPORTANT: Synchronization <B><I>is only needed</I></B> when the {@code SettingsRec}
     *                instance shall be "re-used" in other threads.
     */
    @IntoHTMLTable(background=BlueDither, title="Synchronized Variant of Method 'numericToJava'")
    public static <NUMERIC_DATA_TYPE extends Number, RETURN_TYPE> RETURN_TYPE
        numericToJavaSync(
            final JsonArray                                     ja,
            final SettingsRec<NUMERIC_DATA_TYPE, RETURN_TYPE>   rec
        )
    { synchronized (rec) { return numericToJava(ja, rec); } }

    /**
     * <BR>Synchronized: Nothing More than a "Synchronized Wrapper" which can be used in a
     *                   Multi-Threaded Programming-Environment.
     * <BR>Wraps: Method {@link #booleanToJava(JsonArray, SettingsRec)}
     * <BR>Locking-Object: Input-Parameter {@code 'rec'}, the {@code SettingsRec} instance 
     * <BR>IMPORTANT: Synchronizations <B><I>is only needed</I></B> when the {@code SettingsRec}
     *                instance shall be "re-used" in other threads.
     */
    @IntoHTMLTable(background=GreenDither, title="Synchronized Variant of Method 'booleanToJava'")
    public static <RETURN_TYPE> RETURN_TYPE booleanToJavaSync(
            final JsonArray                         ja,
            final SettingsRec<Boolean, RETURN_TYPE> rec
        )
    { synchronized (rec) { return booleanToJava(ja, rec); } }

    /**
     * <BR>Synchronized: Nothing More than a "Synchronized Wrapper" which can be used in a
     *                     Multi-Threaded Programming-Environment.
     * <BR>Wraps: Method {@link #objToJava(JsonArray, SettingsRec)}
     * <BR>Locking-Object: Input-Parameter {@code 'rec'}, the {@code SettingsRec} instance 
     * <BR>IMPORTANT: Synchronizations <B><I>is only needed</I></B> when the {@code SettingsRec}
     *                instance shall be "re-used" in other threads.
     */
    @IntoHTMLTable(background=BlueDither, title="Synchronized Variant of Method 'objToJava'")
    public static <T, RETURN_TYPE> RETURN_TYPE objToJavaSync(
            final JsonArray                     ja,
            final SettingsRec<T, RETURN_TYPE>   rec
        )
    { synchronized (rec) { return objToJava(ja, rec); } }

    /**
     * <BR>Synchronized: Nothing More than a "Synchronized Wrapper" which can be used in a
     *                     Multi-Threaded Programming-Environment.
     * <BR>Wraps: Method {@link #strToJava(JsonArray, SettingsRec)}
     * <BR>Locking-Object: Input-Parameter {@code 'rec'}, the {@code SettingsRec} instance 
     * <BR>IMPORTANT: Synchronizations <B><I>is only needed</I></B> when the {@code SettingsRec}
     *                instance shall be "re-used" in other threads.
     */
    @IntoHTMLTable(background=GreenDither, title="Synchronized Variant of Method 'strToJava'")
    public static <RETURN_TYPE> RETURN_TYPE strToJavaSync(
        final JsonArray                         ja,
        final SettingsRec<String, RETURN_TYPE>  rec
    )
    { synchronized (rec) { return strToJava(ja, rec); } }

}