package org.json.simple.serialization;
import org.json.simple.serialization.collections.PrimitiveArrayCodec;
import sun.reflect.generics.reflectiveObjects.TypeVariableImpl;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import org.json.simple.serialization.primitives.*;
import org.json.simple.serialization.collections.CollectionCodec;
import org.json.simple.serialization.collections.ArrayCodec;
import org.json.simple.serialization.collections.MapCodec;
/**
* @author karl.wettin@kodapan.se
* @since 2009-jul-29 21:18:08
*/
public class CodecRegistry {
private ClassResolver classResolver = new ClassResolver();
private Map<Class, Codec> primitiveCodecs = new HashMap<Class, Codec>();
private Map<Class, BeanCodec> beanCodecs = new HashMap<Class, BeanCodec>();
public CodecRegistry() {
primitiveCodecs.put(boolean.class, new BooleanCodec());
primitiveCodecs.put(byte.class, new ByteCodec());
primitiveCodecs.put(short.class, new ShortCodec());
primitiveCodecs.put(int.class, new IntegerCodec());
primitiveCodecs.put(long.class, new LongCodec());
primitiveCodecs.put(float.class, new FloatCodec());
primitiveCodecs.put(double.class, new DoubleCodec());
primitiveCodecs.put(char.class, new CharacterCodec());
primitiveCodecs.put(java.lang.Boolean.class, new BooleanCodec());
primitiveCodecs.put(java.lang.Byte.class, new ByteCodec());
primitiveCodecs.put(java.lang.Short.class, new ShortCodec());
primitiveCodecs.put(java.lang.Integer.class, new IntegerCodec());
primitiveCodecs.put(java.lang.Long.class, new LongCodec());
primitiveCodecs.put(java.lang.Float.class, new FloatCodec());
primitiveCodecs.put(java.lang.Double.class, new DoubleCodec());
primitiveCodecs.put(java.lang.Number.class, new NumberCodec());
primitiveCodecs.put(java.math.BigInteger.class, new BigIntegerCodec());
primitiveCodecs.put(java.math.BigDecimal.class, new BigDecimalCodec());
primitiveCodecs.put(java.lang.Character.class, new CharacterCodec());
primitiveCodecs.put(java.lang.String.class, new StringCodec());
primitiveCodecs.put(java.util.Date.class, new DateCodec());
primitiveCodecs.put(java.util.List.class, new CollectionCodec(this, Object.class) {
@Override
public Collection collectionFactory() {
return new ArrayList();
}
});
primitiveCodecs.put(java.util.Set.class, new CollectionCodec(this, Object.class) {
@Override
public Collection collectionFactory() {
return new HashSet();
}
});
}
public <T> Codec<T> getCodec(Class<T> type) throws InstantiationException, IllegalAccessException {
Codec codec = primitiveCodecs.get(type);
if (codec != null) {
return codec;
}
codec = beanCodecs.get(type);
if (codec != null) {
return codec;
}
if (type.isEnum()) {
return (Codec<T>)new EnumCodec((Class<Enum>)type);
}
BeanCodec beanCodec = new BeanCodec();
beanCodecs.put(type, beanCodec);
// codec must be in map at this point to avoid eternal loop.
beanCodec.resolve(this, type);
return beanCodec;
}
/**
*
* @param field
* @param instance if not null the classtype of this instance will be choosed over the class type of the field.
* @return
*/
public Codec getCodec(Field field, Object instance) throws IllegalAccessException, InstantiationException {
if (field.getType().isArray()) {
Class genericType = field.getType().getComponentType();
if (genericType.isPrimitive()) {
return new PrimitiveArrayCodec(this, genericType);
} else {
return new ArrayCodec(this, genericType);
}
} else if (List.class.isAssignableFrom(field.getType())) {
Class genericType = getPrimitiveGenericType(field);
return new CollectionCodec(this, genericType) {
@Override
public Collection collectionFactory() {
return new ArrayList();
}
};
} else if (Set.class.isAssignableFrom(field.getType())) {
Class genericType = getPrimitiveGenericType(field);
return new CollectionCodec(this, genericType) {
@Override
public Collection collectionFactory() {
return new LinkedHashSet();
}
};
} else if (Map.class.isAssignableFrom(field.getType())) {
Class[] genericTypes = getGenericTypes(field);
if (genericTypes != null) {
return new MapCodec(this, genericTypes[0], genericTypes[1]);
} else {
return new MapCodec(this, Object.class, Object.class);
}
}
if (instance == null) {
return getCodec(field.getType());
} else {
return getCodec(instance.getClass());
}
}
/**
* @param field
* @return class of values in a collection, e.g. Integer in List<Integer>.
*/
private Class getPrimitiveGenericType(Field field) {
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
if (pt.getActualTypeArguments()[0] instanceof TypeVariableImpl) {
// so this is a List<D> where D is defined in the owner class.
// this value is not read here,
// instead the class of the type is picked up from the json value "class" (Codec#classIdentifier) of each instance.
return null;
} else if (pt.getActualTypeArguments()[0] instanceof ParameterizedType) {
// this is an array/collection/map that contains another array/collection/map
return (Class)((ParameterizedType) pt.getActualTypeArguments()[0]).getRawType();
} else {
return (Class) pt.getActualTypeArguments()[0];
}
} else {
return null;
// throw new RuntimeException("Missing generic type argument! " + field.toString());
}
}
private Class[] getGenericTypes(Field field) {
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
Class[] result = new Class[pt.getActualTypeArguments().length];
for (int i = 0; i < pt.getActualTypeArguments().length; i++) {
result[i] = (Class) pt.getActualTypeArguments()[i];
}
return result;
} else {
return null;
// throw new RuntimeException("Missing generic type argument! " + field.toString());
}
}
public Map<Class, Codec> getPrimitiveCodecs() {
return primitiveCodecs;
}
public Map<Class, BeanCodec> getBeanCodecs() {
return beanCodecs;
}
public ClassResolver getClassResolver() {
return classResolver;
}
public void setClassResolver(ClassResolver classResolver) {
this.classResolver = classResolver;
}
}