]> source.dussan.org Git - sonarqube.git/commitdiff
Extract implementation from plugin API - Server rule definition
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Thu, 6 Jun 2019 14:45:41 +0000 (09:45 -0500)
committerSonarTech <sonartech@sonarsource.com>
Fri, 12 Jul 2019 18:21:14 +0000 (20:21 +0200)
22 files changed:
plugins/sonar-xoo-plugin/build.gradle
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java
server/sonar-server/src/main/java/org/sonar/server/rule/DefaultDebtRemediationFunctions.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/rule/DefaultNewParam.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/rule/DefaultNewRepository.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/rule/DefaultNewRule.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/rule/DefaultParam.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/rule/DefaultRepository.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/rule/DefaultRule.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/rule/RuleDefinitionContext.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/rule/RuleDefinitionsLoader.java
server/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoaderTest.java
sonar-plugin-api/build.gradle
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/DefaultDebtRemediationFunctions.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleTagsToTypeConverter.java
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionAnnotationLoaderTest.java
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionI18nLoaderTest.java
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionXmlLoaderTest.java
sonar-scanner-engine/build.gradle
sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java

index d9236cf050b399e2c08776776a765cf50e8b5d0e..eb6a3c603b5828ab2ee03be4253064e7fcaed12c 100644 (file)
@@ -15,6 +15,7 @@ dependencies {
   testCompile 'junit:junit'
   testCompile 'org.assertj:assertj-core'
   testCompile 'org.mockito:mockito-core'
+  testCompile project(':server:sonar-server')
 }
 
 jar {
index c1e3bca6dc01e102f4f9e852852218e7a5aaf91f..2a4c1eda98b5a81ede05c53aa11f3898c05d1a92 100644 (file)
@@ -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/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultDebtRemediationFunctions.java b/server/sonar-server/src/main/java/org/sonar/server/rule/DefaultDebtRemediationFunctions.java
new file mode 100644 (file)
index 0000000..933aa36
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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.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;
+
+/**
+ * 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/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 (file)
index 0000000..ea27402
--- /dev/null
@@ -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 (file)
index 0000000..3607e60
--- /dev/null
@@ -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 (file)
index 0000000..3496d2d
--- /dev/null
@@ -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 (file)
index 0000000..3e18fd7
--- /dev/null
@@ -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 (file)
index 0000000..5723aa9
--- /dev/null
@@ -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 (file)
index 0000000..8465110
--- /dev/null
@@ -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 (file)
index 0000000..69a876d
--- /dev/null
@@ -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;
+  }
+}
index d985d1b999bd28ddcc223df61a7e944f598796f3..ebe02c77f9862414f8fafbd39a6d95ace872f0d6 100644 (file)
@@ -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);
index cc35b56f66c5650ec0df268445b9e240bed84647..3637ea757559032bb29f7e6a77d824bce49b64af 100644 (file)
@@ -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()
index 1475a50d80081200fc292d59fca158033251f744..109d187e80b8b0af566b386f52c262590b7e8799 100644 (file)
@@ -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 (file)
index e597b20..0000000
+++ /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()));
-    }
-  }
-
-}
index f5948b7a2cced0e0b90c3a3a5e0aee15c3214092..13175b65cbadd476b1e9c0ce9db89ac4176a5b72 100644 (file)
@@ -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;
     }
index c5a494dc23bdd91678665137d8e01f5e13cd5d4f..6ad36f5998e639912890acfee19f6e34a0f92a9c 100644 (file)
  */
 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();
   }
 
   /**
index 888702ddfc5ff6114e606b24698bed9cfc72efc9..bf7678ab7574d49de426575e0ac860e2fd4f6af1 100644 (file)
@@ -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();
index db3fcfeb40f138126a058e033a0681133f87f2bd..d7c0ab4e3d82a638feeb06a1f4dc581318917e93 100644 (file)
@@ -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");
 
index 468ada4317b29f298d7d9c3b53a4de5080bd5d6d..50eea7cf408c508efc4e6fb3c9704eb978829163 100644 (file)
@@ -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
index 61111aa3847b78eae71f9138517ef5915ff2955d..3e180e3abca4c3bf346af6910b7da777fbcef726 100644 (file)
@@ -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();
index 77c26c7466d8e0a67cec1b400ec9b7a8013462d2..68880e4ae0af80ea94cd84524e7e8fac9b5a34ab 100644 (file)
@@ -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 {
index 530102bed50733bfec6a19f6ad21b4f5f4ddc375..244dec3cfcc9be08936376925c3ae2565827e2a9 100644 (file)
@@ -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) {