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