Browse Source

SONAR-1766 : Definition of the XPath rule and first version of the export mechanism

tags/2.6
fmallet 13 years ago
parent
commit
83a555da85

+ 1
- 1
plugins/sonar-pmd-plugin/pmd-result.xml View File

@@ -1,3 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<pmd version="4.2.5" timestamp="2010-09-15T13:59:35.931">
<pmd version="4.2.5" timestamp="2010-09-15T16:58:28.966">
</pmd>

+ 3
- 0
plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdConstants.java View File

@@ -27,4 +27,7 @@ public final class PmdConstants {
public static final String REPOSITORY_NAME = "PMD";
public static final String PLUGIN_NAME = "PMD";
public static final String PLUGIN_KEY = CoreProperties.PMD_PLUGIN;
public static final String XPATH_CLASS ="net.sourceforge.pmd.rules.XPathRule";
public static final String XPATH_EXPRESSION_PARAM ="xpath";
public static final String XPATH_MESSAGE_PARAM ="message";
}

+ 17
- 5
plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdProfileExporter.java View File

@@ -30,7 +30,6 @@ import org.sonar.api.profiles.RulesProfile;
import org.sonar.api.resources.Java;
import org.sonar.api.rules.ActiveRule;
import org.sonar.api.rules.ActiveRuleParam;
import org.sonar.api.rules.RulePriority;
import org.sonar.api.utils.SonarException;
import org.sonar.plugins.pmd.xml.PmdProperty;
import org.sonar.plugins.pmd.xml.PmdRule;
@@ -49,15 +48,15 @@ public class PmdProfileExporter extends ProfileExporter {
@Override
public void exportProfile(RulesProfile profile, Writer writer) {
try {
PmdRuleset tree = buildModuleTree(profile.getActiveRulesByRepository(PmdConstants.REPOSITORY_KEY), profile.getName());
String xmlModules = buildXmlFromModuleTree(tree);
PmdRuleset tree = createPmdRuleset(profile.getActiveRulesByRepository(PmdConstants.REPOSITORY_KEY), profile.getName());
String xmlModules = exportPmdRulesetToXml(tree);
writer.append(xmlModules);
} catch (IOException e) {
throw new SonarException("Fail to export the profile " + profile, e);
}
}

protected PmdRuleset buildModuleTree(List<ActiveRule> activeRules, String profileName) {
protected PmdRuleset createPmdRuleset(List<ActiveRule> activeRules, String profileName) {
PmdRuleset ruleset = new PmdRuleset(profileName);
for (ActiveRule activeRule : activeRules) {
if (activeRule.getRule().getPluginName().equals(CoreProperties.PMD_PLUGIN)) {
@@ -72,12 +71,25 @@ public class PmdProfileExporter extends ProfileExporter {
}
rule.setProperties(properties);
ruleset.addRule(rule);
processXPathRule(activeRule.getRuleKey(), rule);
}
}
return ruleset;
}

protected String buildXmlFromModuleTree(PmdRuleset tree) {
protected void processXPathRule(String sonarRuleKey, PmdRule rule) {
if (PmdConstants.XPATH_CLASS.equals(rule.getRef())) {
rule.setRef(null);
rule.setMessage(rule.getProperty(PmdConstants.XPATH_MESSAGE_PARAM).getValue());
rule.removeProperty(PmdConstants.XPATH_MESSAGE_PARAM);
PmdProperty xpathExp = rule.getProperty(PmdConstants.XPATH_EXPRESSION_PARAM);
xpathExp.setValue("" + xpathExp.getValue() + "");
rule.setClazz(PmdConstants.XPATH_CLASS);
rule.setName(sonarRuleKey);
}
}

protected String exportPmdRulesetToXml(PmdRuleset tree) {
XStream xstream = new XStream();
xstream.setClassLoader(getClass().getClassLoader());
xstream.processAnnotations(PmdRuleset.class);

+ 4
- 0
plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdProperty.java View File

@@ -43,4 +43,8 @@ public class PmdProperty {
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

+ 55
- 8
plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdRule.java View File

@@ -19,13 +19,13 @@
*/
package org.sonar.plugins.pmd.xml;
import java.util.ArrayList;
import java.util.List;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
import java.util.ArrayList;
import java.util.List;
@XStreamAlias("rule")
public class PmdRule implements Comparable<String> {
@@ -34,20 +34,26 @@ public class PmdRule implements Comparable<String> {
private String priority;
@XStreamAsAttribute
private String name;
@XStreamAsAttribute
private String message;
private List<PmdProperty> properties = new ArrayList<PmdProperty>();
@XStreamOmitField
private String description; //NOSONAR unused private field
private String description; // NOSONAR unused private field
@XStreamOmitField
private String exclude;//NOSONAR unused private field
private String exclude;// NOSONAR unused private field
@XStreamOmitField
private String example;//NOSONAR unused private field
private String example;// NOSONAR unused private field
@XStreamOmitField
@XStreamAsAttribute
@XStreamAlias(value = "class")
private String clazz;//NOSONAR unused private field
private String clazz;// NOSONAR unused private field
public PmdRule(String ref) {
this(ref, null);
@@ -70,6 +76,15 @@ public class PmdRule implements Comparable<String> {
return properties;
}
public PmdProperty getProperty(String propertyName) {
for (PmdProperty prop : properties) {
if (propertyName.equals(prop.getName())) {
return prop;
}
}
return null;
}
public int compareTo(String o) {
return o.compareTo(ref);
}
@@ -89,4 +104,36 @@ public class PmdRule implements Comparable<String> {
properties.add(property);
}
public void setName(String name) {
this.name = name;
}
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public String getClazz() {
return clazz;
}
public void setRef(String ref) {
this.ref = ref;
}
public void removeProperty(String propertyName) {
PmdProperty prop = getProperty(propertyName);
properties.remove(prop);
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
public String getName() {
return name;
}
}

+ 34
- 0
plugins/sonar-pmd-plugin/src/main/resources/org/sonar/plugins/pmd/rules.xml View File

@@ -2133,4 +2133,38 @@ public abstract class ShouldBeAbstract

</param>
</rule>
<rule key="XPathRule" priority="MAJOR">
<name><![CDATA[XPath rule template]]></name>
<configKey><![CDATA[net.sourceforge.pmd.rules.XPathRule]]></configKey>
<category name="Maintainability"/>
<cardinality>MULTIPLE</cardinality>
<description>
<![CDATA[PMD provides a very handy method for creating new rules by writing an XPath query. When the XPath query finds a match, a violation is created.
Let's take a simple example : assume we have a Factory class that must be always declared final.
We'd like to report a violation each time a declaration of Factory is not declared final. Consider the following class: :
<pre>
public class a {
Factory f1;

void myMethod() {
Factory f2;
int a;
}
}
</pre>
The following expression does the magic we need:
<pre>
//VariableDeclarator
[../Type/ReferenceType/ClassOrInterfaceType
[@Image = 'Factory'] and ..[@Final='false']]
</pre>
See the <a href="http://pmd.sourceforge.net/xpathruletutorial.html">XPath rule tutorial</a> for more information.]]>
</description>
<param key="xpath" type="s">
<description><![CDATA[XPath expressions.]]></description>
</param>
<param key="message" type="s">
<description><![CDATA[Message to display when a violation occurs.]]></description>
</param>
</rule>
</rules>

+ 79
- 49
plugins/sonar-pmd-plugin/src/test/java/org/sonar/plugins/pmd/PmdProfileExporterTest.java View File

@@ -1,21 +1,28 @@
package org.sonar.plugins.pmd;

import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.sonar.api.CoreProperties;
import org.sonar.api.platform.ServerFileSystem;
import org.sonar.api.profiles.RulesProfile;
import org.sonar.api.rules.ActiveRule;
import org.sonar.api.rules.ActiveRuleParam;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RuleParam;
import org.sonar.api.rules.RulePriority;
import org.sonar.api.rules.RuleFinder;
import org.sonar.api.rules.RuleQuery;
import org.sonar.api.utils.ValidationMessages;
import org.sonar.plugins.pmd.xml.PmdProperty;
import org.sonar.plugins.pmd.xml.PmdRule;
import org.sonar.test.TestUtils;
import org.xml.sax.SAXException;

@@ -25,57 +32,80 @@ public class PmdProfileExporterTest {

@Test
public void testExportProfile() throws IOException, SAXException {
List<ActiveRule> activeRulesExpected = buildActiveRulesFixture(buildRulesFixture());
RulesProfile activeProfile = new RulesProfile();
activeProfile.setActiveRules(activeRulesExpected);
activeProfile.setName("A test profile");
StringWriter xmlOutput = new StringWriter();
exporter.exportProfile(activeProfile, xmlOutput);
assertXmlAreSimilar(xmlOutput.toString(), "test_xml_complete.xml");
}
ServerFileSystem fileSystem = mock(ServerFileSystem.class);
PmdRuleRepository repository = new PmdRuleRepository(fileSystem);
List<Rule> rules = repository.createRules();

private List<org.sonar.api.rules.Rule> buildRulesFixture() {
final Rule rule1 = new Rule("Coupling Between Objects", "CouplingBetweenObjects",
"rulesets/coupling.xml/CouplingBetweenObjects", null, CoreProperties.PMD_PLUGIN, null);
RuleParam ruleParam1 = new RuleParam(rule1, "threshold", null, "i");
rule1.setParams(Arrays.asList(ruleParam1));
RuleFinder ruleFinder = new PmdRuleFinder(rules);
PmdProfileImporter importer = new PmdProfileImporter(ruleFinder);
Reader reader = new StringReader(TestUtils.getResourceContent("/org/sonar/plugins/pmd/simple.xml"));
RulesProfile rulesProfile = importer.importProfile(reader, ValidationMessages.create());

final Rule rule2 = new Rule("Excessive Imports", "ExcessiveImports",
"rulesets/coupling.xml/ExcessiveImports", null, CoreProperties.PMD_PLUGIN, null);
RuleParam ruleParam2 = new RuleParam(rule2, "max", null, "i");
rule2.setParams(Arrays.asList(ruleParam2));

final Rule rule3 = new Rule("Use Notify All Instead Of Notify", "UseNotifyAllInsteadOfNotify",
"rulesets/design.xml/UseNotifyAllInsteadOfNotify", null, CoreProperties.PMD_PLUGIN, null);
StringWriter xmlOutput = new StringWriter();
exporter.exportProfile(rulesProfile, xmlOutput);
assertEquals(TestUtils.getResourceContent("/org/sonar/plugins/pmd/export_simple.xml"), xmlOutput.toString());
}

final org.sonar.api.rules.Rule rule4 = new org.sonar.api.rules.Rule("Class names should always begin with an upper case character.",
"ClassNamingConventions",
"rulesets/naming.xml/ClassNamingConventions", null, CoreProperties.PMD_PLUGIN, null);
@Test
public void testExportXPathRule() {
StringWriter xmlOutput = new StringWriter();
RulesProfile profile = RulesProfile.create();
Rule xpathTemplate = Rule.create(PmdConstants.REPOSITORY_KEY, "MyOwnRule", "This is my own xpath rule.")
.setConfigKey(PmdConstants.XPATH_CLASS).setPluginName(PmdConstants.REPOSITORY_KEY);
xpathTemplate.createParameter(PmdConstants.XPATH_EXPRESSION_PARAM);
xpathTemplate.createParameter(PmdConstants.XPATH_MESSAGE_PARAM);
ActiveRule xpath = profile.activateRule(xpathTemplate, null);
xpath.setParameter(PmdConstants.XPATH_EXPRESSION_PARAM, "//FieldDeclaration");
xpath.setParameter(PmdConstants.XPATH_MESSAGE_PARAM, "This is bad");
exporter.exportProfile(profile, xmlOutput);
System.out.println(xmlOutput.toString());
assertEquals(TestUtils.getResourceContent("/org/sonar/plugins/pmd/export_xpath_rules.xml"), xmlOutput.toString());
}

return Arrays.asList(rule1, rule2, rule3, rule4);
@Test
public void testProcessingXPathRule() {
String message = "This is bad";
String xpathExpression = "xpathExpression";

PmdRule rule = new PmdRule(PmdConstants.XPATH_CLASS);
rule.addProperty(new PmdProperty(PmdConstants.XPATH_EXPRESSION_PARAM, xpathExpression));
rule.addProperty(new PmdProperty(PmdConstants.XPATH_MESSAGE_PARAM, message));
rule.setName("MyOwnRule");

exporter.processXPathRule("xpathKey", rule);

assertThat(rule.getMessage(), is(message));
assertThat(rule.getRef(), is(nullValue()));
assertThat(rule.getClazz(), is(PmdConstants.XPATH_CLASS));
assertThat(rule.getProperty(PmdConstants.XPATH_MESSAGE_PARAM), is(nullValue()));
assertThat(rule.getName(), is("xpathKey"));
assertThat(rule.getProperty(PmdConstants.XPATH_EXPRESSION_PARAM).getValue(), is(xpathExpression));
}

private List<ActiveRule> buildActiveRulesFixture(List<org.sonar.api.rules.Rule> rules) {
List<ActiveRule> activeRules = new ArrayList<ActiveRule>();
private static class PmdRuleFinder implements RuleFinder {

ActiveRule activeRule1 = new ActiveRule(null, rules.get(0), RulePriority.CRITICAL);
activeRule1.setActiveRuleParams(Arrays.asList(new ActiveRuleParam(activeRule1, rules.get(0).getParams().get(0), "20")));
activeRules.add(activeRule1);
private List<Rule> rules;

ActiveRule activeRule2 = new ActiveRule(null, rules.get(1), RulePriority.MAJOR);
activeRule2.setActiveRuleParams(Arrays.asList(new ActiveRuleParam(activeRule2, rules.get(1).getParams().get(0), "30")));
activeRules.add(activeRule2);
public PmdRuleFinder(List<Rule> rules) {
this.rules = rules;
}

ActiveRule activeRule3 = new ActiveRule(null, rules.get(2), RulePriority.MINOR);
activeRules.add(activeRule3);
public Rule findByKey(String repositoryKey, String key) {
throw new UnsupportedOperationException();
}

return activeRules;
}
public Collection<Rule> findAll(RuleQuery query) {
throw new UnsupportedOperationException();
}

private void assertXmlAreSimilar(String xml, String xmlFileToFind) throws IOException, SAXException {
InputStream input = getClass().getResourceAsStream("/org/sonar/plugins/pmd/" + xmlFileToFind);
String xmlToFind = IOUtils.toString(input);
TestUtils.assertSimilarXml(xmlToFind, xml);
public Rule find(RuleQuery query) {
for (Rule rule : rules) {
if (query.getConfigKey().equals(rule.getConfigKey())) {
rule.setPluginName(PmdConstants.REPOSITORY_KEY);
return rule;
}
}
return null;
}
}

}

+ 14
- 0
plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_simple.xml View File

@@ -0,0 +1,14 @@
<ruleset>
<rule ref="rulesets/coupling.xml/CouplingBetweenObjects">
<priority>2</priority>
<properties>
<property name="threshold" value="20"/>
</properties>
</rule>
<rule ref="rulesets/coupling.xml/ExcessiveImports">
<priority>3</priority>
</rule>
<rule ref="rulesets/design.xml/UseNotifyAllInsteadOfNotify">
<priority>4</priority>
</rule>
</ruleset>

+ 0
- 0
plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_xpath_rules.out.xml View File


+ 8
- 0
plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_xpath_rules.xml View File

@@ -0,0 +1,8 @@
<ruleset>
<rule name="MyOwnRule" message="This is bad" class="net.sourceforge.pmd.rules.XPathRule">
<priority>3</priority>
<properties>
<property name="xpath" value="//FieldDeclaration"/>
</properties>
</rule>
</ruleset>

Loading…
Cancel
Save