package com.jsoniter.spi;
import com.jsoniter.any.Any;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
public class TypeLiteral<T> {
public enum NativeType {
FLOAT,
DOUBLE,
BOOLEAN,
BYTE,
SHORT,
INT,
CHAR,
LONG,
BIG_DECIMAL,
BIG_INTEGER,
STRING,
OBJECT,
ANY,
}
public static Map<Type, NativeType> nativeTypes = new HashMap<Type, NativeType>() {{
put(float.class, NativeType.FLOAT);
put(Float.class, NativeType.FLOAT);
put(double.class, NativeType.DOUBLE);
put(Double.class, NativeType.DOUBLE);
put(boolean.class, NativeType.BOOLEAN);
put(Boolean.class, NativeType.BOOLEAN);
put(byte.class, NativeType.BYTE);
put(Byte.class, NativeType.BYTE);
put(short.class, NativeType.SHORT);
put(Short.class, NativeType.SHORT);
put(int.class, NativeType.INT);
put(Integer.class, NativeType.INT);
put(char.class, NativeType.CHAR);
put(Character.class, NativeType.CHAR);
put(long.class, NativeType.LONG);
put(Long.class, NativeType.LONG);
put(BigDecimal.class, NativeType.BIG_DECIMAL);
put(BigInteger.class, NativeType.BIG_INTEGER);
put(String.class, NativeType.STRING);
put(Object.class, NativeType.OBJECT);
put(Any.class, NativeType.ANY);
}};
private volatile static Map<Type, TypeLiteral> typeLiteralCache = new HashMap<Type, TypeLiteral>();
final Type type;
final String decoderCacheKey;
final String encoderCacheKey;
final NativeType nativeType;
/**
* Constructs a new type literal. Derives represented class from type parameter.
* Clients create an empty anonymous subclass. Doing so embeds the type parameter in the
* anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
*/
@SuppressWarnings("unchecked")
protected TypeLiteral() {
this.type = getSuperclassTypeParameter(getClass());
nativeType = nativeTypes.get(this.type);
decoderCacheKey = generateDecoderCacheKey(type);
encoderCacheKey = generateEncoderCacheKey(type);
}
public TypeLiteral(Type type, String decoderCacheKey, String encoderCacheKey) {
this.type = type;
nativeType = nativeTypes.get(this.type);
this.decoderCacheKey = decoderCacheKey;
this.encoderCacheKey = encoderCacheKey;
}
private static String generateDecoderCacheKey(Type type) {
return generateCacheKey(type, "decoder.");
}
private static String generateEncoderCacheKey(Type type) {
return generateCacheKey(type, "encoder.");
}
private static String generateCacheKey(Type type, String prefix) {
StringBuilder decoderClassName = new StringBuilder(prefix);
if (type instanceof Class) {
Class clazz = (Class) type;
if (clazz.isAnonymousClass()) {
throw new JsonException("anonymous class not supported: " + clazz);
}
if (clazz.isArray()) {
decoderClassName.append(clazz.getCanonicalName().replace("[]", "_array"));
} else {
// for nested class $
decoderClassName.append(clazz.getName().replace("[]", "_array"));
}
} else if (type instanceof ParameterizedType) {
try {
ParameterizedType pType = (ParameterizedType) type;
Class clazz = (Class) pType.getRawType();
decoderClassName.append(clazz.getCanonicalName().replace("[]", "_array"));
for (int i = 0; i < pType.getActualTypeArguments().length; i++) {
String typeName = formatTypeWithoutSpecialCharacter(pType.getActualTypeArguments()[i]);
decoderClassName.append('_');
decoderClassName.append(typeName);
}
} catch (Exception e) {
throw new JsonException("failed to generate cache key for: " + type, e);
}
} else if (type instanceof GenericArrayType) {
GenericArrayType gaType = (GenericArrayType) type;
Type compType = gaType.getGenericComponentType();
decoderClassName.append(formatTypeWithoutSpecialCharacter(compType));
decoderClassName.append("_array");
} else {
throw new UnsupportedOperationException("do not know how to handle: " + type);
}
return decoderClassName.toString().replace("$", "_");
}
private static String formatTypeWithoutSpecialCharacter(Type type) {
if (type instanceof Class) {
Class clazz = (Class) type;
return clazz.getCanonicalName();
}
if (type instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) type;
String typeName = formatTypeWithoutSpecialCharacter(pType.getRawType());
for (Type typeArg : pType.getActualTypeArguments()) {
typeName += "_";
typeName += formatTypeWithoutSpecialCharacter(typeArg);
}
return typeName;
}
if (type instanceof GenericArrayType) {
GenericArrayType gaType = (GenericArrayType) type;
return formatTypeWithoutSpecialCharacter(gaType.getGenericComponentType()) + "_array";
}
throw new JsonException("unsupported type: " + type + ", of class " + type.getClass());
}
static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class) {
throw new JsonException("Missing type parameter.");
}
ParameterizedType parameterized = (ParameterizedType) superclass;
return parameterized.getActualTypeArguments()[0];
}
public static TypeLiteral create(Type valueType) {
TypeLiteral typeLiteral = typeLiteralCache.get(valueType);
if (typeLiteral != null) {
return typeLiteral;
}
return createNew(valueType);
}
private synchronized static TypeLiteral createNew(Type valueType) {
TypeLiteral typeLiteral = typeLiteralCache.get(valueType);
if (typeLiteral != null) {
return typeLiteral;
}
HashMap<Type, TypeLiteral> copy = new HashMap<Type, TypeLiteral>(typeLiteralCache);
typeLiteral = new TypeLiteral(valueType,
generateDecoderCacheKey(valueType),
generateEncoderCacheKey(valueType));
copy.put(valueType, typeLiteral);
typeLiteralCache = copy;
return typeLiteral;
}
public Type getType() {
return type;
}
public String getDecoderCacheKey() {
return decoderCacheKey;
}
public String getEncoderCacheKey() {
return encoderCacheKey;
}
public NativeType getNativeType() {
return nativeType;
}
@Override
public String toString() {
return "TypeLiteral{" +
"type=" + type +
", decoderCacheKey='" + decoderCacheKey + '\'' +
", encoderCacheKey='" + encoderCacheKey + '\'' +
'}';
}
}