As a follow-up to my earlier tutorial Writing pretty code with PMD I am going to be discussing making custom PMD rules using XPath. The first thing to note is that we use a declarative programming paradigm to define our rules. We define or ‘declare’ what constitutes a PMD violation, but not what should be done about it, PMD takes care of that part.

Abstract Syntax Tree

PMD uses Java Abstract Syntax Tree (AST) representation of your source code to apply rules. Essentially the AST is representation of a java class with generic descriptors to refer each element in the source code. In code you may have a class called MyClass but in the AST its just a “ClassOrInterfacedeclaration”. Each element which is contained within an other is its descendant in the tree. For example a field is defined inside a class declaration so it is a descendant of the class description. Similarly all items defined within the same element are siblings in the tree. Any information that can be used to differentiate an instance of the generic type (such as the data type of the field) is stored as a property of the element. If the description above sounds reminiscent of XML its because ASTs are basically XML documents. As basic example of AST representation see the following code and its AST representation.

public class MyClass {

	int x;
	float y;
}

 

PMD1

PMD Rule Designer

Before we start defining our rules we need a sandbox in which to quickly test our code. Luckily for use PMD comes with just such a tool, PMD Rule designer. I have repackaged the tool into a single executable jar file which you can download here PMDRuleDesigner.jar. To run the tool just type java -jar java -jar PMDDesigner.jar into the *nix terminal or windows command line. Write your code in the top left section and hit the “Go” button. The bottom right section will list the AST representation of your code. The two sections on the right deal with XPath, which we will be discussing next.

pmd2

XPath Syntax

"/" Has child

The forward slash defines a child query, For example /TypeDeclaration defines that we want to match a child of the root node which is of type “TypeDeclaration”. We can also use multiple slashes in a query to search for a set of child relation ships. For example the query below will match will both field declarations in the code above.

/TypeDeclaration/ClassOrInterfaceDeclaration/ClassOrInterfaceBody/
	ClassOrInterfaceBodyDeclaration/FieldDeclaration

 

Enter the query specified above into the top left box of the PMD Rule Designer and hit go. In the box in the bottom right you should see the line (Line 4 & Line 5) numbers of the two field declarations.

pmd3

"//" Has Descendant

You don’t always want to define the full path from root for the element you are searching for so instead of using the has child relation ship you can use the double forward slash or has dependent relationship. This will search for any descendant of the current node. There for the query above to search for field definitions could also be written as //FieldDeclaration.

The has descendant is not only useful for shortening queries it is also useful for searching for specific cases regardless of where they exist in the source tree. For example, if we take the code below. There is no query using child relationships alone that will match both variable declarations (int x and float y). Where as the //PrimativeType query matches both.

class MyClass {

	int x;
	void method() {
		float y;
	}
}

 

"@" Has Property

The @ sign is used to denote has property, and we can use properties to get information about specific instances of elements. For example the Image property stores the name of the element (I have no idea why its stored in the Image property). So this query //VariableDeclaratorId/@Image will return two values x and y.

pmd4

"*" Wild Card

Wild cards are useful for matching many similar elements for example if we wanted to find all the fields and method declarations in a class. for fields we would use a query such as //ClassOrInterfaceBodyDeclaration/FieldDeclaration where as for a method declaration it would be something like //ClassOrInterfaceBodyDeclaration/MethodDeclaration. Using a wild card we can define the query as //ClassOrInterfaceBodyDeclaration/*

class MyClass {

	int x;
	void method() {
		float y;
	}
}

 

We can also use wild cards to list all properties of an element by combining @ and * e.g. //ClassOrInterfaceBodyDeclaration/FieldDeclaration/@*

"[]" Predicates

Conditionals are used to filter the set of possible nodes that match certain portions of the XPath string. For example if in the code below we wish to single out the function(s) which return an int we would use the XPath //MethodDeclaration/ResultType[contains(Type/*/@Image, "int")]. The first part defines a tree (or sub-tree) to which the conditional applies. In the example we would only apply the condition to the ResultType nodes which are children of a MethodDeclaration node. In the conditional we specify that the node must have a child or type ‘Type’ which in turn has an Image property containing the value ‘int’. Note how we use the wildcard to avoid having to specify whether the function will return PrimitiveType or ReferenceType.

class MyClass {
	int func2() {
		return 0;
	}	

	void func2() {
	}
}

 

By changing the query slightly we can even find the name of the function(s) with the int return type. Note we are now conditionally selecting a MethodDeclaration node which matches the given criterion. Once we have the node we look for its MethodDeclarator child and retrieve the method name.

	//MethodDeclaration[contains(ResultType/Type/*/@Image, "int")]/MethodDeclarator/@Image

 

We use the contains functions for our query but there are many other function you can use for predicates. An exhaustive list can be found here

My first rule

Ok enough beating around the bush lets create our first PMD rule. This is one of my pet peeves, I hate unused or overly generic imports such as import java.NET.*; that all newbies to java seem to use. So how can we stop them? First of all fire up your pmd rule designer and write some imports to see what they look like, I like to write one example of a violation and one example of proper usage so that I can see the differences. If you look at the properties of the two statements you will see that if we use the wild card import the PackageName property matches the ImportedName property. With the query /ImportDeclaration[@PackageName=@ImportedName] we can select all the imports which use the wild card matching and not the specific imports. Try this query in the Rule designer to verify that it only matches the wild card import.

import java.net.URL;
import java.net.*;

 

pmd5 pmd6

</table>

Ok now that we have our XPath query lets create a rule set file, and add the custom rule definition. We give the rule a name, an admonishing message to be displayed to violators as well as a longer description. We can set how severe want the rule violation to be considered. As you can see I really hate wild card imports because I set it to highest level. The all important XPath rule goes in the property.value item of the property named xpath.

    <rule name="DontImportWild" message="Please no wild card imports" 
    		class="net.sourceforge.pmd.rules.XPathRule">

    <description>We don't take kindly to imports such as java.net.* round these parts</description>
    <priority>1</priority>
    <properties>
      <property name="xpath">
        <value>
          <![CDATA[/ImportDeclaration[@PackageName=@ImportedName]]]>
        </value>
      </property>
    </properties>
    <example>
    <![CDATA[
    	import java.net.*; //is bad
    	import java.net.URL; //is better
    ]]>
    </example>
  </rule>

 

You can append this rule to the pmd ruleset in my earlier tutorial Writing pretty code with PMD which can be downloaded here. When you run the compile you will get the following output:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-pmd-plugin:2.5:check (default) on project 
		helloworld: You have 1 PMD violation. For more details see:helloworld/target/pmd.xml -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.

 

And if you open said file you will see the following information, telling you that the problem is on line 8 of the GuiceCreator.java file and that we should not use wild card imports.

<?xml version="1.0" encoding="UTF-8"?>
<pmd version="4.2.5" timestamp="2011-11-13T21:55:12.677">
<file name="helloworld/src/main/java/com/flybynight/helloworld/GuiceCreator.java">
<violation beginline="8" endline="8" begincolumn="1" endcolumn="18" 
	rule="DontImportWild" ruleset="PMD-Rules" 
	package="com.flybynight.helloworld" priority="1">
Please no wild card imports
</violation>
</file>
</pmd>

 

Conclusion

Despite the length of this article note that we were able to create a small xml snippet which will fix a coding malpractice through out all our code. Granted that this was a simple case but using XPath we can create more complex ad intricate rules. Further more if we spend the effort writing the rule(s) once we can ensure code quality without the overhead of code reviews as PMD can be integrated with our build.

Source Code

Here is the complete project with updated ruleset file and violation. Note that all code and other source provided here are licensed under the BSD License.

External Links