参考链接:
阅读指南:
- 首先要看一下参考链接1——AST和ASTView的简介,然后下载ASTView包,实践感受一下。由于参考链接1里面提供的那个下载ASTView的插件网址moved了,所以我下面给出了一个ASTView包的下载链接;
- 其次我们要看参考链接2,在Eclipse中实现AST的获取与访问,在实现AST之前,要先安装和配置Maven,可以看参考链接3和4;
- 最后我们写代码来实现一个实例:如何将Java源代码转换为AST,即解析源代码。
1.ASTView包的下载与ASTView的使用
-
ASTView包的下载
地址:https://archive.eclipse.org/jdt/ui/update-site/plugins/
我下载的是:
直接将org.eclipse.jdt.astview_1.1.9.201406161921.jar复制到eclipse的dropins文件夹中,再打开Eclipse就可以用了,具体来说:依次点击 Window -> Show View -> Other,然后搜索ASTView,打开即可得到ASTView视图。如下图。
-
ASTView的使用见参考链接1
2.如何将Java源代码转换为AST,即解析源代码——实例
为什么要用Maven呢?
Maven很方便呀。主要它可以进行依赖管理: Maven通过中央仓库和本地仓库来管理项目的依赖库,开发人员只需要在项目配置文件中声明所需的依赖,Maven会自动下载并管理这些依赖。当然它还有很多特别好的功能,具体看参考链接4。
-
安装和配置Maven见参考链接3
图2.1 我的Maven版本
-
准备好Maven之后,写代码将Java源代码转换为AST
可以先导入本项目所需依赖:
在pom.xml文件中添加我们所需要的jar包依赖,可以去官网查找,地址为:https://mvnrepository.com/,每一个依赖都复制其<dependency>的内容,然后粘贴到pom文件中即可
我的pom.xml文件:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.my.ast</groupId>
<artifactId>ast_test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 依赖管理部分,将所有的依赖放在dependencies标签内 -->
<dependencies>
<!-- https://mvnrepository.com/artifact/org.eclipse.jdt/org.eclipse.jdt.core -->
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>org.eclipse.jdt.core</artifactId>
<version>3.40.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.eclipse.platform/org.eclipse.osgi -->
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.osgi</artifactId>
<version>3.22.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.eclipse.platform/org.eclipse.equinox.preferences -->
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.equinox.preferences</artifactId>
<version>3.11.200</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.eclipse.platform/org.eclipse.equinox.common -->
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.equinox.common</artifactId>
<version>3.19.200</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.eclipse.platform/org.eclipse.core.runtime -->
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.core.runtime</artifactId>
<version>3.32.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.eclipse.platform/org.eclipse.core.resources -->
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.core.resources</artifactId>
<version>3.22.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.eclipse.platform/org.eclipse.core.jobs -->
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.core.jobs</artifactId>
<version>3.15.400</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.eclipse.platform/org.eclipse.core.contenttype -->
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.core.contenttype</artifactId>
<version>3.9.600</version>
</dependency>
</dependencies>
</project>
1.首先,新建一个Maven项目,如果没看见Maven Project,可以去 其它 里面搜一下;
2.点击下一步之后,出现下面界面,继续点击下一步;
3.填写Group Id和Artifact Id,点击完成,我们就得到了如下目录;
-
这个是大佬给的注释:
这个是我填写的
得到目录:
4.在 src/main/java 里面新建一个工具类 JdtAstUtil,用于将源代码以字符数组形式解析为AST;
JdtAstUtil类代码:
package ast_test;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
public class JdtAstUtil {
/**
* get compilation unit of source code
* @param javaFilePath
* @return CompilationUnit
*/
//CompilationUnit 代表一个 Java 编译单元,即一个 Java 源文件
//输入参数为需解析的Java源代码文件路径,返回值为该文件对应的CompilationUnit节点
public static CompilationUnit getCompilationUnit(String javaFilePath){
//begin
//读取指定路径的 Java 源文件内容,并将其存储在字节数组 input 中,判断输入文件是否存在和可用
byte[] input = null;
try {
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(javaFilePath));
//将源码存储在字节数组
input = new byte[bufferedInputStream.available()];
bufferedInputStream.read(input);
bufferedInputStream.close();
} catch (FileNotFoundException e) {//异常检测
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//end
//创建ASTParser并将Java源文件解析成AST:
ASTParser astParser = ASTParser.newParser(AST.JLS3);
astParser.setSource(new String(input).toCharArray());
//设置要解析的类型,包含下面四种,可按需选择
//K_COMPILATION_UNIT:编译单元,即一个Java文件
//K_CLASS_BODY_DECLARATIONS:类的声明
//K_EXPRESSION:单个表达式
//K_STATEMENTS:语句块
astParser.setKind(ASTParser.K_COMPILATION_UNIT);
//获取代码的AST,实现AST解析
CompilationUnit result = (CompilationUnit) (astParser.createAST(null));
return result;
}
}
小Tip:平时自己写代码的时候,可以写完class以后使用Ctrl + Shift + O 自动补全相应的import语句
5.将源代码转化为了AST之后,接下来要做的就是访问节点,需要学习两个Eclipse AST的核心类;以下出自参考链接2
- ASTNode:AST节点类,所有的节点类型都是ASTNode的子类,至于某一个节点源代码具体到底是什么节点,可以通过ASTView来查看。
- ASTVisitor:AST访问者类,它针对不同类型的节点提供了一系列重载的visit()和endvisit()方法,意思就是访问到该类型的节点时执行visit()方法,访问完该类型节点后执行endvisit()方法。其中,visit()方法需返回boolean类型的返回值,表示是否继续访问子节点。另外ASTVisitor还提供了preVisit()和postVisit()方法,参数类型为ASTNode,也就是说不论哪种类型的节点,访问前后都要分别执行preVisit()和postVisit()。这些方法的具体实现由ASTVisitor的子类负责,如果不需要对所访问到的节点做处理,则无需在ASTVisitor的子类中覆盖这些方法。
通过ASTView可以确定类、方法和属性的声明对应的AST节点分别是TypeDeclaration、MethodDeclaration和FieldDeclaration;
FieldDeclaration类比较特殊,因为一个FieldDeclaration下可能有多个同类型的属性被声明,也就是说,我们遍历FieldDeclaration时,需要遍历它的子节点VariableDeclarationFragment,
在 src/main/java 里面新建一个访问者子类DemoVisitor,继承自ASTVisitor,并覆盖参数类型为TypeDeclaration、MethodDeclaration和FieldDeclaration的visit()方法,由于需要遍历所有节点,因此将返回值都设为true。
DemoVisitor代码:
package ast_test;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
public class DemoVisitor extends ASTVisitor {
//实现节点的打印,重写父类的一些方法实现节点打印
@Override
public boolean visit(FieldDeclaration node) {
for (Object obj: node.fragments()) {
VariableDeclarationFragment v = (VariableDeclarationFragment)obj;
System.out.println("Field:\t" + v.getName());
}
return true;
}
@Override
public boolean visit(MethodDeclaration node) {
System.out.println("Method:\t" + node.getName());
return true;
}
@Override
public boolean visit(TypeDeclaration node) {
System.out.println("Class:\t" + node.getName());
return true;
}
}
6.写测试类Main,对上述解析源代码的工具类(JdtAstUtil)和访问AST的访问者类(DemoVisitor )进行测试
在 src/test/java 里面新建类Main和ClassDemo
Main类代码:
其中JdtAstUtil.getCompilationUnit()参数为需解析的Java源代码文件路径,示例:
D:\\Eclipse2\\ast_test\\src\\test\\java\\ast_test\\classDemo.java
package ast_test;
import org.eclipse.jdt.core.dom.CompilationUnit;
public class Main {
public static void main(String[] args) {
//源码与AST转换
CompilationUnit comp = JdtAstUtil.getCompilationUnit("D:\\Eclipse2\\ast_test\\src\\test\\java\\ast_test\\classDemo.java");
//实例化访问者类,配合前面写的工具类访问节点
demoVisitor visitor = new demoVisitor();
comp.accept(visitor);
}
}
7.写一个简单的Java源代码文件(ClassDemo.java)如下,对它进行AST解析
package ast_test;
public class ClassDemo {
private String text = "Hello World!", text2;
public void print(int value) {
System.out.println(value);
}
public void input(String value) {
text2 = value;
}
}
End......