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

208 lines
10 KiB
Java

package io.gitlab.jfronny.gson.compile.processor.gprocessor;
import com.squareup.javapoet.*;
import io.gitlab.jfronny.gson.compile.processor.SerializableClass;
import io.gitlab.jfronny.gson.compile.processor.core.value.*;
import io.gitlab.jfronny.gson.compile.processor.Cl;
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.*;
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 writeStatement;
public GProcessor(ValueCreator valueCreator, Messager message, boolean hasManifold, boolean isStatic) {
this.valueCreator = valueCreator;
this.message = message;
this.hasManifold = hasManifold;
this.isStatic = isStatic;
this.readStatement = isStatic ? "read(reader)" : "return read(reader)";
this.writeStatement = isStatic ? "write(writer)" : "write(value, writer)";
}
public abstract void generateDelegatingAdapter(TypeSpec.Builder spec, TypeName classType, ClassName generatedClassName);
public abstract void generateSerialisation(TypeSpec.Builder spec, SerializableClass self, List<TypeVariableName> typeVariables, Set<SerializableClass> otherAdapters) throws ElementException;
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);
}
}
}
public void generateDelegateToAdapter(TypeSpec.Builder spec, TypeName classType, TypeMirror adapter) {
spec.addMethod(
extension(MethodSpec.methodBuilder("read"))
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(Cl.GSON_READER, "reader")
.addException(IOException.class)
.returns(isStatic ? TypeName.VOID : classType)
.addCode(isStatic ? "$T.read(reader);" : "return $T.read(reader);", adapter)
.build()
);
spec.addMethod(
extension(MethodSpec.methodBuilder("write"), isStatic ? null : classType)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(Cl.GSON_WRITER, "writer")
.addException(IOException.class)
.addCode("$T.$L;", adapter, writeStatement)
.build()
);
}
public void generateAuxiliary(TypeSpec.Builder spec, TypeName classType, TypeMirror configure) {
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("read"))
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(TypeName.get(Reader.class), "in")
.addException(IOException.class)
.returns(isStatic ? TypeName.VOID : classType)
.addCode(configureReader.apply(CodeBlock.builder().beginControlFlow("try ($1T reader = new $1T(in))", Cl.GSON_READER))
.addStatement(readStatement)
.endControlFlow()
.build())
.build()
);
spec.addMethod(
extension(MethodSpec.methodBuilder("read"))
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(TypeName.get(String.class), "json")
.addException(IOException.class)
.returns(isStatic ? TypeName.VOID : classType)
.addCode(CodeBlock.builder().beginControlFlow("try ($1T reader = new $1T(json))", StringReader.class)
.addStatement(readStatement)
.endControlFlow()
.build())
.build()
);
spec.addMethod(
extension(MethodSpec.methodBuilder("read"))
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(Cl.GSON_ELEMENT, "tree")
.addException(IOException.class)
.returns(isStatic ? TypeName.VOID : classType)
.addCode(configureReader.apply(CodeBlock.builder().beginControlFlow("try ($1T reader = new $1T(tree))", Cl.GSON_TREE_READER))
.addStatement(readStatement)
.endControlFlow()
.build())
.build()
);
spec.addMethod(
extension(MethodSpec.methodBuilder("read"))
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(Path.class, "path")
.addException(IOException.class)
.returns(isStatic ? TypeName.VOID : classType)
.addCode(CodeBlock.builder().beginControlFlow("try ($T reader = $T.newBufferedReader(path))", BufferedReader.class, Files.class)
.addStatement(readStatement)
.endControlFlow()
.build())
.build()
);
spec.addMethod(
extension(MethodSpec.methodBuilder("write"), isStatic ? null : classType)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(Writer.class, "out")
.addException(IOException.class)
.addCode(configureWriter.apply(CodeBlock.builder().beginControlFlow("try ($1T writer = new $1T(out))", Cl.GSON_WRITER))
.addStatement(writeStatement)
.endControlFlow()
.build())
.build()
);
spec.addMethod(
extension(MethodSpec.methodBuilder("write"), isStatic ? null : classType)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(Path.class, "path")
.addException(IOException.class)
.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())
.build()
);
spec.addMethod(
extension(MethodSpec.methodBuilder("toJson"), isStatic ? null : classType)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addException(IOException.class)
.returns(String.class)
.addCode(CodeBlock.builder().beginControlFlow("try ($1T writer = new $1T())", StringWriter.class)
.addStatement(writeStatement)
.addStatement("return writer.toString()")
.endControlFlow()
.build())
.build()
);
spec.addMethod(
extension(MethodSpec.methodBuilder("toJsonTree"), isStatic ? null : classType)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addException(IOException.class)
.returns(Cl.GSON_ELEMENT)
.addCode(configureWriter.apply(CodeBlock.builder().beginControlFlow("try ($1T writer = new $1T())", Cl.GSON_TREE_WRITER))
.addStatement(writeStatement)
.addStatement("return writer.get()")
.endControlFlow()
.build())
.build()
);
}
}