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

import Torello.Java.StringParse;
import Torello.Java.UnreachableError;

import Torello.Java.Function.FloatConsumer;
import Torello.Java.Function.IntIntTConsumer;
import Torello.Java.Function.IntTFunction;

import Torello.Java.Additional.EffectivelyFinal;
import Torello.Java.Additional.Counter;

import java.math.BigDecimal;

import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.IntConsumer;
import java.util.function.ObjIntConsumer;
import java.util.function.DoubleConsumer;

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

import static javax.json.JsonValue.ValueType.*;
import static Torello.JSON.JFlag.*;

/** <EMBED CLASS=external-html DATA-FILE-ID=SETTINGS_REC> */
public class SettingsRec<T, U>
{
    // ********************************************************************************************
    // ********************************************************************************************
    // Instance-Fields: PACKAGE-PRIVATE - These are used OUTSIDE of this class
    // ********************************************************************************************
    // ********************************************************************************************


    // Provided by User-Input, Assigned after construction, cannot be final
    // When this SettingsRec is used in conjunction withe Multi-Dimensional Array-Processor
    // this field will be assigned and re-assigned multiple times.
    // 
    // This field is THE ONLY NON-FINAL FIELD in this class!

    JsonArray ja;

    // The main stuff:
    final Class<T>      CLASS;
    final ACCEPTOR<T>   acceptor;
    final Runnable      opener;
    final Supplier<U>   closer;


    // This is applied when the user has opted for a "SettingsRec" that is for a Consumer.  
    // Remember that generating a SettingsRec speeds things up, by skipping all of the 
    // configuration computing that goes on.  That's what this class constructor does...
    // 
    // When the user generates a Pre-Constructed "SettingsRec" instance, if that record is for a
    // consumer, then it is extremely likely that he is going to need to update the Conumser that
    // is used with that Record each and every time that it is used on a different array.
    // 
    // As such, as of June 2025, there is a new field called "ChangeableConsumer"
    // This field will always be null, unless this SettingsRec instance was generated by one of the
    // "Generate a Consumer Settings Rec" Classes...

    final CHANGEABLE_CONSUMER<T> changeableConsumer;


    // The Main For-Loop Switch-Statement Handlers!
    // These are all used in the classes: ProcessJsonArray and JSON_ARRAY_DIMN!

    final IntConsumer                   handlerWrongType;
    final IntConsumer                   handlerNull;
    final ObjIntConsumer<JsonString>    handlerJsonString;
    final ObjIntConsumer<JsonNumber>    handlerNumber; // Added Thanksgiving 2024
    final ObjIntConsumer<JsonValue>     jsonStringWrongTypeHandler;


    // This field will be null in all cases of a Settings-Record instance - **UNLESS** this is
    // being constructed for processing an array of JsonObject's to be converted to a
    // User-Specified type 'T'
    // 
    // ALSO, as of May 2025: There are officially two variants of the exact same "Static-Builder"
    // Lambda's / Functional-Interfaces.
    // 
    // Variant 1: A user may build / construct an Object-Instance by passing just JsonObject
    //            to the Lambda Function / Builder.  This is the "Single Argument Constructor"
    //
    // Variant 2: The user will build / construct the instance of 'T' (the User-Defined Type) using
    //            a builder that accepts THREE PARAMETERS, not just ONE PARAMETER.
    //            The two additional arguments which are allowed are the "Array-Index Counter" and
    //            the "Success Counter".  These will often be the same exact integer value, unless
    //            the user has used the JFlag's to skip array locations on a failure...
    //
    // Final point of interest: The "Three Argumeent Builder" is actually saved as a Two-Argument
    // Lambda Method.  This is because the "Success Counter" was already "shoved" inside the lambda
    // that has been saved here.  Though the end user will see a Lambda Function / Method-Pointer 
    // 
    // whose signature is:          apply(int arrayIndex, int successCount, JsonObject jsonObject)
    // the signature saved here is: apply(int arrayIndex, JsonObject jsonObject)
    // 
    // Specifically, the second field listed below is an "IntTFunction", not "IntIntTFunc"

    final Function<JsonObject, T>       singleArgUserTypeBuilder;
    final IntTFunction<JsonObject, T>   tripleArgUserTypeBuilder; 
    final boolean                       builder1Or2;

    // These three are only used for Multi-Dimensional Array-Processing
    final Function<JsonArray, Object>   array1DGenerator;
    final boolean                       IN_NSAT, S_NSAT;


    // ********************************************************************************************
    // ********************************************************************************************
    // Package-Private SettingsRec Constructor
    // ********************************************************************************************
    // ********************************************************************************************


    // The very last line of this method has a cast of SettingsRec<>
    @SuppressWarnings("unchecked")
    SettingsRec(SETTINGS_REC_BUILDER<T, U> b)
    {
        // Less typing
        final BASIC_TYPES<T> basicType = b.basicType;

        // Main stuff
        this.CLASS              = basicType.CLASS;
        this.closer             = b.closer();
        this.acceptor           = new ACCEPTOR<>(b);
        this.changeableConsumer = b.changeableConsumer();


        // Note that nothing could be clobbered here!  For any / all kinds of "consumers", they
        // always have "No-Op" openers.  Openers were originally solely used for streams.  Now with
        // "Updateable Consumers," due to the "Get SettingsRec" methods, consumers too now require
        // an opener.
        // 
        // Their version of opener is to reset the "Accept Count"

        this.opener = b.opener();

        // Only used for building Multi-Dimensional Array's (rarely needed)
        this.array1DGenerator = (b.arrayGen1DRefOrPrimitive() == null)
            ? null 
            : GENERATE_1DARRAY.retrieveAppropriateGenerator
                (this, basicType, b.arrayGen1DRefOrPrimitive().booleanValue());


        // This is the "pseudo-hack".  The issue is allowing "EXTENDED_TYPES" to be defined as a 
        // class which *ACTUALLY* extends the class "BASIC_TYPES".  The two of them have absolutely
        // zero overlap other than the "output class" (Class<?> CLASS) field.
        // 
        // In any case, these three fields are the ones which allow a user to build an object of an
        // arbitrary, user defined type <T> out of an instance of JsonObject. 

        if (basicType.whichType == BASIC_TYPES.EXTENDED_OBJ)
        {
            final EXTENDED_TYPES<T> et = (EXTENDED_TYPES<T>) basicType;

            this.singleArgUserTypeBuilder   = et.singleArgUserTypeBuilder();
            this.tripleArgUserTypeBuilder   = et.tripleArgUserTypeBuilder(this.acceptor);
            this.builder1Or2                = et.ONE_ARG_OR_THREE_ARG;
        }

        else
        {
            this.singleArgUserTypeBuilder   = null;
            this.tripleArgUserTypeBuilder   = null;
            this.builder1Or2                = false; // compltely irrelevant
        }


        // This is a temporary class that is not used once this constructor has
        // completed!  Hopefully the garbage collector picks it up as soon as this is finished.
        // The "FlagsToHandler" actually does a lot of the dispatch for deciding what handler's /
        // Lambda's are to be used when Processing a JsonArray.

        final FlagsToHandler<T> flagsToHandlers = new FlagsToHandler<>(
            b.FLAGS,
            basicType.referenceOrPrimitive,
            this.acceptor
        );

        // Two little flags that are used by the Multi-Dimensional Array Processor
        this.IN_NSAT    = flagsToHandlers.IN_NSAT();
        this.S_NSAT     = flagsToHandlers.S_NSAT();

        this.handlerNull        = flagsToHandlers.selectNullHandler(this);
        this.handlerWrongType   = flagsToHandlers.selectWrongTypeHandler();


        // "handlerJsonString" is used for both Number-Stuff and Boolean
        // It is not Used for String or Object...
        // 
        // If you want to learn where this is used, just go to "ProcessJsonArray",
        // and you will see all of these cute little dispatch/handler dealy's hard at work

        this.handlerJsonString =

                (basicType.whichType != BASIC_TYPES.STRING)
            &&  (basicType.whichType != BASIC_TYPES.EXTENDED_OBJ)
            &&  (basicType.whichType != BASIC_TYPES.JSON_OBJECT)
            &&  (basicType.whichType != BASIC_TYPES.JSON_ARRAY)

            ? flagsToHandlers.selectJsonStringHandler(
                    this,
                    b.userParser,
                    basicType.whichType,
                    basicType.validStrTester,
                    basicType.defaultParser
                )
            : null;


        // The field "isNumberType" is only true if this is actually used to convert a
        // a JsonNumber into a Java-Number.  This will be false for Strings, Objects & Booleans!

        this.handlerNumber = basicType.isNumberType
            ? ChooseNumberHandler.choose(
                    this,
                    this.acceptor,
                    basicType.whichType,
                    basicType.referenceOrPrimitive,
                    basicType.numberConverter,
                    basicType.numberConverterExThrow,
                    basicType.jsonNumWillFit,
                    flagsToHandlers.RJA_AEX(),
                    flagsToHandlers.selectAEXHandler()
                )
            : null;


        // TIf you look at "ProcessJsonArray" - this is only used for converting
        // JsonArray's of String.  All other types will never use this handler.  Therefore, in
        // any case where a User HAS NOT requested to process a JsonArray of Strings, leaving this
        // handler as 'null' will have no effect, and it certainly won't cause NullPointerException

        this.jsonStringWrongTypeHandler = (basicType.whichType == BASIC_TYPES.STRING)
            ? flagsToHandlers.selectJsonStringWrongTypeHandler((SettingsRec<String, ?>) this)
            : null;
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Set the Consumer to something new
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * Assign a value to the internally used consumer, to which {@link JsonArray} data is emitted
     * @param c                     <EMBED CLASS='external-html' DATA-FILE-ID=SR_PARAM_C_1> 
     * @throws WrongModeException   <EMBED CLASS='external-html' DATA-FILE-ID=WRONG_MODE_EX>
     */
    public void setConsumer(Consumer<T> c)
    {
        assertChangeableConsumer();
        this.changeableConsumer.setConsumer(c);
    }

    /**
     * Assign a value to the internally used consumer, to which {@link JsonArray} data is emitted
     * @param c                     <EMBED CLASS='external-html' DATA-FILE-ID=SR_PARAM_C_2> 
     * @throws WrongModeException   <EMBED CLASS='external-html' DATA-FILE-ID=WRONG_MODE_EX>
     */
    public void setConsumer(IntIntTConsumer<T> c)
    {
        assertChangeableConsumer();
        this.changeableConsumer.setConsumer(c);
    }

    private void assertChangeableConsumer()
    {
        if (this.changeableConsumer == null) throw new WrongModeException(
            "This SettingsRec instance was not configured for a Consumer, but rather a Stream " +
            "or an array."
        );
    }

}