如何使用带指令的脚本自动重构 Java 代码?

问题描述 投票:0回答:2

如何通过 Java 代码/Groovy 插件以编程方式重构此类? 比方说,我需要:

  • 重命名
    foo.method2
    foo.method3
  • 重命名
    myMethod
    yourMethod
  • 更改导入包
    org.you.core.util.AnotherClassFromExternalPackage
import com.me.core.util.AnotherClassFromExternalPackage;
import com.me.core.util.Foo;

public class MyClass implements AnotherClassFromExternalPackage {

    private final Foo foo;

    public MyClass() {
        this.foo = new Foo();
    }

    @Override
    public long myMethod() {
        return foo.method2();
    }
}

如何创建一个脚本来解析代码、应用语法转换并使用给定的指令保存它?

上面的代码只是一个例子。更大的问题是我有很多项目都在使用同一个外部库。有时他们会发布一个带有重大更改的新版本,这些更改会在依赖性颠簸之后破坏当前代码。 对于 10 多个项目,我必须每 2 周将依赖项更新到最新版本。我需要编写一个脚本来自动修复这些重大更改。 理想情况下,一个一个地应用代码转换。

java refactoring automated-refactoring
2个回答
2
投票

听起来像是 OpenRewrite 的工作。提供标准配方,例如,用于重命名包。您还可以编写自己的食谱,它在基于树的源代码表示上使用访问者模式(这将保留,例如,缩进)。

您可以使用 Maven 或 Gradle 来执行重写。

例如,入门页面中的更改包配置如下所示:

type: specs.openrewrite.org/v1beta/recipe
name: com.yourorg.VetToVeterinary
recipeList:
  - org.openrewrite.java.ChangePackage:
    oldPackageName: org.springframework.samples.petclinic.vet
    newPackageName: org.springframework.samples.petclinic.veterinary

0
投票

不是基于 Groovy 的(但是其他建议“OpenRewrite”或“IntelliJ”的答案也不是基于 Groovy 的)。

OP要求的是一个程序转换系统(PTS),让他编写自定义转换规则。大多数 PTS 系统为您提供对目标语言 AST 的访问……您可以通过编码在树周围爬行、匹配和拼接树节点的访问者来编写自定义规则。 Eclipse 是这样工作的。

这是一种笨拙的方法,主要是因为 AST 的细节很多而且有点随意,而你的树爬行代码必须知道所有这些。有关更多详细信息,请参阅 AST 与表面语法上的program_transforms。理想情况下,您将使用编程语言的表面语法编写转换,并让 PTS 负责针对实际 AST 进行匹配和替换。

我们的 DMS 软件再造工具包 将让您做到这一点。 op 想要做的转换编码如下:

Domain Java~v11; -- specifies which specific Java dialect grammar is relevant

rule rename_foo(p:access_path_prefix): access_path -> access_path
  = "\p foo.method2" -> "\p foo.method3";

rule rename_myMethod(p:access_path_prefix): access_path -> access_path
  = "\p myMethod" -> "\p your_method";

rule change_import_AnotherClassFromExternalPackage: import_statement -> import_statement
  = "import org.you.core.util.AnotherClassFromExternalPackage"
    -> "import com.me.core.util.AnotherClassFromExternalPackage";

ruleset my_patches = 
   { rename_foo, 
     rename_myMethod, 
     change_import_AnotherClassFromExternalPackage
   }

Domain
关键字告诉 DMS 使用哪种精确语法来解析源文本,包括特定的方言。 (Java 6 与 Java 11 不同)。

个别规则被命名(例如,

rename_foo
)。参数列表(例如
p:access_path_prefix
)允许指定一段命名的语法(例如
p
),它可以匹配语法类别的任意树(例如
access_path_prefix
)。每个规则都指定它是一个映射
->
,从一种语法到另一种语法;对于这些规则,源语法类别和目标语法类别是相同的(例如
access_path
)。

每个规则还指定了映射的详细信息(

= "*match*" -> "*replacement*"
),其中匹配和替换是有效的表面语法模式,分别与源语法类别和目标语法类别匹配。引号的原因是将规则语法与引号内的源/目标语言语法(例如 Java)分开。匹配模式
"\p foo.method2"
匹配任意路径前缀(由点结尾的标识符序列或空的特殊情况,由Java语法定义)后跟access_path结尾
foo.method2
作为操作所需。替换模式是相同的 access_path_prefix
p
后跟更改的访问路径结尾
foo.method3
.

ruleset mypatches
将各个规则收集到一个组中,DMS 可以在任何地方应用。

可以运行为 Java~v11 配置的 DMS RuleRewrite 引擎,将其指向合法的 Java 文件,并应用

mypatches
规则集来实现 OP 的期望。 DMS 解析文件以构建 AST,解析规则以构建匹配所需的 AST,匹配规则 AST 并替换出现匹配的 AST(因此您不必通过程序树黑客来完成所有这些工作)。完成后,DMS 重新生成转换后的源文本,保留缩进、注释等。

所以你得到一个应用感兴趣规则的机械过程,规则很容易编写,因为它们使用(Java)表面语法而不是爬树原语。

这些都是相当简单的例子。我们已经使用 DMS 跨 Java 和 C++ 进行大规模重写,甚至将 COBOL 翻译成 Java。

© www.soinside.com 2019 - 2024. All rights reserved.