java-commons/commons-serialize-generator/src/main/java/io/gitlab/jfronny/commons/serialize/generator/gprocessor/StaticProcessor.java

183 lines
10 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 java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class StaticProcessor extends GProcessor {
public StaticProcessor(ValueCreator valueCreator, Messager message, boolean hasManifold, boolean disableSafe) {
super(valueCreator, message, hasManifold, true, disableSafe);
}
@Override
public ClassName generateDelegatingAdapter(TypeSpec.Builder spec, TypeName classType, ClassName generatedClassName) {
throw new UnsupportedOperationException();
}
// !!!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 = valueCreator.fromStatic(self.classElement());
Properties properties = value.getProperties();
// public static void write(JsonWriter writer) throws IOException
{
CodeBlock.Builder code = CodeBlock.builder();
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("$T.$N", self.getTypeName(), param.getCallableName());
if (param.getType().getKind().isPrimitive()) {
generateComments(param, code);
code.addStatement("writer.name($S)", getSerializedNames(param).getFirst());
Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, refs, message, writeGet);
} else {
code.beginControlFlow("if ($T.$N != null || writer.isSerializeNulls())", self.getTypeName(), param.getCallableName());
generateComments(param, code);
code.addStatement("writer.name($S)", getSerializedNames(param).getFirst());
code.addStatement("if ($T.$N == null) writer.nullValue()", self.getTypeName(), param.getCallableName());
code.beginControlFlow("else");
Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, refs, 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)", getSerializedNames(param).getFirst());
Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, refs, message, () -> code.add("$T.$N()", self.getTypeName(), param.getCallableName()));
} else {
code.addStatement("$T $L$N = $T.$N()", param.getType(), "$", param.getCallableName(), self.getTypeName(), param.getCallableName());
code.beginControlFlow("if ($L$N != null || writer.isSerializeNulls())", "$", param.getCallableName());
generateComments(param, code);
code.addStatement("writer.name($S)", getSerializedNames(param).getFirst());
code.addStatement("if ($L$N == null) writer.nullValue()", "$", param.getCallableName());
code.beginControlFlow("else");
Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, refs, message, () -> code.add("$L$N", "$", param.getCallableName()));
code.endControlFlow();
code.endControlFlow();
}
}
code.addStatement("writer.endObject()");
spec.addMethod(extension(MethodSpec.methodBuilder("serialize"))
.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 void 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")
.endControlFlow();
Map<Property<?>, Property<?>> altMap = new HashMap<>();
boolean isEmpty = true;
for (Property<?> param : properties.names) {
if (isIgnored(param)) {
Property<?> alt = getAlternative(properties, param);
altMap.put(param, alt);
if (alt == null) continue;
param = alt;
}
isEmpty = false;
if (param instanceof Property.Field) {
code.addStatement("$T.$N = $L", self.getTypeName(), param.getName(), TypeHelper.getDefaultValue(param.getType()));
} else {
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 (altMap.containsKey(param)) {
param = altMap.get(param);
if (param == null) continue;
}
List<String> names = getSerializedNames(param);
for (String serializedName : names) {
code.beginControlFlow("case $S ->", serializedName);
code.addStatement("if (has_$N && !reader.isLenient()) throw new $T($S)", param.getName(), Cl.MALFORMED_DATA_EXCEPTION, "Duplicate entry for: " + names.getFirst());
if (param.getType().getKind().isPrimitive()) {
if (param instanceof Property.Field) code.add("$T.$N = ", self.getTypeName(), param.getName());
else code.add("_$N = ", param.getName());
Adapters.generateRead(param, spec, code, typeVariables, otherAdapters, refs, message);
code.add(";\n");
} else {
code.beginControlFlow("if (reader.peek() == $T.NULL)", Cl.GSON_TOKEN)
.addStatement("reader.nextNull()");
if (param instanceof Property.Field) {
code.addStatement("$T.$N = null", self.getTypeName(), param.getName());
code.unindent().add("} else $T.$N = ", self.getTypeName(), param.getName());
} else {
code.addStatement("_$N = null", param.getName());
code.unindent().add("} else _$N = ", param.getName());
}
Adapters.generateRead(param, spec, code, typeVariables, otherAdapters, refs, message);
code.add(";\n");
}
code.addStatement("has_$N = true", param.getName());
code.endControlFlow();
}
}
code.beginControlFlow("case String s -> ")
.addStatement("if (reader.isLenient()) reader.skipValue()")
.addStatement("else throw new $T($S + s)", Cl.MALFORMED_DATA_EXCEPTION, "Unexpected name: ")
.endControlFlow();
code.endControlFlow()
.endControlFlow()
.addStatement("reader.endObject()");
for (Property.Setter setter : properties.setters) {
if (isIgnored(setter)) continue;
code.addStatement("if (has_$N) $T.$N(_$N)", setter.getName(), self.getTypeName(), setter.getCallableName(), setter.getName());
}
}
spec.addMethod(extension(MethodSpec.methodBuilder("deserialize"))
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.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());
}
}
}