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}