重写 CompilationUnit 时保留类型、字段和方法注释

Preserving type, field and method comment when rewriting a CompilationUnit

TL;DR:如何在基于给定类型创建新的 java 类型时保留 Javadoc、行和块注释?

长:

我正在使用 headless eclipse 将基于不需要的基础 class 的无限数量的类型 (Java classes) 批量转换为 Java 枚举类型使用 JDT 的应用程序。我的方法是根据类型信息创建一个新的EnumDeclaration,并在初始[=53]的FieldDeclaration(不包括serialVersionUID)的基础上添加EnumConstantDeclaration =] 类型。之后,我通过简单地将原始 MethodDeclaration 的克隆添加到新创建的 EnumDeclarationBodyDelcaration 来添加 MethodDeclaration(不包括构造函数)。完成后,感谢伟大的 API,这非常简单,我将执行以下操作...

// create the EnumDeclaration from the given UnwantedClass CompilationUnit
final EnumDeclaration enumTypeDeclaration = createEnumDeclaration(cu, astRoot, methodDeclarations, ast);
// Find the original UnwantedClass TypeDeclaration and replace it with the new EnumDeclaration
astRoot.accept(new ASTVisitor() {

  @Override
  public boolean visit(final TypeDeclaration node) {
    rewriter.replace(node, enumTypeDeclaration, null);
    return false;
  }
});

...用新的 EnumDeclaration 替换原来的 Java class Type。这几乎完美。唯一缺少的是原始 Java 类型的所有 Line-、Block- 和 JavadocComment 元素。我发现您至少可以通过以下方式检索所有 Comment 个实例:

List<Comment> comments = cu.getCommentList();
if (comments != null) {
  for (Comment comment : comments) {
    comment.accept(visitor);
  }
}

这给了我所有的评论,但我还没有想出如何将 Comment 实例映射到 BodyDeclaration,因为这些 Comment 实例基本上都是自由浮动的在 Source 文件上,并且仅通过 Source 文件中的 startPosition 链接。

getAlternateRoot 方法,但我还没有设法利用那个方法。

问题是:如何保留原始类型中的 Comment 个实例并将它们放在新类型中的正确位置?

似乎没有直接的方法来解决这个问题,因为 javadoc(或一般的注释)在 java 文件中与它们所指的实际代码没有直接关系评论。这只是开发人员的常识,我们通常将注释放在我们正在注释的代码之上。所以我采取了以下路径来解决这个问题:

  1. 创建一个基于起始位置的索引,它将一个整数(起始位置)映射到所谓的 CommentEntry 实例,它基本上是一个由 org.eclipse.jdt.core.dom.Comment 和实际文本组成的元组,我通过简单的子字符串提取编译单元的源字符串。
  2. 我访问我原来的 CompilationUnit 并检索所有相关的 ASTNode,在我的例子中它们是 Type-、Field- 和 MethodDeclaration 实例,并将它们映射到它们的起始位置。
  3. 现在我从 1. 遍历所有映射的 CommentEntry 位置,并将它们与映射到相同位置(来自 2. 的映射)的 ASTNode 实例相关联。结果是一张带有 ASTNode -> CommentEntry.
  4. 的地图
  5. 在构建我的新 EnumDeclaration 时,我从 3 查询地图。要么使用实际的 ASTNode 类型(TypeDeclaration 仅存在一次),要么使用 ASTNode 的实际实例(我使用原始 FieldDeclarations 中的属性创建 EnumConstants) ,或使用 ASTMatcher 来识别正确的 MethodDeclaration,因为我必须克隆这些以便能够将它们“复制”到新的 EnumDeclaration Body 部分。

注释的添加并不像我想象的那么简单,因为在较新的 JSL 中,您不能简单地在 javadoc 中设置注释字符串(仅在 JSL2 中支持,但在这个版本中我们没有't 枚举)。所以,我使用了以下代码:

private void setJavadoc(final BodyDeclaration bodyDeclaration, final CommentEntry commentEntry) {
    final Javadoc javadoc = (Javadoc) ASTNode.copySubtree(ast, commentEntry.getComment());
    final TagElement tagElement = ast.newTagElement();
    final TextElement textElement = ast.newTextElement();
    textElement.setText(commentEntry.getText());
    tagElement.fragments().add(textElement);
    javadoc.tags().add(tagElement);
    bodyDeclaration.setJavadoc(javadoc);
}

如您所见,可以使用 TextElement 简单地添加实际的评论文本,即使它包含标签。为了使其完美,您可能需要对先前从源文件中提取的注释文本进行子字符串化,因为它将包含 /** 和 */.