转载自:http://www.oschina.net/bbs/thread/9893
简介: Eclipse 为程序员提供了强大的搜索功能,文件搜索 (File Search) 用来搜索工作空间下的所有文本文件,JAVA 搜索 (Java Search) 能够搜索工作空间下的所有 Java 文件。如果被搜索的内容包含于项目依赖的 Jar 文件中,如界面上的字符串,Eclipse 现有的搜索功能就无法满足需求。本文介绍了 Eclipse 提供的 API 以及 JDT 中 Jar 文件相关的 UML 结构图,讲述如何定位 Jar 文件中包含关键字的源代码和文本文件。
引言
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 文件相关的类结构对应图
上图描述了各个节点之间的层次、包含和对应 关系,了解这些信息,对文章下一部分的阅读是必要的。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
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 源代码并比较
得到 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 中查找源码
在 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 程序员的喜爱。
下载本文示例代码
相关推荐
ASTView是JDT的一部分,它提供了一个图形化的界面来显示Java源代码的抽象语法树(Abstract Syntax Tree, AST)。在Eclipse中,ASTView是一个强大的工具,用于查看和理解Java源代码如何被解析为AST结构,这对于理解和...
- janino-2.5.16.jar:这是一个轻量级的Java源代码编译器,Drools可能用它作为替代编译器,特别是在对Eclipse JDT Core有特殊需求或者无法使用的情况下。 - droolsjbpm-ide-common.jar:这个文件可能包含了Drools与...
JDT原代码、工具根据IMB官网 和下载我的源代码进行操作
Jasper-JDT包含Jasper编译器和相关工具,这个编译器将JSP页面转换为Java源代码,然后编译成Servlet。JDT(Java Development Toolkit)是Eclipse IDE的一部分,提供了强大的Java编辑器和工具。jasper-jdt.jar可能是...
1. 编译器:JDT Core中的Java编译器是基于ANTLR(ANother Tool for Language Recognition)的,能够解析和理解Java源代码,生成字节码,进行类型检查和错误报告。它还支持Javadoc的生成以及遵循Java语言规范的新特性...
这个"java JDT core 全套jar相关插件"集合包含了所有你需要用来进行Java源代码分析、编译和调试的库。这些JAR文件是开发者在不使用完整Eclipse IDE的情况下,进行Java开发或者构建工具链时的重要组成部分。 JDT的...
`jdt-compiler.jar`是这个工具集中的关键组件,包含了Java源代码的编译逻辑。 描述中提到的"进度条"和"动画加载的计算"可能是在讨论一个用户界面(UI)设计,其中进度条用于展示动画加载的状态。在软件开发中,特别是...
在Eclipse中运行Tomcat源代码,需要一系列的依赖库,包括JAR包,以便能够正确编译、理解和执行Tomcat服务器的相关组件。以下是对给定的文件信息中提到的几个关键JAR包的详细解释: 1. **org.eclipse.jdt.core_3.4.0...
1. **源代码解析**:解析Java源代码,理解类、方法、变量等结构。 2. **语法高亮**:在编辑器中以不同颜色显示代码,提高可读性。 3. **代码完成**:在编写代码时提供自动补全功能,减少手动输入。 4. **编译与构建*...
1. **源代码编辑器**:JDT提供了一个强大的Java源代码编辑器,具备语法高亮、自动完成、错误检测、代码折叠和格式化等功能,极大地提高了开发效率。 2. **构建系统**:JDT包含构建工具,能够理解Java项目的结构,...
Ant是一个开源的Java构建工具,它的主要功能包括编译源代码、打包、测试、部署等。与传统的Makefile相比,Ant使用XML来描述构建过程,使得构建脚本具有更好的跨平台性和可读性。Ant的核心概念是任务(Task),这些...
9. `org.eclipse.core.contenttype_3.4.1.R35x_v20090826-0451.jar`:定义和管理内容类型,确保正确识别和处理不同的文件类型,这对于识别需要格式化的Java源代码至关重要。 10. `org.eclipse.core.runtime_3.5.0.v...
- `org.eclipse.jdt.launching.sourcelookup`:源代码查找API。 - `org.eclipse.jdt.ui`:用户界面API。 - `org.eclipse.jdt.ui.actions`:用户界面操作API。 - `org.eclipse.jdt.ui.jarpackager`:Jar打包API。...
JDT采用了一种层次化的对象模型来表示Java程序的结构,这种结构基于项目的类路径派生而来。以下是JDT中主要的Java元素及其功能简介: - **IJavaModel**:代表Java模型的根元素,对应于整个工作空间。它是所有具有...
JDT提供了一套完整的API来处理Java源代码,使得Jasper可以使用Eclipse的编译器来编译JSP生成的Java源代码。 3. jasper-el.jar:此文件包含了JavaServer Pages Expression Language (EL)的支持。EL是一种轻量级的...
在Eclipse中,当我们创建自定义的编译器插件或者进行源代码分析时,这些jar包会提供必要的API,让我们能够生成、遍历和修改AST。例如,`org.eclipse.jdt.core`库提供了编译器服务,可以用于解析源代码并生成AST;`...
然而,有时开发者需要查看Java类库的源代码,而这些库可能并未提供源码。这时,就需要借助于反编译工具来将已编译的.class文件转换回可读的.java源码。本文将详细讲解如何在Eclipse中通过jad.exe和...
然而,当你尝试直接导入Tomcat 7.0的源代码到开发环境中,如Eclipse,你可能会遇到编译错误,因为缺少了必要的依赖库。这些错误通常表现为编译器的红叉错误,提示无法找到或解析某些类。 首先,`ant.jar`是一个关键...
jdt-compiler-3.1.1.jar jasperreports-3.5.3.jar commons-javaflow-20060411.jar jasperreports-3.5.3-applet.jar jasperreports-3.5.3-javaflow.jar groovy-all-1.5.5.jar JasperReport报表 fckeditor-java-...
* org.eclipse.jdt.core.eval - 支持对代码片段编辑测试窗或调试器中的代码段进行评估 * org.eclipse.jdt.core.jdom - 支持 Java “文档对象模型”(DOM),它可用于表示 Java 编译单元的结构 * org.eclipse.jdt....