如何通过 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 周将依赖项更新到最新版本。我需要编写一个脚本来自动修复这些重大更改。 理想情况下,一个一个地应用代码转换。
听起来像是 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
不是基于 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。