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}