소스 검색

SONAR-4908 add template rules and param types

tags/4.2
Simon Brandhof 10 년 전
부모
커밋
f5521caa09

+ 35
- 8
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitions.java 파일 보기

@@ -168,6 +168,7 @@ public interface RuleDefinitions extends ServerExtension {
this.name = newRepository.name;
ImmutableMap.Builder<String, Rule> ruleBuilder = ImmutableMap.builder();
for (NewRule newRule : newRepository.newRules.values()) {
newRule.validate();
ruleBuilder.put(newRule.key, new Rule(newRule));
}
this.rulesByKey = ruleBuilder.build();
@@ -221,22 +222,25 @@ public interface RuleDefinitions extends ServerExtension {
static class NewRule {
private final String repoKey, key;
private String name, htmlDescription, metadata, defaultSeverity = Severity.MAJOR;
private boolean template;
private final Set<String> tags = Sets.newHashSet();
private final Map<String, NewParam> paramsByKey = Maps.newHashMap();

private NewRule(String repoKey, String key) {
this.repoKey = repoKey;
this.key = this.name = key;
this.key = key;
}

public NewRule setName(String s) {
if (StringUtils.isBlank(s)) {
throw new IllegalArgumentException(String.format("Name of rule %s is blank", this));
}
this.name = s;
return this;
}

public NewRule setTemplate(boolean template) {
this.template = template;
return this;
}

public NewRule setDefaultSeverity(String s) {
if (!Severity.ALL.contains(s)) {
throw new IllegalArgumentException(String.format("Default severity of rule %s is not correct: %s", this, s));
@@ -246,9 +250,6 @@ public interface RuleDefinitions extends ServerExtension {
}

public NewRule setHtmlDescription(String s) {
if (StringUtils.isBlank(s)) {
throw new IllegalArgumentException(String.format("HTML description of rule %s is blank", this));
}
this.htmlDescription = s;
return this;
}
@@ -292,6 +293,15 @@ public interface RuleDefinitions extends ServerExtension {
return this;
}

private void validate() {
if (StringUtils.isBlank(name)) {
throw new IllegalStateException(String.format("Name of rule %s is empty", this));
}
if (StringUtils.isBlank(htmlDescription)) {
throw new IllegalStateException(String.format("HTML description of rule %s is empty", this));
}
}

@Override
public String toString() {
return String.format("[repository=%s, key=%s]", repoKey, key);
@@ -300,6 +310,7 @@ public interface RuleDefinitions extends ServerExtension {

static class Rule {
private final String repoKey, key, name, htmlDescription, metadata, defaultSeverity;
private final boolean template;
private final Set<String> tags;
private final Map<String, Param> params;

@@ -310,6 +321,7 @@ public interface RuleDefinitions extends ServerExtension {
this.htmlDescription = newRule.htmlDescription;
this.metadata = newRule.metadata;
this.defaultSeverity = newRule.defaultSeverity;
this.template = newRule.template;
this.tags = ImmutableSet.copyOf(newRule.tags);
ImmutableMap.Builder<String, Param> paramsBuilder = ImmutableMap.builder();
for (NewParam newParam : newRule.paramsByKey.values()) {
@@ -335,6 +347,10 @@ public interface RuleDefinitions extends ServerExtension {
return htmlDescription;
}

public boolean template() {
return template;
}

@CheckForNull
public Param param(String key) {
return params.get(key);
@@ -385,7 +401,7 @@ public interface RuleDefinitions extends ServerExtension {
static class NewParam {
private final String key;
private String name, description, defaultValue;
// TODO type
private RuleParamType type = RuleParamType.STRING;

private NewParam(String key) {
this.key = this.name = key;
@@ -397,6 +413,11 @@ public interface RuleDefinitions extends ServerExtension {
return this;
}

public NewParam setType(RuleParamType t) {
this.type = t;
return this;
}

/**
* Plain-text description. Can be null.
*/
@@ -413,12 +434,14 @@ public interface RuleDefinitions extends ServerExtension {

static class Param {
private final String key, name, description, defaultValue;
private final RuleParamType type;

private Param(NewParam newParam) {
this.key = newParam.key;
this.name = newParam.name;
this.description = newParam.description;
this.defaultValue = newParam.defaultValue;
this.type = newParam.type;
}

public String key() {
@@ -439,6 +462,10 @@ public interface RuleDefinitions extends ServerExtension {
return defaultValue;
}

public RuleParamType type() {
return type;
}

@Override
public boolean equals(Object o) {
if (this == o) {

+ 61
- 0
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleParamType.java 파일 보기

@@ -0,0 +1,61 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2013 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.api.rule;

/**
* @since 4.2
*/
public final class RuleParamType {

public static final RuleParamType STRING = new RuleParamType("STRING");
public static final RuleParamType TEXT = new RuleParamType("TEXT");
public static final RuleParamType BOOLEAN = new RuleParamType("BOOLEAN");
public static final RuleParamType INTEGER = new RuleParamType("INTEGER");
public static final RuleParamType REGULAR_EXPRESSION = new RuleParamType("REGULAR_EXPRESSION");

private final String key;

private RuleParamType(String key) {
this.key = key;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

RuleParamType that = (RuleParamType) o;
return key.equals(that.key);
}

@Override
public int hashCode() {
return key.hashCode();
}

@Override
public String toString() {
return key;
}
}

+ 19
- 13
sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleDefinitionsTest.java 파일 보기

@@ -62,12 +62,13 @@ public class RuleDefinitionsTest {
RuleDefinitions.NewRepository newFindbugs = context.newRepository("findbugs", "java");
newFindbugs.newRule("NPE")
.setName("Detect NPE")
.setHtmlDescription("Detect <code>NPE</code>")
.setHtmlDescription("Detect <code>java.lang.NullPointerException</code>")
.setDefaultSeverity(Severity.BLOCKER)
.setMetadata("/something")
.setTags("one", "two")
.addTags("two", "three", "four");
newFindbugs.newRule("ABC");
newFindbugs.newRule("ABC").setName("ABC").setHtmlDescription("ABC");
newFindbugs.done();

RuleDefinitions.Repository findbugs = context.repository("findbugs");
@@ -81,6 +82,7 @@ public class RuleDefinitionsTest {
assertThat(npeRule.tags()).containsOnly("one", "two", "three", "four");
assertThat(npeRule.params()).isEmpty();
assertThat(npeRule.metadata()).isEqualTo("/something");
assertThat(npeRule.template()).isFalse();
assertThat(npeRule.toString()).isEqualTo("[repository=findbugs, key=NPE]");

// test equals() and hashCode()
@@ -92,14 +94,12 @@ public class RuleDefinitionsTest {
@Test
public void define_rule_with_default_fields() {
RuleDefinitions.NewRepository newFindbugs = context.newRepository("findbugs", "java");
newFindbugs.newRule("NPE");
newFindbugs.newRule("NPE").setName("NPE").setHtmlDescription("NPE");
newFindbugs.done();

RuleDefinitions.Rule rule = context.repository("findbugs").rule("NPE");
assertThat(rule.key()).isEqualTo("NPE");
assertThat(rule.name()).isEqualTo("NPE");
assertThat(rule.defaultSeverity()).isEqualTo(Severity.MAJOR);
assertThat(rule.htmlDescription()).isNull();
assertThat(rule.params()).isEmpty();
assertThat(rule.metadata()).isNull();
assertThat(rule.tags()).isEmpty();
@@ -108,8 +108,8 @@ public class RuleDefinitionsTest {
@Test
public void define_rule_parameters() {
RuleDefinitions.NewRepository newFindbugs = context.newRepository("findbugs", "java");
RuleDefinitions.NewRule newNpe = newFindbugs.newRule("NPE");
newNpe.newParam("level").setDefaultValue("LOW").setName("Level").setDescription("The level");
RuleDefinitions.NewRule newNpe = newFindbugs.newRule("NPE").setName("NPE").setHtmlDescription("NPE");
newNpe.newParam("level").setDefaultValue("LOW").setName("Level").setDescription("The level").setType(RuleParamType.INTEGER);
newNpe.newParam("effort");
newFindbugs.done();

@@ -121,11 +121,13 @@ public class RuleDefinitionsTest {
assertThat(level.name()).isEqualTo("Level");
assertThat(level.description()).isEqualTo("The level");
assertThat(level.defaultValue()).isEqualTo("LOW");
assertThat(level.type()).isEqualTo(RuleParamType.INTEGER);

RuleDefinitions.Param effort = rule.param("effort");
assertThat(effort.key()).isEqualTo("effort").isEqualTo(effort.name());
assertThat(effort.description()).isNull();
assertThat(effort.defaultValue()).isNull();
assertThat(effort.type()).isEqualTo(RuleParamType.STRING);

// test equals() and hashCode()
assertThat(level).isEqualTo(level).isNotEqualTo(effort).isNotEqualTo("level").isNotEqualTo(null);
@@ -138,7 +140,7 @@ public class RuleDefinitionsTest {

// for example fb-contrib
RuleDefinitions.NewExtendedRepository newFindbugs = context.extendRepository("findbugs");
newFindbugs.newRule("NPE");
newFindbugs.newRule("NPE").setName("NPE").setHtmlDescription("NPE");
newFindbugs.done();

assertThat(context.repositories()).isEmpty();
@@ -187,11 +189,13 @@ public class RuleDefinitionsTest {

@Test
public void fail_if_blank_rule_name() {
RuleDefinitions.NewRepository newRepository = context.newRepository("findbugs", "java");
newRepository.newRule("NPE").setName(null).setHtmlDescription("NPE");
try {
context.newRepository("findbugs", "java").newRule("NPE").setName(null);
newRepository.done();
fail();
} catch (IllegalArgumentException e) {
assertThat(e).hasMessage("Name of rule [repository=findbugs, key=NPE] is blank");
} catch (IllegalStateException e) {
assertThat(e).hasMessage("Name of rule [repository=findbugs, key=NPE] is empty");
}
}

@@ -208,11 +212,13 @@ public class RuleDefinitionsTest {

@Test
public void fail_if_blank_rule_html_description() {
RuleDefinitions.NewRepository newRepository = context.newRepository("findbugs", "java");
newRepository.newRule("NPE").setName("NPE").setHtmlDescription(null);
try {
context.newRepository("findbugs", "java").newRule("NPE").setHtmlDescription(null);
newRepository.done();
fail();
} catch (IllegalArgumentException e) {
assertThat(e).hasMessage("HTML description of rule [repository=findbugs, key=NPE] is blank");
} catch (IllegalStateException e) {
assertThat(e).hasMessage("HTML description of rule [repository=findbugs, key=NPE] is empty");
}
}


+ 45
- 0
sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleParamTypeTest.java 파일 보기

@@ -0,0 +1,45 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2013 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.api.rule;

import org.junit.Test;

import static org.fest.assertions.Assertions.assertThat;

public class RuleParamTypeTest {
@Test
public void testEquals() throws Exception {
assertThat(RuleParamType.INTEGER)
.isEqualTo(RuleParamType.INTEGER)
.isNotEqualTo(RuleParamType.STRING)
.isNotEqualTo("INTEGER")
.isNotEqualTo(null);
}

@Test
public void testHashCode() throws Exception {
assertThat(RuleParamType.INTEGER.hashCode()).isEqualTo(RuleParamType.INTEGER.hashCode());
}

@Test
public void testToString() throws Exception {
assertThat(RuleParamType.INTEGER.toString()).isEqualTo("INTEGER");
}
}

+ 2
- 0
sonar-server/src/main/java/org/sonar/server/platform/Platform.java 파일 보기

@@ -91,6 +91,7 @@ import org.sonar.server.permission.InternalPermissionTemplateService;
import org.sonar.server.permission.PermissionFinder;
import org.sonar.server.plugins.*;
import org.sonar.server.qualityprofile.*;
import org.sonar.server.rule.DeprecatedRuleDefinitions;
import org.sonar.server.rule.ProfileRules;
import org.sonar.server.rule.RubyRuleService;
import org.sonar.server.rule.RuleRegistry;
@@ -368,6 +369,7 @@ public final class Platform {
ComponentContainer startupContainer = servicesContainer.createChild();
startupContainer.addSingleton(GwtPublisher.class);
startupContainer.addSingleton(RegisterMetrics.class);
startupContainer.addSingleton(DeprecatedRuleDefinitions.class);
startupContainer.addSingleton(RegisterRules.class);
startupContainer.addSingleton(RegisterNewProfiles.class);
startupContainer.addSingleton(JdbcDriverDeployer.class);

+ 103
- 0
sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRuleDefinitions.java 파일 보기

@@ -0,0 +1,103 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2013 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.server.rule;

import org.apache.commons.lang.StringUtils;
import org.sonar.api.rule.RuleDefinitions;
import org.sonar.api.rules.RuleParam;
import org.sonar.api.rules.RuleRepository;
import org.sonar.check.Cardinality;
import org.sonar.core.i18n.RuleI18nManager;

import javax.annotation.CheckForNull;

/**
* Inject deprecated RuleRepository into RuleDefinitions for backward-compatibility.
* @since 4.2
*/
public class DeprecatedRuleDefinitions implements RuleDefinitions {
private final RuleI18nManager i18n;
private final RuleRepository[] repositories;

public DeprecatedRuleDefinitions(RuleI18nManager i18n, RuleRepository[] repositories) {
this.i18n = i18n;
this.repositories = repositories;
}

public DeprecatedRuleDefinitions(RuleI18nManager i18n) {
this(i18n, new RuleRepository[0]);
}

@Override
public void define(Context context) {
for (RuleRepository repository : repositories) {
// RuleRepository API does not handle difference between new and extended repositories,
NewRepository newRepository;
if (context.repository(repository.getKey()) == null) {
newRepository = context.newRepository(repository.getKey(), repository.getLanguage());
newRepository.setName(repository.getName());
} else {
newRepository = (NewRepository) context.extendRepository(repository.getKey());
}
for (org.sonar.api.rules.Rule rule : repository.createRules()) {
// TODO remove org.sonar.api.rules.Rule#tags
NewRule newRule = newRepository.newRule(rule.getKey());
newRule.setName(ruleName(repository.getKey(), rule));
newRule.setHtmlDescription(ruleDescription(repository.getKey(), rule));
newRule.setMetadata(rule.getConfigKey());
newRule.setTemplate(Cardinality.MULTIPLE.equals(rule.getCardinality()));
newRule.setDefaultSeverity(rule.getSeverity().toString());
for (RuleParam param : rule.getParams()) {
NewParam newParam = newRule.newParam(param.getKey());
newParam.setDefaultValue(param.getDefaultValue());
newParam.setDescription(paramDescription(repository.getKey(), rule.getKey(), param));
}
}
newRepository.done();
}
}

@CheckForNull
private String ruleName(String repositoryKey, org.sonar.api.rules.Rule rule) {
String name = i18n.getName(repositoryKey, rule.getKey());
if (StringUtils.isNotBlank(name)) {
return name;
}
return StringUtils.defaultIfBlank(rule.getName(), null);
}

@CheckForNull
private String ruleDescription(String repositoryKey, org.sonar.api.rules.Rule rule) {
String description = i18n.getDescription(repositoryKey, rule.getKey());
if (StringUtils.isNotBlank(description)) {
return description;
}
return StringUtils.defaultIfBlank(rule.getDescription(), null);
}

@CheckForNull
private String paramDescription(String repositoryKey, String ruleKey, RuleParam param) {
String desc = StringUtils.defaultIfEmpty(
i18n.getParamDescription(repositoryKey, ruleKey, param.getKey()),
param.getDescription()
);
return StringUtils.defaultIfBlank(desc, null);
}
}

+ 133
- 0
sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRuleDefinitionsTest.java 파일 보기

@@ -0,0 +1,133 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2013 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.server.rule;

import org.junit.Test;
import org.sonar.api.rule.RuleDefinitions;
import org.sonar.api.rule.Severity;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RulePriority;
import org.sonar.api.rules.RuleRepository;
import org.sonar.core.i18n.RuleI18nManager;

import java.util.Arrays;
import java.util.List;

import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class DeprecatedRuleDefinitionsTest {

static class CheckstyleRules extends RuleRepository {
public CheckstyleRules() {
super("checkstyle", "java");
setName("Checkstyle");
}

@Override
public List<Rule> createRules() {
Rule rule = Rule.create("checkstyle", "ConstantName", "Constant Name");
rule.setDescription("Checks that constant names conform to the specified format");
rule.setConfigKey("Checker/TreeWalker/ConstantName");
rule.setSeverity(RulePriority.BLOCKER);
rule.createParameter("format").setDescription("Regular expression").setDefaultValue("A-Z").setType("REGULAR_EXPRESSION");
return Arrays.asList(rule);
}
}

@Test
public void wrap_deprecated_rule_repositories() throws Exception {
RuleDefinitions.Context context = new RuleDefinitions.Context();
RuleI18nManager i18n = mock(RuleI18nManager.class);

new DeprecatedRuleDefinitions(i18n, new RuleRepository[]{new CheckstyleRules()}).define(context);

assertThat(context.repositories()).hasSize(1);
RuleDefinitions.Repository checkstyle = context.repository("checkstyle");
assertThat(checkstyle).isNotNull();
assertThat(checkstyle.key()).isEqualTo("checkstyle");
assertThat(checkstyle.name()).isEqualTo("Checkstyle");
assertThat(checkstyle.language()).isEqualTo("java");
assertThat(checkstyle.rules()).hasSize(1);
RuleDefinitions.Rule rule = checkstyle.rule("ConstantName");
assertThat(rule).isNotNull();
assertThat(rule.key()).isEqualTo("ConstantName");
assertThat(rule.name()).isEqualTo("Constant Name");
assertThat(rule.htmlDescription()).isEqualTo("Checks that constant names conform to the specified format");
assertThat(rule.defaultSeverity()).isEqualTo(Severity.BLOCKER);
assertThat(rule.metadata()).isEqualTo("Checker/TreeWalker/ConstantName");
assertThat(rule.tags()).isEmpty();
assertThat(rule.params()).hasSize(1);
RuleDefinitions.Param param = rule.param("format");
assertThat(param).isNotNull();
assertThat(param.key()).isEqualTo("format");
assertThat(param.name()).isEqualTo("format");
assertThat(param.description()).isEqualTo("Regular expression");
assertThat(param.defaultValue()).isEqualTo("A-Z");
}

@Test
public void emulate_the_day_deprecated_api_can_be_dropped() throws Exception {
RuleDefinitions.Context context = new RuleDefinitions.Context();
RuleI18nManager i18n = mock(RuleI18nManager.class);

// no more RuleRepository !
new DeprecatedRuleDefinitions(i18n);

assertThat(context.repositories()).isEmpty();
}


static class UseBundles extends RuleRepository {
public UseBundles() {
super("checkstyle", "java");
setName("Checkstyle");
}

@Override
public List<Rule> createRules() {
Rule rule = Rule.create("checkstyle", "ConstantName");
rule.createParameter("format");
return Arrays.asList(rule);
}
}

@Test
public void use_l10n_bundles() throws Exception {
RuleDefinitions.Context context = new RuleDefinitions.Context();
RuleI18nManager i18n = mock(RuleI18nManager.class);
when(i18n.getName("checkstyle", "ConstantName")).thenReturn("Constant Name");
when(i18n.getDescription("checkstyle", "ConstantName")).thenReturn("Checks that constant names conform to the specified format");
when(i18n.getParamDescription("checkstyle", "ConstantName", "format")).thenReturn("Regular expression");

new DeprecatedRuleDefinitions(i18n, new RuleRepository[]{new UseBundles()}).define(context);

RuleDefinitions.Repository checkstyle = context.repository("checkstyle");
RuleDefinitions.Rule rule = checkstyle.rule("ConstantName");
assertThat(rule.key()).isEqualTo("ConstantName");
assertThat(rule.name()).isEqualTo("Constant Name");
assertThat(rule.htmlDescription()).isEqualTo("Checks that constant names conform to the specified format");
RuleDefinitions.Param param = rule.param("format");
assertThat(param.key()).isEqualTo("format");
assertThat(param.name()).isEqualTo("format");
assertThat(param.description()).isEqualTo("Regular expression");
}
}

Loading…
취소
저장