以编程方式编辑Java源文件 [英] Edit java source files programmatically

查看:189
本文介绍了以编程方式编辑Java源文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找一种方法,以编程方式编辑.java文件,而无需使用不稳定的RegExes。由于我是在编辑.java文件而不是.class文件,因此我不需要任何字节码操作工具。

I'm looking for a way to edit .java files programmatically without resorting to unstable RegExes. Since I'm editing .java files, not .class files I do not want any Byte-code manipulation tools.

我需要一些东西:


  • 与IDE无关(因此没有ASTParser我想在CI上自动化它,所以除非有独立版本,否则AST就没有了)

  • 允许我阅读.java文件,为方法添加注释并保存它-因此单纯的源代码生成工具(想到的是CodeModel)

  • 不是太复杂和/或专用于Java-因此没有ANTLR

简而言之,可以重现此情况:

So in short, something to reproduce this scenario:

File f = new File("path/to/.java");
CodeParser p = CodeParser.parse(f);
Method m = p.getMethods.get(0);
if (m.getBody().contains("abcdef") 
     && m.getAnnotation.getClass().equals(Test.class)){
   m.addAnnotation(MyAnnotation.class);
}
p.saveEdits(f);

我尝试了Java反射,但是它不能做到(也因为它是字节码分析,它无法解析方法的主体)。与Java模型API类似。我试图让AST独立运行,但失败了(也许有办法吗?)

I have tried Java reflection, but it can't do it (also since it's byte-code analysis, it can't parse a method's body). Similarly with java model API. I tried to get AST to work standalone but I failed (maybe there is a way?)

如果绝对没有办法或工具可以做到,用正则表达式以独特且稳定的方式处理? (即,除了上面的伪代码外,任何可能的Java源代码都不是操作的输入)。如果是这样,请举一个这样的例子。

If there is absolutely no way or tool to do it, is it possible to do with regexes in a unique and stable way? (i.e. no possible Java sourcecode would be an input for operation other than in above pseudo-code). If so, please give me an example of such.

此外,我不需要编译它,在推动更改之后,CI会为我完成。

Also, I do not need to compile it, after pushing the changes, CI will do it for me.

推荐答案

您可以通过程序转换系统(PTS)。这些是独立于IDE的。

You can do this reliably with a program transformation system (PTS). These are IDE-independent.

其中之一是我们的DMS软件再造工具包。 OP可以使用类似于以下DMS元程序的代码来完成其特定任务:(未经测试,不能处理所有极端情况):

One of these is our DMS Software Reengineering Toolkit. OP can accomplish his specific task with code something like the following DMS meta-program: (not tested and doesn't handle all the edge cases):

 (= parse_Tree  (Domains:Java:Parser:ParseFile (. "path/to/.java")))
 (local (= [method_tree AST:Node] (AST:ScanTree parse_Tree (Registry:Pattern (. `any_method'))
      (ifthen (&& (~= method_tree AST:NullTree)
                  (Registry:PatternMatch method_tree (. `TestClass'))
                  (~= AST:NullTree (AST:ScanTree method_tree 
                                       (Registry:Pattern (. `abcdef_identifier'))))
          (Registry:ApplyTransform method_tree (. `insert_MyAnnotation'))
      )ifthen
 )local
 (Registry:PrettyPrintToFile method_tree (. "path/to/.java"))

DMS的元编程语言看起来像Lisp,带有前缀运算符(克服它:-)
ParseFile读取源文件并构建一个AST,并将其停放在pa中rse_Tree。
ScanTree扫描树以查找提供的谓词( Registry:Pattern(.`any_method'))为真的点,并返回匹配的子树或null。
Registry:PatternMatch检查模式谓词在指定树的根部为true:Registry:ApplyTransform应用源到源转换来修改树。

DMS's metaprogramming language looks like Lisp, with prefix operators. (Get over it :-) ParseFile reads a source file and builds an AST, parked in parse_Tree. ScanTree scans tree looking for a point where the supplied predicate ("Registry:Pattern (. `any_method'") is true, and returns a matching subtree or null. Registry:PatternMatch checks that a pattern predicate is true at the root of the specified tree. Registry:ApplyTransform applies a source-to-source transformation to modify the tree.

该元程序由一组具名的模式,可以很容易地在树上表达测试/转换而无需知道树结构的每个最后细节。
为演示目的,这些过分简化了:

This metaprogram is supported by a set of named patterns, which make it easy to express tests/transforms on tree without knowing every last detail of the tree structure. These are oversimplified for presentation purposes:

 default domain Java~v7;

 pattern any_method(p: path_to_name, name: method_name, args: arguments,
                    b: body, a: annotations):declaration =
    " \p \name(\args) \a \b ";  -- doesn't handle non-functions but easily adjusted

 pattern TestClass(p: path_to_name, name: method_name, args: arguments,
                    b: body, a: annotations):declaration =
    " \p \name(\args) [Test.class] \b ";

 pattern abcdef_identifier():IDENTIFIER =
      "abcdef";

 rule insert_MyAnnotation(p: path_to_name, name: method_name, args: arguments,
                          b: body, a: annotations):declaration =
    " \p \name(\args) \a \b "
    ->
    " \p \name(\args) \a [myAnnotation] \b ";

引号是 metaquotes ;它们描述了整个模式匹配语言的语法与以目标语言(在这种情况下为Java,因为域声明)编写的代码片段之间的界限。在元引号内是目标(Java)语言语法,转义的标识符表示与特定树节点类型相对应的模式变量。您必须知道语法的大致结构才能编写这些语法,但是请注意,我们并没有真正深入了解注释或任何内容的形成方式。

The quote marks are metaquotes; they delineate the boundaries between the syntax of the pattern matching language as a whole, and code fragments written in the target language (in this case, Java, because of the domain declaration). Inside the meta quotes is target (Java) language syntax, with escaped identifiers representing pattern variables that correspond to specific tree node types. You have to know the rough structure of the grammar to write these, but notice we didn't really dive into details of how annotations or anything is formed.

可以说可以将any_method和 TestClass模式折叠为一个(实际上,只是TestClass模式本身,因为它纯粹是 any_method的专门化。)

Arguably the "any_method" and "TestClass" patterns could be folded into one (in fact, just the TestClass pattern itself, since it is pure specialization of "any_method".

最后一个 rule (其他为 patterns ,仅用于匹配)说:如果看到X,则将其替换为Y。具体规则是对方法进行模式匹配

The last rule (the others are patterns, only meant for matching) says, "if you see X, replace it by Y". What the specific rule does is pattern match to the method with some list of annotations, and add another one.

这是可靠的程序转换的方式。如果您不想使用DMS(商业产品),请查看Wikipedia页面上的替代方法。

This is the way to reliable program transformations. If you don't want to use DMS (a commercial product), check out the Wikipedia page for alternatives.

这篇关于以编程方式编辑Java源文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆