package com.dianping.pigeon.remoting.common.codec.json;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import com.dianping.pigeon.config.ConfigManagerLoader;
import com.dianping.pigeon.remoting.common.exception.SerializationException;
import com.dianping.pigeon.util.ReflectUtils;
public class JacksonObjectMapper {
private static String deserializePackage = ConfigManagerLoader.getConfigManager().getStringValue(
"pigeon.codec.jackson.package", "com.dianping");
static JacksonSerializer jacksonSerializer = new JacksonSerializer();
public static <T> T convertObject(T obj) throws SerializationException, ClassNotFoundException,
InstantiationException, IllegalAccessException {
if (obj == null) {
return null;
}
if (obj instanceof LinkedHashMap) {
LinkedHashMap map = (LinkedHashMap) obj;
return (T) convertMap(map);
} else if (obj instanceof Collection) {
Collection list = (Collection) obj;
Collection newList = (Collection) Class.forName(obj.getClass().getName()).newInstance();
if (!list.isEmpty()) {
int i = 0;
String componentType = null;
for (Iterator ir = list.iterator(); ir.hasNext();) {
Object o = ir.next();
if (o instanceof LinkedHashMap) {
String cls = (String) ((Map) o).get("@class");
if (StringUtils.isNotBlank(cls)) {
componentType = cls;
} else if (componentType != null) {
((Map) o).put("@class", componentType);
}
}
newList.add(convertObject(o));
i++;
}
return (T) newList;
}
} else if (obj.getClass().isArray()) {
int len = Array.getLength(obj);
for (int i = 0; i < len; i++) {
Object o = Array.get(obj, i);
String componentType = null;
if (o instanceof LinkedHashMap) {
String cls = (String) ((Map) o).get("@class");
if (StringUtils.isNotBlank(cls)) {
componentType = cls;
} else if (componentType != null) {
((Map) o).put("@class", componentType);
}
}
Array.set(obj, i, convertObject(o));
}
return obj;
} else if (obj instanceof Map) {
Map map = (Map) obj;
if (!map.isEmpty()) {
Map finalMap = new HashMap(map.size());
for (Iterator ir = map.keySet().iterator(); ir.hasNext();) {
Object k = ir.next();
Object v = map.get(k);
Object finalKey = convertObject(k);
Object finalValue = convertObject(v);
finalMap.put(finalKey, finalValue);
}
return (T) finalMap;
}
} else if (obj.getClass().getName().startsWith(deserializePackage) && !obj.getClass().isPrimitive()
&& !obj.getClass().isEnum()) {
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
Class<?> type = field.getType();
if (type.isPrimitive() || type.isEnum()) {
continue;
}
Object fieldValue = ReflectUtils.readDeclaredField(obj, field.getName(), true);
if (type.getName().startsWith(deserializePackage) || Map.class.isAssignableFrom(type)
|| Collection.class.isAssignableFrom(type) || type.isArray()
|| (fieldValue instanceof LinkedHashMap)) {
setFieldValue(obj, type, field.getName(), fieldValue);
}
}
}
return obj;
}
private static void setFieldValue(Object obj, Class<?> fieldType, String fieldName, Object fieldValue)
throws SerializationException, IllegalAccessException, ClassNotFoundException, InstantiationException {
try {
ReflectUtils.writeDeclaredField(obj, fieldName, convertObject(fieldValue), true);
} catch (IllegalArgumentException e) {
ReflectUtils.writeDeclaredField(obj, fieldName,
jacksonSerializer.readObject(fieldType, String.valueOf(fieldValue)), true);
}
}
public static Object convertMap(LinkedHashMap map) throws SerializationException, ClassNotFoundException,
InstantiationException, IllegalAccessException {
String type = (String) map.get("@class");
if (StringUtils.isNotBlank(type)) {
Class clazz = Class.forName(type);
Object obj = clazz.newInstance();
for (Iterator ir = map.keySet().iterator(); ir.hasNext();) {
String key = (String) ir.next();
Object value = map.get(key);
if (!key.equals("@class")) {
Field field = ReflectUtils.getDeclaredField(clazz, key, true);
if (field != null) {
setFieldValue(obj, field.getType(), key, value);
}
}
}
return obj;
}
return map;
}
}