Javaparser使用

什么是Javaparser

分析,转换,生成Java代码.

Javaparser库为你提供了一个 Java 代码的抽象语法树(Abstract Syntax Tree)。
AST 结构允许您以一种简单的编程方式使用 Java 代码。

为什么需要Javaparser

相对于antlr4,Javaparser提供了更多的API,专门操作Java文件,使用起来更简单.

使用

1.解析多个Java文件

/**
* 解析工程下的所有Java文件
*
* @param path 工程根目录
*/
public static void parseProject(String path) {
    Path root = Paths.get(path);
    // only parsing
    ProjectRoot projectRoot = new ParserCollectionStrategy().collect(root);
    projectRoot.getSourceRoots().forEach(sourceRoot -> {
        System.out.println(sourceRoot);
        try {
            // 解析source root
            sourceRoot.tryToParse();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 获取解析后的编译单元列表
        List<CompilationUnit> cuList = sourceRoot.getCompilationUnits();
        cuList.forEach(JavaParserUtil::parseOneFile);
    });
}

/**
* 解析单个Java文件
*
* @param cu 编译单元
*/
public static void parseOneFile(CompilationUnit cu) {
    // 类型声明
    NodeList<TypeDeclaration<?>> types = cu.getTypes();
    for (TypeDeclaration<?> type : types) {
        System.out.println("## " + type.getName());
        // 成员
        NodeList<BodyDeclaration<?>> members = type.getMembers();
        members.forEach(JavaParserUtil::processNode);
    }
}

/**
* 处理类型,方法,成员
*
* @param node
*/
public static void processNode(Node node) {
    if (node instanceof TypeDeclaration) {
        // 类型声明
        // do something with this type declaration

    } else if (node instanceof MethodDeclaration) {
        // 方法声明
        // do something with this method declaration
        String methodName = ((MethodDeclaration) node).getName().getIdentifier();
        System.out.println("方法: " + methodName);

    } else if (node instanceof FieldDeclaration) {
        // 成员变量声明
        // do something with this field declaration
        // 注释
        Comment comment = node.getComment().orElse(null);

        // 变量
        NodeList<VariableDeclarator> variables = ((FieldDeclaration) node).getVariables();
        SimpleName fieldName = variables.get(0).getName();
        if (comment != null) {
            System.out.print(handleComment(comment.getContent()));

        }
        System.out.print("\t");
        System.out.print(fieldName);
        System.out.println();
    }

    for (Node child : node.getChildNodes()) {
        processNode(child);
    }
}

2.修改Java文件

public static void main(String[] args) throws FileNotFoundException {
    // The directory where we store the examples
    String pathToExamplesDir = "." + separator + "src"
            + separator + "main" + separator + "resources";
    String javaName = pathToExamplesDir
            + separator + "ASimpleClass.java";

    // Parse the code of an entire source file, a.k.a. a Compilation Unit
    CompilationUnit compilationUnitNode = StaticJavaParser.parse(new File(MyJavaParserConstant.JAVA_FILE_PATH));
    printCompilationUnit("My original class", compilationUnitNode);

    // Modifying the name of the class
    String className = "HelloWorld";
    compilationUnitNode.getClassByName(className).get()
            .setName("MyRenamedClass");
    printCompilationUnit("Renamed class", compilationUnitNode);

    // Adding a method: we add a setter
    MethodDeclaration setter = compilationUnitNode
            .getClassByName("MyRenamedClass").get()
            .addMethod("setAField", Modifier.Keyword.PUBLIC);
    setter.addParameter("boolean", "aField");
    setter.getBody().get().getStatements().add(new ExpressionStmt(
            new AssignExpr(
                new FieldAccessExpr(new ThisExpr(),"aField"),
            new NameExpr("aField"),
            AssignExpr.Operator.ASSIGN
    )));
    printCompilationUnit("With a setter", compilationUnitNode);
}

3.生成Java代码

/**
 * 代码生成
 *
 * @author zhangcheng
 * @date 2021/12/4
 */
public class CodeGenerationExample {

    public static void main(String[] args) {
        CompilationUnit compilationUnit = new CompilationUnit();
        compilationUnit.setPackageDeclaration("my.example.javaparser");

        // Add an asterisk import for java.util
        compilationUnit.addImport("java.util", false, true);

        // Create a class (not an interface, so the 2nd parameter is false)
        ClassOrInterfaceDeclaration myClass = compilationUnit.addClass("MyClass", Modifier.Keyword.PUBLIC);
        myClass.addField("List<String>", "elements", Modifier.Keyword.PRIVATE);

        // Method to add an element to the field
        MethodDeclaration addElement = myClass.addMethod("addElement", Modifier.Keyword.PUBLIC);
        // our method get a parameter: the value to add to the field
        addElement.addParameter("String", "newElement");
        // the body consists in one expression wrapped into a statement
        // the expression is in invocation of elements.add to which we
        // pass the parameter
        addElement.getBody().get().getStatements().add(new ExpressionStmt(
                new MethodCallExpr(new NameExpr("elements"), new SimpleName("add"),
                        NodeList.nodeList(new NameExpr("newElement")))
        ));

        // Method to get elements
        MethodDeclaration getElements = myClass.addMethod("getElements", Modifier.Keyword.PUBLIC);
        // we specify that we are returning a Collection of String
        getElements.setType("Collection<String>");
        // The body consists of just a return statement. We return the
        // field
        getElements.getBody().get().getStatements().add(new ReturnStmt(
                new NameExpr("elements")));

        System.out.println(compilationUnit);
    }
}

参考

Javaparser github

Javaparser homepage

Javaparser使用教程

Javaparser代码生成

https://github.com/ftomassetti/effectivejava.git

https://github.com/javaparser/javasymbolsolver.git

可用的demos
https://github.com/ftomassetti/analyze-java-code-examples.git