From a2fa3e4651b30a3cb9284a34656ed8ecfc13d274 Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Mon, 9 Feb 2015 17:17:00 +0100 Subject: [PATCH] SONAR-6162 Fix NPE when searching for Check of custom rules --- .../main/java/org/sonar/xoo/XooPlugin.java | 3 +- .../main/java/org/sonar/xoo/checks/Check.java | 32 ++++++ .../sonar/xoo/checks/TemplateRuleCheck.java | 46 ++++++++ .../java/org/sonar/xoo/rule/ChecksSensor.java | 62 +++++++++++ .../sonar/xoo/rule/XooRulesDefinition.java | 5 + .../java/org/sonar/xoo/XooPluginTest.java | 2 +- .../xoo/rule/XooRulesDefinitionTest.java | 2 +- .../sonar/batch/rule/ActiveRulesProvider.java | 1 + .../mediumtest/issues/ChecksMediumTest.java | 105 ++++++++++++++++++ .../org/sonar/api/batch/rule/ActiveRule.java | 7 ++ .../java/org/sonar/api/batch/rule/Checks.java | 9 +- .../rule/internal/DefaultActiveRule.java | 8 +- .../batch/rule/internal/NewActiveRule.java | 7 +- .../api/batch/rule/CheckFactoryTest.java | 6 +- 14 files changed, 284 insertions(+), 11 deletions(-) create mode 100644 plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/checks/Check.java create mode 100644 plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/checks/TemplateRuleCheck.java create mode 100644 plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/ChecksSensor.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java index 102aa38dfb1b..6623cbb3866d 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java @@ -27,6 +27,7 @@ import org.sonar.xoo.lang.SyntaxHighlightingSensor; import org.sonar.xoo.lang.TestCaseSensor; import org.sonar.xoo.lang.XooTokenizerSensor; +import org.sonar.xoo.rule.ChecksSensor; import org.sonar.xoo.rule.CreateIssueByInternalKeySensor; import org.sonar.xoo.rule.OneIssueOnDirPerFileSensor; import org.sonar.xoo.rule.OneIssuePerLineSensor; @@ -72,11 +73,11 @@ public List getExtensions() { TestCaseSensor.class, CoveragePerTestSensor.class, DependencySensor.class, + ChecksSensor.class, OneIssuePerLineSensor.class, OneIssueOnDirPerFileSensor.class, CreateIssueByInternalKeySensor.class ); } - } diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/checks/Check.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/checks/Check.java new file mode 100644 index 000000000000..40911b81ee6f --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/checks/Check.java @@ -0,0 +1,32 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.xoo.checks; + +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.rule.RuleKey; + +public interface Check { + + public Class[] ALL = new Class[] {TemplateRuleCheck.class}; + + void execute(SensorContext context, InputFile file, RuleKey ruleKey); + +} diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/checks/TemplateRuleCheck.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/checks/TemplateRuleCheck.java new file mode 100644 index 000000000000..01fd6ad0c154 --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/checks/TemplateRuleCheck.java @@ -0,0 +1,46 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.xoo.checks; + +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.rule.RuleKey; +import org.sonar.check.Cardinality; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; + +@Rule(key = TemplateRuleCheck.RULE_KEY, cardinality = Cardinality.MULTIPLE, name = "Template rule", description = "Sample template rule") +public class TemplateRuleCheck implements Check { + + public static final String RULE_KEY = "TemplateRule"; + + @RuleProperty(key = "line") + private int line; + + @Override + public void execute(SensorContext sensorContext, InputFile file, RuleKey ruleKey) { + sensorContext.newIssue() + .onFile(file) + .ruleKey(ruleKey) + .atLine(line) + .save(); + } + +} diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/ChecksSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/ChecksSensor.java new file mode 100644 index 000000000000..249967d425e2 --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/ChecksSensor.java @@ -0,0 +1,62 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.xoo.rule; + +import org.sonar.api.batch.fs.FilePredicates; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.InputFile.Type; +import org.sonar.api.batch.rule.CheckFactory; +import org.sonar.api.batch.rule.Checks; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.xoo.Xoo; +import org.sonar.xoo.checks.Check; + +public class ChecksSensor implements Sensor { + + private final CheckFactory checkFactory; + + public ChecksSensor(CheckFactory checkFactory) { + this.checkFactory = checkFactory; + } + + @Override + public void describe(SensorDescriptor descriptor) { + descriptor + .name("ChecksSensor") + .workOnLanguages(Xoo.KEY) + .createIssuesForRuleRepositories(XooRulesDefinition.XOO_REPOSITORY) + .workOnFileTypes(InputFile.Type.MAIN); + } + + @Override + public void execute(SensorContext context) { + Checks checks = checkFactory.create(XooRulesDefinition.XOO_REPOSITORY); + checks.addAnnotatedChecks(Check.ALL); + FilePredicates p = context.fileSystem().predicates(); + for (InputFile file : context.fileSystem().inputFiles(p.and(p.hasLanguages(Xoo.KEY), p.hasType(Type.MAIN)))) { + for (Check check : checks.all()) { + check.execute(context, file, checks.ruleKey(check)); + } + } + } + +} diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java index 0f37cefcb425..7880f8a4b697 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java @@ -23,7 +23,9 @@ import org.sonar.api.rule.Severity; import org.sonar.api.server.rule.RuleParamType; import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.api.server.rule.RulesDefinitionAnnotationLoader; import org.sonar.xoo.Xoo; +import org.sonar.xoo.checks.Check; /** * Define all the coding rules that are supported on the repository named "xoo". @@ -36,6 +38,9 @@ public class XooRulesDefinition implements RulesDefinition { public void define(Context context) { NewRepository repository = context.createRepository(XOO_REPOSITORY, Xoo.KEY).setName("Xoo"); + // Load checks + new RulesDefinitionAnnotationLoader().load(repository, Check.ALL); + // define a single rule programmatically. Note that rules // can be loaded from JSON or XML files too. NewRule x1Rule = repository.createRule("x1") diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java index d5913f277a8e..63f20b2e6284 100644 --- a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java +++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java @@ -27,6 +27,6 @@ public class XooPluginTest { @Test public void provide_extensions() { - assertThat(new XooPlugin().getExtensions()).hasSize(18); + assertThat(new XooPlugin().getExtensions()).hasSize(19); } } diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java index 184e8d470743..86432b6cffb7 100644 --- a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java +++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java @@ -37,7 +37,7 @@ public void define_xoo_rules() { assertThat(repo).isNotNull(); assertThat(repo.name()).isEqualTo("Xoo"); assertThat(repo.language()).isEqualTo("xoo"); - assertThat(repo.rules()).hasSize(1); + assertThat(repo.rules()).hasSize(2); RulesDefinition.Rule x1 = repo.rule("x1"); assertThat(x1.key()).isEqualTo("x1"); diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java index a539b7440735..d90841ee8075 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java @@ -52,6 +52,7 @@ private ActiveRules load(ProjectReferentials ref) { newActiveRule.setSeverity(activeRule.severity()); newActiveRule.setLanguage(activeRule.language()); newActiveRule.setInternalKey(activeRule.internalKey()); + newActiveRule.setTemplateRuleKey(activeRule.templateRuleKey()); // load parameters for (Entry param : activeRule.params().entrySet()) { diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java new file mode 100644 index 000000000000..54c2aab58b4e --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java @@ -0,0 +1,105 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.issues; + +import com.google.common.collect.ImmutableMap; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.issue.Issue; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult; +import org.sonar.batch.protocol.input.ActiveRule; +import org.sonar.xoo.XooPlugin; + +import java.io.File; +import java.io.IOException; + +import static org.fest.assertions.Assertions.assertThat; + +public class ChecksMediumTest { + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .activateRule(new ActiveRule("xoo", "TemplateRule_1234", "TemplateRule", "A template rule", "MAJOR", null, "xoo").addParam("line", "1")) + .activateRule(new ActiveRule("xoo", "TemplateRule_1235", "TemplateRule", "Another template rule", "MAJOR", null, "xoo").addParam("line", "2")) + .bootstrapProperties(ImmutableMap.of("sonar.analysis.mode", "sensor")) + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void testCheckWithTemplate() throws IOException { + + File baseDir = temp.newFolder(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "foo"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + assertThat(result.issues()).hasSize(2); + + boolean foundIssueAtLine1 = false; + boolean foundIssueAtLine2 = false; + for (Issue issue : result.issues()) { + if (issue.line() == 1) { + foundIssueAtLine1 = true; + assertThat(issue.inputPath()).isEqualTo(new DefaultInputFile("com.foo.project", "src/sample.xoo")); + assertThat(issue.message()).isEqualTo("A template rule"); + } + if (issue.line() == 2) { + foundIssueAtLine2 = true; + assertThat(issue.inputPath()).isEqualTo(new DefaultInputFile("com.foo.project", "src/sample.xoo")); + assertThat(issue.message()).isEqualTo("Another template rule"); + } + } + assertThat(foundIssueAtLine1).isTrue(); + assertThat(foundIssueAtLine2).isTrue(); + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRule.java index 35447556df91..06091f372972 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRule.java @@ -63,4 +63,11 @@ public interface ActiveRule { */ @CheckForNull String internalKey(); + + /** + * Optional rule key of the template rule. + * @since 4.5.3 + */ + @CheckForNull + String templateRuleKey(); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Checks.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Checks.java index ab8ff4fbdec3..5cdc2aba2b34 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Checks.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Checks.java @@ -28,6 +28,7 @@ import org.sonar.check.RuleProperty; import javax.annotation.CheckForNull; + import java.lang.reflect.Field; import java.util.Arrays; import java.util.Collection; @@ -122,10 +123,12 @@ public Checks addAnnotatedChecks(Collection checkClassesOrObjects) { } for (ActiveRule activeRule : activeRules.findByRepository(repository)) { - String engineKey = StringUtils.defaultIfBlank(activeRule.internalKey(), activeRule.ruleKey().rule()); + String engineKey = StringUtils.defaultIfBlank(activeRule.templateRuleKey(), activeRule.ruleKey().rule()); Object checkClassesOrObject = checksByEngineKey.get(engineKey); - Object obj = instantiate(activeRule, checkClassesOrObject); - add(activeRule.ruleKey(), (C) obj); + if (checkClassesOrObject != null) { + Object obj = instantiate(activeRule, checkClassesOrObject); + add(activeRule.ruleKey(), (C) obj); + } } return this; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRule.java index 08d34525d69f..bfe7b6208285 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRule.java @@ -31,13 +31,14 @@ public class DefaultActiveRule implements ActiveRule { private final RuleKey ruleKey; private final String name; - private final String severity, internalKey, language; + private final String severity, internalKey, language, templateRuleKey; private final Map params; DefaultActiveRule(NewActiveRule newActiveRule) { this.severity = newActiveRule.severity; this.name = newActiveRule.name; this.internalKey = newActiveRule.internalKey; + this.templateRuleKey = newActiveRule.templateRuleKey; this.ruleKey = newActiveRule.ruleKey; this.params = ImmutableMap.copyOf(newActiveRule.params); this.language = newActiveRule.language; @@ -77,4 +78,9 @@ public Map params() { public String internalKey() { return internalKey; } + + @Override + public String templateRuleKey() { + return templateRuleKey; + } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewActiveRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewActiveRule.java index 3f20d89bf3f3..07e85dc9a76e 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewActiveRule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewActiveRule.java @@ -36,7 +36,7 @@ public class NewActiveRule { String name; String severity = Severity.defaultSeverity(); Map params = new HashMap(); - String internalKey, language; + String internalKey, language, templateRuleKey; private final ActiveRulesBuilder builder; NewActiveRule(ActiveRulesBuilder builder, RuleKey ruleKey) { @@ -59,6 +59,11 @@ public NewActiveRule setInternalKey(@Nullable String internalKey) { return this; } + public NewActiveRule setTemplateRuleKey(@Nullable String templateRuleKey) { + this.templateRuleKey = templateRuleKey; + return this; + } + public NewActiveRule setLanguage(@Nullable String language) { this.language = language; return this; diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/CheckFactoryTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/CheckFactoryTest.java index 42f8d7f66541..d802900f71bb 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/CheckFactoryTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/CheckFactoryTest.java @@ -114,9 +114,9 @@ public void param_as_inherited_field() { } @Test - public void use_engine_key() { - RuleKey ruleKey = RuleKey.of("squid", "One"); - builder.create(ruleKey).setInternalKey("S0001").activate(); + public void use_template_rule_key() { + RuleKey ruleKey = RuleKey.of("squid", "S0001_123"); + builder.create(ruleKey).setTemplateRuleKey("S0001").activate(); CheckFactory checkFactory = new CheckFactory(builder.build()); Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithKey.class);