package com.jsoniter.spi;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.Map;
public class Binding {
// input
public final Class clazz;
public final TypeLiteral clazzTypeLiteral;
public Annotation[] annotations;
public Field field; // obj.XXX
public Method method; // obj.setXXX() or obj.getXXX()
public boolean valueCanReuse;
// input/output
public String name;
public Type valueType;
public TypeLiteral valueTypeLiteral;
// output
public String[] fromNames; // for decoder
public String[] toNames; // for encoder
public Decoder decoder;
public Encoder encoder;
public boolean asMissingWhenNotPresent;
public boolean asExtraWhenPresent;
public boolean isNullable = true;
public boolean isCollectionValueNullable = true;
public boolean shouldOmitNull = true;
// then this property will not be unknown
// but we do not want to bind it anywhere
public boolean shouldSkip;
// attachment, used when generating code or reflection
public int idx;
public long mask;
public Binding(Class clazz, Map<String, Type> lookup, Type valueType) {
this.clazz = clazz;
this.clazzTypeLiteral = TypeLiteral.create(clazz);
this.valueType = substituteTypeVariables(lookup, valueType);
this.valueTypeLiteral = TypeLiteral.create(this.valueType);
}
public String decoderCacheKey() {
return this.name + "@" + this.clazzTypeLiteral.getDecoderCacheKey();
}
public String encoderCacheKey() {
return this.name + "@" + this.clazzTypeLiteral.getEncoderCacheKey();
}
private static Type substituteTypeVariables(Map<String, Type> lookup, Type type) {
if (type instanceof TypeVariable) {
return translateTypeVariable(lookup, (TypeVariable) type);
}
if (type instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) type;
Type[] args = pType.getActualTypeArguments();
for (int i = 0; i < args.length; i++) {
args[i] = substituteTypeVariables(lookup, args[i]);
}
return new ParameterizedTypeImpl(args, pType.getOwnerType(), pType.getRawType());
}
if (type instanceof GenericArrayType) {
GenericArrayType gaType = (GenericArrayType) type;
return new GenericArrayTypeImpl(substituteTypeVariables(lookup, gaType.getGenericComponentType()));
}
return type;
}
private static Type translateTypeVariable(Map<String, Type> lookup, TypeVariable var) {
GenericDeclaration declaredBy = var.getGenericDeclaration();
if (!(declaredBy instanceof Class)) {
// if the <T> is not defined by class, there is no way to get the actual type
return Object.class;
}
Class clazz = (Class) declaredBy;
Type actualType = lookup.get(var.getName() + "@" + clazz.getCanonicalName());
if (actualType == null) {
// should not happen
return Object.class;
}
if (actualType instanceof TypeVariable) {
// translate to another variable, try again
return translateTypeVariable(lookup, (TypeVariable) actualType);
}
return actualType;
}
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
if (annotations == null) {
return null;
}
for (Annotation annotation : annotations) {
if (annotationClass.isAssignableFrom(annotation.getClass())) {
return (T) annotation;
}
}
return null;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Binding binding = (Binding) o;
if (clazz != null ? !clazz.equals(binding.clazz) : binding.clazz != null) return false;
return name != null ? name.equals(binding.name) : binding.name == null;
}
@Override
public int hashCode() {
int result = clazz != null ? clazz.hashCode() : 0;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Binding{" +
"clazz=" + clazz +
", name='" + name + '\'' +
", valueType=" + valueType +
'}';
}
}