资讯专栏INFORMATION COLUMN

Java代码分析器(二): 使用DOM API操作抽象语法树

pf_miles / 1608人阅读

摘要:请下载一份源代码,这里有个对应的源码包元素的组合需要遵守基本的语法规则不是全部。名字也是表达式的一种,是指变量名类名包名等元素,它们在中都属于抽象类,分为和两种具体类。

另载于 http://www.qingjingjie.com/blogs/3

上篇博客末尾提到了一棵抽象语法树长什么样子。JDT提供了一套DOM API来让我们顺利地控制这样一棵树。

读完本篇后请继续完成上篇的延伸阅读:http://help.eclipse.org/ 点击JDT Plug-in User Guide -> Programmer"s Guide -> JDT Core -> Manipulating Java Code。

各种语法元素用不同的结点类来表示,一些主要的类有:
CompilationUnit: 编译单元,相当于一个.java文件
AbstractTypeDeclaration: 各种类型的声明及定义,如类、接口、枚举、注解。以下几个类都继承自该类
TypeDeclaration: 类或接口
EnumDeclaration: 枚举类
AnnotationInterfaceDeclaration: 注解,即@interface Xxx {...}
MethodDeclaration: 方法
FieldDeclaration: 域 (成员变量)
Modifier: 修饰符,如public, static, volatile等,也包括注解,如@Override
Block: 代码块,也就是花括号{...}所包裹的一段代码
Statement: 语句,是代码块的一部分,也就是以;或}结尾的一段代码
Expression: 表达式,是语句或声明的一部分, 例如a+b, "seg", a.call(b), ((Number) this)等

其中一些是抽象类,它们会有一些具体的子类,列举在抽象类的javadoc中。

结点之间通过对象引用来连接,举个例子,MethodDeclaration主要有这么几个属性:

SimpleName name; //方法名
List parameters; //方法参数
List modifiers;
List typeParameters; //泛型参数
Type returnType;
Block body; //方法体
Javadoc javadoc;

为了掌握JDT的使用方法,我们需要观看其javadoc和源代码,因为文档太缺乏。这部分源代码还是挺容易懂的。
请下载一份源代码,这里有个Eclipse 4.3.1对应的JDT源码包 http://grepcode.com/snapshot/repository.grepcode.com/java/eclipse.org/4.3.1/org.eclipse.jdt/core/3.9.1

元素的组合需要遵守基本的语法规则(不是全部)。当我们用上篇博客的方式读入并解析Java代码时,JDT只会做词法分析和语法分析,而不会做语义分析,因此只能发现符号不合法、语句不完整、括号不匹配之类的简单错误,至于变量未声明、类型不匹配、缺少import、方法未实现之类的错误,则不会发现。语义分析需要Eclipse workspace的支持,因此需要实现一个Eclipse插件,本系列博客不研究这件事;如果费点工夫,也是可以自行实现一定程度的语义分析的,如果有空可以分享一下。

为了更好地理解语法树结构,请读者运行上篇的代码,解析一个真实的.java文件,在debug模式下逐级展开观察CompilationUnit的内部结构。

然后我们来介绍一些语法概念,方便进一步理解:

字面量是表达式的一种,int, boolean, String的常量(以及null, X.class)在语法上都是字面量(literal)。

名字也是表达式的一种,是指变量名、类名、包名等元素,它们在JDT中都属于Name抽象类,分为SimpleName和QualifiedName两种具体类。SimpleName是不带"."的标识符,如变量名foo;QualifiedName则是带有"."的合成名字,如包名java.util.List。QualifiedName是由SimpleName组成的。
根据Java语法,标识符只能包含字母、数字、下划线“_”、美元符号“$”,且不得以数字开头。

从上篇最后的代码开始,从CompilationUnit出发跟着对象引用一路走下去,就能分析代码结构了。但是到了代码块一级就要开始面对数不清的甚至有嵌套的语句和表达式了,不得了啊。其实我们可以使用ASTVisitor来遍历所有结点,不用自己硬着头皮一层层去访问。

像下面的代码,就自定义了一个visitor,来遍历所有的方法调用,我们把它应用到CompilationUnit这棵树上(也可以应用到任意一棵子树上):

ASTVisitor visitor = new ASTVisitor() {
    @Override
    public boolean visit(MethodInvocation mi) {
        System.out.println(mi);
        return true;
    }
}
compilationUnit.accept(visitor); //应用到树上

这样它就在这棵树上走了一圈,碰到合适的结点就干活。"return true"的作用是告诉visitor继续前进,如果return false,visitor就会停止前进。

再来溜一段复杂点的代码,我们把visitor的visit方法改一改:

ASTVisitor visitor = new ASTVisitor() {
    @Override
    public boolean visit(MethodInvocation mi) {
        if (mi.getExpression() instanceof ThisExpression) {
            mi.setExpression(null);
        }
        return true;
    }
}
compilationUnit.accept(visitor);
System.out.println(compilationUnit.toString());

如果代码中含有this.doThing()这样的调用,处理后重新输出的代码会变成doThing(),this被去掉了。

[本篇待续]

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64142.html

相关文章

  • Java代码析器(一): JDT入门

    摘要:另载于这是一个关于抽象语法树的故事。抽象语法树是对程序代码的结构化表示,是对代码进行词法分析语法分析后得到的产物。 另载于 http://www.qingjingjie.com/blogs/2 这是一个关于抽象语法树(Abstract Syntax Tree, AST)的故事。 抽象语法树是对程序代码的结构化表示,是对代码进行词法分析、语法分析后得到的产物。编译器要用到它,很多生产力工...

    binaryTree 评论0 收藏0
  • 校招社招必备核心前端面试问题与详细解答

    摘要:本文总结了前端老司机经常问题的一些问题并结合个人总结给出了比较详尽的答案。网易阿里腾讯校招社招必备知识点。此外还有网络线程,定时器任务线程,文件系统处理线程等等。线程核心是引擎。主线程和工作线程之间的通知机制叫做事件循环。 showImg(https://segmentfault.com/img/bVbu4aB?w=300&h=208); 本文总结了前端老司机经常问题的一些问题并结合个...

    DevTalking 评论0 收藏0
  • 校招社招必备核心前端面试问题与详细解答

    摘要:本文总结了前端老司机经常问题的一些问题并结合个人总结给出了比较详尽的答案。网易阿里腾讯校招社招必备知识点。此外还有网络线程,定时器任务线程,文件系统处理线程等等。线程核心是引擎。主线程和工作线程之间的通知机制叫做事件循环。 showImg(https://segmentfault.com/img/bVbu4aB?w=300&h=208); 本文总结了前端老司机经常问题的一些问题并结合个...

    jonh_felix 评论0 收藏0
  • 校招社招必备核心前端面试问题与详细解答

    摘要:本文总结了前端老司机经常问题的一些问题并结合个人总结给出了比较详尽的答案。网易阿里腾讯校招社招必备知识点。此外还有网络线程,定时器任务线程,文件系统处理线程等等。线程核心是引擎。主线程和工作线程之间的通知机制叫做事件循环。 showImg(https://segmentfault.com/img/bVbu4aB?w=300&h=208); 本文总结了前端老司机经常问题的一些问题并结合个...

    Rango 评论0 收藏0

发表评论

0条评论

pf_miles

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<