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 typeVariables, Set 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 configureReader = cb -> { if (configure != null) cb.addStatement("$T.configure(reader)", configure); return cb; }; final UnaryOperator 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() ); } }