XPath has a bunch of handy functions that you can use for writing PMD rules. For example, the AbstractNaming rule finds abstract classes that aren't named something like "AbstractFoo" using the starts-with function:
//ClassOrInterfaceDeclaration
[@Abstract='true' and @Interface='false']
[not (starts-with(@Image,'Abstract'))]
But for real power, you need regular expressions - and thanks to Jaxen's extension functions and Jakarta ORO, PMD supports that now! Here's an example; this would find classes named 'Foo' or 'Bar':
//ClassOrInterfaceDeclaration
[regexp(@Image,'^(Foo|Bar)$')]
Note the use of the new regexp function - that's where the magic happens. For a more complicated example, here's a rule that checks for hardcoded IP addresses:
//PrimaryExpression/PrimaryPrefix/Literal
[matches(@Image,
'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)
\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)
\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)
\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'
)]
Props to both the Jaxen and the ORO guys for their nice APIs; adding this regular expression support took only about 40 lines of code. Very nice!
More details on this feature are in the RFE. And for more info on PMD, get the book!
Updated 1/5/06: function is called 'matches' now per the XPath 2.0 spec. Thanks to Daniel Sheppard for the pointer!
XPath already has a function defined called "matches". You should already be able to do:
//ClassOrInterfaceDeclaration
[matches(@Image,'^(Foo|Bar)$')]
Without any extensions - if Jaxen doesn't actually implement that XPath function, you should probably call your new function "matches". Unless, of course, the ORO engine doesn't match the regex spec in the xpath-functions spec.
http://www.w3.org/TR/xpath-functions/#func-matches
Posted by: Daniel Sheppard | December 29, 2005 at 05:43 PM
Very interesting! Jaxen doesn't implement that function; I guess that's because it's a XPath 2.0 function. Hm. Now's the time to rename it, I reckon, before the 3.5 release... I'll propose that. Thank Daniel!
Posted by: tomcopeland | December 29, 2005 at 05:53 PM
It's renamed 'matches' now in CVS... good times. Thanks Daniel!
Posted by: tomcopeland | January 05, 2006 at 01:57 PM
What is the point of the Abstract Naming rule? In my humble opinion this has no added value. Please explain.
Posted by: anonymous | August 28, 2006 at 10:26 AM
anonymous - it's for checking a naming convention - ensuring that abstract classes are prefixed with "Abstract"; in some shops that's considered a good practice.
Posted by: tomcopeland | August 28, 2006 at 04:08 PM
Hi All,
I need to call a method on a java object if that kind of object is created in my class file.
Sample code can be like this:
public class TestClass {
public void test() {
MyObjectA objA;
MyObjectB objB = objA.createObjB();
String str = new String("Vipul");
str = null; //Just like that so that more BlockStatements are creating in between
objB.cleanup();
}
}
So the intention is to create a rule that can detect that if an object of type MyObjectB is created, we should make sure that we call cleanup() on this object in this class file.
I am wonderinf if this is possible using XPathRule?
I tried creating an XPathRule like this:
//Type/ReferenceType/ClassOrInterfaceType[
(@Image = 'FormContext')
and
(../../../descendant::Name[ends-with(@Image,'createFormContext')])
and
(
(not
(contains
(
(./ancestor::Block/descendant::StatementExpression/descendant::Name/attribute::Image),
concat(../../../VariableDeclarator/VariableDeclaratorId/attribute::Image,'.release')
)
)
)
)
]
But this does not works properly. If I introduce any statement before cleanup() is called on objB, this rule fails.
It stops at first occurence of (./ancestor::Block/descendant::StatementExpression/descendant::Name/attribute::Image and never reaches till the intended place that is 'objB.cleanup'.
The problem is that I am not able to figure out how can make sure my XPathRule does not stop on encountrring first occurrence of the path '(./ancestor::Block/descendant::StatementExpression/descendant::Name/attribute::Image' and keeps moving ahead with all combinations in which it ultimately also finds objB.cleanup no matter if it is inside if, while etc or if there are any other statements in between creation of objB and before cleanup() is called on it.
I would really appreciate if someone can provide a hint to acheive this or tell me if it feasible through XPath.
Thanks in Advance!!!
Posted by: Vipul | March 26, 2009 at 06:18 PM