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 ...
随机推荐
- Git-基本操作(图文)
场景一: 已经用git add 命令把文件加入到暂存区了,这个时候想退回怎么办? 添加文件到暂存区 :git add . 将单个文件撤回到工作区:git rm --cached [文件路径] 将目录撤 ...
- winrar 命令行 解压文件
1,最简单的压缩命令:winrar a asdf.txt.rar asdf.txt a的意思是进行压缩动作,后面第一个参数是被压缩后的文件名,后缀当然是rar了,最后面 的参数就是要被压缩的文件名 2 ...
- JavaScript -- 猜数、遍历
----- 004-猜数.html ----- <!DOCTYPE html> <html> <head> <meta http-equiv="Co ...
- elasticsearch 导入基础数据并索引之 geo_shape
我们看到的图形, 实际是由点来完成的, 有2种类型的格子模型可用于地理星座, 默认使用的是geoHash, 还有一种4叉树(quad trees), 也可用于 判断形状与索引的形状关系 1), int ...
- 第一章 面向对象软件工程与UML
这个OOAD讲的都是很抽象的东西!老师说这个在现在的学习中用到的不是很多,但是以后出去工作的时候就会常用到的. 首先来了解OOAD是讲什么的. OOAD:Object Oriented Analysi ...
- [Codeforces 993E]Nikita and Order Statistics
Description 题库链接 给你一个长度为 \(n\) 的序列 \(A\) ,和一个数 \(x\) ,对于每个 \(i= 0\sim n\) ,求有多少个非空子区间满足恰好有 \(i\) 个数 ...
- Express开发性能优化
1.使用浏览器缓存 在app.js里添加 var CACHETIME = 60 * 1000 * 60 * 24 * 30; app.use(express.static(path.join(__di ...
- [转]Using TRY...CATCH in Transact-SQL
本文转自:https://technet.microsoft.com/en-us/library/ms179296(v=sql.105).aspx Using TRY...CATCH in Trans ...
- C# 之String以及浅拷贝与深拷贝
一.String到底是值类型还是引用类型 MSDN 中明确指出 String 是引用类型而不是值类型,但 String 表面上用起来却像是值类型,这又是什么原因呢? 首先从下面这个例子入手: //值 ...
- $("input[name=name]").val(); 无法获取值问题
<input type="text" class="text" name="name" placeholder= 例如:上海" ...