001package Torello.Java.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.Java.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
051    // The Main For-Loop Switch-Statement Handlers!
052    // These are all used in the classes: ProcessJsonArray and JSON_ARRAY_DIMN!
053
054    final IntConsumer                   handlerWrongType;
055    final IntConsumer                   handlerNull;
056    final ObjIntConsumer<JsonString>    handlerJsonString;
057    final ObjIntConsumer<JsonNumber>    handlerNumber; // Added Thanksgiving 2024
058    final ObjIntConsumer<JsonValue>     jsonStringWrongTypeHandler;
059
060
061    // Allows the user to provide *ANY* lambda function, not just the constructor of an object
062    // when attempting to handle Json-Array's which contain Objects.  This field will be null in
063    // cases of building this Settings-Record instance - **UNLESS** this instance of SettingsRec is
064    // being constructed for processing an array of JsonObject's.
065    // 
066    // ALSO, as of May 2025: There are officially two variants of the exact same "Static-Builder"
067    // Lambda's / Functional-Interfaces.  BOTH of these fields are only used inside the classes
068    // RJArrIntoStream & RJArrIntoConsumer - and only in the 'objArr' and 'objRec' Methods!!
069    // 
070    // The second 'objBuilder2' was added to allow for writing Static-Builder Methods which also
071    // accept the Json-Array-Index, and the Java-Processed-Output-Index (or whatever you want to 
072    // call the little 'Torello.Java.Additional.Counter' that is inserted inside the builder)
073
074    final Function<JsonObject, T>       objBuilder;
075    final IntTFunction<JsonObject, T>   objBuilder2;
076    final boolean                       builder1Or2;
077
078
079    // Copied directly from Helper-Class BASIC_TYPES, at the very beginning of this class
080    // Constructor-Body
081
082    final Class<T> CLASS;
083
084
085    // This is the top-level "handler" for results that are to be sent to the user
086    // This will either be a consumer that the user himself provided, or it will be a 
087    // Stream-Acceptor method that was produced in a sub-class constructor, and passed here
088    // 
089    // final Consumer<T>       acceptor;
090    // final ObjIntConsumer<T> acceptor2;
091    // final boolean           version1OrVersion2;
092
093    final ObjIntConsumer<T> ACCEPTOR;
094
095
096    // These are only used for Stream's, **NOT** Consumer's
097    // Invokes: Stream.builder(), IntStream.builder(), DoubleStream.builder(), etc...
098
099    final Runnable constructNewBuilder;
100
101
102    // These are only used for Stream's, **NOT** Consumer's
103    // Invokes: Stream.Builder.build(), IntStream.Builder.build(), etc...
104
105    final Supplier<U> runBuilderDotBuild;
106
107
108    // This is applied when the user has opted for a "SettingsRec" that is for a Consumer.  
109    // Remember that generating a SettingsRec speeds things up, by skipping all of the 
110    // configuration computing that goes on.  That's what this class constructor does...
111    // 
112    // When the user generates a Pre-Constructed "SettingsRec" instance, if that record is for a
113    // consumer, then it is extremely likely that he is going to need to update the Conumser that
114    // is used with that Record each and every time that it is used on a different array.
115    // 
116    // As such, as of June 2025, there is a new field called "ChangeableConsumer"
117    // This field will always be null, unless this SettingsRec instance was generated by one of the
118    // "Generate a Consumer Settings Rec" Classes...
119
120    final CHANGEABLE_CONSUMER<T> changeableConsumer;
121
122
123    // This is only used for Multi-Dimensional Array-Processing
124    final Function<JsonArray, Object> array1DGenerator;
125    final boolean IN_NSAT, S_NSAT;
126
127
128    // ********************************************************************************************
129    // ********************************************************************************************
130    // Package-Private SettingsRec Constructor
131    // ********************************************************************************************
132    // ********************************************************************************************
133
134
135    // The very last line of this method has a cast of SettingsRec<>
136    @SuppressWarnings("unchecked")
137    SettingsRec(
138            // Parameters passed by the user to initial query
139            final T                     defaultValue,
140            final int                   FLAGS,
141            final Function<String, T>   userParser,
142
143            // Configuration Stuff (Copied Directly from class "BASIC_TYPES")
144            final BASIC_TYPES<T> bt,
145
146            // Sir, Where would you prefer I shove this data?
147            final Consumer<T>   acceptor,
148            final Runnable      constructNewBuilder,
149            final Supplier<U>   runBuilderDotBuild,
150
151            // acceptor2: The "IntIntTConsumer" variant of an acceptor
152            final IntIntTConsumer<T> acceptor2,
153
154            // The Changeable-Acceptor instance
155            final CHANGEABLE_CONSUMER<T> changeableConsumer,
156
157            // Informs this constructor how to build a 1-Dimensional Array for the
158            // Multi-Dimensional Array-Processor
159
160            final Boolean arrayGen1DRefOrPrimitive
161        )
162    {
163        // Configurations determined by type, from class BASIC_TYES
164        this.CLASS = bt.CLASS;
165
166        // Where to send the data.  Consumer?  Stream-Builder? (and eventually a Stream or Array)
167        // this.acceptor = acceptor;
168        this.constructNewBuilder    = constructNewBuilder;
169        this.runBuilderDotBuild     = runBuilderDotBuild;
170
171
172        // Only used in conjunction with a Consumer-Based Settings-Rec.  This field will be null
173        // for all cases, except those in which the User has generated his own copy of this 
174        // SettingsRec class - and it is for a Consumer - not a Stream or an Array.
175    
176        this.changeableConsumer = changeableConsumer;
177
178    
179        // This cute little thing is a "Later Addition".  It provides the second "suite" of methods
180        // in the RJ Into-Consumer Series.  The user now has the option to provide a consumer that 
181        // accepts the Array-Indices as well.
182        // 
183        // Also note, that this concept is "identical" to the Stream-Object method (inside class
184        // RJArrIntoStream) which allows for a Static-Builder Lambda that accepts a Json-Object and
185        // the Java Array-Indices.  However, in this portion of the code, this concept is used,
186        // *STRICTLY* for the classes:
187        // RJArrIntoConsumer, RJArrIntoBoxedConsumer, RJArrIntoPrimConsumer
188
189        final boolean version1OrVersion2 = (acceptor2 == null);
190
191        if (version1OrVersion2)
192            this.ACCEPTOR = (T theValue, int ignoreTheJsonArrIndex) -> acceptor.accept(theValue);
193        else
194        {
195            final Counter javaArrayIndex = new Counter(-1);
196
197            this.ACCEPTOR = (T theValue, int jsonArrayIndex) ->
198                acceptor2.accept(jsonArrayIndex, javaArrayIndex.addOne(), theValue);
199        }
200
201
202        // Only used for building Multi-Dimensional Array's (rarely needed)
203        this.array1DGenerator = (arrayGen1DRefOrPrimitive == null)
204            ? null 
205            : GENERATE_1DARRAY.retrieveAppropriateGenerator(this, bt, arrayGen1DRefOrPrimitive);
206
207
208        // This is the "new thing".  It allows *ANY* lambda (not just a constructor) to produce
209        // an Object of type 'T'  This field is *ALWAYS* null - *UNLESS* this instance of
210        // SettingsRec is being configured to handle Json-Array's which contains objects (not one
211        // of the standard primitive, number or string types).
212        // 
213        // Note the "Marker Boolean" is computed because these are used inside of a loop.  
214        // Also, by default, if the user isn't asking for the Objects to be built, then this 
215        // booean to be assigned 'false', because - in such cases - it just wouldn't matter in the
216        // slightest.
217
218        this.objBuilder     = bt.objBuilder;
219        this.objBuilder2    = bt.objBuilder2;
220        this.builder1Or2    = (bt.objBuilder != null);
221
222
223        // This is a temporary class that is not used once this constructor has
224        // completed!  Hopefully the garbage collector picks it up as soon as this is finished.
225        // The "TempFlags" actually does a lot of the dispatch for deciding what handler's /
226        // Lambda's are to be used when Processing a JsonArray.
227
228        final TempFlags flags = new TempFlags(
229            FLAGS,
230            bt.referenceOrPrimitive,
231            new Lambdas<T>(this.ACCEPTOR, defaultValue)
232        );
233
234        // Two little flags that are used by the Multi-Dimensional Array Processor
235        this.IN_NSAT    = flags.IN_NSAT();
236        this.S_NSAT     = flags.S_NSAT();
237
238        this.handlerNull        = flags.selectNullHandler(this);
239        this.handlerWrongType   = flags.selectWrongTypeHandler();
240
241
242        // "handlerJsonString" is used for both Number-Stuff and Boolean
243        // It is not Used for String or Object...
244        // 
245        // If you want to learn where this is used, just go to "ProcessJsonArray",
246        // and you will see all of these cute little dispatch/handler dealy's hard at work
247
248        this.handlerJsonString =
249            (bt.whichType != BASIC_TYPES.STRING) && (bt.whichType != BASIC_TYPES.OBJECT)
250            ? flags.selectJsonStringHandler(
251                    this,
252                    userParser,
253                    bt.whichType,
254                    bt.validStrTester,
255                    bt.defaultParser
256                )
257            : null;
258
259
260        // The field "isNumberType" is only true if this is actually used to convert a
261        // a JsonNumber into a Java-Number.  This will be false for Strings, Objects & Booleans!
262
263        this.handlerNumber = bt.isNumberType
264            ? ChooseNumberHandler.choose(
265                    this,
266                    this.ACCEPTOR,
267                    bt.whichType,
268                    bt.referenceOrPrimitive,
269                    bt.numberConverter,
270                    bt.numberConverterExThrow,
271                    bt.jsonNumWillFit,
272                    flags.RJA_AEX(),
273                    flags.selectAEXHandler()
274                )
275            : null;
276
277
278        // TIf you look at "ProcessJsonArray" - this is only used for converting
279        // JsonArray's of String.  All other types will never use this handler.  Therefore, in
280        // any case where a User HAS NOT requested to process a JsonArray of Strings, leaving this
281        // handler as 'null' will have no effect, and it certainly won't cause NullPointerException
282
283        this.jsonStringWrongTypeHandler = (bt.whichType == BASIC_TYPES.STRING)
284            ? flags.selectJsonStringWrongTypeHandler((SettingsRec<String, ?>) this)
285            : null;
286    }
287
288
289    // ********************************************************************************************
290    // ********************************************************************************************
291    // Set the Consumer to something new
292    // ********************************************************************************************
293    // ********************************************************************************************
294
295
296    /**
297     * Assign a value to the internally used consumer, to which {@link JsonArray} data is emitted
298     * @param c                     <EMBED CLASS='external-html' DATA-FILE-ID=SR_PARAM_C_1> 
299     * @throws WrongModeException   <EMBED CLASS='external-html' DATA-FILE-ID=WRONG_MODE_EX>
300     */
301    public void setConsumer(Consumer<T> c)
302    {
303        assertChangeableConsumer();
304        this.changeableConsumer.setConsumer(c);
305    }
306
307    /**
308     * Assign a value to the internally used consumer, to which {@link JsonArray} data is emitted
309     * @param c                     <EMBED CLASS='external-html' DATA-FILE-ID=SR_PARAM_C_2> 
310     * @throws WrongModeException   <EMBED CLASS='external-html' DATA-FILE-ID=WRONG_MODE_EX>
311     */
312    public void setConsumer(IntIntTConsumer<T> c)
313    {
314        assertChangeableConsumer();
315        this.changeableConsumer.setConsumer(c);
316    }
317
318    private void assertChangeableConsumer()
319    {
320        if (this.changeableConsumer == null) throw new WrongModeException(
321            "This SettingsRec instance was not configured for a Consumer, but rather a Stream " +
322            "or an array."
323        );
324    }
325
326}