183 lines
8.2 KiB
Java
183 lines
8.2 KiB
Java
|
package io.gitlab.jfronny.gson.compile.processor.adapter;
|
||
|
|
||
|
import com.squareup.javapoet.*;
|
||
|
import io.gitlab.jfronny.gson.compile.processor.Const;
|
||
|
import io.gitlab.jfronny.gson.compile.processor.SerializableClass;
|
||
|
import io.gitlab.jfronny.gson.compile.processor.util.valueprocessor.Property;
|
||
|
|
||
|
import javax.lang.model.element.*;
|
||
|
import javax.lang.model.type.*;
|
||
|
import javax.lang.model.util.SimpleTypeVisitor14;
|
||
|
import javax.tools.Diagnostic;
|
||
|
import java.lang.reflect.ParameterizedType;
|
||
|
import java.util.*;
|
||
|
|
||
|
public class AdapterAdapter extends Adapter {
|
||
|
@Override
|
||
|
public boolean applies() {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void generateWrite(Runnable writeGet) {
|
||
|
code.add(getAdapter() + ".write(writer, ");
|
||
|
writeGet.run();
|
||
|
code.add(");\n");
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void generateRead() {
|
||
|
code.add(getAdapter() + ".read(reader)");
|
||
|
}
|
||
|
|
||
|
private String getAdapter() {
|
||
|
String typeAdapterName = Const.ADAPTER_PREFIX + property.getName();
|
||
|
for (FieldSpec spec : klazz.fieldSpecs) {
|
||
|
if (spec.name.equals(typeAdapterName)) return typeAdapterName;
|
||
|
}
|
||
|
DeclaredType typeAdapterClass = findTypeAdapterClass(property.getAnnotations());
|
||
|
if (typeAdapterClass != null) {
|
||
|
if (isInstance(typeAdapterClass, Const.TYPE_ADAPTER.toString())) {
|
||
|
klazz.addField(
|
||
|
FieldSpec.builder(TypeName.get(typeAdapterClass), typeAdapterName)
|
||
|
.addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
|
||
|
.initializer("new $T()", typeAdapterClass)
|
||
|
.build()
|
||
|
);
|
||
|
} else if (isInstance(typeAdapterClass, Const.TYPE_ADAPTER_FACTORY.toString())) {
|
||
|
TypeName typeAdapterType = ParameterizedTypeName.get(Const.TYPE_ADAPTER, TypeName.get(type).box());
|
||
|
CodeBlock.Builder block = CodeBlock.builder();
|
||
|
block.add("new $T().create($T.HOLDER.getGson(), ", typeAdapterClass, Const.CCORE);
|
||
|
appendFieldTypeToken(block, property, typeVariables, false);
|
||
|
block.add(")");
|
||
|
klazz.addField(
|
||
|
FieldSpec.builder(typeAdapterType, typeAdapterName)
|
||
|
.addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
|
||
|
.initializer(block.build())
|
||
|
.build()
|
||
|
);
|
||
|
} else message.printMessage(Diagnostic.Kind.ERROR, "@JsonAdapter value must by TypeAdapter or TypeAdapterFactory reference.", property.getElement());
|
||
|
} else {
|
||
|
TypeName targetType = TypeName.get(type).box();
|
||
|
for (SerializableClass adapter : other) {
|
||
|
if (TypeName.get(adapter.classElement().asType()).equals(targetType)) {
|
||
|
// Use self-made adapter
|
||
|
return adapter.generatedClassName().toString();
|
||
|
}
|
||
|
}
|
||
|
message.printMessage(options.containsKey("gsonCompileNoReflect") ? Diagnostic.Kind.ERROR : Diagnostic.Kind.WARNING, "Falling back to adapter detection for unsupported type " + type, property.getElement());
|
||
|
TypeName typeAdapterType = ParameterizedTypeName.get(Const.TYPE_ADAPTER, targetType);
|
||
|
CodeBlock.Builder block = CodeBlock.builder();
|
||
|
block.add("$T.HOLDER.getGson().getAdapter(", Const.CCORE);
|
||
|
appendFieldTypeToken(block, property, typeVariables, true);
|
||
|
block.add(")");
|
||
|
klazz.addField(
|
||
|
FieldSpec.builder(typeAdapterType, typeAdapterName)
|
||
|
.addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
|
||
|
.initializer(block.build())
|
||
|
.build()
|
||
|
);
|
||
|
}
|
||
|
return typeAdapterName;
|
||
|
}
|
||
|
|
||
|
private void appendFieldTypeToken(CodeBlock.Builder block, Property<?> property, List<TypeVariableName> typeVariables, boolean allowClassType) {
|
||
|
TypeMirror type = property.getType();
|
||
|
TypeName typeName = TypeName.get(type);
|
||
|
|
||
|
if (isComplexType(type)) {
|
||
|
TypeName typeTokenType = ParameterizedTypeName.get(Const.TYPE_TOKEN, typeName);
|
||
|
List<? extends TypeMirror> typeParams = getGenericTypes(type);
|
||
|
if (typeParams.isEmpty()) {
|
||
|
block.add("new $T() {}", typeTokenType);
|
||
|
} else {
|
||
|
block.add("($T) $T.getParameterized($T.class, ", typeTokenType, Const.TYPE_TOKEN, typeUtils.erasure(type));
|
||
|
for (Iterator<? extends TypeMirror> iterator = typeParams.iterator(); iterator.hasNext(); ) {
|
||
|
TypeMirror typeParam = iterator.next();
|
||
|
int typeIndex = typeVariables.indexOf(TypeVariableName.get(typeParam.toString()));
|
||
|
block.add("(($T)typeToken.getType()).getActualTypeArguments()[$L]", ParameterizedType.class, typeIndex);
|
||
|
if (iterator.hasNext()) {
|
||
|
block.add(", ");
|
||
|
}
|
||
|
}
|
||
|
block.add(")");
|
||
|
}
|
||
|
} else if (isGenericType(type)) {
|
||
|
TypeName typeTokenType = ParameterizedTypeName.get(Const.TYPE_TOKEN, typeName);
|
||
|
int typeIndex = typeVariables.indexOf(TypeVariableName.get(property.getType().toString()));
|
||
|
block.add("($T) $T.get((($T)typeToken.getType()).getActualTypeArguments()[$L])",
|
||
|
typeTokenType, Const.TYPE_TOKEN, ParameterizedType.class, typeIndex);
|
||
|
} else {
|
||
|
if (allowClassType) {
|
||
|
block.add("$T.class", typeName);
|
||
|
} else {
|
||
|
block.add("TypeToken.get($T.class)", typeName);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private boolean isComplexType(TypeMirror type) {
|
||
|
Element element = typeUtils.asElement(type);
|
||
|
if (!(element instanceof TypeElement typeElement)) return false;
|
||
|
return !typeElement.getTypeParameters().isEmpty();
|
||
|
}
|
||
|
|
||
|
private boolean isGenericType(TypeMirror type) {
|
||
|
return type.getKind() == TypeKind.TYPEVAR;
|
||
|
}
|
||
|
|
||
|
private List<? extends TypeMirror> getGenericTypes(TypeMirror type) {
|
||
|
DeclaredType declaredType = asDeclaredType(type);
|
||
|
if (declaredType == null) {
|
||
|
return Collections.emptyList();
|
||
|
}
|
||
|
ArrayList<TypeMirror> result = new ArrayList<>();
|
||
|
for (TypeMirror argType : declaredType.getTypeArguments()) {
|
||
|
if (argType.getKind() == TypeKind.TYPEVAR) {
|
||
|
result.add(argType);
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
private DeclaredType findTypeAdapterClass(List<? extends AnnotationMirror> annotations) {
|
||
|
for (AnnotationMirror annotation : annotations) {
|
||
|
String typeName = annotation.getAnnotationType().toString();
|
||
|
if (typeName.equals(Const.JSON_ADAPTER.toString())) {
|
||
|
Map<? extends ExecutableElement, ? extends AnnotationValue> elements = annotation.getElementValues();
|
||
|
if (!elements.isEmpty()) {
|
||
|
AnnotationValue value = elements.values().iterator().next();
|
||
|
return (DeclaredType) value.getValue();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
private boolean isInstance(DeclaredType type, String parentClassName) {
|
||
|
if (type == null) return false;
|
||
|
TypeElement element = (TypeElement) type.asElement();
|
||
|
for (TypeMirror interfaceType : element.getInterfaces()) {
|
||
|
if (typeUtils.erasure(interfaceType).toString().equals(parentClassName)) return true;
|
||
|
}
|
||
|
TypeMirror superclassType = element.getSuperclass();
|
||
|
if (superclassType != null) {
|
||
|
if (typeUtils.erasure(superclassType).toString().equals(parentClassName)) {
|
||
|
return true;
|
||
|
} else {
|
||
|
return isInstance(asDeclaredType(superclassType), parentClassName);
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private static DeclaredType asDeclaredType(TypeMirror type) {
|
||
|
return type.accept(new SimpleTypeVisitor14<>() {
|
||
|
@Override
|
||
|
public DeclaredType visitDeclared(DeclaredType t, Object o) {
|
||
|
return t;
|
||
|
}
|
||
|
}, null);
|
||
|
}
|
||
|
}
|