以编程方式编辑/修改.java文件?(不是.class文件)

Jay*_*Jay 13 java code-generation

所以,这是一段使用CodeModel生成java代码的代码:

    JCodeModel cm = new JCodeModel();
    JDefinedClass dc = cm._class("foo.Bar");
    JMethod m = dc.method(0, int.class, "foo"); 
    m.body()._return(JExpr.lit(5));
    File f = new File("C:/target/classes");
    f.mkdirs();
    cm.build(f);
Run Code Online (Sandbox Code Playgroud)

此代码生成.java文件:

package foo;
public class Bar {

       int foo() {
        return  5;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,我希望CodeModel为我创建一个新的java文件.我已经有一个.java文件,并希望在其中的方法中添加几行代码.所以,我希望API直接修改java文件/创建它的修改后的副本.有办法做到这一点吗?

Big*_*ich 11

我知道自原始帖子以来已经有一段时间了,但是看起来更容易看到的Java转换库之一似乎是Spoon.

来自Spoon主页:

Spoon使您能够转换(见下文)和分析(参见示例)源代码.Spoon提供了一个完整且细粒度的Java元模型,其中可以访问任何程序元素(类,方法,字段,语句,表达式......)以进行读取和修改.Spoon作为输入源代码并生成可以编译的转换后的源代码.

更新:Square还创建了JavaPoet源代码生成库,流畅的API看起来很简单.


Aro*_*Aro 6

...我希望 API 直接修改 java 文件/创建它的修改副本。有没有办法做到这一点?

JavaParser是一个 API,它允许您读入 Java 文件,修改它们,并以字符串的形式获取结果。

更具体地说,JavaParser 解析文件并构建一个 AST(抽象语法树)。然后,您可以使用 API 修改表示源代码的 JavaParser AST,并检索 AST 的字符串表示。

我已经有一个 .java 文件,并且想向其中的方法添加几行代码。

下面是使用 JavaParser 在方法主体的末尾添加一行并打印结果的示例:

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Optional;

public class Main {
  public static void someMethod() {
    // Lines will be added here.
  }

  public static void main( String[] args ) throws FileNotFoundException {
    String newStatement = "System.out.println(\"Hello world!\");";
    File myClassSourceFile = new File( "Main.java" );

    JavaParser parser = new JavaParser();

    ParseResult<CompilationUnit> pr = parser.parse( myClassSourceFile );
    Optional<CompilationUnit> ocu = pr.getResult();

    if( ocu.isPresent() ) {
      CompilationUnit cu = ocu.get();
      ClassOrInterfaceDeclaration decl = cu.getClassByName( "Main" ).get();
      MethodDeclaration method = decl.getMethods().get( 0 );
      method.getBody().ifPresent( ( b ) -> b.addStatement( newStatement ) );
    }

    // Print out the resulting Java source code.
    System.out.println( pr.toString() );
  }
}
Run Code Online (Sandbox Code Playgroud)

CompilationUnit - 来自 JavaParser 的 javadoc,“此类代表整个编译单元。每个 java 文件表示一个编译单元。”

在您的代码中,Option.get()用适当的处理替换调用。


将方法日志添加到命令行上给出的类名的示例:

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Optional;

public class Main {
  public static void someMethod() {
    // Lines will be added here.
  }

  public static void main( String[] args ) throws FileNotFoundException {
    String newStatement = "System.out.println(\"Hello world!\");";
    File myClassSourceFile = new File( "Main.java" );

    JavaParser parser = new JavaParser();

    ParseResult<CompilationUnit> pr = parser.parse( myClassSourceFile );
    Optional<CompilationUnit> ocu = pr.getResult();

    if( ocu.isPresent() ) {
      CompilationUnit cu = ocu.get();
      ClassOrInterfaceDeclaration decl = cu.getClassByName( "Main" ).get();
      MethodDeclaration method = decl.getMethods().get( 0 );
      method.getBody().ifPresent( ( b ) -> b.addStatement( newStatement ) );
    }

    // Print out the resulting Java source code.
    System.out.println( pr.toString() );
  }
}
Run Code Online (Sandbox Code Playgroud)

这会将更改后的源文件写入标准输出。如果将Main文件放在core项目的主包中,则可以构建core项目的 JAR 文件(例如,mvn package)。将 JAR 文件重命名为javaparser.jar然后运行Main所有 JAR 文件:

for i in $(find . -type f -name "*.java"); do \
  java -cp javaparser.jar com.github.javaparser.Main "$i" > \
    "$i.jp";
done
Run Code Online (Sandbox Code Playgroud)

当然,让 Java 遍历目录树会更有效率。一旦.jp文件存在并且看起来没问题,您可以使用以下方法重命名它们:

find . -type f -name "*jp" -size +100c -exec \
  sh -c 'mv {} $(dirname {})/$(basename {} .jp)' \;
Run Code Online (Sandbox Code Playgroud)

这将破坏原始格式,使其相当不适合签入存储库。某些 Java 14 语句可能无法转换为可以编译的文件。天啊。