1 Java程序文件中函数起始行和终止行在程序文件位置中的判定__抽象语法树方法
应用需求:
实现对BigCloneBench中函数体的克隆检测,必须标注出起始行号和终止行号。
问题:
给定一个Java文件,从中提取出每个函数的起始行和终止行。
难点:
这个问题的难点在于,对于Java的解析器而言,其在形成抽象语法树的过程中,已经对源码文件进行了划分,然后,形成了对函数的抽象语法树。但是这部分操作是不开源的,因此我们无法操作。我们只能在已经形成的抽象语法树上进行操作,读取函数的起始行和终止行。
技术手段:
Eclipse中的Eclipse JDT提供了一组访问和操作Java源代码的API,Eclipse AST是其中一个重要组成部分,它提供了AST、ASTParser、ASTNode、ASTVisitor等类,通过这些类可以获取、创建、访问和修改抽象语法树。
实验与观察:
示例函数:

主体程序代码:
CompilationUnit cu = extractCompilationUnit(sourceFilePath, javaVersion);
//Method visitor
MethodVisitor methodVisitor = new MethodVisitor();
cu.accept(methodVisitor);
List<MethodDeclaration> methods = methodVisitor.getMethods();
for(MethodDeclaration method : methods){
int methodStartLineNumber=cu.getLineNumber(method.getStartPosition());
System.out.println("methodCode:");
System.out.println(method.toString());
System.out.println(methodStartLineNumber);
//Visit the method node and extract all ASTNodes
nodes = ASTNodeVisitor.visitMethod(method);
int j=0;
for (ASTNode node : nodes) {
System.out.println("子节点"+(++j));
System.out.println("所在起始行:"+cu.getLineNumber(node.getStartPosition()));//计算起始行
System.out.println("所在终止行:"+cu.getLineNumber(node.getStartPosition()+node.getLength()-1));//计算终止行
System.out.println("子节点类型:"+ASTNode.nodeClassForType(node.getNodeType()));
System.out.println("子节点内容:");
System.out.println(node.toString());
}
}
其中,cu是使用ASTParser类对Java文件进行解析以后得到的CompilationUnit类的编译单元。MethodVisitor继承ASTVisitor类,是对抽象语法树的每个MethodDeclaration类节点进行存储,构建methods列表,每个元素对应一个函数的抽象语法树的顶层节点。ASTNodeVisitor的visitMethod方法则对method对应抽象语法树的每个节点进行遍历,将节点存储到nodes列表中。
部分输出结果是:
methodCode:
/**
* Creates an instance of {@link Antlr4ErrorLog}.
* @param log The Maven log
*/
public Antlr4ErrorLog(Tool tool,BuildContext buildContext,Log log){
this.tool=tool;
this.buildContext=buildContext;
this.log=log;
}
52
可以看到:示例函数的起始行52是javadoc对应起始行的位置,并不是public起始行的位置。这是因为一个method的抽象语法树单元是包括javadoc单元和block单元的,其规则为:
* <pre>
* MethodDeclaration:
* [ Javadoc ] { ExtendedModifier } [ <b><</b> TypeParameter { <b>,</b> TypeParameter } <b>></b> ] ( Type | <b>void</b> )
* Identifier <b>(</b>
* [ ReceiverParameter <b>,</b> ] [ FormalParameter { <b>,</b> FormalParameter } ]
* <b>)</b> { Dimension }
* [ <b>throws</b> Type { <b>,</b> Type } ]
* ( Block | <b>;</b> )
* ConstructorDeclaration:
* [ Javadoc ] { ExtendedModifier } [ <b><</b> TypeParameter { <b>,</b> TypeParameter } <b>></b> ]
* Identifier <b>(</b>
* [ ReceiverParameter <b>,</b> ] [ FormalParameter { <b>,</b> FormalParameter } ]
* <b>)</b> { Dimension }
* [ <b>throws</b> Type { <b>,</b> Type } ]
* ( Block | <b>;</b> )
* </pre>
可以看到Block是最后的一个元素。
我们做三种进一步的小实验:
实验一:移动大括号{,观察函数主体位置
假如我们将{从public所在行打到下一行去,即58行,我们再观察一下:

输出结果:

我们会看到,block的所在行从57行变成了58行。
实验二:添加人工注释,观察节点内容变化

输出结果:

之前终止行是61行,现在是第62行。然后public还有block等的起始行都是58。虽然第57行注释没有被解析为抽象语法树的内容,但是,整个函数的行数还是包括人工注释的行数!
所以,如何精确计算函数的起始位置和终止位置?不能简简单单的去除javadoc的行数!
结论:人工注释不被解析,只有javadoc格式才会被解析成为抽象语法树的一部分。但是,尽管没有被解析进去,但是人工注释的那一行被包含在内。你会发现函数的总行数增加了。
实验三:在大括号}后添加注释


最终方案:
我们不能简单通过去除javadoc行数的方式得到函数真正函数体和声明的起始行和终止行。
我们只有首先判断是否有javadoc节点,如果没有,接下来的第一个节点的行号就是起始行。然后,函数的终止行很好计算。
于是,就有了方法。
不再展开。
1 Java程序文件中函数起始行和终止行在程序文件位置中的判定__抽象语法树方法的更多相关文章
- C运行时库(C Run-time Library)详解(提供的另一个最重要的功能是为应用程序添加启动函数。Visual C++对控制台程序默认使用单线程的静态链接库,而MFC中的CFile类已暗藏了多线程)
一.什么是C运行时库 1)C运行时库就是 C run-time library,是 C 而非 C++ 语言世界的概念:取这个名字就是因为你的 C 程序运行时需要这些库中的函数. 2)C 语言是所谓的“ ...
- java将类和函数封装成jar,然后在别的项目中使用这个jar包
本来想用idea安装的,不过用maven生成后发现jar有20,30M肯定不对,后来还是用eclipse生成了,方便很多 环境: eclipse luna,jdk1.8_112 1.生成jar包,首先 ...
- IE浏览器下载文件保存时提示:“你没有权限在此位置中保存文件”解决办法
E浏览器下载文件保存时提示 解决办法: 1.Win + R,打开运行命令,输入gpedit.msc,如图所示 2.打开计算机本地组策略编辑器:选择计算机配置-windows设置-安全设置-本地策略-安 ...
- 【JAVA-JDT-AST】Java抽象语法树的构建、遍历及转成dot格式(附Github源码)
Background: 最近为了重现tree-based clone detection的论文:L. Jiang, G. Misherghi, Z. Su, and S. Glondu. Deckar ...
- 【阅读笔记】《C程序员 从校园到职场》第六章 常用文件操作函数 (Part 1)
参考链接:https://blog.csdn.net/zhouzhaoxiong1227/article/details/24926023 让你提前认识软件开发(18):C语言中常用的文件操作函数总结 ...
- PHP中函数的使用
函数是一种可以在任何被需要的时候执行的代码块函数的定义 1.函数是一个被命名的独立的代码段 2.它执行特殊的任务 3.并可以给调用它的程序返回值 函数的优点: 1.提高程序的重 ...
- 总结文件操作函数-文件夹(三)-C语言
获取.改变当前文件夹: 原型为: #include <unistd.h> //头文件 char *getcwd(char *buf, size_t size); //获取当前文件夹.相 ...
- 总结文件操作函数(一)-C语言
在进程一開始执行,就自己主动打开了三个相应设备的文件.它们是标准输入.输出.错误流.分别用全局文件指针stdin.stdout.stderr表示,相应的文件描写叙述符为0.1.2:stdin具有可读属 ...
- C语言样式的文件操作函数
使用C语言样式的文件操作函数,需要包含stdio.h头文件. 1.打开文件的函数: //oflag的取值为“w”或“r”,分别表示以写或读的方式打开 FILE* fd = fopen(filename ...
随机推荐
- android开发中的 Activity 与 Context 区别与联系
Context 是 Application /Activity /Service的基类 Intent(Context , Class); Activity中的上下文Context是随着活动的产生而产生 ...
- ubuntu 安装JDK8
1.下载JDK选择下载Linux版本-64位:jdk-8u65-linux-x64.tar.gz下载链接:http://www.oracle.com/technetwork/java/javase/d ...
- win10系统自带的浏览器ME如何将网页转成PDF
不多说,直接上干货! 很多用户都已经开始玩上win10了,补充玩玩一些技巧,当作小灶. 不多废话,在windows 10网页是可以保存为pdf格式.具体如下: 欢迎大家,加入我的微信公众号:大数据躺过 ...
- 关联更新 Update
Update a set a.Manage_FunctID=b.Manage_FunctID From Manage_PageUrl a Left join Manage_ButtonBar ...
- Chapter 3 Phenomenon——11
"I'm completely fine, Char — Dad," I sighed. "There's nothing wrong with me." “我 ...
- 学习笔记---log4j的使用与配置
Log4j由三个重要的组件构成:日志信息的优先级,日志信息的输出目的地,日志信息的输出格式. 日志信息的优先级从高到低有OFF.FATAL.ERROR.WARN.INFO.DEBUG.ALL,分别用来 ...
- windows server服务器上部署java+tomcat网站域名配置
如果只是部署java项目的话,可以把IIS删除,然后在服务器上安装jdk tomcat 配置好环境变量,就和你在自己计算机上开发一样,把你的项目war包拷到tomcat下的webapps里(任意目录都 ...
- Node.js之Express三
端午节3天说没就没了,自己的脚伤都快一个月了还没好,原本想着去桂林或者厦门呢,可计划赶不上变化,看自己公司C#软件工程师的招聘条件有要求MongoDb,年前就打算自己学习下,买的这本书就叫Node.j ...
- ARP协议抓包之帧长度和Gratuitous ARP的问题
用Winpcap编程构造ARP包选择网卡并发出,遇到若干问题,学到了许多新知识,但是有的还尚未解决,在这里记录下没解决的和解决的问题. 先来看下ARP协议的格式,ARP字段有28个字节,发到以太网中还 ...
- 基于Ionic的移动框架搭建