java 删除注解时在Eclipse中运行注解处理器

li9yvcax  于 2023-03-21  发布在  Java
关注(0)|答案(1)|浏览(135)

我已经编写了一个注解处理器,它只是将注解类的列表转储到一个文本文件中,为了适应Eclipse中的增量构建,它还尝试重新读取文件并检查类是否仍然具有注解,如果没有,它将从列表中删除类。
问题是Eclipse在您删除注解时不会触发注解处理器,因此只有在您将注解添加到另一个类时,文本文件才会更新。
有没有一种方法可以将Eclipse配置为在删除注解时运行注解处理器?该项目正在使用Gradle/Buildship。

@Override
  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    if (!roundEnv.processingOver()) {
      try {
        generateAnnotationFiles(annotations, roundEnv);
      } catch (IOException e) {
        throw new IllegalStateException(e);
      }
    }

    return false;
  }

  private void generateAnnotationFiles(
      Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) throws IOException {
    for (var i : annotations) {
      var elements = roundEnv.getElementsAnnotatedWith(i);
      var fileName = "META-INF/annotations/" + i.getQualifiedName();
      var classBuilder = new HashSet<>(convertElementsToList(elements));
      classBuilder.addAll(getPreviousListedClasses(fileName, i));
      var classes = new ArrayList<>(classBuilder);
      Collections.sort(classes);

      try (var out =
          new PrintWriter(
              processingEnv
                  .getFiler()
                  .createResource(StandardLocation.SOURCE_OUTPUT, "", fileName)
                  .openWriter())) {
        for (var clazz : classes) {
          out.append(clazz);
          out.append('\n');
        }
      }
    }
  }

  private List<String> convertElementsToList(Set<? extends Element> elements) {
    var ret = new ArrayList<String>();

    for (var elem : elements) {
      var packag = processingEnv.getElementUtils().getPackageOf(elem).getQualifiedName().toString();
      var fullyQualifiedName = packag + "." + elem.getSimpleName();

      ret.add(fullyQualifiedName);
    }

    return ret;
  }

  private List<String> getPreviousListedClasses(String fileName, TypeElement annotation)
      throws IOException {
    try {
      var previousFile =
          processingEnv.getFiler().getResource(StandardLocation.SOURCE_OUTPUT, "", fileName);
      var classSplit = openPreviousFile(previousFile);
      var ret = new ArrayList<String>();

      for (var clazz : classSplit) {
        if (classIsStillAnnotated(clazz, annotation)) {
          ret.add(clazz);
        }
      }

      return ret;
    } catch (FilerException e) {
      throw e;
    } catch (IOException e) {
      return Collections.emptyList();
    }
  }

  private String[] openPreviousFile(FileObject previousFile) throws IOException {
    try (var in = previousFile.openInputStream()) {
      var fileContents = ByteStreams.toByteArray(in);
      var fileString = new String(fileContents, StandardCharsets.UTF_8);
      var classSplit = fileString.split("\n");
      return classSplit;
    }
  }

  private boolean classIsStillAnnotated(String clazz, TypeElement annotation) {
    var prevTypeElement = processingEnv.getElementUtils().getTypeElement(clazz);

    if (prevTypeElement == null) {
      return false;
    }

    var annot = prevTypeElement.getAnnotationMirrors();

    for (var i : annot) {
      if (i.getAnnotationType().asElement().equals(annotation)) {
        return true;
      }
    }

    return false;
  }
cgyqldqp

cgyqldqp1#

问题是getSupportedAnnotationTypes()需要返回“*”,因为它需要能够对空的注解集进行操作。它还需要始终运行writeAnnotationFile,而不仅仅是在roundEnv.getElementsAnnotatedWith返回结果时运行。
现在,当注解被删除时,它会正确地更新文件。这意味着当你进行完全重建时,它会消耗所有的类,但是一个慢的处理器比一个根本不工作的处理器要好。它也有一个问题,它会创建空的文本文件,而不是删除它,但这不是什么大问题。

编辑

之前发布的解决方案在Eclipse中运行,但在使用Gradle构建时却无法运行。此解决方案在Eclipse和Gradle中都可以运行。

@SupportedAnnotationTypes({"*"})
@SupportedSourceVersion(SourceVersion.RELEASE_11)
@AutoService(Processor.class)
public class AnnotationTextDump extends AbstractProcessor {
  private List<AnnotationTextDumpWriter> writers = null;

  @Override
  public Set<String> getSupportedAnnotationTypes() {
    return Set.of("*");
  }

  @Override
  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    if (writers == null) {
      writers =
          Arrays.asList(
              new AnnotationTextDumpWriter("your.Annotation1"),
              new AnnotationTextDumpWriter("your.Annotation2"));
    }

    if (!roundEnv.processingOver()) {
      for (var i : writers) {
        try {
          i.appendElements(annotations, roundEnv, processingEnv);
        } catch (IOException e) {
          // You should properly log the exception here, but I'm not sure how to get it to appear
          // in Eclipse's Problem's view.
          e.printStackTrace();
          throw new IllegalStateException(e);
        }
      }
    } else {
      for (var i : writers) {
        i.close();
      }
    }

    return false;
  }
}

class AnnotationTextDumpWriter{
  private String annotationName;
  private boolean initialized;
  private Set<String> classes;
  private PrintWriter fileOutput;

  XmImmutableListWriter(String annotationName) {
    this.annotationName = annotationName;
    initialized = false;
    classes = null;
    fileOutput = null;
  }

  void appendElements(
      Set<? extends TypeElement> annotations,
      RoundEnvironment roundEnv,
      ProcessingEnvironment processingEnv)
      throws IOException {
    initialize(processingEnv);

    for (var i : annotations) {
      if (i.getQualifiedName().toString().equals(annotationName)) {
        var elements = roundEnv.getElementsAnnotatedWith(i);
        convertElementsToList(elements, processingEnv);
      }
    }
  }

  private void initialize(ProcessingEnvironment processingEnv) throws IOException {
    if (initialized) {
      return;
    }

    var fileName = "META-INF/annotations/" + annotationName;

    initialized = true;
    classes = new HashSet<>();
    classes.addAll(getPreviousListedClasses(fileName, annotationName, processingEnv));
    fileOutput =
        new PrintWriter(
            processingEnv
                .getFiler()
                .createResource(StandardLocation.CLASS_OUTPUT, "", fileName)
                .openWriter());
  }

  private void convertElementsToList(
      Set<? extends Element> elements, ProcessingEnvironment processingEnv) {
    for (var elem : elements) {
      var packag = processingEnv.getElementUtils().getPackageOf(elem).getQualifiedName().toString();
      var fullyQualifiedName = packag + "." + elem.getSimpleName();

      classes.add(fullyQualifiedName);
    }
  }

  void close() {
    if (classes != null) {
      var sortedClasses = new ArrayList<>(classes);
      Collections.sort(sortedClasses);

      for (var i : sortedClasses) {
        fileOutput.append(i);
        fileOutput.append('\n');
      }
    }

    fileOutput.close();
  }

  private static List<String> getPreviousListedClasses(
      String fileName, String annotationName, ProcessingEnvironment processingEnv)
      throws IOException {
    try {
      var previousFile =
          processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", fileName);
      var classSplit = openPreviousFile(previousFile);
      var ret = new ArrayList<String>();

      for (var clazz : classSplit) {
        if (classIsStillAnnotated(clazz, annotationName, processingEnv)) {
          ret.add(clazz);
        }
      }

      return ret;
    } catch (FilerException e) {
      throw e;
    } catch (IOException e) {
      return Collections.emptyList();
    }
  }

  private static List<String> openPreviousFile(FileObject previousFile) throws IOException {
    try (var in = previousFile.openInputStream()) {
      var fileContents = ByteStreams.toByteArray(in);
      var fileString = new String(fileContents, StandardCharsets.UTF_8);

      if (fileString.isBlank()) {
        return Collections.emptyList();
      }

      var classSplit = fileString.split("\n");
      return Arrays.asList(classSplit);
    }
  }

  private static boolean classIsStillAnnotated(
      String clazz, String annotationName, ProcessingEnvironment processingEnv) {
    var prevTypeElement = processingEnv.getElementUtils().getTypeElement(clazz);

    if (prevTypeElement == null) {
      return false;
    }

    var annot = prevTypeElement.getAnnotationMirrors();

    for (var i : annot) {
      var annotElem = i.getAnnotationType().asElement();
      var packag =
          processingEnv.getElementUtils().getPackageOf(annotElem).getQualifiedName().toString();
      var qualifiedAnnotName = packag + "." + annotElem.getSimpleName();

      if (qualifiedAnnotName.equals(annotationName)) {
        return true;
      }
    }

    return false;
  }
}

在Eclipse中可用但在Gradle中不可用的旧解决方案

@SupportedAnnotationTypes({"*"})
@SupportedSourceVersion(SourceVersion.RELEASE_11)
@AutoService(Processor.class)
public class AnnotationTextDump extends AbstractProcessor {
  public static final String YOUR_ANNOTATION1 = "your.Annotation1";
  public static final String YOUR_ANNOTATION2 = "your.Annotation2";

  @Override
  public Set<String> getSupportedAnnotationTypes() {
    return Set.of("*");
  }

  @Override
  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    if (!roundEnv.processingOver()) {
      try {
        generateAnnotationFiles(annotations, roundEnv);
      } catch (Exception e) {
        // You should properly log the exception here, but I'm not sure how to get it to appear
        // in Eclipse's Problem's view.
        throw new IllegalStateException(e);
      }
    }

    return false;
  }

  private void generateAnnotationFiles(
      Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) throws IOException {
    Set<? extends Element> elements1 = Collections.<Element>emptySet();
    Set<? extends Element> elements2 = Collections.<Element>emptySet();

    for (var i : annotations) {
      if (i.getQualifiedName().toString().equals(YOUR_ANNOTATION1)) {
        elements1 = roundEnv.getElementsAnnotatedWith(i);
      }

      if (i.getQualifiedName().toString().equals(YOUR_ANNOTATION2)) {
        elements2 = roundEnv.getElementsAnnotatedWith(i);
      }
    }

    writeAnnotationFile(YOUR_ANNOTATION1, elements1);
    writeAnnotationFile(YOUR_ANNOTATION2, elements2);
  }

  private void writeAnnotationFile(String annotationName, Set<? extends Element> elements)
      throws IOException {
    var fileName = "META-INF/annotations/" + annotationName;
    var classBuilder = new HashSet<>(convertElementsToList(elements));
    classBuilder.addAll(getPreviousListedClasses(fileName, annotationName));
    var classes = new ArrayList<>(classBuilder);
    Collections.sort(classes);

    var fileContents = new StringBuilder();

    for (var i : classes) {
      fileContents.append(i);
      fileContents.append('\n');
    }

    try (var out =
        new PrintWriter(
            processingEnv
                .getFiler()
                .createResource(StandardLocation.SOURCE_OUTPUT, "", fileName)
                .openWriter())) {
      out.append(fileContents.toString());
    }
  }

  private List<String> getPreviousListedClasses(String fileName, String annotationName)
      throws IOException {
    try {
      var previousFile =
          processingEnv.getFiler().getResource(StandardLocation.SOURCE_OUTPUT, "", fileName);
      var classSplit = openPreviousFile(previousFile);
      var ret = new ArrayList<String>();

      for (var clazz : classSplit) {
        if (classIsStillAnnotated(clazz, annotationName)) {
          ret.add(clazz);
        }
      }

      return ret;
    } catch (FilerException e) {
      throw e;
    } catch (IOException e) {
      return Collections.emptyList();
    }
  }

  private List<String> openPreviousFile(FileObject previousFile) throws IOException {
    try (var in = previousFile.openInputStream()) {
      var fileContents = ByteStreams.toByteArray(in);
      var fileString = new String(fileContents, StandardCharsets.UTF_8);

      if (fileString.isBlank()) {
        return Collections.emptyList();
      }

      var classSplit = fileString.split("\n");
      return Arrays.asList(classSplit);
    }
  }

  private boolean classIsStillAnnotated(String clazz, String annotationName) {
    var prevTypeElement = processingEnv.getElementUtils().getTypeElement(clazz);

    if (prevTypeElement == null) {
      return false;
    }

    var annot = prevTypeElement.getAnnotationMirrors();

    for (var i : annot) {
      var annotElem = i.getAnnotationType().asElement();
      var packag =
          processingEnv.getElementUtils().getPackageOf(annotElem).getQualifiedName().toString();
      var qualifiedAnnotName = packag + "." + annotElem.getSimpleName();

      if (qualifiedAnnotName.equals(annotationName)) {
        return true;
      }
    }

    return false;
  }

  private List<String> convertElementsToList(Set<? extends Element> elements) {
    var ret = new ArrayList<String>();

    for (var elem : elements) {
      var packag = processingEnv.getElementUtils().getPackageOf(elem).getQualifiedName().toString();
      var fullyQualifiedName = packag + "." + elem.getSimpleName();

      ret.add(fullyQualifiedName);
    }

    return ret;
  }
}

相关问题