Split AdapterAdapter
This commit is contained in:
parent
096ead18f1
commit
d4e056401d
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue