]> source.dussan.org Git - sonarqube.git/commitdiff
Bring back server rule implementations to sonar-plugin-api
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Fri, 12 Jul 2019 20:33:56 +0000 (15:33 -0500)
committerSonarTech <sonartech@sonarsource.com>
Mon, 15 Jul 2019 18:21:11 +0000 (20:21 +0200)
22 files changed:
sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultDebtRemediationFunctions.java [deleted file]
sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultNewParam.java [deleted file]
sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultNewRepository.java [deleted file]
sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultNewRule.java [deleted file]
sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultParam.java [deleted file]
sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultRepository.java [deleted file]
sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultRule.java [deleted file]
sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/RulesDefinitionContext.java
sonar-plugin-api-impl/src/test/java/org/sonar/api/impl/server/DefaultNewRuleTest.java [deleted file]
sonar-plugin-api-impl/src/test/java/org/sonar/api/impl/server/DefaultRepositoryTest.java [deleted file]
sonar-plugin-api-impl/src/test/java/org/sonar/api/impl/server/DefaultRuleTest.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultDebtRemediationFunctions.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewParam.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewRepository.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewRule.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultParam.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultRepository.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultRule.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultNewRuleTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultRepositoryTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultRuleTest.java [new file with mode: 0644]

diff --git a/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultDebtRemediationFunctions.java b/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultDebtRemediationFunctions.java
deleted file mode 100644 (file)
index 52bf120..0000000
+++ /dev/null
@@ -1,68 +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.impl.server;
-
-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/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultNewParam.java b/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultNewParam.java
deleted file mode 100644 (file)
index 6a026db..0000000
+++ /dev/null
@@ -1,85 +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.impl.server;
-
-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 extends 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/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultNewRepository.java b/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultNewRepository.java
deleted file mode 100644 (file)
index 823828d..0000000
+++ /dev/null
@@ -1,113 +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.impl.server;
-
-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 RulesDefinitionContext 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(RulesDefinitionContext 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/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultNewRule.java b/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultNewRule.java
deleted file mode 100644 (file)
index 465b8f9..0000000
+++ /dev/null
@@ -1,350 +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.impl.server;
-
-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 extends 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/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultParam.java b/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultParam.java
deleted file mode 100644 (file)
index ea41445..0000000
+++ /dev/null
@@ -1,87 +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.impl.server;
-
-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/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultRepository.java b/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultRepository.java
deleted file mode 100644 (file)
index 8d56b97..0000000
+++ /dev/null
@@ -1,128 +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.impl.server;
-
-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/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultRule.java b/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/server/DefaultRule.java
deleted file mode 100644 (file)
index d56201d..0000000
+++ /dev/null
@@ -1,238 +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.impl.server;
-
-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);
-  }
-}
-
index 849f598196245294c970367e51da5f765030dfdf..96a63d1cdfa9138efb83b2af30f27c0ff9392b12 100644 (file)
@@ -27,6 +27,8 @@ import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.server.rule.RulesDefinition;
+import org.sonar.api.server.rule.internal.DefaultNewRepository;
+import org.sonar.api.server.rule.internal.DefaultRepository;
 
 import static java.util.Collections.emptyList;
 import static java.util.Collections.unmodifiableList;
@@ -75,7 +77,7 @@ public class RulesDefinitionContext extends RulesDefinition.Context {
     return emptyList();
   }
 
-  void registerRepository(DefaultNewRepository newRepository) {
+  public void registerRepository(DefaultNewRepository newRepository) {
     RulesDefinition.Repository existing = repositoriesByKey.get(newRepository.key());
     if (existing != null) {
       String existingLanguage = existing.language();
diff --git a/sonar-plugin-api-impl/src/test/java/org/sonar/api/impl/server/DefaultNewRuleTest.java b/sonar-plugin-api-impl/src/test/java/org/sonar/api/impl/server/DefaultNewRuleTest.java
deleted file mode 100644 (file)
index eb6ef40..0000000
+++ /dev/null
@@ -1,137 +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.impl.server;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-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.RulesDefinition;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-
-public class DefaultNewRuleTest {
-  @Rule
-  public ExpectedException exception = ExpectedException.none();
-
-  private DefaultNewRule rule = new DefaultNewRule("plugin", "repo", "key");
-
-  @Test
-  public void testSimpleSetGet() {
-    assertThat(rule.pluginKey()).isEqualTo("plugin");
-    assertThat(rule.repoKey()).isEqualTo("repo");
-    assertThat(rule.key()).isEqualTo("key");
-
-    rule.setScope(RuleScope.MAIN);
-    assertThat(rule.scope()).isEqualTo(RuleScope.MAIN);
-
-    rule.setName("   name  ");
-    assertThat(rule.name()).isEqualTo("name");
-
-    rule.setHtmlDescription("   html  ");
-    assertThat(rule.htmlDescription()).isEqualTo("html");
-
-    rule.setTemplate(true);
-    assertThat(rule.template()).isTrue();
-
-    rule.setActivatedByDefault(true);
-    assertThat(rule.activatedByDefault()).isTrue();
-
-    RulesDefinition.NewParam param1 = rule.createParam("param1");
-    assertThat(rule.param("param1")).isEqualTo(param1);
-    assertThat(rule.params()).containsOnly(param1);
-
-    rule.setTags("tag1", "tag2");
-    rule.addTags("tag3");
-    assertThat(rule.tags()).containsExactly("tag1", "tag2", "tag3");
-
-    rule.setEffortToFixDescription("effort");
-    assertThat(rule.gapDescription()).isEqualTo("effort");
-
-    rule.setGapDescription("gap");
-    assertThat(rule.gapDescription()).isEqualTo("gap");
-
-    rule.setInternalKey("internal");
-    assertThat(rule.internalKey()).isEqualTo("internal");
-
-    rule.addDeprecatedRuleKey("deprecatedrepo", "deprecatedkey");
-    assertThat(rule.deprecatedRuleKeys()).containsOnly(RuleKey.of("deprecatedrepo", "deprecatedkey"));
-
-    rule.setStatus(RuleStatus.READY);
-    assertThat(rule.status()).isEqualTo(RuleStatus.READY);
-
-    rule.addCwe(12);
-    rule.addCwe(10);
-    assertThat(rule.securityStandards()).containsOnly("cwe:10", "cwe:12");
-
-    rule.setType(RuleType.SECURITY_HOTSPOT);
-    assertThat(rule.type()).isEqualTo(RuleType.SECURITY_HOTSPOT);
-
-    DebtRemediationFunction f = mock(DebtRemediationFunction.class);
-    rule.setDebtRemediationFunction(f);
-    assertThat(rule.debtRemediationFunction()).isEqualTo(f);
-
-    rule.setSeverity("MAJOR");
-    assertThat(rule.severity()).isEqualTo("MAJOR");
-  }
-
-  @Test
-  public void validate_fails() {
-    rule.setHtmlDescription("html");
-    exception.expect(IllegalStateException.class);
-    rule.validate();
-  }
-
-  @Test
-  public void validate_succeeds() {
-    rule.setHtmlDescription("html");
-    rule.setName("name");
-    rule.validate();
-  }
-
-  @Test
-  public void set_markdown_description() {
-    rule.setMarkdownDescription("markdown");
-    assertThat(rule.markdownDescription()).isEqualTo("markdown");
-  }
-  @Test
-  public void fail_if_severity_is_invalid() {
-    exception.expect(IllegalArgumentException.class);
-    rule.setSeverity("invalid");
-  }
-
-  @Test
-  public void fail_setting_markdown_if_html_is_set() {
-    exception.expect(IllegalStateException.class);
-    rule.setHtmlDescription("html");
-    rule.setMarkdownDescription("markdown");
-  }
-
-  @Test
-  public void fail_if_set_status_to_removed() {
-    exception.expect(IllegalArgumentException.class);
-    rule.setStatus(RuleStatus.REMOVED);
-  }
-}
diff --git a/sonar-plugin-api-impl/src/test/java/org/sonar/api/impl/server/DefaultRepositoryTest.java b/sonar-plugin-api-impl/src/test/java/org/sonar/api/impl/server/DefaultRepositoryTest.java
deleted file mode 100644 (file)
index 96ef99a..0000000
+++ /dev/null
@@ -1,46 +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.impl.server;
-
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-
-public class DefaultRepositoryTest {
-  @Test
-  public void create_simple_repo() {
-    RulesDefinitionContext ctx = mock(RulesDefinitionContext.class);
-    DefaultNewRepository newRepo = new DefaultNewRepository(ctx, "key", "lang", false);
-    newRepo.createRule("rule1")
-      .setName("rule1")
-      .setHtmlDescription("desc");
-    newRepo.setName("name");
-    DefaultRepository repo = new DefaultRepository(newRepo, null);
-
-    assertThat(repo.isExternal()).isFalse();
-    assertThat(repo.key()).isEqualTo("key");
-    assertThat(repo.language()).isEqualTo("lang");
-    assertThat(repo.isExternal()).isFalse();
-    assertThat(repo.name()).isEqualTo("name");
-    assertThat(repo.rules()).extracting(r -> r.key()).containsOnly("rule1");
-
-  }
-}
diff --git a/sonar-plugin-api-impl/src/test/java/org/sonar/api/impl/server/DefaultRuleTest.java b/sonar-plugin-api-impl/src/test/java/org/sonar/api/impl/server/DefaultRuleTest.java
deleted file mode 100644 (file)
index 9be301c..0000000
+++ /dev/null
@@ -1,88 +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.impl.server;
-
-import org.junit.Test;
-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.RulesDefinition;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-
-public class DefaultRuleTest {
-  @Test
-  public void getters() {
-    DefaultRepository repo = mock(DefaultRepository.class);
-    DefaultNewRule rule = new DefaultNewRule("plugin", "repo", "key");
-
-    rule.setScope(RuleScope.MAIN);
-    rule.setName("   name  ");
-    rule.setHtmlDescription("   html  ");
-    rule.setTemplate(true);
-    rule.setActivatedByDefault(true);
-    RulesDefinition.NewParam param1 = rule.createParam("param1");
-    rule.setTags("tag1", "tag2");
-    rule.addTags("tag3");
-    rule.setEffortToFixDescription("effort");
-    rule.setGapDescription("gap");
-    rule.setInternalKey("internal");
-    rule.addDeprecatedRuleKey("deprecatedrepo", "deprecatedkey");
-    rule.setStatus(RuleStatus.READY);
-    rule.addCwe(12);
-    rule.addCwe(10);
-    rule.setType(RuleType.SECURITY_HOTSPOT);
-    DebtRemediationFunction f = mock(DebtRemediationFunction.class);
-    rule.setDebtRemediationFunction(f);
-    rule.setSeverity("MAJOR");
-
-    DefaultRule defaultRule = new DefaultRule(repo, rule);
-    assertThat(defaultRule.scope()).isEqualTo(RuleScope.MAIN);
-    assertThat(defaultRule.name()).isEqualTo("name");
-    assertThat(defaultRule.htmlDescription()).isEqualTo("html");
-    assertThat(defaultRule.template()).isTrue();
-    assertThat(defaultRule.activatedByDefault()).isTrue();
-    assertThat(defaultRule.params()).containsOnly(new DefaultParam(new DefaultNewParam("param1")));
-    assertThat(defaultRule.tags()).containsOnly("tag1", "tag2", "tag3");
-    assertThat(defaultRule.effortToFixDescription()).isEqualTo("gap");
-    assertThat(defaultRule.gapDescription()).isEqualTo("gap");
-    assertThat(defaultRule.internalKey()).isEqualTo("internal");
-    assertThat(defaultRule.deprecatedRuleKeys()).containsOnly(RuleKey.of("deprecatedrepo", "deprecatedkey"));
-    assertThat(defaultRule.status()).isEqualTo(RuleStatus.READY);
-    assertThat(rule.securityStandards()).containsOnly("cwe:10", "cwe:12");
-    assertThat(defaultRule.type()).isEqualTo(RuleType.SECURITY_HOTSPOT);
-    assertThat(defaultRule.debtRemediationFunction()).isEqualTo(f);
-    assertThat(defaultRule.markdownDescription()).isNull();
-    assertThat(defaultRule.severity()).isEqualTo("MAJOR");
-  }
-
-  @Test
-  public void to_string() {
-    DefaultRepository repo = mock(DefaultRepository.class);
-    DefaultNewRule rule = new DefaultNewRule("plugin", "repo", "key");
-    DefaultRule defaultRule = new DefaultRule(repo, rule);
-
-    assertThat(defaultRule.toString()).isEqualTo("[repository=repo, key=key]");
-  }
-}
index d249ac0bda86b8c02443f2b81ff94f7481bd028f..c55ca6ce55038cd7bbb6f684094d64705104c905 100644 (file)
 package org.sonar.api.server.rule;
 
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
@@ -34,8 +37,14 @@ import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.server.ServerSide;
 import org.sonar.api.server.debt.DebtRemediationFunction;
+import org.sonar.api.server.rule.internal.DefaultNewRepository;
+import org.sonar.api.server.rule.internal.DefaultRepository;
 import org.sonarsource.api.sonarlint.SonarLintSide;
 
+import static java.util.Collections.emptyList;
+import static java.util.Collections.unmodifiableList;
+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.
@@ -344,10 +353,78 @@ public interface RulesDefinition {
     }
   }
 
+  /**
+   * This implementation will be removed as soon as analyzers stop instantiating it.
+   * Use RulesDefinitionContext in sonar-plugin-api-impl.
+   */
+  class Context extends AbstractContext {
+    private final Map<String, 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();
+    }
+
+    public 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;
+    }
+  }
+
   /**
    * Instantiated by core but not by plugins, except for their tests.
    */
-  abstract class Context {
+  abstract class AbstractContext {
     /*
      * New builder for {@link org.sonar.api.server.rule.RulesDefinition.Repository}.
      * <br>
@@ -651,49 +728,49 @@ public interface RulesDefinition {
   }
 
   @Immutable
-  interface Rule {
+  abstract class Rule {
 
-    Repository repository();
+    public abstract Repository repository();
 
     /**
      * @since 6.6 the plugin the rule was declared in
      */
     @CheckForNull
-    String pluginKey();
+    public abstract String pluginKey();
 
-    String key();
+    public abstract String key();
 
-    String name();
+    public abstract String name();
 
     /**
      * @since 7.1
      */
-    RuleScope scope();
+    public abstract RuleScope scope();
 
     /**
      * @see NewRule#setType(RuleType)
      * @since 5.5
      */
-    RuleType type();
+    public abstract RuleType type();
 
-    String severity();
+    public abstract String severity();
 
     @CheckForNull
-    String htmlDescription();
+    public abstract String htmlDescription();
 
     @CheckForNull
-    String markdownDescription();
+    public abstract String markdownDescription();
 
-    boolean template();
+    public abstract boolean template();
 
     /**
      * Should this rule be enabled by default. For example in SonarLint standalone.
      *
      * @since 6.0
      */
-    boolean activatedByDefault();
+    public abstract boolean activatedByDefault();
 
-    RuleStatus status();
+    public abstract RuleStatus status();
 
     /**
      * @see #type()
@@ -702,29 +779,29 @@ public interface RulesDefinition {
      */
     @CheckForNull
     @Deprecated
-    String debtSubCharacteristic();
+    public abstract String debtSubCharacteristic();
 
     @CheckForNull
-    DebtRemediationFunction debtRemediationFunction();
+    public abstract DebtRemediationFunction debtRemediationFunction();
 
     /**
      * @deprecated since 5.5, replaced by {@link #gapDescription()}
      */
     @Deprecated
     @CheckForNull
-    String effortToFixDescription();
+    public abstract String effortToFixDescription();
 
     @CheckForNull
-    String gapDescription();
+    public abstract String gapDescription();
 
     @CheckForNull
-    Param param(String key);
+    public abstract Param param(String key);
 
-    List<Param> params();
+    public abstract List<Param> params();
 
-    Set<String> tags();
+    public abstract Set<String> tags();
 
-    Set<String> securityStandards();
+    public abstract Set<String> securityStandards();
 
     /**
      * Deprecated rules keys for this rule.
@@ -784,13 +861,13 @@ public interface RulesDefinition {
      * @see NewRule#addDeprecatedRuleKey(String, String)
      * @since 7.1
      */
-    Set<RuleKey> deprecatedRuleKeys();
+    public abstract Set<RuleKey> deprecatedRuleKeys();
 
     /**
      * @see RulesDefinition.NewRule#setInternalKey(String)
      */
     @CheckForNull
-    String internalKey();
+    public abstract String internalKey();
 
   }
 
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultDebtRemediationFunctions.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultDebtRemediationFunctions.java
new file mode 100644 (file)
index 0000000..29c5ceb
--- /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.api.server.rule.internal;
+
+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 DebtRemediationFunction} that keeps
+ * a context of rule for better error messages. Used only when declaring rules.
+ *
+ * @see 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/internal/DefaultNewParam.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewParam.java
new file mode 100644 (file)
index 0000000..3c98955
--- /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.api.server.rule.internal;
+
+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 extends 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/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewRepository.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewRepository.java
new file mode 100644 (file)
index 0000000..f85824c
--- /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.api.server.rule.internal;
+
+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 RulesDefinition.Context 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<>();
+
+  public DefaultNewRepository(RulesDefinition.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;
+  }
+
+  public String language() {
+    return language;
+  }
+
+  public Map<String, RulesDefinition.NewRule> newRules() {
+    return newRules;
+  }
+
+  public 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/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewRule.java
new file mode 100644 (file)
index 0000000..44a32d4
--- /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.api.server.rule.internal;
+
+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 extends 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/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultParam.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultParam.java
new file mode 100644 (file)
index 0000000..c62e10a
--- /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.api.server.rule.internal;
+
+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/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultRepository.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultRepository.java
new file mode 100644 (file)
index 0000000..5c53460
--- /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.api.server.rule.internal;
+
+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
+public 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;
+
+  public 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/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultRule.java
new file mode 100644 (file)
index 0000000..8ba64b9
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * 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.internal;
+
+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 extends 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()));
+  }
+
+  @Override
+  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/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultNewRuleTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultNewRuleTest.java
new file mode 100644 (file)
index 0000000..33901a1
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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.internal;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+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.RulesDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class DefaultNewRuleTest {
+  @Rule
+  public ExpectedException exception = ExpectedException.none();
+
+  private DefaultNewRule rule = new DefaultNewRule("plugin", "repo", "key");
+
+  @Test
+  public void testSimpleSetGet() {
+    assertThat(rule.pluginKey()).isEqualTo("plugin");
+    assertThat(rule.repoKey()).isEqualTo("repo");
+    assertThat(rule.key()).isEqualTo("key");
+
+    rule.setScope(RuleScope.MAIN);
+    assertThat(rule.scope()).isEqualTo(RuleScope.MAIN);
+
+    rule.setName("   name  ");
+    assertThat(rule.name()).isEqualTo("name");
+
+    rule.setHtmlDescription("   html  ");
+    assertThat(rule.htmlDescription()).isEqualTo("html");
+
+    rule.setTemplate(true);
+    assertThat(rule.template()).isTrue();
+
+    rule.setActivatedByDefault(true);
+    assertThat(rule.activatedByDefault()).isTrue();
+
+    RulesDefinition.NewParam param1 = rule.createParam("param1");
+    assertThat(rule.param("param1")).isEqualTo(param1);
+    assertThat(rule.params()).containsOnly(param1);
+
+    rule.setTags("tag1", "tag2");
+    rule.addTags("tag3");
+    assertThat(rule.tags()).containsExactly("tag1", "tag2", "tag3");
+
+    rule.setEffortToFixDescription("effort");
+    assertThat(rule.gapDescription()).isEqualTo("effort");
+
+    rule.setGapDescription("gap");
+    assertThat(rule.gapDescription()).isEqualTo("gap");
+
+    rule.setInternalKey("internal");
+    assertThat(rule.internalKey()).isEqualTo("internal");
+
+    rule.addDeprecatedRuleKey("deprecatedrepo", "deprecatedkey");
+    assertThat(rule.deprecatedRuleKeys()).containsOnly(RuleKey.of("deprecatedrepo", "deprecatedkey"));
+
+    rule.setStatus(RuleStatus.READY);
+    assertThat(rule.status()).isEqualTo(RuleStatus.READY);
+
+    rule.addCwe(12);
+    rule.addCwe(10);
+    assertThat(rule.securityStandards()).containsOnly("cwe:10", "cwe:12");
+
+    rule.setType(RuleType.SECURITY_HOTSPOT);
+    assertThat(rule.type()).isEqualTo(RuleType.SECURITY_HOTSPOT);
+
+    DebtRemediationFunction f = mock(DebtRemediationFunction.class);
+    rule.setDebtRemediationFunction(f);
+    assertThat(rule.debtRemediationFunction()).isEqualTo(f);
+
+    rule.setSeverity("MAJOR");
+    assertThat(rule.severity()).isEqualTo("MAJOR");
+  }
+
+  @Test
+  public void validate_fails() {
+    rule.setHtmlDescription("html");
+    exception.expect(IllegalStateException.class);
+    rule.validate();
+  }
+
+  @Test
+  public void validate_succeeds() {
+    rule.setHtmlDescription("html");
+    rule.setName("name");
+    rule.validate();
+  }
+
+  @Test
+  public void set_markdown_description() {
+    rule.setMarkdownDescription("markdown");
+    assertThat(rule.markdownDescription()).isEqualTo("markdown");
+  }
+  @Test
+  public void fail_if_severity_is_invalid() {
+    exception.expect(IllegalArgumentException.class);
+    rule.setSeverity("invalid");
+  }
+
+  @Test
+  public void fail_setting_markdown_if_html_is_set() {
+    exception.expect(IllegalStateException.class);
+    rule.setHtmlDescription("html");
+    rule.setMarkdownDescription("markdown");
+  }
+
+  @Test
+  public void fail_if_set_status_to_removed() {
+    exception.expect(IllegalArgumentException.class);
+    rule.setStatus(RuleStatus.REMOVED);
+  }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultRepositoryTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultRepositoryTest.java
new file mode 100644 (file)
index 0000000..c6f443c
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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.internal;
+
+import org.junit.Test;
+import org.sonar.api.impl.server.RulesDefinitionContext;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class DefaultRepositoryTest {
+  @Test
+  public void create_simple_repo() {
+    RulesDefinitionContext ctx = mock(RulesDefinitionContext.class);
+    DefaultNewRepository newRepo = new DefaultNewRepository(ctx, "key", "lang", false);
+    newRepo.createRule("rule1")
+      .setName("rule1")
+      .setHtmlDescription("desc");
+    newRepo.setName("name");
+    DefaultRepository repo = new DefaultRepository(newRepo, null);
+
+    assertThat(repo.isExternal()).isFalse();
+    assertThat(repo.key()).isEqualTo("key");
+    assertThat(repo.language()).isEqualTo("lang");
+    assertThat(repo.isExternal()).isFalse();
+    assertThat(repo.name()).isEqualTo("name");
+    assertThat(repo.rules()).extracting(r -> r.key()).containsOnly("rule1");
+
+  }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultRuleTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultRuleTest.java
new file mode 100644 (file)
index 0000000..a3d8b49
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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.internal;
+
+import org.junit.Test;
+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.RulesDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class DefaultRuleTest {
+  @Test
+  public void getters() {
+    DefaultRepository repo = mock(DefaultRepository.class);
+    DefaultNewRule rule = new DefaultNewRule("plugin", "repo", "key");
+
+    rule.setScope(RuleScope.MAIN);
+    rule.setName("   name  ");
+    rule.setHtmlDescription("   html  ");
+    rule.setTemplate(true);
+    rule.setActivatedByDefault(true);
+    RulesDefinition.NewParam param1 = rule.createParam("param1");
+    rule.setTags("tag1", "tag2");
+    rule.addTags("tag3");
+    rule.setEffortToFixDescription("effort");
+    rule.setGapDescription("gap");
+    rule.setInternalKey("internal");
+    rule.addDeprecatedRuleKey("deprecatedrepo", "deprecatedkey");
+    rule.setStatus(RuleStatus.READY);
+    rule.addCwe(12);
+    rule.addCwe(10);
+    rule.setType(RuleType.SECURITY_HOTSPOT);
+    DebtRemediationFunction f = mock(DebtRemediationFunction.class);
+    rule.setDebtRemediationFunction(f);
+    rule.setSeverity("MAJOR");
+
+    DefaultRule defaultRule = new DefaultRule(repo, rule);
+    assertThat(defaultRule.scope()).isEqualTo(RuleScope.MAIN);
+    assertThat(defaultRule.name()).isEqualTo("name");
+    assertThat(defaultRule.htmlDescription()).isEqualTo("html");
+    assertThat(defaultRule.template()).isTrue();
+    assertThat(defaultRule.activatedByDefault()).isTrue();
+    assertThat(defaultRule.params()).containsOnly(new DefaultParam(new DefaultNewParam("param1")));
+    assertThat(defaultRule.tags()).containsOnly("tag1", "tag2", "tag3");
+    assertThat(defaultRule.effortToFixDescription()).isEqualTo("gap");
+    assertThat(defaultRule.gapDescription()).isEqualTo("gap");
+    assertThat(defaultRule.internalKey()).isEqualTo("internal");
+    assertThat(defaultRule.deprecatedRuleKeys()).containsOnly(RuleKey.of("deprecatedrepo", "deprecatedkey"));
+    assertThat(defaultRule.status()).isEqualTo(RuleStatus.READY);
+    assertThat(rule.securityStandards()).containsOnly("cwe:10", "cwe:12");
+    assertThat(defaultRule.type()).isEqualTo(RuleType.SECURITY_HOTSPOT);
+    assertThat(defaultRule.debtRemediationFunction()).isEqualTo(f);
+    assertThat(defaultRule.markdownDescription()).isNull();
+    assertThat(defaultRule.severity()).isEqualTo("MAJOR");
+  }
+
+  @Test
+  public void to_string() {
+    DefaultRepository repo = mock(DefaultRepository.class);
+    DefaultNewRule rule = new DefaultNewRule("plugin", "repo", "key");
+    DefaultRule defaultRule = new DefaultRule(repo, rule);
+
+    assertThat(defaultRule.toString()).isEqualTo("[repository=repo, key=key]");
+  }
+}