001package Torello.JSON;
002
003import Torello.JavaDoc.Annotations.IntoHTMLTable;
004import static Torello.JavaDoc.Annotations.IntoHTMLTable.Background.GreenDither;
005import static Torello.JavaDoc.Annotations.IntoHTMLTable.Background.BlueDither;
006
007import java.util.function.Predicate;
008import java.util.function.ToIntFunction;
009import java.util.function.ToLongFunction;
010import java.util.function.ToDoubleFunction;
011
012import Torello.Java.Function.ToByteFunction;
013import Torello.Java.Function.ToShortFunction;
014import Torello.Java.Function.ToFloatFunction;
015
016import javax.json.JsonObject;
017import javax.json.JsonArray;
018import javax.json.JsonString;
019import javax.json.JsonValue;
020
021import static javax.json.JsonValue.ValueType.STRING;
022import static javax.json.JsonValue.ValueType.NULL;
023
024/**
025 * Builds on the J2EE Standard Release JSON Parsing Tools by providing additional
026 * help with converting JSON Data into <B STYLE='color: red'>Java Primitive-Types</B>
027 * 
028 * <EMBED CLASS='external-html' DATA-FILE-ID=ALL_CLASSES_NOTE>
029 * <EMBED CLASS='external-html' DATA-FILE-ID=PARSE_PRIM_JSON>
030 * <EMBED CLASS='external-html' DATA-FILE-ID=PARSE_PRIM_PTABLE>
031 * <EMBED CLASS='external-html' DATA-CH=char DATA-FILE-ID=JAVA_LANG_CHAR>
032 * 
033 * @see JsonObject
034 * @see JsonArray
035 */
036@Torello.JavaDoc.Annotations.StaticFunctional
037public class ParsePrimJSON
038{
039    // This is a static class.  Has no program state.
040    private ParsePrimJSON() { }
041
042
043    // ********************************************************************************************
044    // ********************************************************************************************
045    // int
046    // ********************************************************************************************
047    // ********************************************************************************************
048
049
050    /** <EMBED CLASS='external-html' DATA-JTYPE=int DATA-FILE-ID=PARSE_PRIM_JA> */
051    @IntoHTMLTable(
052        title="Extract a JsonString from a JsonArray, and Parse into a Java 'int' Primitive",
053        background=BlueDither
054    )
055    public static int parseInt(
056            final JsonArray             ja,
057            final int                   index,
058            final ToIntFunction<String> optionalUserParser
059        )
060    {
061        final JsonString js = GET(ja, index, int.class);
062
063        try
064        {
065            return (optionalUserParser != null)
066                ? optionalUserParser.applyAsInt(js.getString())
067                : Integer.parseInt(js.getString().trim());
068        }
069
070        catch (Exception e)
071            { throw new JsonStrParseArrException(e, ja, index, js, int.class); }
072    }
073
074    /** <EMBED CLASS='external-html' DATA-JTYPE=long DATA-FILE-ID=PARSE_PRIM_JA> */
075    @IntoHTMLTable(
076        title="Extract a JsonString from a JsonArray, and Parse into a Java 'long' Primitive",
077        background=GreenDither
078    )
079    public static long parseLong(
080            final JsonArray                 ja,
081            final int                       index,
082            final ToLongFunction<String>    optionalUserParser
083        )
084    {
085        final JsonString js = GET(ja, index, long.class);
086
087        try
088        {
089            return (optionalUserParser != null)
090                ? optionalUserParser.applyAsLong(js.getString())
091                : Long.parseLong(js.getString().trim());
092        }
093
094        catch (Exception e)
095            { throw new JsonStrParseArrException(e, ja, index, js, long.class); }
096    }
097
098    /** <EMBED CLASS='external-html' DATA-JTYPE=short DATA-FILE-ID=PARSE_PRIM_JA> */
099    @IntoHTMLTable(
100        title="Extract a JsonString from a JsonArray, and Parse into a Java 'short' Primitive",
101        background=BlueDither
102    )
103    public static short parseShort(
104            final JsonArray                 ja,
105            final int                       index,
106            final ToShortFunction<String>   optionalUserParser
107        )
108    {
109        final JsonString js = GET(ja, index, short.class);
110
111        try
112        {
113            return (optionalUserParser != null)
114                ? optionalUserParser.applyAsShort(js.getString())
115                : Short.parseShort(js.getString().trim());
116        }
117
118        catch (Exception e)
119            { throw new JsonStrParseArrException(e, ja, index, js, short.class); }
120    }
121
122    /** <EMBED CLASS='external-html' DATA-JTYPE=byte DATA-FILE-ID=PARSE_PRIM_JA> */
123    @IntoHTMLTable(
124        title="Extract a JsonString from a JsonArray, and Parse into a Java 'byte' Primitive",
125        background=GreenDither
126    )
127    public static byte parseByte(
128            final JsonArray                 ja,
129            final int                       index,
130            final ToByteFunction<String>    optionalUserParser
131        )
132    {
133        final JsonString js = GET(ja, index, byte.class);
134
135        try
136        {
137            return (optionalUserParser != null)
138                ? optionalUserParser.applyAsByte(js.getString())
139                : Byte.parseByte(js.getString().trim());
140        }
141
142        catch (Exception e)
143            { throw new JsonStrParseArrException(e, ja, index, js, byte.class); }
144    }
145
146    /** <EMBED CLASS='external-html' DATA-JTYPE=double DATA-FILE-ID=PARSE_PRIM_JA> */
147    @IntoHTMLTable(
148        title="Extract a JsonString from a JsonArray, and Parse into a Java 'double' Primitive",
149        background=BlueDither
150    )
151    public static double parseDouble(
152            final JsonArray                 ja,
153            final int                       index,
154            final ToDoubleFunction<String>  optionalUserParser
155        )
156    {
157        final JsonString js = GET(ja, index, double.class);
158
159        try
160        {
161            return (optionalUserParser != null)
162                ? optionalUserParser.applyAsDouble(js.getString())
163                : Double.parseDouble(js.getString().trim());
164        }
165
166        catch (Exception e)
167            { throw new JsonStrParseArrException(e, ja, index, js, double.class); }
168    }
169
170    /** <EMBED CLASS='external-html' DATA-JTYPE=float DATA-FILE-ID=PARSE_PRIM_JA> */
171    @IntoHTMLTable(
172        title="Extract a JsonString from a JsonArray, and Parse into a Java 'float' Primitive",
173        background=GreenDither
174    )
175    public static float parseFloat(
176            final JsonArray                 ja,
177            final int                       index,
178            final ToFloatFunction<String>   optionalUserParser
179        )
180    {
181        final JsonString js = GET(ja, index, float.class);
182
183        try
184        {
185            return (optionalUserParser != null)
186                ? optionalUserParser.applyAsFloat(js.getString())
187                : Float.parseFloat(js.getString().trim());
188        }
189
190        catch (Exception e)
191            { throw new JsonStrParseArrException(e, ja, index, js, float.class); }
192    }
193
194    /** <EMBED CLASS='external-html' DATA-JTYPE=boolean DATA-FILE-ID=PARSE_PRIM_JA> */
195    @IntoHTMLTable(
196        title="Extract a JsonString from a JsonArray, and Parse into a Java 'boolean' Primitive",
197        background=BlueDither
198    )
199    public static boolean parseBoolean(
200            final JsonArray         ja,
201            final int               index,
202            final Predicate<String> optionalUserParser
203        )
204    {
205        final JsonString js = GET(ja, index, boolean.class);
206
207        try
208        {
209            return (optionalUserParser != null)
210                ? optionalUserParser.test(js.getString())
211                : Boolean.parseBoolean(js.getString().trim());
212        }
213
214        catch (Exception e)
215            { throw new JsonStrParseArrException(e, ja, index, js, boolean.class); }
216    }
217
218
219    // ********************************************************************************************
220    // ********************************************************************************************
221    // The Internal Methods
222    // ********************************************************************************************
223    // ********************************************************************************************
224
225
226    /**
227     * This is an internal helper method for retrieving an element from a {@link JsonArray},
228     * and converting it to one of the standard <B STYLE='color: red;'>Java Types</B>.
229     * 
230     * @param ja                Any instance of {@link JsonArray}
231     * @param index             Any index into the array which holds a {@link JsonString}
232     * @param primitiveClass    Expected Primitive Class, to be returned by caller method
233     *
234     * @return The extracted {@link JsonString}.
235     * 
236     * @throws JsonNullPrimitiveArrException If the array element at {@code 'index'} contains
237     *                                       <B STYLE='color: red'>Json-Null</B>
238     * @throws JsonTypeArrException         If the array element at {@code 'index'} doesn't contain
239     *                                      {@link JsonString}
240     * @throws IndexOutOfBoundsException    If {@code 'index'} is out of the bounds of {@code 'ja'}
241     * 
242     * @see #parseInt(JsonArray, int, ToIntFunction)
243     * @see #parseLong(JsonArray, int, ToLongFunction)
244     * @see #parseShort(JsonArray, int, ToShortFunction)
245     * @see #parseByte(JsonArray, int, ToByteFunction)
246     * @see #parseDouble(JsonArray, int, ToDoubleFunction)
247     * @see #parseFloat(JsonArray, int, ToFloatFunction)
248     */
249    protected static <T> JsonString GET(
250            final JsonArray ja,
251            final int       index,
252            final Class<T>  primitiveClass
253        )
254    {
255        // This will throw an IndexOutOfBoundsException if the index is out of bounds.
256        final JsonValue jv = ja.get(index);
257
258        switch (jv.getValueType())
259        {
260            // This particular Array-Location (the one specified by 'index') contained an actual
261            // 'null' in its place.  Primitives cannot be assinged null, but fortunately there is
262            // a JsonException descendant class that handles this exact situation.
263
264            case NULL: throw new JsonNullPrimitiveArrException
265                (ja, index, STRING, primitiveClass);
266
267            case STRING: return (JsonString) jv;
268
269            // The JsonValue at the specified array-index does not contain a JsonString.
270            default: throw new JsonTypeArrException
271                (ja, index, STRING, jv, primitiveClass);
272        }
273    }
274
275
276    // ********************************************************************************************
277    // ********************************************************************************************
278    // int
279    // ********************************************************************************************
280    // ********************************************************************************************
281
282
283    /** <EMBED CLASS='external-html' DATA-JTYPE=int DATA-FILE-ID=PARSE_PRIM_JO> */
284    @IntoHTMLTable(
285        title="Extract a JsonString from a JsonObject, and Parse into a Java 'int' Primitive",
286        background=BlueDither
287    )
288    public static int parseInt(
289            final JsonObject            jo,
290            final String                propertyName,
291            final ToIntFunction<String> optionalUserParser
292        )
293    {
294        final JsonString js = GET(jo, propertyName, int.class);
295
296        try
297        {
298            return (optionalUserParser != null)
299                ? optionalUserParser.applyAsInt(js.getString())
300                : Integer.parseInt(js.getString().trim());
301        }
302
303        catch (Exception e)
304            { throw new JsonStrParseObjException(e, jo, propertyName, js, int.class); }
305    }
306
307    /** <EMBED CLASS='external-html' DATA-JTYPE=long DATA-FILE-ID=PARSE_PRIM_JO> */
308    @IntoHTMLTable(
309        title="Extract a JsonString from a JsonObject, and Parse into a Java 'long' Primitive",
310        background=GreenDither
311    )
312    public static long parseLong(
313            final JsonObject                jo,
314            final String                    propertyName,
315            final ToLongFunction<String>    optionalUserParser
316        )
317    {
318        final JsonString js = GET(jo, propertyName, long.class);
319
320        try
321        {
322            return (optionalUserParser != null)
323                ? optionalUserParser.applyAsLong(js.getString())
324                : Long.parseLong(js.getString().trim());
325        }
326
327        catch (Exception e)
328            { throw new JsonStrParseObjException(e, jo, propertyName, js, long.class); }
329    }
330
331    /** <EMBED CLASS='external-html' DATA-JTYPE=short DATA-FILE-ID=PARSE_PRIM_JO> */
332    @IntoHTMLTable(
333        title="Extract a JsonString from a JsonObject, and Parse into a Java 'short' Primitive",
334        background=BlueDither
335    )
336    public static short parseShort(
337            final JsonObject                jo,
338            final String                    propertyName,
339            final ToShortFunction<String>   optionalUserParser
340        )
341    {
342        final JsonString js = GET(jo, propertyName, short.class);
343
344        try
345        {
346            return (optionalUserParser != null)
347                ? optionalUserParser.applyAsShort(js.getString())
348                : Short.parseShort(js.getString().trim());
349        }
350
351        catch (Exception e)
352            { throw new JsonStrParseObjException(e, jo, propertyName, js, short.class); }
353    }
354
355    /** <EMBED CLASS='external-html' DATA-JTYPE=byte DATA-FILE-ID=PARSE_PRIM_JO> */
356    @IntoHTMLTable(
357        title="Extract a JsonString from a JsonObject, and Parse into a Java 'byte' Primitive",
358        background=GreenDither
359    )
360    public static byte parseByte(
361            final JsonObject                jo,
362            final String                    propertyName,
363            final ToByteFunction<String>    optionalUserParser
364        )
365    {
366        final JsonString js = GET(jo, propertyName, byte.class);
367
368        try
369        {
370            return (optionalUserParser != null)
371                ? optionalUserParser.applyAsByte(js.getString())
372                : Byte.parseByte(js.getString().trim());
373        }
374
375        catch (Exception e)
376            { throw new JsonStrParseObjException(e, jo, propertyName, js, byte.class); }
377    }
378
379    /** <EMBED CLASS='external-html' DATA-JTYPE=double DATA-FILE-ID=PARSE_PRIM_JO> */
380    @IntoHTMLTable(
381        title="Extract a JsonString from a JsonObject, and Parse into a Java 'double' Primitive",
382        background=BlueDither
383    )
384    public static double parseDouble(
385            final JsonObject                jo,
386            final String                    propertyName,
387            final ToDoubleFunction<String>  optionalUserParser
388        )
389    {
390        final JsonString js = GET(jo, propertyName, double.class);
391
392        try
393        {
394            return (optionalUserParser != null)
395                ? optionalUserParser.applyAsDouble(js.getString())
396                : Double.parseDouble(js.getString().trim());
397        }
398
399        catch (Exception e)
400            { throw new JsonStrParseObjException(e, jo, propertyName, js, double.class); }
401    }
402
403    /** <EMBED CLASS='external-html' DATA-JTYPE=float DATA-FILE-ID=PARSE_PRIM_JO> */
404    @IntoHTMLTable(
405        title="Extract a JsonString from a JsonObject, and Parse into a Java 'float' Primitive",
406        background=GreenDither
407    )
408    public static float parseFloat(
409            final JsonObject                jo,
410            final String                    propertyName,
411            final ToFloatFunction<String>   optionalUserParser
412        )
413    {
414        final JsonString js = GET(jo, propertyName, float.class);
415
416        try
417        {
418            return (optionalUserParser != null)
419                ? optionalUserParser.applyAsFloat(js.getString())
420                : Float.parseFloat(js.getString().trim());
421        }
422
423        catch (Exception e)
424            { throw new JsonStrParseObjException(e, jo, propertyName, js, float.class); }
425    }
426
427    /** <EMBED CLASS='external-html' DATA-JTYPE=boolean DATA-FILE-ID=PARSE_PRIM_JO> */
428    @IntoHTMLTable(
429        title="Extract a JsonString from a JsonObject, and Parse into a Java 'boolean' Primitive",
430        background=BlueDither
431    )
432    public static boolean parseBoolean(
433            final JsonObject        jo,
434            final String            propertyName,
435            final Predicate<String> optionalUserParser
436        )
437    {
438        final JsonString js = GET(jo, propertyName, boolean.class);
439
440        try
441        {
442            return (optionalUserParser != null)
443                ? optionalUserParser.test(js.getString())
444                : Boolean.parseBoolean(js.getString().trim());
445        }
446
447        catch (Exception e)
448            { throw new JsonStrParseObjException(e, jo, propertyName, js, boolean.class); }
449    }
450
451
452    // ********************************************************************************************
453    // ********************************************************************************************
454    // The Internal Methods
455    // ********************************************************************************************
456    // ********************************************************************************************
457
458
459    /**
460     * This is an internal helper method for retrieving a property from a {@link JsonObject},
461     * and converting it to one of the standard <B STYLE='color: red;'>Java Types</B>.
462     * 
463     * @param jo Any instance of {@link JsonObject}
464     * @param propertyName Any property name contained by {@code 'jo'}, having a {@link JsonString}
465     * @param primitiveClass Expected Primitive Class, to be returned by caller method
466     * 
467     * @return The extracted {@link JsonString}.
468     * 
469     * @throws JsonNullPrimitiveObjException If the specified property contains
470     *                                       <B STYLE='color: red'>Json-Null</B>
471     * @throws JsonTypeObjException          If the specified property doesn't contain
472     *                                       {@link JsonString}
473     * @throws JsonPropMissingException      If the property is missing.
474     * 
475     * @see #parseInt(JsonObject, String, ToIntFunction)
476     * @see #parseLong(JsonObject, String, ToLongFunction)
477     * @see #parseShort(JsonObject, String, ToShortFunction)
478     * @see #parseByte(JsonObject, String, ToByteFunction)
479     * @see #parseDouble(JsonObject, String, ToDoubleFunction)
480     * @see #parseFloat(JsonObject, String, ToFloatFunction)
481     */
482    protected static <T> JsonString GET(
483            final JsonObject    jo,
484            final String        propertyName,
485            final Class<T>      primitiveClass
486        )
487    {
488        // Here, a 'get' request was made for a property that isn't actually listed among the
489        // properties in the provided JsonObject.  Since this internal 'GET' is used by methods
490        // that are trying to return a Java Primitive (like 'int' or 'float'), then an exception
491        // has to be thrown.  The option of returning 'null' isn't possible here!
492    
493        if (! jo.containsKey(propertyName)) throw new JsonPropMissingException
494            (jo, propertyName, STRING, primitiveClass);
495
496        final JsonValue jv = jo.get(propertyName);
497
498        switch (jv.getValueType())
499        {
500            // A Json-"null" was listed as the "value" assigned to this property
501            // Primitive's cannot encode "null."  Fortunately, there is a specialized Java-HTML
502            // JsonException sub-class that handles exactly this type of situation.
503            // 
504            // "someJsonObjectPropert": null,
505
506            case NULL: throw new JsonNullPrimitiveObjException
507                (jo, propertyName, STRING, primitiveClass);
508
509            case STRING: return (JsonString) jv;
510
511            // The JsonObject property does not contain a JsonString.
512            default: throw new JsonTypeObjException
513                (jo, propertyName, STRING, jv, primitiveClass);
514        }
515    }
516
517}