gson-compile/gson-compile-processor/src/main/java/io/gitlab/jfronny/gson/compile/processor/GsonCompileProcessor.java

551 lines
28 KiB
Java
Raw Normal View History

2022-10-31 20:52:48 +01:00
package io.gitlab.jfronny.gson.compile.processor;
import com.squareup.javapoet.*;
import io.gitlab.jfronny.commons.StringFormatter;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
2022-11-01 13:48:23 +01:00
import io.gitlab.jfronny.gson.compile.processor.adapter.Adapter;
import io.gitlab.jfronny.gson.compile.processor.adapter.Adapters;
2022-10-31 20:52:48 +01:00
import io.gitlab.jfronny.gson.compile.processor.util.AbstractProcessor2;
import io.gitlab.jfronny.gson.compile.processor.util.SupportedAnnotationTypes2;
import io.gitlab.jfronny.gson.compile.processor.util.valueprocessor.Properties;
import io.gitlab.jfronny.gson.compile.processor.util.valueprocessor.*;
2022-10-31 20:52:48 +01:00
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
2022-10-31 20:52:48 +01:00
import javax.tools.Diagnostic;
import java.io.*;
2022-11-01 18:23:44 +01:00
import java.nio.file.*;
2022-10-31 20:52:48 +01:00
import java.util.*;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
2022-10-31 20:52:48 +01:00
@SupportedSourceVersion(SourceVersion.RELEASE_17)
@SupportedAnnotationTypes2({GSerializable.class})
@SupportedOptions({"gsonCompileNoReflect"})
2022-10-31 20:52:48 +01:00
public class GsonCompileProcessor extends AbstractProcessor2 {
private Messager message;
private Filer filer;
private Map<ClassName, TypeSpec.Builder> seen;
2022-10-31 20:52:48 +01:00
private ValueCreator valueCreator;
private Elements elements;
private boolean hasManifold = false;
2022-10-31 20:52:48 +01:00
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
try {
Class.forName("manifold.ext.Model");
System.out.println("Detected manifold!");
hasManifold = true;
} catch (ClassNotFoundException e) {
hasManifold = false;
}
2022-11-24 20:35:28 +01:00
message = processingEnv.messager;
filer = processingEnv.filer;
elements = processingEnv.elementUtils;
seen = new LinkedHashMap<>();
2022-10-31 20:52:48 +01:00
valueCreator = new ValueCreator(processingEnv);
2022-11-01 13:48:23 +01:00
for (Adapter adapter : Adapters.ADAPTERS) {
adapter.init(processingEnv);
}
2022-10-31 20:52:48 +01:00
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
Set<SerializableClass> toGenerate = new LinkedHashSet<>();
// Gather all serializable types
2022-10-31 20:52:48 +01:00
for (TypeElement annotation : annotations) {
for (Element element : roundEnvironment.getElementsAnnotatedWith(annotation)) {
2022-11-24 20:35:28 +01:00
for (AnnotationMirror mirror : element.annotationMirrors) {
2022-11-01 15:39:50 +01:00
try {
2022-11-24 20:35:28 +01:00
if (mirror.annotationType.toString().equals(GSerializable.class.getCanonicalName())) {
2022-11-01 15:39:50 +01:00
var bld = new Object() {
TypeMirror with = null;
TypeMirror builder = null;
TypeMirror configure = null;
2022-11-01 15:39:50 +01:00
Boolean generateAdapter = null;
};
elements.getElementValuesWithDefaults(mirror).forEach((executableElement, value) -> {
2022-11-24 20:35:28 +01:00
String name = executableElement.simpleName.toString();
2022-11-01 15:39:50 +01:00
switch (name) {
case "with" -> {
if (bld.with != null) throw new IllegalArgumentException("Duplicate annotation parameter: with");
2022-11-24 20:35:28 +01:00
bld.with = (TypeMirror) value.value;
2022-11-01 15:39:50 +01:00
}
case "builder" -> {
if (bld.builder != null) throw new IllegalArgumentException("Duplicate annotation parameter: builder");
2022-11-24 20:35:28 +01:00
bld.builder = (TypeMirror) value.value;
2022-11-01 15:39:50 +01:00
}
case "configure" -> {
if (bld.configure != null) throw new IllegalArgumentException("Duplicate annotation parameter: configure");
2022-11-24 20:35:28 +01:00
bld.configure = (TypeMirror) value.value;
}
2022-11-01 15:39:50 +01:00
case "generateAdapter" -> {
if (bld.generateAdapter != null) throw new IllegalArgumentException("Duplicate annotation parameter: generateAdapter");
2022-11-24 20:35:28 +01:00
bld.generateAdapter = (Boolean) value.value;
2022-11-01 15:39:50 +01:00
}
default -> throw new IllegalArgumentException("Unexpected annotation parameter: " + name);
2022-10-31 20:52:48 +01:00
}
2022-11-01 15:39:50 +01:00
});
if (bld.with == null) throw new IllegalArgumentException("Missing annotation parameter: with");
if (bld.builder == null) throw new IllegalArgumentException("Missing annotation parameter: builder");
if (bld.configure == null) throw new IllegalArgumentException("Missing annotation parameter: configure");
2022-11-01 15:39:50 +01:00
if (bld.generateAdapter == null) throw new IllegalArgumentException("Missing annotation parameter: generateAdapter");
toGenerate.add(SerializableClass.of((TypeElement) element, bld.with, bld.builder, bld.configure, bld.generateAdapter, hasManifold));
2022-11-01 15:39:50 +01:00
}
} catch (ElementException e) {
e.printMessage(message);
2022-10-31 20:52:48 +01:00
}
}
}
}
// Do not allow mutation past this point, especially not from individual process tasks
toGenerate = Set.copyOf(toGenerate);
// Generate adapters
for (SerializableClass toProcess : toGenerate) {
2022-10-31 20:52:48 +01:00
try {
process(toProcess, toGenerate);
2022-11-01 15:39:50 +01:00
} catch (ElementException e) {
e.printMessage(message);
2022-10-31 20:52:48 +01:00
}
}
for (var entry : seen.keySet().stream().collect(Collectors.groupingBy(ClassName::packageName)).entrySet()) {
Map<List<String>, TypeSpec.Builder> known = entry.value.stream()
.collect(Collectors.toMap(
ClassName::simpleNames,
seen::get,
(u, v) -> u,
() -> new TreeMap<>(StringListComparator.INSTANCE.reversed())
));
// Generate additional parent classes
for (List<String> klazz : known.keySet().stream().toList()) {
List<String> current = new LinkedList<>();
for (String s : klazz) {
current.add(s);
TypeSpec.Builder builder = find(known, current);
if (builder == null) {
builder = TypeSpec.classBuilder(s).addModifiers(Modifier.PUBLIC);
2022-11-24 19:44:20 +01:00
if (current.size() == 1 && hasManifold) builder.addAnnotation(Cl.MANIFOLD_EXTENSION);
known.put(List.copyOf(current), builder);
}
if (current.size() > 1) builder.addModifiers(Modifier.STATIC);
}
}
// Add to parent class
for (var entry1 : known.entrySet()) {
2022-11-24 20:35:28 +01:00
if (entry1.key.size() == 1) continue;
find(known, entry1.key.subList(0, entry1.key.size() - 1)).addType(entry1.value.build());
}
// Print
// System.out.println("Got " + known.size() + " classes");
// for (var entry1 : known.entrySet()) {
2022-11-24 20:35:28 +01:00
// System.out.println("Class " + entry.key + '.' + String.join(".", entry1.key));
// for (TypeSpec typeSpec : entry1.value.typeSpecs) {
// System.out.println("- " + typeSpec.name);
// }
// }
// Write top-level classes
for (var entry1 : known.entrySet()) {
2022-11-24 20:35:28 +01:00
if (entry1.key.size() == 1) {
JavaFile javaFile = JavaFile.builder(entry.key, entry1.value.build())
.skipJavaLangImports(true)
.indent(" ")
.build();
try {
javaFile.writeTo(filer);
} catch (IOException e) {
message.printMessage(Diagnostic.Kind.ERROR, "Could not write source: " + StringFormatter.toString(e));
}
}
}
}
seen.clear();
2022-10-31 20:52:48 +01:00
return false;
}
private <K, V> V find(Map<List<K>, V> map, List<K> key) {
2022-11-24 20:35:28 +01:00
for (var entry : map.entrySet()) if (entry.key.equals(key)) return entry.value;
return null;
}
private void process(SerializableClass toProcess, Set<SerializableClass> other) throws ElementException {
2022-11-24 20:35:28 +01:00
if (seen.containsKey(toProcess.generatedClassName)) return; // Don't process the same class more than once
2022-10-31 20:52:48 +01:00
2022-11-24 20:35:28 +01:00
TypeName classType = toProcess.typeName;
2022-10-31 20:52:48 +01:00
List<TypeVariableName> typeVariables = new ArrayList<>();
if (classType instanceof ParameterizedTypeName type) {
for (TypeName argument : type.typeArguments) {
typeVariables.add(TypeVariableName.get(argument.toString()));
}
}
2022-11-24 20:35:28 +01:00
TypeSpec.Builder spec = TypeSpec.classBuilder(toProcess.generatedClassName.simpleName())
.addOriginatingElement(toProcess.classElement)
.addModifiers(Modifier.PUBLIC)
.addTypeVariables(typeVariables);
2022-11-24 20:35:28 +01:00
seen.put(toProcess.generatedClassName, spec);
2022-10-31 20:52:48 +01:00
2022-11-24 20:35:28 +01:00
if (toProcess.adapter != null) {
generateDelegateToAdapter(spec, classType, toProcess.adapter);
2022-10-31 20:52:48 +01:00
} else {
2022-11-24 20:35:28 +01:00
if (toProcess.generateAdapter) {
generateDelegatingAdapter(spec, classType, toProcess.generatedClassName);
2022-10-31 20:52:48 +01:00
}
2022-11-01 15:39:50 +01:00
generateSerialisation(spec, toProcess, typeVariables, other);
2022-10-31 20:52:48 +01:00
}
generateAuxiliary(spec, classType, toProcess.configure());
2022-10-31 20:52:48 +01:00
}
private static void generateDelegatingAdapter(TypeSpec.Builder spec, TypeName classType, ClassName generatedClassName) {
spec.addType(
TypeSpec.classBuilder("Adapter")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
2022-11-01 13:48:23 +01:00
.superclass(ParameterizedTypeName.get(Cl.TYPE_ADAPTER, classType))
2022-10-31 20:52:48 +01:00
.addMethod(MethodSpec.methodBuilder("write")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
2022-11-01 13:48:23 +01:00
.addParameter(Cl.GSON_WRITER, "writer")
2022-10-31 20:52:48 +01:00
.addParameter(classType, "value")
.addException(IOException.class)
2022-11-24 20:35:28 +01:00
.addCode(generatedClassName.simpleName + ".write(value, writer);")
2022-10-31 20:52:48 +01:00
.build())
.addMethod(MethodSpec.methodBuilder("read")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
2022-11-01 13:48:23 +01:00
.addParameter(Cl.GSON_READER, "reader")
2022-10-31 20:52:48 +01:00
.addException(IOException.class)
.returns(classType)
2022-11-24 20:35:28 +01:00
.addCode("return " + generatedClassName.simpleName + ".read(reader);")
2022-10-31 20:52:48 +01:00
.build())
.build()
);
}
private void generateDelegateToAdapter(TypeSpec.Builder spec, TypeName classType, TypeMirror adapter) {
2022-10-31 20:52:48 +01:00
spec.addMethod(
extension(MethodSpec.methodBuilder("read"))
2022-10-31 20:52:48 +01:00
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
2022-11-01 13:48:23 +01:00
.addParameter(Cl.GSON_READER, "reader")
2022-10-31 20:52:48 +01:00
.addException(IOException.class)
.returns(classType)
2022-11-01 15:24:59 +01:00
.addCode("return $T.read(reader);", adapter)
2022-10-31 20:52:48 +01:00
.build()
);
spec.addMethod(
2022-11-24 19:57:20 +01:00
extension(MethodSpec.methodBuilder("write"), classType)
2022-10-31 20:52:48 +01:00
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
2022-11-24 19:57:20 +01:00
.addParameter(Cl.GSON_WRITER, "writer")
2022-10-31 20:52:48 +01:00
.addException(IOException.class)
2022-11-01 23:11:29 +01:00
.addCode("$T.write(writer, value);", adapter)
2022-10-31 20:52:48 +01:00
.build()
);
}
private void generateAuxiliary(TypeSpec.Builder spec, TypeName classType, TypeMirror configure) {
2022-11-23 17:41:28 +01:00
final UnaryOperator<CodeBlock.Builder> configureReader = cb -> {
if (configure != null) cb.addStatement("$T.configure(reader)", configure);
return cb;
};
2022-11-23 17:41:28 +01:00
final UnaryOperator<CodeBlock.Builder> configureWriter = cb -> {
if (configure != null) cb.addStatement("$T.configure(writer)", configure);
return cb;
};
2022-11-23 17:41:28 +01:00
final String readStatement = "return read(reader)";
2022-11-24 19:57:20 +01:00
final String writeStatement = "write(value, writer)";
2022-11-23 17:41:28 +01:00
2022-10-31 20:52:48 +01:00
spec.addMethod(
extension(MethodSpec.methodBuilder("read"))
2022-10-31 20:52:48 +01:00
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(TypeName.get(Reader.class), "in")
.addException(IOException.class)
.returns(classType)
.addCode(configureReader.apply(CodeBlock.builder().beginControlFlow("try ($1T reader = new $1T(in))", Cl.GSON_READER))
2022-11-23 17:41:28 +01:00
.addStatement(readStatement)
.endControlFlow()
.build())
2022-10-31 20:52:48 +01:00
.build()
);
spec.addMethod(
extension(MethodSpec.methodBuilder("read"))
2022-10-31 20:52:48 +01:00
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(TypeName.get(String.class), "json")
.addException(IOException.class)
.returns(classType)
.addCode(CodeBlock.builder().beginControlFlow("try ($1T reader = new $1T(json))", StringReader.class)
2022-11-23 17:41:28 +01:00
.addStatement(readStatement)
.endControlFlow()
.build())
2022-10-31 20:52:48 +01:00
.build()
);
spec.addMethod(
extension(MethodSpec.methodBuilder("read"))
2022-10-31 20:52:48 +01:00
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
2022-11-01 13:48:23 +01:00
.addParameter(Cl.GSON_ELEMENT, "tree")
2022-10-31 20:52:48 +01:00
.addException(IOException.class)
.returns(classType)
.addCode(configureReader.apply(CodeBlock.builder().beginControlFlow("try ($1T reader = new $1T(tree))", Cl.GSON_TREE_READER))
2022-11-23 17:41:28 +01:00
.addStatement(readStatement)
.endControlFlow()
.build())
2022-10-31 20:52:48 +01:00
.build()
);
2022-11-01 18:23:44 +01:00
spec.addMethod(
extension(MethodSpec.methodBuilder("read"))
2022-11-01 18:23:44 +01:00
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(Path.class, "path")
.addException(IOException.class)
.returns(classType)
2022-11-23 17:41:28 +01:00
.addCode(CodeBlock.builder().beginControlFlow("try ($T reader = $T.newBufferedReader(path))", BufferedReader.class, Files.class)
.addStatement(readStatement)
.endControlFlow()
.build())
2022-11-01 18:23:44 +01:00
.build()
);
2022-10-31 20:52:48 +01:00
spec.addMethod(
2022-11-24 19:57:20 +01:00
extension(MethodSpec.methodBuilder("write"), classType)
2022-10-31 20:52:48 +01:00
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
2022-11-24 19:57:20 +01:00
.addParameter(Writer.class, "out")
2022-10-31 20:52:48 +01:00
.addException(IOException.class)
.addCode(configureWriter.apply(CodeBlock.builder().beginControlFlow("try ($1T writer = new $1T(out))", Cl.GSON_WRITER))
2022-11-23 17:41:28 +01:00
.addStatement(writeStatement)
.endControlFlow()
.build())
2022-10-31 20:52:48 +01:00
.build()
);
2022-11-01 18:23:44 +01:00
spec.addMethod(
2022-11-24 19:57:20 +01:00
extension(MethodSpec.methodBuilder("write"), classType)
2022-11-01 18:23:44 +01:00
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
2022-11-24 19:57:20 +01:00
.addParameter(Path.class, "path")
2022-11-01 18:23:44 +01:00
.addException(IOException.class)
2022-11-23 17:41:28 +01:00
.addCode(CodeBlock.builder().beginControlFlow("try ($1T writer = $2T.newBufferedWriter(path, $3T.CREATE, $3T.WRITE, $3T.TRUNCATE_EXISTING))", BufferedWriter.class, Files.class, StandardOpenOption.class)
.addStatement(writeStatement)
.endControlFlow()
.build())
2022-11-01 18:23:44 +01:00
.build()
);
2022-10-31 20:52:48 +01:00
spec.addMethod(
2022-11-23 17:41:28 +01:00
extension(MethodSpec.methodBuilder("toJson"), classType)
2022-10-31 20:52:48 +01:00
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addException(IOException.class)
.returns(String.class)
.addCode(CodeBlock.builder().beginControlFlow("try ($1T writer = new $1T())", StringWriter.class)
2022-11-23 17:41:28 +01:00
.addStatement(writeStatement)
.addStatement("return writer.toString()")
.endControlFlow()
.build())
2022-10-31 20:52:48 +01:00
.build()
);
spec.addMethod(
2022-11-23 17:41:28 +01:00
extension(MethodSpec.methodBuilder("toJsonTree"), classType)
2022-10-31 20:52:48 +01:00
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addException(IOException.class)
2022-11-01 13:48:23 +01:00
.returns(Cl.GSON_ELEMENT)
.addCode(configureWriter.apply(CodeBlock.builder().beginControlFlow("try ($1T writer = new $1T())", Cl.GSON_TREE_WRITER))
2022-11-23 17:41:28 +01:00
.addStatement(writeStatement)
.addStatement("return writer.get()")
.endControlFlow()
.build())
2022-10-31 20:52:48 +01:00
.build()
);
}
2022-11-01 15:39:50 +01:00
private void generateSerialisation(TypeSpec.Builder spec, SerializableClass self, List<TypeVariableName> typeVariables, Set<SerializableClass> otherAdapters) throws ElementException {
2022-11-24 20:35:28 +01:00
Value value = self.builder == null ? valueCreator.from(self.classElement, false) : valueCreator.from(TypeHelper.asDeclaredType(self.builder).asElement(), true);
ConstructionSource constructionSource = value.constructionSource;
Properties properties = value.properties;
2022-10-31 20:52:48 +01:00
// public static void write(JsonWriter writer, T value) throws IOException
{
CodeBlock.Builder code = CodeBlock.builder();
code.beginControlFlow("if (value == null)")
.addStatement("writer.nullValue()")
.addStatement("return")
.endControlFlow();
code.addStatement("writer.beginObject()");
2022-11-01 13:48:23 +01:00
for (Property.Field param : properties.fields) {
2022-11-01 16:47:00 +01:00
if (Properties.containsName(properties.getters, param)) continue;
2022-11-24 20:35:28 +01:00
Runnable writeGet = () -> code.add("value.$N", param.callableName);
if (param.type.kind.isPrimitive) {
2022-11-04 13:46:37 +01:00
generateComments(param, code);
code.addStatement("writer.name($S)", getSerializedName(param));
2022-11-01 13:48:23 +01:00
Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, message, writeGet);
} else {
2022-11-24 20:35:28 +01:00
code.beginControlFlow("if (value.$N != null || writer.getSerializeNulls())", param.callableName);
2022-11-04 13:46:37 +01:00
generateComments(param, code);
code.addStatement("writer.name($S)", getSerializedName(param));
2022-11-24 20:35:28 +01:00
code.addStatement("if (value.$N == null) writer.nullValue()", param.callableName);
2022-11-04 13:46:37 +01:00
code.beginControlFlow("else");
2022-11-01 13:48:23 +01:00
Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, message, writeGet);
code.endControlFlow();
2022-11-04 13:46:37 +01:00
code.endControlFlow();
2022-11-01 13:48:23 +01:00
}
2022-10-31 20:52:48 +01:00
}
2022-11-01 13:48:23 +01:00
for (Property.Getter param : properties.getters) {
2022-11-24 20:35:28 +01:00
if (param.type.kind.isPrimitive) {
2022-11-04 13:46:37 +01:00
generateComments(param, code);
code.addStatement("writer.name($S)", getSerializedName(param));
2022-11-24 20:35:28 +01:00
Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, message, () -> code.add("value.$N()", param.callableName));
2022-11-01 13:48:23 +01:00
} else {
2022-11-24 20:35:28 +01:00
code.addStatement("$T $L$N = value.$N()", param.type, "$", param.callableName, param.callableName);
code.beginControlFlow("if ($L$N != null || writer.getSerializeNulls())", "$", param.callableName);
2022-11-04 13:46:37 +01:00
generateComments(param, code);
code.addStatement("writer.name($S)", getSerializedName(param));
2022-11-24 20:35:28 +01:00
code.addStatement("if ($L$N == null) writer.nullValue()", "$", param.callableName);
2022-11-04 13:46:37 +01:00
code.beginControlFlow("else");
2022-11-24 20:35:28 +01:00
Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, message, () -> code.add("$L$N", "$", param.callableName));
2022-11-01 13:48:23 +01:00
code.endControlFlow();
2022-11-04 13:46:37 +01:00
code.endControlFlow();
2022-11-01 13:48:23 +01:00
}
2022-10-31 20:52:48 +01:00
}
code.addStatement("writer.endObject()");
2022-11-24 20:35:28 +01:00
spec.addMethod(extension(MethodSpec.methodBuilder("write"), self.typeName)
2022-10-31 20:52:48 +01:00
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
2022-11-24 19:57:20 +01:00
.addParameter(Cl.GSON_WRITER, "writer")
2022-10-31 20:52:48 +01:00
.addException(IOException.class)
.addCode(code.build())
.build());
}
// public static T read(JsonReader reader) throws IOException
{
CodeBlock.Builder code = CodeBlock.builder();
2022-11-01 13:48:23 +01:00
code.beginControlFlow("if (reader.peek() == $T.NULL)", Cl.GSON_TOKEN)
2022-10-31 20:52:48 +01:00
.addStatement("reader.nextNull()")
.addStatement("return null")
.endControlFlow();
boolean isEmpty = true;
for (Property<?> param : properties.names) {
isEmpty = false;
2022-11-24 20:35:28 +01:00
code.addStatement("$T _$N = $L", param.type, param.name, TypeHelper.getDefaultValue(param.type));
2022-10-31 20:52:48 +01:00
}
if (isEmpty) {
code.addStatement("reader.skipValue()");
} else {
code.addStatement("reader.beginObject()")
.beginControlFlow("while (reader.hasNext())")
.beginControlFlow("switch (reader.nextName())");
for (Property<?> param : properties.names) {
2022-11-24 20:35:28 +01:00
if (param.type.kind.isPrimitive) {
code.add("case $S -> _$N = ", getSerializedName(param), param.name);
2022-11-01 13:48:23 +01:00
Adapters.generateRead(param, spec, code, typeVariables, otherAdapters, message);
code.add(";\n");
2022-10-31 20:52:48 +01:00
} else {
code.beginControlFlow("case $S ->", getSerializedName(param))
2022-11-01 13:48:23 +01:00
.beginControlFlow("if (reader.peek() == $T.NULL)", Cl.GSON_TOKEN)
2022-10-31 20:52:48 +01:00
.addStatement("reader.nextNull()")
2022-11-24 20:35:28 +01:00
.addStatement("_$N = null", param.name);
code.unindent().add("} else _$N = ", param.name);
2022-11-01 13:48:23 +01:00
Adapters.generateRead(param, spec, code, typeVariables, otherAdapters, message);
code.add(";\n")
2022-10-31 20:52:48 +01:00
.endControlFlow();
}
}
code.add("default -> ")
.addStatement("reader.skipValue()");
code.endControlFlow()
.endControlFlow()
.addStatement("reader.endObject()");
}
2022-11-24 20:35:28 +01:00
code.addStatement("$T result", self.typeName);
ClassName creatorName = ClassName.get((TypeElement) constructionSource.constructionElement.enclosingElement);
2022-10-31 20:52:48 +01:00
if (constructionSource instanceof ConstructionSource.Builder builder) {
2022-11-01 16:47:00 +01:00
StringBuilder args = new StringBuilder();
for (Property.ConstructorParam param : properties.constructorParams) {
2022-11-24 20:35:28 +01:00
args.append(", _").append(param.name);
2022-11-01 16:47:00 +01:00
}
2022-11-24 20:35:28 +01:00
code.add("$T builder = ", builder.builderClass);
if (constructionSource.isConstructor) {
code.add("new $T($L)", builder.builderClass, args.length() > 0 ? args.substring(2) : "");
2022-10-31 20:52:48 +01:00
} else {
2022-11-24 20:35:28 +01:00
code.add("$T.$N($L)", creatorName, self.classElement.simpleName, args.length() > 0 ? args.substring(2) : "");
2022-10-31 20:52:48 +01:00
}
2022-11-01 16:47:00 +01:00
code.add(";\n");
for (Property.Setter param : properties.builderParams) {
2022-11-24 20:35:28 +01:00
code.addStatement("builder.$N(_$N)", param.callableName, param.name);
2022-10-31 20:52:48 +01:00
}
2022-11-24 20:35:28 +01:00
code.addStatement("result = builder.$N()", builder.buildMethod.simpleName);
2022-10-31 20:52:48 +01:00
} else {
2022-11-01 16:47:00 +01:00
StringBuilder args = new StringBuilder();
for (Property.Param param : properties.params) {
2022-11-24 20:35:28 +01:00
args.append(", _").append(param.name);
2022-11-01 16:47:00 +01:00
}
2022-11-24 20:35:28 +01:00
if (constructionSource.isConstructor) {
code.addStatement("result = new $T($L)", self.typeName, args.length() > 0 ? args.substring(2) : "");
2022-10-31 20:52:48 +01:00
} else {
2022-11-24 20:35:28 +01:00
code.addStatement("result = $T.$N($L)", creatorName, constructionSource.constructionElement.simpleName, args.length() > 0 ? args.substring(2) : "");
2022-10-31 20:52:48 +01:00
}
}
2022-11-01 16:47:00 +01:00
for (Property.Setter setter : properties.setters) {
2022-11-24 20:35:28 +01:00
code.addStatement("result.$N(_$N)", setter.callableName, setter.name);
2022-11-01 16:47:00 +01:00
}
for (Property.Field field : properties.fields) {
if (Properties.containsName(properties.setters, field)) continue;
if (Properties.containsName(properties.params, field)) continue;
2022-11-24 20:35:28 +01:00
code.addStatement("result.$N = _$N", field.name, field.callableName);
2022-11-01 16:47:00 +01:00
}
code.addStatement("return result");
2022-10-31 20:52:48 +01:00
spec.addMethod(extension(MethodSpec.methodBuilder("read"))
2022-10-31 20:52:48 +01:00
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
2022-11-24 20:35:28 +01:00
.returns(self.typeName)
2022-11-01 13:48:23 +01:00
.addParameter(Cl.GSON_READER, "reader")
2022-10-31 20:52:48 +01:00
.addException(IOException.class)
.addCode(code.build())
.build());
}
}
2022-11-01 10:08:03 +01:00
private void generateComments(Property<?> prop, CodeBlock.Builder code) {
2022-11-24 20:35:28 +01:00
for (AnnotationMirror annotation : prop.annotations) {
if (annotation.annotationType.asElement().toString().equals(Cl.GCOMMENT.toString())) {
String comment = (String) annotation.elementValues.values().iterator().next().value;
2022-11-01 13:48:23 +01:00
code.addStatement("if (writer.isLenient()) writer.comment($S)", comment);
2022-11-01 10:08:03 +01:00
}
}
}
2022-10-31 20:52:48 +01:00
private static String getSerializedName(Property<?> property) {
2022-11-24 20:35:28 +01:00
for (AnnotationMirror annotationMirror : property.annotations) {
if (annotationMirror.annotationType.asElement().toString().equals(Cl.SERIALIZED_NAME.toString())) {
return (String) annotationMirror.elementValues.values().iterator().next().value;
2022-10-31 20:52:48 +01:00
}
}
2022-11-24 20:35:28 +01:00
return property.name;
2022-10-31 20:52:48 +01:00
}
private MethodSpec.Builder extension(MethodSpec.Builder method) {
if (hasManifold) method.addAnnotation(Cl.MANIFOLD_EXTENSION);
return method;
}
2022-11-23 17:41:28 +01:00
private MethodSpec.Builder extension(MethodSpec.Builder method, TypeName thizName) {
if (hasManifold) {
method.addAnnotation(Cl.MANIFOLD_EXTENSION);
method.addParameter(ParameterSpec.builder(thizName, "value").addAnnotation(Cl.MANIFOLD_THIS).build());
}
else {
method.addParameter(thizName, "value");
}
return method;
}
2022-10-31 20:52:48 +01:00
}