Static analysis IBM Rational Software Analyzer: Part 2. Creating rules and rule filters to extend Java code review

This is the second article in a four-part series about IBM Rational Software Analyzer and its related capabilities in IBM Rational Application Developer and IBM Rational Software Architect. It focuses on using the Java code review capabilities for analyzing your code and walks you through the process of creating rules and filters.

IBM® Rational® Software Analyzer encourages and simplifies the process of automated code quality improvements. It was initially designed as an API and user interface to create and integrate static analysis tools into other Rational products, such as Rational Application Developer and Rational Software Architect. It has evolved into a complete, standalone offering for automated code review and structural analysis in the daily software development process.

Steve Gutz (sgutz@ca.ibm.com), Software Manager, IBM

Steve GutzSteve Gutz is a development manager responsible for IBM Rational’s code analysis tools. In addition architecting and managing IBM’s commercial products Steve is also a past contributor on the Eclipse Test and Performance Tools project (TPTP), focusing on improvements in implementation and integration of basic code review tools. Previous to joining the IBM Ottawa Lab in 2002, he held senior management and executive positions in several public and private companies including two of his own successful start-ups. He is also the author of two books and many articles, and is a regular conference speaker.



29 April 2008

Also available in Chinese

In the first article of this four-part series, we examined static analysis from a high level and introduced the common user interface for analysis available in IBM® Rational® Software Analyzer (also in IBM Rational Software Architect and IBM Rational Application Developer) and the process of creating an analysis configuration by selecting providers, categories and rules. We also discussed the results view that shows the outcome of the static analysis, as well as some of the basic reporting capabilities built into the software.

In this part and Part 3, you will learn how to write rules to extend the current Java code review rule set. The Rational Software Analyzer analysis framework (referred to in code as rsar) includes several extension points and a complete API for adding new forms of analysis and new rules. But more importantly for this purpose, it also supplies a fully functional analysis provider to perform automated code reviews for Java™ source code. Together, this and the next article describe enough of the framework to understand and write any rule that you need.

This article starts with a description of the components that make up a rule. Then we cover the category and rule extension points plus a very quick overview of the Java IDE. Finally, we walk through the complete process that you follow to create your own rules by implementing simple rules from the group.

Java tools overview

As you may already know, the Eclipse platform supplies a complete parser for Java source code that is used internally for common functions, such as displaying the Java source outline view, finding dependencies, and syntax highlighting. Fortunately, Eclipse has also exposed this IDE for platform developers to use. JDT parses a Java source file and produces an in-memory tree structure representation that can be queried by other areas of the API (application programming interface). The topmost portion of the Abstract Syntax Tree (AST) tree for a Java file is structured roughly this way:

CompilationUnit:
    [ PackageDeclaration ]
        { ImportDeclaration }
        { TypeDeclaration | EnumDeclaration | AnnotationTypeDeclaration | ; }

The CompilationUnit is the root type, which, conceptually, is the Java file itself and is a container for TypeDeclarations (which are either classes or interfaces). There are also MethodInvocations, MethodDeclarations, and many more node types that you will use regularly to write rules. A complete description of all nodes is available in the Eclipse Help system. There are more than 100 Application Server Toolkit (AST) node types in the documentation, but don't be intimidated, because the Javadoc information is well-written and complete.

To perform queries on the AST parse tree, the Java IDE uses a visitor pattern. This offers great performance, but it can be somewhat intimidating to write a visitor class to get valid, useful results. The analysis API eliminates most of the challenges associated with visiting AST nodes by providing a common visitor class and abstracting the query mechanism. This API will be described throughout the remainder of this article. For now, all you need is a basic understanding of the AST tree structure, and the Eclipse Java IDE Help can provide that. Take some time to peruse this documentation, because you will eventually need to write AST code for some of your more advanced rules.

Java code review extension points

We won't go into a deep description here of all extension points available in the Analyzer analysis framework, because there are many. Instead we will focus on just few that you need to create new categories and rules. We will cover others, such as the provider and result extension points, in the next article of this series.

The Code Review for Java provider is available in Rational Software Analyzer, Rational Software Architect, and Rational Application Developer, and any rule that you write will be deployable to any of those tools. If you open the analysis configuration dialog and expand this provider, you will see that it has several categories. Each of these is contributed with an extension in a plugin.xml file, such as the one that you will find in the com.ibm.rsaz.analysis.codereview.java.rules plug-in in Analyzer. For example, consider this arbitrary category from the plugin.xml source file:

<analysisCategory            
	class="com.ibm.rsaz.analysis.core.category.DefaultAnalysisCategory"
id="codereview.java.category.awt"
label="%label.category.j2sebestpractices.awt" 
category="codereview.java.j2sebestpractices"
	help="com.ibm.rsaz.analysis.codereview.java.rules.awt"/>

The class attribute defines a class that manages the category. Even though a custom category class can be written, this code uses the default category supplied by the API, which will be effective in most cases. Custom categories must handle common characteristics of a category, such as managing a list of contained children, label, icon name, and so forth. This is beyond the scope of this article so, for now, just accept that the default category can provide all of the services required for any category. In almost all situations, you will use the supplied class for a category.

A category also has a unique ID, which is used by other categories and rules to determine containment. Naturally, a category must also have a label and a description, which are shown to the user when this category is displayed.

In this example, the extension also has a category attribute that indicates that this category is nested within another category with a unique ID of codereview.java.j2sebestpractives. If you want to create a top-level category, replace the category attribute with a provider=provider.id, as in this example, where codereview.java.analysisProvider is the unique identifier of the Java code review provider:

<analysisCategory
class="com.ibm.rsaz.analysis.core.category.DefaultAnalysisCategory"
	id="codereview.java.j2sebestpractices"
	label="%label.category.j2sebestpractices"
	provider="codereview.java.analysisProvider"
	help="com.ibm.rsaz.analysis.codereview.java.rules.j2sebestpractices"
</analysisCategory>

Rule extensions work in a similar way but include a few more details. This is a sample extension for a rule:

<analysisRule
category="codereview.java.category.awt"
class="com.ibm.rsaz.analysis.codereview.java.internal.rules.awt.RuleAwtPeer"
id="codereview.java.rules.awt.RuleAwtPeer"
label="%label.relrule.j2sebestpractices.awt.awtpeer"
severity="2"
help="com.ibm.rsaz.analysis.codereview.java.rules.awtpeer">
</analysisRule>

You should recognize the first part of the rule extension, because it is almost identical to the category extension previously shown. In this case, the class attribute contains a fully qualified class path for the rule.

The rule also contains additional information. The Help tag supplies an ID for a help context in a related help.xml file. This allows a rule to provide nicely formatted example and solution information to the user. We will discuss Help and other advanced rule concepts in the next article. (See the link at the top to other articles in this series.)

Anatomy of a code review rule

As we discussed in the previous section, the first half of a rule definition is an Eclipse extension. The second half is a Java class that uses the rule API or parts of JDT, or both, to query the content of a class and produce results for any issues that match the rule criteria. The rule class is quite simple, typically containing only a single method. Given that rules usually extend a default rule class provided by the Rational Software Analyzer analysis framework, most of the necessary functionality is already implemented. The code in Listing 1 is a complete rule that creates results for any line of code that uses the Java "==" operator to compare two objects:

Listing 1. Rule for comparing two objects
public class RuleComparisonReferenceEquality
	extends AbstractAnalysisRule
{
	private static final String[] OPERATORS = { "==", "!=" };
	private static final int[] OPERANDS = { ASTModifier.TYPE_PRIMITIVE,
ASTModifier.TYPE_NULLTYPE }; 

	// Rule filters
	private static IRuleFilter[] EXPFILTERS = {
		new OperatorRuleFilter( OPERATORS, true ),
		new LeftOperandRuleFilter( OPERANDS, false ),
		new RightOperandRuleFilter( OPERANDS, false )
	};

	/**
	 * Analyze this rule
	 * 
	 * @param history	A reference to the history record for this analysis
	 * 
	 * @throws CoreException
	 */
	public void analyze(final AnalysisHistory history, 
					final CodeReviewResource resource )
	{
List list = resource.getTypedNodeList( resource.getResourceCompUnit(),
ASTNode.INFIX_EXPRESSION );
      ASTHelper.satisfy( list, EXPFILTERS );

   for(Iterator it=list.iterator(); it.hasNext(); ) {
      InfixExpression tempInf = (InfixExpression)it.next();
      ITypeBinding leftBinding = tempInf.getLeftOperand().resolveTypeBinding();
      ITypeBinding rightBinding = tempInf.getRightOperand().resolveTypeBinding();
      if( (leftBinding != null && leftBinding.isPrimitive()) || 
      (rightBinding != null && rightBinding.isPrimitive()) ) {
       it.remove();
      }
      }
		
	resource.generateResultsForASTNodes( this, history.getHistoryId(), list );
	}
}

The rule class extends com.ibm.rsaz.analysis.codereview.java.AbstractCodeReviewRule from the analysis framework, and, because this class is abstract, the derived rule must implement the ##analyze() method. This method takes one parameter (history), which points to the analysis session for which rules that are being executed. Every time that the user performs an analysis, a new history is registered to collect results. In addition to results, the history element also keeps track of which providers, categories, and rules were selected.

The parameters passed into the analyze() method contain information about the scan history where any results will be stored and a reference to the resource being analyzed. The AbstractCodeReviewRule class, which is part of the Java code review provider, keeps track of the compilation unit for the class file being analyzed and manages queries into the AST tree.

Because the rule in this example is concerned with infix expressions (expressions with a left and right side, such as a comparison), it uses the code review resource to get a list of such expressions in the compilation unit:

List list = resource.getTypedNodeList( resource.getResourceCompUnit(), 
					ASTNode.INFIX_EXPRESSION );

Next, the rule uses the ASTHelper.satisfy() method to filter expressions from the list that do not meet a given criteria. Criteria are defined in the static block at the top of the class:

	// Rule filters
	private static IRuleFilter[] EXPFILTERS = {
		new OperatorRuleFilter( OPERATORS, true ),
		new LeftOperandRuleFilter( OPERANDS, false ),
		new RightOperandRuleFilter( OPERANDS, false )
	};

The EXPFILTERS array contains three types of rule filters. The first removes any expressions where the operator is neither "==" nor "!=". The second filter removes any list items where the left side of the expression is a primitive type or a null. The final filter repeats this for the right side of the expression. The satisfy() method accepts the input list and the filter list and performs all filtering in one step. After this line of code is executed the list will contain only expressions where the left and right side are objects and the operator is either "==" or "!=". This is the only concern of this rule; therefore, every item remaining in the list should be reported in the result list. This is accomplished with the final line in the rule:

resource.generateResultsForASTNodes( this, history.getHistoryId(), list );

That was easy, wasn't it? The API for writing a Java rule contains just a few methods, but it is quite powerful.

Writing a rule from scratch

Now that you have been lulled into the simplicity of code review rules, let's make it a bit more challenging. The rule that we previously examined is about as simple as a rule can get. It queries for only a single type of node in the AST tree and blindly reports all results remaining after filter. Let's consider all of the steps that are necessary to write a complete rule plug-in with a new category and rule.

Create a plug-in

To create a rule, you first need to create a plug-in:

  1. Use the Eclipse New Project wizard to create a new plug-in project in your workspace named #com.ibm.rsar.codereview.java.examples.
Figure 1. New Plug-in Project wizard
image of dialog box
  1. On the second screen of the wizard (Figure 2), uncheck the check box that says "This plug-in makes contributions to the UI."
  2. Click the Finish button to create the plug-in.
Figure 2. Plug-in Content view
image of dialog box

To successfully build rules, you need to add dependencies to the plug-in:

  1. Open the plug-in manifest files, and select the Dependencies tab.
  2. Add plug-ins by using the Add button to select org.eclipse.core.runtime plus these three plug-ins, as shown in Figure 3:
    • com.ibm.rsaz.analysis.core, which contains the basic static analysis framework API and extension points
    • com.ibm.rsaz.analysis.codereview.java, which contains the API for writing Java code review rules
    • org.eclipse.jdt.core, which contains the JDT API including the AST parser and query framework
Figure 3. Add plug-ins
image of workspace

Typically, you need all three of these plug-in dependencies when creating new rules.

Create a rule class

This first rule is practical. In this section, you will create a rule that detects finalize() methods in Java code that do nothing but call super.finalize(). If a finalize() method calls only its parent, and then the method can simply be removed. For example:

public class Example 
{
    private void method() {
        // Do something
    }

    // Don't do this
    protected  void finalize()  throws Throwable {
    	super.finalize();
    }
}

So what do you need to do to detect this kind of problem? Let's examine these two steps:

  • Step 1. You first need to build a list of method declarations with the source code, specifically those named finalize. Moreover, those method declarations should have no parameters.
  • Step 2. After you have a list of method declarations that meet your criteria, you need to look at the lines of code within them to see if they have just one statement that is a super.finalize() super method invocation.

Create a rule for method declarations

You can build a rule by following these steps:

  1. First, create a new class in the com.ibm.rsar.codereview.java.examples package of the examples plug-in. Call this class RuleFinalizerSuper.
  2. Make sure that this rule class extends com.ibm.rsaz.analysis.codereview.java.AbstractCodeReviewRule.
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 RuleFinalizerSuper extends AbstractCodeReviewRule
{	
}
  1. You know that you will need an #analyze method, so add this code to the class:
	/**
	 * Analyze this rule
	 * 
	 * @param history	A reference to the history record
	 */
	public void analyze(AnalysisHistory history,  CodeReviewResource resource ) {
	}

According to Step 1 of this algorithm, you need to collect a list of method declarations from the class that is being analyzed.

  1. Add the following to the analyze() method to accomplish this:
List list = resource.getTypedNodeList( resource.getResourceCompUnit(), 
ASTNode.METHOD_DECLARATION );

This line visits the AST tree, starting at the compilation unit (the top-most node), and queries for a list of all method declarations. Notice that the getTypedNodeList() method has several signatures. This one makes recursive calls by default; therefore, it will also find method declarations in any inner classes.

To complete the requirements for Step 1 of the algorithm, you need to filter methods that you do not need out of the method declaration list. As in the first rule that we examined, you can create a list of rule filters to do this.

  1. Add the following lines to the top of the rule class:
	// Rule filters
	private static final IRuleFilter[] MIFILTERS =  {
	};
  1. First, you need to filter methods that are not declarations of finalize(). In the empty static block add:
new MethodNameRuleFilter( "finalize", true ),

The #MethodNameRuleFilter class tells the filtering mechanism that you are interested in filtering by method names, specifically #finalize. The #true Boolean indicates that finalize() methods should be retained in the list. If this field were #false, then filtering would remove finalize() methods and keep everything else.

You also need to ensure that you manage only true finalize() methods. Given that it is possible for a developer to create a finalize() method that returns a value, you need your rule to ignore those.

  1. You can use the ReturnTypeRuleFilter class to add filtering to eliminate finalize() methods that do not have a void return type:
new ReturnTypeRuleFilter( "void", true ),

To further help reduce the filtered list size, you need only finalize() methods that have no parameters. Methods with parameters are not class finalizers (in fact, a rule should be written to report these as invalid).

  1. Add the following line to the static block:
new ParameterCountRuleFilter( 0, true )

In this case, you tell the filter code to pay attention to the parameter count, and it should be 0.

The final filter array looks like Listing 2.

Listing 2. Final filter array
	// Rule filters
	private static final IRuleFilter[] MIFILTERS =  {
		new MethodNameRuleFilter( "finalize", true ), 
		new ReturnTypeRuleFilter( "void", true ),
		new ParameterCountRuleFilter( 0, true )
	};

The final step needed to complete Step 1 of the planned algorithm is to perform the actual filtering.

  1. At the end of the analyze() method add this line:
ASTHelper.satisfy( list, MIFILTERS );

Make sure that there is only one line that is a super method invocation

The easy part of the rule is finished; now it gets a bit more complicated.

You need to examine the lines of code within the method and check to make sure that there is only one line. Remember that you have a list of method declarations. You need check each of them. There should be only one item in the list, because Java does not support more than one method declaration with the same signature.

  1. To be safe, create a list iterator by adding these lines to the end of the analyze() method:
	for( Iterator it = list.iterator(); it.hasNext(); ) {
		MethodDeclaration md = (MethodDeclaration)it.next();
	}

Every method declaration contains a body (the statements inside the braces). When you have a method declaration, you can use the getBody() method to get a Block (this is an ASTNode). The body contains a list of statements. Here, you care only about method declarations with a single statement.

  1. Add this code to the end of the iterator loop:
	Block block = md.getBody();
	if( md.getBody().statements().size() == 1 ) {
	}

You're getting close. All that remains is to check the statement to see if it is a call to super.finalize(). This isn't as obvious is it might seem.

  1. Within the if statement, add this code:
	List superList = 
        resource.getTypedNodeList( block, ASTNode.SUPER_METHOD_INVOCATION );
	if( superList.size() > 0 ) {
	}

This needs some explanation. The body of a method declaration is made up of statements, which can be any kind of action (for example: for statement, if statement, or expression). You need to look in the body to see if there are any super method invocations, which are a specific type of statement. You do this by obtaining a list of ASTNodes within the block that have a SUPER_METHOD_INVOCATION type. You already know that the body has just one statement, so this list of super invocations will likely have 0 or 1 element. If the list has 1 (one) or more elements, then you can assume that the body makes a call only to super.finalize().

The last step is to generate a result in the analysis results view. The code review engine provides a simple API for this.

  1. Within the empty if statement, add this line:
resource.generateResultsForASTNode( this, history.getHistoryId(), md.getName() );

The first two parameters of this method call are always the same. They are used to identify the rule that generates the result and the history collection where the result will be placed. The third parameter indicates the ASTNode that will be highlighted. In this case, for every matching method declaration (the finalize() method), a result is created where the name is highlighted.

The completed rule looks like Listing 3.

Listing 3. Completed rule
package com.ibm.rsar.codereview.java.examples;

import java.util.Iterator;
import java.util.List;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.MethodDeclaration;

import com.ibm.rsaz.analysis.codereview.java.AbstractCodeReviewRule;
import com.ibm.rsaz.analysis.codereview.java.CodeReviewResource;
import com.ibm.rsaz.analysis.codereview.java.IRuleFilter;
import com.ibm.rsaz.analysis.codereview.java.ast.ASTHelper;
import com.ibm.rsaz.analysis.codereview.java.rulefilter.MethodNameRuleFilter;
import com.ibm.rsaz.analysis.codereview.java.rulefilter.ParameterCountRuleFilter;
import com.ibm.rsaz.analysis.codereview.java.rulefilter.ReturnTypeRuleFilter;
import com.ibm.rsaz.analysis.core.history.AnalysisHistory;

public class RuleFinalizerSuper extends AbstractCodeReviewRule
{
	// Rule filters
	private static final IRuleFilter[] MIFILTERS =  {
		new MethodNameRuleFilter( "finalize", true ), 
		new ReturnTypeRuleFilter( "void", true ),
		new ParameterCountRuleFilter( 0, true )
	};

	/**
	 * Analyze this rule
	 * 
	 * @param history	A reference to the history record for this analysis
	 */
	public void analyze
        ( final AnalysisHistory history, final CodeReviewResource resource ) {
	  List list = 
          resource.getTypedNodeList( resource.getResourceCompUnit(), 
	   ASTNode.METHOD_DECLARATION );
	   ASTHelper.satisfy( list, MIFILTERS );
	
	for( Iterator it = list.iterator(); it.hasNext(); ) {
	    MethodDeclaration md = (MethodDeclaration)it.next();
	    Block block = md.getBody();

	if( md.getBody().statements().size() == 1 ) {
        List superList = 
        resource.getTypedNodeList( block, ASTNode.SUPER_METHOD_INVOCATION );

     if( superList.size() > 0 ) {
     resource.generateResultsForASTNode( this, history.getHistoryId(), md.getName() );
		}
	}
	}
	}
}

Create extension points

After the rule class is completed, it needs to be described through the provided extension points in the analysis framework so that the code review engine can discover it. For this example, you will first create your own category, then you will associate the rule with that category.

Creating a new category is a relatively simple task:

  1. Open the example plug-in manifest created in the first part of this section.
  2. Select the Extensions tab in the editor, and click the Add button.
  3. From the list, select the com.ibm.rsaz.analysis.core.analysisCategory extension point, as shown in Figure 4.
Figure 4. Extension Point Selection view
image of dialog box
  1. Under the Extension tab, you should now see a new extension point entry. Right-click on it, and add a new analysisCategory. Fill in these extension fields with the details shown (illustrated in Figure 5):
    • id*: analysis.codereview.java.examples
    • label*: Examples
    • category: codereview.java.j2sebestpractices
    • class: com.ibm.rsaz.analysis.core.categoryDefaultAnalysisCategory (use the Browse button to select)
Figure 5. Extension Element Details view
image of dialog box

This category has a parent category (codereview.java.j2sebestpractices), which nests it within the Java™ 2 Platform, Special Edition Best Practices category in the user interface. The class, DefaultAnalysisCategory, can be used to supply a predefined category that provides all of the functionality required for most categories that you will ever create.

Populating these details results in the following text being inserted into the plugin.xml file:

   <extension
         point="com.ibm.rsaz.analysis.core.analysisCategory">
      <analysisCategory
            category="codereview.java.j2sebestpractices"
            class="com.ibm.rsaz.analysis.core.category.DefaultAnalysisCategory"
            id="analysis.codereview.java.examples"
            label="Examples">
      </analysisCategory>
   </extension>

Next, you need to add your rule to the new category.

  1. Add a new extension to the plug-in as we did for the category. This time, select the com.ibm.rsaz.analysis.core.analysisRule extension point.
  2. In the Extensions tab, right-click on the rule extension and add a new analysisRule.
  3. Populate the rule details (shown in Figure 6):
    • id*: codereview.java.example.finalSuper
    • label*: Avoid finalize() methods that call only super
    • category: analysis.codereview.java.examples
    • class: com.ibm.rsar.codereview.java.examples.RuleFinalizerSuper (use Browse button to select)
    • severity: 2 (use the drop-down menu to select)
Figure 6. Properties of analysisRule
image of dialog box
  1. Enter a label and description (this will appear in the user interface when the user sees your rule).

Note:
These strings should be localized for any production rule, but you can ignore this requirement for now.

The class field contains the qualified name of the rule class that you created earlier. The category field contains the identifier string for your Examples category. After the details are created, the plugin.xml file will contain the following text:

   <extension
         point="com.ibm.rsaz.analysis.core.analysisRule">
      <analysisRule
            category="com.ibm.rca.codereview.java.examples.RuleFinalizeSuper"
            id="codereview.java.example.finalSuper"
            label="Avoid finalize() methods that call only super"
            severity="2">
      </analysisRule>
   </extension>

Running the rule

  1. From the Eclipse view, select the Run option, and create a new Eclipse Application.
  2. In the configuration panel, click the Run button. This invokes a new runtime workbench to test your rule.
  3. When the workbench appears, import some Java source code into it so that there is some code to review. Try to find some code with a finalize() method that calls only super.finalize() so that you can test the rule
  4. With source code in the workbench, select the Analysis option from the Eclipse Run menu.
  5. In the dialog that appears, create a new analysis configuration and go to the Domains tab.
  6. Expand the Java Code Review branch of the tree until the Example subcategory is visible (see Figure 7).
Figure 7. Java Code Review expanded to show Example
image of workspace
  1. With the new rule selected, click Analyze to start the code review process.

If the source code in the workbench has code with a finalize() method that fits your rule, the analysis results view will contain a result like the one that Figure 8 shows.

Figure 8. Analysis results view
image of dialog box

Rule filters revisited

In this section, we have examined certain rules and, more specifically, how they use Rule Filters. The API supplies rule filters to support most of the rules that you would ever want to write. This is a list of the rule filters provided by Rational Software Analyzer (the Javadoc for the Analysis API has more information on how to use each of these rule filters:

  • ArgumentTypeRuleFilter
  • ConstructorRuleFilter
  • DeclaringClassRuleFilter
  • EnclosingNodeRuleFilter
  • ExceptionCountRuleFilter
  • ExpressionRuleFilter
  • ForInitializerCountRuleFilter
  • ForUpdateCountRuleFilter
  • FragmentCountRuleFilter
  • IfElseStatementCountRuleFilter
  • IfElseStatementRuleFilter
  • IfThenStatementCountRuleFilter
  • IfThenStatementRuleFilter
  • ImplementedInterfaceRuleFilter
  • LeftOperandRuleFilter
  • MethodNameRuleFilter
  • ModifierRuleFilter
  • OperatorRuleFilter
  • ParameterCountRuleFilter
  • ParameterTypeRuleFilter
  • ReturnTypeRuleFilter
  • RightOperandRuleFilter
  • SuperClassRuleFilter
  • TypeRuleFilter

There are also two rule filters for logical operations (LogicalOrFilter and LogicalAndFilter). You can use these to avoid duplication in rules that must compare a similar array of filters that differ in a particular way. For example:

	private static final IRuleFilter[] MIFILTERS =  {
		new MethodNameRuleFilter( "testMethod", true ), 
		new ReturnTypeRuleFilter( "void", true ),
		new LogicalOrFilter( 
		new ParameterCountRuleFilter( 0, true ),
		new ParameterCountRuleFilter( 1, true )
	};

This code filters methods named testMethod() that return void and contain 0 or 1 as parameters.

It is possible that the rule filter you need is not available, but you can write your own rule filters without much effort. The API provides an interface (IRuleFilter) the you can implement to plug into the filter system. Listing 4 is an example of one of the rule filters supplied with the Java code review provider in Rational Software Analyzer:

Listing 4. Example of a rule filter included in Rational Software Analyzer
public class SuperClassRuleFilter extends AbstractRuleFilter {
	private static final String SATISFIES_SUPER_CLASS = "satisfiesSuperClass";
	private String superclassName;
	
	/**
	 * Constructor
	 * 
	 * @param superclassName
	 *            The super class name by which to filter
	 * @param inclusive
	 *            True if filtering will include only nodes that match the
	 *            filter criteria, false to exclude matching nodes
	 */
	public SuperClassRuleFilter( String superclassName, boolean inclusive ) {
		super( inclusive );
		
		this.superclassName = superclassName;
	}
	
	/**
	 * Determine if the node is satisfied by the specified filter rule
	 * 
	 * @param node	The ASTNode to test
	 * @return	true if the node satisifes the filtering rule
	 */
	public boolean satisfies( ASTNode node ) {
		try {			
			if (node.getNodeType() == ASTNode.TYPE_DECLARATION) {
				// Get the super class type. It is null if this type
				// declaration does not extend any type
				Type superClassType = 
                              ((TypeDeclaration)node).getSuperclassType();
			if( superClassType != null ) {
			      return superClassType.resolveBinding().getQualifiedName()
							.equals( superclassName );
				}
			} else {
				Log.severe (Messages.bind(Messages.RULE_FILTER_ERROR_,
						new Object[]{ SATISFIES_SUPER_CLASS,
						node.getClass().getName()}));
			}
		} catch (NullPointerException e) {
			// Do nothing
		}

		return false;		
	}
}

As you can see, the code is straightforward:

  • You need to create a constructor to accept the values that you need to make the node evaluation. In the example shown here, the constructor accepts a string that represent the superclass name.

    Tip:
    Always accept the Boolean parameter to support inclusionary and exclusionary filtering.
  • You must also implement the satisfy() method, which, in all cases, will accept the AST node under test.
  • Finally, because rule filters must be reliable, you need to ensure that all exceptions are caught within satisfy(). In the example here, the code catches NullPointerException in case binding resolution in the code fails.

When your rule filter is created, you can use it like any of the predefined filters by placing it in an IRuleFilter array and calling ASTHelper.satisfy() within you rules.

Figure 9. Rules tab showing rule sets
image of workspace

Summary and what's next

This tutorial explained the inner workings of the analysis API as it applies to writing new code review rules for Java. It discussed the core concepts of creating categories and rule specification in the plugin.xml file, and then described how to write a class to create a basic rule. The next article covers the concepts of rule severity, custom rule parameters, and creating rule templates.

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. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. 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, DevOps
ArticleID=302441
ArticleTitle=Static analysis IBM Rational Software Analyzer: Part 2. Creating rules and rule filters to extend Java code review
publish-date=04292008