Add your own rules to Rational Software Analyzer

IBM® Rational® Software Analyzer is a static analysis framework that detects potential problems in source code. It can detect a wide range of problems, from issues with coding style to resource leaks and potential lack of null pointer references. Although this software features many different rules, it is impossible to cover everyone's static analysis needs. But by writing custom rules for Rational Software Analyzer, developers can catch almost any problem that the tool does not include. Because it is a framework, it is very simple to modify and extend it for domain-specific requirements. This article walks you through the process of creating a useful rule and demonstrates how to make it better. In addition, you will add a quick fix to the rule so that you can quickly resolve the problem if you find it in your source code.

Share:

Mohammed Mostafa (mmostafa@ca.ibm.com), Senior Software Engineer, IBM

Mohammed MostafaMohammed Mostafa is a senior software developer for IBM Rational software in Ottawa, Ontario, Canada, and the lead architect for Rational Software Analyzer. For the last nine years, he has been working on different modeling and static analysis applications. Mohammed worked on UML compare-merge support for both IBM Rational XDE and EMF models and on the UML Modeler in IBM Rational Software Architect. He also worked on the diagram layer for the GMF Runtime.



Joshua Tessier (tessierj@ca.ibm.com), Software Developer, IBM

Joshua TessierJoshua Tessier is a software developer for the Rational Software Analyzer team. He graduated in 2009 from Carleton University with a B.S. in computer science.



25 March 2010

Also available in Chinese Vietnamese Portuguese

When developing an application, it is very easy to make mistakes. Whether the mistake is the simple addition of an extra character or an accidental inversion of logic in a line of code, mistakes almost always result in bugs that cripple applications of all sizes. These bugs obviously originate from the developers of the application but, regardless of the education or experience level of the developer, it is easy to accidentally introduce bugs into an application.

Unfortunately, bugs still get introduced despite the tremendous efforts that people in the technology industry have put into producing high-quality code. These efforts range from renewed emphasis on the agile process to peer code reviews. Because of this, it has become increasingly obvious that we need the assistance of powerful and flexible automated analysis tools. Static analysis tools are one way of improving overall code quality. Rather than waiting until the application is in the build stage or running, this kind of took can analyze the source code and identify potential problems during development, thereby reducing the total number of defects in an application. These tools can help identify problems ranging from simple mistakes to complex application logic issues.

Although the tools help identify problems and increase code quality, they are merely an aid, not a replacement for manual code reviews and testing. One of the main reasons for this is that generic tools cannot identify domain-specific problems that relate to business logic or to specific guidelines that are relevant only within the organization.

This article will introduce you to the IBM® Rational® Software Analyzer static analysis tools and framework. Through the public interface exposed by this software, you can write custom rules to cover domain-specific problems and to enhance the overall coverage of the tool. The goal of the article is to introduce you to the Rational Software Analyzer extensible API and to guide you in writing your own custom rules so that you can catch problems that cannot be generalized. To accomplish this, we will describe two different ways of performing an analysis. You will also learn how to create a quick fix for a problem.

Required knowledge and tools

To follow this article, you need at least some knowledge of and experience with these technologies and concepts:

  • Eclipse (Version 3.4 or later)
  • Eclipse plug-in development
  • Java™ technology (Version 1.5 or later)

In addition, you need at least some understanding of the following concepts and technologies:

  • Abstract syntax trees
  • Java development tools (JDT) within Eclipse
  • Rational Software Analyzer Version 7.1

Also, you must either have Rational Software Analyzer installed on your system or have access to its plug-ins to be able to complete the steps in this article.

Get started

The first step in writing a custom rule is to correctly identify the problem that you want to detect.

Identify the problem

In this example, you will tackle the problem of unused imports within Java source code. Although unused imports do not affect runtime performance, they can confuse the reader and affect the readability of the code. In addition, having dependencies on classes that are not required should be avoided at all costs. Consider the case that the code in Listing 1 illustrates.

Listing 1. Sample code with an unused import
import org.eclipse.core.resources.IResource; public class 
Internal { //... code that does not use IResource }

The code in Listing 1 imports the org.eclipse.core.resources.IResource class, which creates a dependency on the org.eclipse.core.resources plug-in. Although this might seem harmless, imagine what would happen if Internal were a base class for many of your internal classes. With every use of this class, there would be an implicit dependency on the resources plug-in. This could create a long chain of dependencies on plug-ins that are not required.

Identify the solution

The next step is to identify the solution. However, solutions can vary tremendously. They can be complex and require the implementation of certain methods and alternate algorithms, or they can be as simple as removing the infraction. In this example, the solution is quite simple: simply remove the unused imports from any Java file. Identifying the correct solution plays a key part in truly understanding the problem and verifying that the problem is worthy enough to identify by using static analysis.


Write your own rule

Create the project and add the necessary dependencies

First, you must create a plug-in project.

  1. Open the File menu and select New > New Project.
  2. Select Plug-in Project in the dialog box and give the project a name. For the purposes of this demo, name the project com.ibm.rsar.example.

Next, add the necessary dependencies to the project.

  1. Open the MANIFEST.MF file in the META-INF folder of your project, and select the Dependencies tab.
  2. Click Add, select com.ibm.rsaz.analysis.core, and then save the file.
  3. Follow the same procedure and add com.ibm.rsaz.analysis.codereview.java as another dependency.

You have now created the project and added the necessary dependencies to get started. At this point, your MANIFEST.MF file should look similar to what Listing 2 shows.

Listing 2. The updated 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

Use extension points and create the category

If you are unfamiliar with the Eclipse extension point framework, there are plenty of resources on the Internet that will help you get familiar with it. However, as an analogy: extension points are like electrical sockets and extensions are like plugs. The designer of an application specifies the extension points, and the developer who wants to extend the application can create the extensions. In this example, we use these extension points:

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

An analysis category represents a group of rules within the static analysis framework. By default, it simply allows each rule to execute separately to identify problems. However, there might be cases where special logic is required before and after the execution of each rule, which would prompt the necessity for a custom category. An analysis rule is an atomic piece of work that will be performed throughout an analysis. In other words, the rules are what identify problems in source code.

One extension point that is not mentioned in this tutorial is the com.ibm.rsaz.analysis.core.analysisProvider extension point. An analysis provider helps group rules and categories for a specific domain of analysis. A domain of analysis covers other coding languages or different types of analysis. In this example, you will work on a simple Java™ code review problem that will require creating only a category and a rule.

To begin, specify a category.

  1. Open the MANIFEST.MF file again and switch to the Extensions tab.
  2. Within the Extension Point filter, enter com.ibm.rsaz.analysis.core.analysisCategory, and select it from the list.

You must now specify a few properties for the extension that you are specifying.

  1. The "id" field must be a unique ID for category. For this purpuse, call it com.ibm.rsar.demoCategory.
  2. Add a label to the category: Demo Category.
  3. For the class, specify the standard Java code review category:
    com.ibm.rsaz.analysis.codereview.java.CodeReviewCategory .
  4. You must specify a provider. Set the provider field to codereview.java.analysisProvider, which is the provider ID for Java code review.

You have just added a category to the Java code review provider without writing any code!

To verify that this actually works, start a runtime environment.

Note:
Keep in mind that this is fine for development, but when it comes time to deploying the plug-in, you must export the plug-in and put it in the development environment.

Follow these steps to start a runtime environment:

  1. Open the Run menu.
  2. Select Run Configurations.
  3. Double-click Eclipse Application to create a new Eclipse Run Configuration.
  4. Choose an appropriate workspace, and click Run.

After the workbench has loaded, you can verify your work by following these steps:

  1. Open the Run menu.
  2. Select Analysis.
  3. Create a new analysis configuration by double-clicking Software Analyzer.
  4. Switch to the Rules tab.
  5. Expand the Java Code Review tree item.

You should now see a screen that resembles Figure 1.

Figure 1. The Demo category displayed under Java Code Review
Analysis Domanins and Rules tree screen capture

Create the basic rule

It is now time to create your rule. Before you specify your extension, you need to create it.

  1. First, create a class by right-clicking your project (com.ibm.rsar.example) in the Package Explorer view.
  2. Then choose New > Class from the menu.
  3. For this example, name your class RuleAvoidUnusedImports.
  4. Then click Finish.

That creates the class shown in Listing 3.

Listing 3. The rule's empty shell
package  com.ibm.rsar.example; public class AvoidUnusedImports { }
  1. The next step is to change the class to extend the AbstractCodeReviewRule.

The AbstractCodeReviewRule is a Java Code Review abstract class that contains a variety of utility methods and helper functions for writing code review rules. You will need to extend the analyze(AnalysisHistory, CodeReviewResource) method and add a System.out.println("Example"); line inside of it. The analyze method is where all of the work is done; it is what is invoked and it performs the analysis during a scan. Here, you will simply make it display some information on the console; however, you will expand on this in the next section.

Listing 4. The basic 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");
	}
}

The code in Listing 4 (above) is the bare bones structure of a rule in the Rational Software Analyzer framework. This rule will be executed for each Java file specified in the scope of the analysis (regardless of whether it is the entire workspace or a specific project), and it will display "Avoid unused imports" in the console for each file.

It is now time to ensure that the rule is loaded and displayed in the user interface.

  1. Once again, open the MANIFEST.MF file and then open the Extensions tab.
  2. Add a new extension by clicking Add and selecting the com.ibm.rsaz.analysis.core.analysisRule extension.

As you did for the category, you must now specify different fields to point to your newly created rule.

  1. First, select the class. Click Browse and select the rule that you created earlier (com.ibm.rsar.example.AvoidUnusedImports).
  2. Specify the ID of the rule. Typically, the class name is sufficient. For this example, select com.ibm.rsar.example.AvoidUnusedImports.
  3. Specify a label by typing "Avoid Unused Imports" in the Label field.
  4. Specify the category. The category field must correspond with the category's ID. Earlier, you used com.ibm.rsar.demoCategory, so enter that in the corresponding field.
  5. Set the severity field to 0, which represents a recommendation.

That's it! Your rule should now appear in the Analysis Configurations dialog box. Start a new runtime environment to verify that everything works. Figure 2 shows what to expect.

Figure 2. The Avoid Unused Imports Rule
Same tree with rule checked plus Properties tab

Now, try what you've created.

  1. Use the check box to ensure that the rule is selected and then click Analyze.

This will execute the rule on all Java files within the selected scope of the configuration. If there is at least one Java file in the scope, the console will show the message that you specified for the rule to print:
"Avoid unused imports"


Common rule patterns

So far, you have created a rule that only prints a hard-coded value to the console. Now you need to start writing logic that will identify problems within source code. To begin, we'll introduce a few very simple patterns for writing rules that can be reused, and then we'll cover the example of catching unused imports. Although these patterns are relatively simple, they help us to identify most problems in source code in a very efficient and logical manner.

Fetch and Flag pattern

The first pattern is the Fetch and Flag pattern. This pattern is used to identify elements within a file that simply do not belong where they currently are. An example of this is finding return statements within a finally block. To locate this problem, you would want a rule that includes only fetching.

Fetching is defined as looking at a resource and identifying elements within it that are interesting. Any elements that are obtained with the fetch action are immediately flagged as problems.

Fetch and Filter pattern

The second pattern is the the Fetch and Filter pattern, which you will use later. This pattern resembles the Fetch and Flag pattern, because it also includes a fetch step. However, rather than only fetching and then flagging results, this pattern applies a filter step that scours the elements found and identifies the problematic ones. This pattern is often reapplied several times through a rule as you dig down through an abstract syntax tree.

An example of this pattern would be to identify all variables that are declared public and flag them as potential security problems. This rule would have to look for all variables and identify which ones are public and filter out the others.


Add functionality to the rule by using Eclipse w

The seasoned Eclipse user will have noticed by now that Eclipse already provides built-in warnings for unused imports. By leveraging the existing tooling, you can automatically detect any unused imports and flag the problem in a more prominent way.

There are two new dependencies to add to plug-in to access this functionality:

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

After they have been added, the MANIFEST.MF file should look similar to this Listing 5.

Listing 5. The updated MANIFEST.MF file
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

You now need to change your analyze method to use Eclipse warnings. To reiterate: each analysis rule has an analyze method that performs all of the work during an analysis. Earlier, you simply displayed information to the console. However, now it's time to do something a little more interesting. Replace the contents of the analyze method with the code snippet in Listing 6.

Listing 6. The New Analyze Method
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());
	}
}

That code is all of the logic that you need to identify unused imports within a Java source file in Eclipse. If you simply drop this code into the rule, it will work. However, we will decompose it section-by-section to explain what this does.

First, this rule follows the Fetch and Filter pattern. We first fetch all of the Eclipse problem markers and then iterate over the fetched elements to find the problematic ones. Here's the breakdown:

  1. Get the IResource. The IResource is Eclipse's representation of a system resource (such as a file) within the workspace. The IResource contains valuable information that you will use.
    IResource iResource = resource.getIResource();
  2. This is the fetch step. The line of code asks the IResource to find all of the Java problem markers within a file. The problem markers are the warnings and errors that the Java compiler within Eclipse exposes to the user.
    IMarker[] markers = iResource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
  3. This is the start of the filtering step. You must iterate over all of the markers that you found:
    for (int i = 0; i < markers.length; i++){
  4. You must now identify what kind of marker this is. Eclipse uses a numerical identifier to identify the type of marker:
    int id = marker.getAttribute(IJavaModelMarker.ID, -1);
  5. This is where the logic of your filtering step occurs. Identify the markers that are UnusedImports markers:
    if (id == IProblem.UnusedImport) {
  6. This line will create a Rational Software Analyzer result:
    ResourceAnalysisResult result = new CodeReviewResult(iResource.getFullPath().toString(), lineNumber, start, length, iResource, this, historyId, true);
  7. We now set the owner of the result to this particular rule since it generated the result:
    result.setOwner(this);
  8. Finally, this step adds the result to the current scan's history. An Analysis History represents a static analysis scan over some resources.
    addHistoryResultSet(historyId,result);

That's it. There were a few lines that we skipped to simplify things. The lines that we skipped were there only to get values for the purpose of identifying the location of the problem. All of this was completed in only about 20 lines of code.

It's now time to try the rule and watch it find all unused imports. Follow the same procedure as before by starting a runtime environment and running the rule on Java files. If there are any files with unused imports, you'll see something similar to Figure 3.

Figure 3. The Avoid Unused Imports rule in the action
Avoid Unused Imports rule code, result in the UI

Use the abstract syntax tree when you cannot use Eclipse markers

Unfortunately, Eclipse markers are not always present in the development environment. For example, if you're using the Rational Software Analyzer Enterprise Edition command-line feature, the markers will not be present. If you decide to to disable the Unused Imports warning, those markers will not be present. This points out a significant flaw in the logic of the rule, because it has a dependency not only on using Eclipse as a development environment, but it also relies on having the warnings enabled.

To avoid this flaw, you will reconstruct the rule by using Java's abstract syntax tree (AST) to identify the problems. The AST is a tree representation of the source code that contains everything from variable declarations to function definitions and method invocations. Rather than traverse the tree manually, you will use the included Rational Software Analyzer utility methods to traverse the tree and fetch the appropriate nodes. You will then use the filtering capabilities to narrow your search for unused imports.

Identify classes that are being used

The first step in writing this rule is the identification of classes that are being used within the resource. Rather than traverse the tree, you will use the Search capability of the Java development tools (JDT) to identify the used classes.

To do this, you must first create a Search Requestor.

  1. Create a class called UsageSearchRequestor that extendsSearchRequestor.
  2. Add a Set<IType> type data member to the UsageSearchRequestor:
    private Set<IType> usedTypes = new HashSet<IType>(2);
  3. Add the respective get method for your data member:
    public Set<IType> getUsedTypes(){ return usedTypes; }
  4. Implement the acceptSearchMatch method (defined in SearchRequestor):
    public void acceptSearchMatch(SearchMatch match) throws CoreException { Object matchedElement = match.getElement(); if(!match.isInsideDocComment()) { IType type = (IType) matchedElement; usedTypes.add(type.getFullyQualifiedName()); } }

The UsageSearchRequestor will identify all types that are being used within a source file. It will be notified every time that there is a match for the search that you are executing.

Modify the rule

You must now modify the rule to use the search engine and your requestor, yet not take advantage of Eclipse warnings.

  1. First, add a new data member to the rule that holds the SearchEngine:
    static private final SearchEngine engine = new SearchEngine();
  2. Modify the analyze method within the rule to leverage the search engine:
    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()); } }

The rule should now look like this Listing 7.

Listing 7. The final rule.
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());
    }
  }
}

Decompose the logic

As before, the above piece of code contains all of the logic that is needed to identify unused imports within a source file. However this version is much more robust and does not depend on us enabling the warning.

  1. This time, you will fetch all of the import statements in the abstract syntax tree. The getTypedNodeList method is a utility method defined in the CodeReviewResource class to help search the AST.
    List<ASTNode> imports = resource.getTypedNodeList(resource.getResourceCompUnit(), ASTNode.IMPORT_DECLARATION,true);
  2. Afterward, you can invoke the search engine and pass it your search requestor:
    UsageSearchRequestor requestor = new UsageSearchRequestor(); engine.searchDeclarationsOfReferencedTypes( resource.getICompilationUnit(),requestor, null);
  3. When the search is finished, you can grab the set of used types from the requestor:
    Set<String> usedTypes = requestor.getUsedTypes();
  4. Afterward, you go through all of the imports that you collected earlier:
    for (ASTNode node : imports) { ImportDeclaration importDecl = ( ImportDeclaration)node; String name = importDecl.getName().getFullyQualifiedName(); if(!usedTypes.contains(name)){ resource.generateResultsForASTNode(this, history.getHistoryId(),node); } }
  5. And for each import that you don not find in your usedTypes set, generate a result (this is an unused import). Here is your filter step:
    for (ASTNode node : imports) { ImportDeclaration importDecl = (ImportDeclaration)node; String name = importDecl.getName().getFullyQualifiedName(); if (!usedTypes.contains(name)){ resource.generateResultsForASTNode(this,history.getHistoryId(),node); } }

You have now successfully completed your rule, so try it, to make sure that it works. Start a runtime environment again and run the rule. The result should be the same as before. However, this time, try disabling the Eclipse warnings about unused imports.


Add a quick fix

The final step in writing any rule is to identify whether an easy fix exists for it. As mentioned earlier, simply removing the import suffices. Because there is an easy fix for this problem, it is obvious that the fix should be automated.

  1. Before moving on to writing the quick fix, you must first add a dependency to org.eclipse.jface.text in the MANIFEST.MF file. Listing 8 shows our final MANIFEST.MF file.
Listing 8. The final MANIFEST.MF file
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. To continue, you must create a new class called UnusedImportsQuickFix that extends JavaCodeReviewQuickFix. Listing 9 shows the code that you will use for the class.
Listing 9. Code for the quick fix
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; 
	}
}

Quick fixes are provided only with the violating ASTNode (the node from the abstract syntax tree) and the IDocument, which is the resource that holds the node.

  1. The first step is to grab the AST from the node (essentially an ASTNode factory). All abstract syntax tree functions typically pass through the AST object:
    AST ast = theNode.getAST();
  2. Afterward, all that is left to do is to remove the node and create the edits (changes in the file). In this case, the edit is to simply remove the import node:
    ASTRewrite rewriter = ASTRewrite.create(ast); rewriter.remove(theNode, null); TextEdit edits = new MultiTextEdit(); edits.addChild(rewriter.rewriteAST(docToChange, null)); return edits;

Now that you have covered the code, you must integrate the quick fix into Rational Software Analyzer quick fixes. All of those leverage the analysisRuleQuickFix extension point.

  1. Open the MANIFEST.MF file one last time, and switch to the Extensions tab.
  2. Click Add.
  3. Change the class to the quick fix class that you just created. (This demo will use com.ibm.rsar.example.UnusedImportsQuickFix).
  4. Change the ID of the quick fix to com.ibm.rsar.example.UnusedImportsQuickFix.
  5. Expand the analysis rule extension that you added earlier.
  6. Right-click the Unused Imports rule that you created earlier.
  7. Select New > quickFix.
  8. Set the ID of the quick fix to be the one that you used in Step 3 (com.ibm.rsar.example.UnusedImportsQuickFix, in this example).

The only thing left is to see your work in action.

  1. Start a runtime environment, and run the analysis once more. If there is a result, you will see something similar to Figure 4.
Figure 4. The result with a quick fix
The result with a quick fix

Notice that there is a little lightbulb image next to the icon for the rule. This indicates that the rule has a quick fix available to use.

  1. Right-click the result and run the quick fix.

Summary

This completes the process of writing a rule and a quick fix for Rational Software Analyzer. In this article, we covered two different ways of writing the same rule, and we discussed reasons for choosing either approach.

In addition, we covered the creation of a simple quick fix for your rule, which helps you to automatically clean up your source code. It is very beneficial to include quick fixes with custom rules, because it removes the complexity of having to devise a fix and it also standardizes the way that a problem is fixed.

With what you have learned in this article, you have the tools and the necessary starting points to write any domain-specific rule and its associated quick fix. However, it is up to you to come up with rule ideas and to determine how to identify them in source code.


Download

DescriptionNameSize
Completed Rule and Quick Fix from this articleRSARDemoProject.zip12KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Rational software on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Rational, Java technology
ArticleID=476618
ArticleTitle=Add your own rules to Rational Software Analyzer
publish-date=03252010