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();
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));
}
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;
}
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);
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;
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()) {
return htmlDescription;
}
+ public boolean template() {
+ return template;
+ }
+
@CheckForNull
public Param param(String key) {
return params.get(key);
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;
return this;
}
+ public NewParam setType(RuleParamType t) {
+ this.type = t;
+ return this;
+ }
+
/**
* Plain-text description. Can be null.
*/
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() {
return defaultValue;
}
+ public RuleParamType type() {
+ return type;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
--- /dev/null
+/*
+ * 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;
+ }
+}
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");
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()
@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();
@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();
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);
// 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();
@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");
}
}
@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");
}
}
--- /dev/null
+/*
+ * 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");
+ }
+}
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;
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);
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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");
+ }
+}