data-generator/data-generator-plugin/src/main/java/it/cavallium/data/generator/plugin/classgen/GenSerializerSuperX.java

150 lines
5.5 KiB
Java

package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeSpec.Builder;
import it.cavallium.data.generator.DataSerializer;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedType;
import it.cavallium.data.generator.plugin.ComputedTypeSuper;
import it.cavallium.data.generator.plugin.ComputedVersion;
import it.cavallium.stream.SafeDataInput;
import it.cavallium.stream.SafeDataOutput;
import java.io.NotSerializableException;
import java.util.Objects;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
import org.jetbrains.annotations.NotNull;
public class GenSerializerSuperX extends ClassGenerator {
public GenSerializerSuperX(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
return dataModel.getVersionsSet().parallelStream().flatMap(this::generateVersionClasses);
}
private Stream<GeneratedClass> generateVersionClasses(ComputedVersion version) {
return dataModel
.getSuperTypesComputed(version)
.filter(type -> type.getVersion().equals(version))
.map(type -> generateTypeVersioned(version, type));
}
private GeneratedClass generateTypeVersioned(ComputedVersion version, ComputedTypeSuper typeSuper) {
ClassName serializerClassName = typeSuper.getJSerializerName(basePackageName);
ClassName typeSuperClassName = typeSuper.getJTypeName(basePackageName);
var classBuilder = TypeSpec.classBuilder(serializerClassName.simpleName());
classBuilder.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
classBuilder.addSuperinterface(ParameterizedTypeName.get(ClassName.get(DataSerializer.class), typeSuperClassName));
generateCheckIdValidity(version, typeSuper, classBuilder);
generateSerialize(version, typeSuper, classBuilder);
generateDeserialize(version, typeSuper, classBuilder);
return new GeneratedClass(serializerClassName.packageName(), classBuilder);
}
private void generateCheckIdValidity(ComputedVersion version, ComputedTypeSuper typeSuper, Builder classBuilder) {
int max = typeSuper.subTypes().size();
var method = MethodSpec.methodBuilder("checkIdValidity");
method.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL);
method.addParameter(ParameterSpec.builder(int.class, "id").build());
method.beginControlFlow("if (id < 0 || id >= $L)", max);
method.addStatement("throw new $T(id)", IndexOutOfBoundsException.class);
method.endControlFlow();
classBuilder.addMethod(method.build());
}
private void generateSerialize(ComputedVersion version, ComputedTypeSuper typeSuper, Builder classBuilder) {
var method = MethodSpec.methodBuilder("serialize");
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
method.addParameter(ParameterSpec.builder(SafeDataOutput.class, "out").build());
method.addParameter(ParameterSpec
.builder(typeSuper.getJTypeName(basePackageName), "data")
.addAnnotation(NotNull.class)
.build());
if (generateOldSerializers || version.isCurrent()) {
method.addStatement("$T.requireNonNull(data)", Objects.class);
method.addStatement("int id = data.getMetaId$$$N()", typeSuper.getName());
method.addStatement("out.writeByte(id)");
method.beginControlFlow("switch (id)");
var subTypes = typeSuper.subTypes().toArray(ComputedType[]::new);
int max = subTypes.length;
for (int i = 0; i < max; i++) {
var subType = subTypes[i];
var subSerializerInstance = subType.getJSerializerInstance(basePackageName);
method.addStatement("case $L -> $T.$N.serialize(out, ($T) data)",
i,
subSerializerInstance.className(),
subSerializerInstance.fieldName(),
subType.getJTypeName(basePackageName)
);
}
method.beginControlFlow("default ->");
method.addStatement("checkIdValidity(id)");
method.addComment("Not reachable:");
method.addStatement("throw new $T()", IllegalStateException.class);
method.endControlFlow();
method.endControlFlow();
} else {
method.addStatement("throw new $T()", NotSerializableException.class);
}
classBuilder.addMethod(method.build());
}
private void generateDeserialize(ComputedVersion version, ComputedTypeSuper typeSuper, Builder classBuilder) {
var method = MethodSpec.methodBuilder("deserialize");
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
ClassName typeSuperClassName = typeSuper.getJTypeName(basePackageName);
method.returns(typeSuperClassName);
method.addAnnotation(NotNull.class);
method.addParameter(ParameterSpec.builder(SafeDataInput.class, "in").build());
method.addStatement("int id = in.readUnsignedByte()");
method.beginControlFlow("return switch (id)");
var subTypes = typeSuper.subTypes().toArray(ComputedType[]::new);
int max = subTypes.length;
for (int i = 0; i < max; i++) {
var subType = subTypes[i];
var subSerializerInstance = subType.getJSerializerInstance(basePackageName);
method.addStatement("case $L -> ($T) $T.$N.deserialize(in)",
i,
subType.getJTypeName(basePackageName),
subSerializerInstance.className(),
subSerializerInstance.fieldName()
);
}
method.beginControlFlow("default ->");
method.addStatement("checkIdValidity(id)");
method.addComment("Not reachable:");
method.addStatement("throw new $T()", IllegalStateException.class);
method.endControlFlow();
method.addCode("$<};");
classBuilder.addMethod(method.build());
}
}