aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfmallet <freddy.mallet@gmail.com>2010-09-15 15:01:17 +0000
committerfmallet <freddy.mallet@gmail.com>2010-09-15 15:01:17 +0000
commit83a555da8548a0b4e22874e8a11f91ce9cdabbe6 (patch)
tree8e0828193578057b3aae3580df8e121938c15a9f
parent5ac2f4fb7d93f5ce867125cbd4c27718029cc1d7 (diff)
downloadsonarqube-83a555da8548a0b4e22874e8a11f91ce9cdabbe6.tar.gz
sonarqube-83a555da8548a0b4e22874e8a11f91ce9cdabbe6.zip
SONAR-1766 : Definition of the XPath rule and first version of the export mechanism
-rw-r--r--plugins/sonar-pmd-plugin/pmd-result.xml2
-rw-r--r--plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdConstants.java3
-rw-r--r--plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdProfileExporter.java22
-rw-r--r--plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdProperty.java4
-rw-r--r--plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdRule.java63
-rw-r--r--plugins/sonar-pmd-plugin/src/main/resources/org/sonar/plugins/pmd/rules.xml34
-rw-r--r--plugins/sonar-pmd-plugin/src/test/java/org/sonar/plugins/pmd/PmdProfileExporterTest.java128
-rw-r--r--plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_simple.xml14
-rw-r--r--plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_xpath_rules.out.xml0
-rw-r--r--plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_xpath_rules.xml8
10 files changed, 215 insertions, 63 deletions
diff --git a/plugins/sonar-pmd-plugin/pmd-result.xml b/plugins/sonar-pmd-plugin/pmd-result.xml
index 61052e2878a..03947144c7f 100644
--- a/plugins/sonar-pmd-plugin/pmd-result.xml
+++ b/plugins/sonar-pmd-plugin/pmd-result.xml
@@ -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> \ No newline at end of file
diff --git a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdConstants.java b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdConstants.java
index ed7d5444940..682d706a039 100644
--- a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdConstants.java
+++ b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdConstants.java
@@ -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";
}
diff --git a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdProfileExporter.java b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdProfileExporter.java
index 6485c8c972f..23f84558299 100644
--- a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdProfileExporter.java
+++ b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdProfileExporter.java
@@ -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);
diff --git a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdProperty.java b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdProperty.java
index 38a29362161..91d1800c803 100644
--- a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdProperty.java
+++ b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdProperty.java
@@ -43,4 +43,8 @@ public class PmdProperty {
public String getValue() {
return value;
}
+
+ public void setValue(String value) {
+ this.value = value;
+ }
}
diff --git a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdRule.java b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdRule.java
index 0523bbeadcc..3a15b33b487 100644
--- a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdRule.java
+++ b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdRule.java
@@ -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;
+ }
} \ No newline at end of file
diff --git a/plugins/sonar-pmd-plugin/src/main/resources/org/sonar/plugins/pmd/rules.xml b/plugins/sonar-pmd-plugin/src/main/resources/org/sonar/plugins/pmd/rules.xml
index e05df7e66dd..bb48e02abef 100644
--- a/plugins/sonar-pmd-plugin/src/main/resources/org/sonar/plugins/pmd/rules.xml
+++ b/plugins/sonar-pmd-plugin/src/main/resources/org/sonar/plugins/pmd/rules.xml
@@ -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> \ No newline at end of file
diff --git a/plugins/sonar-pmd-plugin/src/test/java/org/sonar/plugins/pmd/PmdProfileExporterTest.java b/plugins/sonar-pmd-plugin/src/test/java/org/sonar/plugins/pmd/PmdProfileExporterTest.java
index c83b01bf8a4..99c261afd18 100644
--- a/plugins/sonar-pmd-plugin/src/test/java/org/sonar/plugins/pmd/PmdProfileExporterTest.java
+++ b/plugins/sonar-pmd-plugin/src/test/java/org/sonar/plugins/pmd/PmdProfileExporterTest.java
@@ -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;
+ }
}
-
}
diff --git a/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_simple.xml b/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_simple.xml
new file mode 100644
index 00000000000..0d1527930bb
--- /dev/null
+++ b/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_simple.xml
@@ -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> \ No newline at end of file
diff --git a/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_xpath_rules.out.xml b/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_xpath_rules.out.xml
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_xpath_rules.out.xml
diff --git a/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_xpath_rules.xml b/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_xpath_rules.xml
new file mode 100644
index 00000000000..33e7ce9f1cd
--- /dev/null
+++ b/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_xpath_rules.xml
@@ -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> \ No newline at end of file