299 lines
18 KiB
Java
299 lines
18 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.core.value.ElementException;
|
|
import io.gitlab.jfronny.commons.serialize.generator.core.value.Properties;
|
|
import io.gitlab.jfronny.commons.serialize.generator.core.value.Property;
|
|
import io.gitlab.jfronny.commons.serialize.generator.core.value.ValueCreator;
|
|
|
|
import javax.annotation.processing.Messager;
|
|
import javax.lang.model.element.AnnotationMirror;
|
|
import javax.lang.model.element.Modifier;
|
|
import javax.lang.model.type.TypeMirror;
|
|
import java.io.*;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.StandardCopyOption;
|
|
import java.nio.file.StandardOpenOption;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import java.util.function.UnaryOperator;
|
|
|
|
public abstract class GProcessor {
|
|
protected final ValueCreator valueCreator;
|
|
protected final Messager message;
|
|
protected final boolean hasManifold;
|
|
|
|
private final boolean isStatic;
|
|
private final String readStatement;
|
|
private final String readStatementT;
|
|
private final String writeStatement;
|
|
private final String writeStatementT;
|
|
private final boolean disableSafe;
|
|
|
|
public GProcessor(ValueCreator valueCreator, Messager message, boolean hasManifold, boolean isStatic, boolean disableSafe) {
|
|
this.valueCreator = valueCreator;
|
|
this.message = message;
|
|
this.hasManifold = hasManifold;
|
|
this.disableSafe = disableSafe;
|
|
|
|
this.isStatic = isStatic;
|
|
this.readStatement = isStatic ? "deserialize(reader)" : "return deserialize(reader)";
|
|
this.readStatementT = isStatic ? "deserialize(reader, transport)" : "return deserialize(reader, transport)";
|
|
this.writeStatement = isStatic ? "serialize(writer)" : "serialize(value, writer)";
|
|
this.writeStatementT = isStatic ? "serialize(writer, transport)" : "serialize(value, writer, transport)";
|
|
}
|
|
|
|
public abstract ClassName generateDelegatingAdapter(TypeSpec.Builder spec, TypeName classType, ClassName generatedClassName);
|
|
public abstract void generateSerialisation(TypeSpec.Builder spec, SerializableClass self, List<TypeVariableName> typeVariables, Set<SerializableClass> otherAdapters, Set<AdapterRef> refs) throws ElementException;
|
|
|
|
protected boolean isIgnored(Property<?> property) {
|
|
for (AnnotationMirror annotationMirror : property.getAnnotations()) {
|
|
if (annotationMirror.getAnnotationType().asElement().toString().equals(Cl.IGNORE.toString())) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected Property<?> getAlternative(Properties properties, Property<?> ignored) {
|
|
List<List<? extends Property<?>>> toSearch = List.of(properties.params, properties.getters, properties.setters, properties.fields);
|
|
for (List<? extends Property<?>> search : toSearch) {
|
|
Property<?> prop = Properties.findName(search, ignored);
|
|
if (prop == null || isIgnored(prop)) continue;
|
|
return prop;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected String getSerializedName(Property<?> property) {
|
|
for (AnnotationMirror annotationMirror : property.getAnnotations()) {
|
|
if (annotationMirror.getAnnotationType().asElement().toString().equals(Cl.SERIALIZED_NAME.toString())) {
|
|
return (String) annotationMirror.getElementValues().values().iterator().next().getValue();
|
|
}
|
|
}
|
|
return property.getName();
|
|
}
|
|
|
|
protected MethodSpec.Builder extension(MethodSpec.Builder method) {
|
|
if (hasManifold) method.addAnnotation(Cl.MANIFOLD_EXTENSION);
|
|
return method;
|
|
}
|
|
|
|
protected MethodSpec.Builder extension(MethodSpec.Builder method, TypeName thizName) {
|
|
if (thizName == null) return extension(method);
|
|
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;
|
|
}
|
|
|
|
protected void generateComments(Property<?> prop, CodeBlock.Builder code) {
|
|
for (AnnotationMirror annotation : prop.getAnnotations()) {
|
|
if (annotation.getAnnotationType().asElement().toString().equals(Cl.GCOMMENT.toString())) {
|
|
String comment = (String) annotation.getElementValues().values().iterator().next().getValue();
|
|
code.addStatement("if (writer.isLenient()) writer.comment($S)", comment);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected TypeName nonGeneric(TypeName from) {
|
|
if (from instanceof ParameterizedTypeName pn) {
|
|
//TODO be more clever
|
|
return ParameterizedTypeName.get(pn.rawType, pn.typeArguments.stream().map(s -> WildcardTypeName.subtypeOf(Object.class)).toArray(TypeName[]::new)).annotated(pn.annotations);
|
|
}
|
|
return from;
|
|
}
|
|
|
|
public void generateDelegateToAdapter(TypeSpec.Builder spec, TypeName classType, TypeMirror adapter, boolean isStatic) {
|
|
if (!isStatic) {
|
|
spec.addField(
|
|
FieldSpec.builder(TypeName.get(adapter), "adapter", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
|
|
.initializer("new $T()", adapter)
|
|
.build()
|
|
);
|
|
}
|
|
classType = nonGeneric(classType);
|
|
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)
|
|
.returns(isStatic ? TypeName.VOID : classType)
|
|
.addCode(isStatic ? this.isStatic ? "$T.deserialize(reader);" : "return $T.deserialize(reader);" : this.isStatic ? "$L.deserialize(reader);" : "return $L.deserialize(reader);", isStatic ? adapter : "adapter")
|
|
.build()
|
|
);
|
|
spec.addMethod(
|
|
extension(MethodSpec.methodBuilder("serialize"), this.isStatic ? null : classType)
|
|
.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(isStatic ? "$T.$L;" : "$L.$L;", isStatic ? adapter : "adapter", writeStatement)
|
|
.build()
|
|
);
|
|
}
|
|
|
|
public void generateAuxiliary(TypeSpec.Builder spec, TypeName classType, TypeMirror configure) {
|
|
classType = nonGeneric(classType);
|
|
final UnaryOperator<CodeBlock.Builder> configureReader = cb -> {
|
|
if (configure != null) cb.addStatement("$T.configure(reader)", configure);
|
|
return cb;
|
|
};
|
|
final UnaryOperator<CodeBlock.Builder> configureWriter = cb -> {
|
|
if (configure != null) cb.addStatement("$T.configure(writer)", configure);
|
|
return cb;
|
|
};
|
|
|
|
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"))))
|
|
.addTypeVariable(TypeVariableName.get("Writer", ParameterizedTypeName.get(Cl.SERIALIZE_WRITER, TypeVariableName.get("TEx"), TypeVariableName.get("Writer"))))
|
|
.addParameter(TypeName.get(Reader.class), "in")
|
|
.addParameter(ParameterizedTypeName.get(Cl.TRANSPORT, TypeVariableName.get("TEx"), TypeVariableName.get("Reader"), TypeVariableName.get("Writer")), "transport")
|
|
.addException(TypeVariableName.get("TEx"))
|
|
.addException(Cl.MALFORMED_DATA_EXCEPTION)
|
|
.returns(isStatic ? TypeName.VOID : classType)
|
|
.addCode(configureReader.apply(CodeBlock.builder().beginControlFlow((isStatic ? "" : "return ") + "transport.read(in, reader -> "))
|
|
.addStatement(readStatement)
|
|
.addStatement("$<})")
|
|
.build())
|
|
.build()
|
|
);
|
|
|
|
spec.addMethod(
|
|
extension(MethodSpec.methodBuilder("deserialize"))
|
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
|
.addParameter(TypeName.get(String.class), "serialized")
|
|
.addTypeVariable(TypeVariableName.get("TEx", Exception.class))
|
|
.addTypeVariable(TypeVariableName.get("Reader", ParameterizedTypeName.get(Cl.SERIALIZE_READER, TypeVariableName.get("TEx"), TypeVariableName.get("Reader"))))
|
|
.addTypeVariable(TypeVariableName.get("Writer", ParameterizedTypeName.get(Cl.SERIALIZE_WRITER, TypeVariableName.get("TEx"), TypeVariableName.get("Writer"))))
|
|
.addParameter(ParameterizedTypeName.get(Cl.TRANSPORT, TypeVariableName.get("TEx"), TypeVariableName.get("Reader"), TypeVariableName.get("Writer")), "transport")
|
|
.addException(TypeVariableName.get("TEx"))
|
|
.addException(Cl.MALFORMED_DATA_EXCEPTION)
|
|
.returns(isStatic ? TypeName.VOID : classType)
|
|
.addCode(CodeBlock.builder().beginControlFlow("try ($1T reader = new $1T(serialized))", StringReader.class)
|
|
.addStatement(readStatementT)
|
|
.endControlFlow()
|
|
.build())
|
|
.build()
|
|
);
|
|
|
|
spec.addMethod(
|
|
extension(MethodSpec.methodBuilder("deserialize"))
|
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
|
.addParameter(Cl.GSON_ELEMENT, "tree")
|
|
.addException(Cl.MALFORMED_DATA_EXCEPTION)
|
|
.returns(isStatic ? TypeName.VOID : classType)
|
|
.addCode(configureReader.apply(CodeBlock.builder().beginControlFlow("try ($1T reader = new $1T(tree))", Cl.EMULATED_READER))
|
|
.addStatement(readStatement)
|
|
.endControlFlow()
|
|
.build())
|
|
.build()
|
|
);
|
|
|
|
spec.addMethod(
|
|
extension(MethodSpec.methodBuilder("deserialize"))
|
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
|
.addParameter(Path.class, "path")
|
|
.addTypeVariable(TypeVariableName.get("TEx", Exception.class))
|
|
.addTypeVariable(TypeVariableName.get("Reader", ParameterizedTypeName.get(Cl.SERIALIZE_READER, TypeVariableName.get("TEx"), TypeVariableName.get("Reader"))))
|
|
.addTypeVariable(TypeVariableName.get("Writer", ParameterizedTypeName.get(Cl.SERIALIZE_WRITER, TypeVariableName.get("TEx"), TypeVariableName.get("Writer"))))
|
|
.addParameter(ParameterizedTypeName.get(Cl.TRANSPORT, TypeVariableName.get("TEx"), TypeVariableName.get("Reader"), TypeVariableName.get("Writer")), "transport")
|
|
.addException(TypeVariableName.get("TEx"))
|
|
.addException(Cl.MALFORMED_DATA_EXCEPTION)
|
|
.addException(IOException.class)
|
|
.returns(isStatic ? TypeName.VOID : classType)
|
|
.addCode(CodeBlock.builder().beginControlFlow("try ($T reader = $T.newBufferedReader(path))", BufferedReader.class, Files.class)
|
|
.addStatement(readStatementT)
|
|
.endControlFlow()
|
|
.build())
|
|
.build()
|
|
);
|
|
|
|
spec.addMethod(
|
|
extension(MethodSpec.methodBuilder("serialize"), isStatic ? null : classType)
|
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
|
.addParameter(Writer.class, "out")
|
|
.addTypeVariable(TypeVariableName.get("TEx", Exception.class))
|
|
.addTypeVariable(TypeVariableName.get("Reader", ParameterizedTypeName.get(Cl.SERIALIZE_READER, TypeVariableName.get("TEx"), TypeVariableName.get("Reader"))))
|
|
.addTypeVariable(TypeVariableName.get("Writer", ParameterizedTypeName.get(Cl.SERIALIZE_WRITER, TypeVariableName.get("TEx"), TypeVariableName.get("Writer"))))
|
|
.addParameter(ParameterizedTypeName.get(Cl.TRANSPORT, TypeVariableName.get("TEx"), TypeVariableName.get("Reader"), TypeVariableName.get("Writer")), "transport")
|
|
.addException(TypeVariableName.get("TEx"))
|
|
.addException(Cl.MALFORMED_DATA_EXCEPTION)
|
|
.addCode(configureWriter.apply(CodeBlock.builder().beginControlFlow("transport.write(out, writer -> ", Cl.SERIALIZE_WRITER))
|
|
.addStatement(writeStatement)
|
|
.addStatement("$<})")
|
|
.build())
|
|
.build()
|
|
);
|
|
|
|
spec.addMethod(
|
|
extension(MethodSpec.methodBuilder("serialize"), isStatic ? null : classType)
|
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
|
.addParameter(Path.class, "path")
|
|
.addTypeVariable(TypeVariableName.get("TEx", Exception.class))
|
|
.addTypeVariable(TypeVariableName.get("Reader", ParameterizedTypeName.get(Cl.SERIALIZE_READER, TypeVariableName.get("TEx"), TypeVariableName.get("Reader"))))
|
|
.addTypeVariable(TypeVariableName.get("Writer", ParameterizedTypeName.get(Cl.SERIALIZE_WRITER, TypeVariableName.get("TEx"), TypeVariableName.get("Writer"))))
|
|
.addParameter(ParameterizedTypeName.get(Cl.TRANSPORT, TypeVariableName.get("TEx"), TypeVariableName.get("Reader"), TypeVariableName.get("Writer")), "transport")
|
|
.addException(TypeVariableName.get("TEx"))
|
|
.addException(Cl.MALFORMED_DATA_EXCEPTION)
|
|
.addException(IOException.class)
|
|
.addCode(disableSafe ? CodeBlock.builder().beginControlFlow("try ($1T writer = $2T.newBufferedWriter(path, $3T.CREATE, $3T.WRITE, $3T.TRUNCATE_EXISTING))", BufferedWriter.class, Files.class, StandardOpenOption.class)
|
|
.addStatement(writeStatementT)
|
|
.endControlFlow()
|
|
.build() : CodeBlock.builder()
|
|
.addStatement("$T temp = $T.createTempFile($S, $S)", Path.class, Files.class, "serializegenerator-", ".json")
|
|
.beginControlFlow("try ($1T writer = $2T.newBufferedWriter(temp, $3T.CREATE, $3T.WRITE, $3T.TRUNCATE_EXISTING))", BufferedWriter.class, Files.class, StandardOpenOption.class)
|
|
.addStatement(writeStatementT)
|
|
.addStatement("$T.move(temp, path, $T.REPLACE_EXISTING)", Files.class, StandardCopyOption.class)
|
|
.nextControlFlow("finally")
|
|
.addStatement("$T.deleteIfExists(temp)", Files.class)
|
|
.endControlFlow()
|
|
.build())
|
|
.build()
|
|
);
|
|
|
|
spec.addMethod(
|
|
extension(MethodSpec.methodBuilder("serializeToString"), isStatic ? null : classType)
|
|
.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"))))
|
|
.addTypeVariable(TypeVariableName.get("Writer", ParameterizedTypeName.get(Cl.SERIALIZE_WRITER, TypeVariableName.get("TEx"), TypeVariableName.get("Writer"))))
|
|
.addParameter(ParameterizedTypeName.get(Cl.TRANSPORT, TypeVariableName.get("TEx"), TypeVariableName.get("Reader"), TypeVariableName.get("Writer")), "transport")
|
|
.addException(TypeVariableName.get("TEx"))
|
|
.addException(Cl.MALFORMED_DATA_EXCEPTION)
|
|
.addException(IOException.class)
|
|
.returns(String.class)
|
|
.addCode("return transport.write(writer -> "+ writeStatement + ");")
|
|
.build()
|
|
);
|
|
|
|
spec.addMethod(
|
|
extension(MethodSpec.methodBuilder("toDataTree"), isStatic ? null : classType)
|
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
|
.addException(Cl.MALFORMED_DATA_EXCEPTION)
|
|
.returns(Cl.GSON_ELEMENT)
|
|
.addCode(configureWriter.apply(CodeBlock.builder().beginControlFlow("try ($1T writer = new $1T())", Cl.EMULATED_WRITER))
|
|
.addStatement(writeStatement)
|
|
.addStatement("return writer.get()")
|
|
.endControlFlow()
|
|
.build())
|
|
.build()
|
|
);
|
|
}
|
|
}
|