001package Torello.JSON;
002
003import javax.json.*;
004
005import java.lang.reflect.Constructor;
006import java.lang.reflect.Modifier;
007
008import java.math.*;
009
010import static javax.json.JsonValue.ValueType.*;
011import static Torello.JSON.JFlag.*;
012
013import java.util.Objects;
014import java.util.function.Function;
015
016/**
017 * Class which provides a series of helper functions for all JSON Type-Binding Reader 
018 * Classes.
019 * 
020 * <BR /><BR /><DIV CLASS=JDHint>
021 * 100% of the helper-methods that appear here are protected, and cannot be accessed
022 * outside of this package.  They are included in the documentation solely for the purposes of
023 * (<I>if you happen to be interested</I>) letting you know how the JSON-Tools work.  It is not
024 * intended that programmers would ever need to invoke, directly, any of the methods in this
025 * class!
026 * </DIV>
027 */
028@Torello.JavaDoc.Annotations.StaticFunctional
029public class RJInternal
030{
031    private RJInternal() { }
032
033
034    // ********************************************************************************************
035    // ********************************************************************************************
036    // "Helpers for the Helpers for the Helpers"
037    // ********************************************************************************************
038    // ********************************************************************************************
039
040
041    private static void throwAE_INFINITY
042        (JsonNumber jn, String primTypeName, boolean positiveOrNegative)
043    {
044        throw new ArithmeticException(
045            "When attempting to conver the JsonNumber [" + jn.toString() + "] to a " +
046            primTypeName + " primitive, the number had a magnitude that was too large: " +
047            (positiveOrNegative ? "Positive" : "Negative") + " Infinity was returned."
048        );
049    }
050
051    private static void throwAE_INFINITY(BigDecimal bd, String primTypeName)
052    {
053        throw new ArithmeticException(
054            "When attempting to conver the JsonNumber [" + bd.toString() + "] to a " +
055            primTypeName + " primitive, the number had a magnitude that was infinite."
056        );
057    }
058
059    private static void throwAE_PRECISION(BigDecimal bd, String primTypeName)
060    {
061        throw new ArithmeticException(
062            "When attempting to conver the JsonNumber [" + bd.toString() + "] to a " +
063            primTypeName + " primitive, the number had a loss of precision."
064        );
065    }
066
067    /*
068     * Converts a {@link JsonNumber} into a Java {@code double}
069     * @param jn Any {@link JsonNumber}
070     * @return java {@code double} primitive
071     * @throws ArithmeticException If infinity is returned from the call to
072     * {@code BigDecimal.doubleValue()}
073     * @see JsonNumber#bigDecimalValue()
074     */
075    protected static double DOUBLE_WITH_CHECK(JsonNumber jn)
076    { return DOUBLE_WITH_CHECK(jn.bigDecimalValue()); }
077
078    /**
079     * Converts a {@code BigDecimal} into a Java {@code double}
080     * @param bd Any {@code BigDecimal}
081     * @return Java {@code double} primitive
082     * @throws ArithmeticException If infinity is returned from the call to
083     * {@code code BigDecimal.doubleValue()}
084     */
085    protected static double DOUBLE_WITH_CHECK(BigDecimal bd)
086    {
087        double ret = bd.doubleValue();
088
089        if (Double.isInfinite(ret)) throwAE_INFINITY(bd, "double");
090
091        if (BigDecimal.valueOf(ret).compareTo(bd) != 0) throwAE_PRECISION(bd, "double");
092
093        return ret;
094    }
095
096    /**
097     * Converts a {@link JsonNumber} into a Java {@code float}
098     * @param jn Any {@link JsonNumber}
099     * @return java {@code float} primitive
100     * @throws ArithmeticException If infinity is returned from the call to
101     * {@code BigDecimal.floatValue()}
102     * @see JsonNumber#bigDecimalValue()
103     */
104    protected static float FLOAT_WITH_CHECK(JsonNumber jn)
105    { return FLOAT_WITH_CHECK(jn.bigDecimalValue()); }
106
107    /**
108     * Converts a {@code BigDecimal} into a Java {@code float}
109     * @param bd Any {@code BigDecimal}
110     * @return Java {@code float} primitive
111     * @throws ArithmeticException If infinity is returned from the call to
112     * {@code code BigDecimal.floatValue()}
113     */
114    protected static float FLOAT_WITH_CHECK(BigDecimal bd)
115    {
116        float ret = bd.floatValue();
117
118        if (Float.isInfinite(ret)) throwAE_INFINITY(bd, "float");
119
120        if (BigDecimal.valueOf(ret).compareTo(bd) != 0) throwAE_PRECISION(bd, "float");
121
122        return ret;
123    }
124
125    /**
126     * Converts a {@code long} into a Java {@code byte}
127     * @param l A long that has been produced by {@link JsonNumber#longValueExact()}
128     * @return A valid {@code byte} primitive
129     * @throws ArithmeticException If the {@code long} doesn't fit into a {@code byte}
130     */
131    protected static byte BYTE_FROM_LONG(final long l)
132    {
133        if ((l < Byte.MIN_VALUE) || (l > Byte.MAX_VALUE))
134            throw new ArithmeticException("byte out of range: " + l);
135
136        return (byte) l;
137    }
138
139    /**
140     * Converts a {@code long} into a Java {@code short}
141     * @param l A long that has been produced by {@link JsonNumber#longValueExact()}
142     * @return A valid {@code short} primitive
143     * @throws ArithmeticException If the {@code long} doesn't fit into a {@code short}
144     */
145    protected static short SHORT_FROM_LONG(final long l)
146    {
147        if ((l < Short.MIN_VALUE) || (l > Short.MAX_VALUE))
148            throw new ArithmeticException("short out of range: " + l);
149
150        return (short) l;
151    }
152
153
154    // ********************************************************************************************
155    // ********************************************************************************************
156    // FLAG-CHECKER METHODS another section of "Helpers for the Helpers ..."
157    // ********************************************************************************************
158    // ********************************************************************************************
159
160
161    /**
162     * Flag Checker for {@code IndexOutOfBoundsException}.
163     * 
164     * <BR /><BR />Checks whether the relevant flags were set in the users {@code FLAGS} parameter,
165     * and either returns the appropriate value accordingly, or throws
166     * {@code IndexOutOfBoundsException}.
167     * 
168     * <EMBED CLASS='external-html' DATA-FILE-ID=FLAG_PRECEDENCE>
169     * 
170     * @param <T> If requested, the default-value is returned, and this is its type.
171     * 
172     * @return Can return either the user-provided default-value, or null depending on whether a
173     * match was found in the user's request settings ({@code 'FLAGS'}).
174     * 
175     * @throws IndexOutOfBoundsException If no flag was set specifying one of the two return-value
176     * options.
177     * 
178     * @see JFlag#RETURN_NULL_ON_IOB
179     * @see JFlag#RETURN_DEFVAL_ON_IOB
180     * @see JFlag#RETURN_NULL_ON_ANY_ALL
181     * @see JFlag#RETURN_DEFVAL_ON_ANY_ALL
182     */
183    protected static <T> T IOOBEX(JsonArray ja, int index, T defaultValue, int FLAGS)
184    {
185        if ((FLAGS & RETURN_NULL_ON_IOB) != 0)          return null;
186        if ((FLAGS & RETURN_DEFVAL_ON_IOB) != 0)        return defaultValue;
187        if ((FLAGS & RETURN_NULL_ON_ANY_ALL) != 0)      return null;
188        if ((FLAGS & RETURN_DEFVAL_ON_ANY_ALL) != 0)    return defaultValue;
189
190        ja.get(index); // Throws an IndexOutOfBoundsException
191
192        // If you have reached this statment, this method was not applied properly
193        throw new Torello.Java.UnreachableError();
194    }
195
196    /**
197     * Flag Checker for {@link JsonPropMissingException}
198     * 
199     * <BR /><BR />Checks whether the relevant flags were set in the users {@code FLAGS} parameter,
200     * and either returns the appropriate value accordingly, or throws
201     * {@code JsonPropMissingException}.
202     * 
203     * <EMBED CLASS='external-html' DATA-FILE-ID=FLAG_PRECEDENCE>
204     * 
205     * @param <T> If requested, the default-value is returned, and this is its type.
206     * 
207     * @return Can return either the user-provided default-value, or null depending on whether a
208     * match was found in the user's request settings ({@code 'FLAGS'}).
209     * 
210     * @throws JsonPropMissingException If no flag was set specifying one of the two return-value
211     * options.
212     * 
213     * @see JFlag#RETURN_NULL_ON_MISSING
214     * @see JFlag#RETURN_DEFVAL_ON_MISSING
215     * @see JFlag#RETURN_NULL_ON_ANY_ALL
216     * @see JFlag#RETURN_DEFVAL_ON_ANY_ALL
217     */
218    protected static <T> T JPMEX(
219            JsonObject jo, String propertyName, T defaultValue, int FLAGS,
220            JsonValue.ValueType expectedType, Class<T> returnClass
221        )
222    {
223        if ((FLAGS & RETURN_NULL_ON_MISSING) != 0)      return null;
224        if ((FLAGS & RETURN_DEFVAL_ON_MISSING) != 0)    return defaultValue;
225        if ((FLAGS & RETURN_NULL_ON_ANY_ALL) != 0)      return null;
226        if ((FLAGS & RETURN_DEFVAL_ON_ANY_ALL) != 0)    return defaultValue;
227
228        throw new JsonPropMissingException(jo, propertyName, expectedType, returnClass);
229    }
230
231    /**
232     * Flag Checker for {@link JsonNullArrException}
233     * 
234     * <BR /><BR />Checks whether the relevant flags were set in the users {@code FLAGS} parameter,
235     * and either returns the appropriate value accordingly, or throws
236     * {@code JsonNullArrException}.
237     * 
238     * <EMBED CLASS='external-html' DATA-FILE-ID=FLAG_PRECEDENCE>
239     * 
240     * @param <T> If requested, the default-value is returned, and this is its type.
241     * 
242     * @return Can return either the user-provided default-value, or null depending on whether a
243     * match was found in the user's request settings ({@code 'FLAGS'}).
244     * 
245     * @throws JsonNullArrException If no flag was set specifying one of the two return-value
246     * options.
247     * 
248     * @see JFlag#RETURN_NULL_ON_NULL
249     * @see JFlag#RETURN_DEFVAL_ON_NULL
250     * @see JFlag#RETURN_NULL_ON_ANY_ALL
251     * @see JFlag#RETURN_DEFVAL_ON_ANY_ALL
252     */
253    protected static <T> T JNAEX(
254            JsonArray ja, int index, T defaultValue, int FLAGS, JsonValue.ValueType expectedType,
255            Class<T> returnClass
256        )
257    {
258        if ((FLAGS & RETURN_NULL_ON_NULL) != 0)         return null;
259        if ((FLAGS & RETURN_DEFVAL_ON_NULL) != 0)       return defaultValue;
260        if ((FLAGS & RETURN_NULL_ON_ANY_ALL) != 0)      return null;
261        if ((FLAGS & RETURN_DEFVAL_ON_ANY_ALL) != 0)    return defaultValue;
262
263        throw new JsonNullArrException(ja, index, expectedType, returnClass);
264    }
265
266    /**
267     * Flag Checker for {@link JsonNullObjException}
268     * 
269     * <BR /><BR />Checks whether the relevant flags were set in the users {@code FLAGS} parameter,
270     * and either returns the appropriate value accordingly, or throws
271     * {@code JsonNullObjException}.
272     * 
273     * <EMBED CLASS='external-html' DATA-FILE-ID=FLAG_PRECEDENCE>
274     * 
275     * @param <T> If requested, the default-value is returned, and this is its type.
276     * 
277     * @return Can return either the user-provided default-value, or null depending on whether a
278     * match was found in the user's request settings ({@code 'FLAGS'}).
279     * 
280     * @throws JsonNullObjException If no flag was set specifying one of the two return-value
281     * options.
282     * 
283     * @see JFlag#RETURN_NULL_ON_NULL
284     * @see JFlag#RETURN_DEFVAL_ON_NULL
285     * @see JFlag#RETURN_NULL_ON_ANY_ALL
286     * @see JFlag#RETURN_DEFVAL_ON_ANY_ALL
287     */
288    protected static <T> T JNOEX(
289            JsonObject jo, String propertyName, T defaultValue, int FLAGS,
290            JsonValue.ValueType expectedType, Class<T> returnClass
291        )
292    {
293        if ((FLAGS & RETURN_NULL_ON_NULL) != 0)         return null;
294        if ((FLAGS & RETURN_DEFVAL_ON_NULL) != 0)       return defaultValue;
295        if ((FLAGS & RETURN_NULL_ON_ANY_ALL) != 0)      return null;
296        if ((FLAGS & RETURN_DEFVAL_ON_ANY_ALL) != 0)    return defaultValue;
297
298        throw new JsonNullObjException(jo, propertyName, expectedType, returnClass);
299    }
300
301    /**
302     * Flag Checker for {@link JsonTypeArrException}
303     * 
304     * <BR /><BR />Checks whether the relevant flags were set in the users {@code FLAGS} parameter,
305     * and either returns the appropriate value accordingly, or throws
306     * {@code JsonTypeArrException}.
307     * 
308     * <EMBED CLASS='external-html' DATA-FILE-ID=FLAG_PRECEDENCE>
309     * 
310     * @param <T> If requested, the default-value is returned, and this is its type.
311     * 
312     * @return Can return either the user-provided default-value, or null depending on whether a
313     * match was found in the user's request settings ({@code 'FLAGS'}).
314     * 
315     * @throws JsonTypeArrException If no flag was set specifying one of the two return-value
316     * options.
317     * 
318     * @see JFlag#RETURN_NULL_ON_WRONG_JSONTYPE
319     * @see JFlag#RETURN_DEFVAL_ON_WRONG_JSONTYPE
320     * @see JFlag#RETURN_NULL_ON_ANY_ALL
321     * @see JFlag#RETURN_DEFVAL_ON_ANY_ALL
322     */
323    protected static <T> T JTAEX(
324            JsonArray ja, int index, T defaultValue, int FLAGS, JsonValue.ValueType expectedType,
325            JsonValue retrievedValue, Class<T> returnClass
326        )
327    {
328        if ((FLAGS & RETURN_NULL_ON_WRONG_JSONTYPE) != 0)   return null;
329        if ((FLAGS & RETURN_DEFVAL_ON_WRONG_JSONTYPE) != 0) return defaultValue;
330        if ((FLAGS & RETURN_NULL_ON_ANY_ALL) != 0)          return null;
331        if ((FLAGS & RETURN_DEFVAL_ON_ANY_ALL) != 0)        return defaultValue;
332
333        throw new JsonTypeArrException(ja, index, expectedType, retrievedValue, returnClass);
334    }
335
336    /**
337     * Flag Checker for {@link JsonTypeObjException}
338     * 
339     * <BR /><BR />Checks whether the relevant flags were set in the users {@code FLAGS} parameter,
340     * and either returns the appropriate value accordingly, or throws
341     * {@code JsonNullObjException}.
342     * 
343     * <EMBED CLASS='external-html' DATA-FILE-ID=FLAG_PRECEDENCE>
344     * 
345     * @param <T> If requested, the default-value is returned, and this is its type.
346     * 
347     * @return Can return either the user-provided default-value, or null depending on whether a
348     * match was found in the user's request settings ({@code 'FLAGS'}).
349     * 
350     * @throws JsonNullObjException If no flag was set specifying one of the two return-value
351     * options.
352     * 
353     * @see JFlag#RETURN_NULL_ON_WRONG_JSONTYPE
354     * @see JFlag#RETURN_DEFVAL_ON_WRONG_JSONTYPE
355     * @see JFlag#RETURN_NULL_ON_ANY_ALL
356     * @see JFlag#RETURN_DEFVAL_ON_ANY_ALL
357     */
358    protected static <T> T JTOEX(
359            JsonObject jo, String propertyName, T defaultValue, int FLAGS,
360            JsonValue.ValueType expectedType, JsonValue retrievedValue, Class<T> returnClass
361        )
362    {
363        if ((FLAGS & RETURN_NULL_ON_WRONG_JSONTYPE) != 0)   return null;
364        if ((FLAGS & RETURN_DEFVAL_ON_WRONG_JSONTYPE) != 0) return defaultValue;
365        if ((FLAGS & RETURN_NULL_ON_ANY_ALL) != 0)          return null;
366        if ((FLAGS & RETURN_DEFVAL_ON_ANY_ALL) != 0)        return defaultValue;
367
368        throw new JsonTypeObjException
369            (jo, propertyName, expectedType, retrievedValue, returnClass);
370    }
371
372    /**
373     * Flag Checker for {@link JsonStrParseArrException}
374     * 
375     * <BR /><BR />Checks whether the relevant flags were set in the users {@code FLAGS} parameter,
376     * and either returns the appropriate value accordingly, or throws
377     * {@code JsonStrParseArrException}.
378     * 
379     * <EMBED CLASS='external-html' DATA-FILE-ID=FLAG_PRECEDENCE>
380     * 
381     * @param <T> If requested, the default-value is returned, and this is its type.
382     * 
383     * @return Can return either the user-provided default-value, or null depending on whether a
384     * match was found in the user's request settings ({@code 'FLAGS'}).
385     * 
386     * @throws JsonStrParseArrException If no flag was set specifying one of the two return-value
387     * options.
388     * 
389     * @see JFlag#RETURN_NULL_ON_SPEX
390     * @see JFlag#RETURN_DEFVAL_ON_SPEX
391     * @see JFlag#RETURN_NULL_ON_ANY_ALL
392     * @see JFlag#RETURN_DEFVAL_ON_ANY_ALL
393     */
394    protected static <T> T JSPAEX(
395            Exception e, JsonArray ja, int index, T defaultValue, int FLAGS,
396            JsonValue retrievedValue, Class<T> returnClass
397        )
398    {
399        if ((FLAGS & RETURN_NULL_ON_SPEX) != 0)         return null;
400        if ((FLAGS & RETURN_DEFVAL_ON_SPEX) != 0)       return defaultValue;
401        if ((FLAGS & RETURN_NULL_ON_ANY_ALL) != 0)      return null;
402        if ((FLAGS & RETURN_DEFVAL_ON_ANY_ALL) != 0)    return defaultValue;
403
404        throw new JsonStrParseArrException(e, ja, index, retrievedValue, returnClass);
405    }
406
407    /**
408     * Flag Checker for {@link JsonStrParseObjException}
409     * 
410     * <BR /><BR />Checks whether the relevant flags were set in the users {@code FLAGS} parameter,
411     * and either returns the appropriate value accordingly, or throws
412     * {@code JsonStrParseObjException}.
413     * 
414     * <EMBED CLASS='external-html' DATA-FILE-ID=FLAG_PRECEDENCE>
415     * 
416     * @param <T> If requested, the default-value is returned, and this is its type.
417     * 
418     * @return Can return either the user-provided default-value, or null depending on whether a
419     * match was found in the user's request settings ({@code 'FLAGS'}).
420     * 
421     * @throws JsonStrParseObjException If no flag was set specifying one of the two return-value
422     * options.
423     * 
424     * @see JFlag#RETURN_NULL_ON_SPEX
425     * @see JFlag#RETURN_DEFVAL_ON_SPEX
426     * @see JFlag#RETURN_NULL_ON_ANY_ALL
427     * @see JFlag#RETURN_DEFVAL_ON_ANY_ALL
428     */
429    protected static <T> T JSPOEX(
430            Exception e, JsonObject jo, String propertyName, T defaultValue, int FLAGS,
431            JsonValue retrievedValue, Class<T> returnClass
432        )
433    {
434        if ((FLAGS & RETURN_NULL_ON_SPEX) != 0)         return null;
435        if ((FLAGS & RETURN_DEFVAL_ON_SPEX) != 0)       return defaultValue;
436        if ((FLAGS & RETURN_NULL_ON_ANY_ALL) != 0)      return null;
437        if ((FLAGS & RETURN_DEFVAL_ON_ANY_ALL) != 0)    return defaultValue;
438
439        throw new JsonStrParseObjException(e, jo, propertyName, retrievedValue, returnClass);
440    }
441
442
443}