 | Level: Intermediate Steve Gutz (sgutz@ca.ibm.com), Software Manager, IBM
29 Apr 2008 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.
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:
- 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
- On the second screen of the wizard (Figure 2), uncheck the check box that
says "This plug-in makes contributions to the UI."
- Click the Finish button to create the plug-in.
Figure 2. Plug-in Content view
To successfully build rules, you need to add dependencies to the plug-in:
- Open the plug-in manifest files, and select the Dependencies tab.
- 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
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:
- First, create a new class in the com.ibm.rsar.codereview.java.examples
package of the examples plug-in. Call this class
RuleFinalizerSuper.
- 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
{
}
|
- 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.
- 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.
- Add the following lines to the top of the rule class:
// Rule filters
private static final IRuleFilter[] MIFILTERS = {
};
|
- 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.
- 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).
- 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.
- 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.
- 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.
- 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.
- 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.
- 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:
- Open the example plug-in manifest created in the first part of this section.
- Select the Extensions tab in the editor, and click the Add button.
- From the list, select the com.ibm.rsaz.analysis.core.analysisCategory
extension point, as shown in Figure 4.
Figure 4. Extension Point Selection view
- 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
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.
- 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.
- In the Extensions tab, right-click on the rule extension and add a new
analysisRule.
- 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
- 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
- From the Eclipse view, select the Run option, and create a new Eclipse
Application.
- In the configuration panel, click the Run button. This invokes a new
runtime workbench to test your rule.
- 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
- With source code in the workbench, select the Analysis option from the
Eclipse Run menu.
- In the dialog that appears, create a new analysis configuration and go to the
Domains tab.
- 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
- 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
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
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
- Read part 1 of this series, Getting started, for an introduction to static analysis with Rational Software Analyzer".
- Part 3 of this series offers suggestions on Enhancing rules for Java code review.
- Part 4 of this series discusses techniques for Integrating your own analysis tools.
- Visit the Rational Software Analyzer area on developerWorks for introductory to in-depth information.
- Visit the
Rational software area on developerWorks
for technical resources and best practices for Rational Software Delivery Platform
products.
- Explore
Rational computer-based, Web-based, and instructor-led online courses.
Hone your skills and learn more about Rational tools with these courses, which
range from introductory to advanced. The courses on this catalog are available for
purchase through computer-based training or Web-based training. Additionally, some
"Getting Started" courses are available free of charge.
- Subscribe to the
Rational Edge newsletter
for articles on the concepts behind effective software development.
- Subscribe to the
IBM developerWorks newsletter,
a weekly update on the best of developerWorks tutorials, articles, downloads,
community activities, webcasts and events.
- Browse the
technology bookstore
for books on these and other technical topics.
Get products and technologies
Discuss
About the author  | 
|  | Steve 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. |
Rate this page
|  |