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();
    }
}