import org.sonar.core.config.CorePropertyDefinitions; | import org.sonar.core.config.CorePropertyDefinitions; | ||||
import org.sonar.core.extension.CoreExtensionRepositoryImpl; | import org.sonar.core.extension.CoreExtensionRepositoryImpl; | ||||
import org.sonar.core.extension.CoreExtensionsLoader; | import org.sonar.core.extension.CoreExtensionsLoader; | ||||
import org.sonar.core.i18n.RuleI18nManager; | |||||
import org.sonar.core.platform.ComponentContainer; | import org.sonar.core.platform.ComponentContainer; | ||||
import org.sonar.core.platform.EditionProvider; | import org.sonar.core.platform.EditionProvider; | ||||
import org.sonar.core.platform.Module; | import org.sonar.core.platform.Module; | ||||
// depends on plugins | // depends on plugins | ||||
ServerI18n.class, // used by RuleI18nManager | ServerI18n.class, // used by RuleI18nManager | ||||
RuleI18nManager.class, // used by DebtRulesXMLImporter | |||||
Durations.class // used in Web Services and DebtCalculator | Durations.class // used in Web Services and DebtCalculator | ||||
); | ); | ||||
} | } |
assertThat(picoContainer.getParent().getParent().getComponentAdapters()).hasSize( | assertThat(picoContainer.getParent().getParent().getComponentAdapters()).hasSize( | ||||
CONTAINER_ITSELF | CONTAINER_ITSELF | ||||
+ 6 // MigrationConfigurationModule | + 6 // MigrationConfigurationModule | ||||
+ 16 // level 2 | |||||
+ 15 // level 2 | |||||
); | ); | ||||
assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize( | assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize( | ||||
COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION | COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION |
/* | |||||
* 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 com.google.common.base.Predicate; | |||||
import com.google.common.collect.Iterables; | |||||
import java.io.Reader; | |||||
import java.util.Collection; | |||||
import java.util.List; | |||||
import javax.annotation.CheckForNull; | |||||
import javax.annotation.Nonnull; | |||||
import javax.annotation.Nullable; | |||||
import org.apache.commons.io.IOUtils; | |||||
import org.apache.commons.lang.StringUtils; | |||||
import org.sonar.api.rule.RuleKey; | |||||
import org.sonar.api.rule.RuleStatus; | |||||
import org.sonar.api.rules.RuleParam; | |||||
import org.sonar.api.rules.RuleRepository; | |||||
import org.sonar.api.server.ServerSide; | |||||
import org.sonar.api.server.debt.DebtRemediationFunction; | |||||
import org.sonar.api.server.rule.RuleParamType; | |||||
import org.sonar.api.server.rule.RulesDefinition; | |||||
import org.sonar.api.utils.ValidationMessages; | |||||
import org.sonar.api.utils.log.Logger; | |||||
import org.sonar.api.utils.log.Loggers; | |||||
import org.sonar.core.i18n.RuleI18nManager; | |||||
import org.sonar.server.debt.DebtModelPluginRepository; | |||||
import org.sonar.server.debt.DebtModelXMLExporter; | |||||
import org.sonar.server.debt.DebtModelXMLExporter.RuleDebt; | |||||
import org.sonar.server.debt.DebtRulesXMLImporter; | |||||
import org.sonar.server.plugins.ServerPluginRepository; | |||||
import static com.google.common.collect.Lists.newArrayList; | |||||
/** | |||||
* Inject deprecated RuleRepository into {@link org.sonar.api.server.rule.RulesDefinition} for backward-compatibility. | |||||
*/ | |||||
@ServerSide | |||||
public class DeprecatedRulesDefinitionLoader { | |||||
private static final Logger LOG = Loggers.get(DeprecatedRulesDefinitionLoader.class); | |||||
private final RuleI18nManager i18n; | |||||
private final RuleRepository[] repositories; | |||||
private final DebtModelPluginRepository languageModelFinder; | |||||
private final DebtRulesXMLImporter importer; | |||||
private final ServerPluginRepository serverPluginRepository; | |||||
public DeprecatedRulesDefinitionLoader(RuleI18nManager i18n, DebtModelPluginRepository languageModelFinder, DebtRulesXMLImporter importer, | |||||
ServerPluginRepository serverPluginRepository, RuleRepository[] repositories) { | |||||
this.i18n = i18n; | |||||
this.serverPluginRepository = serverPluginRepository; | |||||
this.repositories = repositories; | |||||
this.languageModelFinder = languageModelFinder; | |||||
this.importer = importer; | |||||
} | |||||
/** | |||||
* Used when no deprecated repositories | |||||
*/ | |||||
public DeprecatedRulesDefinitionLoader(RuleI18nManager i18n, DebtModelPluginRepository languageModelFinder, DebtRulesXMLImporter importer, | |||||
ServerPluginRepository serverPluginRepository) { | |||||
this(i18n, languageModelFinder, importer, serverPluginRepository, new RuleRepository[0]); | |||||
} | |||||
void complete(RulesDefinition.Context context) { | |||||
// Load rule debt definitions from xml files provided by plugin | |||||
List<RuleDebt> ruleDebts = loadRuleDebtList(); | |||||
for (RuleRepository repository : repositories) { | |||||
context.setCurrentPluginKey(serverPluginRepository.getPluginKey(repository)); | |||||
// RuleRepository API does not handle difference between new and extended repositories, | |||||
RulesDefinition.NewRepository newRepository; | |||||
if (context.repository(repository.getKey()) == null) { | |||||
newRepository = context.createRepository(repository.getKey(), repository.getLanguage()); | |||||
newRepository.setName(repository.getName()); | |||||
} else { | |||||
newRepository = context.extendRepository(repository.getKey(), repository.getLanguage()); | |||||
} | |||||
for (org.sonar.api.rules.Rule rule : repository.createRules()) { | |||||
RulesDefinition.NewRule newRule = newRepository.createRule(rule.getKey()); | |||||
newRule.setName(ruleName(repository.getKey(), rule)); | |||||
newRule.setHtmlDescription(ruleDescription(repository.getKey(), rule)); | |||||
newRule.setInternalKey(rule.getConfigKey()); | |||||
newRule.setTemplate(rule.isTemplate()); | |||||
newRule.setSeverity(rule.getSeverity().toString()); | |||||
newRule.setStatus(rule.getStatus() == null ? RuleStatus.defaultStatus() : RuleStatus.valueOf(rule.getStatus())); | |||||
newRule.setTags(rule.getTags()); | |||||
for (RuleParam param : rule.getParams()) { | |||||
RulesDefinition.NewParam newParam = newRule.createParam(param.getKey()); | |||||
newParam.setDefaultValue(param.getDefaultValue()); | |||||
newParam.setDescription(paramDescription(repository.getKey(), rule.getKey(), param)); | |||||
newParam.setType(RuleParamType.parse(param.getType())); | |||||
} | |||||
updateRuleDebtDefinitions(newRule, repository.getKey(), rule.getKey(), ruleDebts); | |||||
} | |||||
newRepository.done(); | |||||
} | |||||
} | |||||
private static void updateRuleDebtDefinitions(RulesDefinition.NewRule newRule, String repoKey, String ruleKey, List<RuleDebt> ruleDebts) { | |||||
RuleDebt ruleDebt = findRequirement(ruleDebts, repoKey, ruleKey); | |||||
if (ruleDebt != null) { | |||||
newRule.setDebtRemediationFunction(remediationFunction(DebtRemediationFunction.Type.valueOf(ruleDebt.function()), | |||||
ruleDebt.coefficient(), | |||||
ruleDebt.offset(), | |||||
newRule.debtRemediationFunctions(), | |||||
repoKey, ruleKey)); | |||||
} | |||||
} | |||||
private static DebtRemediationFunction remediationFunction(DebtRemediationFunction.Type function, @Nullable String coefficient, @Nullable String offset, | |||||
RulesDefinition.DebtRemediationFunctions functions, String repoKey, String ruleKey) { | |||||
if (DebtRemediationFunction.Type.LINEAR.equals(function) && coefficient != null) { | |||||
return functions.linear(coefficient); | |||||
} else if (DebtRemediationFunction.Type.CONSTANT_ISSUE.equals(function) && offset != null) { | |||||
return functions.constantPerIssue(offset); | |||||
} else if (DebtRemediationFunction.Type.LINEAR_OFFSET.equals(function) && coefficient != null && offset != null) { | |||||
return functions.linearWithOffset(coefficient, offset); | |||||
} else { | |||||
throw new IllegalArgumentException(String.format("Debt definition on rule '%s:%s' is invalid", repoKey, ruleKey)); | |||||
} | |||||
} | |||||
@CheckForNull | |||||
private String ruleName(String repositoryKey, org.sonar.api.rules.Rule rule) { | |||||
String name = i18n.getName(repositoryKey, rule.getKey()); | |||||
if (StringUtils.isNotBlank(name)) { | |||||
return name; | |||||
} | |||||
return StringUtils.defaultIfBlank(rule.getName(), null); | |||||
} | |||||
@CheckForNull | |||||
private String ruleDescription(String repositoryKey, org.sonar.api.rules.Rule rule) { | |||||
String description = i18n.getDescription(repositoryKey, rule.getKey()); | |||||
if (StringUtils.isNotBlank(description)) { | |||||
return description; | |||||
} | |||||
return StringUtils.defaultIfBlank(rule.getDescription(), null); | |||||
} | |||||
@CheckForNull | |||||
private String paramDescription(String repositoryKey, String ruleKey, RuleParam param) { | |||||
String desc = StringUtils.defaultIfEmpty( | |||||
i18n.getParamDescription(repositoryKey, ruleKey, param.getKey()), | |||||
param.getDescription()); | |||||
return StringUtils.defaultIfBlank(desc, null); | |||||
} | |||||
public List<DebtModelXMLExporter.RuleDebt> loadRuleDebtList() { | |||||
List<RuleDebt> ruleDebtList = newArrayList(); | |||||
for (String pluginKey : getContributingPluginListWithoutSqale()) { | |||||
ruleDebtList.addAll(loadRuleDebtsFromXml(pluginKey)); | |||||
} | |||||
return ruleDebtList; | |||||
} | |||||
public List<RuleDebt> loadRuleDebtsFromXml(String pluginKey) { | |||||
Reader xmlFileReader = null; | |||||
try { | |||||
xmlFileReader = languageModelFinder.createReaderForXMLFile(pluginKey); | |||||
ValidationMessages validationMessages = ValidationMessages.create(); | |||||
List<RuleDebt> rules = importer.importXML(xmlFileReader, validationMessages); | |||||
validationMessages.log(LOG); | |||||
return rules; | |||||
} finally { | |||||
IOUtils.closeQuietly(xmlFileReader); | |||||
} | |||||
} | |||||
private Collection<String> getContributingPluginListWithoutSqale() { | |||||
Collection<String> pluginList = newArrayList(languageModelFinder.getContributingPluginList()); | |||||
pluginList.remove(DebtModelPluginRepository.DEFAULT_MODEL); | |||||
return pluginList; | |||||
} | |||||
@CheckForNull | |||||
private static RuleDebt findRequirement(List<RuleDebt> requirements, final String repoKey, final String ruleKey) { | |||||
return Iterables.find(requirements, new RuleDebtMatchRepoKeyAndRuleKey(repoKey, ruleKey), null); | |||||
} | |||||
private static class RuleDebtMatchRepoKeyAndRuleKey implements Predicate<RuleDebt> { | |||||
private final String repoKey; | |||||
private final String ruleKey; | |||||
public RuleDebtMatchRepoKeyAndRuleKey(String repoKey, String ruleKey) { | |||||
this.repoKey = repoKey; | |||||
this.ruleKey = ruleKey; | |||||
} | |||||
@Override | |||||
public boolean apply(@Nonnull RuleDebt input) { | |||||
return input.ruleKey().equals(RuleKey.of(repoKey, ruleKey)); | |||||
} | |||||
} | |||||
} |
*/ | */ | ||||
public class RuleDefinitionsLoader { | public class RuleDefinitionsLoader { | ||||
private final DeprecatedRulesDefinitionLoader deprecatedDefConverter; | |||||
private final CommonRuleDefinitions coreCommonDefs; | private final CommonRuleDefinitions coreCommonDefs; | ||||
private final RulesDefinition[] pluginDefs; | private final RulesDefinition[] pluginDefs; | ||||
private final ServerPluginRepository serverPluginRepository; | private final ServerPluginRepository serverPluginRepository; | ||||
public RuleDefinitionsLoader(DeprecatedRulesDefinitionLoader deprecatedDefConverter, | |||||
CommonRuleDefinitions coreCommonDefs, ServerPluginRepository serverPluginRepository, RulesDefinition[] pluginDefs) { | |||||
this.deprecatedDefConverter = deprecatedDefConverter; | |||||
public RuleDefinitionsLoader(CommonRuleDefinitions coreCommonDefs, ServerPluginRepository serverPluginRepository, RulesDefinition[] pluginDefs) { | |||||
this.coreCommonDefs = coreCommonDefs; | this.coreCommonDefs = coreCommonDefs; | ||||
this.serverPluginRepository = serverPluginRepository; | this.serverPluginRepository = serverPluginRepository; | ||||
this.pluginDefs = pluginDefs; | this.pluginDefs = pluginDefs; | ||||
/** | /** | ||||
* Used when no definitions at all. | * Used when no definitions at all. | ||||
*/ | */ | ||||
public RuleDefinitionsLoader(DeprecatedRulesDefinitionLoader converter, | |||||
CommonRuleDefinitions coreCommonDefs, ServerPluginRepository serverPluginRepository) { | |||||
this(converter, coreCommonDefs, serverPluginRepository, new RulesDefinition[0]); | |||||
public RuleDefinitionsLoader(CommonRuleDefinitions coreCommonDefs, ServerPluginRepository serverPluginRepository) { | |||||
this(coreCommonDefs, serverPluginRepository, new RulesDefinition[0]); | |||||
} | } | ||||
public RulesDefinition.Context load() { | public RulesDefinition.Context load() { | ||||
context.setCurrentPluginKey(serverPluginRepository.getPluginKey(pluginDefinition)); | context.setCurrentPluginKey(serverPluginRepository.getPluginKey(pluginDefinition)); | ||||
pluginDefinition.define(context); | pluginDefinition.define(context); | ||||
} | } | ||||
deprecatedDefConverter.complete(context); | |||||
context.setCurrentPluginKey(null); | context.setCurrentPluginKey(null); | ||||
coreCommonDefs.define(context); | coreCommonDefs.define(context); | ||||
return context; | return context; |
/* | |||||
* 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.Reader; | |||||
import java.util.Arrays; | |||||
import java.util.List; | |||||
import org.junit.Test; | |||||
import org.junit.runner.RunWith; | |||||
import org.mockito.Mock; | |||||
import org.mockito.runners.MockitoJUnitRunner; | |||||
import org.sonar.api.rule.RuleKey; | |||||
import org.sonar.api.rule.RuleStatus; | |||||
import org.sonar.api.rule.Severity; | |||||
import org.sonar.api.rules.Rule; | |||||
import org.sonar.api.rules.RulePriority; | |||||
import org.sonar.api.rules.RuleRepository; | |||||
import org.sonar.api.server.debt.DebtRemediationFunction; | |||||
import org.sonar.api.server.rule.RulesDefinition; | |||||
import org.sonar.api.utils.ValidationMessages; | |||||
import org.sonar.core.i18n.RuleI18nManager; | |||||
import org.sonar.api.impl.server.RulesDefinitionContext; | |||||
import org.sonar.server.debt.DebtModelPluginRepository; | |||||
import org.sonar.server.debt.DebtModelXMLExporter; | |||||
import org.sonar.server.debt.DebtRulesXMLImporter; | |||||
import org.sonar.server.plugins.ServerPluginRepository; | |||||
import static com.google.common.collect.Lists.newArrayList; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.junit.Assert.fail; | |||||
import static org.mockito.ArgumentMatchers.any; | |||||
import static org.mockito.ArgumentMatchers.eq; | |||||
import static org.mockito.Mockito.mock; | |||||
import static org.mockito.Mockito.when; | |||||
@RunWith(MockitoJUnitRunner.class) | |||||
public class DeprecatedRulesDefinitionLoaderTest { | |||||
@Mock | |||||
RuleI18nManager i18n; | |||||
@Mock | |||||
DebtModelPluginRepository debtModelRepository; | |||||
@Mock | |||||
DebtRulesXMLImporter importer; | |||||
@Mock | |||||
ServerPluginRepository pluginRepository; | |||||
static class CheckstyleRules extends RuleRepository { | |||||
public CheckstyleRules() { | |||||
super("checkstyle", "java"); | |||||
setName("Checkstyle"); | |||||
} | |||||
@Override | |||||
public List<Rule> createRules() { | |||||
Rule rule = Rule.create("checkstyle", "ConstantName", "Constant Name"); | |||||
rule.setDescription("Checks that constant names conform to the specified format"); | |||||
rule.setConfigKey("Checker/TreeWalker/ConstantName"); | |||||
rule.setSeverity(RulePriority.BLOCKER); | |||||
rule.setStatus(Rule.STATUS_BETA); | |||||
rule.setTags(new String[] {"style", "clumsy"}); | |||||
rule.createParameter("format").setDescription("Regular expression").setDefaultValue("A-Z").setType("REGULAR_EXPRESSION"); | |||||
return Arrays.asList(rule); | |||||
} | |||||
} | |||||
static class UseBundles extends RuleRepository { | |||||
public UseBundles() { | |||||
super("checkstyle", "java"); | |||||
setName("Checkstyle"); | |||||
} | |||||
@Override | |||||
public List<Rule> createRules() { | |||||
Rule rule = Rule.create("checkstyle", "ConstantName"); | |||||
rule.createParameter("format"); | |||||
return Arrays.asList(rule); | |||||
} | |||||
} | |||||
@Test | |||||
public void wrap_deprecated_rule_repositories() { | |||||
RulesDefinition.Context context = new RulesDefinitionContext(); | |||||
CheckstyleRules checkstyleRules = new CheckstyleRules(); | |||||
when(pluginRepository.getPluginKey(checkstyleRules)).thenReturn("unittest"); | |||||
new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, pluginRepository, new RuleRepository[] {checkstyleRules}).complete(context); | |||||
assertThat(context.repositories()).hasSize(1); | |||||
RulesDefinition.Repository checkstyle = context.repository("checkstyle"); | |||||
assertThat(checkstyle).isNotNull(); | |||||
assertThat(checkstyle.key()).isEqualTo("checkstyle"); | |||||
assertThat(checkstyle.name()).isEqualTo("Checkstyle"); | |||||
assertThat(checkstyle.language()).isEqualTo("java"); | |||||
assertThat(checkstyle.rules()).hasSize(1); | |||||
RulesDefinition.Rule rule = checkstyle.rule("ConstantName"); | |||||
assertThat(rule).isNotNull(); | |||||
assertThat(rule.key()).isEqualTo("ConstantName"); | |||||
assertThat(rule.pluginKey()).isEqualTo("unittest"); | |||||
assertThat(rule.name()).isEqualTo("Constant Name"); | |||||
assertThat(rule.htmlDescription()).isEqualTo("Checks that constant names conform to the specified format"); | |||||
assertThat(rule.severity()).isEqualTo(Severity.BLOCKER); | |||||
assertThat(rule.internalKey()).isEqualTo("Checker/TreeWalker/ConstantName"); | |||||
assertThat(rule.status()).isEqualTo(RuleStatus.BETA); | |||||
assertThat(rule.tags()).containsOnly("style", "clumsy"); | |||||
assertThat(rule.params()).hasSize(1); | |||||
RulesDefinition.Param param = rule.param("format"); | |||||
assertThat(param).isNotNull(); | |||||
assertThat(param.key()).isEqualTo("format"); | |||||
assertThat(param.name()).isEqualTo("format"); | |||||
assertThat(param.description()).isEqualTo("Regular expression"); | |||||
assertThat(param.defaultValue()).isEqualTo("A-Z"); | |||||
} | |||||
@Test | |||||
public void emulate_the_day_deprecated_api_can_be_dropped() { | |||||
RulesDefinition.Context context = new RulesDefinitionContext(); | |||||
// no more RuleRepository ! | |||||
new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, pluginRepository); | |||||
assertThat(context.repositories()).isEmpty(); | |||||
} | |||||
@Test | |||||
public void use_l10n_bundles() { | |||||
RulesDefinition.Context context = new RulesDefinitionContext(); | |||||
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"); | |||||
new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, pluginRepository, new RuleRepository[] {new UseBundles()}).complete(context); | |||||
RulesDefinition.Repository checkstyle = context.repository("checkstyle"); | |||||
RulesDefinition.Rule rule = checkstyle.rule("ConstantName"); | |||||
assertThat(rule.key()).isEqualTo("ConstantName"); | |||||
assertThat(rule.name()).isEqualTo("Constant Name"); | |||||
assertThat(rule.htmlDescription()).isEqualTo("Checks that constant names conform to the specified format"); | |||||
RulesDefinition.Param param = rule.param("format"); | |||||
assertThat(param.key()).isEqualTo("format"); | |||||
assertThat(param.name()).isEqualTo("format"); | |||||
assertThat(param.description()).isEqualTo("Regular expression"); | |||||
} | |||||
@Test | |||||
public void define_rule_debt() { | |||||
RulesDefinition.Context context = new RulesDefinitionContext(); | |||||
List<DebtModelXMLExporter.RuleDebt> ruleDebts = newArrayList( | |||||
new DebtModelXMLExporter.RuleDebt() | |||||
.setRuleKey(RuleKey.of("checkstyle", "ConstantName")) | |||||
.setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name()) | |||||
.setCoefficient("1d") | |||||
.setOffset("10min")); | |||||
Reader javaModelReader = mock(Reader.class); | |||||
when(debtModelRepository.createReaderForXMLFile("java")).thenReturn(javaModelReader); | |||||
when(debtModelRepository.getContributingPluginList()).thenReturn(newArrayList("java")); | |||||
when(importer.importXML(eq(javaModelReader), any(ValidationMessages.class))).thenReturn(ruleDebts); | |||||
new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, pluginRepository, new RuleRepository[] {new CheckstyleRules()}).complete(context); | |||||
assertThat(context.repositories()).hasSize(1); | |||||
RulesDefinition.Repository checkstyle = context.repository("checkstyle"); | |||||
assertThat(checkstyle.rules()).hasSize(1); | |||||
RulesDefinition.Rule rule = checkstyle.rule("ConstantName"); | |||||
assertThat(rule).isNotNull(); | |||||
assertThat(rule.key()).isEqualTo("ConstantName"); | |||||
assertThat(rule.debtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET); | |||||
assertThat(rule.debtRemediationFunction().gapMultiplier()).isEqualTo("1d"); | |||||
assertThat(rule.debtRemediationFunction().baseEffort()).isEqualTo("10min"); | |||||
} | |||||
@Test | |||||
public void fail_on_invalid_rule_debt() { | |||||
RulesDefinition.Context context = new RulesDefinitionContext(); | |||||
List<DebtModelXMLExporter.RuleDebt> ruleDebts = newArrayList( | |||||
new DebtModelXMLExporter.RuleDebt() | |||||
.setRuleKey(RuleKey.of("checkstyle", "ConstantName")) | |||||
.setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name()) | |||||
.setCoefficient("1d")); | |||||
Reader javaModelReader = mock(Reader.class); | |||||
when(debtModelRepository.createReaderForXMLFile("java")).thenReturn(javaModelReader); | |||||
when(debtModelRepository.getContributingPluginList()).thenReturn(newArrayList("java")); | |||||
when(importer.importXML(eq(javaModelReader), any(ValidationMessages.class))).thenReturn(ruleDebts); | |||||
try { | |||||
new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, pluginRepository, new RuleRepository[] {new CheckstyleRules()}).complete(context); | |||||
fail(); | |||||
} catch (Exception e) { | |||||
assertThat(e).isInstanceOf(IllegalArgumentException.class); | |||||
} | |||||
assertThat(context.repositories()).isEmpty(); | |||||
} | |||||
} |
private void execute(RulesDefinition... defs) { | private void execute(RulesDefinition... defs) { | ||||
ServerPluginRepository pluginRepository = mock(ServerPluginRepository.class); | ServerPluginRepository pluginRepository = mock(ServerPluginRepository.class); | ||||
when(pluginRepository.getPluginKey(any(RulesDefinition.class))).thenReturn(FAKE_PLUGIN_KEY); | when(pluginRepository.getPluginKey(any(RulesDefinition.class))).thenReturn(FAKE_PLUGIN_KEY); | ||||
RuleDefinitionsLoader loader = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), mock(CommonRuleDefinitionsImpl.class), pluginRepository, | |||||
RuleDefinitionsLoader loader = new RuleDefinitionsLoader(mock(CommonRuleDefinitionsImpl.class), pluginRepository, | |||||
defs); | defs); | ||||
Languages languages = mock(Languages.class); | Languages languages = mock(Languages.class); | ||||
when(languages.get(any())).thenReturn(mock(Language.class)); | when(languages.get(any())).thenReturn(mock(Language.class)); |
@Test | @Test | ||||
public void no_definitions() { | public void no_definitions() { | ||||
CommonRuleDefinitions commonRulesDefinitions = mock(CommonRuleDefinitions.class); | CommonRuleDefinitions commonRulesDefinitions = mock(CommonRuleDefinitions.class); | ||||
RulesDefinition.Context context = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), commonRulesDefinitions, mock(ServerPluginRepository.class)).load(); | |||||
RulesDefinition.Context context = new RuleDefinitionsLoader(commonRulesDefinitions, mock(ServerPluginRepository.class)).load(); | |||||
assertThat(context.repositories()).isEmpty(); | assertThat(context.repositories()).isEmpty(); | ||||
} | } | ||||
@Test | @Test | ||||
public void load_definitions() { | public void load_definitions() { | ||||
CommonRuleDefinitions commonRulesDefinitions = mock(CommonRuleDefinitions.class); | CommonRuleDefinitions commonRulesDefinitions = mock(CommonRuleDefinitions.class); | ||||
RulesDefinition.Context context = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), commonRulesDefinitions, mock(ServerPluginRepository.class), | |||||
RulesDefinition.Context context = new RuleDefinitionsLoader(commonRulesDefinitions, mock(ServerPluginRepository.class), | |||||
new RulesDefinition[] { | new RulesDefinition[] { | ||||
new FindbugsDefinitions(), new SquidDefinitions() | new FindbugsDefinitions(), new SquidDefinitions() | ||||
}).load(); | }).load(); | ||||
@Test | @Test | ||||
public void define_common_rules() { | public void define_common_rules() { | ||||
CommonRuleDefinitions commonRulesDefinitions = new FakeCommonRuleDefinitions(); | CommonRuleDefinitions commonRulesDefinitions = new FakeCommonRuleDefinitions(); | ||||
RulesDefinition.Context context = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), commonRulesDefinitions, mock(ServerPluginRepository.class), | |||||
RulesDefinition.Context context = new RuleDefinitionsLoader(commonRulesDefinitions, mock(ServerPluginRepository.class), | |||||
new RulesDefinition[] { | new RulesDefinition[] { | ||||
new SquidDefinitions() | new SquidDefinitions() | ||||
}).load(); | }).load(); | ||||
@Test | @Test | ||||
public void plugin_common_rules_are_overridden() { | public void plugin_common_rules_are_overridden() { | ||||
CommonRuleDefinitions commonRulesDefinitions = new FakeCommonRuleDefinitions(); | CommonRuleDefinitions commonRulesDefinitions = new FakeCommonRuleDefinitions(); | ||||
RulesDefinition.Context context = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), commonRulesDefinitions, mock(ServerPluginRepository.class), | |||||
RulesDefinition.Context context = new RuleDefinitionsLoader(commonRulesDefinitions, mock(ServerPluginRepository.class), | |||||
new RulesDefinition[] { | new RulesDefinition[] { | ||||
new PluginCommonRuleDefinitions() | new PluginCommonRuleDefinitions() | ||||
}).load(); | }).load(); |
import org.sonar.api.utils.Durations; | import org.sonar.api.utils.Durations; | ||||
import org.sonar.core.extension.CoreExtensionsInstaller; | import org.sonar.core.extension.CoreExtensionsInstaller; | ||||
import org.sonar.core.i18n.RuleI18nManager; | |||||
import org.sonar.core.platform.ComponentContainer; | import org.sonar.core.platform.ComponentContainer; | ||||
import org.sonar.core.platform.PluginClassloaderFactory; | import org.sonar.core.platform.PluginClassloaderFactory; | ||||
import org.sonar.core.platform.PluginLoader; | import org.sonar.core.platform.PluginLoader; | ||||
// depends on plugins | // depends on plugins | ||||
ServerI18n.class, | ServerI18n.class, | ||||
RuleI18nManager.class, | |||||
OfficialDistribution.class); | OfficialDistribution.class); | ||||
import org.sonar.server.qualityprofile.ws.QProfilesWsModule; | import org.sonar.server.qualityprofile.ws.QProfilesWsModule; | ||||
import org.sonar.server.root.ws.RootWsModule; | import org.sonar.server.root.ws.RootWsModule; | ||||
import org.sonar.server.rule.CommonRuleDefinitionsImpl; | import org.sonar.server.rule.CommonRuleDefinitionsImpl; | ||||
import org.sonar.server.rule.DeprecatedRulesDefinitionLoader; | |||||
import org.sonar.server.rule.RuleCreator; | import org.sonar.server.rule.RuleCreator; | ||||
import org.sonar.server.rule.RuleDefinitionsLoader; | import org.sonar.server.rule.RuleDefinitionsLoader; | ||||
import org.sonar.server.rule.RuleUpdater; | import org.sonar.server.rule.RuleUpdater; | ||||
AnnotationRuleParser.class, | AnnotationRuleParser.class, | ||||
XMLRuleParser.class, | XMLRuleParser.class, | ||||
WebServerRuleFinderImpl.class, | WebServerRuleFinderImpl.class, | ||||
DeprecatedRulesDefinitionLoader.class, | |||||
RuleDefinitionsLoader.class, | RuleDefinitionsLoader.class, | ||||
CommonRuleDefinitionsImpl.class, | CommonRuleDefinitionsImpl.class, | ||||
RulesDefinitionXmlLoader.class, | RulesDefinitionXmlLoader.class, |
/* | |||||
* 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.core.i18n; | |||||
import java.util.Locale; | |||||
import javax.annotation.CheckForNull; | |||||
import org.apache.commons.lang.StringUtils; | |||||
import org.sonar.api.ce.ComputeEngineSide; | |||||
import org.sonar.api.i18n.RuleI18n; | |||||
import org.sonar.api.rules.Rule; | |||||
import org.sonar.api.server.ServerSide; | |||||
/** | |||||
* @deprecated in 4.1. Rules are not localized anymore. See http://jira.sonarsource.com/browse/SONAR-4885 | |||||
*/ | |||||
@Deprecated | |||||
@ServerSide | |||||
@ComputeEngineSide | |||||
public class RuleI18nManager implements RuleI18n { | |||||
private static final String NAME_SUFFIX = ".name"; | |||||
private static final String RULE_PREFIX = "rule."; | |||||
private DefaultI18n defaultI18n; | |||||
public RuleI18nManager(DefaultI18n defaultI18n) { | |||||
this.defaultI18n = defaultI18n; | |||||
} | |||||
/** | |||||
* @deprecated in 4.1. Rules are not localized anymore. See http://jira.sonarsource.com/browse/SONAR-4885 | |||||
*/ | |||||
@Override | |||||
@Deprecated | |||||
public String getName(String repositoryKey, String ruleKey, Locale locale) { | |||||
return getName(repositoryKey, ruleKey); | |||||
} | |||||
/** | |||||
* @deprecated in 4.1. Rules are not localized anymore. See http://jira.sonarsource.com/browse/SONAR-4885 | |||||
*/ | |||||
@Override | |||||
@Deprecated | |||||
public String getName(Rule rule, Locale locale) { | |||||
return getName(rule); | |||||
} | |||||
/** | |||||
* @deprecated in 4.1. Rules are not localized anymore. See http://jira.sonarsource.com/browse/SONAR-4885 | |||||
*/ | |||||
@Override | |||||
@Deprecated | |||||
public String getDescription(String repositoryKey, String ruleKey, Locale locale) { | |||||
return getDescription(repositoryKey, ruleKey); | |||||
} | |||||
/** | |||||
* @deprecated in 4.1. Rules are not localized anymore. See http://jira.sonarsource.com/browse/SONAR-4885 | |||||
*/ | |||||
@Override | |||||
@Deprecated | |||||
public String getParamDescription(String repositoryKey, String ruleKey, String paramKey, Locale locale) { | |||||
return getParamDescription(repositoryKey, ruleKey, paramKey); | |||||
} | |||||
@Override | |||||
@CheckForNull | |||||
public String getName(String repositoryKey, String ruleKey) { | |||||
return message(repositoryKey, ruleKey, NAME_SUFFIX); | |||||
} | |||||
@Override | |||||
@CheckForNull | |||||
public String getName(Rule rule) { | |||||
String name = message(rule.getRepositoryKey(), rule.getKey(), NAME_SUFFIX); | |||||
return name != null ? name : rule.getName(); | |||||
} | |||||
@Override | |||||
public String getDescription(String repositoryKey, String ruleKey) { | |||||
String relatedProperty = new StringBuilder().append(RULE_PREFIX).append(repositoryKey).append(".").append(ruleKey).append(NAME_SUFFIX).toString(); | |||||
String ruleDescriptionFilePath = "rules/" + repositoryKey + "/" + ruleKey + ".html"; | |||||
String description = defaultI18n.messageFromFile(Locale.ENGLISH, ruleDescriptionFilePath, relatedProperty); | |||||
if (description == null) { | |||||
// Following line is to ensure backward compatibility (SONAR-3319) | |||||
description = lookUpDescriptionInFormerLocation(ruleKey, relatedProperty); | |||||
} | |||||
return description; | |||||
} | |||||
/* | |||||
* Method used to ensure backward compatibility for language plugins that store HTML rule description files in the former | |||||
* location (which was used prior to Sonar 3.0). | |||||
* | |||||
* See http://jira.sonarsource.com/browse/SONAR-3319 | |||||
*/ | |||||
private String lookUpDescriptionInFormerLocation(String ruleKey, String relatedProperty) { | |||||
return defaultI18n.messageFromFile(Locale.ENGLISH, ruleKey + ".html", relatedProperty); | |||||
} | |||||
@Override | |||||
@CheckForNull | |||||
public String getParamDescription(String repositoryKey, String ruleKey, String paramKey) { | |||||
return message(repositoryKey, ruleKey, ".param." + paramKey); | |||||
} | |||||
@CheckForNull | |||||
String message(String repositoryKey, String ruleKey, String suffix) { | |||||
String propertyKey = new StringBuilder().append(RULE_PREFIX).append(repositoryKey).append(".").append(ruleKey).append(suffix).toString(); | |||||
return defaultI18n.message(Locale.ENGLISH, propertyKey, null); | |||||
} | |||||
static boolean isRuleProperty(String propertyKey) { | |||||
return StringUtils.startsWith(propertyKey, RULE_PREFIX) && StringUtils.endsWith(propertyKey, NAME_SUFFIX) && !propertyKey.contains(".param."); | |||||
} | |||||
} |
/* | |||||
* 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.core.i18n; | |||||
import java.util.Locale; | |||||
import org.junit.Test; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.mockito.Mockito.mock; | |||||
import static org.mockito.Mockito.verify; | |||||
import static org.mockito.Mockito.verifyNoMoreInteractions; | |||||
import static org.mockito.Mockito.when; | |||||
public class RuleI18nManagerTest { | |||||
@Test | |||||
public void shouldGetName() { | |||||
DefaultI18n i18n = mock(DefaultI18n.class); | |||||
RuleI18nManager ruleI18n = new RuleI18nManager(i18n); | |||||
ruleI18n.getName("checkstyle", "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck", Locale.ENGLISH); | |||||
String propertyKey = "rule.checkstyle.com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.name"; | |||||
verify(i18n).message(Locale.ENGLISH, propertyKey, null /* no default value */); | |||||
verifyNoMoreInteractions(i18n); | |||||
} | |||||
@Test | |||||
public void shouldGetParamDescription() { | |||||
DefaultI18n i18n = mock(DefaultI18n.class); | |||||
RuleI18nManager ruleI18n = new RuleI18nManager(i18n); | |||||
ruleI18n.getParamDescription("checkstyle", "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck", "pattern", Locale.ENGLISH); | |||||
String propertyKey = "rule.checkstyle.com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.param.pattern"; | |||||
verify(i18n).message(Locale.ENGLISH, propertyKey, null /* no default value */); | |||||
verifyNoMoreInteractions(i18n); | |||||
} | |||||
@Test | |||||
public void shouldGetDescriptionFromFile() { | |||||
String propertyKeyForName = "rule.checkstyle.com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.name"; | |||||
DefaultI18n i18n = mock(DefaultI18n.class); | |||||
when(i18n.messageFromFile(Locale.ENGLISH, "rules/checkstyle/com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.html", propertyKeyForName)).thenReturn( | |||||
"Description"); | |||||
RuleI18nManager ruleI18n = new RuleI18nManager(i18n); | |||||
String description = ruleI18n.getDescription("checkstyle", "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck", Locale.ENGLISH); | |||||
assertThat(description).isEqualTo("Description"); | |||||
verify(i18n).messageFromFile(Locale.ENGLISH, "rules/checkstyle/com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.html", propertyKeyForName); | |||||
verifyNoMoreInteractions(i18n); | |||||
} | |||||
// see http://jira.sonarsource.com/browse/SONAR-3319 | |||||
@Test | |||||
public void shouldGetDescriptionFromFileWithBackwardCompatibility() { | |||||
String propertyKeyForName = "rule.checkstyle.com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.name"; | |||||
DefaultI18n i18n = mock(DefaultI18n.class); | |||||
// this is the "old" way of storing HTML description files for rules (they are not in the "rules/<repo-key>" folder) | |||||
when(i18n.messageFromFile(Locale.ENGLISH, "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.html", propertyKeyForName)).thenReturn("Description"); | |||||
RuleI18nManager ruleI18n = new RuleI18nManager(i18n); | |||||
String description = ruleI18n.getDescription("checkstyle", "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck", Locale.ENGLISH); | |||||
assertThat(description).isEqualTo("Description"); | |||||
verify(i18n).messageFromFile(Locale.ENGLISH, "rules/checkstyle/com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.html", propertyKeyForName); | |||||
verify(i18n).messageFromFile(Locale.ENGLISH, "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.html", propertyKeyForName); | |||||
verifyNoMoreInteractions(i18n); | |||||
} | |||||
@Test | |||||
public void shoudlReturnNullIfMissingDescription() { | |||||
DefaultI18n i18n = mock(DefaultI18n.class); | |||||
RuleI18nManager ruleI18n = new RuleI18nManager(i18n); | |||||
assertThat(ruleI18n.getDescription("checkstyle", "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck", Locale.ENGLISH)).isNull(); | |||||
} | |||||
@Test | |||||
public void shouldBeRuleKey() { | |||||
assertThat(RuleI18nManager.isRuleProperty("rule.checkstyle.com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.name")).isTrue(); | |||||
assertThat(RuleI18nManager.isRuleProperty("rule.pmd.Header.name")).isTrue(); | |||||
} | |||||
@Test | |||||
public void shouldNotBeRuleKey() { | |||||
// this is the parameter "name" | |||||
assertThat(RuleI18nManager.isRuleProperty("rule.checkstyle.com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.param.name")).isFalse(); | |||||
assertThat(RuleI18nManager.isRuleProperty("by")).isFalse(); | |||||
assertThat(RuleI18nManager.isRuleProperty("something.else")).isFalse(); | |||||
assertThat(RuleI18nManager.isRuleProperty("checkstyle.page.name")).isFalse(); | |||||
} | |||||
} |
/* | |||||
* 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.i18n; | |||||
import java.util.Locale; | |||||
import javax.annotation.CheckForNull; | |||||
import org.sonar.api.ce.ComputeEngineSide; | |||||
import org.sonar.api.rules.Rule; | |||||
import org.sonar.api.server.ServerSide; | |||||
/** | |||||
* {@link I18n}-companion component that provides translation facilities for rule names, descriptions and parameter names. | |||||
* | |||||
* @since 3.2 | |||||
* @deprecated in 4.1. Rules are not localized anymore. See http://jira.sonarsource.com/browse/SONAR-4885 | |||||
*/ | |||||
@Deprecated | |||||
@ServerSide | |||||
@ComputeEngineSide | |||||
public interface RuleI18n { | |||||
/** | |||||
* Returns the localized name of the rule identified by its repository key and rule key. | |||||
* <br> | |||||
* If the name is not found in the given locale, then the default name is returned (the English one). | |||||
* This method could return null if no default name found. This is the cause for instance the copies rules. | |||||
* | |||||
* @param repositoryKey the repository key | |||||
* @param ruleKey the rule key | |||||
* @param locale not used | |||||
* @return the translated name of the rule, or the default English one if the given locale is not supported, or null | |||||
* @deprecated since 4.1. Rules are not localized anymore. See http://jira.sonarsource.com/browse/SONAR-4885 | |||||
*/ | |||||
@Deprecated | |||||
@CheckForNull | |||||
String getName(String repositoryKey, String ruleKey, Locale locale); | |||||
/** | |||||
* Returns the name of the rule identified by its repository key and rule key. | |||||
* <br> | |||||
* This method could return null if no default name found. This is the cause for instance the copies rules. | |||||
* | |||||
* @param repositoryKey the repository key | |||||
* @param ruleKey the rule key | |||||
* @return the nullable name of the rule | |||||
* @since 4.1 | |||||
*/ | |||||
@CheckForNull | |||||
String getName(String repositoryKey, String ruleKey); | |||||
/** | |||||
* Returns the localized name or the name of the rule. | |||||
* <br> | |||||
* If the name is not found in the given locale, then the default name is returned (the English one). | |||||
* It the default name is not found, then the rule name is returned. | |||||
* | |||||
* @param rule the rule | |||||
* @param locale the locale to translate into | |||||
* @return the translated name of the rule, or the default English one if the given locale is not supported, or the rule name. | |||||
* @deprecated since 4.1. Rules are not localized anymore. See http://jira.sonarsource.com/browse/SONAR-4885 | |||||
*/ | |||||
@Deprecated | |||||
@CheckForNull | |||||
String getName(Rule rule, Locale locale); | |||||
/** | |||||
* Returns the name of the rule. | |||||
* <br> | |||||
* It the default name is not found, then the rule name is returned. | |||||
* | |||||
* @param rule the rule | |||||
* @return the nullable name of the rule | |||||
* @since 4.1 | |||||
*/ | |||||
@CheckForNull | |||||
String getName(Rule rule); | |||||
/** | |||||
* Returns the localized description of the rule identified by its repository key and rule key. | |||||
* <br> | |||||
* If the description is not found in the given locale, then the default description is returned (the English one). | |||||
* As a rule must have a description (this is a constraint in Sonar), this method never returns null. | |||||
* | |||||
* @param repositoryKey the repository key | |||||
* @param ruleKey the rule key | |||||
* @param locale the locale to translate into | |||||
* @return the translated description of the rule, or the default English one if the given locale is not supported | |||||
* @deprecated since 4.1. Rules are not localized anymore. See http://jira.sonarsource.com/browse/SONAR-4885 | |||||
*/ | |||||
@Deprecated | |||||
String getDescription(String repositoryKey, String ruleKey, Locale locale); | |||||
/** | |||||
* Returns the description of the rule identified by its repository key and rule key. | |||||
* <br> | |||||
* As a rule must have a description (this is a constraint in SonarQube), this method never returns null. | |||||
* | |||||
* @param repositoryKey the repository key | |||||
* @param ruleKey the rule key | |||||
* @return the description of the rule | |||||
* @since 4.1 | |||||
*/ | |||||
String getDescription(String repositoryKey, String ruleKey); | |||||
/** | |||||
* Returns the localized name of the rule parameter identified by the rules's key and repository key, and by the parameter key. | |||||
* <br> | |||||
* If the name is not found in the given locale, then the English translation is searched and return if found. Otherwise, | |||||
* this method returns null (= if no translation can be found). | |||||
* | |||||
* @param repositoryKey the repository key | |||||
* @param ruleKey the rule key | |||||
* @param paramKey the parameter key | |||||
* @param locale the locale to translate into | |||||
* @return the translated name of the rule parameter, or the default English one if the given locale is not supported, or null if | |||||
* no translation can be found. | |||||
* @deprecated since 4.1. Rules are not localized anymore. See http://jira.sonarsource.com/browse/SONAR-4885 | |||||
*/ | |||||
@Deprecated | |||||
@CheckForNull | |||||
String getParamDescription(String repositoryKey, String ruleKey, String paramKey, Locale locale); | |||||
/** | |||||
* Returns the name of the rule parameter identified by the rules's key and repository key, and by the parameter key. | |||||
* | |||||
* @param repositoryKey the repository key | |||||
* @param ruleKey the rule key | |||||
* @param paramKey the parameter key | |||||
* @return the nullable name of the rule parameter | |||||
* @since 4.1 | |||||
*/ | |||||
@CheckForNull | |||||
String getParamDescription(String repositoryKey, String ruleKey, String paramKey); | |||||
} |
/* | |||||
* 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.rules; | |||||
import java.util.List; | |||||
import org.apache.commons.lang.StringUtils; | |||||
import org.apache.commons.lang.builder.ToStringBuilder; | |||||
import org.apache.commons.lang.builder.ToStringStyle; | |||||
import org.sonar.api.ExtensionPoint; | |||||
import org.sonar.api.ce.ComputeEngineSide; | |||||
import org.sonar.api.server.ServerSide; | |||||
/** | |||||
* @since 2.3 | |||||
* @deprecated in 4.2. Replaced by org.sonar.api.server.rule.RulesDefinition | |||||
*/ | |||||
@Deprecated | |||||
@ServerSide | |||||
@ComputeEngineSide | |||||
@ExtensionPoint | |||||
public abstract class RuleRepository { | |||||
private String key; | |||||
private String language; | |||||
private String name; | |||||
protected RuleRepository(String key, String language) { | |||||
this.key = key; | |||||
this.language = language; | |||||
} | |||||
public final String getKey() { | |||||
return key; | |||||
} | |||||
public final String getLanguage() { | |||||
return language; | |||||
} | |||||
public final String getName() { | |||||
return name; | |||||
} | |||||
public final String getName(boolean useKeyIfEmpty) { | |||||
if (useKeyIfEmpty) { | |||||
return StringUtils.defaultIfEmpty(name, key); | |||||
} | |||||
return name; | |||||
} | |||||
public final RuleRepository setName(String s) { | |||||
this.name = s; | |||||
return this; | |||||
} | |||||
public abstract List<Rule> createRules(); | |||||
@Override | |||||
public String toString() { | |||||
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) | |||||
.append("key", key) | |||||
.append("language", language) | |||||
.append("name", name) | |||||
.toString(); | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU Lesser General Public | |||||
* License as published by the Free Software Foundation; either | |||||
* version 3 of the License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
* Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program; if not, write to the Free Software Foundation, | |||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
*/ | |||||
package org.sonar.api.server.rule; | |||||
import org.apache.commons.lang.StringUtils; | |||||
import org.sonar.api.i18n.RuleI18n; | |||||
/** | |||||
* Loads the English bundles of rules (name, description and parameters) that are | |||||
* deprecated since 4.2. It can be useful when loading existing XML files that | |||||
* do not contain rule names and descriptions. | |||||
* <br> | |||||
* This class must be executed after declaring rules on {@link RulesDefinition.NewRepository}. | |||||
* <br> | |||||
* Note that localization of rules was dropped in version 4.2. All texts are English. | |||||
* | |||||
* @see org.sonar.api.server.rule.RulesDefinition for an example | |||||
* @since 4.3 | |||||
*/ | |||||
public class RulesDefinitionI18nLoader { | |||||
private final RuleI18n i18n; | |||||
public RulesDefinitionI18nLoader(RuleI18n i18n) { | |||||
this.i18n = i18n; | |||||
} | |||||
/** | |||||
* Loads descriptions of rules and related rule parameters. Existing descriptions | |||||
* are overridden if English labels exist in bundles. | |||||
*/ | |||||
public void load(RulesDefinition.NewRepository repo) { | |||||
for (RulesDefinition.NewRule rule : repo.rules()) { | |||||
String name = i18n.getName(repo.key(), rule.key()); | |||||
if (StringUtils.isNotBlank(name)) { | |||||
rule.setName(name); | |||||
} | |||||
String desc = i18n.getDescription(repo.key(), rule.key()); | |||||
if (StringUtils.isNotBlank(desc)) { | |||||
rule.setHtmlDescription(desc); | |||||
} | |||||
for (RulesDefinition.NewParam param : rule.params()) { | |||||
String paramDesc = i18n.getParamDescription(repo.key(), rule.key(), param.key()); | |||||
if (StringUtils.isNotBlank(paramDesc)) { | |||||
param.setDescription(paramDesc); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU Lesser General Public | |||||
* License as published by the Free Software Foundation; either | |||||
* version 3 of the License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
* Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program; if not, write to the Free Software Foundation, | |||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
*/ | |||||
package org.sonar.api.server.rule; | |||||
import org.junit.Test; | |||||
import org.sonar.api.i18n.RuleI18n; | |||||
import org.sonar.api.impl.server.RulesDefinitionContext; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.mockito.Mockito.mock; | |||||
import static org.mockito.Mockito.when; | |||||
public class RulesDefinitionI18nLoaderTest { | |||||
RuleI18n i18n = mock(RuleI18n.class); | |||||
RulesDefinitionI18nLoader loader = new RulesDefinitionI18nLoader(i18n); | |||||
@Test | |||||
public void complete_rule_name_and_description() { | |||||
when(i18n.getName("squid", "S0001")).thenReturn("SOne"); | |||||
when(i18n.getDescription("squid", "S0001")).thenReturn("S One"); | |||||
RulesDefinition.Context context = new RulesDefinitionContext(); | |||||
RulesDefinition.NewRepository repo = context.createRepository("squid", "java"); | |||||
// rule without description | |||||
repo.createRule("S0001"); | |||||
loader.load(repo); | |||||
repo.done(); | |||||
RulesDefinition.Rule rule = context.repository("squid").rule("S0001"); | |||||
assertThat(rule.name()).isEqualTo("SOne"); | |||||
assertThat(rule.htmlDescription()).isEqualTo("S One"); | |||||
} | |||||
@Test | |||||
public void do_not_override_if_no_bundle() { | |||||
// i18n returns null values | |||||
RulesDefinition.Context context = new RulesDefinitionContext(); | |||||
RulesDefinition.NewRepository repo = context.createRepository("squid", "java"); | |||||
repo.createRule("S0001").setName("SOne").setHtmlDescription("S One"); | |||||
loader.load(repo); | |||||
repo.done(); | |||||
RulesDefinition.Rule rule = context.repository("squid").rule("S0001"); | |||||
assertThat(rule.name()).isEqualTo("SOne"); | |||||
assertThat(rule.htmlDescription()).isEqualTo("S One"); | |||||
} | |||||
@Test | |||||
public void override_existing() { | |||||
when(i18n.getName("squid", "S0001")).thenReturn("SOne"); | |||||
when(i18n.getDescription("squid", "S0001")).thenReturn("S One"); | |||||
RulesDefinition.Context context = new RulesDefinitionContext(); | |||||
RulesDefinition.NewRepository repo = context.createRepository("squid", "java"); | |||||
repo.createRule("S0001").setName("Bad").setHtmlDescription("Bad"); | |||||
loader.load(repo); | |||||
repo.done(); | |||||
RulesDefinition.Rule rule = context.repository("squid").rule("S0001"); | |||||
assertThat(rule.name()).isEqualTo("SOne"); | |||||
assertThat(rule.htmlDescription()).isEqualTo("S One"); | |||||
} | |||||
@Test | |||||
public void complete_param_description() { | |||||
when(i18n.getParamDescription("squid", "S0001", "max")).thenReturn("Maximum"); | |||||
RulesDefinition.Context context = new RulesDefinitionContext(); | |||||
RulesDefinition.NewRepository repo = context.createRepository("squid", "java"); | |||||
repo.createRule("S0001").setName("SOne").setHtmlDescription("S One").createParam("max"); | |||||
loader.load(repo); | |||||
repo.done(); | |||||
RulesDefinition.Rule rule = context.repository("squid").rule("S0001"); | |||||
assertThat(rule.param("max").description()).isEqualTo("Maximum"); | |||||
} | |||||
} |