001package Torello.Java.JSON;
002
003import Torello.Java.StringParse;
004
005import java.lang.reflect.Array;
006
007import javax.json.JsonArray;
008import javax.json.JsonValue;
009
010import java.util.Objects;
011
012import static javax.json.JsonValue.ValueType.*;
013
014
015
016public class ProcessMultiDimJsonArray
017{
018    private ProcessMultiDimJsonArray() { }
019
020    /**
021     * This class is invoked by the class {@link RJArrDimN}
022     * 
023     * @param <BASIC_TYPE> The "Component Type" of the Output-Array.
024     * 
025     * @param <STREAM_TYPE> The "Intermediate Stream Type", which is present before the conversion
026     * to an Array.
027     * 
028     * @param <RETURN_ARR_TYPE> The actual Return-Type of the method.  This must be an Array-Class,
029     * such as {@code int[][].class} or (in the case of Boxed-Type Arrays) {@code Integer[][]}.
030     * 
031     * @param ja Any instance of {@link JsonArray}.  The contents sof this array should match the 
032     * dimensionality of the expected Output-Array Type, or an exception will likelyy throw.
033     * 
034     * @param rec An instance of {@link SettingsRec} that's been configured to return a 
035     * multi-dimensional array.
036     * 
037     * @param retArrClass The class of the return array.
038     * @return An instance of {@code 'retArrClass'}
039     */
040    public static <BASIC_TYPE, STREAM_TYPE, RETURN_ARR_TYPE> RETURN_ARR_TYPE jsonArrayToJava(
041            final JsonArray                             ja,
042            final SettingsRec<BASIC_TYPE, STREAM_TYPE>  rec,
043            final Class<RETURN_ARR_TYPE>                retArrClass
044        )
045    {
046        CHECK_ARRAY_CLASS(retArrClass, rec.CLASS);
047        return jsonArrayToJava_INTERNAL(ja, rec, retArrClass);
048    }
049
050    @SuppressWarnings("unchecked")
051    private static <BASIC_TYPE, STREAM_TYPE, RETURN_ARR_TYPE> RETURN_ARR_TYPE
052        jsonArrayToJava_INTERNAL(
053            final JsonArray                             ja,
054            final SettingsRec<BASIC_TYPE, STREAM_TYPE>  rec,
055            final Class<RETURN_ARR_TYPE>                retArrClass
056        )
057    {
058        // If this is requesting a one-dimensional array, get it using the 1D-Generator,
059        // and simply return that array.  This requires a cast because there is no way to prove
060        // to the Java-Compiler that <T> is equal to any return-value at all.
061        //
062        // Remember that this is only guaranteed (it works!) because the helper methods are all
063        // protected or private, and it has been guaranteed through rigorous testing, and
064        // preventing the user from playing with it!
065
066        if (StringParse.countCharacters(retArrClass.getSimpleName(), '[') == 1)
067            return (RETURN_ARR_TYPE) rec.array1DGenerator.apply(ja);
068
069
070        // Otherwise, this is not a single-dimension (1D) array.  Instead, the JsonArray needs
071        // to be iterated, and this method called, recursively, on each of the sub-arrays.
072        //
073        // NOTE: 'compClass' will also be an array, but with one fewer dimensions
074
075        final Class<?>          compClass   = retArrClass.getComponentType();
076        final int               SIZE        = ja.size();
077        final RETURN_ARR_TYPE   retArr      = (RETURN_ARR_TYPE) Array.newInstance(compClass, SIZE);
078
079        JsonValue jv = null;
080
081        for (int i=0; i < SIZE; i++)
082
083            switch ((jv = ja.get(i)).getValueType())
084            {
085                // javax.json.JsonValue.ValueType.NULL
086                case NULL: Array.set(retArr, i, null); break;
087
088                // javax.json.JsonValue.ValueType.ARRAY (JsonArray)
089                case ARRAY:
090
091                    Array.set(
092                        retArr,
093                        i,
094                        JSON_ARRAY_DIMN.jsonArrayToJava((JsonArray) jv, rec, compClass)
095                    );
096
097                    break;
098
099                // javax.json.JsonValue.ValueType.TRUE, FALSE, NUMBER, STRING, OBJECT
100                default:
101
102                    if (rec.IN_NSAT)        Array.set(retArr, i, null);
103                    else if (rec.S_NSAT)    continue;
104                    else throw new JsonTypeArrException(ja, i, ARRAY, jv, retArrClass);
105            }
106
107        return retArr;
108    }
109
110
111
112    // ********************************************************************************************
113    // ********************************************************************************************
114    // One Helper
115    // ********************************************************************************************
116    // ********************************************************************************************
117
118
119    /**
120     * Check user input, and throws exceptions if the array-class has not been properly
121     * chosen.
122     * 
123     * @param retArrClass This must be a primitive-array class, possibly of multiple dimensions
124     * 
125     * @param expectedRootClass The expected "root class".  For {@code int[][].class}, the root
126     * class would be {@code int.class}.
127     * 
128     * @return Parameter {@code retArrClass} is the return value of this checker method
129     * 
130     * @throws IllegalArgumentExcetion If parameter retArrClass:
131     * If calling retArrClass.isArray() returns FALSE
132     * If the root-array type is not the appropriate type for the method that was called
133     */
134    protected static <T> Class<T> CHECK_ARRAY_CLASS(
135            final Class<T> retArrClass,
136            final Class<?> expectedRootClass
137        )
138    {
139        Objects.requireNonNull(retArrClass, "Return-Array Type, Parameter 'retArrClass' is null");
140
141        if (! retArrClass.isArray()) throw new IllegalArgumentException(
142            "The class you have passed to parameter 'retArrClass' " +
143            "[" + retArrClass.getSimpleName() + "], is not an array"
144        );
145
146        Class<?> componentClass = retArrClass.getComponentType();
147
148        while (componentClass.isArray()) componentClass = componentClass.getComponentType();
149
150        if (! expectedRootClass.equals(componentClass)) throw new IllegalArgumentException(
151            "The class you have passed to parameter 'retArrClass' " +
152            "[" + retArrClass.getSimpleName() + "], is not a(n) " +
153            expectedRootClass.getSimpleName() + "-array."
154        );
155
156        return retArrClass;
157    }
158}