Split AdapterAdapter

This commit is contained in:
Johannes Frohnmeyer 2022-11-01 11:38:37 +01:00
parent 096ead18f1
commit d4e056401d
Signed by: Johannes
GPG Key ID: E76429612C2929F4
7 changed files with 233 additions and 136 deletions

View File

@ -24,7 +24,14 @@ import java.util.stream.Collectors;
@SupportedOptions({"gsonCompileNoReflect"})
public class GsonCompileProcessor extends AbstractProcessor2 {
//TODO handle lists, sets, arrays, other common types (-> https://github.com/bluelinelabs/LoganSquare/blob/development/docs/TypeConverters.md)
private static final List<Adapter> ADAPTERS = List.of(new PrimitiveAdapter(), new StringAdapter(), new AdapterAdapter());
private static final List<Adapter> ADAPTERS = List.of(
new DeclaredAdapter(),
new PrimitiveAdapter(),
new StringAdapter(),
new OtherSerializableAdapter(),
new ReflectAdapter()
);
private Messager message;
private Filer filer;
private Set<ClassName> seen;
@ -392,7 +399,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
adapter.dehydrate();
return;
}
throw new IllegalArgumentException("Could not find adapter for property " + prop);
message.printMessage(Diagnostic.Kind.ERROR, "Could not find applicable adapter for property " + prop);
}
private void generateRead(Property<?> prop, TypeSpec.Builder klazz, CodeBlock.Builder code, List<TypeVariableName> typeVariables, Set<SerializableClass> otherAdapters) {
@ -404,7 +411,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
return;
} else adapter.dehydrate();
}
throw new IllegalArgumentException("Could not find adapter for property " + prop);
message.printMessage(Diagnostic.Kind.ERROR, "Could not find applicable adapter for property " + prop);
}
private static String getDefaultValue(TypeMirror type) {

View File

@ -0,0 +1,60 @@
package io.gitlab.jfronny.gson.compile.processor;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.*;
import javax.lang.model.util.SimpleTypeVisitor14;
import javax.lang.model.util.Types;
import java.util.*;
public class TypeHelper {
public static boolean isComplexType(TypeMirror type, Types typeUtils) {
Element element = typeUtils.asElement(type);
if (!(element instanceof TypeElement typeElement)) return false;
return !typeElement.getTypeParameters().isEmpty();
}
public static boolean isGenericType(TypeMirror type) {
return type.getKind() == TypeKind.TYPEVAR;
}
public static 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;
}
public static DeclaredType asDeclaredType(TypeMirror type) {
return type.accept(new SimpleTypeVisitor14<>() {
@Override
public DeclaredType visitDeclared(DeclaredType t, Object o) {
return t;
}
}, null);
}
public static boolean isInstance(DeclaredType type, String parentClassName, Types typeUtils) {
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, typeUtils);
}
}
return false;
}
}

View File

@ -23,6 +23,7 @@ public abstract class Adapter {
protected Messager message;
protected Types typeUtils;
protected Map<String, String> options;
protected TypeName typeName;
public abstract boolean applies();
public abstract void generateWrite(Runnable writeGet);
@ -47,6 +48,7 @@ public abstract class Adapter {
this.unboxedType = type;
}
this.argName = Const.ARG_PREFIX + this.property.getName();
this.typeName = TypeName.get(type).box();
}
public void dehydrate() {
@ -58,5 +60,6 @@ public abstract class Adapter {
this.code = null;
this.unboxedType = null;
this.argName = null;
this.typeName = null;
}
}

View File

@ -2,22 +2,14 @@ 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 io.gitlab.jfronny.gson.compile.processor.TypeHelper;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.lang.model.util.SimpleTypeVisitor14;
import javax.tools.Diagnostic;
import javax.lang.model.type.TypeMirror;
import java.lang.reflect.ParameterizedType;
import java.util.*;
public class AdapterAdapter extends Adapter {
@Override
public boolean applies() {
return true;
}
import java.util.Iterator;
import java.util.List;
public abstract class AdapterAdapter extends Adapter {
@Override
public void generateWrite(Runnable writeGet) {
code.add(getAdapter() + ".write(writer, ");
@ -35,148 +27,43 @@ public class AdapterAdapter extends Adapter {
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;
return createAdapter(typeAdapterName);
}
private void appendFieldTypeToken(CodeBlock.Builder block, Property<?> property, List<TypeVariableName> typeVariables, boolean allowClassType) {
protected abstract String createAdapter(String name);
protected void appendFieldTypeToken(boolean allowClassType) {
TypeMirror type = property.getType();
TypeName typeName = TypeName.get(type);
if (isComplexType(type)) {
if (TypeHelper.isComplexType(type, typeUtils)) {
TypeName typeTokenType = ParameterizedTypeName.get(Const.TYPE_TOKEN, typeName);
List<? extends TypeMirror> typeParams = getGenericTypes(type);
List<? extends TypeMirror> typeParams = TypeHelper.getGenericTypes(type);
if (typeParams.isEmpty()) {
block.add("new $T() {}", typeTokenType);
code.add("new $T() {}", typeTokenType);
} else {
block.add("($T) $T.getParameterized($T.class, ", typeTokenType, Const.TYPE_TOKEN, typeUtils.erasure(type));
code.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);
code.add("(($T)typeToken.getType()).getActualTypeArguments()[$L]", ParameterizedType.class, typeIndex);
if (iterator.hasNext()) {
block.add(", ");
code.add(", ");
}
}
block.add(")");
code.add(")");
}
} else if (isGenericType(type)) {
} else if (TypeHelper.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])",
code.add("($T) $T.get((($T)typeToken.getType()).getActualTypeArguments()[$L])",
typeTokenType, Const.TYPE_TOKEN, ParameterizedType.class, typeIndex);
} else {
if (allowClassType) {
block.add("$T.class", typeName);
code.add("$T.class", typeName);
} else {
block.add("TypeToken.get($T.class)", typeName);
code.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);
}
}

View File

@ -0,0 +1,70 @@
package io.gitlab.jfronny.gson.compile.processor.adapter;
import com.squareup.javapoet.*;
import io.gitlab.jfronny.gson.compile.processor.*;
import io.gitlab.jfronny.gson.compile.processor.util.valueprocessor.Property;
import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import javax.tools.Diagnostic;
import java.util.*;
public class DeclaredAdapter extends AdapterAdapter {
private DeclaredType typeAdapterClass;
@Override
public boolean applies() {
return typeAdapterClass != null;
}
@Override
public void hydrate(Property<?> property, TypeSpec.Builder klazz, CodeBlock.Builder code, List<TypeVariableName> typeVariables, Set<SerializableClass> other) {
super.hydrate(property, klazz, code, typeVariables, other);
this.typeAdapterClass = findTypeAdapterClass(property.getAnnotations());
}
@Override
public void dehydrate() {
super.dehydrate();
typeAdapterClass = null;
}
@Override
protected String createAdapter(String typeAdapterName) {
if (TypeHelper.isInstance(typeAdapterClass, Const.TYPE_ADAPTER.toString(), typeUtils)) {
klazz.addField(
FieldSpec.builder(TypeName.get(typeAdapterClass), typeAdapterName)
.addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
.initializer("new $T()", typeAdapterClass)
.build()
);
} else if (TypeHelper.isInstance(typeAdapterClass, Const.TYPE_ADAPTER_FACTORY.toString(), typeUtils)) {
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(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());
return typeAdapterName;
}
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;
}
}

View File

@ -0,0 +1,39 @@
package io.gitlab.jfronny.gson.compile.processor.adapter;
import com.squareup.javapoet.*;
import io.gitlab.jfronny.gson.compile.processor.SerializableClass;
import io.gitlab.jfronny.gson.compile.processor.util.valueprocessor.Property;
import java.util.List;
import java.util.Set;
public class OtherSerializableAdapter extends AdapterAdapter {
private String adapter;
@Override
public boolean applies() {
return adapter != null;
}
@Override
protected String createAdapter(String name) {
return adapter;
}
@Override
public void hydrate(Property<?> property, TypeSpec.Builder klazz, CodeBlock.Builder code, List<TypeVariableName> typeVariables, Set<SerializableClass> other) {
super.hydrate(property, klazz, code, typeVariables, other);
for (SerializableClass adapter : other) {
if (TypeName.get(adapter.classElement().asType()).equals(typeName)) {
// Use self-made adapter
this.adapter = adapter.generatedClassName().toString();
}
}
}
@Override
public void dehydrate() {
super.dehydrate();
this.adapter = null;
}
}

View File

@ -0,0 +1,31 @@
package io.gitlab.jfronny.gson.compile.processor.adapter;
import com.squareup.javapoet.*;
import io.gitlab.jfronny.gson.compile.processor.Const;
import javax.lang.model.element.Modifier;
import javax.tools.Diagnostic;
public class ReflectAdapter extends AdapterAdapter {
@Override
public boolean applies() {
return !options.containsKey("gsonCompileNoReflect");
}
@Override
protected String createAdapter(String typeAdapterName) {
message.printMessage(Diagnostic.Kind.WARNING, "Falling back to adapter detection for unsupported type " + type, property.getElement());
TypeName typeAdapterType = ParameterizedTypeName.get(Const.TYPE_ADAPTER, typeName);
CodeBlock.Builder block = CodeBlock.builder();
block.add("$T.HOLDER.getGson().getAdapter(", Const.CCORE);
appendFieldTypeToken(true);
block.add(")");
klazz.addField(
FieldSpec.builder(typeAdapterType, typeAdapterName)
.addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
.initializer(block.build())
.build()
);
return typeAdapterName;
}
}