内容


向 Rational Software Analyzer 添加您自己的规则

Comments

当您在开发软件时,很容易犯错误。不管是一些简单字符的额外添加,还是一行代码逻辑上的矛盾,都可能会成为一个导致各种规模程序崩溃的漏洞。这些漏洞一般都是来自于程序的开发员,但是,如果不考虑开发员的受教育水平与经验问题,程序中还是会偶然地引入一些漏洞。

不幸的是,尽管业界人士在提高代码质量方面付出了巨大的心血,漏洞还是会由于各种因素产生。他们所作出的努力包括从强调灵敏过程到严格代码评审。因为这一点,事实变得越来越明显,那就是我们功能强大且灵活的自动化分析工具的支持和帮助。静态分析工具是提供总体代码质量的唯一途径。这种类型的工具不是等到开发过程中程序已经处于构建或者运行阶段才开始发挥作用,而是可以分析源代码并识别开发过程中的潜在问题,从而降低了一个程序中缺陷的总体数量。这些工具所能识别的问题,包括从简单的代码错误到复杂的逻辑问题都可以。

尽管工具可以帮助您识别问题并提高代码的质量,但是它们仅仅是一个辅助手段,而不能替代手动人工的代码评审与测试操作。这样做一个很主要的原因在于,该通用工具尽管功能强大,但是还不能识别与业务逻辑相关的特定域问题,以及只与公司内部相关的特定指导原则。

本文向您介绍了 IBM® Rational® Software Analyzer 静态分析工具以及框架。通过使用本款软件提供的公共界面,您就可以编写公共的规则以处理特定域的问题,并提高工具的作用领域。本文的写作目的在于,向您介绍 Rational Software Analyzer 可扩展的 API 并指导您编写您自己的通用规则,这样您就可以处理没有概括过的问题了。为了实现这一点,我们将会描述执行一次分析所用的两种方式。您还会学到怎样为这个问题创建一种快速修复功能。

需要的知识与工具

如果您想要学习本文的内容,您最少需要了解一些术语以及概念并对其有一些使用经验:

  • Eclipse(3.4 版本或者更新的版本)
  • Eclipse 插件开发
  • Java™ 技术(1.5 版本或者更新的版本)

另外,您必须理解下述的概念与技术:

  • 抽象的语法树形结构
  • Eclipse 内部的 Java 开发工具(JDT)
  • Rational Software Analyzer 7.1 版本

同样,您必须在您的系统上安装 Rational Software Analyzer,并且能够使用其插件完成本文所介绍的合步操作。

开始

编写通用规则的第一步,是正确地识别您想要检测到的问题。

识别问题

在本例中,您将会处理 Java 源代码未使用导入中所存在的问题。尽管未使用的导入不会影响到运行时的性能,但是它们会混淆读者的思想并影响代码的可读性。另外,应该不惜一切代价来避免对不需要的类产生依赖关系。考虑一下代码清单 1 中所演示的代码的情况。

清单 1. 无用导入的范例代码
import org.eclipse.core.resources.IResource; public class
Internal { //... code that does not use IResource }

代码清单 1 中的代码导入了 org.eclipse.core.resources.IResource 类,它创建了对 org.eclipse.core.resources 插件的依赖关系。尽管这可能是有害的,但是想象一下如果 Internal 是您内部类的基类时会发生什么呢。使用这些类,对资源插件就可以有一个不那么明显的依赖了。它将会产生一个对不需要插件依赖的长链。

识别方案

接下来的一步是识别方案。但是,方案之间的差别非常之大。它们可能会非常复杂且需要实施特定的方法以及不同的运算法则,抑或只需要简单地删除掉片段即可。在这里的范例中,方案是非常之简单的:您只需从 Java 文件中轻松地删除掉未使用的导入即可。识别合适的方案,对于真正理解这个问题,并验证问题值得使用静态分析发来解决来说,意义重大。

编写您自己的规则

创建项目并添加需要的附件

首先,您必须创建一个插件项目。

  1. 打开 File 菜单并选择 New > New Project
  2. 在对话框中选择 Plug-in Project 并给项目起一个名字。处于此处演示的考虑,在这里将其命名为 com.ibm.rsar.example

接下来,向项目添加需要的附件。

  1. 在项目的 META-INF 文件夹中打开 MANIFEST.MF 文件,并选择 Dependencies 项。
  2. 点击 Add,选择 com.ibm.rsaz.analysis.core,然后保存文件。
  3. 按照相同的程序来添加 com.ibm.rsaz.analysis.codereview.java 作为另外一个附件。

现在您已经创建了项目并添加了启动所需要的附件。此时,您的 MANIFEST.MF 文件应该如下所示:

清单 2. 更新的 MANIFEST.MF
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Example
Plug-in Bundle-SymbolicName: com.ibm.rsar.example;singleton:=true
Bundle-Version: 1.0.0 Bundle-Activator: com.ibm.rsar.example.
Activator Bundle-Vendor: IBM Require-Bundle:org.eclipse.ui,
org.eclipse.core.runtime, com.ibm.rsaz.analysis.core;bundle-
version="7.1.0", com.ibm.rsaz.analysis.codereview.java;bundle-
version="7.0.102", Bundle-RequiredExecutionEnvironment:
JavaSE-1.6 Bundle-ActivationPolicy: lazy

使用扩展点并创建类别

如果您对 Eclipse 扩展点框架不熟悉的话,那么在因特网上有大量的资源可以帮助您去熟悉它。但是,打个比方的话:所谓的 扩展点 就像是一个用电的插座而 扩展 就是那个插头。程序的设计员会指定扩展点,而那些想要扩展程序的开发员可以创建扩展。在这里的范例中,我们使用以下的扩展点:

  • com.ibm.rsaz.analysis.core.analysisCategory
  • com.ibm.rsaz.analysis.core.analysisRule

所谓的 类别 代表了静态分析框架之中的一组规则。默认条件下,它会允许每一个规则去单独地执行以识别问题的存在。但是,在执行每一次规则之前或者之后都需要一种特定的逻辑方式。所谓的分析 规则 就是在整个分析期间执行工作的最小组成部分。换句话说,规则就是在源代码中所识别的问题。

该指南中没有提到过的一个扩展点就是 com.ibm.rsaz.analysis.core.analysisProvider 扩展点。所谓的分析 提供者 能够帮助对一个特定领域分析的规则或者类别进行分组。在这里的例子中,您会处理一个简单的 Java™ 代码评审问题,它只需要创建一个类别以及一条规则。

开始时,指定一个类别。

  1. 再一次打开 MANIFEST.MF 文件并切换至 Extensions 项。
  2. 在 Extension Point 文件夹中,输入 com.ibm.rsaz.analysis.core.analysisCategory,并从列表中选中它。

现在您必须为指定的扩展再指定一些属性。

  1. “id”区域对于类别来说必须是一个独一无二的 ID。出于这个目的考虑,我们将其称作 com.ibm.rsar.demoCategory
  2. 向类别添加一个标签:Demo Category
  3. 对于类,指定标准的 Java 代码评审类别:
    com.ibm.rsaz.analysis.codereview.java.CodeReviewCategory

  4. 您必须指定一种属性。将提供者区域设置为 codereview.java.analysisProvider,它就是 Java 代码评审的提供商 ID。

您已经向 Java 代码评审提供者在没有编写任何代码的情况下添加了一个类别!

为了确认它能发挥实际的功能,您可以先启动一个运行时环境。

注意:
记住对于开发来讲这很好,但是换作是在部署插件时,您就必须导出插件并将其置于开发环境了。

按照以下的步骤来启动一个运行时环境:

  1. 打开 Run 菜单。
  2. 选择 Run Configurations
  3. 双击 Eclipse Application 以创建一个新的 Eclipse Run Configuration。
  4. 选择一个合适的工作区,并点击 Run

在载入工作区之后,您就可以按照下述的步骤来验证您的工作了:

  1. 打开 Run 菜单。
  2. 选择 Analysis
  3. 双击 Software Analyzer 以创建一个新的分析配置。
  4. 切换至 Rules 项。
  5. 展开 Java Code Review 树形视图。

您将会看到一个与图 1 相类似的界面。

图 1. Java 代码评审下面显示的 Demo 类别
Analysis Domanins 与 Rules 界面屏幕截图
Analysis Domanins 与 Rules 界面屏幕截图

创建基本规则

现在是时候创建您的规则了。在您指定扩展之前,您需要先创建它。

  1. 首先,右击 Package Explorer 视图中您的项目(com.ibm.rsar.example)来创建一个类。
  2. 然后从菜单中选择 New > Class
  3. 对于该范例,将该类命名为 RuleAvoidUnusedImports。
  4. 然后点击 Finish

这将会创建如代码清单 3 所示的类。

清单 3. 规则的空内核
package com.ibm.rsar.example; public class AvoidUnusedImports { }
  1. 接下来的一步是更改类以扩展 AbstractCodeReviewRule

AbstractCodeReviewRule 是一种 Java 代码评审抽象类,它包含了一系列的效用方法和帮助功能,以编写代码评审规则。您需要扩展 analyze(AnalysisHistory, CodeReviewResource) 方法并在它里面添加一个 System.out.println("Example"); 代码清单。分析方法就是工作完成的地方;它就是调用的程序并在扫描期间执行分析操作。在这里,您可以简单地在操控台上显示一些信息;但是,在接下来的部分中您可以将其扩展。

清单 4. 基本的 AvoidUnusedImports Rule
package com.ibm.rsar.example; import com.ibm.rsaz.analysis.codereview.java.AbstractCodeReviewRule; import com.ibm.rsaz.analysis.codereview.java.CodeReviewResource; import com.ibm.rsaz.analysis.core.history.AnalysisHistory; public class AvoidUnusedImports extends AbstractCodeReviewRule { public void analyze(AnalysisHistory history, CodeReviewResource resource) { System.out.println("Avoid unused imports"); } }

代码清单 4(上面)是 Rational Software Analyzer 框架中规则的骨架结构。分析范围内为每一个指定的 Java 文件都执行了该规则(不管它是整个的工作区域还是一个特定的项目),而且在操控台中为每一个文件都显示了“避免未使用的导入”。

现在是时间去确保载入规则并在用户界面中显示了。

  1. 再一次打开 MANIFEST.MF 文件并打开 Extensions 项。
  2. 点击 Add 并选择 com.ibm.rsaz.analysis.core.analysisRule 扩展来添加一个新的扩展。

正如您对类别所做的一样,现在您必须指定不同的区域以指向您新创建的规则。

  1. 首先,选择类。选择 Browse 并选择您刚刚创建的(com.ibm.rsar.example.AvoidUnusedImports)。
  2. 指定规则的 ID。一般来说,类名是充分的。对于这里的范例,您可以选择 com.ibm.rsar.example.AvoidUnusedImports
  3. 通过在 Label 区域中输入“Avoid Unused Imports”来指定一个标签。
  4. 指定类别。类别区域必须与类别的 ID 相对应。开始时,您要使用 com.ibm.rsar.demoCategory,所以在相应的区域内输入它。
  5. 将 severity 区域设置成 0,它就是推荐的值。

就是这样的!现在您的规则就会出现在 Analysis Configurations 对话框中了。启动一个新的运行时环境以确认每一项都能正常工作。图 2 显示了预计的情况。

图 2. Avoid Unused Imports 规则
相同的树形结构与选中 Properties 项的规则
相同的树形结构与选中 Properties 项的规则

现在,试用一下您所创建的。

  1. 使用复选框来确保规则被选中了,然后点击 Analyze

这将会对配置所选择的 scope 所有 Java 文件执行规则。如果在范围中有不止一个 Java 文件,那么操控台将会显示您为规则所指定的信息:
“避免未使用的导入”

普通的规则模式

到目前为止,您已经创建了一条规则,它对操控台只打印了一个硬代码的值。现在您需要开始启动编写逻辑,它会识别源代码之内的问题。开始时,我们将会为可以重复使用的规则引入一些非常简单的模式,然后我们将会谈到未使用导入的范例。尽管这些模式相对来说十分简单,但是它们可以帮助我们以一种十分有效且逻辑性很强的方式,来识别源代码中的大多数问题。

Fetch 与 Flag 模式

第一个模式是 Fetch 与 Flag 模式。该模式用于识别一个文件内的元素,这些元素并不是位于当前的位置中。其中的一个范例是找到 finally 块内的 return 声明。为了找到这些问题,您可能想要一个只包含 fetching 的规则。

读取被定义为查找一个资源,并识别其中您所感兴趣的元素。所有可以通过读取操作获得的元素都可以立即标记为问题。

Fetch 与 Filter 模式

第二个模式是 Fetch 与 Filter 模式,稍后您将会使用到它。该模式集中了 Fetch 与 Flag 模式,因为它还包含了一个 读取 步骤。但是,该模式并不是只读取然后标记结果,而是应用一个 filter 步骤,它搜索找到的元素,并识别其中出现问题的部分。在您沿着抽象语法树深入时,该模式通常会通过一条规则重复应用几次。

该模式的一种范例是识别所有声明为 public 的变量,并将它们标记为安全性问题。该规则将会搜索所有的变量,并识别哪些是公共的然后将其他的过滤掉。

使用 Eclipse w 来向规则添加功能性

现在 Eclipse 用户将会注意到 Eclipse 已经为未使用的导入提供了内构的警告。通过权衡使用已存在的工具,您可以以一种更加有效的方式,自动地检测任意未使用的导入并标记出问题。

对插件添加两种新的附件以访问这种功能性:

  • org.eclipse.core.resources
  • org.eclipse.jdt.core

在它们被添加之后,MANIFEST.MF 文件如代码清单 5 所示。

清单 5. 更新过的 MANIFEST.MF 文件
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name:
Example Plug-in Bundle-SymbolicName: com.ibm.rsar.example; singleton:=true
Bundle-Version: 1.0.0 Bundle-Activator: com.ibm.rsar.example.Activator Bundle-Vendor:
IBM Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime,
com.ibm.rsaz.analysis.core;bundle-version="7.1.0",
com.ibm.rsaz.analysis.codereview.java;bundle-version="7.0.102",
org.eclipse.jdt.core;bundle-version="3.4.4",
org.eclipse.core.resources;bundle-version="3.4.2"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy

现在您需要更改您的分析方法以使用 Eclipse 警告。您可以按照以下方法来重新迭代:每一个分析规则都有一种可以执行分析期间所有工作的分析方法。在开始时,您只是简单地将信息显示到操控台上。但是,现在是时间去做一些您更感兴趣的事情了。将分析方法的内容替换为如代码清单 6 所示的代码片段。

清单 6. 新的分析方法
public void analyze(AnalysisHistory history, CodeReviewResource resource) {
  
  IResource iResource = resource.getIResource();
  String historyId = history.getHistoryId();
  try {
  IMarker[] markers =
  iResource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER,
    false,
    IResource.DEPTH_INFINITE);
    for (int i = 0; i < markers.length; i++) {
    IMarker marker = markers[i];
    int id = marker.getAttribute(IJavaModelMarker.ID, -1);
    if (id == IProblem.UnusedImport) {
    int start = marker.getAttribute(IMarker.CHAR_START, 0);
    int length = marker.getAttribute(IMarker.CHAR_END, 0) - start;
    int lineNumber = marker.getAttribute(IMarker.LINE_NUMBER, 0);
    ResourceAnalysisResult result =
    new CodeReviewResult(iResource.getFullPath().toString(),
    lineNumber,start,length,
    iResource,this,historyId, true);
    result.setOwner(this);
    addHistoryResultSet(historyId, result);
    }
    }
    } catch (CoreException e) {
    Log.severe(e.getLocalizedMessage());
    }
    }

代码就是您识别 Eclipse 中 Java 源文件内未使用导入的所有逻辑规则。如果您只是简单地将该代码集中到规则中,那么它就会发挥作用。但是,我们可以将其分解为一段段的以解释它的效用。

首先,该规则会遵循 Fetch 与 Filter 模式。我们会读取所有的 Eclipse 问题标记,然后重复读取的元素以找到出现问题的部分。下面就是这种情况:

  1. 得到 IResource。 IResource 是工作区内某个系统资源(例如一个文件)Eclipse 的代表。IResource 包含了您所使用的变量信息。
    IResource iResource = resource.getIResource();
  2. 这就是所谓的 读取 步骤。代码清单将会让 IResource 去搜索一个文件内所有的 Java 问题标记。问题标记就是 Eclipse 向用户所揭示的 Java 汇编的警告与错误了。
    IMarker[] markers =
    iResource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER,
    false,
    IResource.DEPTH_INFINITE);
  3. 这就是 过滤 步骤的开始部分。您必须在您所找到的所有标记上进行重复:
    for (int i = 0; i < markers.length; i++){
  4. 现在您需要识别它是什么类型的标记。Eclipse 使用数值型的标示符来识别标记的类型:
    int id = marker.getAttribute(IJavaModelMarker.ID, -1);
  5. 这就是 过滤 步骤逻辑发生作用的地方。识别 UnusedImports 标记的地方:
    if (id == IProblem.UnusedImport) {
  6. 该行代码将会产生一种 Rational Software Analyzer 结果:
    ResourceAnalysisResult result =
    new CodeReviewResult(iResource.getFullPath().toString(), lineNumber, start,
    length, iResource, this, historyId, true);
  7. 现在我们将会把结果的拥有者设置为 特定的规则,因为它生成了结果:
    result.setOwner(this);
  8. 最后,该步会向当前扫描的历史添加结果。一个分析结果代表了对一些资源扫描后的静态分析。
    addHistoryResultSet(historyId,result);

这就是了。我们确实跳过了一些内容没讲以简化讨论。跳过的内容只对识别出现问题的位置有价值。所有这些都是在 20 行代码中完成的。

现在是时候去使用规则并找到所有未使用过的导入了。按照与上面相同的方法来操作,先启动一个运行时环境并对 Java 文件运行规则。如果只有含未使用导入的文件,那么您就可以看到与图 3 相类似的情景了。

图 3. 操作中的 Avoid Unused Imports 规则
Avoid Unused Imports 规则代码产生了 UI
Avoid Unused Imports 规则代码产生了 UI

当您不能使用 Eclipse 标记时可以使用抽象语法树

不幸的是,Eclipse 标记并不是一直出现在开发环境中。例如,如果您使用的是 Rational Software Analyzer Enterprise Edition 命令行特性,那么特性就不会显示。如果您决定不去管未使用导入的警告,那么标记也不会显示。于是在规则的逻辑里面就会产生一种明显的缺陷,因为它不但对使用 Eclipse 作为开发环境产生依赖,也会对激活的警告产生依赖。

为了避免这种缺陷,您可以使用 Java 的抽象语法树(AST)来识别这种问题,从而重新构筑规则。AST 是源代码的树形表示,它包含了从变量声明到功能定义和方法调用的全部内容。您不需要手动地去倒转树形结构,而是使用包含的 Rational Software Analyzer 效用方法来倒转树形结构,并 读取 合适的节点。然后您会使用 过滤 功能来降低对未使用导入的搜索范围。

识别使用的类

编写规则的第一步是识别在资源中所使用的类。您不是倒转树形结构,而是使用 Java 开发工具(JDT)的 搜索 功能以识别使用的类。

为了实现这一点,首先您必须创建一个 Search Requestor

  1. 创建一个名为 UsageSearchRequestor 的类,它 扩展了SearchRequestor
  2. UsageSearchRequestor 添加一个 Set<IType> 类型数据成员:
    private Set<IType>
    usedTypes = new HashSet<IType>(2);
  3. 向您的数据成员添加相关的获得方法:
    public Set<IType> getUsedTypes(){
    return usedTypes;
    }
  4. 执行 acceptSearchMatch 方法(在 SearchRequestor 中定义):
    public void acceptSearchMatch(SearchMatch match) throws CoreException {
    Object matchedElement = match.getElement();
    if(!match.isInsideDocComment()) {
    IType type = (IType) matchedElement;
    usedTypes.add(type.getFullyQualifiedName());
    }
    }

UsageSearchRequestor 将会识别一个源文件中所用到的所有类型。每次当它发现有匹配您所搜索的项目时,都会提醒您结果。

编辑规则

现在您必须编辑规则以使用搜索引擎以及请求器,不用去理会 Eclipse 的警告。

  1. 首先,并对保持 SearchEngine 的规则添加一个新的数据成员:
    static private final SearchEngine engine = new SearchEngine();
  2. 编辑规则中的 analyze 方法以使用搜索引擎:
    public void analyze(AnalysisHistory history,CodeReviewResource resource) {
        try { List<ASTNode>
        imports = resource.getTypedNodeList(resource .getResourceCompUnit(),
        ASTNode.IMPORT_DECLARATION, true);
        UsageSearchRequestor requestor = new UsageSearchRequestor();
        engine.searchDeclarationsOfReferencedTypes (resource
        .getICompilationUnit(), requestor, null);
        Set<String>usedTypes = requestor. getUsedTypes();
        for (ASTNode node : imports) {
        ImportDeclaration importDecl = (ImportDeclaration) node;
        String name = importDecl.getName(). getFullyQualifiedName();
        if (!usedTypes.contains(name)){
        resource.generateResultsForASTNode(this, history .getHistoryId(), node);
        }
        }
        } catch (JavaModelException e) {
        Log.severe(e.getLocalizedMessage());
        }
        }

规则如同代码清单 7 所示:

清单 7. 最终的规则。
package com.ibm.rsar.example;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.search.SearchEngine;
import com.ibm.rsaz.analysis.codereview.java. AbstractCodeReviewRule;
import com.ibm.rsaz.analysis.codereview.java.CodeReviewResource;
import com.ibm.rsaz.analysis.core.history.AnalysisHistory;
import com.ibm.rsaz.analysis.core.logging.Log;
public class AvoidUnusedImports extends AbstractCodeReviewRule {
static private final SearchEngine engine = new SearchEngine();
public void analyze(AnalysisHistory history, CodeReviewResource resource) {
try {
List<ASTNode>
imports = resource.getTypedNodeList(resource. getResourceCompUnit(),
ASTNode.IMPORT_DECLARATION,
true);
UsageSearchRequestor requestor = new
UsageSearchRequestor();
engine.searchDeclarationsOfReferencedTypes(
resource.getICompilationUnit(), requestor, null);
Set<String> usedTypes = requestor.getUsedTypes();
for (ASTNode node : imports) {
ImportDeclaration importDecl = (ImportDeclaration) node;
String name = importDecl.getName(). getFullyQualifiedName();
if (!usedTypes.contains(name)) {
resource.generateResultsForASTNode(this, history .getHistoryId(), node);
}
}
} catch (JavaModelException e) {
Log.severe(e.getLocalizedMessage());
}
}
}

分解为逻辑

在以前,代码的以上部分包含了一个源文件内识别未使用导入所需要的一切逻辑。但是,这个版本功能更加稳定强大,而且并不需要我们去处理警告。

  1. 这一次,您可以 读取 抽象语法树中的所有导入声明了。getTypedNodeList 方法是在 CodeReviewResource 类中定义的方法,帮助您搜索 AST。
    List<ASTNode>
    imports = resource.getTypedNodeList(resource. getResourceCompUnit(),
    ASTNode.IMPORT_DECLARATION,true);
  2. 以后,您就可以调用搜索引擎,并将其传递给您的搜索请求器了:
    UsageSearchRequestor requestor = new UsageSearchRequestor();
    engine.searchDeclarationsOfReferencedTypes(
    resource.getICompilationUnit(),requestor, null);
  3. 当搜索完成以后,您就可以从请求器中找到使用的类型了:
    Set<String>
    usedTypes = requestor.getUsedTypes();
  4. 以后,您就会完成早期所选择的所有导入了:
    for (ASTNode node : imports) {
    ImportDeclaration importDecl = (
    ImportDeclaration)node;
    String name = importDecl.getName(). getFullyQualifiedName();
    if(!usedTypes.contains(name)){
    resource.generateResultsForASTNode(this,
    history.getHistoryId(),node); }
    }
  5. 对于您不能在 usedTypes 集合中找到的导入,会生成一个结果(它是未使用的导入)。下面是 过滤器 操作的步骤:
    for (ASTNode node : imports) {
    ImportDeclaration importDecl = (ImportDeclaration)node;
    String name = importDecl.getName().getFullyQualifiedName();
    if (!usedTypes.contains(name)){
    resource.generateResultsForASTNode(this,history. getHistoryId(),node);
    }
    }

现在您已经成功地完成了您的规则,所以可以试用一下它了以确定它是否能够正常工作。再次启动一个运行时环境并运行规则。结果应该与以前一样,但是,这一次您可以试着忽略关于未使用导入的 Eclipse 警告。

添加一种快速修复功能

编写规则的最后一步,是识别是否存在一个轻松的修复机制。正如上面所提到的那样,简单地删除掉导入就足够了。因为对于这个问题有修复机制,所以很明显修复是自动化的。

  1. 在编写快速修复功能之前,首先您必须向 MANIFEST.MF 文件中的 org.eclipse.jface.text 添加一个附件。代码清单 8 显示了我们的最终的 MANIFEST.MF 文件。
清单 8. 最终的 MANIFEST.MF 文件
Manifest-Version: 1.0 Bundle-ManifestVersion: 2
Bundle-Name: Example Plug-in
Bundle-SymbolicName: com.ibm.rsar.example;singleton:=true
Bundle-Version: 1.0.0 Bundle-Activator: com.ibm.rsar.example.Activator
Bundle-Vendor: IBM
Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime,
com.ibm.rsaz.analysis.core;bundle-version="7.1.0",
com.ibm.rsaz.analysis.codereview.java;bundle-version="7.0.102",
org.eclipse.jdt.core;bundle-version="3.4.4",
org.eclipse.core.resources;bundle-version="3.4.2",
org.eclipse.jface.text;bundle-version="3.4.2"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy
  1. 接着,您必须创建一个名为 UnusedImportsQuickFix 的新类,它 扩展了 JavaCodeReviewQuickFix ,代码清单 9 显示了对于类您将会使用到的代码。
清单 9. 快速修复的代码
package com.ibm.rsar.example;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.TextEdit;
import com.ibm.rsaz.analysis.codereview.java.quickfix. JavaCodeReviewQuickFix;
public class UnusedImportsQuickFix extends JavaCodeReviewQuickFix {
public TextEdit fixCodeReviewResult(ASTNode theNode,
IDocument docToChange) {
AST ast = theNode.getAST();
ASTRewrite rewriter = ASTRewrite.create(ast);
rewriter.remove(theNode, null);
TextEdit edits = new MultiTextEdit();
edits.addChild(rewriter.rewriteAST(docToChange, null)); return edits;
}
}

快速修复只与违反 ASTNode(来自抽象语法树的节点)以及 IDocument ,它是含有节点的资源。

  1. 第一步是从节点(特别是一种 ASTNode 库)中找到。所有的抽象语法树功能一般都会通过 AST 对象:
    AST ast = theNode.getAST();
  2. 接下来,剩下来您所要做的,是是删除掉节点并创建 edits(文件中的变更)。在这种情况下,编辑只是会简单地删除掉导入节点:
    ASTRewrite rewriter = ASTRewrite.create(ast);
    rewriter.remove(theNode, null);
    TextEdit edits = new MultiTextEdit();
    edits.addChild(rewriter.rewriteAST(docToChange, null));
    return edits;

现在您已经完成了代码,您必须将快速修复集成到 Rational Software Analyzer 快速修复中。所有这些都会权衡 analysisRuleQuickFix 扩展点。

  1. 再一次打开 MANIFEST.MF 文件,并切换至 Extensions 项。
  2. Click Add.
  3. 将类更改为您刚刚所创建的快速修复类。(该演示会使用 com.ibm.rsar.example.UnusedImportsQuickFix)。
  4. 将快速修复的 ID 修改为 com.ibm.rsar.example.UnusedImportsQuickFix。
  5. 展开 前面您所添加的分析规则扩展。
  6. 右击 Unused Imports 前面您所选择的规则。
  7. 选择 New > quickFix
  8. 设置您在第三步中使用到的快速修复的 ID(在本例中是 com.ibm.rsar.example.UnusedImportsQuickFix)。

剩下您所要做的是看到工作执行的结果。

  1. 启动运行时环境,并再一次开始分析。如果出现了一个结果,那么您可以看到如图 4 所示的现象。
图 4. 快速修复后的结果
带有快速修复功能的结果
带有快速修复功能的结果

注意在规则图标的旁边有一个轻微发亮的图像。这意味着规则可以使用一种快速修复功能。

  1. 右击结果并运行快速修复。

总结

到目前为止,这已经完成了书写规则以及向 Rational Software Analyzer 添加快速修复功能的操作。在本文中,我们论述了两种编写相同规则的不同方式,并讨论了选择每一种方式的理由。

另外,我们谈到了为规则创建简单修复功能的内容,它可以帮助您自动检查您的源代码。对于向通用规则添加快速修复功能来说,这一点非常有用,因为它消除了添加修复功能的复杂性,同时使修复问题的方式得到标准化。

有了您在本文中学到的内容,您就拥有了书写任意特定域规则及其相关修复功能的工具了。但是,您现在该跟的上规则的思想,并决定怎样在源代码中识别它们了。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Rational, Java technology
ArticleID=630990
ArticleTitle=向 Rational Software Analyzer 添加您自己的规则
publish-date=05122010