基于JDT的JAR源代码搜索
副标题#e#
引言
Eclipse 为措施员提供了强大的文本搜索成果,措施员可以利便 的在事情空间中搜索到需要的 JAVA 代码可能文本。可是有时候,措施员但愿在 .class 文件源码可能普通文本文件中搜索某个字符串,而这些文件包括于 Jar 文件中,此时 Eclipse 就无法满意要求。好比,用户试图寻找 UI 上显示的某 字符串的界说位置,这就需要在 Jar 文件内的普通文本文件 , 以及 .class 文 件源码中搜索。这些 Jar 文件包括于项目类路径中,这个成果在 RCP 开拓中是 常常需要的,而 Eclipse 今朝还未提供这个特性。本文通过利用 JDT(Java Development Toolkit)中与 Jar 相关的接口,办理了这个问题,并给出示例及 措施。
Eclipse 中搜索的道理
Eclipse 回收 Lucene 技能开拓其搜索内核, 该内核通过对要害字举办索引,快速定位方针文件。譬喻,Eclipse 会对 JAVA 源文件中的类名、字段名、要领名等举办索引,当措施员利用 Open Type 成果 (快捷键:CTRL+SHIFT+T)举办类搜索时,便可以通过类名这个索引字段举办快 速搜索 ; 在利用 JAVA 搜索(Java Search)成果时,Eclipse 也会让用户指定 详细的索引字段(Search For),如可以选择要领名、类名、字段名、包名、构 造器名等,Eclipse 会按照选择的索引字段与用户的输入,快速搜索到源代码。
Eclipse 在提供 Jar 源代码搜索方面的限制
Eclipse 提供了文 件搜索(File Search)的成果,用来搜索指定范畴 ( 项目、事情空间等 ) 内 的文本文件。这个成果并没有按照某些非凡要害字举办索引。因为对付任意字符 串的搜索,是无法找到特定要害字举办索引的。因此为了提高搜索效率, Eclipse 对付任意字符串的搜索范畴仅限于用户编写的文本文件,而没有对项目 所依赖的 Jar 文件中的类的源代码举办搜索。因为 Jar 源代码的数量往往数量 复杂,搜索它们将是一个相当费时的操纵。可是在许多环境下,措施员有须要进 行类源代码的搜索,通过查察需要的源代码办理一些问题,Eclipse 今朝提供的 搜索成果就无法满意这样的需求。
JDT 中 Jar 文件相关的类布局图阐明
下面两图展示了 JDT 中与 Jar 文件处理惩罚相关的类,从图中,可以清晰 的相识它们的条理、包括和对应干系。
图 1. JDT 中与 Jar 文件相关的 UML 类布局图
图 2. JDT 中与 Jar 文件相关的类布局对应图
#p#副标题#e#
上图描写了各个节点之间的条理、包括和对应干系,相识这些信息, 对文章下一部门的阅读是须要的。Java 项目,Jar 文件,Jar 文件中的包,Jar 文件中的普通文件夹,class 文件和非 class 文件别离对应于 IJavaProject, JarPackageFragmentRoot,JarPackageFragment,JarEntryDirectory, IClassFile 以及 JarEntryFile。IJavaProject 包括了多个 JarPackageFragmentRoot。而每个 JarPackageFragmentRoot 包括多个的 JarPackageFragment、JarEntryDirectory 和 JarEntryFile。雷同的, JarPackageFragment 包括多个的 IClassFile 和 JarEntryFile;每个 JarEntryDirectory 包括多个的 JarEntryFile 和 JarEntryDirectory。
Jar 源代码搜索办理方案
本文将用一个例子措施,慢慢先容如何 实现 Jar 源代码的搜索,进一步相识 JDT 提供的 API。办理方案的主要逻辑为 :遍历事情空间下的所有 JAVA 项目,而且逐一得到项目所依赖的 Jar 文件列 表。然后遍历该列表得到每个 Jar 文件中的 class 文件源码和非 class 文件 的文本内容,利用正则表达式举办匹配查找。最后输出功效的类名、文件路径、 匹配的起始位置和匹配的字符串长度等信息。
团结这个逻辑以及 JDT 中 Jar 文件相关的类布局,上述办理方案中的主要技能问题包罗:
ResourcesPlugin.getWorkspace().getRoot().getProjects() 可以得到 事情空间下的所有项目,范例为 IProject。那么如何将 IProject 工具转换为 JAVA 项目对应的 IJavaProject 工具?(文中步调 1 办理该问题)
获 得 IJavaProject 工具后,如何得到它所依赖的 Jar 文件列表,也就是 JarPackageFragmentRoot 工具列表?(文中步调 2 办理该问题)
得到 JarPackageFragmentRoot 工具后,如何得到它下面的包(JarPackageFragment ),又如何得到包下的 class 文件(IClassFile)和非 class 文本文件 (JarFileEntry)?(文中步调 3、5 办理该问题)
如何得到 IClassFile 的源代码,又如何得到 JarFileEntry 的文本内容?(文中步调 4 、6 办理该问题)
下面将对各个步调逐一地举办阐明,而且一一办理上 面提到的问题。
步调 1. 转换 IProject 为 IJavaProject
#p#分页标题#e#
Eclipse 事情空间下,大概存在很多范例的项目,有 JAVA 项目也有非 JAVA 项目,为了得到项目依赖的 Jar 文件,该项目必需是 Java 范例的项目。以下代码通过挪用 JDT 提供的接口,得到 JAVA 项目列表。
清单 1. 得到事情空间下的所有 JAVA 项目
/* 得到事情 空间下的所有项目 */
IProject[] projects = ResourcesPlugin.getWorkspace().getRoot()
.getProjects();
if (projects != null)
{
for (IProject p : projects)
{
/* 实验转换普通项目为 JAVA 项目 */
IJavaProject create = JavaCore.create(p);
/* 判定项目是否是 JAVA 项目 */
if (create != null && create.exists())
{
/* 操 作 JAVA 项目 */
…
}
}
}
步调 2. 得到依赖的 JarPackageFragmentRoot 列表
得到 IJavaProject 工具后,需要获得它 所依赖的 Jar 文件列表。在 JDT 中,Jar 文件对应的类为 JarPackageFragmentRoot,下面一段措施用于得到 Jar 文件列表。
清单 2. 得到 JAVA 项目依赖的 Jar 文件列表的代码
IJavaProject project= …
IJavaElement[] children = project.getChildren();
if (children != null)
{
/* 遍历 project 下的 Java 元素 */
for (IJavaElement ele : children)
{
/* 判定是否 是 JarPackageFragmentRoot 工具 */
if (ele instanceof JarPackageFragmentRoot)
{
/* 操纵此 jar 文件 */
…
}
}
}
大大都时候措施员想要搜索的范畴并不包括 JRE 库的源代码,因此为了提高搜索效率,需要屏蔽 JRE 库源代码的搜索,下面一 段措施展示如何实现这个需求。
清单 3. 屏蔽 JRE 库的源码搜索
JarPackageFragmentRoot jarFile = … ;
IJavaProject project = … ;
/**
* 判定此 Jar 是否在 JRE 库中。假如是,将其屏蔽 , 以提高效率
*/
IClasspathEntry rawClasspathEntry = jarFile.getRawClasspathEntry ();
IClasspathContainer classpathContainer = JavaCore.getClasspathContainer(
rawClasspathEntry.getPath(), project);
/* 判定此 jar 是否属 于项目默认的 JRE 库。假如是,不查抄该 jar*/
if (classpathContainer.getKind() == IClasspathContainer.K_DEFAULT_SYSTEM)
{
/* 跳过此 jar 的搜索 */
}
步调 3. 得到 JarPackageFragmentRoot 中的 IClassFile 列表
假如想得到 .class 文 件的源码,就需要得到 .class 文件对应的 IClassFile 工具,下面的措施展示 了如何从 JarPackageFragmentRoot 工具开始遍历,得到其包括的 PackageFragment 工具,又从 PackageFragment 工具中得到 IClassFile 工具 列表。
清单 4. 获取所有类文件的代码
JarPackageFragmentRoot root = …
IJavaElement[] children = root.getChildren();
if (children != null)
{
/* 遍历 JarPackageFragmentRoot 下的 所有包元素 */
for (IJavaElement ele : children)
{
if (ele instanceof PackageFragment)
{
IJavaElement[] classes = ((PackageFragment) ele).getChildren();
/* 遍历 PackageFragment 下的所有类元素 */
for (IJavaElement cls : children)
{
if (ele instanceof IClassFile)
{
/* 得到 IClassFile 工具举办操纵 */
}
}
}
}
}
需要留意的是,JDT 中 Jar 文件 中的包(package)对应的类为 JarPackageFragment,可是该类为 default 类 型,无法引用,可以先将它转换为它的父类 PackageFragment,然后举办处理惩罚。
步调 4. 得到 IClassFile 源代码并较量
#p#分页标题#e#
获得 IClassFile 工具 后,需要得到其源码 .IClassFile 提供了很是利便的接口: getSource。应用 该要领可以得到源码字符串。假如该要领的输出值为 null,说明这个类还未绑 定源代码。这种环境下可以通过双击 .class 文件,点击 Change Attached Source 按钮举办源代码的绑定。下面的措施展示了如何按照用户输入的正则表 达式举办较量搜索。
清单 5. 获取类文件源码并较量
IClassFile cf = … ;
Pattern searchPattern = Pattern.compile("用户输入的正则表达式");
/* 得到 IClassFile 源码 */
String source = cf.getSource ();
if (source != null)
{
Matcher matcher = searchPattern.matcher(source);
while (matcher.find())
{
/* 得到偏移量 */
int offset = matcher.start();
String group = matcher.group();
/* 得到长度 */
int length = group.length ();
}
}
步调 5. 得到 JarPackageFragmentRoot 中的非 JAVA 资源
Jar 文件中 JAVA 资源主要 是 .java 文件和 .class 文件。Jar 文件中非 JAVA 的资源,对应的类为 IJarEntryResource,好比 Jar 中的 .properties 文件、META-INF 文件夹、 META-INF 文件夹下的 MANIFEST.MF 等,都属于非 JAVA 资源,这些非 JAVA 资 源可以存放于 JarPackageFragmentRoot 下,也可以存放于 JarPackageFragment 下。下面的措施展示如何遍历得到 JarPackageFragmentRoot 下的所有非 JAVA 资源。
清单 6. 得到非类文 件的资源
/* 注:ele 也可以是 PackageFragment, 它们都拥 有 getNonJavaResources 要领 */
JarPackageFragmentRoot ele = … ;
Object[] nonjavares = ele.getNonJavaResources();
if (nonjavares != null)
{
for (Object o : nonjavares)
{
/* JarEntryDirectory 相当于 META-INF 文件夹 */
if (o instanceof JarEntryDirectory)
{
JarEntryDirectory ff = (JarEntryDirectory)o;
IJarEntryResource[] children = ff.getChildren();
for (IJarEntryResource e : children)
{
if (e instanceof JarEntryDirectory)
{
/* 这里需要递归处 理 */
}
else if (e instanceof JarEntryFile)
{
/* 处理惩罚该文本文件 */
}
}
}
/* JarEntryFile 相当于 META-INF 文件夹下的 MANIFEST.MF 可能 .properties 文件等 */
else if (o instanceof JarEntryFile)
{
JarEntryFile ff = (JarEntryFile) o;
/* 处理惩罚该文本文件 */
}
}
}
步调 6. 得到非 JAVA 资源的源代码并较量
在本文的代码示例中只展示从 JarEntryFile 得到源代码的要领,假如需要利用其他范例的非 JAVA 资源的获 取要领,请查察附件中的源码。
清单 7. 得到非类文件的文本内容而且 较量
/**
* 该要领用于得到 JarEntryFile 源代码并 且较量得到功效
*/
private void cooperate (JarEntryFile ff, Pattern searchPattern)
{
ByteArrayInputStream contents;
try
{
contents = (ByteArrayInputStream) ff.getContents ();
byte[] bs = new byte[contents.available ()];
contents.read(bs);
String con = new String(bs);
Matcher matcher = searchPattern.matcher(con);
while (matcher.find())
{
/* 得到功效的偏移和长度 */
int start = matcher.start();
int length = matcher.group().length();
}
}
catch (Exception e)
{
}
}
步调 7. 输出功效
为了简朴,措施会将功效打 印到节制台上,包罗功效中的偏移量、长度、以及查找到源代码路径。
详细应用情况 —RCP 中查找源码
#p#分页标题#e#
在 RCP 二次开拓中,有时候很是需要 查察已有 UI 的源码,供措施员参考利用。下面将利用前面开拓的例子搜索包括 UI 上某字符串的源文件或源代码。一般来说,界面上的字符串都被存放于 .properties 文件,利便修改和多语言处理惩罚。由于 .properties 中大概呈现占 位符,界面显示的是处理惩罚占位符后的功效,所以需要选取符合的字符串举办搜索 。搜索到 .properties 文件后,就按照该 .properties 文件地址的包名,和自 身的文件名,搜索引用该 properties 文件的类。如对付 com.ibm.wise.A_zh_CN.propertis 文件,搜索 com.ibm.wise.A 即可,详细原 因可以搜索 ResourceBundle 的相关资料举办查阅。下面展示这一搜索进程。
步调 1. 按照 UI 上的字符串得到其 properties 文件地址
图 3. 要搜索的 UI 字符串
例子将试图搜索包括“This section provides general information about” 字符串的 properties 文件,假如未搜索到,可以 适当缩短字符串长度。
图 4. 输入 UI 字符串的措施界面
点击 OK 就可以举办搜索。
图 5. 属性文件搜索功效(用时 3.23 秒)
功效显示了包括该字符串的 properties 文件路径,以及字符串在该文件中 呈现的起始位置和长度信息。该文件包括于 Jar 文件中。按照搜索到的路径, 打开文件查察源码。
图 6. 属性文件源代码
图中灰色配景部门就是需要查找的字符串。
步调 2. 按照 properties 文件名得到引用该 properties 文件的类
在得到 properties 文件名之后,按照搜索到的 properties 文件名(在本文的例子中 是 gui.properties),搜索引用该属性文件的 java 类 , 输入查询的字符串为 :com.ibm.btools.blm.ui.attributesview.resource.gui。
图 7. 输入 利用属性文件的字符串措施界面
图 8. 类搜索功效(用时 2.43 秒)
通过 Open Type 成果,输入 BLMAttributesviewMessageKeys,就可 以找到此 .class 文件,打开 .class 文件就可以查察它的源代码。
其 他接口简朴先容
这里简朴的先容了 JDT 中与 Jar 相关的类的其他有用 接口,如表 1 所示。
表 1. 其他接口先容
类 | 接口 |
IClassFile | isClass():boolean 判定是否是 class 范例 isInterface():boolean 判定是否是 interface 范例 |
PackageFragment | createCompilationUnit ():ICompilationUnit 新建 JAVA 文件 delete():void 删除此包 getClassFile():IClassFile 获得包下某个 class 文件 getClassFiles():IClassFile[] 获得包下所有 class 文件 getCompilationUnit():ICompilationUnit 获得包下某个 java 文件 getCompilationUnits():ICompilationUnit[] 获得包下所有 java 文件 rename():void 重定名 |
JarPackageFragmentRoot | getJar():ZipFile 获得 jar 文件对应的 ZipFile getKind ():int 可以是 IPackageFragmentRoot.K_SOURCE 可能 IPackageFragmentRoot.K_BINARY,暗示是源代码范例照旧二进制范例 isArchive():boolean 判定是否是压缩文件 isReadOnly():boolean 判 断是否只读 |
竣事语
在举办二次开拓时,通 过查察源代码可以很好的辅佐措施员相识原有系统,同时对付查找和阐明代码漏 洞也有很大的辅佐。该文章简朴的先容了如何利用 JDT 提供的接口举办源代码 的搜索,文章内容仅供参考,有乐趣的伴侣可以参考该文章的实现,举办进一步 的优化,提高搜索效率,可能做成实用的插件宣布,相信会受到许多 Java 措施 员的喜爱。
原文地点:http://www.ibm.com/developerworks/cn/opensource/os-ecl- jdtsearch/