Support for nested types within same sources
This commit is contained in:
parent
1da2263bef
commit
37bc7f9782
|
@ -30,4 +30,4 @@ The goal of this AP is to
|
|||
## Credit
|
||||
The Gson-Compile processor is based on [gsonvalue](https://github.com/evant/gsonvalue) and [value-processor](https://github.com/evant/value-processor) by Eva Tatarka.
|
||||
The API was inspired by [kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) by Jetbrains
|
||||
The serialization is based on [my fork](https://gitlab.com/JFronny/gson-comments) of [gson](https://github.com/google/gson) by Google
|
||||
The serialization is powered by [my fork](https://gitlab.com/JFronny/gson-comments) of [gson](https://github.com/google/gson) by Google
|
|
@ -5,5 +5,5 @@ import java.lang.annotation.*;
|
|||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface GComment {
|
||||
String value() default "";
|
||||
String value();
|
||||
}
|
||||
|
|
|
@ -21,5 +21,13 @@ public class Main {
|
|||
public String getBass() {
|
||||
return "Yes";
|
||||
}
|
||||
|
||||
public ExamplePojo2 nested;
|
||||
}
|
||||
|
||||
@GSerializable
|
||||
public static class ExamplePojo2 {
|
||||
@GComment("Yes!")
|
||||
public boolean primitive;
|
||||
}
|
||||
}
|
|
@ -47,6 +47,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
|
||||
Set<ToProcess> toProcesses = new LinkedHashSet<>();
|
||||
// Gather all serializable types
|
||||
for (TypeElement annotation : annotations) {
|
||||
for (Element element : roundEnvironment.getElementsAnnotatedWith(annotation)) {
|
||||
for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
|
||||
|
@ -73,14 +74,21 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
if (bld.generateAdapter == null) throw new IllegalArgumentException("Missing annotation parameter: generateAdapter");
|
||||
if (bld.with.toString().equals("void")) bld.with = null;
|
||||
if (bld.with != null && bld.generateAdapter) throw new IllegalArgumentException("Adapter for " + element + " already exists, not generating another!");
|
||||
toProcesses.add(new ToProcess((TypeElement) element, bld.with, bld.generateAdapter));
|
||||
|
||||
TypeElement typeElement = (TypeElement) element;
|
||||
ClassName className = ClassName.get(typeElement);
|
||||
ClassName generatedClassName = ClassName.get(className.packageName(), Const.PREFIX + String.join("_", className.simpleNames()));
|
||||
toProcesses.add(new ToProcess(typeElement, generatedClassName, bld.with, bld.generateAdapter));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Do not allow mutation past this point, especially not from individual process tasks
|
||||
toProcesses = Set.copyOf(toProcesses);
|
||||
// Generate adapters
|
||||
for (ToProcess toProcess : toProcesses) {
|
||||
try {
|
||||
process(toProcess.element, toProcess.adapter, toProcess.generateAdapter);
|
||||
process(toProcess.element, toProcess.generatedClassName, toProcess.adapter, toProcess.generateAdapter, toProcesses);
|
||||
} catch (IOException | ElementException e) {
|
||||
message.printMessage(Diagnostic.Kind.ERROR, "GsonCompile threw an exception: " + StringFormatter.toString(e), toProcess.element);
|
||||
}
|
||||
|
@ -88,13 +96,12 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
return false;
|
||||
}
|
||||
|
||||
record ToProcess(TypeElement element, @Nullable TypeMirror adapter, boolean generateAdapter) {}
|
||||
record ToProcess(TypeElement element, ClassName generatedClassName, @Nullable TypeMirror adapter, boolean generateAdapter) {}
|
||||
|
||||
private void process(TypeElement classElement, @Nullable TypeMirror adapter, boolean generateAdapter) throws IOException, ElementException {
|
||||
private void process(TypeElement classElement, ClassName generatedClassName, @Nullable TypeMirror adapter, boolean generateAdapter, Set<ToProcess> other) throws IOException, ElementException {
|
||||
ClassName className = ClassName.get(classElement);
|
||||
if (!seen.add(className)) return; // Don't process the same class more than once
|
||||
|
||||
ClassName generatedClassName = ClassName.get(className.packageName(), Const.PREFIX + String.join("_", className.simpleNames()));
|
||||
TypeName classType = TypeName.get(classElement.asType());
|
||||
List<TypeVariableName> typeVariables = new ArrayList<>();
|
||||
if (classType instanceof ParameterizedTypeName type) {
|
||||
|
@ -114,13 +121,11 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
if (generateAdapter) {
|
||||
generateDelegatingAdapter(spec, classType, generatedClassName);
|
||||
}
|
||||
generateSerialisation(spec, classType, classElement, typeVariables);
|
||||
generateSerialisation(spec, classType, classElement, typeVariables, other);
|
||||
}
|
||||
|
||||
generateAuxiliary(spec, classType);
|
||||
|
||||
//TODO register adapter as available and use in generated adapters
|
||||
|
||||
JavaFile javaFile = JavaFile.builder(className.packageName(), spec.build())
|
||||
.skipJavaLangImports(true)
|
||||
.indent(" ")
|
||||
|
@ -264,7 +269,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
);
|
||||
}
|
||||
|
||||
private void generateSerialisation(TypeSpec.Builder spec, TypeName classType, TypeElement classElement, List<TypeVariableName> typeVariables) throws ElementException {
|
||||
private void generateSerialisation(TypeSpec.Builder spec, TypeName classType, TypeElement classElement, List<TypeVariableName> typeVariables, Set<ToProcess> otherAdapters) throws ElementException {
|
||||
Value value = valueCreator.from(classElement);
|
||||
ConstructionSource constructionSource = value.getConstructionSource();
|
||||
Properties properties = value.getProperties();
|
||||
|
@ -281,12 +286,12 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
for (Property.Field field : properties.fields) {
|
||||
generateComments(field, code);
|
||||
code.addStatement("writer.name($S)", getSerializedName(field));
|
||||
generateWrite(field, spec, code, "writer", "value.$N", typeVariables);
|
||||
generateWrite(field, spec, code, "writer", "value.$N", typeVariables, otherAdapters);
|
||||
}
|
||||
for (Property.Getter getter : properties.getters) {
|
||||
generateComments(getter, code);
|
||||
code.addStatement("writer.name($S)", getSerializedName(getter));
|
||||
generateWrite(getter, spec, code, "writer", "value.$N()", typeVariables);
|
||||
generateWrite(getter, spec, code, "writer", "value.$N()", typeVariables, otherAdapters);
|
||||
}
|
||||
code.addStatement("writer.endObject()");
|
||||
|
||||
|
@ -319,7 +324,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
.beginControlFlow("while (reader.hasNext())")
|
||||
.beginControlFlow("switch (reader.nextName())");
|
||||
for (Property<?> param : properties.names) {
|
||||
String read = generateRead(param, spec, "reader", typeVariables);
|
||||
String read = generateRead(param, spec, "reader", typeVariables, otherAdapters);
|
||||
if (param.getType().getKind().isPrimitive()) {
|
||||
code.add("case $S -> ", getSerializedName(param));
|
||||
code.addStatement("$L = $L", Const.ARG_PREFIX + param.getName(), read);
|
||||
|
@ -381,7 +386,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
}
|
||||
}
|
||||
|
||||
private void generateWrite(Property<?> prop, TypeSpec.Builder klazz, CodeBlock.Builder code, String writer, String get, List<TypeVariableName> typeVariables) {
|
||||
private void generateWrite(Property<?> prop, TypeSpec.Builder klazz, CodeBlock.Builder code, String writer, String get, List<TypeVariableName> typeVariables, Set<ToProcess> otherAdapters) {
|
||||
TypeMirror unboxed = unbox(prop.getType());
|
||||
if (unbox(prop.getType()).getKind().isPrimitive() || prop.getType().toString().equals(String.class.getCanonicalName())) {
|
||||
if (prop.getType().equals(unboxed)) {
|
||||
|
@ -397,10 +402,10 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
}
|
||||
//TODO handle lists, sets, arrays, other common types (-> https://github.com/bluelinelabs/LoganSquare/blob/development/docs/TypeConverters.md)
|
||||
//TODO support comment annotations
|
||||
code.addStatement(getAdapter(prop, klazz, typeVariables) + ".write(" + writer + ", " + get + ")", prop.getCallableName());
|
||||
code.addStatement(getAdapter(prop, klazz, typeVariables, otherAdapters) + ".write(" + writer + ", " + get + ")", prop.getCallableName());
|
||||
}
|
||||
|
||||
private String generateRead(Property<?> prop, TypeSpec.Builder klazz, String reader, List<TypeVariableName> typeVariables) {
|
||||
private String generateRead(Property<?> prop, TypeSpec.Builder klazz, String reader, List<TypeVariableName> typeVariables, Set<ToProcess> otherAdapters) {
|
||||
TypeMirror unboxed = unbox(prop.getType());
|
||||
if (unboxed.getKind().isPrimitive()) {
|
||||
return switch (unboxed.getKind()) {
|
||||
|
@ -419,10 +424,10 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
return reader + ".nextString()";
|
||||
}
|
||||
//TODO handle lists, sets, arrays, other common types (-> https://github.com/bluelinelabs/LoganSquare/blob/development/docs/TypeConverters.md)
|
||||
return getAdapter(prop, klazz, typeVariables) + ".read(" + reader + ")";
|
||||
return getAdapter(prop, klazz, typeVariables, otherAdapters) + ".read(" + reader + ")";
|
||||
}
|
||||
|
||||
private String getAdapter(Property<?> prop, TypeSpec.Builder klazz, List<TypeVariableName> typeVariables) {
|
||||
private String getAdapter(Property<?> prop, TypeSpec.Builder klazz, List<TypeVariableName> typeVariables, Set<ToProcess> otherAdapters) {
|
||||
String typeAdapterName = Const.ADAPTER_PREFIX + prop.getName();
|
||||
for (FieldSpec spec : klazz.fieldSpecs) {
|
||||
if (spec.name.equals(typeAdapterName)) return typeAdapterName;
|
||||
|
@ -450,9 +455,16 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
);
|
||||
} else message.printMessage(Diagnostic.Kind.ERROR, "@JsonAdapter value must by TypeAdapter or TypeAdapterFactory reference.", prop.getElement());
|
||||
} else {
|
||||
TypeName targetType = TypeName.get(prop.getType()).box();
|
||||
for (ToProcess adapter : otherAdapters) {
|
||||
if (TypeName.get(adapter.element.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 " + prop.getType(), prop.getElement());
|
||||
//TODO handle known custom type adapters and return proper static class
|
||||
TypeName typeAdapterType = ParameterizedTypeName.get(Const.TYPE_ADAPTER, TypeName.get(prop.getType()).box());
|
||||
TypeName typeAdapterType = ParameterizedTypeName.get(Const.TYPE_ADAPTER, targetType);
|
||||
CodeBlock.Builder block = CodeBlock.builder();
|
||||
block.add("$T.HOLDER.getGson().getAdapter(", Const.CCORE);
|
||||
appendFieldTypeToken(block, prop, typeVariables, true);
|
||||
|
|
Loading…
Reference in New Issue