diff options
author | fmallet <freddy.mallet@gmail.com> | 2010-09-15 15:01:17 +0000 |
---|---|---|
committer | fmallet <freddy.mallet@gmail.com> | 2010-09-15 15:01:17 +0000 |
commit | 83a555da8548a0b4e22874e8a11f91ce9cdabbe6 (patch) | |
tree | 8e0828193578057b3aae3580df8e121938c15a9f | |
parent | 5ac2f4fb7d93f5ce867125cbd4c27718029cc1d7 (diff) | |
download | sonarqube-83a555da8548a0b4e22874e8a11f91ce9cdabbe6.tar.gz sonarqube-83a555da8548a0b4e22874e8a11f91ce9cdabbe6.zip |
SONAR-1766 : Definition of the XPath rule and first version of the export mechanism
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 |