230 lines
14 KiB
Java
230 lines
14 KiB
Java
package io.gitlab.jfronny.commons.serialize.generator.gprocessor;
|
|
|
|
import com.squareup.javapoet.*;
|
|
import io.gitlab.jfronny.commons.serialize.generator.AdapterRef;
|
|
import io.gitlab.jfronny.commons.serialize.generator.Cl;
|
|
import io.gitlab.jfronny.commons.serialize.generator.SerializableClass;
|
|
import io.gitlab.jfronny.commons.serialize.generator.adapter.Adapters;
|
|
import io.gitlab.jfronny.commons.serialize.generator.core.TypeHelper;
|
|
import io.gitlab.jfronny.commons.serialize.generator.core.value.*;
|
|
|
|
import javax.annotation.processing.Messager;
|
|
import javax.lang.model.element.Modifier;
|
|
import javax.lang.model.element.TypeElement;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
public class InstanceProcessor extends GProcessor {
|
|
public InstanceProcessor(ValueCreator valueCreator, Messager message, boolean hasManifold, boolean disableSafe) {
|
|
super(valueCreator, message, hasManifold, false, disableSafe);
|
|
}
|
|
|
|
@Override
|
|
public ClassName generateDelegatingAdapter(TypeSpec.Builder spec, TypeName classType, ClassName generatedClassName) {
|
|
classType = nonGeneric(classType);
|
|
spec.addType(
|
|
TypeSpec.classBuilder("Adapter")
|
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
|
.addAnnotation(AnnotationSpec.builder(Cl.SERIALIZER_FOR).addMember("targets", CodeBlock.builder().add("$T.class", classType).build()).build())
|
|
.superclass(ParameterizedTypeName.get(Cl.TYPE_ADAPTER, classType))
|
|
.addMethod(MethodSpec.methodBuilder("serialize")
|
|
.addAnnotation(Override.class)
|
|
.addModifiers(Modifier.PUBLIC)
|
|
.addTypeVariable(TypeVariableName.get("TEx", Exception.class))
|
|
.addTypeVariable(TypeVariableName.get("Writer", ParameterizedTypeName.get(Cl.SERIALIZE_WRITER, TypeVariableName.get("TEx"), TypeVariableName.get("Writer"))))
|
|
.addParameter(classType, "value")
|
|
.addParameter(TypeVariableName.get("Writer"), "writer")
|
|
.addException(TypeVariableName.get("TEx"))
|
|
.addException(Cl.MALFORMED_DATA_EXCEPTION)
|
|
.addCode(generatedClassName.simpleName() + ".serialize(value, writer);")
|
|
.build())
|
|
.addMethod(MethodSpec.methodBuilder("deserialize")
|
|
.addAnnotation(Override.class)
|
|
.addModifiers(Modifier.PUBLIC)
|
|
.addTypeVariable(TypeVariableName.get("TEx", Exception.class))
|
|
.addTypeVariable(TypeVariableName.get("Reader", ParameterizedTypeName.get(Cl.SERIALIZE_READER, TypeVariableName.get("TEx"), TypeVariableName.get("Reader"))))
|
|
.addParameter(TypeVariableName.get("Reader"), "reader")
|
|
.addException(TypeVariableName.get("TEx"))
|
|
.addException(Cl.MALFORMED_DATA_EXCEPTION)
|
|
.returns(classType)
|
|
.addCode("return " + generatedClassName.simpleName() + ".deserialize(reader);")
|
|
.build())
|
|
.build()
|
|
);
|
|
return generatedClassName.nestedClass("Adapter");
|
|
}
|
|
|
|
// !!!WARNING!!!
|
|
// A lot of this code is common between InstanceProcessor and StaticProcessor
|
|
// Make sure they don't get out of sync!
|
|
// (Or, alternatively, create one common solution for these)
|
|
// !!!WARNING!!!
|
|
@Override
|
|
public void generateSerialisation(TypeSpec.Builder spec, SerializableClass self, List<TypeVariableName> typeVariables, Set<SerializableClass> otherAdapters, Set<AdapterRef> refs) throws ElementException {
|
|
Value value = self.builder() == null ? valueCreator.from(self.classElement(), false) : valueCreator.from(TypeHelper.asDeclaredType(self.builder()).asElement(), true);
|
|
ConstructionSource constructionSource = value.getConstructionSource();
|
|
Properties properties = value.getProperties();
|
|
|
|
// 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()");
|
|
for (Property.Field param : properties.fields) {
|
|
Property<?> altGetter = Properties.findName(properties.getters, param);
|
|
if (altGetter != null && !isIgnored(altGetter)) continue;
|
|
if (isIgnored(param)) continue;
|
|
Runnable writeGet = () -> code.add("value.$N", param.getCallableName());
|
|
if (param.getType().getKind().isPrimitive()) {
|
|
generateComments(param, code);
|
|
code.addStatement("writer.name($S)", getSerializedName(param));
|
|
Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, message, writeGet);
|
|
} else {
|
|
code.beginControlFlow("if (value.$N != null || writer.isSerializeNulls())", param.getCallableName());
|
|
generateComments(param, code);
|
|
code.addStatement("writer.name($S)", getSerializedName(param));
|
|
code.addStatement("if (value.$N == null) writer.nullValue()", param.getCallableName());
|
|
code.beginControlFlow("else");
|
|
Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, message, writeGet);
|
|
code.endControlFlow();
|
|
code.endControlFlow();
|
|
}
|
|
}
|
|
for (Property.Getter param : properties.getters) {
|
|
if (isIgnored(param)) continue;
|
|
if (param.getType().getKind().isPrimitive()) {
|
|
generateComments(param, code);
|
|
code.addStatement("writer.name($S)", getSerializedName(param));
|
|
Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, message, () -> code.add("value.$N()", param.getCallableName()));
|
|
} else {
|
|
code.addStatement("$T $L$N = value.$N()", param.getType(), "$", param.getCallableName(), param.getCallableName());
|
|
code.beginControlFlow("if ($L$N != null || writer.isSerializeNulls())", "$", param.getCallableName());
|
|
generateComments(param, code);
|
|
code.addStatement("writer.name($S)", getSerializedName(param));
|
|
code.addStatement("if ($L$N == null) writer.nullValue()", "$", param.getCallableName());
|
|
code.beginControlFlow("else");
|
|
Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, message, () -> code.add("$L$N", "$", param.getCallableName()));
|
|
code.endControlFlow();
|
|
code.endControlFlow();
|
|
}
|
|
}
|
|
code.addStatement("writer.endObject()");
|
|
|
|
spec.addMethod(extension(MethodSpec.methodBuilder("serialize"), self.getTypeName())
|
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
|
.addTypeVariable(TypeVariableName.get("TEx", Exception.class))
|
|
.addTypeVariable(TypeVariableName.get("Writer", ParameterizedTypeName.get(Cl.SERIALIZE_WRITER, TypeVariableName.get("TEx"), TypeVariableName.get("Writer"))))
|
|
.addParameter(TypeVariableName.get("Writer"), "writer")
|
|
.addException(TypeVariableName.get("TEx"))
|
|
.addException(Cl.MALFORMED_DATA_EXCEPTION)
|
|
.addCode(code.build())
|
|
.build());
|
|
}
|
|
|
|
// public static T read(JsonReader reader) throws IOException
|
|
{
|
|
CodeBlock.Builder code = CodeBlock.builder();
|
|
code.beginControlFlow("if (reader.peek() == $T.NULL)", Cl.GSON_TOKEN)
|
|
.addStatement("reader.nextNull()")
|
|
.addStatement("return null")
|
|
.endControlFlow();
|
|
|
|
boolean isEmpty = true;
|
|
for (Property<?> param : properties.names) {
|
|
if (isIgnored(param) && getAlternative(properties, param) == null) continue;
|
|
isEmpty = false;
|
|
code.addStatement("$T _$N = $L", param.getType(), param.getName(), TypeHelper.getDefaultValue(param.getType()));
|
|
code.addStatement("boolean has_$N = false", param.getName());
|
|
}
|
|
if (isEmpty) {
|
|
code.addStatement("reader.skipValue()");
|
|
} else {
|
|
code.addStatement("reader.beginObject()")
|
|
.beginControlFlow("while (reader.hasNext())")
|
|
.beginControlFlow("switch (reader.nextName())");
|
|
for (Property<?> param : properties.names) {
|
|
if (isIgnored(param) && getAlternative(properties, param) == null) continue;
|
|
code.beginControlFlow("case $S ->", getSerializedName(param));
|
|
if (param.getType().getKind().isPrimitive()) {
|
|
code.add("_$N = ", param.getName());
|
|
Adapters.generateRead(param, spec, code, typeVariables, otherAdapters, message);
|
|
code.add(";\n");
|
|
} else {
|
|
code.beginControlFlow("if (reader.peek() == $T.NULL)", Cl.GSON_TOKEN)
|
|
.addStatement("reader.nextNull()")
|
|
.addStatement("_$N = null", param.getName());
|
|
code.unindent().add("} else _$N = ", param.getName());
|
|
Adapters.generateRead(param, spec, code, typeVariables, otherAdapters, message);
|
|
code.add(";\n");
|
|
}
|
|
code.addStatement("has_$N = true", param.getName());
|
|
code.endControlFlow();
|
|
}
|
|
code.add("default -> ")
|
|
.addStatement("reader.skipValue()");
|
|
|
|
code.endControlFlow()
|
|
.endControlFlow()
|
|
.addStatement("reader.endObject()");
|
|
}
|
|
|
|
code.addStatement("$T result", self.getTypeName());
|
|
ClassName creatorName = ClassName.get((TypeElement) constructionSource.getConstructionElement().getEnclosingElement());
|
|
if (constructionSource instanceof ConstructionSource.Builder builder) {
|
|
StringBuilder args = new StringBuilder();
|
|
for (Property.ConstructorParam param : properties.constructorParams) {
|
|
args.append(", _").append(param.getName());
|
|
}
|
|
code.add("$T builder = ", builder.getBuilderClass());
|
|
if (constructionSource.isConstructor()) {
|
|
code.add("new $T($L)", builder.getBuilderClass(), !args.isEmpty() ? args.substring(2) : "");
|
|
} else {
|
|
code.add("$T.$N($L)", creatorName, self.classElement().getSimpleName(), !args.isEmpty() ? args.substring(2) : "");
|
|
}
|
|
code.add(";\n");
|
|
for (Property.Setter param : properties.builderParams) {
|
|
if (isIgnored(param)) continue;
|
|
code.addStatement("if (has_$N) builder.$N(_$N)", param.getName(), param.getCallableName(), param.getName());
|
|
}
|
|
code.addStatement("result = builder.$N()", builder.getBuildMethod().getSimpleName());
|
|
} else {
|
|
StringBuilder args = new StringBuilder();
|
|
for (Property.Param param : properties.params) {
|
|
args.append(", _").append(param.getName());
|
|
}
|
|
if (constructionSource.isConstructor()) {
|
|
code.addStatement("result = new $T($L)", self.getTypeName(), !args.isEmpty() ? args.substring(2) : "");
|
|
} else {
|
|
code.addStatement("result = $T.$N($L)", creatorName, constructionSource.getConstructionElement().getSimpleName(), !args.isEmpty() ? args.substring(2) : "");
|
|
}
|
|
}
|
|
for (Property.Setter setter : properties.setters) {
|
|
if (isIgnored(setter)) continue;
|
|
code.addStatement("if (has_$N) result.$N(_$N)", setter.getName(), setter.getCallableName(), setter.getName());
|
|
}
|
|
for (Property.Field field : properties.fields) {
|
|
if (Properties.containsName(properties.setters, field)) continue;
|
|
if (Properties.containsName(properties.params, field)) continue;
|
|
if (isIgnored(field)) continue;
|
|
code.addStatement("if (has_$N) result.$N = _$N", field.getName(), field.getCallableName(), field.getName());
|
|
}
|
|
code.addStatement("return result");
|
|
|
|
spec.addMethod(extension(MethodSpec.methodBuilder("deserialize"))
|
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
|
.returns(self.getTypeName())
|
|
.addTypeVariable(TypeVariableName.get("TEx", Exception.class))
|
|
.addTypeVariable(TypeVariableName.get("Reader", ParameterizedTypeName.get(Cl.SERIALIZE_READER, TypeVariableName.get("TEx"), TypeVariableName.get("Reader"))))
|
|
.addParameter(TypeVariableName.get("Reader"), "reader")
|
|
.addException(TypeVariableName.get("TEx"))
|
|
.addException(Cl.MALFORMED_DATA_EXCEPTION)
|
|
.addCode(code.build())
|
|
.build());
|
|
}
|
|
}
|
|
}
|