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}