一、基础知识 uyj*v]AE'
PMD是一种分析Java代码错误的工具。与其他分析工具不同的是,PMD通过静态分析获知代码错误。也就是说,在不运行Java程序的情况下报告错误。PMD附带了许多可以直接使用的规则,利用这些规则可以找出Java源程序的许多问题,例如没有用到的变量、多余的变量创建操作、空的catch块,等等。此外,用户还可以自己定义规则,检查Java代码是否符合某些特定的编码规范。例如,你可以编写一个规则,要求PMD找出所有创建Thread和Socket对象的操作。 PEIr-qs%D
最初,PMD是为了支持Cougaar项目而开发的。Cougaar是美国国防高级研究计划局(Defense Advanced Research Projects Agency,DARPA)的一个项目。DARPA开放了PMD的源代码,所以PMD被发布到了SourceForge网站上。不久前,PMD的下载次数就超过了14000次,页面浏览次数超过了130000次。更重要的是,在源代码开放作者的努力下,越来越多的PMD规则和IDE插件被开发出来,然后加入到了PMD的核心项目之中。 dDbC0} x/
你可以从PMD的网站下载PMD的二进制版本,或下载带源代码的版本,下载得到的都是ZIP文件。假设你下载了二进制版本,先把它解压缩到任意一个目录。接下来怎么做,就要看你准备怎么用它――最简单的,如果要在一个Java源代码目录中运行PMD,只需直接在命令行上运行下面的命令: !e `=UZe1
C:\data\pmd\pmd>java -jar lib\pmd-1.02.jar c:\j2sdk1.4.1_01\src\java\util GcCs}
(eo
text rulesets/unusedcode.xml ]:s|.C%q I
Q8oo5vqQ#C
输出结果类如: ~7a BeD
c:\j2sdk1.4.1_01\src\java\util\AbstractMap.java 650
!ei20@
Avoid unused local variables such as 'v' f :5/y^M&
c:\j2sdk1.4.1_01\src\java\util\Date.java 438 OvG0UXRU
Avoid unused local variables such as 'millis' X~3P?O]kFv
9m4rNvb
除了直接在命令行上运行PMD之外,还可以通过Ant、Maven或者各种集成开发环境(IDE)运行PMD,例如jEdit、Netbeans、Eclipse、Emacs、IDEAJ和JBuilder等。 /
nFw
二、内建规则
%
cdP*
PMD本身就附带了许多规则。下面是几个例子。 J|24I4
没有用到的代码显然是应该被清除的。 L?+|%[
public class Foo { MlE~gCD
// 下面这个实例变量没有用到
Z
-a(3&
private List bar = new ArrayList(500); ;%alZ
} qu+2..3
如果用一个接口也能达到同样的目标,为什么要返回一个具体的类?例如,下例可以改用List接口。 Zr;=p"cXr
public ArrayList getList() { )du{ZWr
return new ArrayList(); gDNW~?/
} |l(lrJ{
当if的条件为真时,if代码块其实不做任何事情。下面这段代码其实可以写得更加简洁一些。 J9.p8A^^2
public void doSomething(int y) {
Xy<f_
if (y >= 2) { &X,)+b=
} else { eE%yo3
System.out.println("Less than two"); nF$)F?||
} 0 _}89:-
} F@bCm+z-
为什么要创建一个新的String对象?只要改用String x="x"就可以了。 MToQ8qKs
String x = new String("x"); \#,#_
*8H;KG
e=
PMD还包含其他许多内建规则,但从上面几个例子已经可以看出PMD的基本工作方式。只要定义适当的静态规则,PMD就可以象一个富有经验的程序员那样,帮你指出代码存在的问题。 SVh4)}.x
三、工作原理 KB*=a
PMD的核心是JavaCC解析器生成器。PMD结合运用JavaCC和EBNF(扩展巴科斯-诺尔范式,Extended Backus-Naur Formal)语法,再加上JJTree,把Java源代码解析成抽象语法树(AST,Abstract Syntax Tree)。显然,这句话不那么好懂,且看下文具体说明。 <h51KPo^P
从根本上看,Java源代码只是一些普通的文本。不过,为了让解析器承认这些普通的文本是合法的Java代码,它们必须符合某种特定的结构要求。这种结构可以用一种称为EBNF的句法元语言表示,通常称为“语法”(Grammar)。JavaCC根据语法要求生成解析器,这个解析器就可以用于解析用Java编程语言编写的程序。 *+4iBpyiB
不过实际运行中的PMD还要经过JJTree的一次转换。JJTree是一个JavaCC的插件,通过AST扩充JavaCC生成的解析器。AST是一个Java符号流之上的语义层。有了JJTree,语法分析的结果不再是“System, ., out, ., . println”之类的符号序列,而是一个由对象构成的树型层次结构。例如,下面是一段简单的Java代码以及与之对应的AST。 ]?$
y}
Java源代码: 0\nhg5]?
public class Foo { KocXSh U
public void bar() { NV4W2thYo
System.out.println("hello world"); g GT,PP(k
} d}cJ5!d
} [+,U0OV,
对应的抽象语法树 (1j$*?iGA
CompilationUnit jdf)bO(9#
TypeDeclaration F)ld@Ydk=
ClassDeclaration &"%|`gE
UnmodifiedClassDeclaration e*6` dz@
ClassBody u>6/_^iq
ClassBodyDeclaration f+Li'?
MethodDeclaration @zF:{=+]+
ResultType an[~%vxw}
MethodDeclarator g*r;( H>e
FormalParameters +/86w59
Block nvVsO>2{ o
BlockStatement ,ri--<
Statement ,nRwwFd.
StatementExpression ]u ~Fn2
PrimaryExpression A!Ct,%
PrimaryPrefix $!!=fFX*y
Name )}\@BtcjA]
PrimarySuffix V9+"CB^
Arguments PVc|y.
ArgumentList G$9|aaf`1#
Expression kdPm # $-
PrimaryExpression
Rha3
PrimaryPrefix ps
y(]Pf
Literal 6x,=SW@4
:gaeb8`t
四、编写规则 DB'KIw
前面我们看到了Java源代码以及与之对应的对象层次结构。下面我们就要利用这些对象编写PMD规则检查代码存在的问题。 x0$:"68PW
一般地,一个PMD规则可以看成一个Visitor,它遍历AST,寻找多个对象之间的一种特定模式,这种模式表示代码存在的问题。问题模式可能简单也可能复杂,简单的如查找代码中是否包含new Thread关键词,复杂的如确定一个类是否正确覆盖了equals和hashcode。 ]m#M
wN$
下面是一个寻找空if语句的简单PMD规则。 Mv`L F
//扩展AbstractRule,以启用Visitor模式 ||ZufFO
public class EmptyIfStmtRule extends AbstractRule implements Rule { E(kb!Rz
//当源代码中出现一个Block,下面的方法被调用 ,bp pM
public Object visit(ASTBlock node, Object data){ <O)X89dFM
//如果父节点是一个if语句且代码块里面没有任何内容 L8xprHgL
if ((node.jjtGetParent().jjtGetParent() instanceof ASTIfStatement) (DP9
& b
&& node.jjtGetNumChildren()==0) { `Mp7})
//肯定代码存在问题。把一个RuleViolation加入到Report。 =&
:f+!1$
RuleContext ctx = (RuleContext)data; eEGcio}_I9
ctx.getReport().addRuleViolation(createRuleViolation(ctx, maEpT43f
node.getBeginLine())); Q<