001package Torello.Java.JSON;
002
003import javax.json.*;
004import java.lang.reflect.*;
005import java.math.*;
006
007import java.util.function.Function;
008
009import static javax.json.JsonValue.ValueType.*;
010import static Torello.Java.JSON.JFlag.*;
011import static Torello.Java.JSON.RJInternal.*;
012
013/**
014 * Builds on the J2EE Standard Release JSON Parsing Tools by providing additional
015 * help with converting JSON Data into the Best-Fit
016 * <B STYLE='color: red'><CODE>java.lang.Number</CODE></B>
017 * 
018 * <EMBED CLASS='external-html' DATA-FILE-ID=GLASS_FISH_NOTE>
019 * 
020 * @see Json
021 * @see JsonObject
022 * @see JsonArray
023 */
024@Torello.JavaDoc.StaticFunctional
025@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="JSON_JDHBI")
026public class ReadNumberJSON
027{
028    // This is a static class.  Has no program state.
029    private ReadNumberJSON() { }
030
031
032    // ********************************************************************************************
033    // ********************************************************************************************
034    // Number from JsonArray
035    // ********************************************************************************************
036    // ********************************************************************************************
037
038
039    /**
040     * Retrieve a {@link JsonArray} element, and transform it to a {@code java.lang.Number}.
041     * <EMBED CLASS=defs DATA-JTYPE=JsonNumber DATA-TYPE='java.lang.Number'>
042     * <EMBED CLASS='external-html' DATA-FILE-ID=JR_NUMBER>
043     * 
044     * @param ja            Any instance of {@link JsonArray}
045     * @param index         <EMBED CLASS='external-html' DATA-FILE-ID=JR_INDEX_JA>
046     * @param throwOnNull   <EMBED CLASS='external-html' DATA-FILE-ID=JR_TON_JA>
047     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=JR_RET_TON_JA>
048     * 
049     * @throws IndexOutOfBoundsException If {@code 'index'} is out of the bounds of {@code 'ja'}
050     * @throws JsonTypeArrException      <EMBED CLASS='external-html' DATA-FILE-ID=JR_JTAEX>
051     * @throws JsonNullArrException      <EMBED CLASS='external-html' DATA-FILE-ID=JR_JNAEX>
052     * 
053     * @see JsonValue#getValueType()
054     * @see RJInternal#convertToNumber(JsonNumber) 
055     */
056    public static Number get(JsonArray ja, int index, boolean throwOnNull)
057    {
058        // This will throw an IndexOutOfBoundsException if the index is out of bounds.
059        JsonValue jv = ja.get(index);
060
061        switch (jv.getValueType())
062        {
063            case NULL:
064
065                // This is simple-stuff (not rocket-science).  "Type Mapping" Code has to worry
066                // about what the meaning of "null" should be.
067
068                if (throwOnNull) throw new JsonNullArrException(ja, index, NUMBER, Number.class);
069                else return null;
070
071            case NUMBER: return convertToNumber((JsonNumber) jv);
072
073            // The JsonValue at the specified array-index does not contain a JsonString.
074            default: throw new JsonTypeArrException(ja, index, NUMBER, jv, Number.class);
075        }
076    }
077
078    /**
079     * Retrieve a {@link JsonArray} element, and transform it to a {@code java.lang.Number}.
080     * <EMBED CLASS=defs DATA-TYPE='java.lang.Number' DATA-JTYPE=JsonNumber>
081     * <EMBED CLASS='external-html' DATA-FILE-ID=JR_NUMBER>
082     * 
083     * @param ja            Any instance of {@link JsonArray}
084     * @param index         The array index containing the element to retrieve.
085     * @param FLAGS         Return-value / exception-throw constants defined in {@link JFlag}
086     * @param defaultValue  <EMBED CLASS='external-html' DATA-FILE-ID=JRF_DEFV>
087     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=JRF_RET_JA>
088     * 
089     * @throws IndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=JRF_IOOBEX>
090     * @throws JsonNullArrException      <EMBED CLASS='external-html' DATA-FILE-ID=JRF_JNAEX>
091     * @throws JsonTypeArrException      <EMBED CLASS='external-html' DATA-FILE-ID=JRF_JTAEX>
092     * 
093     * @see RJInternal#GET(JsonArray, int, int, Number, Class, Function, Function)
094     * @see RJInternal#convertToNumber(JsonNumber)
095     */
096    public static Number get(JsonArray ja, int index, int FLAGS, Number defaultValue)
097    { return GET(ja, index, FLAGS, defaultValue, Number.class, RJInternal::convertToNumber, null); }
098
099    /**
100     * Retrieve a {@link JsonArray} element containing a {@link JsonString}, and transform it to a
101     * {@code java.lang.Number}, with either a user-provided parser, or the standard java parser
102     * 
103     * <EMBED CLASS=defs DATA-TYPE='java.lang.Number' DATA-JTYPE=JsonString
104     *  DATA-PARSER='new BigDecimal'>
105     * 
106     * <BR /><BR />If {@code 'parser'} is passed null, the default parser is used, which is just
107     * the {@code java.math.BigDecimal} constructor which accepts a {@code String}.  What is done
108     * with the parsed {@code BigDecimal} instance is explained below.
109     * 
110     * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=JR_NUM_PARSE>
111     * 
112     * @param ja             Any instance of {@link JsonArray}
113     * @param index          The array index containing the {@link JsonString} element to retrieve.
114     * @param FLAGS          Return-value / exception-throw constants defined in {@link JFlag}
115     * @param defaultValue   <EMBED CLASS='external-html' DATA-FILE-ID=JRF_DEFV>
116     * @param optionalParser <EMBED CLASS='external-html' DATA-FILE-ID=JRF_PARSER>
117     * @return               <EMBED CLASS='external-html' DATA-FILE-ID=JRF_PARSERET_JA>
118     * 
119     * @throws IndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=JRF_IOOBEX>
120     * @throws JsonStrParseArrException  <EMBED CLASS='external-html' DATA-FILE-ID=JRF_JSPAEX>
121     * @throws JsonNullArrException      <EMBED CLASS='external-html' DATA-FILE-ID=JRF_JNAEX>
122     * @throws JsonTypeArrException      <EMBED CLASS='external-html' DATA-FILE-ID=JRF_JTAEX>
123     * 
124     * @see RJInternal#PARSE(JsonObject, String, int, Number, Class, Function, Function, Function)
125     * @see RJInternal#convertToNumber(String)
126     */
127    public static Number parse(
128            JsonArray ja, int index, int FLAGS, Number defaultValue,
129            Function<String, Number> optionalParser
130        )
131    {
132        return PARSE(
133            ja, index, FLAGS, defaultValue, Number.class, optionalParser,
134            RJInternal::convertToNumber, null  /* Not Needed, convertToNumber won't throw */
135        );
136    }
137
138
139    // ********************************************************************************************
140    // ********************************************************************************************
141    // Number from JsonObject
142    // ********************************************************************************************
143    // ********************************************************************************************
144
145
146    /**
147     * Extract a {@link JsonObject} property, and transform it to a {@code java.lang.Number}.
148     * <EMBED CLASS=defs DATA-TYPE='java.lang.Number' DATA-JTYPE=JsonNumber>
149     * <EMBED CLASS='external-html' DATA-FILE-ID=JR_NUMBER>
150     * 
151     * @param jo            Any instance of {@link JsonObject}.
152     * @param propertyName  <EMBED CLASS='external-html' DATA-FILE-ID=JR_PN_JO>
153     * @param isOptional    <EMBED CLASS='external-html' DATA-FILE-ID=JR_ISOPT_JO>
154     * @param throwOnNull   <EMBED CLASS='external-html' DATA-FILE-ID=JR_TON_JO>
155     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=JR_RET_TON_JO>
156     * 
157     * @throws JsonPropMissingException <EMBED CLASS='external-html' DATA-FILE-ID=JR_JPMEX>
158     * @throws JsonTypeObjException     <EMBED CLASS='external-html' DATA-FILE-ID=JR_JTOEX>
159     * @throws JsonNullObjException     <EMBED CLASS='external-html' DATA-FILE-ID=JR_JNOEX>
160     * 
161     * @see JsonValue#getValueType()
162     * @see RJInternal#convertToNumber(JsonNumber)
163     */
164    public static Number get
165        (JsonObject jo, String propertyName, boolean isOptional, boolean throwOnNull)
166    {
167        if (! jo.containsKey(propertyName))
168        {
169            if (isOptional) return null;
170            else throw new JsonPropMissingException(jo, propertyName, NUMBER, Number.class);
171        }
172
173        JsonValue jv = jo.get(propertyName);
174
175        switch (jv.getValueType())
176        {
177            case NULL:
178
179                // This is simple-stuff (not rocket-science).  "Type Mapping" Code has to worry
180                // about what the meaning of "null" should be.
181
182                if (throwOnNull) throw new JsonNullObjException
183                    (jo, propertyName, NUMBER, Number.class);
184
185                else return null;
186
187            case NUMBER: return convertToNumber((JsonNumber) jv);
188
189            // The JsonObject propertydoes not contain a JsonNumber.
190            default: throw new JsonTypeObjException
191                (jo, propertyName, NUMBER, jv, Number.class);
192        }
193    }
194
195    /**
196     * Retrieve a {@link JsonObject} property, and transform it to a {@code java.lang.Number}.
197     * <EMBED CLASS=defs DATA-TYPE='java.lang.Number' DATA-JTYPE=JsonNumber>
198     * <EMBED CLASS='external-html' DATA-FILE-ID=JR_NUMBER>
199     * 
200     * @param jo            Any instance of {@link JsonObject}
201     * @param propertyName  <EMBED CLASS='external-html' DATA-FILE-ID=JR_PN_JO>
202     * @param FLAGS         Return-value / exception-throw constants defined in {@link JFlag}
203     * @param defaultValue  <EMBED CLASS='external-html' DATA-FILE-ID=JRF_DEFV>
204     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=JRF_RET_JO>
205     * 
206     * @throws JsonPropMissingException <EMBED CLASS='external-html' DATA-FILE-ID=JRF_JPMEX>
207     * @throws JsonNullObjException     <EMBED CLASS='external-html' DATA-FILE-ID=JRF_JNOEX>
208     * @throws JsonTypeObjException     <EMBED CLASS='external-html' DATA-FILE-ID=JRF_JTOEX>
209     * 
210     * @see RJInternal#GET(JsonObject, String, int, Number, Class, Function, Function)
211     * @see RJInternal#convertToNumber(JsonNumber)
212     */
213    public static Number get
214        (JsonObject jo, String propertyName, int FLAGS, Number defaultValue)
215    {
216        return GET
217            (jo, propertyName, FLAGS, defaultValue, Number.class, RJInternal::convertToNumber, null);
218    }
219
220    /**
221     * Retrieve a {@link JsonObject} property containing a {@link JsonString}, and transform it to
222     * a {@code java.lang.Number}, with either a user-provided parser, or the standard java
223     * parser
224     * 
225     * <EMBED CLASS=defs DATA-TYPE='java.lang.Number' DATA-JTYPE=JsonString
226     *  DATA-PARSER='new BigDecimal'>
227     * 
228     * <BR /><BR />If {@code 'parser'} is passed null, the default parser is used, which is just
229     * the {@code java.math.BigDecimal} constructor which accepts a {@code String}.  What is done
230     * with the parsed {@code BigDecimal} instance is explained below.
231     * 
232     * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=JR_NUM_PARSE>
233     * 
234     * @param jo             Any instance of {@link JsonObject}
235     * @param propertyName   <EMBED CLASS='external-html' DATA-FILE-ID=JR_PN_JO>
236     * @param FLAGS          Return-value / exception-throw constants defined in {@link JFlag}
237     * @param defaultValue   <EMBED CLASS='external-html' DATA-FILE-ID=JRF_DEFV>
238     * @param optionalParser <EMBED CLASS='external-html' DATA-FILE-ID=JRF_PARSER>
239     * @return               <EMBED CLASS='external-html' DATA-FILE-ID=JRF_PARSERET_JO>
240     * 
241     * @throws JsonPropMissingException <EMBED CLASS='external-html' DATA-FILE-ID=JRF_JPMEX>
242     * @throws JsonStrParseObjException <EMBED CLASS='external-html' DATA-FILE-ID=JRF_JSPOEX>
243     * @throws JsonNullObjException     <EMBED CLASS='external-html' DATA-FILE-ID=JRF_JNOEX>
244     * @throws JsonTypeObjException     <EMBED CLASS='external-html' DATA-FILE-ID=JRF_JTOEX>
245     * 
246     * @see RJInternal#PARSE(JsonObject, String, int, Number, Class, Function, Function, Function)
247     * @see RJInternal#convertToNumber(String)
248     */
249    public static Number parse(
250            JsonObject jo, String propertyName, int FLAGS, Number defaultValue,
251            Function<String, Number> optionalParser
252        )
253    {
254        return PARSE(
255            jo, propertyName, FLAGS, defaultValue, Number.class, optionalParser,
256            RJInternal::convertToNumber, null /* Not Needed, convertToNumber won't throw */
257        );
258    }
259
260
261    // ********************************************************************************************
262    // ********************************************************************************************
263    // Number from JsonString Parse  **OR**  from JsonNumber
264    // ********************************************************************************************
265    // ********************************************************************************************
266
267
268    /**
269     * <EMBED CLASS=defs DATA-TYPE=Number DATA-PARSER='new BigDecimal'>
270     * <EMBED CLASS='external-html' DATA-FILE-ID=JRXL_DESC_JA>
271     * 
272     * @param ja             Any {@link JsonArray}
273     * @param i              Any index into the {@code JsonArray}
274     * @param FLAGS          Return-value / exception-throw constants defined in {@link JFlag}
275     * @param defaultValue   <EMBED CLASS='external-html' DATA-FILE-ID=JRXL_DEFVAL>
276     * @param optionalParser <EMBED CLASS='external-html' DATA-FILE-ID=JRXL_PARSER>
277     * @return               <EMBED CLASS='external-html' DATA-FILE-ID=JRXL_RET_JA>
278     * 
279     * @throws IndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=JRF_IOOBEX>
280     * @throws JsonStrParseArrException  <EMBED CLASS='external-html' DATA-FILE-ID=JRXL_JSPAEX>
281     * @throws JsonNullArrException      <EMBED CLASS='external-html' DATA-FILE-ID=JRXL_JNAEX>
282     * @throws JsonTypeArrException      <EMBED CLASS='external-html' DATA-FILE-ID=JRXL_JTAEX>
283     * 
284     * @see #get(JsonArray, int, int, Number)
285     * @see #parse(JsonArray, int, int, Number, Function)
286     */
287    public static Number get(
288            JsonArray ja, int i, int FLAGS, Number defaultValue,
289            Function<String, Number> optionalParser
290        )
291    {
292        JsonValue.ValueType t;
293
294        return ((i >= ja.size()) || ((t=ja.get(i).getValueType()) == NUMBER) || (t != STRING))
295            ? get(ja, i, FLAGS, defaultValue)
296            : parse(ja, i, FLAGS, defaultValue, optionalParser);
297    }
298
299    /**
300     * <EMBED CLASS=defs DATA-TYPE=Number DATA-PARSER='new BigDecimal'>
301     * <EMBED CLASS='external-html' DATA-FILE-ID=JRXL_DESC_JO>
302     * 
303     * @param jo             Any {@link JsonObject}
304     * @param propertyName   Any of the properties defined in the {@code JsonObject}
305     * @param FLAGS          Return-value / exception-throw constants defined in {@link JFlag}
306     * @param defaultValue   <EMBED CLASS='external-html' DATA-FILE-ID=JRXL_DEFVAL>
307     * @param optionalParser <EMBED CLASS='external-html' DATA-FILE-ID=JRXL_PARSER>
308     * @return               <EMBED CLASS='external-html' DATA-FILE-ID=JRXL_RET_JO>
309     * 
310     * @throws JsonPropMissingException <EMBED CLASS='external-html' DATA-FILE-ID=JRF_JPMEX>
311     * @throws JsonStrParseObjException <EMBED CLASS='external-html' DATA-FILE-ID=JRXL_JSPOEX>
312     * @throws JsonNullObjException     <EMBED CLASS='external-html' DATA-FILE-ID=JRXL_JNOEX>
313     * @throws JsonTypeObjException     <EMBED CLASS='external-html' DATA-FILE-ID=JRXL_JTOEX>
314     * 
315     * @see #get(JsonObject, String, int, Number)
316     * @see #parse(JsonObject, String, int, Number, Function)
317     */
318    public static Number get(
319                JsonObject jo, String propertyName, int FLAGS, Number defaultValue,
320                Function<String, Number> optionalParser
321            )
322    {
323        JsonValue.ValueType t;
324
325        return (    (! jo.containsKey(propertyName))
326                ||  ((t = jo.get(propertyName).getValueType()) == NUMBER)
327                ||  (t != STRING)
328            )
329            ? get(jo, propertyName, FLAGS, defaultValue)
330            : parse(jo, propertyName, FLAGS, defaultValue, optionalParser);
331    }
332}