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 | package Torello.Java.JSON; import Torello.Java.Function.FloatConsumer; import java.util.function.IntConsumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.DoubleConsumer; import java.util.function.ObjIntConsumer; import java.math.BigDecimal; import javax.json.JsonNumber; import static javax.json.JsonValue.ValueType.NUMBER; // This class in invoked ONLY once. The SettingsRec<T, U> Constructor calls it. // Once this class a returned a "Json-Number Handler", only the selected handler // itself will ever be invoked during the Main-Loop-Processing // // Though this may look like the most ascinine thing that has ever been written // in Java. Over a period of three years it has, indeed, been testing and retested // to the point of being extremely feasible. // // All this class does is generate an "Object-Int-Consumer". // This "Consumer" is just a method that accepts: // // 1) JsonNumber // 2) Array-Index as an Integer (the 'int i' you see in the Lambda-Expressions) // // All this conumer does is generate a Valid Java-Number, and insert it into the // user's consumer. If the user has actually requested a "Stream<SomeNumberType>", // Then the Stream.Builder<SomeType> is converted into a consumer and used by the // Lambda's inside this method. // // // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // The 'ja' instance declared in class "SettingsRec" **IS NOT** declared final. It can be reset ! // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // // IMPORTANT NOTE: The 'ja' that is passed to the Functional-Interface Implementatons which are // constructed in this method is "de-referenced" from the SettingsRec -- It **IS NOT** passed as // a parameter to this method. If it **WERE NOT** retrieved from the 'SettingsRec', then 'sr' // instance, then the 'ja' that would be used would be permanently-solidified to the 'ja' // referenced that were passed right at the beginning, upon construction of the SettingsRec // instance. class ChooseNumberHandler { // handlerNumber: **ONLY USED BY** ProcessJsonArray.numericToJava // // This makes the method ProcessJsonArray.numericJsonArrayTo look a million times better // It is also slightly more efficient this way... slightly - since the 'if-statements' // are "outside" of the lambda's, not inside. @SuppressWarnings({"unchecked"}) // Consumer<Number> c = (Consumer<Number>) this.acceptor; static <T> ObjIntConsumer<JsonNumber> choose( final SettingsRec<T, ?> sr, final ObjIntConsumer<T> ACCEPTOR, final byte whichType, // BASIC_TYPES: bt.whichType, final boolean refOrPrim, // BASIC_TYPES: bt.referenceOrPrimitive, final Function<Number, T> numberConverter, // BASIC_TYPES: bt.numberConverter, final Function<BigDecimal, T> numberConverterExThrow, // BASIC_TYPES: bt.jsonNumWillFit final Predicate<JsonNumber> jsonNumWillFit, // BASIC_TYPES: bt.numberConverterExThrow, final boolean RJA_AEX, final IntConsumer handlerAEX ) { // The Type-Checking Downside... There is no way I can 'prove' that this // Consumer *REALLY* is a 'Number' consumer... but it must be! if (whichType == BASIC_TYPES.NUMBER) return (JsonNumber jn, int i) -> ((ObjIntConsumer<Number>) ACCEPTOR).accept(RJInternal.convertToNumber(jn), i); else if (RJA_AEX) return (JsonNumber jn, int i) -> ACCEPTOR.accept(numberConverter.apply(jn.numberValue()), i); else if (handlerAEX == null) { if (whichType == BASIC_TYPES.FLOAT) return (JsonNumber jn, int i) -> { // IMPORTANT NOTE: See the comment at the top of this class regarding the 'sr.ja' // versus 'ja' (being passed as a parameter to this method). if (! handleFloat(ACCEPTOR, jn, i)) throw new JsonArithmeticArrException( ChooseNumberHandler.getAEX(jn, numberConverterExThrow), sr.ja, i, NUMBER, /* used to be 'jv' */ jn, sr.CLASS ); }; else if (whichType == BASIC_TYPES.DOUBLE) return (JsonNumber jn, int i) -> { // IMPORTANT NOTE: See the comment at the top of this class regarding the 'sr.ja' // versus 'ja' (being passed as a parameter to this method). if (! handleDouble(ACCEPTOR, jn, i)) throw new JsonArithmeticArrException( ChooseNumberHandler.getAEX(jn, numberConverterExThrow), sr.ja, i, NUMBER, jn, sr.CLASS ); }; else return (JsonNumber jn, int i) -> { if (jsonNumWillFit.test(jn)) ACCEPTOR.accept(numberConverter.apply(jn.numberValue()), i); // IMPORTANT NOTE: See the comment at the top of this class regarding the 'sr.ja' // versus 'ja' (being passed as a parameter to this method). else throw new JsonArithmeticArrException( ChooseNumberHandler.getAEX(jn, numberConverterExThrow), sr.ja, i, NUMBER, /* used to be 'jv' */ jn, sr.CLASS ); }; } else { if (whichType == BASIC_TYPES.FLOAT) return (JsonNumber jn, int i) -> { if (! handleFloat(ACCEPTOR, jn, i)) handlerAEX.accept(i); }; else if (whichType == BASIC_TYPES.DOUBLE) return (JsonNumber jn, int i) -> { if (! handleDouble(ACCEPTOR, jn, i)) handlerAEX.accept(i); }; else return (JsonNumber jn, int i) -> { if (jsonNumWillFit.test(jn)) ACCEPTOR.accept(numberConverter.apply(jn.numberValue()), i); else handlerAEX.accept(i); }; } } // As a part of the Java-HTML experience is the need to make sure that the types // which are converted do not accidentally do any "rounding" or "truncation" unless the user // has explicitly requested it. // // This method simply puts a JsonNumber into a Double-Consumer.... // **HOWEVER** It checks that no rounding & truncation is necesary before doing this. // // NOTE: The Class "PREDICATES" hasL // * shortTypePred // * longTypePred // * byteTypePred // // Note that the *ONLY WAY* to actually check if the `BigDecimal.doubleValue` is actually // returning a number that has been properly fit into a Valid Double-Value is to go ahead and // generate the `Double`, and the check (using the static BigDecimal.valueOf(d) Method) to // convert it *BACK INTO A BigDecimal INSTANCE*. // // As a Result, since the 'double' is generated during the processing of checking whether the // double is a Valid-Conversion or not, it would be somewhat inefficient to do this conversion // **TWICE** - Once in the class PREDICATES, and then a Second-Time (dircectly) above in the // 'choose(..)' method. @SuppressWarnings({"rawtypes", "unchecked"}) private static boolean handleDouble (final ObjIntConsumer ACCEPTOR, final JsonNumber jn, final int i) { BigDecimal bd = jn.bigDecimalValue(); double d = bd.doubleValue(); if ((!Double.isInfinite(d)) && BigDecimal.valueOf(d).compareTo(bd) == 0) { ((ObjIntConsumer<Double>) ACCEPTOR).accept(d, i); return true; } return false; } // Same as above, but for Float's instead of Double's. @SuppressWarnings({"rawtypes", "unchecked"}) private static boolean handleFloat (final ObjIntConsumer ACCEPTOR, final JsonNumber jn, final int i) { BigDecimal bd = jn.bigDecimalValue(); float f = bd.floatValue(); if ((!Float.isInfinite(f)) && BigDecimal.valueOf(f).compareTo(bd) == 0) { ((ObjIntConsumer<Float>) ACCEPTOR).accept(f, i); return true; } return false; } // NOTE: It is **A LOT** Smarter to check for the exception instead of using the try-catch // since I have read that generating an exception is one of the most costly-expensive // things the JRE does. It isn't the Exception-Constructor that costs a lot - it is the // creating of the **STACK-TRACE** that costs well over 100 to 1,000 times the number of // of CPU-Cycles that the cost of a simple constructor. This is an "Only if abolutely" // necessary things. private static <T extends Number> ArithmeticException getAEX (JsonNumber jn, Function<BigDecimal, ?> converter) { try { converter.apply(jn.bigDecimalValue()); } catch (ArithmeticException aex) { return aex; } throw new Torello.Java.UnreachableError(); } } |