@@ -27,6 +27,7 @@ import org.sonar.xoo.lang.SymbolReferencesSensor; | |||
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 class XooPlugin extends SonarPlugin { | |||
TestCaseSensor.class, | |||
CoveragePerTestSensor.class, | |||
DependencySensor.class, | |||
ChecksSensor.class, | |||
OneIssuePerLineSensor.class, | |||
OneIssueOnDirPerFileSensor.class, | |||
CreateIssueByInternalKeySensor.class | |||
); | |||
} | |||
} |
@@ -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<Check>[] ALL = new Class[] {TemplateRuleCheck.class}; | |||
void execute(SensorContext context, InputFile file, RuleKey ruleKey); | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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<Check> 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)); | |||
} | |||
} | |||
} | |||
} |
@@ -23,7 +23,9 @@ import org.sonar.api.rule.RuleStatus; | |||
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") |
@@ -27,6 +27,6 @@ public class XooPluginTest { | |||
@Test | |||
public void provide_extensions() { | |||
assertThat(new XooPlugin().getExtensions()).hasSize(18); | |||
assertThat(new XooPlugin().getExtensions()).hasSize(19); | |||
} | |||
} |
@@ -37,7 +37,7 @@ public class XooRulesDefinitionTest { | |||
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"); |
@@ -52,6 +52,7 @@ public class ActiveRulesProvider extends ProviderAdapter { | |||
newActiveRule.setSeverity(activeRule.severity()); | |||
newActiveRule.setLanguage(activeRule.language()); | |||
newActiveRule.setInternalKey(activeRule.internalKey()); | |||
newActiveRule.setTemplateRuleKey(activeRule.templateRuleKey()); | |||
// load parameters | |||
for (Entry<String, String> param : activeRule.params().entrySet()) { |
@@ -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.<String, String>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(); | |||
} | |||
} |
@@ -63,4 +63,11 @@ public interface ActiveRule { | |||
*/ | |||
@CheckForNull | |||
String internalKey(); | |||
/** | |||
* Optional rule key of the template rule. | |||
* @since 4.5.3 | |||
*/ | |||
@CheckForNull | |||
String templateRuleKey(); | |||
} |
@@ -28,6 +28,7 @@ import org.sonar.api.utils.SonarException; | |||
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 class Checks<C> { | |||
} | |||
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; | |||
} |
@@ -31,13 +31,14 @@ import java.util.Map; | |||
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<String, String> 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 class DefaultActiveRule implements ActiveRule { | |||
public String internalKey() { | |||
return internalKey; | |||
} | |||
@Override | |||
public String templateRuleKey() { | |||
return templateRuleKey; | |||
} | |||
} |
@@ -36,7 +36,7 @@ public class NewActiveRule { | |||
String name; | |||
String severity = Severity.defaultSeverity(); | |||
Map<String, String> params = new HashMap<String, String>(); | |||
String internalKey, language; | |||
String internalKey, language, templateRuleKey; | |||
private final ActiveRulesBuilder builder; | |||
NewActiveRule(ActiveRulesBuilder builder, RuleKey ruleKey) { | |||
@@ -59,6 +59,11 @@ public class NewActiveRule { | |||
return this; | |||
} | |||
public NewActiveRule setTemplateRuleKey(@Nullable String templateRuleKey) { | |||
this.templateRuleKey = templateRuleKey; | |||
return this; | |||
} | |||
public NewActiveRule setLanguage(@Nullable String language) { | |||
this.language = language; | |||
return this; |
@@ -114,9 +114,9 @@ public class CheckFactoryTest { | |||
} | |||
@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); |