diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2019-06-06 09:45:41 -0500 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2019-07-12 20:21:14 +0200 |
commit | 7c7d9b6b90244d2c974207862071caccdb2c9bb5 (patch) | |
tree | d1b1035076f996207d9fcd60ec5ea0c06234ece4 | |
parent | 97e15208790028ed50187e58cd4580e6cef8e6b3 (diff) | |
download | sonarqube-7c7d9b6b90244d2c974207862071caccdb2c9bb5.tar.gz sonarqube-7c7d9b6b90244d2c974207862071caccdb2c9bb5.zip |
Extract implementation from plugin API - Server rule definition
21 files changed, 1206 insertions, 636 deletions
diff --git a/plugins/sonar-xoo-plugin/build.gradle b/plugins/sonar-xoo-plugin/build.gradle index d9236cf050b..eb6a3c603b5 100644 --- a/plugins/sonar-xoo-plugin/build.gradle +++ b/plugins/sonar-xoo-plugin/build.gradle @@ -15,6 +15,7 @@ dependencies { testCompile 'junit:junit' testCompile 'org.assertj:assertj-core' testCompile 'org.mockito:mockito-core' + testCompile project(':server:sonar-server') } jar { 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 c1e3bca6dc0..2a4c1eda98b 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 @@ -27,6 +27,7 @@ import org.sonar.api.internal.SonarRuntimeImpl; import org.sonar.api.server.debt.DebtRemediationFunction; import org.sonar.api.server.rule.RulesDefinition; import org.sonar.api.utils.Version; +import org.sonar.server.rule.RuleDefinitionContext; import static org.assertj.core.api.Assertions.assertThat; @@ -36,7 +37,7 @@ public class XooRulesDefinitionTest { @Before public void setUp() { XooRulesDefinition def = new XooRulesDefinition(SonarRuntimeImpl.forSonarQube(Version.create(7, 3), SonarQubeSide.SCANNER, SonarEdition.COMMUNITY)); - context = new RulesDefinition.Context(); + context = new RuleDefinitionContext(); def.define(context); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/DefaultDebtRemediationFunctions.java b/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultDebtRemediationFunctions.java index e597b20390a..933aa366b2f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/DefaultDebtRemediationFunctions.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultDebtRemediationFunctions.java @@ -17,11 +17,12 @@ * 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.server.rule; +package org.sonar.server.rule; import javax.annotation.Nullable; import org.sonar.api.server.debt.DebtRemediationFunction; import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction; +import org.sonar.api.server.rule.RulesDefinition; import org.sonar.api.utils.MessageException; /** diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultNewParam.java b/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultNewParam.java new file mode 100644 index 00000000000..ea2740210ce --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultNewParam.java @@ -0,0 +1,85 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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 javax.annotation.Nullable; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.server.rule.RuleParamType; +import org.sonar.api.server.rule.RulesDefinition; + +import static org.apache.commons.lang.StringUtils.defaultIfEmpty; + +public class DefaultNewParam implements RulesDefinition.NewParam { + private final String key; + private String name; + private String description; + private String defaultValue; + private RuleParamType type = RuleParamType.STRING; + + DefaultNewParam(String key) { + this.key = this.name = key; + } + + @Override + public String key() { + return key; + } + + @Override + public DefaultNewParam setName(@Nullable String s) { + // name must never be null. + this.name = StringUtils.defaultIfBlank(s, key); + return this; + } + + @Override + public DefaultNewParam setType(RuleParamType t) { + this.type = t; + return this; + } + + @Override + public DefaultNewParam setDescription(@Nullable String s) { + this.description = StringUtils.defaultIfBlank(s, null); + return this; + } + + @Override + public DefaultNewParam setDefaultValue(@Nullable String s) { + this.defaultValue = defaultIfEmpty(s, null); + return this; + } + + public String name() { + return name; + } + + public String description() { + return description; + } + + public String defaultValue() { + return defaultValue; + } + + public RuleParamType type() { + return type; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultNewRepository.java b/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultNewRepository.java new file mode 100644 index 00000000000..3607e601340 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultNewRepository.java @@ -0,0 +1,113 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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 java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.server.rule.RulesDefinition; + +import static org.sonar.api.utils.Preconditions.checkArgument; + +public class DefaultNewRepository implements RulesDefinition.NewRepository { + private final RuleDefinitionContext context; + private final String key; + private final boolean isExternal; + private final String language; + private String name; + private final Map<String, RulesDefinition.NewRule> newRules = new HashMap<>(); + + DefaultNewRepository(RuleDefinitionContext context, String key, String language, boolean isExternal) { + this.context = context; + this.key = key; + this.name = key; + this.language = language; + this.isExternal = isExternal; + } + + @Override + public boolean isExternal() { + return isExternal; + } + + @Override + public String key() { + return key; + } + + String language() { + return language; + } + + Map<String, RulesDefinition.NewRule> newRules() { + return newRules; + } + + String name() { + return name; + } + + @Override + public DefaultNewRepository setName(@Nullable String s) { + if (StringUtils.isNotEmpty(s)) { + this.name = s; + } + return this; + } + + @Override + public RulesDefinition.NewRule createRule(String ruleKey) { + checkArgument(!newRules.containsKey(ruleKey), "The rule '%s' of repository '%s' is declared several times", ruleKey, key); + RulesDefinition.NewRule newRule = new DefaultNewRule(context.currentPluginKey(), key, ruleKey); + newRules.put(ruleKey, newRule); + return newRule; + } + + @CheckForNull + @Override + public RulesDefinition.NewRule rule(String ruleKey) { + return newRules.get(ruleKey); + } + + @Override + public Collection<RulesDefinition.NewRule> rules() { + return newRules.values(); + } + + @Override + public void done() { + // note that some validations can be done here, for example for + // verifying that at least one rule is declared + + context.registerRepository(this); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("NewRepository{"); + sb.append("key='").append(key).append('\''); + sb.append(", language='").append(language).append('\''); + sb.append('}'); + return sb.toString(); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultNewRule.java b/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultNewRule.java new file mode 100644 index 00000000000..3496d2d1035 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultNewRule.java @@ -0,0 +1,350 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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 java.io.IOException; +import java.net.URL; +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.apache.commons.io.IOUtils; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleScope; +import org.sonar.api.rule.RuleStatus; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.RuleType; +import org.sonar.api.server.debt.DebtRemediationFunction; +import org.sonar.api.server.rule.RuleTagFormat; +import org.sonar.api.server.rule.RulesDefinition; + +import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.apache.commons.lang.StringUtils.isEmpty; +import static org.apache.commons.lang.StringUtils.trimToNull; +import static org.sonar.api.utils.Preconditions.checkArgument; +import static org.sonar.api.utils.Preconditions.checkState; + +class DefaultNewRule implements RulesDefinition.NewRule { + private final String pluginKey; + private final String repoKey; + private final String key; + private RuleType type; + private String name; + private String htmlDescription; + private String markdownDescription; + private String internalKey; + private String severity = Severity.MAJOR; + private boolean template; + private RuleStatus status = RuleStatus.defaultStatus(); + private DebtRemediationFunction debtRemediationFunction; + private String gapDescription; + private final Set<String> tags = new TreeSet<>(); + private final Set<String> securityStandards = new TreeSet<>(); + private final Map<String, RulesDefinition.NewParam> paramsByKey = new HashMap<>(); + private final RulesDefinition.DebtRemediationFunctions functions; + private boolean activatedByDefault; + private RuleScope scope; + private final Set<RuleKey> deprecatedRuleKeys = new TreeSet<>(); + + DefaultNewRule(@Nullable String pluginKey, String repoKey, String key) { + this.pluginKey = pluginKey; + this.repoKey = repoKey; + this.key = key; + this.functions = new DefaultDebtRemediationFunctions(repoKey, key); + } + + @Override + public String key() { + return this.key; + } + + @CheckForNull + @Override + public RuleScope scope() { + return this.scope; + } + + @Override + public DefaultNewRule setScope(RuleScope scope) { + this.scope = scope; + return this; + } + + @Override + public DefaultNewRule setName(String s) { + this.name = trimToNull(s); + return this; + } + + @Override + public DefaultNewRule setTemplate(boolean template) { + this.template = template; + return this; + } + + @Override + public DefaultNewRule setActivatedByDefault(boolean activatedByDefault) { + this.activatedByDefault = activatedByDefault; + return this; + } + + @Override + public DefaultNewRule setSeverity(String s) { + checkArgument(Severity.ALL.contains(s), "Severity of rule %s is not correct: %s", this, s); + this.severity = s; + return this; + } + + @Override + public DefaultNewRule setType(RuleType t) { + this.type = t; + return this; + } + + @Override + public DefaultNewRule setHtmlDescription(@Nullable String s) { + checkState(markdownDescription == null, "Rule '%s' already has a Markdown description", this); + this.htmlDescription = trimToNull(s); + return this; + } + + @Override + public DefaultNewRule setHtmlDescription(@Nullable URL classpathUrl) { + if (classpathUrl != null) { + try { + setHtmlDescription(IOUtils.toString(classpathUrl, UTF_8)); + } catch (IOException e) { + throw new IllegalStateException("Fail to read: " + classpathUrl, e); + } + } else { + this.htmlDescription = null; + } + return this; + } + + @Override + public DefaultNewRule setMarkdownDescription(@Nullable String s) { + checkState(htmlDescription == null, "Rule '%s' already has an HTML description", this); + this.markdownDescription = trimToNull(s); + return this; + } + + @Override + public DefaultNewRule setMarkdownDescription(@Nullable URL classpathUrl) { + if (classpathUrl != null) { + try { + setMarkdownDescription(IOUtils.toString(classpathUrl, UTF_8)); + } catch (IOException e) { + throw new IllegalStateException("Fail to read: " + classpathUrl, e); + } + } else { + this.markdownDescription = null; + } + return this; + } + + @Override + public DefaultNewRule setStatus(RuleStatus status) { + checkArgument(RuleStatus.REMOVED != status, "Status 'REMOVED' is not accepted on rule '%s'", this); + this.status = status; + return this; + } + + @Override + public DefaultNewRule setDebtSubCharacteristic(@Nullable String s) { + return this; + } + + @Override + public RulesDefinition.DebtRemediationFunctions debtRemediationFunctions() { + return functions; + } + + @Override + public DefaultNewRule setDebtRemediationFunction(@Nullable DebtRemediationFunction fn) { + this.debtRemediationFunction = fn; + return this; + } + + @Deprecated + @Override + public DefaultNewRule setEffortToFixDescription(@Nullable String s) { + return setGapDescription(s); + } + + @Override + public DefaultNewRule setGapDescription(@Nullable String s) { + this.gapDescription = s; + return this; + } + + @Override + public RulesDefinition.NewParam createParam(String paramKey) { + checkArgument(!paramsByKey.containsKey(paramKey), "The parameter '%s' is declared several times on the rule %s", paramKey, this); + DefaultNewParam param = new DefaultNewParam(paramKey); + paramsByKey.put(paramKey, param); + return param; + } + + @CheckForNull + @Override + public RulesDefinition.NewParam param(String paramKey) { + return paramsByKey.get(paramKey); + } + + @Override + public Collection<RulesDefinition.NewParam> params() { + return paramsByKey.values(); + } + + @Override + public DefaultNewRule addTags(String... list) { + for (String tag : list) { + RuleTagFormat.validate(tag); + tags.add(tag); + } + return this; + } + + @Override + public DefaultNewRule setTags(String... list) { + tags.clear(); + addTags(list); + return this; + } + + @Override + public DefaultNewRule addOwaspTop10(RulesDefinition.OwaspTop10... standards) { + for (RulesDefinition.OwaspTop10 owaspTop10 : standards) { + String standard = "owaspTop10:" + owaspTop10.name().toLowerCase(Locale.ENGLISH); + securityStandards.add(standard); + } + return this; + } + + @Override + public DefaultNewRule addCwe(int... nums) { + for (int num : nums) { + String standard = "cwe:" + num; + securityStandards.add(standard); + } + return this; + } + + @Override + public DefaultNewRule setInternalKey(@Nullable String s) { + this.internalKey = s; + return this; + } + + void validate() { + if (isEmpty(name)) { + throw new IllegalStateException(format("Name of rule %s is empty", this)); + } + if (isEmpty(htmlDescription) && isEmpty(markdownDescription)) { + throw new IllegalStateException(format("One of HTML description or Markdown description must be defined for rule %s", this)); + } + } + + @Override + public DefaultNewRule addDeprecatedRuleKey(String repository, String key) { + deprecatedRuleKeys.add(RuleKey.of(repository, key)); + return this; + } + + String pluginKey() { + return pluginKey; + } + + String repoKey() { + return repoKey; + } + + RuleType type() { + return type; + } + + String name() { + return name; + } + + String htmlDescription() { + return htmlDescription; + } + + String markdownDescription() { + return markdownDescription; + } + + @CheckForNull + String internalKey() { + return internalKey; + } + + String severity() { + return severity; + } + + boolean template() { + return template; + } + + RuleStatus status() { + return status; + } + + DebtRemediationFunction debtRemediationFunction() { + return debtRemediationFunction; + } + + String gapDescription() { + return gapDescription; + } + + Set<String> tags() { + return tags; + } + + Set<String> securityStandards() { + return securityStandards; + } + + Map<String, RulesDefinition.NewParam> paramsByKey() { + return paramsByKey; + } + + boolean activatedByDefault() { + return activatedByDefault; + } + + Set<RuleKey> deprecatedRuleKeys() { + return deprecatedRuleKeys; + } + + @Override + public String toString() { + return format("[repository=%s, key=%s]", repoKey, key); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultParam.java b/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultParam.java new file mode 100644 index 00000000000..3e18fd740bd --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultParam.java @@ -0,0 +1,87 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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 javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import org.sonar.api.server.rule.RuleParamType; +import org.sonar.api.server.rule.RulesDefinition; + +@Immutable +public class DefaultParam implements RulesDefinition.Param { + private final String key; + private final String name; + private final String description; + private final String defaultValue; + private final RuleParamType type; + + DefaultParam(DefaultNewParam newParam) { + this.key = newParam.key(); + this.name = newParam.name(); + this.description = newParam.description(); + this.defaultValue = newParam.defaultValue(); + this.type = newParam.type(); + } + + @Override + public String key() { + return key; + } + + @Override + public String name() { + return name; + } + + @Override + @Nullable + public String description() { + return description; + } + + @Override + @Nullable + public String defaultValue() { + return defaultValue; + } + + @Override + public RuleParamType type() { + return type; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RulesDefinition.Param that = (RulesDefinition.Param) o; + return key.equals(that.key()); + } + + @Override + public int hashCode() { + return key.hashCode(); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultRepository.java b/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultRepository.java new file mode 100644 index 00000000000..5723aa996bc --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultRepository.java @@ -0,0 +1,128 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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 java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.api.utils.log.Loggers; + +import static java.lang.String.format; +import static java.util.Collections.unmodifiableList; +import static java.util.Collections.unmodifiableMap; + +@Immutable +class DefaultRepository implements RulesDefinition.Repository { + private final String key; + private final String language; + private final String name; + private final boolean isExternal; + private final Map<String, RulesDefinition.Rule> rulesByKey; + + DefaultRepository(DefaultNewRepository newRepository, @Nullable RulesDefinition.Repository mergeInto) { + this.key = newRepository.key(); + this.language = newRepository.language(); + this.isExternal = newRepository.isExternal(); + Map<String, RulesDefinition.Rule> ruleBuilder = new HashMap<>(); + if (mergeInto != null) { + if (!StringUtils.equals(newRepository.language(), mergeInto.language()) || !StringUtils.equals(newRepository.key(), mergeInto.key())) { + throw new IllegalArgumentException(format("Bug - language and key of the repositories to be merged should be the sames: %s and %s", newRepository, mergeInto)); + } + this.name = StringUtils.defaultIfBlank(mergeInto.name(), newRepository.name()); + for (RulesDefinition.Rule rule : mergeInto.rules()) { + if (!newRepository.key().startsWith("common-") && ruleBuilder.containsKey(rule.key())) { + Loggers.get(getClass()).warn("The rule '{}' of repository '{}' is declared several times", rule.key(), mergeInto.key()); + } + ruleBuilder.put(rule.key(), rule); + } + } else { + this.name = newRepository.name(); + } + for (RulesDefinition.NewRule newRule : newRepository.newRules().values()) { + DefaultNewRule defaultNewRule = (DefaultNewRule) newRule; + defaultNewRule.validate(); + ruleBuilder.put(newRule.key(), new DefaultRule(this, defaultNewRule)); + } + this.rulesByKey = unmodifiableMap(ruleBuilder); + } + + @Override + public String key() { + return key; + } + + @Override + public String language() { + return language; + } + + @Override + public String name() { + return name; + } + + @Override + public boolean isExternal() { + return isExternal; + } + + @Override + @CheckForNull + public RulesDefinition.Rule rule(String ruleKey) { + return rulesByKey.get(ruleKey); + } + + @Override + public List<RulesDefinition.Rule> rules() { + return unmodifiableList(new ArrayList<>(rulesByKey.values())); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DefaultRepository that = (DefaultRepository) o; + return key.equals(that.key); + } + + @Override + public int hashCode() { + return key.hashCode(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("Repository{"); + sb.append("key='").append(key).append('\''); + sb.append(", language='").append(language).append('\''); + sb.append('}'); + return sb.toString(); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultRule.java b/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultRule.java new file mode 100644 index 00000000000..84651101225 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultRule.java @@ -0,0 +1,238 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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 java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleScope; +import org.sonar.api.rule.RuleStatus; +import org.sonar.api.rules.RuleType; +import org.sonar.api.server.debt.DebtRemediationFunction; +import org.sonar.api.server.rule.RuleTagsToTypeConverter; +import org.sonar.api.server.rule.RulesDefinition; + +import static java.lang.String.format; +import static java.util.Collections.unmodifiableList; + +@Immutable +public class DefaultRule implements RulesDefinition.Rule { + private final String pluginKey; + private final RulesDefinition.Repository repository; + private final String repoKey; + private final String key; + private final String name; + private final RuleType type; + private final String htmlDescription; + private final String markdownDescription; + private final String internalKey; + private final String severity; + private final boolean template; + private final DebtRemediationFunction debtRemediationFunction; + private final String gapDescription; + private final Set<String> tags; + private final Set<String> securityStandards; + private final Map<String, RulesDefinition.Param> params; + private final RuleStatus status; + private final boolean activatedByDefault; + private final RuleScope scope; + private final Set<RuleKey> deprecatedRuleKeys; + + DefaultRule(DefaultRepository repository, DefaultNewRule newRule) { + this.pluginKey = newRule.pluginKey(); + this.repository = repository; + this.repoKey = newRule.repoKey(); + this.key = newRule.key(); + this.name = newRule.name(); + this.htmlDescription = newRule.htmlDescription(); + this.markdownDescription = newRule.markdownDescription(); + this.internalKey = newRule.internalKey(); + this.severity = newRule.severity(); + this.template = newRule.template(); + this.status = newRule.status(); + this.debtRemediationFunction = newRule.debtRemediationFunction(); + this.gapDescription = newRule.gapDescription(); + this.scope = newRule.scope() == null ? RuleScope.MAIN : newRule.scope(); + this.type = newRule.type() == null ? RuleTagsToTypeConverter.convert(newRule.tags()) : newRule.type(); + Set<String> tagsBuilder = new TreeSet<>(newRule.tags()); + tagsBuilder.removeAll(RuleTagsToTypeConverter.RESERVED_TAGS); + this.tags = Collections.unmodifiableSet(tagsBuilder); + this.securityStandards = Collections.unmodifiableSet(new TreeSet<>(newRule.securityStandards())); + Map<String, RulesDefinition.Param> paramsBuilder = new HashMap<>(); + for (RulesDefinition.NewParam newParam : newRule.paramsByKey().values()) { + paramsBuilder.put(newParam.key(), new DefaultParam((DefaultNewParam) newParam)); + } + this.params = Collections.unmodifiableMap(paramsBuilder); + this.activatedByDefault = newRule.activatedByDefault(); + this.deprecatedRuleKeys = Collections.unmodifiableSet(new TreeSet<>(newRule.deprecatedRuleKeys())); + } + + public RulesDefinition.Repository repository() { + return repository; + } + + @Override + @CheckForNull + public String pluginKey() { + return pluginKey; + } + + @Override + public String key() { + return key; + } + + @Override + public String name() { + return name; + } + + @Override + public RuleScope scope() { + return scope; + } + + @Override + public RuleType type() { + return type; + } + + @Override + public String severity() { + return severity; + } + + @Override + @CheckForNull + public String htmlDescription() { + return htmlDescription; + } + + @Override + @CheckForNull + public String markdownDescription() { + return markdownDescription; + } + + @Override + public boolean template() { + return template; + } + + @Override + public boolean activatedByDefault() { + return activatedByDefault; + } + + @Override + public RuleStatus status() { + return status; + } + + @CheckForNull + @Deprecated + @Override + public String debtSubCharacteristic() { + return null; + } + + @CheckForNull + @Override + public DebtRemediationFunction debtRemediationFunction() { + return debtRemediationFunction; + } + + @Deprecated + @CheckForNull + @Override + public String effortToFixDescription() { + return gapDescription(); + } + + @CheckForNull + @Override + public String gapDescription() { + return gapDescription; + } + + @CheckForNull + @Override + public RulesDefinition.Param param(String key) { + return params.get(key); + } + + @Override + public List<RulesDefinition.Param> params() { + return unmodifiableList(new ArrayList<>(params.values())); + } + + @Override + public Set<String> tags() { + return tags; + } + + @Override + public Set<String> securityStandards() { + return securityStandards; + } + + @Override + public Set<RuleKey> deprecatedRuleKeys() { + return deprecatedRuleKeys; + } + + @CheckForNull + @Override + public String internalKey() { + return internalKey; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DefaultRule other = (DefaultRule) o; + return key.equals(other.key) && repoKey.equals(other.repoKey); + } + + @Override + public int hashCode() { + int result = repoKey.hashCode(); + result = 31 * result + key.hashCode(); + return result; + } + + @Override + public String toString() { + return format("[repository=%s, key=%s]", repoKey, key); + } +} + diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/RuleDefinitionContext.java b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleDefinitionContext.java new file mode 100644 index 00000000000..69a876dabaa --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleDefinitionContext.java @@ -0,0 +1,97 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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 java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.rule.RulesDefinition; + +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; +import static org.sonar.api.utils.Preconditions.checkState; + +public class RuleDefinitionContext implements RulesDefinition.Context { + private final Map<String, RulesDefinition.Repository> repositoriesByKey = new HashMap<>(); + private String currentPluginKey; + + @Override + public RulesDefinition.NewRepository createRepository(String key, String language) { + return new DefaultNewRepository(this, key, language, false); + } + + @Override + public RulesDefinition.NewRepository createExternalRepository(String engineId, String language) { + return new DefaultNewRepository(this, RuleKey.EXTERNAL_RULE_REPO_PREFIX + engineId, language, true); + } + + @Override + @Deprecated + public RulesDefinition.NewRepository extendRepository(String key, String language) { + return createRepository(key, language); + } + + @Override + @CheckForNull + public RulesDefinition.Repository repository(String key) { + return repositoriesByKey.get(key); + } + + @Override + public List<RulesDefinition.Repository> repositories() { + return unmodifiableList(new ArrayList<>(repositoriesByKey.values())); + } + + @Override + @Deprecated + public List<RulesDefinition.ExtendedRepository> extendedRepositories(String repositoryKey) { + return emptyList(); + } + + @Override + @Deprecated + public List<RulesDefinition.ExtendedRepository> extendedRepositories() { + return emptyList(); + } + + void registerRepository(DefaultNewRepository newRepository) { + RulesDefinition.Repository existing = repositoriesByKey.get(newRepository.key()); + if (existing != null) { + String existingLanguage = existing.language(); + checkState(existingLanguage.equals(newRepository.language()), + "The rule repository '%s' must not be defined for two different languages: %s and %s", + newRepository.key(), existingLanguage, newRepository.language()); + } + repositoriesByKey.put(newRepository.key(), new DefaultRepository(newRepository, existing)); + } + + public String currentPluginKey() { + return currentPluginKey; + } + + @Override + public void setCurrentPluginKey(@Nullable String pluginKey) { + this.currentPluginKey = pluginKey; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/RuleDefinitionsLoader.java b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleDefinitionsLoader.java index d985d1b999b..ebe02c77f98 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/RuleDefinitionsLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleDefinitionsLoader.java @@ -50,7 +50,7 @@ public class RuleDefinitionsLoader { } public RulesDefinition.Context load() { - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); for (RulesDefinition pluginDefinition : pluginDefs) { context.setCurrentPluginKey(serverPluginRepository.getPluginKey(pluginDefinition)); pluginDefinition.define(context); diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoaderTest.java index cc35b56f66c..3637ea75755 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoaderTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoaderTest.java @@ -99,7 +99,7 @@ public class DeprecatedRulesDefinitionLoaderTest { @Test public void wrap_deprecated_rule_repositories() { - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); CheckstyleRules checkstyleRules = new CheckstyleRules(); when(pluginRepository.getPluginKey(checkstyleRules)).thenReturn("unittest"); new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, pluginRepository, new RuleRepository[] {checkstyleRules}).complete(context); @@ -132,7 +132,7 @@ public class DeprecatedRulesDefinitionLoaderTest { @Test public void emulate_the_day_deprecated_api_can_be_dropped() { - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); // no more RuleRepository ! new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, pluginRepository); @@ -142,7 +142,7 @@ public class DeprecatedRulesDefinitionLoaderTest { @Test public void use_l10n_bundles() { - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); 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"); @@ -162,7 +162,7 @@ public class DeprecatedRulesDefinitionLoaderTest { @Test public void define_rule_debt() { - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); List<DebtModelXMLExporter.RuleDebt> ruleDebts = newArrayList( new DebtModelXMLExporter.RuleDebt() @@ -192,7 +192,7 @@ public class DeprecatedRulesDefinitionLoaderTest { @Test public void fail_on_invalid_rule_debt() { - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); List<DebtModelXMLExporter.RuleDebt> ruleDebts = newArrayList( new DebtModelXMLExporter.RuleDebt() diff --git a/sonar-plugin-api/build.gradle b/sonar-plugin-api/build.gradle index 1475a50d800..109d187e80b 100644 --- a/sonar-plugin-api/build.gradle +++ b/sonar-plugin-api/build.gradle @@ -34,6 +34,8 @@ dependencies { testCompile 'org.assertj:assertj-core' testCompile 'org.mockito:mockito-core' testCompile project(':sonar-scanner-engine') + testCompile project(':server:sonar-server') + } sourceSets { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleTagsToTypeConverter.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleTagsToTypeConverter.java index f5948b7a2cc..13175b65cba 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleTagsToTypeConverter.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleTagsToTypeConverter.java @@ -31,18 +31,18 @@ import static java.util.Collections.unmodifiableSet; * @see org.sonar.api.server.rule.RulesDefinition.NewRule#setType(RuleType) * @since 5.5 */ -class RuleTagsToTypeConverter { +public class RuleTagsToTypeConverter { public static final String TAG_BUG = "bug"; public static final String TAG_SECURITY = "security"; - static final Set<String> RESERVED_TAGS = unmodifiableSet(new HashSet<>(asList(TAG_BUG, TAG_SECURITY))); + public static final Set<String> RESERVED_TAGS = unmodifiableSet(new HashSet<>(asList(TAG_BUG, TAG_SECURITY))); private RuleTagsToTypeConverter() { // only statics } - static RuleType convert(Collection<String> tags) { + public static RuleType convert(Collection<String> tags) { if (tags.contains(TAG_BUG)) { return RuleType.BUG; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java index c5a494dc23b..6ad36f5998e 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java @@ -19,45 +19,23 @@ */ package org.sonar.api.server.rule; -import java.io.IOException; import java.net.URL; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Locale; -import java.util.Map; import java.util.Set; -import java.util.TreeSet; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; import org.sonar.api.ExtensionPoint; import org.sonar.api.ce.ComputeEngineSide; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleScope; import org.sonar.api.rule.RuleStatus; -import org.sonar.api.rule.Severity; import org.sonar.api.rules.RuleType; import org.sonar.api.server.ServerSide; import org.sonar.api.server.debt.DebtRemediationFunction; -import org.sonar.api.utils.log.Loggers; import org.sonarsource.api.sonarlint.SonarLintSide; -import static java.lang.String.format; -import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.Collections.emptyList; -import static java.util.Collections.unmodifiableList; -import static java.util.Collections.unmodifiableMap; -import static org.apache.commons.lang.StringUtils.defaultIfEmpty; -import static org.apache.commons.lang.StringUtils.isEmpty; -import static org.apache.commons.lang.StringUtils.trimToNull; -import static org.sonar.api.utils.Preconditions.checkArgument; -import static org.sonar.api.utils.Preconditions.checkState; - /** * Defines some coding rules of the same repository. For example the Java Findbugs plugin provides an implementation of * this extension point in order to define the rules that it supports. @@ -369,20 +347,15 @@ public interface RulesDefinition { /** * Instantiated by core but not by plugins, except for their tests. */ - class Context { - private final Map<String, Repository> repositoriesByKey = new HashMap<>(); - private String currentPluginKey; - - /** + interface Context { + /* * New builder for {@link org.sonar.api.server.rule.RulesDefinition.Repository}. * <br> * A plugin can add rules to a repository that is defined then executed by another plugin. For instance * the FbContrib plugin contributes to the Findbugs plugin rules. In this case no need * to execute {@link org.sonar.api.server.rule.RulesDefinition.NewRepository#setName(String)} */ - public NewRepository createRepository(String key, String language) { - return new NewRepositoryImpl(this, key, language, false); - } + NewRepository createRepository(String key, String language); /** * Creates a repository of rules from external rule engines. @@ -390,59 +363,34 @@ public interface RulesDefinition { * * @since 7.2 */ - public NewRepository createExternalRepository(String engineId, String language) { - return new NewRepositoryImpl(this, RuleKey.EXTERNAL_RULE_REPO_PREFIX + engineId, language, true); - } + NewRepository createExternalRepository(String engineId, String language); /** * @deprecated since 5.2. Simply use {@link #createRepository(String, String)} */ @Deprecated - public NewRepository extendRepository(String key, String language) { - return createRepository(key, language); - } + NewRepository extendRepository(String key, String language); @CheckForNull - public Repository repository(String key) { - return repositoriesByKey.get(key); - } + Repository repository(String key); - public List<Repository> repositories() { - return unmodifiableList(new ArrayList<>(repositoriesByKey.values())); - } + List<Repository> repositories(); /** * @deprecated returns empty list since 5.2. Concept of "extended repository" was misleading and not valuable. Simply declare * repositories and use {@link #repositories()}. See http://jira.sonarsource.com/browse/SONAR-6709 */ @Deprecated - public List<ExtendedRepository> extendedRepositories(String repositoryKey) { - return emptyList(); - } + List<ExtendedRepository> extendedRepositories(String repositoryKey); /** * @deprecated returns empty list since 5.2. Concept of "extended repository" was misleading and not valuable. Simply declare * repositories and use {@link #repositories()}. See http://jira.sonarsource.com/browse/SONAR-6709 */ @Deprecated - public List<ExtendedRepository> extendedRepositories() { - return emptyList(); - } + List<ExtendedRepository> extendedRepositories(); - private void registerRepository(NewRepositoryImpl newRepository) { - Repository existing = repositoriesByKey.get(newRepository.key()); - if (existing != null) { - String existingLanguage = existing.language(); - checkState(existingLanguage.equals(newRepository.language), - "The rule repository '%s' must not be defined for two different languages: %s and %s", - newRepository.key, existingLanguage, newRepository.language); - } - repositoriesByKey.put(newRepository.key, new RepositoryImpl(newRepository, existing)); - } - - public void setCurrentPluginKey(@Nullable String pluginKey) { - this.currentPluginKey = pluginKey; - } + void setCurrentPluginKey(@Nullable String pluginKey); } interface NewExtendedRepository { @@ -477,77 +425,6 @@ public interface RulesDefinition { A1, A2, A3, A4, A5, A6, A7, A8, A9, A10; } - class NewRepositoryImpl implements NewRepository { - private final Context context; - private final String key; - private final boolean isExternal; - private String language; - private String name; - private final Map<String, NewRule> newRules = new HashMap<>(); - - private NewRepositoryImpl(Context context, String key, String language, boolean isExternal) { - this.context = context; - this.key = key; - this.name = key; - this.language = language; - this.isExternal = isExternal; - } - - @Override - public boolean isExternal() { - return isExternal; - } - - @Override - public String key() { - return key; - } - - @Override - public NewRepositoryImpl setName(@Nullable String s) { - if (StringUtils.isNotEmpty(s)) { - this.name = s; - } - return this; - } - - @Override - public NewRule createRule(String ruleKey) { - checkArgument(!newRules.containsKey(ruleKey), "The rule '%s' of repository '%s' is declared several times", ruleKey, key); - NewRule newRule = new NewRule(context.currentPluginKey, key, ruleKey); - newRules.put(ruleKey, newRule); - return newRule; - } - - @CheckForNull - @Override - public NewRule rule(String ruleKey) { - return newRules.get(ruleKey); - } - - @Override - public Collection<NewRule> rules() { - return newRules.values(); - } - - @Override - public void done() { - // note that some validations can be done here, for example for - // verifying that at least one rule is declared - - context.registerRepository(this); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("NewRepository{"); - sb.append("key='").append(key).append('\''); - sb.append(", language='").append(language).append('\''); - sb.append('}'); - return sb.toString(); - } - } - interface ExtendedRepository { String key(); @@ -568,98 +445,6 @@ public interface RulesDefinition { boolean isExternal(); } - @Immutable - class RepositoryImpl implements Repository { - private final String key; - private final String language; - private final String name; - private final boolean isExternal; - private final Map<String, Rule> rulesByKey; - - private RepositoryImpl(NewRepositoryImpl newRepository, @Nullable Repository mergeInto) { - this.key = newRepository.key; - this.language = newRepository.language; - this.isExternal = newRepository.isExternal; - Map<String, Rule> ruleBuilder = new HashMap<>(); - if (mergeInto != null) { - if (!StringUtils.equals(newRepository.language, mergeInto.language()) || !StringUtils.equals(newRepository.key, mergeInto.key())) { - throw new IllegalArgumentException(format("Bug - language and key of the repositories to be merged should be the sames: %s and %s", newRepository, mergeInto)); - } - this.name = StringUtils.defaultIfBlank(mergeInto.name(), newRepository.name); - for (Rule rule : mergeInto.rules()) { - if (!newRepository.key().startsWith("common-") && ruleBuilder.containsKey(rule.key())) { - Loggers.get(getClass()).warn("The rule '{}' of repository '{}' is declared several times", rule.key(), mergeInto.key()); - } - ruleBuilder.put(rule.key(), rule); - } - } else { - this.name = newRepository.name; - } - for (NewRule newRule : newRepository.newRules.values()) { - newRule.validate(); - ruleBuilder.put(newRule.key, new Rule(this, newRule)); - } - this.rulesByKey = unmodifiableMap(ruleBuilder); - } - - @Override - public String key() { - return key; - } - - @Override - public String language() { - return language; - } - - @Override - public String name() { - return name; - } - - @Override - public boolean isExternal() { - return isExternal; - } - - @Override - @CheckForNull - public Rule rule(String ruleKey) { - return rulesByKey.get(ruleKey); - } - - @Override - public List<Rule> rules() { - return unmodifiableList(new ArrayList<>(rulesByKey.values())); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - RepositoryImpl that = (RepositoryImpl) o; - return key.equals(that.key); - } - - @Override - public int hashCode() { - return key.hashCode(); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("Repository{"); - sb.append("key='").append(key).append('\''); - sb.append(", language='").append(language).append('\''); - sb.append('}'); - return sb.toString(); - } - } - /** * Factory of {@link org.sonar.api.server.debt.DebtRemediationFunction}. */ @@ -699,83 +484,36 @@ public interface RulesDefinition { DebtRemediationFunction create(DebtRemediationFunction.Type type, @Nullable String gapMultiplier, @Nullable String baseEffort); } - class NewRule { - private final String pluginKey; - private final String repoKey; - private final String key; - private RuleType type; - private String name; - private String htmlDescription; - private String markdownDescription; - private String internalKey; - private String severity = Severity.MAJOR; - private boolean template; - private RuleStatus status = RuleStatus.defaultStatus(); - private DebtRemediationFunction debtRemediationFunction; - private String gapDescription; - private final Set<String> tags = new TreeSet<>(); - private final Set<String> securityStandards = new TreeSet<>(); - private final Map<String, NewParam> paramsByKey = new HashMap<>(); - private final DebtRemediationFunctions functions; - private boolean activatedByDefault; - private RuleScope scope; - private final Set<RuleKey> deprecatedRuleKeys = new TreeSet<>(); - - private NewRule(@Nullable String pluginKey, String repoKey, String key) { - this.pluginKey = pluginKey; - this.repoKey = repoKey; - this.key = key; - this.functions = new DefaultDebtRemediationFunctions(repoKey, key); - } + interface NewRule { - public String key() { - return this.key; - } + String key(); /** * @since 7.1 */ @CheckForNull - public RuleScope scope() { - return this.scope; - } + RuleScope scope(); /** * @since 7.1 */ - public NewRule setScope(RuleScope scope) { - this.scope = scope; - return this; - } + NewRule setScope(RuleScope scope); /** * Required rule name */ - public NewRule setName(String s) { - this.name = trimToNull(s); - return this; - } + NewRule setName(String s); - public NewRule setTemplate(boolean template) { - this.template = template; - return this; - } + NewRule setTemplate(boolean template); /** * Should this rule be enabled by default. For example in SonarLint standalone. * * @since 6.0 */ - public NewRule setActivatedByDefault(boolean activatedByDefault) { - this.activatedByDefault = activatedByDefault; - return this; - } + NewRule setActivatedByDefault(boolean activatedByDefault); - public NewRule setSeverity(String s) { - checkArgument(Severity.ALL.contains(s), "Severity of rule %s is not correct: %s", this, s); - this.severity = s; - return this; - } + NewRule setSeverity(String s); /** * The type as defined by the SonarQube Quality Model. @@ -793,73 +531,36 @@ public interface RulesDefinition { * * @since 5.5 */ - public NewRule setType(RuleType t) { - this.type = t; - return this; - } + NewRule setType(RuleType t); /** * The optional description, in HTML format, has no max length. It's exclusive with markdown description * (see {@link #setMarkdownDescription(String)}) */ - public NewRule setHtmlDescription(@Nullable String s) { - checkState(markdownDescription == null, "Rule '%s' already has a Markdown description", this); - this.htmlDescription = trimToNull(s); - return this; - } + NewRule setHtmlDescription(@Nullable String s); /** * Load description from a file available in classpath. Example : <code>setHtmlDescription(getClass().getResource("/myrepo/Rule1234.html")</code> */ - public NewRule setHtmlDescription(@Nullable URL classpathUrl) { - if (classpathUrl != null) { - try { - setHtmlDescription(IOUtils.toString(classpathUrl, UTF_8)); - } catch (IOException e) { - throw new IllegalStateException("Fail to read: " + classpathUrl, e); - } - } else { - this.htmlDescription = null; - } - return this; - } + NewRule setHtmlDescription(@Nullable URL classpathUrl); /** * The optional description, in a restricted Markdown format, has no max length. It's exclusive with HTML description * (see {@link #setHtmlDescription(String)}) */ - public NewRule setMarkdownDescription(@Nullable String s) { - checkState(htmlDescription == null, "Rule '%s' already has an HTML description", this); - this.markdownDescription = trimToNull(s); - return this; - } + NewRule setMarkdownDescription(@Nullable String s); /** * Load description from a file available in classpath. Example : {@code setMarkdownDescription(getClass().getResource("/myrepo/Rule1234.md")} */ - public NewRule setMarkdownDescription(@Nullable URL classpathUrl) { - if (classpathUrl != null) { - try { - setMarkdownDescription(IOUtils.toString(classpathUrl, UTF_8)); - } catch (IOException e) { - throw new IllegalStateException("Fail to read: " + classpathUrl, e); - } - } else { - this.markdownDescription = null; - } - return this; - } + NewRule setMarkdownDescription(@Nullable URL classpathUrl); /** * Default value is {@link org.sonar.api.rule.RuleStatus#READY}. The value * {@link org.sonar.api.rule.RuleStatus#REMOVED} is not accepted and raises an * {@link java.lang.IllegalArgumentException}. */ - public NewRule setStatus(RuleStatus status) { - checkArgument(RuleStatus.REMOVED != status, "Status 'REMOVED' is not accepted on rule '%s'", this); - this.status = status; - return this; - } + NewRule setStatus(RuleStatus status); /** * SQALE sub-characteristic. See http://www.sqale.org @@ -869,32 +570,23 @@ public interface RulesDefinition { * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model. This method does nothing. * See https://jira.sonarsource.com/browse/MMF-184 */ - public NewRule setDebtSubCharacteristic(@Nullable String s) { - return this; - } + NewRule setDebtSubCharacteristic(@Nullable String s); /** * Factory of {@link org.sonar.api.server.debt.DebtRemediationFunction} */ - public DebtRemediationFunctions debtRemediationFunctions() { - return functions; - } + DebtRemediationFunctions debtRemediationFunctions(); /** * @see #debtRemediationFunctions() */ - public NewRule setDebtRemediationFunction(@Nullable DebtRemediationFunction fn) { - this.debtRemediationFunction = fn; - return this; - } + NewRule setDebtRemediationFunction(@Nullable DebtRemediationFunction fn); /** * @deprecated since 5.5, replaced by {@link #setGapDescription(String)} */ @Deprecated - public NewRule setEffortToFixDescription(@Nullable String s) { - return setGapDescription(s); - } + NewRule setEffortToFixDescription(@Nullable String s); /** * For rules that use LINEAR or LINEAR_OFFSET remediation functions, the meaning @@ -905,90 +597,44 @@ public interface RulesDefinition { * remediation function gap multiplier/base effort would be something like * "Effort to test one uncovered condition". */ - public NewRule setGapDescription(@Nullable String s) { - this.gapDescription = s; - return this; - } + NewRule setGapDescription(@Nullable String s); /** * Create a parameter with given unique key. Max length of key is 128 characters. */ - public NewParam createParam(String paramKey) { - checkArgument(!paramsByKey.containsKey(paramKey), "The parameter '%s' is declared several times on the rule %s", paramKey, this); - NewParam param = new NewParam(paramKey); - paramsByKey.put(paramKey, param); - return param; - } + NewParam createParam(String paramKey); @CheckForNull - public NewParam param(String paramKey) { - return paramsByKey.get(paramKey); - } + NewParam param(String paramKey); - public Collection<NewParam> params() { - return paramsByKey.values(); - } + Collection<NewParam> params(); /** * @see RuleTagFormat */ - public NewRule addTags(String... list) { - for (String tag : list) { - RuleTagFormat.validate(tag); - tags.add(tag); - } - return this; - } + NewRule addTags(String... list); /** * @see RuleTagFormat */ - public NewRule setTags(String... list) { - tags.clear(); - addTags(list); - return this; - } + NewRule setTags(String... list); /** * @since 7.3 */ - public NewRule addOwaspTop10(OwaspTop10... standards) { - for (OwaspTop10 owaspTop10 : standards) { - String standard = "owaspTop10:" + owaspTop10.name().toLowerCase(Locale.ENGLISH); - securityStandards.add(standard); - } - return this; - } + NewRule addOwaspTop10(OwaspTop10... standards); /** * @since 7.3 */ - public NewRule addCwe(int... nums) { - for (int num : nums) { - String standard = "cwe:" + num; - securityStandards.add(standard); - } - return this; - } + NewRule addCwe(int... nums); /** * Optional key that can be used by the rule engine. Not displayed * in webapp. For example the Java Checkstyle plugin feeds this field * with the internal path ("Checker/TreeWalker/AnnotationUseStyle"). */ - public NewRule setInternalKey(@Nullable String s) { - this.internalKey = s; - return this; - } - - private void validate() { - if (isEmpty(name)) { - throw new IllegalStateException(format("Name of rule %s is empty", this)); - } - if (isEmpty(htmlDescription) && isEmpty(markdownDescription)) { - throw new IllegalStateException(format("One of HTML description or Markdown description must be defined for rule %s", this)); - } - } + NewRule setInternalKey(@Nullable String s); /** * Register a repository and key under which this rule used to be known @@ -1000,134 +646,56 @@ public interface RulesDefinition { * @see Rule#deprecatedRuleKeys * @since 7.1 */ - public NewRule addDeprecatedRuleKey(String repository, String key) { - deprecatedRuleKeys.add(RuleKey.of(repository, key)); - return this; - } + NewRule addDeprecatedRuleKey(String repository, String key); @Override - public String toString() { - return format("[repository=%s, key=%s]", repoKey, key); - } + String toString(); } @Immutable - class Rule { - private final String pluginKey; - private final Repository repository; - private final String repoKey; - private final String key; - private final String name; - private final RuleType type; - private final String htmlDescription; - private final String markdownDescription; - private final String internalKey; - private final String severity; - private final boolean template; - private final DebtRemediationFunction debtRemediationFunction; - private final String gapDescription; - private final Set<String> tags; - private final Set<String> securityStandards; - private final Map<String, Param> params; - private final RuleStatus status; - private final boolean activatedByDefault; - private final RuleScope scope; - private final Set<RuleKey> deprecatedRuleKeys; - - private Rule(Repository repository, NewRule newRule) { - this.pluginKey = newRule.pluginKey; - this.repository = repository; - this.repoKey = newRule.repoKey; - this.key = newRule.key; - this.name = newRule.name; - this.htmlDescription = newRule.htmlDescription; - this.markdownDescription = newRule.markdownDescription; - this.internalKey = newRule.internalKey; - this.severity = newRule.severity; - this.template = newRule.template; - this.status = newRule.status; - this.debtRemediationFunction = newRule.debtRemediationFunction; - this.gapDescription = newRule.gapDescription; - this.scope = newRule.scope == null ? RuleScope.MAIN : newRule.scope; - this.type = newRule.type == null ? RuleTagsToTypeConverter.convert(newRule.tags) : newRule.type; - Set<String> tagsBuilder = new TreeSet<>(newRule.tags); - tagsBuilder.removeAll(RuleTagsToTypeConverter.RESERVED_TAGS); - this.tags = Collections.unmodifiableSet(tagsBuilder); - this.securityStandards = Collections.unmodifiableSet(new TreeSet<>(newRule.securityStandards)); - Map<String, Param> paramsBuilder = new HashMap<>(); - for (NewParam newParam : newRule.paramsByKey.values()) { - paramsBuilder.put(newParam.key, new Param(newParam)); - } - this.params = Collections.unmodifiableMap(paramsBuilder); - this.activatedByDefault = newRule.activatedByDefault; - this.deprecatedRuleKeys = Collections.unmodifiableSet(new TreeSet<>(newRule.deprecatedRuleKeys)); - } + interface Rule { - public Repository repository() { - return repository; - } + Repository repository(); /** * @since 6.6 the plugin the rule was declared in */ @CheckForNull - public String pluginKey() { - return pluginKey; - } + String pluginKey(); - public String key() { - return key; - } + String key(); - public String name() { - return name; - } + String name(); /** * @since 7.1 */ - public RuleScope scope() { - return scope; - } + RuleScope scope(); /** * @see NewRule#setType(RuleType) * @since 5.5 */ - public RuleType type() { - return type; - } + RuleType type(); - public String severity() { - return severity; - } + String severity(); @CheckForNull - public String htmlDescription() { - return htmlDescription; - } + String htmlDescription(); @CheckForNull - public String markdownDescription() { - return markdownDescription; - } + String markdownDescription(); - public boolean template() { - return template; - } + boolean template(); /** * Should this rule be enabled by default. For example in SonarLint standalone. * * @since 6.0 */ - public boolean activatedByDefault() { - return activatedByDefault; - } + boolean activatedByDefault(); - public RuleStatus status() { - return status; - } + RuleStatus status(); /** * @see #type() @@ -1136,45 +704,29 @@ public interface RulesDefinition { */ @CheckForNull @Deprecated - public String debtSubCharacteristic() { - return null; - } + String debtSubCharacteristic(); @CheckForNull - public DebtRemediationFunction debtRemediationFunction() { - return debtRemediationFunction; - } + DebtRemediationFunction debtRemediationFunction(); /** * @deprecated since 5.5, replaced by {@link #gapDescription()} */ @Deprecated @CheckForNull - public String effortToFixDescription() { - return gapDescription(); - } + String effortToFixDescription(); @CheckForNull - public String gapDescription() { - return gapDescription; - } + String gapDescription(); @CheckForNull - public Param param(String key) { - return params.get(key); - } + Param param(String key); - public List<Param> params() { - return unmodifiableList(new ArrayList<>(params.values())); - } + List<Param> params(); - public Set<String> tags() { - return tags; - } + Set<String> tags(); - public Set<String> securityStandards() { - return securityStandards; - } + Set<String> securityStandards(); /** * Deprecated rules keys for this rule. @@ -1234,140 +786,47 @@ public interface RulesDefinition { * @see NewRule#addDeprecatedRuleKey(String, String) * @since 7.1 */ - public Set<RuleKey> deprecatedRuleKeys() { - return deprecatedRuleKeys; - } + Set<RuleKey> deprecatedRuleKeys(); /** * @see RulesDefinition.NewRule#setInternalKey(String) */ @CheckForNull - public String internalKey() { - return internalKey; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Rule other = (Rule) o; - return key.equals(other.key) && repoKey.equals(other.repoKey); - } + String internalKey(); - @Override - public int hashCode() { - int result = repoKey.hashCode(); - result = 31 * result + key.hashCode(); - return result; - } - - @Override - public String toString() { - return format("[repository=%s, key=%s]", repoKey, key); - } } - class NewParam { - private final String key; - private String name; - private String description; - private String defaultValue; - private RuleParamType type = RuleParamType.STRING; - - private NewParam(String key) { - this.key = this.name = key; - } - - public String key() { - return key; - } + interface NewParam { + String key(); - public NewParam setName(@Nullable String s) { - // name must never be null. - this.name = StringUtils.defaultIfBlank(s, key); - return this; - } + NewParam setName(@Nullable String s); - public NewParam setType(RuleParamType t) { - this.type = t; - return this; - } + NewParam setType(RuleParamType t); /** * Plain-text description. Can be null. Max length is 4000 characters. */ - public NewParam setDescription(@Nullable String s) { - this.description = StringUtils.defaultIfBlank(s, null); - return this; - } + NewParam setDescription(@Nullable String s); /** * Empty default value will be converted to null. Max length is 4000 characters. */ - public NewParam setDefaultValue(@Nullable String s) { - this.defaultValue = defaultIfEmpty(s, null); - return this; - } + NewParam setDefaultValue(@Nullable String s); } @Immutable - class Param { - private final String key; - private final String name; - private final String description; - private final String 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 key; - } + interface Param { + String key(); - public String name() { - return name; - } + String name(); @Nullable - public String description() { - return description; - } + String description(); @Nullable - public String defaultValue() { - return defaultValue; - } - - public RuleParamType type() { - return type; - } + String defaultValue(); - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Param that = (Param) o; - return key.equals(that.key); - } - - @Override - public int hashCode() { - return key.hashCode(); - } + RuleParamType type(); } /** diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionAnnotationLoaderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionAnnotationLoaderTest.java index 888702ddfc5..bf7678ab757 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionAnnotationLoaderTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionAnnotationLoaderTest.java @@ -25,6 +25,7 @@ import org.sonar.api.rule.RuleStatus; import org.sonar.api.rule.Severity; import org.sonar.api.server.rule.RulesDefinition.NewRule; import org.sonar.check.Priority; +import org.sonar.server.rule.RuleDefinitionContext; import static org.assertj.core.api.Assertions.assertThat; @@ -57,7 +58,7 @@ public class RulesDefinitionAnnotationLoaderTest { @Test public void override_annotation_programmatically() { - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); RulesDefinition.NewRepository newRepository = context.createRepository("squid", "java"); NewRule newRule = annotationLoader.loadRule(newRepository, RuleWithProperty.class); newRule.setName("Overridden name"); @@ -144,7 +145,7 @@ public class RulesDefinitionAnnotationLoaderTest { } private RulesDefinition.Repository load(Class annotatedClass) { - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); RulesDefinition.NewExtendedRepository newRepository = context.createRepository("squid", "java"); annotationLoader.load(newRepository, annotatedClass); newRepository.done(); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionI18nLoaderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionI18nLoaderTest.java index db3fcfeb40f..d7c0ab4e3d8 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionI18nLoaderTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionI18nLoaderTest.java @@ -21,6 +21,7 @@ package org.sonar.api.server.rule; import org.junit.Test; import org.sonar.api.i18n.RuleI18n; +import org.sonar.server.rule.RuleDefinitionContext; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -36,7 +37,7 @@ public class RulesDefinitionI18nLoaderTest { when(i18n.getName("squid", "S0001")).thenReturn("SOne"); when(i18n.getDescription("squid", "S0001")).thenReturn("S One"); - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); RulesDefinition.NewRepository repo = context.createRepository("squid", "java"); // rule without description repo.createRule("S0001"); @@ -53,7 +54,7 @@ public class RulesDefinitionI18nLoaderTest { public void do_not_override_if_no_bundle() { // i18n returns null values - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); RulesDefinition.NewRepository repo = context.createRepository("squid", "java"); repo.createRule("S0001").setName("SOne").setHtmlDescription("S One"); @@ -70,7 +71,7 @@ public class RulesDefinitionI18nLoaderTest { when(i18n.getName("squid", "S0001")).thenReturn("SOne"); when(i18n.getDescription("squid", "S0001")).thenReturn("S One"); - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); RulesDefinition.NewRepository repo = context.createRepository("squid", "java"); repo.createRule("S0001").setName("Bad").setHtmlDescription("Bad"); @@ -86,7 +87,7 @@ public class RulesDefinitionI18nLoaderTest { public void complete_param_description() { when(i18n.getParamDescription("squid", "S0001", "max")).thenReturn("Maximum"); - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); RulesDefinition.NewRepository repo = context.createRepository("squid", "java"); repo.createRule("S0001").setName("SOne").setHtmlDescription("S One").createParam("max"); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java index 468ada4317b..50eea7cf408 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java @@ -41,6 +41,7 @@ import org.sonar.api.rule.Severity; import org.sonar.api.rules.RuleType; import org.sonar.api.server.debt.DebtRemediationFunction; import org.sonar.api.utils.log.LogTester; +import org.sonar.server.rule.RuleDefinitionContext; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; @@ -48,7 +49,7 @@ import static org.junit.Assert.fail; @RunWith(DataProviderRunner.class) public class RulesDefinitionTest { - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); @Rule public LogTester logTester = new LogTester(); @@ -400,6 +401,7 @@ public class RulesDefinitionTest { /** * This is temporarily accepted only for the support of the common-rules that are still declared * by plugins. It could be removed in 7.0 + * * @since 5.2 */ @Test diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionXmlLoaderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionXmlLoaderTest.java index 61111aa3847..3e180e3abca 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionXmlLoaderTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionXmlLoaderTest.java @@ -30,6 +30,7 @@ import org.sonar.api.rule.RuleStatus; import org.sonar.api.rule.Severity; import org.sonar.api.rules.RuleType; import org.sonar.api.server.debt.DebtRemediationFunction; +import org.sonar.server.rule.RuleDefinitionContext; import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.api.utils.ExceptionCauseMatcher.hasType; @@ -294,7 +295,7 @@ public class RulesDefinitionXmlLoaderTest { } private RulesDefinition.Repository load(InputStream input, String encoding) { - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); RulesDefinition.NewRepository newRepository = context.createRepository("squid", "java"); underTest.load(newRepository, input, encoding); newRepository.done(); @@ -302,7 +303,7 @@ public class RulesDefinitionXmlLoaderTest { } private RulesDefinition.Repository load(String xml) { - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); RulesDefinition.NewRepository newRepository = context.createRepository("squid", "java"); underTest.load(newRepository, new StringReader(xml)); newRepository.done(); diff --git a/sonar-scanner-engine/build.gradle b/sonar-scanner-engine/build.gradle index 77c26c7466d..68880e4ae0a 100644 --- a/sonar-scanner-engine/build.gradle +++ b/sonar-scanner-engine/build.gradle @@ -44,6 +44,8 @@ dependencies { testCompile 'org.mockito:mockito-core' testCompile project(':plugins:sonar-xoo-plugin') testCompile project(':sonar-plugin-api').sourceSets.test.output + testCompile project(':server:sonar-server') + } license { diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java index 530102bed50..244dec3cfcc 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java @@ -73,6 +73,7 @@ import org.sonar.scanner.scan.branch.BranchConfigurationLoader; import org.sonar.scanner.scan.branch.BranchType; import org.sonar.scanner.scan.branch.ProjectBranches; import org.sonar.scanner.scan.branch.ProjectPullRequests; +import org.sonar.server.rule.RuleDefinitionContext; import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile; import org.sonarqube.ws.Rules.ListResponse.Rule; @@ -168,7 +169,7 @@ public class ScannerMediumTester extends ExternalResource { } public ScannerMediumTester addRules(RulesDefinition rulesDefinition) { - RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.Context context = new RuleDefinitionContext(); rulesDefinition.define(context); List<Repository> repositories = context.repositories(); for (Repository repo : repositories) { |