001package Torello.JSON;
002
003import Torello.Java.StringParse;
004import Torello.Java.UnreachableError;
005
006import Torello.Java.Function.FloatConsumer;
007import Torello.Java.Function.IntIntTConsumer;
008import Torello.Java.Function.IntTFunction;
009
010import Torello.Java.Additional.EffectivelyFinal;
011import Torello.Java.Additional.Counter;
012
013import java.math.BigDecimal;
014
015import java.util.function.Consumer;
016import java.util.function.Supplier;
017import java.util.function.Function;
018import java.util.function.Predicate;
019import java.util.function.IntConsumer;
020import java.util.function.ObjIntConsumer;
021import java.util.function.DoubleConsumer;
022
023import javax.json.JsonArray;
024import javax.json.JsonNumber;
025import javax.json.JsonString;
026import javax.json.JsonValue;
027import javax.json.JsonObject;
028
029import static javax.json.JsonValue.ValueType.*;
030import static Torello.JSON.JFlag.*;
031
032/** <EMBED CLASS=external-html DATA-FILE-ID=SETTINGS_REC> */
033public class SettingsRec<T, U>
034{
035    // ********************************************************************************************
036    // ********************************************************************************************
037    // Instance-Fields: PACKAGE-PRIVATE - These are used OUTSIDE of this class
038    // ********************************************************************************************
039    // ********************************************************************************************
040
041
042    // Provided by User-Input, Assigned after construction, cannot be final
043    // When this SettingsRec is used in conjunction withe Multi-Dimensional Array-Processor
044    // this field will be assigned and re-assigned multiple times.
045    // 
046    // This field is THE ONLY NON-FINAL FIELD in this class!
047
048    JsonArray ja;
049
050    // The main stuff:
051    final Class<T>      CLASS;
052    final ACCEPTOR<T>   acceptor;
053    final Runnable      opener;
054    final Supplier<U>   closer;
055
056
057    // This is applied when the user has opted for a "SettingsRec" that is for a Consumer.  
058    // Remember that generating a SettingsRec speeds things up, by skipping all of the 
059    // configuration computing that goes on.  That's what this class constructor does...
060    // 
061    // When the user generates a Pre-Constructed "SettingsRec" instance, if that record is for a
062    // consumer, then it is extremely likely that he is going to need to update the Conumser that
063    // is used with that Record each and every time that it is used on a different array.
064    // 
065    // As such, as of June 2025, there is a new field called "ChangeableConsumer"
066    // This field will always be null, unless this SettingsRec instance was generated by one of the
067    // "Generate a Consumer Settings Rec" Classes...
068
069    final CHANGEABLE_CONSUMER<T> changeableConsumer;
070
071
072    // The Main For-Loop Switch-Statement Handlers!
073    // These are all used in the classes: ProcessJsonArray and JSON_ARRAY_DIMN!
074
075    final IntConsumer                   handlerWrongType;
076    final IntConsumer                   handlerNull;
077    final ObjIntConsumer<JsonString>    handlerJsonString;
078    final ObjIntConsumer<JsonNumber>    handlerNumber; // Added Thanksgiving 2024
079    final ObjIntConsumer<JsonValue>     jsonStringWrongTypeHandler;
080
081
082    // This field will be null in all cases of a Settings-Record instance - **UNLESS** this is
083    // being constructed for processing an array of JsonObject's to be converted to a
084    // User-Specified type 'T'
085    // 
086    // ALSO, as of May 2025: There are officially two variants of the exact same "Static-Builder"
087    // Lambda's / Functional-Interfaces.
088    // 
089    // Variant 1: A user may build / construct an Object-Instance by passing just JsonObject
090    //            to the Lambda Function / Builder.  This is the "Single Argument Constructor"
091    //
092    // Variant 2: The user will build / construct the instance of 'T' (the User-Defined Type) using
093    //            a builder that accepts THREE PARAMETERS, not just ONE PARAMETER.
094    //            The two additional arguments which are allowed are the "Array-Index Counter" and
095    //            the "Success Counter".  These will often be the same exact integer value, unless
096    //            the user has used the JFlag's to skip array locations on a failure...
097    //
098    // Final point of interest: The "Three Argumeent Builder" is actually saved as a Two-Argument
099    // Lambda Method.  This is because the "Success Counter" was already "shoved" inside the lambda
100    // that has been saved here.  Though the end user will see a Lambda Function / Method-Pointer 
101    // 
102    // whose signature is:          apply(int arrayIndex, int successCount, JsonObject jsonObject)
103    // the signature saved here is: apply(int arrayIndex, JsonObject jsonObject)
104    // 
105    // Specifically, the second field listed below is an "IntTFunction", not "IntIntTFunc"
106
107    final Function<JsonObject, T>       singleArgUserTypeBuilder;
108    final IntTFunction<JsonObject, T>   tripleArgUserTypeBuilder; 
109    final boolean                       builder1Or2;
110
111    // These three are only used for Multi-Dimensional Array-Processing
112    final Function<JsonArray, Object>   array1DGenerator;
113    final boolean                       IN_NSAT, S_NSAT;
114
115
116    // ********************************************************************************************
117    // ********************************************************************************************
118    // Package-Private SettingsRec Constructor
119    // ********************************************************************************************
120    // ********************************************************************************************
121
122
123    // The very last line of this method has a cast of SettingsRec<>
124    @SuppressWarnings("unchecked")
125    SettingsRec(SETTINGS_REC_BUILDER<T, U> b)
126    {
127        // Less typing
128        final BASIC_TYPES<T> basicType = b.basicType;
129
130        // Main stuff
131        this.CLASS              = basicType.CLASS;
132        this.closer             = b.closer();
133        this.acceptor           = new ACCEPTOR<>(b);
134        this.changeableConsumer = b.changeableConsumer();
135
136
137        // Note that nothing could be clobbered here!  For any / all kinds of "consumers", they
138        // always have "No-Op" openers.  Openers were originally solely used for streams.  Now with
139        // "Updateable Consumers," due to the "Get SettingsRec" methods, consumers too now require
140        // an opener.
141        // 
142        // Their version of opener is to reset the "Accept Count"
143
144        this.opener = b.opener();
145
146        // Only used for building Multi-Dimensional Array's (rarely needed)
147        this.array1DGenerator = (b.arrayGen1DRefOrPrimitive() == null)
148            ? null 
149            : GENERATE_1DARRAY.retrieveAppropriateGenerator
150                (this, basicType, b.arrayGen1DRefOrPrimitive().booleanValue());
151
152
153        // This is the "pseudo-hack".  The issue is allowing "EXTENDED_TYPES" to be defined as a 
154        // class which *ACTUALLY* extends the class "BASIC_TYPES".  The two of them have absolutely
155        // zero overlap other than the "output class" (Class<?> CLASS) field.
156        // 
157        // In any case, these three fields are the ones which allow a user to build an object of an
158        // arbitrary, user defined type <T> out of an instance of JsonObject. 
159
160        if (basicType.whichType == BASIC_TYPES.EXTENDED_OBJ)
161        {
162            final EXTENDED_TYPES<T> et = (EXTENDED_TYPES<T>) basicType;
163
164            this.singleArgUserTypeBuilder   = et.singleArgUserTypeBuilder();
165            this.tripleArgUserTypeBuilder   = et.tripleArgUserTypeBuilder(this.acceptor);
166            this.builder1Or2                = et.ONE_ARG_OR_THREE_ARG;
167        }
168
169        else
170        {
171            this.singleArgUserTypeBuilder   = null;
172            this.tripleArgUserTypeBuilder   = null;
173            this.builder1Or2                = false; // compltely irrelevant
174        }
175
176
177        // This is a temporary class that is not used once this constructor has
178        // completed!  Hopefully the garbage collector picks it up as soon as this is finished.
179        // The "FlagsToHandler" actually does a lot of the dispatch for deciding what handler's /
180        // Lambda's are to be used when Processing a JsonArray.
181
182        final FlagsToHandler<T> flagsToHandlers = new FlagsToHandler<>(
183            b.FLAGS,
184            basicType.referenceOrPrimitive,
185            this.acceptor
186        );
187
188        // Two little flags that are used by the Multi-Dimensional Array Processor
189        this.IN_NSAT    = flagsToHandlers.IN_NSAT();
190        this.S_NSAT     = flagsToHandlers.S_NSAT();
191
192        this.handlerNull        = flagsToHandlers.selectNullHandler(this);
193        this.handlerWrongType   = flagsToHandlers.selectWrongTypeHandler();
194
195
196        // "handlerJsonString" is used for both Number-Stuff and Boolean
197        // It is not Used for String or Object...
198        // 
199        // If you want to learn where this is used, just go to "ProcessJsonArray",
200        // and you will see all of these cute little dispatch/handler dealy's hard at work
201
202        this.handlerJsonString =
203
204                (basicType.whichType != BASIC_TYPES.STRING)
205            &&  (basicType.whichType != BASIC_TYPES.EXTENDED_OBJ)
206            &&  (basicType.whichType != BASIC_TYPES.JSON_OBJECT)
207            &&  (basicType.whichType != BASIC_TYPES.JSON_ARRAY)
208
209            ? flagsToHandlers.selectJsonStringHandler(
210                    this,
211                    b.userParser,
212                    basicType.whichType,
213                    basicType.validStrTester,
214                    basicType.defaultParser
215                )
216            : null;
217
218
219        // The field "isNumberType" is only true if this is actually used to convert a
220        // a JsonNumber into a Java-Number.  This will be false for Strings, Objects & Booleans!
221
222        this.handlerNumber = basicType.isNumberType
223            ? ChooseNumberHandler.choose(
224                    this,
225                    this.acceptor,
226                    basicType.whichType,
227                    basicType.referenceOrPrimitive,
228                    basicType.numberConverter,
229                    basicType.numberConverterExThrow,
230                    basicType.jsonNumWillFit,
231                    flagsToHandlers.RJA_AEX(),
232                    flagsToHandlers.selectAEXHandler()
233                )
234            : null;
235
236
237        // TIf you look at "ProcessJsonArray" - this is only used for converting
238        // JsonArray's of String.  All other types will never use this handler.  Therefore, in
239        // any case where a User HAS NOT requested to process a JsonArray of Strings, leaving this
240        // handler as 'null' will have no effect, and it certainly won't cause NullPointerException
241
242        this.jsonStringWrongTypeHandler = (basicType.whichType == BASIC_TYPES.STRING)
243            ? flagsToHandlers.selectJsonStringWrongTypeHandler((SettingsRec<String, ?>) this)
244            : null;
245    }
246
247
248    // ********************************************************************************************
249    // ********************************************************************************************
250    // Set the Consumer to something new
251    // ********************************************************************************************
252    // ********************************************************************************************
253
254
255    /**
256     * Assign a value to the internally used consumer, to which {@link JsonArray} data is emitted
257     * @param c                     <EMBED CLASS='external-html' DATA-FILE-ID=SR_PARAM_C_1> 
258     * @throws WrongModeException   <EMBED CLASS='external-html' DATA-FILE-ID=WRONG_MODE_EX>
259     */
260    public void setConsumer(Consumer<T> c)
261    {
262        assertChangeableConsumer();
263        this.changeableConsumer.setConsumer(c);
264    }
265
266    /**
267     * Assign a value to the internally used consumer, to which {@link JsonArray} data is emitted
268     * @param c                     <EMBED CLASS='external-html' DATA-FILE-ID=SR_PARAM_C_2> 
269     * @throws WrongModeException   <EMBED CLASS='external-html' DATA-FILE-ID=WRONG_MODE_EX>
270     */
271    public void setConsumer(IntIntTConsumer<T> c)
272    {
273        assertChangeableConsumer();
274        this.changeableConsumer.setConsumer(c);
275    }
276
277    private void assertChangeableConsumer()
278    {
279        if (this.changeableConsumer == null) throw new WrongModeException(
280            "This SettingsRec instance was not configured for a Consumer, but rather a Stream " +
281            "or an array."
282        );
283    }
284
285}