package org.test4j.json.decoder;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.test4j.json.JSONException;
import org.test4j.json.decoder.base.BaseDecoder;
import org.test4j.json.decoder.base.DecoderException;
import org.test4j.json.decoder.base.DecoderFactory;
import org.test4j.json.helper.JSONArray;
import org.test4j.json.helper.JSONMap;
import org.test4j.json.helper.JSONObject;
import org.test4j.tools.commons.PrimitiveHelper;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class ArrayDecoder extends BaseDecoder {
public static final BaseDecoder toARRAY = new ArrayDecoder();
public <T> T decode(JSONObject json, Type toType, Map<String, Object> references) {
if (json == null) {
return null;
}
if (json instanceof JSONMap) {
Object o = this.parseFromJSONMap((JSONMap) json, toType, references);
return (T) o;
} else if (json instanceof JSONArray) {
List list = this.parseFromJSONArray(((JSONArray) json), toType, references);
Object[] a = this.getArray(toType);
return (T) list.toArray(a);
} else {
throw new DecoderException(
"illegal type for ArrayDecoder. the type can only is JSONArray or JSONMap, but actual is JSONSingle.");
}
}
private <T> T parseFromJSONMap(JSONMap map, Type toType, Map<String, Object> references) {
String referenceID = map.getReferFromJSONProp();
if (referenceID != null) {
Object o = references.get(referenceID);
return (T) o;
}
Type type = map.getClazzFromJSONFProp(toType);
if (this.accept(type) == false) {
throw new JSONException("JSONMap must have property that declared the array type.");
}
JSONObject array = map.getValueFromJSONProp();
if (!(array instanceof JSONArray)) {
throw new JSONException("illegal type for ArrayDecoder. the type can only be JSONArray, but actual is "
+ array.getClass().getName());
}
List list = this.parseFromJSONArray(((JSONArray) array), toType, references);
Object[] a = this.getArray(toType);
Object oa = list.toArray(a);
String newID = map.getReferenceID();
if (newID != null) {
references.put(newID, oa);
}
return (T) oa;
}
private final List parseFromJSONArray(JSONArray jsonArray, Type toType, Map<String, Object> references) {
List list = new ArrayList();
for (Iterator<JSONObject> it = jsonArray.iterator(); it.hasNext();) {
JSONObject jsonObject = it.next();
Type componentType = this.getComponent(jsonObject, toType);
IDecoder decoder = DecoderFactory.getDecoder(componentType);
Object o = decoder.decode(jsonObject, componentType, references);
list.add(o);
}
return list;
}
public boolean accept(Type type) {
if (type instanceof Class) {
return ((Class) type).isArray();
} else if (type instanceof GenericArrayType) {
return true;
} else {
return false;
}
}
private Object[] getArray(Type type) {
while (type instanceof GenericArrayType) {
type = ((GenericArrayType) type).getGenericComponentType();
}
if (!(type instanceof Class)) {
return new String[0];
}
int length = 0;
Class argClaz = (Class) type;
while (argClaz.getComponentType() != null) {
argClaz = argClaz.getComponentType();
length++;
}
int[] dimensions = new int[length];
for (int index = 0; index < length; index++) {
dimensions[index] = 0;
}
argClaz = PrimitiveHelper.getPrimitiveBoxType(argClaz);
return (Object[]) Array.newInstance(argClaz, dimensions);
}
private Type getComponent(Type toType) {
if (toType instanceof Class) {
Class claz = ((Class) toType).getComponentType();
return Object.class.equals(claz) ? HashMap.class : claz;
} else if (toType instanceof GenericArrayType) {
return ((GenericArrayType) toType).getGenericComponentType();
} else {
throw new DecoderException("the ArrayDecoder only accpt array type, but actual is:" + toType);
}
}
private Type getComponent(JSONObject jo, Type toType) {
Type argType = this.getComponent(toType);
if (!(jo instanceof JSONMap)) {
return argType;
}
Type type = ((JSONMap) jo).getClazzFromJSONFProp(argType);
return type;
}
}