Advertisement
Guest User

Untitled

a guest
Oct 22nd, 2019
104
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 12.61 KB | None | 0 0
  1. package ru.ifmo.java.serialization.processor;
  2.  
  3.  
  4. import com.squareup.javapoet.*;
  5. import ru.ifmo.java.serialization.Letter;
  6. import ru.ifmo.java.serialization.Letterize;
  7.  
  8. import javax.annotation.processing.*;
  9. import javax.lang.model.SourceVersion;
  10. import javax.lang.model.element.*;
  11. import javax.lang.model.type.TypeKind;
  12. import javax.lang.model.type.TypeMirror;
  13. import javax.lang.model.util.Elements;
  14. import javax.lang.model.util.Types;
  15. import javax.tools.Diagnostic;
  16. import java.io.DataInputStream;
  17. import java.io.DataOutputStream;
  18. import java.io.IOException;
  19. import java.util.*;
  20. import java.util.concurrent.Callable;
  21. import java.util.function.Consumer;
  22. import java.util.function.Function;
  23.  
  24. @SupportedSourceVersion(SourceVersion.RELEASE_11)
  25. @SupportedAnnotationTypes({"ru.ifmo.java.serialization.Letterize", "ru.ifmo.java.serialization.LetterizeOptional"})
  26. public class AutoSerializationProcessor extends AbstractProcessor {
  27.     private Types typeUtils;
  28.     private Elements elementUtils;
  29.     private Filer filer;
  30.     private Messager messager;
  31.  
  32.     final TypeName wildcard = WildcardTypeName.subtypeOf(Object.class);
  33.     final TypeName classOfAny = ParameterizedTypeName.get(
  34.             ClassName.get(Class.class), wildcard);
  35.  
  36.     final TypeName typeMapForSerialized = ParameterizedTypeName.get(
  37.             ClassName.get(HashMap.class), TypeName.get(Class.class), TypeName.get(Consumer.class)
  38.     );
  39.  
  40.     final TypeName typeMapForDeserialized = ParameterizedTypeName.get(
  41.             ClassName.get(HashMap.class), TypeName.get(Class.class), TypeName.get(Callable.class)
  42.     );
  43.  
  44.     @Override
  45.     public synchronized void init(ProcessingEnvironment processingEnv) {
  46.         super.init(processingEnv);
  47.         typeUtils = processingEnv.getTypeUtils();
  48.         elementUtils = processingEnv.getElementUtils();
  49.         filer = processingEnv.getFiler();
  50.         messager = processingEnv.getMessager();
  51.     }
  52.  
  53.     @Override
  54.     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
  55.         List<MethodSpec> serilalizeMethods = new ArrayList<>();
  56.         List<MethodSpec> deserilalizeMethods = new ArrayList<>();
  57.  
  58.         for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(Letterize.class)) {
  59.             // Check if a class has been annotated with @Factory
  60.             if (annotatedElement.getKind() != ElementKind.CLASS) {
  61.                 error(annotatedElement, "Only classes can be annotated with @%s", Letterize.class.getSimpleName());
  62.                 return true; // Exit processing
  63.             }
  64.             TypeElement typeElement = (TypeElement) annotatedElement;
  65.             if (!checkLetterImplementation(typeElement)) {
  66.                 error(annotatedElement, "Only classes can be interface with @%s", Letter.class.getSimpleName());
  67.             }
  68.  
  69.             serilalizeMethods.add(getSerializeMethod(typeElement));
  70.             deserilalizeMethods.add(getDeserializeMethod(typeElement));
  71.  
  72.         }
  73.  
  74.         if (!annotations.isEmpty()) {
  75.             generateSerializer(serilalizeMethods);
  76.             generateDeserializer(deserilalizeMethods);
  77.         }
  78.  
  79.         return false;
  80.     }
  81.  
  82.     private MethodSpec getDeserializeMethod(TypeElement typeElement) {
  83.         String methodName = "deserialize" + typeElement.getSimpleName();
  84.         MethodSpec.Builder builder = MethodSpec.methodBuilder(methodName)
  85.                 .addException(IOException.class);
  86.  
  87.         TypeMirror typeMirror = typeElement.asType();
  88.         TypeName typeName = TypeName.get(typeMirror);
  89.         builder.returns(typeName);
  90.  
  91.         builder.addStatement("$N element = new $N()",
  92.                 typeElement.getSimpleName(),
  93.                 typeElement.getSimpleName());
  94.  
  95.         List<VariableElement> typeParameters = getTypeParameters(typeElement);
  96.         typeParameters.forEach((x) -> addToMethodDeserializer(builder, x));
  97.  
  98.         builder.addStatement("return element");
  99.         return builder.build();
  100.     }
  101.  
  102.  
  103.     private MethodSpec getSerializeMethod(TypeElement typeElement) {
  104.         String methodName = "serialize" + typeElement.getSimpleName();
  105.         MethodSpec.Builder builder = MethodSpec.methodBuilder(methodName)
  106.                 .addException(IOException.class);
  107.  
  108.         TypeMirror typeMirror = typeElement.asType();
  109.         TypeName typeName = TypeName.get(typeMirror);
  110.  
  111.         builder.addParameter(typeName, "element");
  112.  
  113.         List<VariableElement> typeParameters = getTypeParameters(typeElement);
  114.         typeParameters.forEach((x) -> addToMethodSerializer(builder, x));
  115.         return builder.build();
  116.     }
  117.  
  118.     private List<VariableElement> getTypeParameters(TypeElement typeElement) {
  119.         TypeElement objectType = elementUtils.getTypeElement(Object.class.getCanonicalName());
  120.         List<VariableElement> res = new ArrayList<>();
  121.         while (!typeElement.equals(objectType)) {
  122.             for (Element element : typeElement.getEnclosedElements()) {
  123.                 if (element.getKind().equals(ElementKind.FIELD)) {
  124.                     VariableElement variableElement = (VariableElement) element;
  125.                     res.add(variableElement);
  126.                 }
  127.             }
  128.             typeElement = (TypeElement) typeUtils.asElement(typeElement.getSuperclass());
  129.         }
  130.         return res;
  131.     }
  132.  
  133.     private void addToMethodSerializer(MethodSpec.Builder builder, VariableElement variableElement) {
  134.         TypeMirror typeMirror = variableElement.asType();
  135.  
  136.         Name nameVariable = variableElement.getSimpleName();
  137.         String typeVariable = getTypeVariable(variableElement);
  138.         boolean isString = TypeName.get(typeMirror).equals(TypeName.get(String.class));
  139.         if (typeMirror.getKind().equals(TypeKind.DECLARED) && !isString) {
  140.             builder.addStatement("serialize$N(element.$N)", typeVariable, nameVariable);
  141.         } else{
  142.             builder.addStatement("output.write$N(element.$N)",typeVariable, nameVariable);
  143.         }
  144.  
  145.     }
  146.  
  147.     private void addToMethodDeserializer(MethodSpec.Builder builder, VariableElement variableElement) {
  148.         TypeMirror typeMirror = variableElement.asType();
  149.  
  150.         Name nameVariable = variableElement.getSimpleName();
  151.         String typeVariable = getTypeVariable(variableElement);
  152.         boolean isString = TypeName.get(typeMirror).equals(TypeName.get(String.class));
  153.         if (typeMirror.getKind().equals(TypeKind.DECLARED) && !isString) {
  154.             builder.addStatement("element.$N=deserialize$N()", nameVariable, typeVariable);
  155.         } else{
  156.             builder.addStatement("element.$N=input.read$N()", nameVariable, typeVariable);
  157.         }
  158.     }
  159.  
  160.     private String getTypeVariable(VariableElement variableElement) {
  161.         TypeMirror typeMirror = variableElement.asType();
  162.         TypeElement typeElement = (TypeElement) typeUtils.asElement(typeMirror);
  163.  
  164.         if (typeMirror.getKind().equals(TypeKind.DECLARED)) {
  165.             boolean isString = TypeName.get(typeMirror).equals(TypeName.get(String.class));
  166.             if (isString) {
  167.                 return "UTF";
  168.             } else {
  169.                 return typeElement.getSimpleName().toString();
  170.             }
  171.         } else {
  172.             Function<String, String> firstLetterUp = (String name) -> name.substring(0, 1).toUpperCase() + name.substring(1);
  173.             return firstLetterUp.apply(typeMirror.toString());
  174.         }
  175.     }
  176.  
  177.     private void generateDeserializer(List<MethodSpec> methods) {
  178.  
  179.         MethodSpec constructor = getConstructorDeserialized(methods);
  180.  
  181.         TypeSpec.Builder builder = TypeSpec.classBuilder("Deserializer")
  182.                 .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
  183.                 .addField(DataInputStream.class, "input",
  184.                         Modifier.PRIVATE, Modifier.FINAL)
  185.                 .addMethod(constructor);
  186.  
  187.         builder.addMethods(methods);
  188.  
  189.         builder.addField(FieldSpec.builder(typeMapForDeserialized, "typeToMethod").initializer("new HashMap<>()")
  190.                 .addModifiers(Modifier.FINAL, Modifier.PRIVATE).build());
  191.  
  192.         String code = "try {\n" +
  193.                 "return typeToMethod.get(clazz).call(); \n" +
  194.                 "catch (Exception e) {\n" +
  195.                 "throw new RuntimeException().addSuspend(e)}\n return null";
  196.  
  197.         MethodSpec serialize = MethodSpec.methodBuilder("deserialize")
  198.                 .addParameter(classOfAny, "clazz")
  199.                 .returns(Object.class)
  200.                 .addStatement(code).build();
  201.  
  202.         builder.addMethod(serialize);
  203.  
  204.         toFiler(builder);
  205.     }
  206.  
  207.     private void generateSerializer(List<MethodSpec> methods) {
  208.  
  209.         MethodSpec constructor = getConstructorDeserialized(methods);
  210.  
  211.         TypeSpec.Builder builder = TypeSpec.classBuilder("Serializer")
  212.                 .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
  213.                 .addField(DataOutputStream.class, "output",
  214.                         Modifier.PRIVATE, Modifier.FINAL)
  215.                 .addMethod(constructor);
  216.  
  217.         builder.addMethods(methods);
  218.  
  219.         builder.addField(FieldSpec.builder(typeMapForSerialized, "typeToMethod").initializer("new HashMap<>()")
  220.                 .addModifiers(Modifier.FINAL, Modifier.PRIVATE).build());
  221.  
  222.         MethodSpec serialize = MethodSpec.methodBuilder("serialize")
  223.                 .addParameter(classOfAny, "clazz")
  224.                 .addParameter(Object.class, "object")
  225.                 .addStatement("typeToMethod.get(clazz).accept(object)").build();
  226.  
  227.         builder.addMethod(serialize);
  228.  
  229.  
  230.         toFiler(builder);
  231.     }
  232.  
  233.     private MethodSpec getConstructorDeserialized(List<MethodSpec> methods) {
  234.         MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
  235.                 .addModifiers(Modifier.PUBLIC)
  236.                 .addParameter(DataInputStream.class, "input")
  237.                 .addStatement("this.$N = $N", "input", "input");
  238.  
  239.         for (MethodSpec method : methods) {
  240.             String typeString = method.returnType.toString();
  241.             String code = " typeToMethod.put($N.class, (Callable <$N>) () -> {\n" +
  242.                     "try {\n "+
  243.                     "  return this.$N();\n"+
  244.                     "} catch (Exception e) {\n"+
  245.                     " new RuntimeException());} return null;})";
  246.  
  247.             constructorBuilder.addStatement(code,
  248.                     typeString,
  249.                     typeString,
  250.                     method.name);
  251.         }
  252.         return constructorBuilder.build();
  253.     }
  254.  
  255.  
  256.     private MethodSpec getConstructorSerialized(List<MethodSpec> methods) {
  257.  
  258.         MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
  259.                 .addModifiers(Modifier.PUBLIC)
  260.                 .addParameter(DataOutputStream.class, "output")
  261.                 .addStatement("this.$N = $N", "output", "output");
  262.  
  263.  
  264.         for (MethodSpec method : methods) {
  265.             String typeString = method.parameters.get(0).type.toString();
  266.             String code = " typeToMethod.put($N.class, (Consumer <$N>) ($N x) -> {\n" +
  267.                     "try {\n "+
  268.                     "  this.$N(x);\n"+
  269.                     "} catch (Exception e) {\n"+
  270.                     " e.addSuppressed(new RuntimeException());}})";
  271.             constructorBuilder.addStatement(code,
  272.                     typeString,
  273.                     typeString,
  274.                     typeString,
  275.                     method.name);
  276.         }
  277.         return constructorBuilder.build();
  278.     }
  279.  
  280.  
  281.     private void toFiler(TypeSpec.Builder builder) {
  282.         final JavaFile javaFile = JavaFile.builder("ru.ifmo.java.serialization", builder.build()).build();
  283.  
  284.         try {
  285.             javaFile.writeTo(filer);
  286.         } catch (IOException e) {
  287.             e.printStackTrace();
  288.             throw new RuntimeException("Can't write to filer");
  289.         }
  290.     }
  291.  
  292.     private boolean checkLetterImplementation(TypeElement typeElement) {
  293.         while (true) {
  294.             TypeElement objectType = elementUtils.getTypeElement(Object.class.getCanonicalName());
  295.             TypeElement letterType = elementUtils.getTypeElement(Letter.class.getCanonicalName());
  296.             TypeElement supperType = (TypeElement) typeUtils.asElement(typeElement.getSuperclass());
  297.  
  298.             boolean contains = typeElement.getInterfaces().contains(letterType.asType());
  299.             if (contains) {
  300.                 return true;
  301.             }
  302.             if (supperType.equals(objectType)) {
  303.                 return false;
  304.             }
  305.  
  306.             typeElement = supperType;
  307.         }
  308.     }
  309.  
  310.     private void error(Element e, String msg, Object... args) {
  311.         messager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args), e);
  312.    }
  313. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement