All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.api.server.rule.RulesDefinition Maven / Gradle / Ivy

There is a newer version: 10.5.0.78949
Show newest version
/*
 * SonarQube
 * Copyright (C) 2009-2021 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 java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
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.rules.RuleType;
import org.sonar.api.server.ServerSide;
import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.api.server.rule.internal.DefaultNewRepository;
import org.sonar.api.server.rule.internal.DefaultRepository;
import org.sonarsource.api.sonarlint.SonarLintSide;

import static java.util.Collections.unmodifiableList;
import static org.sonar.api.utils.Preconditions.checkState;

/**
 * Defines some coding rules of the same repository. For example the Java Findbugs plugin provides an implementation of
 * this extension point in order to define the rules that it supports.
 * 
* This interface replaces the deprecated class org.sonar.api.rules.RuleRepository. *
*

How to use

*
 * public class MyJsRulesDefinition implements RulesDefinition {
 *   {@literal @}Override
 *   public void define(Context context) {
 *     NewRepository repository = context.createRepository("my_js", "js").setName("My Javascript Analyzer");
 *     // define a rule programmatically. Note that rules
 *     // could be loaded from files (JSON, XML, ...)
 *     NewRule x1Rule = repository.createRule("x1")
 *      .setName("No empty line")
 *      .setHtmlDescription("Generate an issue on empty lines")
 *      // optional tags
 *      .setTags("style", "stupid")
 *     // optional status. Default value is READY.
 *     .setStatus(RuleStatus.BETA)
 *     // default severity when the rule is activated on a Quality profile. Default value is MAJOR.
 *     .setSeverity(Severity.MINOR);
 *     // optional type for SonarQube Quality Model. Default is RulesDefinition.Type.CODE_SMELL.
 *     .setType(RulesDefinition.Type.BUG)
 *     x1Rule
 *       .setDebtRemediationFunction(x1Rule.debtRemediationFunctions().linearWithOffset("1h", "30min"));
 *     x1Rule.createParam("acceptWhitespace")
 *       .setDefaultValue("false")
 *       .setType(RuleParamType.BOOLEAN)
 *       .setDescription("Accept whitespaces on the line");
 *     // don't forget to call done() to finalize the definition
 *     repository.done();
 *   }
 * }
 * 
*
* If rules are declared in a XML file with the standard SonarQube format (see * {@link org.sonar.api.server.rule.RulesDefinitionXmlLoader}), then it can be loaded by using : *
*
 * public class MyJsRulesDefinition implements RulesDefinition {
 *   private final RulesDefinitionXmlLoader xmlLoader;
 *   public MyJsRulesDefinition(RulesDefinitionXmlLoader xmlLoader) {
 *     this.xmlLoader = xmlLoader;
 *   }
 *   {@literal @}Override
 *   public void define(Context context) {
 *     NewRepository repository = context.createRepository("my_js", "js").setName("My Javascript Analyzer");
 *     // see javadoc of RulesDefinitionXmlLoader for the format
 *     xmlLoader.load(repository, getClass().getResourceAsStream("/path/to/rules.xml"));
 *     repository.done();
 *   }
 * }
 * 
*
* In the above example, XML file must contain name and description of each rule. If it's not the case, then the * (deprecated) English bundles can be used : *
*
 * public class MyJsRulesDefinition implements RulesDefinition {
 *   private final RulesDefinitionXmlLoader xmlLoader;
 *   private final RulesDefinitionI18nLoader i18nLoader;
 *   public MyJsRulesDefinition(RulesDefinitionXmlLoader xmlLoader, RulesDefinitionI18nLoader i18nLoader) {
 *     this.xmlLoader = xmlLoader;
 *     this.i18nLoader = i18nLoader;
 *   }
 *   {@literal @}Override
 *   public void define(Context context) {
 *     NewRepository repository = context.createRepository("my_js", "js").setName("My Javascript Analyzer");
 *     xmlLoader.load(repository, getClass().getResourceAsStream("/path/to/rules.xml"), "UTF-8");
 *     i18nLoader.load(repository);
 *     repository.done();
 *   }
 * }
 * 
* * @since 4.3 */ @ServerSide @ComputeEngineSide @SonarLintSide @ExtensionPoint public interface RulesDefinition { /** * This implementation will be removed as soon as analyzers stop instantiating it. * Use RulesDefinitionContext in sonar-plugin-api-impl. */ class Context extends AbstractContext { private final Map repositoriesByKey = new HashMap<>(); private String currentPluginKey = null; @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 @CheckForNull public RulesDefinition.Repository repository(String key) { return repositoriesByKey.get(key); } @Override public List repositories() { return unmodifiableList(new ArrayList<>(repositoriesByKey.values())); } public void registerRepository(DefaultNewRepository newRepository) { RulesDefinition.Repository existing = repositoriesByKey.get(newRepository.key()); if (existing != null) { String existingLanguage = existing.language(); checkState(existingLanguage.equals(newRepository.language()), "The rule repository '%s' must not be defined for two different languages: %s and %s", newRepository.key(), existingLanguage, newRepository.language()); } repositoriesByKey.put(newRepository.key(), new DefaultRepository(newRepository, existing)); } public String currentPluginKey() { return currentPluginKey; } @Override public void setCurrentPluginKey(@Nullable String pluginKey) { this.currentPluginKey = pluginKey; } } /** * Instantiated by core but not by plugins, except for their tests. */ abstract class AbstractContext { /* * New builder for {@link org.sonar.api.server.rule.RulesDefinition.Repository}. *
* 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 abstract NewRepository createRepository(String key, String language); /** * Creates a repository of rules from external rule engines. * The repository key will be "external_[engineId]". * * @since 7.2 */ public abstract NewRepository createExternalRepository(String engineId, String language); @CheckForNull public abstract Repository repository(String key); public abstract List repositories(); public abstract void setCurrentPluginKey(@Nullable String pluginKey); } interface NewExtendedRepository { /** * Create a rule with specified key. Max length of key is 200 characters. Key must be unique * among the repository * * @throws IllegalArgumentException is key is not unique. Note a warning was logged up to version 5.4 (included) */ NewRule createRule(String ruleKey); @CheckForNull NewRule rule(String ruleKey); Collection rules(); String key(); void done(); } interface NewRepository extends NewExtendedRepository { NewRepository setName(String s); /** * @since 7.2 */ boolean isExternal(); } enum OwaspTop10 { A1, A2, A3, A4, A5, A6, A7, A8, A9, A10 } interface ExtendedRepository { String key(); String language(); @CheckForNull Rule rule(String ruleKey); List rules(); } interface Repository extends ExtendedRepository { String name(); /** * @since 7.2 */ boolean isExternal(); } /** * Factory of {@link org.sonar.api.server.debt.DebtRemediationFunction}. */ interface DebtRemediationFunctions { /** * Shortcut for {@code create(Type.LINEAR, gap multiplier, null)}. * * @param gapMultiplier the duration to fix one issue. See {@link DebtRemediationFunction} for details about format. * @see org.sonar.api.server.debt.DebtRemediationFunction.Type#LINEAR */ DebtRemediationFunction linear(String gapMultiplier); /** * Shortcut for {@code create(Type.LINEAR_OFFSET, gap multiplier, base effort)}. * * @param gapMultiplier duration to fix one point of complexity. See {@link DebtRemediationFunction} for details and format. * @param baseEffort duration to make basic analysis. See {@link DebtRemediationFunction} for details and format. * @see org.sonar.api.server.debt.DebtRemediationFunction.Type#LINEAR_OFFSET */ DebtRemediationFunction linearWithOffset(String gapMultiplier, String baseEffort); /** * Shortcut for {@code create(Type.CONSTANT_ISSUE, null, base effort)}. * * @param baseEffort cost per issue. See {@link DebtRemediationFunction} for details and format. * @see org.sonar.api.server.debt.DebtRemediationFunction.Type#CONSTANT_ISSUE */ DebtRemediationFunction constantPerIssue(String baseEffort); /** * Flexible way to create a {@link DebtRemediationFunction}. An unchecked exception is thrown if * coefficient and/or offset are not valid according to the given @{code type}. * * @since 5.3 */ DebtRemediationFunction create(DebtRemediationFunction.Type type, @Nullable String gapMultiplier, @Nullable String baseEffort); } abstract class NewRule { public abstract String key(); /** * @since 7.1 */ @CheckForNull public abstract RuleScope scope(); /** * @since 7.1 */ public abstract NewRule setScope(RuleScope scope); /** * Required rule name */ public abstract NewRule setName(String s); public abstract NewRule setTemplate(boolean template); /** * Should this rule be enabled by default. For example in SonarLint standalone. * * @since 6.0 */ public abstract NewRule setActivatedByDefault(boolean activatedByDefault); public abstract NewRule setSeverity(String s); /** * The type as defined by the SonarQube Quality Model. *
* When a plugin does not define rule type, then it is deduced from * tags: *
    *
  • if the rule has the "bug" tag then type is {@link RuleType#BUG}
  • *
  • if the rule has the "security" tag then type is {@link RuleType#VULNERABILITY}
  • *
  • if the rule has both tags "bug" and "security", then type is {@link RuleType#BUG}
  • *
  • default type is {@link RuleType#CODE_SMELL}
  • *
* Finally the "bug" and "security" tags are considered as reserved. They * are silently removed from the final state of definition. * * @since 5.5 */ public abstract 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 abstract NewRule setHtmlDescription(@Nullable String s); /** * Load description from a file available in classpath. Example : setHtmlDescription(getClass().getResource("/myrepo/Rule1234.html") */ public abstract 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 abstract NewRule setMarkdownDescription(@Nullable String s); /** * Load description from a file available in classpath. Example : {@code setMarkdownDescription(getClass().getResource("/myrepo/Rule1234.md")} */ public abstract 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 abstract NewRule setStatus(RuleStatus status); /** * Factory of {@link org.sonar.api.server.debt.DebtRemediationFunction} */ public abstract DebtRemediationFunctions debtRemediationFunctions(); /** * @see #debtRemediationFunctions() */ public abstract NewRule setDebtRemediationFunction(@Nullable DebtRemediationFunction fn); /** * For rules that use LINEAR or LINEAR_OFFSET remediation functions, the meaning * of the function parameter (= "gap") must be set. This description * explains what 1 point of "gap" represents for the rule. *
* Example: for the "Insufficient condition coverage", this description for the * remediation function gap multiplier/base effort would be something like * "Effort to test one uncovered condition". */ public abstract NewRule setGapDescription(@Nullable String s); /** * Create a parameter with given unique key. Max length of key is 128 characters. */ public abstract NewParam createParam(String paramKey); @CheckForNull public abstract NewParam param(String paramKey); public abstract Collection params(); /** * @see RuleTagFormat */ public abstract NewRule addTags(String... list); /** * @see RuleTagFormat */ public abstract NewRule setTags(String... list); /** * @since 7.3 */ public abstract NewRule addOwaspTop10(OwaspTop10... standards); /** * @since 7.3 */ public abstract 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 abstract NewRule setInternalKey(@Nullable String s); /** * Register a repository and key under which this rule used to be known * (see {@link Rule#deprecatedRuleKeys} for details). *

* Deprecated keys should be added with this method in order, oldest first, for documentation purpose. * * @throws IllegalArgumentException if {@code repository} or {@code key} is {@code null} or empty. * @see Rule#deprecatedRuleKeys * @since 7.1 */ public abstract NewRule addDeprecatedRuleKey(String repository, String key); } @Immutable abstract class Rule { public abstract Repository repository(); /** * @since 6.6 the plugin the rule was declared in */ @CheckForNull public abstract String pluginKey(); public abstract String key(); public abstract String name(); /** * @since 7.1 */ public abstract RuleScope scope(); /** * @see NewRule#setType(RuleType) * @since 5.5 */ public abstract RuleType type(); public abstract String severity(); @CheckForNull public abstract String htmlDescription(); @CheckForNull public abstract String markdownDescription(); public abstract boolean template(); /** * Should this rule be enabled by default. For example in SonarLint standalone. * * @since 6.0 */ public abstract boolean activatedByDefault(); public abstract RuleStatus status(); @CheckForNull public abstract DebtRemediationFunction debtRemediationFunction(); @CheckForNull public abstract String gapDescription(); @CheckForNull public abstract Param param(String key); public abstract List params(); public abstract Set tags(); public abstract Set securityStandards(); /** * Deprecated rules keys for this rule. *

* If you want to rename the key of a rule or change its repository or both, register the rule's previous repository * and key (see {@link NewRule#addDeprecatedRuleKey(String, String) addDeprecatedRuleKey}). This will allow * SonarQube to support "issue re-keying" for this rule. *

* If the repository and/or key of an existing rule is changed without declaring deprecated keys, existing issues * for this rule, created under the rule's previous repository and/or key, will be closed and new ones will be * created under the issue's new repository and/or key. *

* Several deprecated keys can be provided to allow SonarQube to support several key (and/or repository) changes * across multiple versions of a plugin. *
* Consider the following use case scenario: *

    *
  • Rule {@code Foo:A} is defined in version 1 of the plugin *
         * NewRepository newRepository = context.createRepository("Foo", "my_language");
         * NewRule r = newRepository.createRule("A");
         * 
    *
  • *
  • Rule's key is renamed to B in version 2 of the plugin *
         * NewRepository newRepository = context.createRepository("Foo", "my_language");
         * NewRule r = newRepository.createRule("B")
         *   .addDeprecatedRuleKey("Foo", "A");
         * 
    *
  • *
  • All rules, including {@code Foo:B}, are moved to a new repository Bar in version 3 of the plugin *
         * NewRepository newRepository = context.createRepository("Bar", "my_language");
         * NewRule r = newRepository.createRule("B")
         *   .addDeprecatedRuleKey("Foo", "A")
         *   .addDeprecatedRuleKey("Bar", "B");
         * 
    *
  • *
* With all deprecated keys defined in version 3 of the plugin, SonarQube will be able to support "issue re-keying" * for this rule in all cases: *
    *
  • plugin upgrade from v1 to v2,
  • *
  • plugin upgrade from v2 to v3
  • *
  • AND plugin upgrade from v1 to v3
  • *
*

* Finally, repository/key pairs must be unique across all rules and their deprecated keys. *
* This implies that no rule can use the same repository and key as the deprecated key of another rule. This * uniqueness applies across plugins. *

* Note that, even though this method returns a {@code Set}, its elements are ordered according to calls to * {@link NewRule#addDeprecatedRuleKey(String, String) addDeprecatedRuleKey}. This allows to describe the history * of a rule's repositories and keys over time. Oldest repository and key must be specified first. * * @see NewRule#addDeprecatedRuleKey(String, String) * @since 7.1 */ public abstract Set deprecatedRuleKeys(); /** * @see RulesDefinition.NewRule#setInternalKey(String) */ @CheckForNull public abstract String internalKey(); } abstract class NewParam { public abstract String key(); public abstract NewParam setName(@Nullable String s); public abstract NewParam setType(RuleParamType t); /** * Plain-text description. Can be null. Max length is 4000 characters. */ public abstract NewParam setDescription(@Nullable String s); /** * Empty default value will be converted to null. Max length is 4000 characters. */ public abstract NewParam setDefaultValue(@Nullable String s); } @Immutable interface Param { String key(); String name(); @Nullable String description(); @Nullable String defaultValue(); RuleParamType type(); } /** * This method is executed when server is started. */ void define(Context context); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy