반응형

PMD is a static code analyzer for Java. Developers use PMD to comply with coding standards and deliver quality code. Team leaders and Quality Assurance folks use it to change the nature of code reviews. PMD has the potential to transform a mechanical and syntax check oriented code review into a to dynamic peer-to-peer discussion.

This article looks at PMD as an Eclipse plugin and the ways it can be used to improve the code quality and shorten the code review process. Since every organization has a unique set of coding conventions and quality metrics, it also demonstrates how to customize PMD to meet these needs.

What is PMD?

PMD works by scanning Java code and checks for violations in three major areas:

  • Compliance with coding standards such as:
    • Naming conventions - class, method, parameter and variable names
    • Class and method length
    • Existence and formatting of comments and JavaDocs
  • Coding antipatterns such as:
    • Empty try/catch/finally/switch blocks
    • Unused local variables, parameters and private methods
    • Empty if/while statements
    • Overcomplicated expressions - unnecessary if statements, for loops that could be while loops
    • Classes with high Cyclomatic Complexity measurements
  • Cut and Paste Detector (CPD) - a tool that scans files and looks for suspect code replication. CPD can be parameterized by the minimum size of the code block.

In its current version, PMD comes packaged with 149 rules in 19 rulesets. Most of these rules can be parameterized at runtime by supplying properties or parameters. The standard package offers many well-thought rules. In addition users also have the ability to add their own rules for particular coding convention or quality metrics. Here are some of the rules distributed with PMD:

  • EmptyFinalizer - If the finalize() method is empty, then it does not need to exist.
  • EmptyFinallyBlock - Avoid empty finally blocks - these can be deleted.
  • UnnecessaryReturn - Avoid unnecessary return statements
  • OnlyOneReturn - A method should have only one exit point, and that should be the last statement in the method.
  • CyclomaticComplexity - Complexity is determined by the number of decision points in a method plus one for the method entry. The decision points are 'if', 'while', 'for', and 'case labels'. Generally, 1-4 is low complexity, 5-7 indicates moderate complexity, 8-10 is high complexity, and 11+ is very high complexity.
  • TooManyFields - Classes that have too many fields could be redesigned to have fewer fields, possibly through some nested object grouping of some of the information. For example, a class with city/state/zip fields could instead have one Address field.
  • LongVariable - Detects when a field, formal or local variable is declared with a long name.
  • NoPackage - Detects when a class or interface does not have a package definition.

Where to find it?

PMD started as a standalone application and today it is an open-source project hosted under SourceForge.net. It is still distributed as a standalone application, however there is now substantial support for most popular Java IDEs. The PMD team has developed plugins for JDeveloper, Eclipse, JEdit, JBuilder, Omnicore's CodeGuide, NetBeans/Sun Java Studio Enterprise/Creator, IntelliJ IDEA, TextPad, Maven, Ant, Gel, JCreator, and Emacs. This article covers PMD's Eclipse plugin only.

PMD binaries and source files can be downloaded from PMD's Sourceforge directory. For an open-source project, PMD is relatively well-documented. Among the items in PMD's main site are topics such as installation, PMD-related products and books, best practices, licensing info, usage guides and more. There is also a section for developers interested in joining the project. The following items target the latter: compiling PMD, project info, project reports, and development process.

How to install it?

The easiest way to install PMD is by using the remote update site. Users behind firewalls should check proxy settings before going any further. If these settings are misconfigured the updater will not work. PMD also supplies a zip file for manual installation. Download the file and follow the readme. Demonstrated below is installing PMD via the Eclipse Software Updater.

  1. Launch Eclipse.
  2. Navigate to Help | Software Updates | Find and Install...
  3. Select "Search for new features to install" and click Next
  4. Click New Remote Site...
  5. Enter a name (PMD) and the URL http://pmd.sourceforge.net/eclipse
  6. In Sites to include in search check PMD and click Finish
  7. In the Search Results dialog check PMD for Eclipse 3 3.1.0 and click Next
  8. Accept the terms of the license agreements and click Next
  9. Click Finish.
  10. Wait for Eclipse to download the required jar files, then click Install
  11. Restart the workbench. This will load the PMD plugin.
  12. Navigate to Window | Show View | Other...
  13. Select PMD | PMD Violations
  14. PMD Violations view should appear at the bottom pane

How to use it?

Before launching Eclipse make sure you have enough memory for PMD. This is particularly important when analyzing large projects. In these situations PMD tends to be memory-hungry. Hence, make sure to start with as much memory as you can afford, for example 512M (eclipse.exe -vmargs -Xmx512M)

  1. Launch Eclipse
  2. If you have previously created a Java Project, skip to Step 6. Otherwise click File | New | Project...
  3. Select Java Project and click Next
  4. Enter a project name (QA Project) and leave everything else in the default state.
  5. Click Finish. Eclipse will ask if you want to switch to the Java Perspective. Click Yes.
  6. In the Package Explorer right-click on QA Project and select New | Class
  7. In the following dialog enter the class name as Ying and click Finish
  8. A new class Ying is created in project's default package. Paste the following code into the new class:
    public class Ying {
    
    	private static final String gOOD = "GOOD";
    
    	public void StartSomething() {
    		System.out.println("Hello PMD World!");
    	}
    
    	public String thisIsCutAndPaste(String pFirst, int pSecond) {
    		System.out.println("New world");
    		return "New world";
    	}
    }
  9. Similarly, create a second class Yang and paste the following code:
    public class Yang extends Thread {
    	public Yang(String str) {
    		super(str);
    	}
    
    	public void run() {
    		for (int i = 0; i < 10; i++) {
    			System.out.println(i + " " + getName());
    			try {
    				sleep((long) (Math.random() * 1000));
    			} catch (InterruptedException e) {
    			}
    		}
    		System.out.println("DONE! " + getName());
    	}
    
    	public void WRITE_SOMETHING(String INPUT_PARAMETER) {
    		System.out.println(INPUT_PARAMETER);
    	}
    
    	public static void main(String[] args) {
    		new Yang("Good").start();
    		new Yang("Bad").start();
    	}
    
    	public String thisIsCutAndPaste(String pFirst, int pSecond) {
    		System.out.println("New world");
    		return "New world";
    	}
    }
  10. In the Package Explorer right-click on QA Project and select PMD | Check Code With PMD
  11. Wait for PMD to scan Ying and Yang
  12. If PMD Violations view is not open navigate to Window | Show View | Other... and select PMD | PMD Violations
  13. In the PMD Violations view notice a list of 17 violations. In large projects this list could easily grow up to several thousand. This is one of the reasons PMD allows violations be filtered by priority. Priority is a configurable attribute of a PMD rule. PMD assigns priorities from 1 to 5 and each priority is represented by a colored square at the top-right corner of the view. These little squares are actually clickable on-off switches used to control the visibility of the violations they represent.
    The results table itself is well laid out and most columns are sortable. It is also possible to get more detail on a violation by simply right-clicking it and selecting Show rule. PMD pops-up a dialog with information such as rule name, implementation class, message, description and an example. This feature can be helpful when trying to makes sense of a new rule or letting inhouse developers know about a particular company rule or coding convention.
    Finally, in the PMD Violations table it is possible to add review annotations to the source where the violation occurred. This can be an effective way to mark target files for further review. Just right-click on any violation in the list and select Mark review. PMD will insert a review annotation to the Java source right above the violation line itself. The review annotation should look like this:
    // @PMD:REVIEWED:MethodNamingConventions: by Levent Gurses on 3/28/04 5:04 PM
    
    
    Review annotations can be removed anytime by right-clicking QA Project and selecting PMD | Clear violations reviews. Similarly, PMD Violations can be cleaned-up by right-clicking QA Project and selecting PMD | Clear PMD Violations..

Finding Cut and Paste Code

Repeated (Cut&Paste) code generally indicates poor planning or team coordination. Therefore, refactoring classes with repeating code should be given a high priority. PMD can help identify these classes by scanning the code in a way similar to PMD violation checks. The number of lines of similarity (the metrics used by PMD to match code patterns) is 25 by default and can be set in PMD's Preferences page.

  1. In Package Explorer right-click on QA Project and select PMD | Find Suspect Cut And Paste
  2. PMD creates a folder reports under QA Project and stores the result text file
  3. Select Window | Show View | Navigator
  4. In Navigator view click on QA Project and then on the reports folder
  5. The report file cpd-report.txt should look like this:
    =====================================================================
    Found a 8 line (25 tokens) duplication in the following files:
    Starting at line 6 of C:\temp\QA Project\Ying.java
    Starting at line 23 of C:\temp\QA Project\Yang.java
    		new Yang("Bad").start();
    	}
    
    	public String thisIsCutAndPaste(String pFirst, int pSecond) {
    		System.out.println("New world");
    		return "New world";
    	}
    }

Generating Reports

After running a lengthy violation check it may be desirable to share the findings with peers or store them for later reference. For situations of this kind PMD provides a handy reporting tool capable of generating reports in multiple formats. Currently PMD can generate reports in HTML, XML, plain text as well as Comma Separated Value (CSV) formats.

  1. In Package Explorer right-click on QA Project and select PMD | Generate reports
  2. PMD creates a folder reports under QA Project and stores four report files
  3. Select Window | Show View | Navigator
  4. In Navigator view click on QA Project and then on the reports folder
  5. Of the four report files pmd-report.html should look like this:
    # File Line Problem
    1 Yang.java 8 System.out.print is used
    2 Yang.java 14 System.out.print is used
    3 Yang.java 17 Method name does not begin with a lower case character.
    4 Yang.java 17 Method names should not contain underscores
    5 Yang.java 17 Parameter 'INPUT_PARAMETER' is not assigned and could be declared final
    6 Yang.java 18 System.out.print is used
    7 Yang.java 21 Parameter 'args' is not assigned and could be declared final
    8 Yang.java 27 System.out.print is used
    9 Ying.java 1 Each class should declare at least one constructor
    10 Ying.java 3 Avoid unused private fields such as 'gOOD'
    11 Ying.java 5 Method name does not begin with a lower case character.
    12 Ying.java 6 System.out.print is used
    13 Ying.java 9 Parameter 'pFirst' is not assigned and could be declared final
    14 Ying.java 9 Parameter 'pSecond' is not assigned and could be declared final
    15 Ying.java 10 System.out.print is used

Customizing PMD

The easiest way to begin customizing PMD is by playing with existing rules. Adding new rules is also possible as well as removing unnecessary ones, however these require more knowledge. Since experimenting with existing rules is the easiest, it makes sense to start with them.

Each PMD rule has six attributes:

  • Rule name - immutable
  • Rule implementation class - immutable
  • Priority
  • Message
  • Description
  • Example

Of these six attributes the first two are immutable - they cannot be customized by users. While Message, Description and Example are text-based properties and can accept any String data, Priority is an integer field ranging from 1 to 5.

PMD stores rule configuration in a special repository referred to as the Ruleset XML file. This configuration file carries information about currently installed rules and their attributes. Changes made through the Eclipse Preferences page are also stored in this file. In addition, the PMD Preferences page allows exporting and importing Rulesets which makes them a convenient vehicle for sharing rules and coding conventions across the enterprise.

Before starting the customization it may be a good idea to back-up existing configuration.

  1. Navigate to Window | Preferences... | PMD | Rules configuration
  2. Click Export rule set...
  3. Enter a file name e.g. pmd-rules.xml and click Save
  4. Back in the Rules configuration page scroll-down and click on SystemPrintln rule
  5. With the rule selected, click the Priority column and change the priority from Error (2) to Information (5)
  6. With the rule still selected, click the Edit rule... button on the right
  7. Change the Message, Description and Example. Click OK when done
  8. Click Export rule set...
  9. Enter a new file name e.g. pmd-rules-customized.xml and click Save
  10. Compare the two XML files - they are different
  11. In Package Explorer right-click on QA Project and select PMD | Clear PMD Violations
  12. In Package Explorer right-click on QA Project and select PMD | Check Code With PMD
  13. Wait for PMD to finish the scan
  14. If PMD Violations view is not open navigate to Window | Show View | Other... and select PMD | PMD Violations
  15. Notice SystemPrintln is now priority 5

Adding New Rules

PMD allows new rules in two formats: Java-based rules where the rule is written as a Java implementation class and XPath rules where the rule is defined in an XML file. Both ways are equally applicable and the choice is probably a matter of preference. This article will demonstrate creating a new rule with both Java and XPath. The rule, ParameterNameConvention, will be a simple coding convention checker making sure all method parameters start with a lowercase "p". To accomplish its task the rule will take advantage of regular expressions and will enforce the following expression to all method parameters: [p][a-zA-Z]+. Later this expression will be made into a rule property which will allow further customization by rule users.

This is all good, but before going any further into the custom PMD rules let's see how this PMD thing actually works.

How PMD works?

PMD relies on the concept of Abstract Syntax Tree (AST), a finite, labeled tree where nodes represent the operators and the edges represent the operands of the operators. PMD creates the AST of the source file checked and executes each rule against that tree. The violations are collected and presented in a report. PMD executes the following steps when invoked from Eclipse (Based on PMD's documentation):

  1. The Eclipse PMD plugin passes a file name, a directory or a project to the core PMD engine (Also part of the Eclipse plugin, however housed in a separate package). This engine then uses the RuleSets as defined in the PMD preferences page to check the file(s) for violations. In the case of a directory or project (multiple source files) the plugin executes the following steps for each file in the set.
  2. PMD uses JavaCC to obtain a Java language parser
  3. PMD passes an InputStream of the source file to the parser
  4. The parser returns a reference of an Abstract Syntax Tree back to the PMD plugin
  5. PMD hands the AST off to the symbol table layer which builds scopes, finds declarations, and find usages
  6. If any rules need data flow analysis, PMD hands the AST over to the DFA layer for building control flow graphs and data flow nodes
  7. Each Rule in the RuleSet gets to traverse the AST and check for violations
  8. The Report is generated based on a list of RuleViolations. These are displayed in the PMD Violations view or get logged in an XML, TXT, CSV or HTML report.
Abstract Syntax Tree
In computer science, an Abstract Syntax Tree (AST) is a finite, labeled, directed tree, where the nodes are labeled by operators, and the edges represent the operands of the node operators. Thus, the leaves have nullary operators, i.e., variables or constants. In computing, it is used in a parser as an intermediate between a parse tree and a data structure, the latter which is often used as a compiler or interpreter's internal representation of a computer program while it is being optimized and from which code generation is performed. The range of all possible such structures is described by the abstract syntax. An AST differs from a parse tree by omitting nodes and edges for syntax rules that do not affect the semantics of the program. The classic example of such an omission is grouping parentheses, since in an AST the grouping of operands is explicit in the tree structure. 
Creating an AST in a parser for a language described by a context free grammar, as nearly all programming languages are, is straightforward. Most rules in the grammar create a new node with the nodes edges being the symbols in the rule. Rules that do not contribute to the AST, such as grouping rules, merely pass through the node for one of their symbols. Alternatively, a parser can create a full parse tree, and a post-pass over the parse tree can convert it to an AST by removing the nodes and edges not used in the abstract syntax.
From Wikipedia

PMD Class Hierarchy

Since PMD works on a tree data structure, its unit of operation is Node. Every construct in a Java source file examined by PMD is assigned a node in the Abstract Syntax Tree. These nodes are then visited by all rules in the ruleset and the method public Object visit(SimpleNode node, Object data) of the rule implementation class gets invoked. It is inside this method where the rule logic is defined.

In PMD every node extends net.sourceforge.pmd.ast.SimpleNode. This concrete class implementsnet.sourceforge.pmd.ast.Node and has 104 first-level children as well as an additional 8 second-level children coming from net.sourceforge.pmd.ast.AccessNode.

This screenshot captures portion of SimpleNodes children. The entire list is available with the source distribution of PMD. This class hierarchy is important for writing custom PMD because each custom rule will rely on one or more of PMD's SimpleNodes to accomplish its task.

This is a good place to see the Abstract Syntax Tree in action.

  1. In Java Perspective, go to Package Explorer and double-click on QA Project and next (default package)
  2. Right-click on Ying.java and select PMD | Generate Abstract Syntax Tree
  3. Click on Window | Show View | Navigator
  4. In Navigator view click on QA Project
  5. The Abstract Syntax Tree file Ying.ast is located in the root level. It should look like this:
    <?xml version="1.0" encoding="UTF-8"?>
    <CompilationUnit beginColumn="1" beginLine="13" endColumn="3" endLine="13">
        <TypeDeclaration beginColumn="1" beginLine="1" endColumn="1" endLine="13">
            <ClassOrInterfaceDeclaration abstract="false" beginColumn="8" beginLine="1" endColumn="1" endLine="13" final="false" image="Ying" interface="false">
                 ...........
                 ...........
    		<MethodDeclarator beginColumn="23" beginLine="9" endColumn="67" endLine="9" image="thisIsCutAndPaste" parameterCount="2">
    		    <FormalParameters beginColumn="40" beginLine="9" endColumn="67" endLine="9" parameterCount="2">
    			<FormalParameter abstract="false" array="false" arrayDepth="0" beginColumn="41" beginLine="9" endColumn="53" endLine="9" final="false">
    			    <Type array="false" arrayDepth="0" beginColumn="41" beginLine="9" endColumn="46" endLine="9">
    				<ReferenceType array="false" arrayDepth="0" beginColumn="41" beginLine="9" endColumn="46" endLine="9">
    				    <ClassOrInterfaceType beginColumn="41" beginLine="9" endColumn="46" endLine="9" image="String"/>
    				</ReferenceType>
    			    </Type>
    			    <VariableDeclaratorId array="false" arrayDepth="0" beginColumn="48" beginLine="9" endColumn="53" endLine="9" exceptionBlockParameter="false" image="pFirst" typeNameNode="ReferenceType" typeNode="Type"/>
    			</FormalParameter>
    			<FormalParameter abstract="false" array="false" arrayDepth="0" beginColumn="56" beginLine="9" endColumn="66" endLine="9" final="false">
    			    <Type array="false" arrayDepth="0" beginColumn="56" beginLine="9" endColumn="58" endLine="9">
    				<PrimitiveType array="false" arrayDepth="0" beginColumn="56" beginLine="9" boolean="false" endColumn="58" endLine="9" image="int"/>
    			    </Type>
    			    <VariableDeclaratorId array="false" arrayDepth="0" beginColumn="60" beginLine="9" endColumn="66" endLine="9" exceptionBlockParameter="false" image="pSecond" typeNameNode="PrimitiveType" typeNode="Type"/>
    			</FormalParameter>
    		    </FormalParameters>
    		</MethodDeclarator>
                 ...........
                 ...........
            </ClassOrInterfaceDeclaration>
        </TypeDeclaration>
    </CompilationUnit>

This AST report gives a full synopsis of the examinee, in this case Ying.java. It is possible for example to gain insight about the method parameter pFirst by expanding the FormalParameter node. Studying this Abstract Syntax Tree can contribute to a better understanding of how a Java source file is examined and what variables play role in the process. This in turn could prove useful when crafting custom rules.

Writing Custom PMD Java Rules

Now, going back to ParameterNameConvention it is time to crank some code. This article will create a Java class for the rule implementation and package it as a plugin fragment. A fragment is an extension of a plug-in and all the classes and resource files it contains are automatically added to the main plug-in classpath. Since PMD searches main plugins classpath for rule implementation classes the fragment will be automatically available. In addition, this structure will allow for faster development and easier distribution.

Creating the plugin fragment in Eclipse

  1. In Eclipse Navigate to Window | Open Perspective | Other...
  2. Select Plug-in Development
  3. In Package Explorer right-click and select New | Project...
  4. In the New Project wizard select Plug-in Development | Fragment Project and click Next
  5. Enter a project name of your liking, e.g. com.jacoozi.pmd.rules and click Next
  6. In the Fragment Content dialog enter details for the fragment and the host plug-in. Click Browse... to locate the host plugin.
  7. In the Plug-in Selection dialog type net.sourceforge.pmd.eclipse then select the only item in the list. Click OK
  8. Back in the Fragment Content dialog select Greater or Equal for Match Rule. This will ensure that future upgrades to the host PMD plug-in do not affect the fragment. The dialog should look like the following:
  9. Click Finish

Implementing the rule class

  1. In Package Explorer right-click on the source folder src and select New | Class
  2. Enter a package name of liking, e.g. com.jacoozi.pmd.rules
  3. Enter the class name ParameterNameConvention
  4. Click Finish
  5. Paste the following code into the new Java class:
    package com.jacoozi.pmd.rules;
    
    import java.util.Iterator;
    
    import net.sourceforge.pmd.AbstractRule;
    import net.sourceforge.pmd.RuleContext;
    import net.sourceforge.pmd.RuleViolation;
    import net.sourceforge.pmd.ast.ASTFormalParameter;
    import net.sourceforge.pmd.ast.ASTMethodDeclaration;
    import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
    
    /**
     * @author Levent Gurses
     * Copyright 2005 Jacoozi
     */
    public class ParameterNameConvention extends AbstractRule {
    	private final static String PATTERN = "[p][a-zA-Z]+";
    
    	public Object visit(ASTMethodDeclaration node, Object data) {
    		RuleContext result = (RuleContext) data;
    		String rulePattern = (!getStringProperty("rulePattern").equalsIgnoreCase("")) ? getStringProperty("rulePattern") : PATTERN;
    		if (node.containsChildOfType(ASTFormalParameter.class)) {
    			Iterator iterator = node.findChildrenOfType(ASTFormalParameter.class).iterator();
    			while (iterator.hasNext()) {
    				ASTFormalParameter element = (ASTFormalParameter) iterator.next();
    				Iterator decIdIterator = element.findChildrenOfType(ASTVariableDeclaratorId.class).iterator();
    				while (decIdIterator.hasNext()) {
    					ASTVariableDeclaratorId decElement = (ASTVariableDeclaratorId) decIdIterator.next();
    					if (!decElement.getImage().matches(rulePattern)) {
    						
    						result.getReport().addRuleViolation(new RuleViolation(this,
    								node.getBeginLine(), "Parameter '" + decElement.getImage() + "' should match regular expression pattern '" +
    								rulePattern + "'", result));
    					}
    				}
    			}
    		}
    		return result;
    	}
    }

    Couple of points here. First, notice it extends import net.sourceforge.pmd.AbstractRule. All custom rules must extend this class. Second, notice the AST traversal starts from the method declaration. It then iterates through its children and looks for ASTFormalParameter. Finally, it compares thenode.getImage() against the rule regular expression. Notice that a mismatch causes the creation of a new RuleViolation. As a side note, notice also the regular expression is fed into the rule as a property.

Testing the new rule

The rule class is now complete and there are no compilation errors (red Xs). This means it is ready for testing.

  1. In Plug-in Package Explorer right-click on com.jacoozi.pmd.rules and select Run As | Eclipse Application (Alt+Shift+X,E). Alternatively, to launch a Debug session click on Run | Debug As | Eclipse Application (Alt+Shift+D,E)
  2. Eclipse will launch a fresh instance to test the new PMD rule. In Package Explorer right-click and selectImport... | Existing Project into Workspace. Click Next
  3. In Import Projects click Browse to locate the QA Project. Navigate to C:\Documents and Settings\[Your name]\workspace and click OK. This assumes you created the QA Project in Eclipse's default workspace. If not, locate the project in your custom workspace.
  4. Check the QA Project and click Finish
  5. Select Window | Preferences... | PMD | Rules configuration
  6. Click Add rule..., fill the form with the following and click OK
    Rule name: ParameterNameConvention
    Rule implementation class: com.jacoozi.pmd.rules.ParameterNameConvention
    Message: Method parameters should begin with a lowercase "p"
    Description: Method parameters should always begin with a "p". This is equivalent to the parameters complying with regular expression [p][a-zA-Z]+. This expression can be changed the preferences page by adding a property "rulePattern".
    Example:
    public void bringItHome( String pName, int pNumber, boolean pDoneThat, List pTr)
  7. Change rule priority to Error high
  8. Click on Add property... and enter rulePattern as name and [p][a-zA-Z]+ as value.
  9. Click on OK and agree to rebuild.
  10. In Package Explorer right-click on QA Project and select PMD | Check Code with PMD. Open the PMD Violations view. It should display three ParameterNameConvention violations in Yang.java.

Packaging and distributing the new rule

What happens if you want to share a rule with colleagues or make it a common convention for the team? Simple. The fragment project can be exported as a zip file making it easy to distribute. First, export the fragment project; next unzip the file into Eclipse's plugins folder and it's done.

  1. In Plug-in Package Explorer right-click on com.jacoozi.pmd.rules and select Export... | Deployable plug-ins and fragments
  2. Enter a name for the zip file, e.g. com.jacoozi.pmd.rules and click Finish. Eclipse will export the fragment classes into a new archive com.jacoozi.pmd.rules.zip.
  3. Export ruleset. In PMD Preferences page Click Export rule set.... Zip this file and the fragment zip from previous step together. The portable plugin fragment is now ready for distribution.

Installing the new rule

What happens if you want to share a rule with colleagues or make it a common convention for the team? Simple. The fragment project can be exported as a zip file making it easy to distribute. First, export the fragment project; next unzip the file into Eclipse's plugins folder. That's pretty much it.

  1. Unzip the archive to the target machine. If the target environment uses default Eclipse settings, unzip the file under C:\eclipse (The zip file contains a folder plugins)
  2. Navigate to PMD Preferences page and click Import rule set.... Select the ruleset file from previous step and click OK
  3. Click OK to close the Preferences page.

The new rule is now ready for use.

Writing Custom PMD XPath Rules

The second way to add custom rules to PMD requires some XPath knowledge. XPath has been out for some time now and has proven to be an effective query language for DOM-based XML. Detailed XPath would fill an entire book, therefore for the sake of time and space it is left out. The following Wikipedia definition provides a basic idea of what the language is all about.

XPath
XPath (XML Path Language) is a terse (non-XML) syntax for addressing portions of an XML document.

Originally motivated by a desire to provide a common syntax and behavior model between XPointer and XSL, XPath has rapidly been adopted by developers as a small query language.

The most common kind of XPath expression (and the one which gave the language its name) is a path expression. A path expression is written as a sequence of steps to get from one set of nodes to another set of nodes. The steps are separated by "/" (i.e. path) characters. Each step has three components:
  • Axis Specifier
  • Node Test
  • Predicate
The notation is compact, allowing many defaults and abbreviations for common cases. 

The simplest kind of path expression takes a form such as /A/B/C, which selects C elements that are children of B elements that are children of the A element that forms the outermost element of the document. 

XPath syntax is designed to mimic URI (Uniform Resource Identifier) syntax or file name syntax. More complex expressions can be constructed by specifying an axis other than the default child axis, a node test, other than a simple name, or predicates, which can be written in square brackets after each step. For example, the expression /A/B/following-sibling::*[1] selects all elements (whatever their name) that immediately follow a B element that is a child of the outermost A element.
From Wikipedia

What is needed here is a way to implement ParameterNameConvention as an XPath rule. The chief advantage of XPath rules in PMD is the power, elegance and simplicity of XPath compared to Java. The main disadvantage is that not many people are familiar with XPath. With that being said, let's see how much time XPath can actually save.

  1. To save confusion, first remove the Java ParameterNameConvention from Eclipse. Navigate toWindow | Preferences... | PMD | Rules configuration
  2. Scroll-down the list and select ParameterNameConvention. Click Remove rule
  3. Now, create the new XPath rule. In PMD Rules configuration dialog click Add rule...
  4. This time check XPath rule. The rule implementation class will automatically change tonet.sourceforge.pmd.rules.XPathRule. Fill the rest of the form with the following:
    Rule name: ParameterNameConvention
    Message: Method parameters should begin with a lowercase "p"
    Description: Method parameters should always begin with a "p".
    Example:
    public void bringItHome( String pName, int pNumber, boolean pDoneThat, List pTr)
  5. Change rule priority to Error high
  6. Click on Add property... and enter xpath as name and //FormalParameter/VariableDeclaratorId[not (starts-with(@Image, 'p'))] as value.
  7. Click on OK and agree to rebuild.
  8. In Package Explorer right-click on QA Project and select PMD | Check Code with PMD. Open the PMD Violations view. It should display three ParameterNameConvention violations in Yang.java.

That's it. The whole rule takes a single line:

//FormalParameter/VariableDeclaratorId[not (starts-with(@Image, 'p'))]

This one-line XPath rule tells PMD to watch for method parameters whose VariableDeclaratorId name ('Image') does not start with a "p". Very elegant.

XPath-based PMD rules offer an efficient alternative to Java-based rules. As more development tools become XPath-compatible it is likely that an investment in this powerful query language will prove valuable for Java developers.

Wish List

I must admit, I am impressed by PMD's capabilities. It has proven itself as a production grade tool, capable of handling large volumes of Java source files. There are couple of things I wish PMD had. The list below is dedicated to what's missing or can be improved.

  • Add better filtering capabilities to the PMD Violations view. For example add pagination, an important feature when working on large projects. Similarly, improve memory management - large projects may cause the environment to run out of memory.
  • Improve code checking capability by adding advanced query options. For example, add ability to check for a specific rule violation. Support by generating reports for specific rule violation or other more advanced search queries.
  • Move the report generation context menu from the project menu into the PMD Violations view. Support this functionality with advanced filters so that generated could reports include a subset of the violations and not be limited to the entire set.
  • Simplify the rule addition process. Today, Java rules are a bit of a complication to add. Create a WEB-INF-type folder under the core plugin where the classloader would monitor for custom rule implementation classes. This would free developers from having to create plugin fragment projects and a single rule class would be sufficient.
  • Since XPath is such a powerful and efficient way to add custom rules, use AST trees generated from source files to visually guide developers while creating XPath queries. This would help people with little or no XPath experience to begin writing custom rules faster. Alternatively, create an XPath based visual AST editor for Eclipse.
  • Improve the PMD Preferences page. Add multi-line support for Rule properties - important when adding XPath rules.

Summary

There is little doubt that code quality is becoming a prime factor in today's software economy. Many companies are taking real steps to transform old-fashioned, paper-based and in many cases simply ineffective QA practices into modern, efficient, value-added software lifecycle practices. In this path, they are realizing the power of software as a tool to check, correct and improve itself. And a new breed of automated code analyzers is slowly emerging. Designed to free developers from repetitive and error-prone manual code checks, these software "robocops" take the burden from developers, thus enabling them to spend more time on real design and performance issues such as patterns and multithreaded execution.

Automated code analyzers are not meant to replace manual QA. Instead, they should be used in tandem with well-designed manual review processes. Research shows that companies using automated QA tools with paperless, manual QA gain competitive advantage by considerably reducing software maintenance costs. This reduction comes mainly from lower number of defects and less time spent on each defect.

PMD and Eclipse are two great tools for improving personal code quality as well as implementing a consistent company-wide coding convention. PMD operates by applying a set of rules to source files. It comes with a rich suite of rules which can be extended in two ways. The traditional way of adding new rules to PMD is by implementing the rule class in Java and then adding it to the plugin's classpath. PMD has recently added XPath support for custom rules. XPath helps PMD take advantage of the fact it operates on an XML-based Abstract Syntax Tree. Compared to Java rules, XPath rules are much shorter and look way cooler. XPath is a powerful XML query language capable of small wonders. The elegance and beauty of XPath makes it a great choice for developing custom PMD rules.

References

System Information

  • Windows 2000
  • Eclipse 3.1.0
  • JDK 1.4.2_04
  • PMD - pmd-eclipse-3.1
  • XPath 2.0
반응형

'Code Inspection' 카테고리의 다른 글

package-info.java  (0) 2013.05.22
checkstyle rule  (0) 2013.05.22

+ Recent posts