aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-plugin-api
diff options
context:
space:
mode:
authorDuarte Meneses <duarte.meneses@sonarsource.com>2019-06-06 09:45:41 -0500
committerSonarTech <sonartech@sonarsource.com>2019-07-12 20:21:14 +0200
commit7c7d9b6b90244d2c974207862071caccdb2c9bb5 (patch)
treed1b1035076f996207d9fcd60ec5ea0c06234ece4 /sonar-plugin-api
parent97e15208790028ed50187e58cd4580e6cef8e6b3 (diff)
downloadsonarqube-7c7d9b6b90244d2c974207862071caccdb2c9bb5.tar.gz
sonarqube-7c7d9b6b90244d2c974207862071caccdb2c9bb5.zip
Extract implementation from plugin API - Server rule definition
Diffstat (limited to 'sonar-plugin-api')
-rw-r--r--sonar-plugin-api/build.gradle2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/server/rule/DefaultDebtRemediationFunctions.java67
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleTagsToTypeConverter.java6
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java689
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionAnnotationLoaderTest.java5
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionI18nLoaderTest.java9
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java4
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionXmlLoaderTest.java5
8 files changed, 93 insertions, 694 deletions
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/DefaultDebtRemediationFunctions.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/DefaultDebtRemediationFunctions.java
deleted file mode 100644
index e597b20390a..00000000000
--- a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/DefaultDebtRemediationFunctions.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.api.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.utils.MessageException;
-
-/**
- * Factory of {@link org.sonar.api.server.debt.DebtRemediationFunction} that keeps
- * a context of rule for better error messages. Used only when declaring rules.
- *
- * @see org.sonar.api.server.rule.RulesDefinition
- */
-class DefaultDebtRemediationFunctions implements RulesDefinition.DebtRemediationFunctions {
-
- private final String repoKey;
- private final String key;
-
- DefaultDebtRemediationFunctions(String repoKey, String key) {
- this.repoKey = repoKey;
- this.key = key;
- }
-
- @Override
- public DebtRemediationFunction linear(String gapMultiplier) {
- return create(DefaultDebtRemediationFunction.Type.LINEAR, gapMultiplier, null);
- }
-
- @Override
- public DebtRemediationFunction linearWithOffset(String gapMultiplier, String baseEffort) {
- return create(DefaultDebtRemediationFunction.Type.LINEAR_OFFSET, gapMultiplier, baseEffort);
- }
-
- @Override
- public DebtRemediationFunction constantPerIssue(String baseEffort) {
- return create(DefaultDebtRemediationFunction.Type.CONSTANT_ISSUE, null, baseEffort);
- }
-
- @Override
- public DebtRemediationFunction create(DebtRemediationFunction.Type type, @Nullable String gapMultiplier, @Nullable String baseEffort) {
- try {
- return new DefaultDebtRemediationFunction(type, gapMultiplier, baseEffort);
- } catch (Exception e) {
- throw MessageException.of(String.format("The rule '%s:%s' is invalid : %s ", this.repoKey, this.key, e.getMessage()));
- }
- }
-
-}
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();