]> source.dussan.org Git - sonarqube.git/commitdiff
Rename RuleDefinitions to RulesDefinition
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 14 Mar 2014 13:43:48 +0000 (14:43 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 14 Mar 2014 17:49:24 +0000 (18:49 +0100)
28 files changed:
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooQualityProfile.java
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRuleDefinitions.java [deleted file]
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java [new file with mode: 0644]
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRuleDefinitionsTest.java [deleted file]
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleDefinitions.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleDefinitionsFromAnnotations.java
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleDefinitionsFromXml.java
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleDefinitionsFromAnnotationsTest.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleDefinitionsTest.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionFromAnnotationsTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionFromXmlTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRuleDefinitions.java [deleted file]
sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinition.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/rule/RuleDefinitionsLoader.java
sonar-server/src/main/java/org/sonar/server/rule/RuleRegistration.java
sonar-server/src/main/java/org/sonar/server/rule/RuleRepositories.java
sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRuleDefinitionsTest.java [deleted file]
sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/rule/RuleDefinitionsLoaderTest.java [deleted file]
sonar-server/src/test/java/org/sonar/server/rule/RuleRegistrationTest.java
sonar-server/src/test/java/org/sonar/server/rule/RuleRepositoriesTest.java
sonar-server/src/test/java/org/sonar/server/rule/RulesDefinitionLoaderTest.java [new file with mode: 0644]

index 111be1d02ca5a6cc10ee71c90e63976af3caa30b..95b65dd31a3bcf51eabc3beb1983254647021e2d 100644 (file)
@@ -21,7 +21,7 @@ package org.sonar.xoo;
 
 import org.sonar.api.SonarPlugin;
 import org.sonar.xoo.rule.XooQualityProfile;
-import org.sonar.xoo.rule.XooRuleDefinitions;
+import org.sonar.xoo.rule.XooRulesDefinition;
 
 import java.util.Arrays;
 import java.util.List;
@@ -38,7 +38,7 @@ public class XooPlugin extends SonarPlugin {
   public List getExtensions() {
     return Arrays.asList(
       Xoo.class,
-      XooRuleDefinitions.class,
+      XooRulesDefinition.class,
       XooQualityProfile.class);
   }
 
index a43075894c137e6608f094f51cd4e478349cb1f5..f5143163b572e5911f4fa389b221415f088d6ddf 100644 (file)
@@ -32,7 +32,7 @@ public class XooQualityProfile extends ProfileDefinition {
   public RulesProfile createProfile(ValidationMessages validation) {
     final RulesProfile profile = RulesProfile.create("Basic", Xoo.KEY);
 
-    profile.activateRule(Rule.create(XooRuleDefinitions.XOO_REPOSITORY, "x1"), RulePriority.MAJOR);
+    profile.activateRule(Rule.create(XooRulesDefinition.XOO_REPOSITORY, "x1"), RulePriority.MAJOR);
 
     return profile;
   }
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRuleDefinitions.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRuleDefinitions.java
deleted file mode 100644 (file)
index 96fe35e..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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.xoo.rule;
-
-import org.sonar.api.rule.RuleStatus;
-import org.sonar.api.rule.Severity;
-import org.sonar.api.server.rule.RuleDefinitions;
-import org.sonar.api.server.rule.RuleParamType;
-import org.sonar.xoo.Xoo;
-
-/**
- * Define all the coding rules that are supported on the repository named "xoo".
- */
-public class XooRuleDefinitions implements RuleDefinitions {
-
-  public static final String XOO_REPOSITORY = "xoo";
-
-  @Override
-  public void define(Context context) {
-    NewRepository repository = context.createRepository(XOO_REPOSITORY, Xoo.KEY).setName("Xoo");
-
-    // define a single rule programmatically. Note that rules
-    // can be loaded from JSON or XML files too.
-    NewRule x1Rule = repository.createRule("x1")
-      .setName("No empty line")
-      .setHtmlDescription("Generate an issue on empty lines of Xoo source files")
-
-        // optional tags
-      .setTags("style", "security")
-
-        // 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);
-
-    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();
-  }
-
-}
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java
new file mode 100644 (file)
index 0000000..f1fa1e6
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.xoo.rule;
+
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.server.rule.RuleParamType;
+import org.sonar.api.server.rule.RulesDefinition;
+import org.sonar.xoo.Xoo;
+
+/**
+ * Define all the coding rules that are supported on the repository named "xoo".
+ */
+public class XooRulesDefinition implements RulesDefinition {
+
+  public static final String XOO_REPOSITORY = "xoo";
+
+  @Override
+  public void define(Context context) {
+    NewRepository repository = context.createRepository(XOO_REPOSITORY, Xoo.KEY).setName("Xoo");
+
+    // define a single rule programmatically. Note that rules
+    // can be loaded from JSON or XML files too.
+    NewRule x1Rule = repository.createRule("x1")
+      .setName("No empty line")
+      .setHtmlDescription("Generate an issue on empty lines of Xoo source files")
+
+        // optional tags
+      .setTags("style", "security")
+
+        // 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);
+
+    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();
+  }
+
+}
diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRuleDefinitionsTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRuleDefinitionsTest.java
deleted file mode 100644 (file)
index 6cff184..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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.xoo.rule;
-
-import org.junit.Test;
-import org.sonar.api.server.rule.RuleDefinitions;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class XooRuleDefinitionsTest {
-
-  @Test
-  public void define_xoo_rules() {
-    XooRuleDefinitions def = new XooRuleDefinitions();
-    RuleDefinitions.Context context = new RuleDefinitions.Context();
-    def.define(context);
-
-    RuleDefinitions.Repository repo = context.repository("xoo");
-    assertThat(repo).isNotNull();
-    assertThat(repo.name()).isEqualTo("Xoo");
-    assertThat(repo.language()).isEqualTo("xoo");
-    assertThat(repo.rules()).hasSize(1);
-
-    RuleDefinitions.Rule x1 = repo.rule("x1");
-    assertThat(x1.key()).isEqualTo("x1");
-    assertThat(x1.tags()).containsOnly("style", "security");
-    assertThat(x1.htmlDescription()).isNotEmpty();
-  }
-}
diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java
new file mode 100644 (file)
index 0000000..2de402e
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.xoo.rule;
+
+import org.junit.Test;
+import org.sonar.api.server.rule.RulesDefinition;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class XooRulesDefinitionTest {
+
+  @Test
+  public void define_xoo_rules() {
+    XooRulesDefinition def = new XooRulesDefinition();
+    RulesDefinition.Context context = new RulesDefinition.Context();
+    def.define(context);
+
+    RulesDefinition.Repository repo = context.repository("xoo");
+    assertThat(repo).isNotNull();
+    assertThat(repo.name()).isEqualTo("Xoo");
+    assertThat(repo.language()).isEqualTo("xoo");
+    assertThat(repo.rules()).hasSize(1);
+
+    RulesDefinition.Rule x1 = repo.rule("x1");
+    assertThat(x1.key()).isEqualTo("x1");
+    assertThat(x1.tags()).containsOnly("style", "security");
+    assertThat(x1.htmlDescription()).isNotEmpty();
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleDefinitions.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleDefinitions.java
deleted file mode 100644 (file)
index 0233e28..0000000
+++ /dev/null
@@ -1,694 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 com.google.common.collect.*;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.ServerExtension;
-import org.sonar.api.rule.RemediationFunction;
-import org.sonar.api.rule.RuleStatus;
-import org.sonar.api.rule.Severity;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Defines the coding rules. For example the Java Findbugs plugin provides an implementation of
- * this extension point in order to define the rules that it supports.
- * <p/>
- * This interface replaces the deprecated class org.sonar.api.rules.RuleRepository.
- *
- * @since 4.3
- */
-public interface RuleDefinitions extends ServerExtension {
-
-  /**
-   * Instantiated by core but not by plugins
-   */
-  class Context {
-    private final Map<String, Repository> repositoriesByKey = Maps.newHashMap();
-    private final ListMultimap<String, ExtendedRepository> extendedRepositoriesByKey = ArrayListMultimap.create();
-
-
-    public NewRepository createRepository(String key, String language) {
-      return new NewRepositoryImpl(this, key, language, false);
-    }
-
-    public NewExtendedRepository extendRepository(String key, String language) {
-      return new NewRepositoryImpl(this, key, language, true);
-    }
-
-    @CheckForNull
-    public Repository repository(String key) {
-      return repositoriesByKey.get(key);
-    }
-
-    public List<Repository> repositories() {
-      return ImmutableList.copyOf(repositoriesByKey.values());
-    }
-
-    public List<ExtendedRepository> extendedRepositories(String repositoryKey) {
-      return ImmutableList.copyOf(extendedRepositoriesByKey.get(repositoryKey));
-    }
-
-    public List<ExtendedRepository> extendedRepositories() {
-      return ImmutableList.copyOf(extendedRepositoriesByKey.values());
-    }
-
-    private void registerRepository(NewRepositoryImpl newRepository) {
-      if (repositoriesByKey.containsKey(newRepository.key)) {
-        throw new IllegalStateException(String.format("The rule repository '%s' is defined several times", newRepository.key));
-      }
-      repositoriesByKey.put(newRepository.key, new RepositoryImpl(newRepository));
-    }
-
-    private void registerExtendedRepository(NewRepositoryImpl newRepository) {
-      extendedRepositoriesByKey.put(newRepository.key, new RepositoryImpl(newRepository));
-    }
-  }
-
-  interface NewExtendedRepository {
-    NewRule createRule(String ruleKey);
-
-    /**
-     * Reads definition of rule from the annotations provided by the library sonar-check-api.
-     */
-    NewRule loadAnnotatedClass(Class clazz);
-
-    /**
-     * Reads definitions of rules from the annotations provided by the library sonar-check-api.
-     */
-    NewExtendedRepository loadAnnotatedClasses(Class... classes);
-
-    /**
-     * Reads definitions of rules from a XML file. Format is :
-     * <pre>
-     * &lt;rules&gt;
-     * &lt;rule&gt;
-     * &lt;!-- required fields --&gt;
-     * &lt;key&gt;the-rule-key&lt;/key&gt;
-     * &lt;name&gt;The purpose of the rule&lt;/name&gt;
-     * &lt;description&gt;
-     * &lt;![CDATA[The description]]&gt;
-     * &lt;/description&gt;
-     *
-     * &lt;!-- optional fields --&gt;
-     * &lt;internalKey&gt;Checker/TreeWalker/LocalVariableName&lt;/internalKey&gt;
-     * &lt;severity&gt;BLOCKER&lt;/severity&gt;
-     * &lt;cardinality&gt;MULTIPLE&lt;/cardinality&gt;
-     * &lt;status&gt;BETA&lt;/status&gt;
-     * &lt;param&gt;
-     * &lt;key&gt;the-param-key&lt;/key&gt;
-     * &lt;tag&gt;style&lt;/tag&gt;
-     * &lt;tag&gt;security&lt;/tag&gt;
-     * &lt;description&gt;
-     * &lt;![CDATA[
-     * the param-description
-     * ]]&gt;
-     * &lt;/description&gt;
-     * &lt;defaultValue&gt;42&lt;/defaultValue&gt;
-     * &lt;/param&gt;
-     * &lt;param&gt;
-     * &lt;key&gt;another-param&lt;/key&gt;
-     * &lt;/param&gt;
-     *
-     * &lt;!-- deprecated fields --&gt;
-     * &lt;configKey&gt;Checker/TreeWalker/LocalVariableName&lt;/configKey&gt;
-     * &lt;priority&gt;BLOCKER&lt;/priority&gt;
-     * &lt;/rule&gt;
-     * &lt;/rules&gt;
-     *
-     * </pre>
-     */
-    NewExtendedRepository loadXml(InputStream xmlInput, String encoding);
-
-    void done();
-  }
-
-  interface NewRepository extends NewExtendedRepository {
-    NewRepository setName(String s);
-
-    @CheckForNull
-    NewRule rule(String ruleKey);
-  }
-
-  class NewRepositoryImpl implements NewRepository {
-    private final Context context;
-    private final boolean extended;
-    private final String key;
-    private String language;
-    private String name;
-    private final Map<String, NewRule> newRules = Maps.newHashMap();
-
-    private NewRepositoryImpl(Context context, String key, String language, boolean extended) {
-      this.extended = extended;
-      this.context = context;
-      this.key = this.name = key;
-      this.language = language;
-    }
-
-    @Override
-    public NewRepositoryImpl setName(@Nullable String s) {
-      if (StringUtils.isNotEmpty(s)) {
-        this.name = s;
-      }
-      return this;
-    }
-
-    @Override
-    public NewRule createRule(String ruleKey) {
-      if (newRules.containsKey(ruleKey)) {
-        // Should fail in a perfect world, but at the time being the Findbugs plugin
-        // defines several times the rule EC_INCOMPATIBLE_ARRAY_COMPARE
-        // See http://jira.codehaus.org/browse/SONARJAVA-428
-        LoggerFactory.getLogger(getClass()).warn(String.format("The rule '%s' of repository '%s' is declared several times", ruleKey, key));
-      }
-      NewRule newRule = new NewRule(key, ruleKey);
-      newRules.put(ruleKey, newRule);
-      return newRule;
-    }
-
-    @CheckForNull
-    @Override
-    public NewRule rule(String ruleKey) {
-      return newRules.get(ruleKey);
-    }
-
-    @Override
-    public NewRepositoryImpl loadAnnotatedClasses(Class... classes) {
-      new RuleDefinitionsFromAnnotations().loadRules(this, classes);
-      return this;
-    }
-
-    @Override
-    public RuleDefinitions.NewRule loadAnnotatedClass(Class clazz) {
-      return new RuleDefinitionsFromAnnotations().loadRule(this, clazz);
-    }
-
-    @Override
-    public NewRepositoryImpl loadXml(InputStream xmlInput, String encoding) {
-      new RuleDefinitionsFromXml().loadRules(this, xmlInput, encoding);
-      return this;
-    }
-
-    @Override
-    public void done() {
-      // note that some validations can be done here, for example for
-      // verifying that at least one rule is declared
-
-      if (extended) {
-        context.registerExtendedRepository(this);
-      } else {
-        context.registerRepository(this);
-      }
-    }
-  }
-
-  interface ExtendedRepository {
-    String key();
-
-    String language();
-
-    @CheckForNull
-    Rule rule(String ruleKey);
-
-    List<Rule> rules();
-  }
-
-  interface Repository extends ExtendedRepository {
-    String name();
-  }
-
-  @Immutable
-  class RepositoryImpl implements Repository {
-    private final String key, language, name;
-    private final Map<String, Rule> rulesByKey;
-
-    private RepositoryImpl(NewRepositoryImpl newRepository) {
-      this.key = newRepository.key;
-      this.language = newRepository.language;
-      this.name = newRepository.name;
-      ImmutableMap.Builder<String, Rule> ruleBuilder = ImmutableMap.builder();
-      for (NewRule newRule : newRepository.newRules.values()) {
-        newRule.validate();
-        ruleBuilder.put(newRule.key, new Rule(this, newRule));
-      }
-      this.rulesByKey = ruleBuilder.build();
-    }
-
-    @Override
-    public String key() {
-      return key;
-    }
-
-    @Override
-    public String language() {
-      return language;
-    }
-
-    @Override
-    public String name() {
-      return name;
-    }
-
-    @Override
-    @CheckForNull
-    public Rule rule(String ruleKey) {
-      return rulesByKey.get(ruleKey);
-    }
-
-    @Override
-    public List<Rule> rules() {
-      return ImmutableList.copyOf(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();
-    }
-  }
-
-  class NewRule {
-    private final String repoKey, key;
-    private String name, htmlDescription, internalKey, severity = Severity.MAJOR;
-    private boolean template;
-    private RuleStatus status = RuleStatus.defaultStatus();
-    private String characteristicKey;
-    private RemediationFunction remediationFunction;
-    private String remediationFactor;
-    private String remediationOffset;
-    private String effortToFixL10nKey;
-    private final Set<String> tags = Sets.newTreeSet();
-    private final Map<String, NewParam> paramsByKey = Maps.newHashMap();
-
-    private NewRule(String repoKey, String key) {
-      this.repoKey = repoKey;
-      this.key = key;
-    }
-
-    public String key() {
-      return this.key;
-    }
-
-    public NewRule setName(@Nullable String s) {
-      this.name = StringUtils.trim(s);
-      return this;
-    }
-
-    public NewRule setTemplate(boolean template) {
-      this.template = template;
-      return this;
-    }
-
-    public NewRule setSeverity(String s) {
-      if (!Severity.ALL.contains(s)) {
-        throw new IllegalArgumentException(String.format("Severity of rule %s is not correct: %s", this, s));
-      }
-      this.severity = s;
-      return this;
-    }
-
-    public NewRule setHtmlDescription(String s) {
-      this.htmlDescription = StringUtils.trim(s);
-      return this;
-    }
-
-    /**
-     * 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));
-        } catch (IOException e) {
-          throw new IllegalStateException("Fail to read: " + classpathUrl, e);
-        }
-      } else {
-        this.htmlDescription = null;
-      }
-      return this;
-    }
-
-    public NewRule setStatus(RuleStatus status) {
-      if (status.equals(RuleStatus.REMOVED)) {
-        throw new IllegalArgumentException(String.format("Status 'REMOVED' is not accepted on rule '%s'", this));
-      }
-      this.status = status;
-      return this;
-    }
-
-    public NewRule setCharacteristicKey(@Nullable String characteristicKey) {
-      this.characteristicKey = characteristicKey;
-      return this;
-    }
-
-    public NewRule setRemediationFunction(@Nullable RemediationFunction remediationFunction) {
-      this.remediationFunction = remediationFunction;
-      return this;
-    }
-
-    public NewRule setRemediationFactor(@Nullable String remediationFactor) {
-      // TODO validate format
-      this.remediationFactor = StringUtils.deleteWhitespace(remediationFactor);
-      return this;
-    }
-
-    public NewRule setRemediationOffset(@Nullable String remediationOffset) {
-      // TODO validate format
-      this.remediationOffset = StringUtils.deleteWhitespace(remediationOffset);
-      return this;
-    }
-
-    public NewRule setEffortToFixL10nKey(@Nullable String effortToFixL10nKey) {
-      this.effortToFixL10nKey = effortToFixL10nKey;
-      return this;
-    }
-
-    public NewParam createParam(String paramKey) {
-      if (paramsByKey.containsKey(paramKey)) {
-        throw new IllegalArgumentException(String.format("The parameter '%s' is declared several times on the rule %s", paramKey, this));
-      }
-      NewParam param = new NewParam(paramKey);
-      paramsByKey.put(paramKey, param);
-      return param;
-    }
-
-    @CheckForNull
-    public NewParam param(String paramKey) {
-      return paramsByKey.get(paramKey);
-    }
-
-    /**
-     * @see RuleTagFormat
-     */
-    public NewRule addTags(String... list) {
-      for (String tag : list) {
-        RuleTagFormat.validate(tag);
-        tags.add(tag);
-      }
-      return this;
-    }
-
-    /**
-     * @see RuleTagFormat
-     */
-    public NewRule setTags(String... list) {
-      tags.clear();
-      addTags(list);
-      return this;
-    }
-
-    /**
-     * 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 (StringUtils.isBlank(name)) {
-        throw new IllegalStateException(String.format("Name of rule %s is empty", this));
-      }
-      if (StringUtils.isBlank(htmlDescription)) {
-        throw new IllegalStateException(String.format("HTML description of rule %s is empty", this));
-      }
-    }
-
-    @Override
-    public String toString() {
-      return String.format("[repository=%s, key=%s]", repoKey, key);
-    }
-  }
-
-  @Immutable
-  class Rule {
-    private final Repository repository;
-    private final String repoKey, key, name, htmlDescription, internalKey, severity;
-    private final boolean template;
-    private final String characteristicKey;
-    private final RemediationFunction remediationFunction;
-    private final String remediationFactor;
-    private final String remediationOffset;
-    private final String effortToFixL10nKey;
-    private final Set<String> tags;
-    private final Map<String, Param> params;
-    private final RuleStatus status;
-
-    private Rule(Repository repository, NewRule newRule) {
-      this.repository = repository;
-      this.repoKey = newRule.repoKey;
-      this.key = newRule.key;
-      this.name = newRule.name;
-      this.htmlDescription = newRule.htmlDescription;
-      this.internalKey = newRule.internalKey;
-      this.severity = newRule.severity;
-      this.template = newRule.template;
-      this.status = newRule.status;
-      this.characteristicKey = newRule.characteristicKey;
-      this.remediationFunction = newRule.remediationFunction;
-      this.remediationFactor = newRule.remediationFactor;
-      this.remediationOffset = newRule.remediationOffset;
-      this.effortToFixL10nKey = newRule.effortToFixL10nKey;
-      this.tags = ImmutableSortedSet.copyOf(newRule.tags);
-      ImmutableMap.Builder<String, Param> paramsBuilder = ImmutableMap.builder();
-      for (NewParam newParam : newRule.paramsByKey.values()) {
-        paramsBuilder.put(newParam.key, new Param(newParam));
-      }
-      this.params = paramsBuilder.build();
-    }
-
-    public Repository repository() {
-      return repository;
-    }
-
-    public String key() {
-      return key;
-    }
-
-    public String name() {
-      return name;
-    }
-
-    public String severity() {
-      return severity;
-    }
-
-    @CheckForNull
-    public String htmlDescription() {
-      return htmlDescription;
-    }
-
-    public boolean template() {
-      return template;
-    }
-
-    public RuleStatus status() {
-      return status;
-    }
-
-    @CheckForNull
-    public String characteristicKey() {
-      return characteristicKey;
-    }
-
-    @CheckForNull
-    public RemediationFunction remediationFunction() {
-      return remediationFunction;
-    }
-
-    @CheckForNull
-    public String remediationFactor() {
-      return remediationFactor;
-    }
-
-    @CheckForNull
-    public String remediationOffset() {
-      return remediationOffset;
-    }
-
-    @CheckForNull
-    public String effortToFixL10nKey() {
-      return effortToFixL10nKey;
-    }
-
-    @CheckForNull
-    public Param param(String key) {
-      return params.get(key);
-    }
-
-    public List<Param> params() {
-      return ImmutableList.copyOf(params.values());
-    }
-
-    public Set<String> tags() {
-      return tags;
-    }
-
-    /**
-     * @see RuleDefinitions.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);
-    }
-
-    @Override
-    public int hashCode() {
-      int result = repoKey.hashCode();
-      result = 31 * result + key.hashCode();
-      return result;
-    }
-
-    @Override
-    public String toString() {
-      return String.format("[repository=%s, key=%s]", repoKey, key);
-    }
-  }
-
-  class NewParam {
-    private final String key;
-    private String name, description, defaultValue;
-    private RuleParamType type = RuleParamType.STRING;
-
-    private NewParam(String key) {
-      this.key = this.name = key;
-    }
-
-    public NewParam setName(@Nullable String s) {
-      // name must never be null.
-      this.name = StringUtils.defaultIfBlank(s, key);
-      return this;
-    }
-
-    public NewParam setType(RuleParamType t) {
-      this.type = t;
-      return this;
-    }
-
-    /**
-     * Plain-text description. Can be null.
-     */
-    public NewParam setDescription(@Nullable String s) {
-      this.description = StringUtils.defaultIfBlank(s, null);
-      return this;
-    }
-
-    public NewParam setDefaultValue(@Nullable String s) {
-      this.defaultValue = s;
-      return this;
-    }
-  }
-
-  @Immutable
-  class Param {
-    private final String key, name, description, 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;
-    }
-
-    public String name() {
-      return name;
-    }
-
-    @Nullable
-    public String description() {
-      return description;
-    }
-
-    @Nullable
-    public String defaultValue() {
-      return defaultValue;
-    }
-
-    public RuleParamType type() {
-      return type;
-    }
-
-    @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();
-    }
-  }
-
-  /**
-   * This method is executed when server is started.
-   */
-  void define(Context context);
-
-}
index a9b1143ce15759efd6ffa3de7cb8af2815c54742..a5ad00af8393062e0977debfe5f7528d580eb2cc 100644 (file)
@@ -39,7 +39,7 @@ import java.util.List;
 /**
  * Read definitions of rules based on the annotations provided by sonar-check-api.
  * </p>
- * It is internally used by {@link RuleDefinitions} and can't be directly
+ * It is internally used by {@link RulesDefinition} and can't be directly
  * used by plugins.
  *
  * @since 4.2
@@ -48,14 +48,14 @@ class RuleDefinitionsFromAnnotations {
 
   private static final Logger LOG = LoggerFactory.getLogger(RuleDefinitionsFromAnnotations.class);
 
-  void loadRules(RuleDefinitions.NewRepository repo, Class... annotatedClasses) {
+  void loadRules(RulesDefinition.NewRepository repo, Class... annotatedClasses) {
     for (Class annotatedClass : annotatedClasses) {
       loadRule(repo, annotatedClass);
     }
   }
 
   @CheckForNull
-  RuleDefinitions.NewRule loadRule(RuleDefinitions.NewRepository repo, Class clazz) {
+  RulesDefinition.NewRule loadRule(RulesDefinition.NewRepository repo, Class clazz) {
     org.sonar.check.Rule ruleAnnotation = AnnotationUtils.getAnnotation(clazz, org.sonar.check.Rule.class);
     if (ruleAnnotation != null) {
       return loadRule(repo, clazz, ruleAnnotation);
@@ -65,12 +65,12 @@ class RuleDefinitionsFromAnnotations {
     }
   }
 
-  private RuleDefinitions.NewRule loadRule(RuleDefinitions.NewRepository repo, Class clazz, org.sonar.check.Rule ruleAnnotation) {
+  private RulesDefinition.NewRule loadRule(RulesDefinition.NewRepository repo, Class clazz, org.sonar.check.Rule ruleAnnotation) {
     String ruleKey = StringUtils.defaultIfEmpty(ruleAnnotation.key(), clazz.getCanonicalName());
     String ruleName = StringUtils.defaultIfEmpty(ruleAnnotation.name(), null);
     String description = StringUtils.defaultIfEmpty(ruleAnnotation.description(), null);
 
-    RuleDefinitions.NewRule rule = repo.createRule(ruleKey);
+    RulesDefinition.NewRule rule = repo.createRule(ruleKey);
     rule.setName(ruleName).setHtmlDescription(description);
     rule.setSeverity(ruleAnnotation.priority().name());
     rule.setTemplate(ruleAnnotation.cardinality() == Cardinality.MULTIPLE);
@@ -85,11 +85,11 @@ class RuleDefinitionsFromAnnotations {
     return rule;
   }
 
-  private void loadParameters(RuleDefinitions.NewRule rule, Field field) {
+  private void loadParameters(RulesDefinition.NewRule rule, Field field) {
     org.sonar.check.RuleProperty propertyAnnotation = field.getAnnotation(org.sonar.check.RuleProperty.class);
     if (propertyAnnotation != null) {
       String fieldKey = StringUtils.defaultIfEmpty(propertyAnnotation.key(), field.getName());
-      RuleDefinitions.NewParam param = rule.createParam(fieldKey)
+      RulesDefinition.NewParam param = rule.createParam(fieldKey)
         .setDescription(propertyAnnotation.description())
         .setDefaultValue(propertyAnnotation.defaultValue());
 
index 1fbba08db7a513dc63a0bf18df412c44d7ff4d47..80b9bec7e0627654b5ce72f3f7e54401e15dde91 100644 (file)
@@ -24,12 +24,13 @@ import org.apache.commons.lang.StringUtils;
 import org.codehaus.staxmate.SMInputFactory;
 import org.codehaus.staxmate.in.SMHierarchicCursor;
 import org.codehaus.staxmate.in.SMInputCursor;
-import org.sonar.api.rule.Severity;
 import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rule.Severity;
 import org.sonar.check.Cardinality;
 
 import javax.xml.stream.XMLInputFactory;
 import javax.xml.stream.XMLStreamException;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -42,7 +43,7 @@ import java.util.List;
  */
 class RuleDefinitionsFromXml {
 
-  void loadRules(RuleDefinitions.NewRepository repo, InputStream input, String encoding) {
+  void loadRules(RulesDefinition.NewRepository repo, InputStream input, String encoding) {
     Reader reader = null;
     try {
       reader = new InputStreamReader(input, encoding);
@@ -56,7 +57,7 @@ class RuleDefinitionsFromXml {
     }
   }
 
-  void loadRules(RuleDefinitions.NewRepository repo, Reader reader) {
+  void loadRules(RulesDefinition.NewRepository repo, Reader reader) {
     XMLInputFactory xmlFactory = XMLInputFactory.newInstance();
     xmlFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
     xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
@@ -79,7 +80,7 @@ class RuleDefinitionsFromXml {
     }
   }
 
-  private void processRule(RuleDefinitions.NewRepository repo, SMInputCursor ruleC) throws XMLStreamException {
+  private void processRule(RulesDefinition.NewRepository repo, SMInputCursor ruleC) throws XMLStreamException {
     String key = null, name = null, description = null, internalKey = null, severity = Severity.defaultSeverity(), status = null;
     Cardinality cardinality = Cardinality.SINGLE;
     List<ParamStruct> params = new ArrayList<ParamStruct>();
@@ -135,7 +136,7 @@ class RuleDefinitionsFromXml {
         tags.add(StringUtils.trim(cursor.collectDescendantText(false)));
       }
     }
-    RuleDefinitions.NewRule rule = repo.createRule(key)
+    RulesDefinition.NewRule rule = repo.createRule(key)
       .setHtmlDescription(description)
       .setSeverity(severity)
       .setName(name)
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java
new file mode 100644 (file)
index 0000000..3ff8fac
--- /dev/null
@@ -0,0 +1,694 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 com.google.common.collect.*;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.ServerExtension;
+import org.sonar.api.rule.RemediationFunction;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rule.Severity;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Defines the coding rules. For example the Java Findbugs plugin provides an implementation of
+ * this extension point in order to define the rules that it supports.
+ * <p/>
+ * This interface replaces the deprecated class org.sonar.api.rules.RuleRepository.
+ *
+ * @since 4.3
+ */
+public interface RulesDefinition extends ServerExtension {
+
+  /**
+   * Instantiated by core but not by plugins
+   */
+  class Context {
+    private final Map<String, Repository> repositoriesByKey = Maps.newHashMap();
+    private final ListMultimap<String, ExtendedRepository> extendedRepositoriesByKey = ArrayListMultimap.create();
+
+
+    public NewRepository createRepository(String key, String language) {
+      return new NewRepositoryImpl(this, key, language, false);
+    }
+
+    public NewExtendedRepository extendRepository(String key, String language) {
+      return new NewRepositoryImpl(this, key, language, true);
+    }
+
+    @CheckForNull
+    public Repository repository(String key) {
+      return repositoriesByKey.get(key);
+    }
+
+    public List<Repository> repositories() {
+      return ImmutableList.copyOf(repositoriesByKey.values());
+    }
+
+    public List<ExtendedRepository> extendedRepositories(String repositoryKey) {
+      return ImmutableList.copyOf(extendedRepositoriesByKey.get(repositoryKey));
+    }
+
+    public List<ExtendedRepository> extendedRepositories() {
+      return ImmutableList.copyOf(extendedRepositoriesByKey.values());
+    }
+
+    private void registerRepository(NewRepositoryImpl newRepository) {
+      if (repositoriesByKey.containsKey(newRepository.key)) {
+        throw new IllegalStateException(String.format("The rule repository '%s' is defined several times", newRepository.key));
+      }
+      repositoriesByKey.put(newRepository.key, new RepositoryImpl(newRepository));
+    }
+
+    private void registerExtendedRepository(NewRepositoryImpl newRepository) {
+      extendedRepositoriesByKey.put(newRepository.key, new RepositoryImpl(newRepository));
+    }
+  }
+
+  interface NewExtendedRepository {
+    NewRule createRule(String ruleKey);
+
+    /**
+     * Reads definition of rule from the annotations provided by the library sonar-check-api.
+     */
+    NewRule loadAnnotatedClass(Class clazz);
+
+    /**
+     * Reads definitions of rules from the annotations provided by the library sonar-check-api.
+     */
+    NewExtendedRepository loadAnnotatedClasses(Class... classes);
+
+    /**
+     * Reads definitions of rules from a XML file. Format is :
+     * <pre>
+     * &lt;rules&gt;
+     * &lt;rule&gt;
+     * &lt;!-- required fields --&gt;
+     * &lt;key&gt;the-rule-key&lt;/key&gt;
+     * &lt;name&gt;The purpose of the rule&lt;/name&gt;
+     * &lt;description&gt;
+     * &lt;![CDATA[The description]]&gt;
+     * &lt;/description&gt;
+     *
+     * &lt;!-- optional fields --&gt;
+     * &lt;internalKey&gt;Checker/TreeWalker/LocalVariableName&lt;/internalKey&gt;
+     * &lt;severity&gt;BLOCKER&lt;/severity&gt;
+     * &lt;cardinality&gt;MULTIPLE&lt;/cardinality&gt;
+     * &lt;status&gt;BETA&lt;/status&gt;
+     * &lt;param&gt;
+     * &lt;key&gt;the-param-key&lt;/key&gt;
+     * &lt;tag&gt;style&lt;/tag&gt;
+     * &lt;tag&gt;security&lt;/tag&gt;
+     * &lt;description&gt;
+     * &lt;![CDATA[
+     * the param-description
+     * ]]&gt;
+     * &lt;/description&gt;
+     * &lt;defaultValue&gt;42&lt;/defaultValue&gt;
+     * &lt;/param&gt;
+     * &lt;param&gt;
+     * &lt;key&gt;another-param&lt;/key&gt;
+     * &lt;/param&gt;
+     *
+     * &lt;!-- deprecated fields --&gt;
+     * &lt;configKey&gt;Checker/TreeWalker/LocalVariableName&lt;/configKey&gt;
+     * &lt;priority&gt;BLOCKER&lt;/priority&gt;
+     * &lt;/rule&gt;
+     * &lt;/rules&gt;
+     *
+     * </pre>
+     */
+    NewExtendedRepository loadXml(InputStream xmlInput, String encoding);
+
+    void done();
+  }
+
+  interface NewRepository extends NewExtendedRepository {
+    NewRepository setName(String s);
+
+    @CheckForNull
+    NewRule rule(String ruleKey);
+  }
+
+  class NewRepositoryImpl implements NewRepository {
+    private final Context context;
+    private final boolean extended;
+    private final String key;
+    private String language;
+    private String name;
+    private final Map<String, NewRule> newRules = Maps.newHashMap();
+
+    private NewRepositoryImpl(Context context, String key, String language, boolean extended) {
+      this.extended = extended;
+      this.context = context;
+      this.key = this.name = key;
+      this.language = language;
+    }
+
+    @Override
+    public NewRepositoryImpl setName(@Nullable String s) {
+      if (StringUtils.isNotEmpty(s)) {
+        this.name = s;
+      }
+      return this;
+    }
+
+    @Override
+    public NewRule createRule(String ruleKey) {
+      if (newRules.containsKey(ruleKey)) {
+        // Should fail in a perfect world, but at the time being the Findbugs plugin
+        // defines several times the rule EC_INCOMPATIBLE_ARRAY_COMPARE
+        // See http://jira.codehaus.org/browse/SONARJAVA-428
+        LoggerFactory.getLogger(getClass()).warn(String.format("The rule '%s' of repository '%s' is declared several times", ruleKey, key));
+      }
+      NewRule newRule = new NewRule(key, ruleKey);
+      newRules.put(ruleKey, newRule);
+      return newRule;
+    }
+
+    @CheckForNull
+    @Override
+    public NewRule rule(String ruleKey) {
+      return newRules.get(ruleKey);
+    }
+
+    @Override
+    public NewRepositoryImpl loadAnnotatedClasses(Class... classes) {
+      new RuleDefinitionsFromAnnotations().loadRules(this, classes);
+      return this;
+    }
+
+    @Override
+    public RulesDefinition.NewRule loadAnnotatedClass(Class clazz) {
+      return new RuleDefinitionsFromAnnotations().loadRule(this, clazz);
+    }
+
+    @Override
+    public NewRepositoryImpl loadXml(InputStream xmlInput, String encoding) {
+      new RuleDefinitionsFromXml().loadRules(this, xmlInput, encoding);
+      return this;
+    }
+
+    @Override
+    public void done() {
+      // note that some validations can be done here, for example for
+      // verifying that at least one rule is declared
+
+      if (extended) {
+        context.registerExtendedRepository(this);
+      } else {
+        context.registerRepository(this);
+      }
+    }
+  }
+
+  interface ExtendedRepository {
+    String key();
+
+    String language();
+
+    @CheckForNull
+    Rule rule(String ruleKey);
+
+    List<Rule> rules();
+  }
+
+  interface Repository extends ExtendedRepository {
+    String name();
+  }
+
+  @Immutable
+  class RepositoryImpl implements Repository {
+    private final String key, language, name;
+    private final Map<String, Rule> rulesByKey;
+
+    private RepositoryImpl(NewRepositoryImpl newRepository) {
+      this.key = newRepository.key;
+      this.language = newRepository.language;
+      this.name = newRepository.name;
+      ImmutableMap.Builder<String, Rule> ruleBuilder = ImmutableMap.builder();
+      for (NewRule newRule : newRepository.newRules.values()) {
+        newRule.validate();
+        ruleBuilder.put(newRule.key, new Rule(this, newRule));
+      }
+      this.rulesByKey = ruleBuilder.build();
+    }
+
+    @Override
+    public String key() {
+      return key;
+    }
+
+    @Override
+    public String language() {
+      return language;
+    }
+
+    @Override
+    public String name() {
+      return name;
+    }
+
+    @Override
+    @CheckForNull
+    public Rule rule(String ruleKey) {
+      return rulesByKey.get(ruleKey);
+    }
+
+    @Override
+    public List<Rule> rules() {
+      return ImmutableList.copyOf(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();
+    }
+  }
+
+  class NewRule {
+    private final String repoKey, key;
+    private String name, htmlDescription, internalKey, severity = Severity.MAJOR;
+    private boolean template;
+    private RuleStatus status = RuleStatus.defaultStatus();
+    private String characteristicKey;
+    private RemediationFunction remediationFunction;
+    private String remediationFactor;
+    private String remediationOffset;
+    private String effortToFixL10nKey;
+    private final Set<String> tags = Sets.newTreeSet();
+    private final Map<String, NewParam> paramsByKey = Maps.newHashMap();
+
+    private NewRule(String repoKey, String key) {
+      this.repoKey = repoKey;
+      this.key = key;
+    }
+
+    public String key() {
+      return this.key;
+    }
+
+    public NewRule setName(@Nullable String s) {
+      this.name = StringUtils.trim(s);
+      return this;
+    }
+
+    public NewRule setTemplate(boolean template) {
+      this.template = template;
+      return this;
+    }
+
+    public NewRule setSeverity(String s) {
+      if (!Severity.ALL.contains(s)) {
+        throw new IllegalArgumentException(String.format("Severity of rule %s is not correct: %s", this, s));
+      }
+      this.severity = s;
+      return this;
+    }
+
+    public NewRule setHtmlDescription(String s) {
+      this.htmlDescription = StringUtils.trim(s);
+      return this;
+    }
+
+    /**
+     * 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));
+        } catch (IOException e) {
+          throw new IllegalStateException("Fail to read: " + classpathUrl, e);
+        }
+      } else {
+        this.htmlDescription = null;
+      }
+      return this;
+    }
+
+    public NewRule setStatus(RuleStatus status) {
+      if (status.equals(RuleStatus.REMOVED)) {
+        throw new IllegalArgumentException(String.format("Status 'REMOVED' is not accepted on rule '%s'", this));
+      }
+      this.status = status;
+      return this;
+    }
+
+    public NewRule setCharacteristicKey(@Nullable String characteristicKey) {
+      this.characteristicKey = characteristicKey;
+      return this;
+    }
+
+    public NewRule setRemediationFunction(@Nullable RemediationFunction remediationFunction) {
+      this.remediationFunction = remediationFunction;
+      return this;
+    }
+
+    public NewRule setRemediationFactor(@Nullable String remediationFactor) {
+      // TODO validate format
+      this.remediationFactor = StringUtils.deleteWhitespace(remediationFactor);
+      return this;
+    }
+
+    public NewRule setRemediationOffset(@Nullable String remediationOffset) {
+      // TODO validate format
+      this.remediationOffset = StringUtils.deleteWhitespace(remediationOffset);
+      return this;
+    }
+
+    public NewRule setEffortToFixL10nKey(@Nullable String effortToFixL10nKey) {
+      this.effortToFixL10nKey = effortToFixL10nKey;
+      return this;
+    }
+
+    public NewParam createParam(String paramKey) {
+      if (paramsByKey.containsKey(paramKey)) {
+        throw new IllegalArgumentException(String.format("The parameter '%s' is declared several times on the rule %s", paramKey, this));
+      }
+      NewParam param = new NewParam(paramKey);
+      paramsByKey.put(paramKey, param);
+      return param;
+    }
+
+    @CheckForNull
+    public NewParam param(String paramKey) {
+      return paramsByKey.get(paramKey);
+    }
+
+    /**
+     * @see RuleTagFormat
+     */
+    public NewRule addTags(String... list) {
+      for (String tag : list) {
+        RuleTagFormat.validate(tag);
+        tags.add(tag);
+      }
+      return this;
+    }
+
+    /**
+     * @see RuleTagFormat
+     */
+    public NewRule setTags(String... list) {
+      tags.clear();
+      addTags(list);
+      return this;
+    }
+
+    /**
+     * 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 (StringUtils.isBlank(name)) {
+        throw new IllegalStateException(String.format("Name of rule %s is empty", this));
+      }
+      if (StringUtils.isBlank(htmlDescription)) {
+        throw new IllegalStateException(String.format("HTML description of rule %s is empty", this));
+      }
+    }
+
+    @Override
+    public String toString() {
+      return String.format("[repository=%s, key=%s]", repoKey, key);
+    }
+  }
+
+  @Immutable
+  class Rule {
+    private final Repository repository;
+    private final String repoKey, key, name, htmlDescription, internalKey, severity;
+    private final boolean template;
+    private final String characteristicKey;
+    private final RemediationFunction remediationFunction;
+    private final String remediationFactor;
+    private final String remediationOffset;
+    private final String effortToFixL10nKey;
+    private final Set<String> tags;
+    private final Map<String, Param> params;
+    private final RuleStatus status;
+
+    private Rule(Repository repository, NewRule newRule) {
+      this.repository = repository;
+      this.repoKey = newRule.repoKey;
+      this.key = newRule.key;
+      this.name = newRule.name;
+      this.htmlDescription = newRule.htmlDescription;
+      this.internalKey = newRule.internalKey;
+      this.severity = newRule.severity;
+      this.template = newRule.template;
+      this.status = newRule.status;
+      this.characteristicKey = newRule.characteristicKey;
+      this.remediationFunction = newRule.remediationFunction;
+      this.remediationFactor = newRule.remediationFactor;
+      this.remediationOffset = newRule.remediationOffset;
+      this.effortToFixL10nKey = newRule.effortToFixL10nKey;
+      this.tags = ImmutableSortedSet.copyOf(newRule.tags);
+      ImmutableMap.Builder<String, Param> paramsBuilder = ImmutableMap.builder();
+      for (NewParam newParam : newRule.paramsByKey.values()) {
+        paramsBuilder.put(newParam.key, new Param(newParam));
+      }
+      this.params = paramsBuilder.build();
+    }
+
+    public Repository repository() {
+      return repository;
+    }
+
+    public String key() {
+      return key;
+    }
+
+    public String name() {
+      return name;
+    }
+
+    public String severity() {
+      return severity;
+    }
+
+    @CheckForNull
+    public String htmlDescription() {
+      return htmlDescription;
+    }
+
+    public boolean template() {
+      return template;
+    }
+
+    public RuleStatus status() {
+      return status;
+    }
+
+    @CheckForNull
+    public String characteristicKey() {
+      return characteristicKey;
+    }
+
+    @CheckForNull
+    public RemediationFunction remediationFunction() {
+      return remediationFunction;
+    }
+
+    @CheckForNull
+    public String remediationFactor() {
+      return remediationFactor;
+    }
+
+    @CheckForNull
+    public String remediationOffset() {
+      return remediationOffset;
+    }
+
+    @CheckForNull
+    public String effortToFixL10nKey() {
+      return effortToFixL10nKey;
+    }
+
+    @CheckForNull
+    public Param param(String key) {
+      return params.get(key);
+    }
+
+    public List<Param> params() {
+      return ImmutableList.copyOf(params.values());
+    }
+
+    public Set<String> tags() {
+      return tags;
+    }
+
+    /**
+     * @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);
+    }
+
+    @Override
+    public int hashCode() {
+      int result = repoKey.hashCode();
+      result = 31 * result + key.hashCode();
+      return result;
+    }
+
+    @Override
+    public String toString() {
+      return String.format("[repository=%s, key=%s]", repoKey, key);
+    }
+  }
+
+  class NewParam {
+    private final String key;
+    private String name, description, defaultValue;
+    private RuleParamType type = RuleParamType.STRING;
+
+    private NewParam(String key) {
+      this.key = this.name = key;
+    }
+
+    public NewParam setName(@Nullable String s) {
+      // name must never be null.
+      this.name = StringUtils.defaultIfBlank(s, key);
+      return this;
+    }
+
+    public NewParam setType(RuleParamType t) {
+      this.type = t;
+      return this;
+    }
+
+    /**
+     * Plain-text description. Can be null.
+     */
+    public NewParam setDescription(@Nullable String s) {
+      this.description = StringUtils.defaultIfBlank(s, null);
+      return this;
+    }
+
+    public NewParam setDefaultValue(@Nullable String s) {
+      this.defaultValue = s;
+      return this;
+    }
+  }
+
+  @Immutable
+  class Param {
+    private final String key, name, description, 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;
+    }
+
+    public String name() {
+      return name;
+    }
+
+    @Nullable
+    public String description() {
+      return description;
+    }
+
+    @Nullable
+    public String defaultValue() {
+      return defaultValue;
+    }
+
+    public RuleParamType type() {
+      return type;
+    }
+
+    @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();
+    }
+  }
+
+  /**
+   * This method is executed when server is started.
+   */
+  void define(Context context);
+
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleDefinitionsFromAnnotationsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleDefinitionsFromAnnotationsTest.java
deleted file mode 100644 (file)
index f83e13e..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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.junit.rules.ExpectedException;
-import org.sonar.api.rule.RuleStatus;
-import org.sonar.api.rule.Severity;
-import org.sonar.api.server.rule.RuleDefinitions.NewRule;
-import org.sonar.check.Priority;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class RuleDefinitionsFromAnnotationsTest {
-
-  @org.junit.Rule
-  public final ExpectedException thrown = ExpectedException.none();
-
-  @Test
-  public void rule_with_property() {
-    RuleDefinitions.Repository repository = load(RuleWithProperty.class);
-    assertThat(repository.rules()).hasSize(1);
-    RuleDefinitions.Rule rule = repository.rules().get(0);
-    assertThat(rule.key()).isEqualTo("foo");
-    assertThat(rule.status()).isEqualTo(RuleStatus.BETA);
-    assertThat(rule.name()).isEqualTo("bar");
-    assertThat(rule.htmlDescription()).isEqualTo("Foo Bar");
-    assertThat(rule.severity()).isEqualTo(Severity.BLOCKER);
-    assertThat(rule.params()).hasSize(1);
-    assertThat(rule.tags()).isEmpty();
-
-    RuleDefinitions.Param prop = rule.param("property");
-    assertThat(prop.key()).isEqualTo("property");
-    assertThat(prop.description()).isEqualTo("Ignore ?");
-    assertThat(prop.defaultValue()).isEqualTo("false");
-    assertThat(prop.type()).isEqualTo(RuleParamType.STRING);
-  }
-
-  @Test
-  public void override_annotation_programmatically() {
-    RuleDefinitions.Context context = new RuleDefinitions.Context();
-    RuleDefinitions.NewRepository newRepository = context.createRepository("squid", "java");
-    NewRule newRule = newRepository.loadAnnotatedClass(RuleWithProperty.class);
-    newRule.setName("Overriden name");
-    newRule.param("property").setDefaultValue("true");
-    newRule.param("property").setDescription("Overriden");
-    newRepository.done();
-
-    RuleDefinitions.Repository repository = context.repository("squid");
-    assertThat(repository.rules()).hasSize(1);
-    RuleDefinitions.Rule rule = repository.rules().get(0);
-    assertThat(rule.key()).isEqualTo("foo");
-    assertThat(rule.status()).isEqualTo(RuleStatus.BETA);
-    assertThat(rule.name()).isEqualTo("Overriden name");
-    assertThat(rule.htmlDescription()).isEqualTo("Foo Bar");
-    assertThat(rule.severity()).isEqualTo(Severity.BLOCKER);
-    assertThat(rule.params()).hasSize(1);
-
-    RuleDefinitions.Param prop = rule.param("property");
-    assertThat(prop.key()).isEqualTo("property");
-    assertThat(prop.description()).isEqualTo("Overriden");
-    assertThat(prop.defaultValue()).isEqualTo("true");
-    assertThat(prop.type()).isEqualTo(RuleParamType.STRING);
-  }
-
-  @Test
-  public void rule_with_integer_property() {
-    RuleDefinitions.Repository repository = load(RuleWithIntegerProperty.class);
-
-    RuleDefinitions.Param prop = repository.rules().get(0).param("property");
-    assertThat(prop.description()).isEqualTo("Max");
-    assertThat(prop.defaultValue()).isEqualTo("12");
-    assertThat(prop.type()).isEqualTo(RuleParamType.INTEGER);
-  }
-
-  @Test
-  public void rule_with_text_property() {
-    RuleDefinitions.Repository repository = load(RuleWithTextProperty.class);
-
-    RuleDefinitions.Param prop = repository.rules().get(0).param("property");
-    assertThat(prop.description()).isEqualTo("text");
-    assertThat(prop.defaultValue()).isEqualTo("Long text");
-    assertThat(prop.type()).isEqualTo(RuleParamType.TEXT);
-  }
-
-  @Test
-  public void should_recognize_type() {
-    assertThat(RuleDefinitionsFromAnnotations.guessType(Integer.class)).isEqualTo(RuleParamType.INTEGER);
-    assertThat(RuleDefinitionsFromAnnotations.guessType(int.class)).isEqualTo(RuleParamType.INTEGER);
-    assertThat(RuleDefinitionsFromAnnotations.guessType(Float.class)).isEqualTo(RuleParamType.FLOAT);
-    assertThat(RuleDefinitionsFromAnnotations.guessType(float.class)).isEqualTo(RuleParamType.FLOAT);
-    assertThat(RuleDefinitionsFromAnnotations.guessType(Boolean.class)).isEqualTo(RuleParamType.BOOLEAN);
-    assertThat(RuleDefinitionsFromAnnotations.guessType(boolean.class)).isEqualTo(RuleParamType.BOOLEAN);
-    assertThat(RuleDefinitionsFromAnnotations.guessType(String.class)).isEqualTo(RuleParamType.STRING);
-    assertThat(RuleDefinitionsFromAnnotations.guessType(Object.class)).isEqualTo(RuleParamType.STRING);
-  }
-
-  @Test
-  public void use_classname_when_missing_key() {
-    RuleDefinitions.Repository repository = load(RuleWithoutKey.class);
-    assertThat(repository.rules()).hasSize(1);
-    RuleDefinitions.Rule rule = repository.rules().get(0);
-    assertThat(rule.key()).isEqualTo(RuleWithoutKey.class.getCanonicalName());
-    assertThat(rule.name()).isEqualTo("foo");
-  }
-
-  @Test
-  public void rule_with_tags() {
-    RuleDefinitions.Repository repository = load(RuleWithTags.class);
-    assertThat(repository.rules()).hasSize(1);
-    RuleDefinitions.Rule rule = repository.rules().get(0);
-    assertThat(rule.tags()).containsOnly("style", "security");
-  }
-
-  @Test
-  public void overridden_class() {
-    RuleDefinitions.Repository repository = load(OverridingRule.class);
-    assertThat(repository.rules()).hasSize(1);
-    RuleDefinitions.Rule rule = repository.rules().get(0);
-    assertThat(rule.key()).isEqualTo("overriding_foo");
-    assertThat(rule.name()).isEqualTo("Overriding Foo");
-    assertThat(rule.severity()).isEqualTo(Severity.MAJOR);
-    assertThat(rule.htmlDescription()).isEqualTo("Desc of Overriding Foo");
-    assertThat(rule.params()).hasSize(2);
-  }
-
-  private RuleDefinitions.Repository load(Class annotatedClass) {
-    RuleDefinitions.Context context = new RuleDefinitions.Context();
-    RuleDefinitions.NewExtendedRepository newRepository = context.createRepository("squid", "java")
-      .loadAnnotatedClasses(annotatedClass);
-    newRepository.done();
-    return context.repository("squid");
-  }
-
-  @org.sonar.check.Rule(name = "foo", description = "Foo")
-  static class RuleWithoutKey {
-  }
-
-  @org.sonar.check.Rule(key = "foo", name = "bar", description = "Foo Bar", priority = Priority.BLOCKER, status = "BETA")
-  static class RuleWithProperty {
-    @org.sonar.check.RuleProperty(description = "Ignore ?", defaultValue = "false")
-    private String property;
-  }
-
-  @org.sonar.check.Rule(key = "overriding_foo", name = "Overriding Foo", description = "Desc of Overriding Foo")
-  static class OverridingRule extends RuleWithProperty {
-    @org.sonar.check.RuleProperty
-    private String additionalProperty;
-  }
-
-  @org.sonar.check.Rule(key = "foo", name = "bar", description = "Foo Bar", priority = Priority.BLOCKER)
-  static class RuleWithIntegerProperty {
-    @org.sonar.check.RuleProperty(description = "Max", defaultValue = "12")
-    private Integer property;
-  }
-
-  @org.sonar.check.Rule(key = "foo", name = "bar", description = "Foo Bar", priority = Priority.BLOCKER)
-  static class RuleWithTextProperty {
-    @org.sonar.check.RuleProperty(description = "text", defaultValue = "Long text", type = "TEXT")
-    protected String property;
-  }
-
-  @org.sonar.check.Rule(key = "foo", name = "bar", description = "Foo Bar", priority = Priority.BLOCKER)
-  static class RuleWithInvalidPropertyType {
-    @org.sonar.check.RuleProperty(description = "text", defaultValue = "Long text", type = "INVALID")
-    public String property;
-  }
-
-  @org.sonar.check.Rule(key = "foo", name = "bar", description = "Bar", tags = {"style", "security"})
-  static class RuleWithTags {
-  }
-}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest.java
deleted file mode 100644 (file)
index 213a352..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 com.google.common.base.Charsets;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.rule.Severity;
-import org.sonar.api.rule.RuleStatus;
-
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.UnsupportedEncodingException;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class RuleDefinitionsFromXmlTest {
-
-  @org.junit.Rule
-  public final ExpectedException thrown = ExpectedException.none();
-
-  private RuleDefinitions.Repository load(Reader reader) {
-    RuleDefinitions.Context context = new RuleDefinitions.Context();
-    RuleDefinitions.NewRepository newRepository = context.createRepository("squid", "java");
-    new RuleDefinitionsFromXml().loadRules(newRepository, reader);
-    newRepository.done();
-    return context.repository("squid");
-  }
-
-  @Test
-  public void should_parse_xml() throws Exception {
-    InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream("/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest/rules.xml"), Charsets.UTF_8.name());
-    RuleDefinitions.Repository repository = load(reader);
-    assertThat(repository.rules()).hasSize(2);
-
-    RuleDefinitions.Rule rule = repository.rule("complete");
-    assertThat(rule.key()).isEqualTo("complete");
-    assertThat(rule.name()).isEqualTo("Complete");
-    assertThat(rule.htmlDescription()).isEqualTo("Description of Complete");
-    assertThat(rule.severity()).isEqualTo(Severity.BLOCKER);
-    assertThat(rule.template()).isTrue();
-    assertThat(rule.status()).isEqualTo(RuleStatus.BETA);
-    assertThat(rule.internalKey()).isEqualTo("Checker/TreeWalker/LocalVariableName");
-    assertThat(rule.tags()).containsOnly("style", "security");
-
-    assertThat(rule.params()).hasSize(2);
-    RuleDefinitions.Param ignore = rule.param("ignore");
-    assertThat(ignore.key()).isEqualTo("ignore");
-    assertThat(ignore.description()).isEqualTo("Ignore ?");
-    assertThat(ignore.defaultValue()).isEqualTo("false");
-
-    rule = repository.rule("minimal");
-    assertThat(rule.key()).isEqualTo("minimal");
-    assertThat(rule.name()).isEqualTo("Minimal");
-    assertThat(rule.htmlDescription()).isEqualTo("Description of Minimal");
-    assertThat(rule.params()).isEmpty();
-    assertThat(rule.status()).isEqualTo(RuleStatus.READY);
-    assertThat(rule.severity()).isEqualTo(Severity.MAJOR);
-  }
-
-  @Test
-  public void should_fail_if_missing_rule_key() {
-    thrown.expect(IllegalStateException.class);
-    load(new StringReader("<rules><rule><name>Foo</name></rule></rules>"));
-  }
-
-  @Test
-  public void should_fail_if_missing_property_key() {
-    thrown.expect(IllegalStateException.class);
-    load(new StringReader("<rules><rule><key>foo</key><name>Foo</name><param></param></rule></rules>"));
-  }
-
-  @Test
-  public void should_fail_on_invalid_rule_parameter_type() {
-    thrown.expect(IllegalStateException.class);
-    load(new StringReader("<rules><rule><key>foo</key><name>Foo</name><param><key>key</key><type>INVALID</type></param></rule></rules>"));
-  }
-
-  @Test
-  public void test_utf8_encoding() throws UnsupportedEncodingException {
-    InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream("/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest/utf8.xml"), Charsets.UTF_8.name());
-    RuleDefinitions.Repository repository = load(reader);
-
-    assertThat(repository.rules()).hasSize(1);
-    RuleDefinitions.Rule rule = repository.rules().get(0);
-    assertThat(rule.key()).isEqualTo("com.puppycrawl.tools.checkstyle.checks.naming.LocalVariableNameCheck");
-    assertThat(rule.name()).isEqualTo("M & M");
-    assertThat(rule.htmlDescription().charAt(0)).isEqualTo('\u00E9');
-    assertThat(rule.htmlDescription().charAt(1)).isEqualTo('\u00E0');
-    assertThat(rule.htmlDescription().charAt(2)).isEqualTo('\u0026');
-  }
-
-  @Test
-  public void should_support_deprecated_format() throws UnsupportedEncodingException {
-    // the deprecated format uses some attributes instead of nodes
-    InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream("/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest/deprecated.xml"), Charsets.UTF_8.name());
-    RuleDefinitions.Repository repository = load(reader);
-
-    assertThat(repository.rules()).hasSize(1);
-    RuleDefinitions.Rule rule = repository.rules().get(0);
-    assertThat(rule.key()).isEqualTo("org.sonar.it.checkstyle.MethodsCountCheck");
-    assertThat(rule.internalKey()).isEqualTo("Checker/TreeWalker/org.sonar.it.checkstyle.MethodsCountCheck");
-    assertThat(rule.severity()).isEqualTo(Severity.CRITICAL);
-    assertThat(rule.htmlDescription()).isEqualTo("Count methods");
-    assertThat(rule.param("minMethodsCount")).isNotNull();
-  }
-}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleDefinitionsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleDefinitionsTest.java
deleted file mode 100644 (file)
index 023719c..0000000
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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.Ignore;
-import org.junit.Test;
-import org.sonar.api.rule.RemediationFunction;
-import org.sonar.api.rule.RuleStatus;
-import org.sonar.api.rule.Severity;
-
-import java.net.URL;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.fest.assertions.Fail.fail;
-
-public class RuleDefinitionsTest {
-
-  RuleDefinitions.Context context = new RuleDefinitions.Context();
-
-  @Test
-  public void define_repositories() throws Exception {
-    assertThat(context.repositories()).isEmpty();
-
-    context.createRepository("findbugs", "java").setName("Findbugs").done();
-    context.createRepository("checkstyle", "java").done();
-
-    assertThat(context.repositories()).hasSize(2);
-    RuleDefinitions.Repository findbugs = context.repository("findbugs");
-    assertThat(findbugs).isNotNull();
-    assertThat(findbugs.key()).isEqualTo("findbugs");
-    assertThat(findbugs.language()).isEqualTo("java");
-    assertThat(findbugs.name()).isEqualTo("Findbugs");
-    assertThat(findbugs.rules()).isEmpty();
-    RuleDefinitions.Repository checkstyle = context.repository("checkstyle");
-    assertThat(checkstyle).isNotNull();
-    assertThat(checkstyle.key()).isEqualTo("checkstyle");
-    assertThat(checkstyle.language()).isEqualTo("java");
-
-    // default name is key
-    assertThat(checkstyle.name()).isEqualTo("checkstyle");
-    assertThat(checkstyle.rules()).isEmpty();
-    assertThat(context.repository("unknown")).isNull();
-
-    // test equals() and hashCode()
-    assertThat(findbugs).isEqualTo(findbugs).isNotEqualTo(checkstyle).isNotEqualTo("findbugs").isNotEqualTo(null);
-    assertThat(findbugs.hashCode()).isEqualTo(findbugs.hashCode());
-  }
-
-  @Test
-  public void define_rules() {
-    RuleDefinitions.NewRepository newFindbugs = context.createRepository("findbugs", "java");
-    newFindbugs.createRule("NPE")
-      .setName("Detect NPE")
-      .setHtmlDescription("Detect <code>java.lang.NullPointerException</code>")
-      .setSeverity(Severity.BLOCKER)
-      .setInternalKey("/something")
-      .setStatus(RuleStatus.BETA)
-      .setCharacteristicKey("COMPILER")
-      .setRemediationFunction(RemediationFunction.LINEAR_OFFSET)
-      .setRemediationFactor("1h")
-      .setRemediationOffset("10min")
-      .setEffortToFixL10nKey("squid.S115.effortToFix")
-      .setTags("one", "two")
-      .addTags("two", "three", "four");
-    newFindbugs.createRule("ABC").setName("ABC").setHtmlDescription("ABC");
-    newFindbugs.done();
-
-    RuleDefinitions.Repository findbugs = context.repository("findbugs");
-    assertThat(findbugs.rules()).hasSize(2);
-
-    RuleDefinitions.Rule npeRule = findbugs.rule("NPE");
-    assertThat(npeRule.key()).isEqualTo("NPE");
-    assertThat(npeRule.name()).isEqualTo("Detect NPE");
-    assertThat(npeRule.severity()).isEqualTo(Severity.BLOCKER);
-    assertThat(npeRule.htmlDescription()).isEqualTo("Detect <code>java.lang.NullPointerException</code>");
-    assertThat(npeRule.tags()).containsOnly("one", "two", "three", "four");
-    assertThat(npeRule.params()).isEmpty();
-    assertThat(npeRule.internalKey()).isEqualTo("/something");
-    assertThat(npeRule.template()).isFalse();
-    assertThat(npeRule.status()).isEqualTo(RuleStatus.BETA);
-    assertThat(npeRule.characteristicKey()).isEqualTo("COMPILER");
-    assertThat(npeRule.remediationFunction()).isEqualTo(RemediationFunction.LINEAR_OFFSET);
-    assertThat(npeRule.remediationFactor()).isEqualTo("1h");
-    assertThat(npeRule.remediationOffset()).isEqualTo("10min");
-    assertThat(npeRule.effortToFixL10nKey()).isEqualTo("squid.S115.effortToFix");
-    assertThat(npeRule.toString()).isEqualTo("[repository=findbugs, key=NPE]");
-    assertThat(npeRule.repository()).isSameAs(findbugs);
-
-    // test equals() and hashCode()
-    RuleDefinitions.Rule otherRule = findbugs.rule("ABC");
-    assertThat(npeRule).isEqualTo(npeRule).isNotEqualTo(otherRule).isNotEqualTo("NPE").isNotEqualTo(null);
-    assertThat(npeRule.hashCode()).isEqualTo(npeRule.hashCode());
-  }
-
-  @Test
-  public void define_rule_with_default_fields() {
-    RuleDefinitions.NewRepository newFindbugs = context.createRepository("findbugs", "java");
-    newFindbugs.createRule("NPE").setName("NPE").setHtmlDescription("NPE");
-    newFindbugs.done();
-
-    RuleDefinitions.Rule rule = context.repository("findbugs").rule("NPE");
-    assertThat(rule.key()).isEqualTo("NPE");
-    assertThat(rule.severity()).isEqualTo(Severity.MAJOR);
-    assertThat(rule.params()).isEmpty();
-    assertThat(rule.internalKey()).isNull();
-    assertThat(rule.status()).isEqualTo(RuleStatus.defaultStatus());
-    assertThat(rule.tags()).isEmpty();
-    assertThat(rule.characteristicKey()).isNull();
-    assertThat(rule.remediationFunction()).isNull();
-    assertThat(rule.remediationFactor()).isNull();
-    assertThat(rule.remediationOffset()).isNull();
-  }
-
-  @Test
-  public void define_rule_parameters() {
-    RuleDefinitions.NewRepository newFindbugs = context.createRepository("findbugs", "java");
-    RuleDefinitions.NewRule newNpe = newFindbugs.createRule("NPE").setName("NPE").setHtmlDescription("NPE");
-    newNpe.createParam("level").setDefaultValue("LOW").setName("Level").setDescription("The level").setType(RuleParamType.INTEGER);
-    newNpe.createParam("effort");
-    newFindbugs.done();
-
-    RuleDefinitions.Rule rule = context.repository("findbugs").rule("NPE");
-    assertThat(rule.params()).hasSize(2);
-
-    RuleDefinitions.Param level = rule.param("level");
-    assertThat(level.key()).isEqualTo("level");
-    assertThat(level.name()).isEqualTo("Level");
-    assertThat(level.description()).isEqualTo("The level");
-    assertThat(level.defaultValue()).isEqualTo("LOW");
-    assertThat(level.type()).isEqualTo(RuleParamType.INTEGER);
-
-    RuleDefinitions.Param effort = rule.param("effort");
-    assertThat(effort.key()).isEqualTo("effort").isEqualTo(effort.name());
-    assertThat(effort.description()).isNull();
-    assertThat(effort.defaultValue()).isNull();
-    assertThat(effort.type()).isEqualTo(RuleParamType.STRING);
-
-    // test equals() and hashCode()
-    assertThat(level).isEqualTo(level).isNotEqualTo(effort).isNotEqualTo("level").isNotEqualTo(null);
-    assertThat(level.hashCode()).isEqualTo(level.hashCode());
-  }
-
-  @Test
-  public void sanitize_rule_name() {
-    RuleDefinitions.NewRepository newFindbugs = context.createRepository("findbugs", "java");
-    newFindbugs.createRule("NPE").setName("   \n  NullPointer   \n   ").setHtmlDescription("NPE");
-    newFindbugs.done();
-
-    RuleDefinitions.Rule rule = context.repository("findbugs").rule("NPE");
-    assertThat(rule.name()).isEqualTo("NullPointer");
-  }
-
-  @Test
-  public void sanitize_remediation_factor_and_offset() {
-    RuleDefinitions.NewRepository newFindbugs = context.createRepository("findbugs", "java");
-    newFindbugs.createRule("NPE")
-      .setName("Detect NPE")
-      .setHtmlDescription("NPE")
-      .setRemediationFactor("   1   h   ")
-      .setRemediationOffset(" 10  mi n ");
-    newFindbugs.done();
-
-    RuleDefinitions.Rule npeRule = context.repository("findbugs").rule("NPE");
-    assertThat(npeRule.remediationFactor()).isEqualTo("1h");
-    assertThat(npeRule.remediationOffset()).isEqualTo("10min");
-  }
-
-  @Test
-  public void extend_repository() {
-    assertThat(context.extendedRepositories()).isEmpty();
-
-    // for example fb-contrib
-    RuleDefinitions.NewExtendedRepository newFindbugs = context.extendRepository("findbugs", "java");
-    newFindbugs.createRule("NPE").setName("NPE").setHtmlDescription("NPE");
-    newFindbugs.done();
-
-    assertThat(context.repositories()).isEmpty();
-    assertThat(context.extendedRepositories()).hasSize(1);
-    assertThat(context.extendedRepositories("other")).isEmpty();
-    assertThat(context.extendedRepositories("findbugs")).hasSize(1);
-
-    RuleDefinitions.ExtendedRepository findbugs = context.extendedRepositories("findbugs").get(0);
-    assertThat(findbugs.language()).isEqualTo("java");
-    assertThat(findbugs.rule("NPE")).isNotNull();
-  }
-
-  @Test
-  public void cant_set_blank_repository_name() throws Exception {
-    context.createRepository("findbugs", "java").setName(null).done();
-
-    assertThat(context.repository("findbugs").name()).isEqualTo("findbugs");
-  }
-
-  @Test
-  public void fail_if_duplicated_repo_keys() {
-    context.createRepository("findbugs", "java").done();
-    try {
-      context.createRepository("findbugs", "whatever_the_language").done();
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("The rule repository 'findbugs' is defined several times");
-    }
-  }
-
-  @Test
-  public void warning_if_duplicated_rule_keys() {
-    RuleDefinitions.NewRepository findbugs = context.createRepository("findbugs", "java");
-    findbugs.createRule("NPE");
-    findbugs.createRule("NPE");
-    // do not fail as long as http://jira.codehaus.org/browse/SONARJAVA-428 is not fixed
-  }
-
-  @Test
-  public void fail_if_duplicated_rule_param_keys() {
-    RuleDefinitions.NewRule rule = context.createRepository("findbugs", "java").createRule("NPE");
-    rule.createParam("level");
-    try {
-      rule.createParam("level");
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("The parameter 'level' is declared several times on the rule [repository=findbugs, key=NPE]");
-    }
-  }
-
-  @Test
-  public void fail_if_blank_rule_name() {
-    RuleDefinitions.NewRepository newRepository = context.createRepository("findbugs", "java");
-    newRepository.createRule("NPE").setName(null).setHtmlDescription("NPE");
-    try {
-      newRepository.done();
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("Name of rule [repository=findbugs, key=NPE] is empty");
-    }
-  }
-
-  @Test
-  public void fail_if_bad_rule_tag() {
-    try {
-      // whitespaces are not allowed in tags
-      context.createRepository("findbugs", "java").createRule("NPE").setTags("coding style");
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(IllegalArgumentException.class)
-        .hasMessage("Tag 'coding style' is invalid. Rule tags accept only the following characters: a-z, 0-9, '+', '-', '#', '.'");
-    }
-  }
-
-  @Test
-  public void load_rule_description_from_file() {
-    RuleDefinitions.NewRepository newRepository = context.createRepository("findbugs", "java");
-    newRepository.createRule("NPE").setName("NPE").setHtmlDescription(getClass().getResource("/org/sonar/api/server/rule/RuleDefinitionsTest/sample.html"));
-    newRepository.done();
-
-    RuleDefinitions.Rule rule = context.repository("findbugs").rule("NPE");
-    assertThat(rule.htmlDescription()).isEqualTo("description of rule loaded from file");
-  }
-
-  @Test
-  public void fail_to_load_rule_description_from_file() {
-    RuleDefinitions.NewRepository newRepository = context.createRepository("findbugs", "java");
-    newRepository.createRule("NPE").setName("NPE").setHtmlDescription((URL)null);
-    try {
-      newRepository.done();
-      fail();
-    } catch (IllegalStateException e) {
-      assertThat(e).hasMessage("HTML description of rule [repository=findbugs, key=NPE] is empty");
-    }
-  }
-
-  @Test
-  public void fail_if_blank_rule_html_description() {
-    RuleDefinitions.NewRepository newRepository = context.createRepository("findbugs", "java");
-    newRepository.createRule("NPE").setName("NPE").setHtmlDescription((String)null);
-    try {
-      newRepository.done();
-      fail();
-    } catch (IllegalStateException e) {
-      assertThat(e).hasMessage("HTML description of rule [repository=findbugs, key=NPE] is empty");
-    }
-  }
-
-  @Test
-  public void fail_if_bad_rule_severity() {
-    try {
-      context.createRepository("findbugs", "java").createRule("NPE").setSeverity("VERY HIGH");
-      fail();
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessage("Severity of rule [repository=findbugs, key=NPE] is not correct: VERY HIGH");
-    }
-  }
-
-  @Test
-  public void fail_if_removed_status() {
-    try {
-      context.createRepository("findbugs", "java").createRule("NPE").setStatus(RuleStatus.REMOVED);
-      fail();
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessage("Status 'REMOVED' is not accepted on rule '[repository=findbugs, key=NPE]'");
-    }
-  }
-
-  @Test
-  @Ignore("TODO")
-  public void fail_if_bad_remediation_factor_or_offset() {
-    try {
-      context.createRepository("findbugs", "java").createRule("NPE").setRemediationFactor("ten hours");
-      fail();
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessage("Duration 'ten hours' is invalid, it should use the following sample format : 2d 10h 15min");
-    }
-
-    try {
-      context.createRepository("findbugs", "java").createRule("NPE").setRemediationOffset("ten hours");
-      fail();
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessage("Duration 'ten hours' is invalid, it should use the following sample format : 2d 10h 15min");
-    }
-  }
-
-}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionFromAnnotationsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionFromAnnotationsTest.java
new file mode 100644 (file)
index 0000000..65efa9d
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.junit.rules.ExpectedException;
+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 static org.fest.assertions.Assertions.assertThat;
+
+public class RulesDefinitionFromAnnotationsTest {
+
+  @org.junit.Rule
+  public final ExpectedException thrown = ExpectedException.none();
+
+  @Test
+  public void rule_with_property() {
+    RulesDefinition.Repository repository = load(RuleWithProperty.class);
+    assertThat(repository.rules()).hasSize(1);
+    RulesDefinition.Rule rule = repository.rules().get(0);
+    assertThat(rule.key()).isEqualTo("foo");
+    assertThat(rule.status()).isEqualTo(RuleStatus.BETA);
+    assertThat(rule.name()).isEqualTo("bar");
+    assertThat(rule.htmlDescription()).isEqualTo("Foo Bar");
+    assertThat(rule.severity()).isEqualTo(Severity.BLOCKER);
+    assertThat(rule.params()).hasSize(1);
+    assertThat(rule.tags()).isEmpty();
+
+    RulesDefinition.Param prop = rule.param("property");
+    assertThat(prop.key()).isEqualTo("property");
+    assertThat(prop.description()).isEqualTo("Ignore ?");
+    assertThat(prop.defaultValue()).isEqualTo("false");
+    assertThat(prop.type()).isEqualTo(RuleParamType.STRING);
+  }
+
+  @Test
+  public void override_annotation_programmatically() {
+    RulesDefinition.Context context = new RulesDefinition.Context();
+    RulesDefinition.NewRepository newRepository = context.createRepository("squid", "java");
+    NewRule newRule = newRepository.loadAnnotatedClass(RuleWithProperty.class);
+    newRule.setName("Overriden name");
+    newRule.param("property").setDefaultValue("true");
+    newRule.param("property").setDescription("Overriden");
+    newRepository.done();
+
+    RulesDefinition.Repository repository = context.repository("squid");
+    assertThat(repository.rules()).hasSize(1);
+    RulesDefinition.Rule rule = repository.rules().get(0);
+    assertThat(rule.key()).isEqualTo("foo");
+    assertThat(rule.status()).isEqualTo(RuleStatus.BETA);
+    assertThat(rule.name()).isEqualTo("Overriden name");
+    assertThat(rule.htmlDescription()).isEqualTo("Foo Bar");
+    assertThat(rule.severity()).isEqualTo(Severity.BLOCKER);
+    assertThat(rule.params()).hasSize(1);
+
+    RulesDefinition.Param prop = rule.param("property");
+    assertThat(prop.key()).isEqualTo("property");
+    assertThat(prop.description()).isEqualTo("Overriden");
+    assertThat(prop.defaultValue()).isEqualTo("true");
+    assertThat(prop.type()).isEqualTo(RuleParamType.STRING);
+  }
+
+  @Test
+  public void rule_with_integer_property() {
+    RulesDefinition.Repository repository = load(RuleWithIntegerProperty.class);
+
+    RulesDefinition.Param prop = repository.rules().get(0).param("property");
+    assertThat(prop.description()).isEqualTo("Max");
+    assertThat(prop.defaultValue()).isEqualTo("12");
+    assertThat(prop.type()).isEqualTo(RuleParamType.INTEGER);
+  }
+
+  @Test
+  public void rule_with_text_property() {
+    RulesDefinition.Repository repository = load(RuleWithTextProperty.class);
+
+    RulesDefinition.Param prop = repository.rules().get(0).param("property");
+    assertThat(prop.description()).isEqualTo("text");
+    assertThat(prop.defaultValue()).isEqualTo("Long text");
+    assertThat(prop.type()).isEqualTo(RuleParamType.TEXT);
+  }
+
+  @Test
+  public void should_recognize_type() {
+    assertThat(RuleDefinitionsFromAnnotations.guessType(Integer.class)).isEqualTo(RuleParamType.INTEGER);
+    assertThat(RuleDefinitionsFromAnnotations.guessType(int.class)).isEqualTo(RuleParamType.INTEGER);
+    assertThat(RuleDefinitionsFromAnnotations.guessType(Float.class)).isEqualTo(RuleParamType.FLOAT);
+    assertThat(RuleDefinitionsFromAnnotations.guessType(float.class)).isEqualTo(RuleParamType.FLOAT);
+    assertThat(RuleDefinitionsFromAnnotations.guessType(Boolean.class)).isEqualTo(RuleParamType.BOOLEAN);
+    assertThat(RuleDefinitionsFromAnnotations.guessType(boolean.class)).isEqualTo(RuleParamType.BOOLEAN);
+    assertThat(RuleDefinitionsFromAnnotations.guessType(String.class)).isEqualTo(RuleParamType.STRING);
+    assertThat(RuleDefinitionsFromAnnotations.guessType(Object.class)).isEqualTo(RuleParamType.STRING);
+  }
+
+  @Test
+  public void use_classname_when_missing_key() {
+    RulesDefinition.Repository repository = load(RuleWithoutKey.class);
+    assertThat(repository.rules()).hasSize(1);
+    RulesDefinition.Rule rule = repository.rules().get(0);
+    assertThat(rule.key()).isEqualTo(RuleWithoutKey.class.getCanonicalName());
+    assertThat(rule.name()).isEqualTo("foo");
+  }
+
+  @Test
+  public void rule_with_tags() {
+    RulesDefinition.Repository repository = load(RuleWithTags.class);
+    assertThat(repository.rules()).hasSize(1);
+    RulesDefinition.Rule rule = repository.rules().get(0);
+    assertThat(rule.tags()).containsOnly("style", "security");
+  }
+
+  @Test
+  public void overridden_class() {
+    RulesDefinition.Repository repository = load(OverridingRule.class);
+    assertThat(repository.rules()).hasSize(1);
+    RulesDefinition.Rule rule = repository.rules().get(0);
+    assertThat(rule.key()).isEqualTo("overriding_foo");
+    assertThat(rule.name()).isEqualTo("Overriding Foo");
+    assertThat(rule.severity()).isEqualTo(Severity.MAJOR);
+    assertThat(rule.htmlDescription()).isEqualTo("Desc of Overriding Foo");
+    assertThat(rule.params()).hasSize(2);
+  }
+
+  private RulesDefinition.Repository load(Class annotatedClass) {
+    RulesDefinition.Context context = new RulesDefinition.Context();
+    RulesDefinition.NewExtendedRepository newRepository = context.createRepository("squid", "java")
+      .loadAnnotatedClasses(annotatedClass);
+    newRepository.done();
+    return context.repository("squid");
+  }
+
+  @org.sonar.check.Rule(name = "foo", description = "Foo")
+  static class RuleWithoutKey {
+  }
+
+  @org.sonar.check.Rule(key = "foo", name = "bar", description = "Foo Bar", priority = Priority.BLOCKER, status = "BETA")
+  static class RuleWithProperty {
+    @org.sonar.check.RuleProperty(description = "Ignore ?", defaultValue = "false")
+    private String property;
+  }
+
+  @org.sonar.check.Rule(key = "overriding_foo", name = "Overriding Foo", description = "Desc of Overriding Foo")
+  static class OverridingRule extends RuleWithProperty {
+    @org.sonar.check.RuleProperty
+    private String additionalProperty;
+  }
+
+  @org.sonar.check.Rule(key = "foo", name = "bar", description = "Foo Bar", priority = Priority.BLOCKER)
+  static class RuleWithIntegerProperty {
+    @org.sonar.check.RuleProperty(description = "Max", defaultValue = "12")
+    private Integer property;
+  }
+
+  @org.sonar.check.Rule(key = "foo", name = "bar", description = "Foo Bar", priority = Priority.BLOCKER)
+  static class RuleWithTextProperty {
+    @org.sonar.check.RuleProperty(description = "text", defaultValue = "Long text", type = "TEXT")
+    protected String property;
+  }
+
+  @org.sonar.check.Rule(key = "foo", name = "bar", description = "Foo Bar", priority = Priority.BLOCKER)
+  static class RuleWithInvalidPropertyType {
+    @org.sonar.check.RuleProperty(description = "text", defaultValue = "Long text", type = "INVALID")
+    public String property;
+  }
+
+  @org.sonar.check.Rule(key = "foo", name = "bar", description = "Bar", tags = {"style", "security"})
+  static class RuleWithTags {
+  }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionFromXmlTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionFromXmlTest.java
new file mode 100644 (file)
index 0000000..8be3591
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 com.google.common.base.Charsets;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rule.Severity;
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class RulesDefinitionFromXmlTest {
+
+  @org.junit.Rule
+  public final ExpectedException thrown = ExpectedException.none();
+
+  private RulesDefinition.Repository load(Reader reader) {
+    RulesDefinition.Context context = new RulesDefinition.Context();
+    RulesDefinition.NewRepository newRepository = context.createRepository("squid", "java");
+    new RuleDefinitionsFromXml().loadRules(newRepository, reader);
+    newRepository.done();
+    return context.repository("squid");
+  }
+
+  @Test
+  public void should_parse_xml() throws Exception {
+    InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream("/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest/rules.xml"), Charsets.UTF_8.name());
+    RulesDefinition.Repository repository = load(reader);
+    assertThat(repository.rules()).hasSize(2);
+
+    RulesDefinition.Rule rule = repository.rule("complete");
+    assertThat(rule.key()).isEqualTo("complete");
+    assertThat(rule.name()).isEqualTo("Complete");
+    assertThat(rule.htmlDescription()).isEqualTo("Description of Complete");
+    assertThat(rule.severity()).isEqualTo(Severity.BLOCKER);
+    assertThat(rule.template()).isTrue();
+    assertThat(rule.status()).isEqualTo(RuleStatus.BETA);
+    assertThat(rule.internalKey()).isEqualTo("Checker/TreeWalker/LocalVariableName");
+    assertThat(rule.tags()).containsOnly("style", "security");
+
+    assertThat(rule.params()).hasSize(2);
+    RulesDefinition.Param ignore = rule.param("ignore");
+    assertThat(ignore.key()).isEqualTo("ignore");
+    assertThat(ignore.description()).isEqualTo("Ignore ?");
+    assertThat(ignore.defaultValue()).isEqualTo("false");
+
+    rule = repository.rule("minimal");
+    assertThat(rule.key()).isEqualTo("minimal");
+    assertThat(rule.name()).isEqualTo("Minimal");
+    assertThat(rule.htmlDescription()).isEqualTo("Description of Minimal");
+    assertThat(rule.params()).isEmpty();
+    assertThat(rule.status()).isEqualTo(RuleStatus.READY);
+    assertThat(rule.severity()).isEqualTo(Severity.MAJOR);
+  }
+
+  @Test
+  public void should_fail_if_missing_rule_key() {
+    thrown.expect(IllegalStateException.class);
+    load(new StringReader("<rules><rule><name>Foo</name></rule></rules>"));
+  }
+
+  @Test
+  public void should_fail_if_missing_property_key() {
+    thrown.expect(IllegalStateException.class);
+    load(new StringReader("<rules><rule><key>foo</key><name>Foo</name><param></param></rule></rules>"));
+  }
+
+  @Test
+  public void should_fail_on_invalid_rule_parameter_type() {
+    thrown.expect(IllegalStateException.class);
+    load(new StringReader("<rules><rule><key>foo</key><name>Foo</name><param><key>key</key><type>INVALID</type></param></rule></rules>"));
+  }
+
+  @Test
+  public void test_utf8_encoding() throws UnsupportedEncodingException {
+    InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream("/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest/utf8.xml"), Charsets.UTF_8.name());
+    RulesDefinition.Repository repository = load(reader);
+
+    assertThat(repository.rules()).hasSize(1);
+    RulesDefinition.Rule rule = repository.rules().get(0);
+    assertThat(rule.key()).isEqualTo("com.puppycrawl.tools.checkstyle.checks.naming.LocalVariableNameCheck");
+    assertThat(rule.name()).isEqualTo("M & M");
+    assertThat(rule.htmlDescription().charAt(0)).isEqualTo('\u00E9');
+    assertThat(rule.htmlDescription().charAt(1)).isEqualTo('\u00E0');
+    assertThat(rule.htmlDescription().charAt(2)).isEqualTo('\u0026');
+  }
+
+  @Test
+  public void should_support_deprecated_format() throws UnsupportedEncodingException {
+    // the deprecated format uses some attributes instead of nodes
+    InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream("/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest/deprecated.xml"), Charsets.UTF_8.name());
+    RulesDefinition.Repository repository = load(reader);
+
+    assertThat(repository.rules()).hasSize(1);
+    RulesDefinition.Rule rule = repository.rules().get(0);
+    assertThat(rule.key()).isEqualTo("org.sonar.it.checkstyle.MethodsCountCheck");
+    assertThat(rule.internalKey()).isEqualTo("Checker/TreeWalker/org.sonar.it.checkstyle.MethodsCountCheck");
+    assertThat(rule.severity()).isEqualTo(Severity.CRITICAL);
+    assertThat(rule.htmlDescription()).isEqualTo("Count methods");
+    assertThat(rule.param("minMethodsCount")).isNotNull();
+  }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java
new file mode 100644 (file)
index 0000000..f37ec36
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.Ignore;
+import org.junit.Test;
+import org.sonar.api.rule.RemediationFunction;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rule.Severity;
+
+import java.net.URL;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+
+public class RulesDefinitionTest {
+
+  RulesDefinition.Context context = new RulesDefinition.Context();
+
+  @Test
+  public void define_repositories() throws Exception {
+    assertThat(context.repositories()).isEmpty();
+
+    context.createRepository("findbugs", "java").setName("Findbugs").done();
+    context.createRepository("checkstyle", "java").done();
+
+    assertThat(context.repositories()).hasSize(2);
+    RulesDefinition.Repository findbugs = context.repository("findbugs");
+    assertThat(findbugs).isNotNull();
+    assertThat(findbugs.key()).isEqualTo("findbugs");
+    assertThat(findbugs.language()).isEqualTo("java");
+    assertThat(findbugs.name()).isEqualTo("Findbugs");
+    assertThat(findbugs.rules()).isEmpty();
+    RulesDefinition.Repository checkstyle = context.repository("checkstyle");
+    assertThat(checkstyle).isNotNull();
+    assertThat(checkstyle.key()).isEqualTo("checkstyle");
+    assertThat(checkstyle.language()).isEqualTo("java");
+
+    // default name is key
+    assertThat(checkstyle.name()).isEqualTo("checkstyle");
+    assertThat(checkstyle.rules()).isEmpty();
+    assertThat(context.repository("unknown")).isNull();
+
+    // test equals() and hashCode()
+    assertThat(findbugs).isEqualTo(findbugs).isNotEqualTo(checkstyle).isNotEqualTo("findbugs").isNotEqualTo(null);
+    assertThat(findbugs.hashCode()).isEqualTo(findbugs.hashCode());
+  }
+
+  @Test
+  public void define_rules() {
+    RulesDefinition.NewRepository newFindbugs = context.createRepository("findbugs", "java");
+    newFindbugs.createRule("NPE")
+      .setName("Detect NPE")
+      .setHtmlDescription("Detect <code>java.lang.NullPointerException</code>")
+      .setSeverity(Severity.BLOCKER)
+      .setInternalKey("/something")
+      .setStatus(RuleStatus.BETA)
+      .setCharacteristicKey("COMPILER")
+      .setRemediationFunction(RemediationFunction.LINEAR_OFFSET)
+      .setRemediationFactor("1h")
+      .setRemediationOffset("10min")
+      .setEffortToFixL10nKey("squid.S115.effortToFix")
+      .setTags("one", "two")
+      .addTags("two", "three", "four");
+    newFindbugs.createRule("ABC").setName("ABC").setHtmlDescription("ABC");
+    newFindbugs.done();
+
+    RulesDefinition.Repository findbugs = context.repository("findbugs");
+    assertThat(findbugs.rules()).hasSize(2);
+
+    RulesDefinition.Rule npeRule = findbugs.rule("NPE");
+    assertThat(npeRule.key()).isEqualTo("NPE");
+    assertThat(npeRule.name()).isEqualTo("Detect NPE");
+    assertThat(npeRule.severity()).isEqualTo(Severity.BLOCKER);
+    assertThat(npeRule.htmlDescription()).isEqualTo("Detect <code>java.lang.NullPointerException</code>");
+    assertThat(npeRule.tags()).containsOnly("one", "two", "three", "four");
+    assertThat(npeRule.params()).isEmpty();
+    assertThat(npeRule.internalKey()).isEqualTo("/something");
+    assertThat(npeRule.template()).isFalse();
+    assertThat(npeRule.status()).isEqualTo(RuleStatus.BETA);
+    assertThat(npeRule.characteristicKey()).isEqualTo("COMPILER");
+    assertThat(npeRule.remediationFunction()).isEqualTo(RemediationFunction.LINEAR_OFFSET);
+    assertThat(npeRule.remediationFactor()).isEqualTo("1h");
+    assertThat(npeRule.remediationOffset()).isEqualTo("10min");
+    assertThat(npeRule.effortToFixL10nKey()).isEqualTo("squid.S115.effortToFix");
+    assertThat(npeRule.toString()).isEqualTo("[repository=findbugs, key=NPE]");
+    assertThat(npeRule.repository()).isSameAs(findbugs);
+
+    // test equals() and hashCode()
+    RulesDefinition.Rule otherRule = findbugs.rule("ABC");
+    assertThat(npeRule).isEqualTo(npeRule).isNotEqualTo(otherRule).isNotEqualTo("NPE").isNotEqualTo(null);
+    assertThat(npeRule.hashCode()).isEqualTo(npeRule.hashCode());
+  }
+
+  @Test
+  public void define_rule_with_default_fields() {
+    RulesDefinition.NewRepository newFindbugs = context.createRepository("findbugs", "java");
+    newFindbugs.createRule("NPE").setName("NPE").setHtmlDescription("NPE");
+    newFindbugs.done();
+
+    RulesDefinition.Rule rule = context.repository("findbugs").rule("NPE");
+    assertThat(rule.key()).isEqualTo("NPE");
+    assertThat(rule.severity()).isEqualTo(Severity.MAJOR);
+    assertThat(rule.params()).isEmpty();
+    assertThat(rule.internalKey()).isNull();
+    assertThat(rule.status()).isEqualTo(RuleStatus.defaultStatus());
+    assertThat(rule.tags()).isEmpty();
+    assertThat(rule.characteristicKey()).isNull();
+    assertThat(rule.remediationFunction()).isNull();
+    assertThat(rule.remediationFactor()).isNull();
+    assertThat(rule.remediationOffset()).isNull();
+  }
+
+  @Test
+  public void define_rule_parameters() {
+    RulesDefinition.NewRepository newFindbugs = context.createRepository("findbugs", "java");
+    RulesDefinition.NewRule newNpe = newFindbugs.createRule("NPE").setName("NPE").setHtmlDescription("NPE");
+    newNpe.createParam("level").setDefaultValue("LOW").setName("Level").setDescription("The level").setType(RuleParamType.INTEGER);
+    newNpe.createParam("effort");
+    newFindbugs.done();
+
+    RulesDefinition.Rule rule = context.repository("findbugs").rule("NPE");
+    assertThat(rule.params()).hasSize(2);
+
+    RulesDefinition.Param level = rule.param("level");
+    assertThat(level.key()).isEqualTo("level");
+    assertThat(level.name()).isEqualTo("Level");
+    assertThat(level.description()).isEqualTo("The level");
+    assertThat(level.defaultValue()).isEqualTo("LOW");
+    assertThat(level.type()).isEqualTo(RuleParamType.INTEGER);
+
+    RulesDefinition.Param effort = rule.param("effort");
+    assertThat(effort.key()).isEqualTo("effort").isEqualTo(effort.name());
+    assertThat(effort.description()).isNull();
+    assertThat(effort.defaultValue()).isNull();
+    assertThat(effort.type()).isEqualTo(RuleParamType.STRING);
+
+    // test equals() and hashCode()
+    assertThat(level).isEqualTo(level).isNotEqualTo(effort).isNotEqualTo("level").isNotEqualTo(null);
+    assertThat(level.hashCode()).isEqualTo(level.hashCode());
+  }
+
+  @Test
+  public void sanitize_rule_name() {
+    RulesDefinition.NewRepository newFindbugs = context.createRepository("findbugs", "java");
+    newFindbugs.createRule("NPE").setName("   \n  NullPointer   \n   ").setHtmlDescription("NPE");
+    newFindbugs.done();
+
+    RulesDefinition.Rule rule = context.repository("findbugs").rule("NPE");
+    assertThat(rule.name()).isEqualTo("NullPointer");
+  }
+
+  @Test
+  public void sanitize_remediation_factor_and_offset() {
+    RulesDefinition.NewRepository newFindbugs = context.createRepository("findbugs", "java");
+    newFindbugs.createRule("NPE")
+      .setName("Detect NPE")
+      .setHtmlDescription("NPE")
+      .setRemediationFactor("   1   h   ")
+      .setRemediationOffset(" 10  mi n ");
+    newFindbugs.done();
+
+    RulesDefinition.Rule npeRule = context.repository("findbugs").rule("NPE");
+    assertThat(npeRule.remediationFactor()).isEqualTo("1h");
+    assertThat(npeRule.remediationOffset()).isEqualTo("10min");
+  }
+
+  @Test
+  public void extend_repository() {
+    assertThat(context.extendedRepositories()).isEmpty();
+
+    // for example fb-contrib
+    RulesDefinition.NewExtendedRepository newFindbugs = context.extendRepository("findbugs", "java");
+    newFindbugs.createRule("NPE").setName("NPE").setHtmlDescription("NPE");
+    newFindbugs.done();
+
+    assertThat(context.repositories()).isEmpty();
+    assertThat(context.extendedRepositories()).hasSize(1);
+    assertThat(context.extendedRepositories("other")).isEmpty();
+    assertThat(context.extendedRepositories("findbugs")).hasSize(1);
+
+    RulesDefinition.ExtendedRepository findbugs = context.extendedRepositories("findbugs").get(0);
+    assertThat(findbugs.language()).isEqualTo("java");
+    assertThat(findbugs.rule("NPE")).isNotNull();
+  }
+
+  @Test
+  public void cant_set_blank_repository_name() throws Exception {
+    context.createRepository("findbugs", "java").setName(null).done();
+
+    assertThat(context.repository("findbugs").name()).isEqualTo("findbugs");
+  }
+
+  @Test
+  public void fail_if_duplicated_repo_keys() {
+    context.createRepository("findbugs", "java").done();
+    try {
+      context.createRepository("findbugs", "whatever_the_language").done();
+      fail();
+    } catch (Exception e) {
+      assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("The rule repository 'findbugs' is defined several times");
+    }
+  }
+
+  @Test
+  public void warning_if_duplicated_rule_keys() {
+    RulesDefinition.NewRepository findbugs = context.createRepository("findbugs", "java");
+    findbugs.createRule("NPE");
+    findbugs.createRule("NPE");
+    // do not fail as long as http://jira.codehaus.org/browse/SONARJAVA-428 is not fixed
+  }
+
+  @Test
+  public void fail_if_duplicated_rule_param_keys() {
+    RulesDefinition.NewRule rule = context.createRepository("findbugs", "java").createRule("NPE");
+    rule.createParam("level");
+    try {
+      rule.createParam("level");
+      fail();
+    } catch (Exception e) {
+      assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("The parameter 'level' is declared several times on the rule [repository=findbugs, key=NPE]");
+    }
+  }
+
+  @Test
+  public void fail_if_blank_rule_name() {
+    RulesDefinition.NewRepository newRepository = context.createRepository("findbugs", "java");
+    newRepository.createRule("NPE").setName(null).setHtmlDescription("NPE");
+    try {
+      newRepository.done();
+      fail();
+    } catch (Exception e) {
+      assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("Name of rule [repository=findbugs, key=NPE] is empty");
+    }
+  }
+
+  @Test
+  public void fail_if_bad_rule_tag() {
+    try {
+      // whitespaces are not allowed in tags
+      context.createRepository("findbugs", "java").createRule("NPE").setTags("coding style");
+      fail();
+    } catch (Exception e) {
+      assertThat(e).isInstanceOf(IllegalArgumentException.class)
+        .hasMessage("Tag 'coding style' is invalid. Rule tags accept only the following characters: a-z, 0-9, '+', '-', '#', '.'");
+    }
+  }
+
+  @Test
+  public void load_rule_description_from_file() {
+    RulesDefinition.NewRepository newRepository = context.createRepository("findbugs", "java");
+    newRepository.createRule("NPE").setName("NPE").setHtmlDescription(getClass().getResource("/org/sonar/api/server/rule/RuleDefinitionsTest/sample.html"));
+    newRepository.done();
+
+    RulesDefinition.Rule rule = context.repository("findbugs").rule("NPE");
+    assertThat(rule.htmlDescription()).isEqualTo("description of rule loaded from file");
+  }
+
+  @Test
+  public void fail_to_load_rule_description_from_file() {
+    RulesDefinition.NewRepository newRepository = context.createRepository("findbugs", "java");
+    newRepository.createRule("NPE").setName("NPE").setHtmlDescription((URL)null);
+    try {
+      newRepository.done();
+      fail();
+    } catch (IllegalStateException e) {
+      assertThat(e).hasMessage("HTML description of rule [repository=findbugs, key=NPE] is empty");
+    }
+  }
+
+  @Test
+  public void fail_if_blank_rule_html_description() {
+    RulesDefinition.NewRepository newRepository = context.createRepository("findbugs", "java");
+    newRepository.createRule("NPE").setName("NPE").setHtmlDescription((String)null);
+    try {
+      newRepository.done();
+      fail();
+    } catch (IllegalStateException e) {
+      assertThat(e).hasMessage("HTML description of rule [repository=findbugs, key=NPE] is empty");
+    }
+  }
+
+  @Test
+  public void fail_if_bad_rule_severity() {
+    try {
+      context.createRepository("findbugs", "java").createRule("NPE").setSeverity("VERY HIGH");
+      fail();
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessage("Severity of rule [repository=findbugs, key=NPE] is not correct: VERY HIGH");
+    }
+  }
+
+  @Test
+  public void fail_if_removed_status() {
+    try {
+      context.createRepository("findbugs", "java").createRule("NPE").setStatus(RuleStatus.REMOVED);
+      fail();
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessage("Status 'REMOVED' is not accepted on rule '[repository=findbugs, key=NPE]'");
+    }
+  }
+
+  @Test
+  @Ignore("TODO")
+  public void fail_if_bad_remediation_factor_or_offset() {
+    try {
+      context.createRepository("findbugs", "java").createRule("NPE").setRemediationFactor("ten hours");
+      fail();
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessage("Duration 'ten hours' is invalid, it should use the following sample format : 2d 10h 15min");
+    }
+
+    try {
+      context.createRepository("findbugs", "java").createRule("NPE").setRemediationOffset("ten hours");
+      fail();
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessage("Duration 'ten hours' is invalid, it should use the following sample format : 2d 10h 15min");
+    }
+  }
+
+}
index b72c019dc07ca22d6d45c7397817c1ba7a27009a..9eaaa73e1dc6d224808c8090910627d9f217bbd6 100644 (file)
@@ -432,7 +432,7 @@ public final class Platform {
     ComponentContainer startupContainer = servicesContainer.createChild();
     startupContainer.addSingleton(GwtPublisher.class);
     startupContainer.addSingleton(RegisterMetrics.class);
-    startupContainer.addSingleton(DeprecatedRuleDefinitions.class);
+    startupContainer.addSingleton(DeprecatedRulesDefinition.class);
     startupContainer.addSingleton(RuleDefinitionsLoader.class);
     startupContainer.addSingleton(RuleRegistration.class);
     startupContainer.addSingleton(RegisterNewProfiles.class);
diff --git a/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRuleDefinitions.java b/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRuleDefinitions.java
deleted file mode 100644 (file)
index 42171ca..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 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.rule.RuleDefinitions;
-import org.sonar.api.server.rule.RuleParamType;
-import org.sonar.check.Cardinality;
-import org.sonar.core.i18n.RuleI18nManager;
-import org.sonar.core.technicaldebt.DebtRulesXMLImporter;
-import org.sonar.core.technicaldebt.TechnicalDebtModelRepository;
-
-import javax.annotation.CheckForNull;
-
-import java.io.Reader;
-import java.util.Collection;
-import java.util.List;
-
-import static com.google.common.collect.Lists.newArrayList;
-
-/**
- * Inject deprecated RuleRepository into RuleDefinitions for backward-compatibility.
- *
- * @since 4.2
- */
-public class DeprecatedRuleDefinitions implements RuleDefinitions {
-
-  private final RuleI18nManager i18n;
-  private final RuleRepository[] repositories;
-
-  private final TechnicalDebtModelRepository languageModelFinder;
-  private final DebtRulesXMLImporter importer;
-
-  public DeprecatedRuleDefinitions(RuleI18nManager i18n, RuleRepository[] repositories, TechnicalDebtModelRepository languageModelFinder, DebtRulesXMLImporter importer) {
-    this.i18n = i18n;
-    this.repositories = repositories;
-    this.languageModelFinder = languageModelFinder;
-    this.importer = importer;
-  }
-
-  public DeprecatedRuleDefinitions(RuleI18nManager i18n, TechnicalDebtModelRepository languageModelFinder, DebtRulesXMLImporter importer) {
-    this(i18n, new RuleRepository[0], languageModelFinder, importer);
-  }
-
-  @Override
-  public void define(Context context) {
-    // Load rule debt definitions from xml files provided by plugin
-    List<DebtRulesXMLImporter.RuleDebt> ruleDebts = loadRuleDebtList();
-
-    for (RuleRepository repository : repositories) {
-      // RuleRepository API does not handle difference between new and extended repositories,
-      NewRepository newRepository;
-      if (context.repository(repository.getKey()) == null) {
-        newRepository = context.createRepository(repository.getKey(), repository.getLanguage());
-        newRepository.setName(repository.getName());
-      } else {
-        newRepository = (NewRepository) context.extendRepository(repository.getKey(), repository.getLanguage());
-      }
-      for (org.sonar.api.rules.Rule rule : repository.createRules()) {
-        NewRule newRule = newRepository.createRule(rule.getKey());
-        newRule.setName(ruleName(repository.getKey(), rule));
-        newRule.setHtmlDescription(ruleDescription(repository.getKey(), rule));
-        newRule.setInternalKey(rule.getConfigKey());
-        newRule.setTemplate(Cardinality.MULTIPLE.equals(rule.getCardinality()));
-        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()) {
-          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 void updateRuleDebtDefinitions(NewRule newRule, String repoKey, String ruleKey, List<DebtRulesXMLImporter.RuleDebt> ruleDebts){
-    DebtRulesXMLImporter.RuleDebt ruleDebt = findRequirement(ruleDebts, repoKey, ruleKey);
-    if (ruleDebt != null) {
-      newRule.setCharacteristicKey(ruleDebt.characteristicKey());
-      newRule.setRemediationFunction(ruleDebt.function());
-      newRule.setRemediationFactor(ruleDebt.factor());
-      newRule.setRemediationOffset(ruleDebt.offset());
-    }
-  }
-
-  @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<DebtRulesXMLImporter.RuleDebt> loadRuleDebtList() {
-    List<DebtRulesXMLImporter.RuleDebt> ruleDebtList = newArrayList();
-    for (String pluginKey : getContributingPluginListWithoutSqale()) {
-      ruleDebtList.addAll(loadRuleDebtsFromXml(pluginKey));
-    }
-    return ruleDebtList;
-  }
-
-  public List<DebtRulesXMLImporter.RuleDebt> loadRuleDebtsFromXml(String pluginKey) {
-    Reader xmlFileReader = null;
-    try {
-      xmlFileReader = languageModelFinder.createReaderForXMLFile(pluginKey);
-      return importer.importXML(xmlFileReader);
-    } finally {
-      IOUtils.closeQuietly(xmlFileReader);
-    }
-  }
-
-  private Collection<String> getContributingPluginListWithoutSqale() {
-    Collection<String> pluginList = newArrayList(languageModelFinder.getContributingPluginList());
-    pluginList.remove(TechnicalDebtModelRepository.DEFAULT_MODEL);
-    return pluginList;
-  }
-
-  @CheckForNull
-  private DebtRulesXMLImporter.RuleDebt findRequirement(List<DebtRulesXMLImporter.RuleDebt> requirements, final String repoKey, final String ruleKey) {
-    return Iterables.find(requirements, new Predicate<DebtRulesXMLImporter.RuleDebt>() {
-      @Override
-      public boolean apply(DebtRulesXMLImporter.RuleDebt input) {
-        return input.ruleKey().equals(RuleKey.of(repoKey, ruleKey));
-      }
-    }, null);
-  }
-
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinition.java b/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinition.java
new file mode 100644 (file)
index 0000000..de9574e
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 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.rule.RuleParamType;
+import org.sonar.api.server.rule.RulesDefinition;
+import org.sonar.check.Cardinality;
+import org.sonar.core.i18n.RuleI18nManager;
+import org.sonar.core.technicaldebt.DebtRulesXMLImporter;
+import org.sonar.core.technicaldebt.TechnicalDebtModelRepository;
+
+import javax.annotation.CheckForNull;
+
+import java.io.Reader;
+import java.util.Collection;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * Inject deprecated RuleRepository into RuleDefinitions for backward-compatibility.
+ *
+ * @since 4.2
+ */
+public class DeprecatedRulesDefinition implements RulesDefinition {
+
+  private final RuleI18nManager i18n;
+  private final RuleRepository[] repositories;
+
+  private final TechnicalDebtModelRepository languageModelFinder;
+  private final DebtRulesXMLImporter importer;
+
+  public DeprecatedRulesDefinition(RuleI18nManager i18n, RuleRepository[] repositories, TechnicalDebtModelRepository languageModelFinder, DebtRulesXMLImporter importer) {
+    this.i18n = i18n;
+    this.repositories = repositories;
+    this.languageModelFinder = languageModelFinder;
+    this.importer = importer;
+  }
+
+  public DeprecatedRulesDefinition(RuleI18nManager i18n, TechnicalDebtModelRepository languageModelFinder, DebtRulesXMLImporter importer) {
+    this(i18n, new RuleRepository[0], languageModelFinder, importer);
+  }
+
+  @Override
+  public void define(Context context) {
+    // Load rule debt definitions from xml files provided by plugin
+    List<DebtRulesXMLImporter.RuleDebt> ruleDebts = loadRuleDebtList();
+
+    for (RuleRepository repository : repositories) {
+      // RuleRepository API does not handle difference between new and extended repositories,
+      NewRepository newRepository;
+      if (context.repository(repository.getKey()) == null) {
+        newRepository = context.createRepository(repository.getKey(), repository.getLanguage());
+        newRepository.setName(repository.getName());
+      } else {
+        newRepository = (NewRepository) context.extendRepository(repository.getKey(), repository.getLanguage());
+      }
+      for (org.sonar.api.rules.Rule rule : repository.createRules()) {
+        NewRule newRule = newRepository.createRule(rule.getKey());
+        newRule.setName(ruleName(repository.getKey(), rule));
+        newRule.setHtmlDescription(ruleDescription(repository.getKey(), rule));
+        newRule.setInternalKey(rule.getConfigKey());
+        newRule.setTemplate(Cardinality.MULTIPLE.equals(rule.getCardinality()));
+        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()) {
+          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 void updateRuleDebtDefinitions(NewRule newRule, String repoKey, String ruleKey, List<DebtRulesXMLImporter.RuleDebt> ruleDebts){
+    DebtRulesXMLImporter.RuleDebt ruleDebt = findRequirement(ruleDebts, repoKey, ruleKey);
+    if (ruleDebt != null) {
+      newRule.setCharacteristicKey(ruleDebt.characteristicKey());
+      newRule.setRemediationFunction(ruleDebt.function());
+      newRule.setRemediationFactor(ruleDebt.factor());
+      newRule.setRemediationOffset(ruleDebt.offset());
+    }
+  }
+
+  @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<DebtRulesXMLImporter.RuleDebt> loadRuleDebtList() {
+    List<DebtRulesXMLImporter.RuleDebt> ruleDebtList = newArrayList();
+    for (String pluginKey : getContributingPluginListWithoutSqale()) {
+      ruleDebtList.addAll(loadRuleDebtsFromXml(pluginKey));
+    }
+    return ruleDebtList;
+  }
+
+  public List<DebtRulesXMLImporter.RuleDebt> loadRuleDebtsFromXml(String pluginKey) {
+    Reader xmlFileReader = null;
+    try {
+      xmlFileReader = languageModelFinder.createReaderForXMLFile(pluginKey);
+      return importer.importXML(xmlFileReader);
+    } finally {
+      IOUtils.closeQuietly(xmlFileReader);
+    }
+  }
+
+  private Collection<String> getContributingPluginListWithoutSqale() {
+    Collection<String> pluginList = newArrayList(languageModelFinder.getContributingPluginList());
+    pluginList.remove(TechnicalDebtModelRepository.DEFAULT_MODEL);
+    return pluginList;
+  }
+
+  @CheckForNull
+  private DebtRulesXMLImporter.RuleDebt findRequirement(List<DebtRulesXMLImporter.RuleDebt> requirements, final String repoKey, final String ruleKey) {
+    return Iterables.find(requirements, new Predicate<DebtRulesXMLImporter.RuleDebt>() {
+      @Override
+      public boolean apply(DebtRulesXMLImporter.RuleDebt input) {
+        return input.ruleKey().equals(RuleKey.of(repoKey, ruleKey));
+      }
+    }, null);
+  }
+
+}
index 427579dde02c540dd0e7989aa2a0af5339c3caed..399286db5bc86f04af2722f16b0fb337645b297c 100644 (file)
 package org.sonar.server.rule;
 
 import org.sonar.api.ServerComponent;
-import org.sonar.api.server.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RulesDefinition;
 
 /**
  * Loads all instances of RuleDefinitions and initializes RuleRepositories. Used at server startup.
  */
 public class RuleDefinitionsLoader implements ServerComponent {
-  private final RuleDefinitions[] definitions;
+  private final RulesDefinition[] definitions;
   private final RuleRepositories repositories;
 
-  public RuleDefinitionsLoader(RuleRepositories repositories, RuleDefinitions[] definitions) {
+  public RuleDefinitionsLoader(RuleRepositories repositories, RulesDefinition[] definitions) {
     this.repositories = repositories;
     this.definitions = definitions;
   }
 
   public RuleDefinitionsLoader(RuleRepositories repositories) {
-    this(repositories, new RuleDefinitions[0]);
+    this(repositories, new RulesDefinition[0]);
   }
 
-  public RuleDefinitions.Context load() {
-    RuleDefinitions.Context context = new RuleDefinitions.Context();
-    for (RuleDefinitions definition : definitions) {
+  public RulesDefinition.Context load() {
+    RulesDefinition.Context context = new RulesDefinition.Context();
+    for (RulesDefinition definition : definitions) {
       definition.define(context);
     }
     repositories.register(context);
index f3e699dba4e27a46fa0d823fa035b204b1d476ab..c205f526e4eef137749ab235a9f47aa6fbf8231e 100644 (file)
@@ -31,7 +31,7 @@ import org.slf4j.LoggerFactory;
 import org.sonar.api.rule.RemediationFunction;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.Rule;
-import org.sonar.api.server.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RulesDefinition;
 import org.sonar.api.utils.MessageException;
 import org.sonar.api.utils.System2;
 import org.sonar.api.utils.TimeProfiler;
@@ -95,7 +95,7 @@ public class RuleRegistration implements Startable {
     TimeProfiler profiler = new TimeProfiler().start("Register rules");
     SqlSession sqlSession = myBatis.openSession();
     try {
-      RuleDefinitions.Context context = defLoader.load();
+      RulesDefinition.Context context = defLoader.load();
       Buffer buffer = new Buffer(system.now());
       List<CharacteristicDto> characteristicDtos = characteristicDao.selectEnabledCharacteristics();
       selectRulesFromDb(buffer, sqlSession);
@@ -133,11 +133,11 @@ public class RuleRegistration implements Startable {
     }
   }
 
-  private void enableRuleDefinitions(RuleDefinitions.Context context, Buffer buffer, List<CharacteristicDto> characteristicDtos, SqlSession sqlSession) {
-    for (RuleDefinitions.Repository repoDef : context.repositories()) {
+  private void enableRuleDefinitions(RulesDefinition.Context context, Buffer buffer, List<CharacteristicDto> characteristicDtos, SqlSession sqlSession) {
+    for (RulesDefinition.Repository repoDef : context.repositories()) {
       enableRepository(buffer, sqlSession, repoDef, characteristicDtos);
     }
-    for (RuleDefinitions.ExtendedRepository extendedRepoDef : context.extendedRepositories()) {
+    for (RulesDefinition.ExtendedRepository extendedRepoDef : context.extendedRepositories()) {
       if (context.repository(extendedRepoDef.key()) == null) {
         LOG.warn(String.format("Extension is ignored, repository %s does not exist", extendedRepoDef.key()));
       } else {
@@ -146,9 +146,9 @@ public class RuleRegistration implements Startable {
     }
   }
 
-  private void enableRepository(Buffer buffer, SqlSession sqlSession, RuleDefinitions.ExtendedRepository repoDef, List<CharacteristicDto> characteristicDtos) {
+  private void enableRepository(Buffer buffer, SqlSession sqlSession, RulesDefinition.ExtendedRepository repoDef, List<CharacteristicDto> characteristicDtos) {
     int count = 0;
-    for (RuleDefinitions.Rule ruleDef : repoDef.rules()) {
+    for (RulesDefinition.Rule ruleDef : repoDef.rules()) {
       RuleDto dto = buffer.rule(RuleKey.of(ruleDef.repository().key(), ruleDef.key()));
       if (dto == null) {
         dto = enableAndInsert(buffer, sqlSession, ruleDef, characteristicDtos);
@@ -164,7 +164,7 @@ public class RuleRegistration implements Startable {
     sqlSession.commit();
   }
 
-  private RuleDto enableAndInsert(Buffer buffer, SqlSession sqlSession, RuleDefinitions.Rule ruleDef, List<CharacteristicDto> characteristicDtos) {
+  private RuleDto enableAndInsert(Buffer buffer, SqlSession sqlSession, RulesDefinition.Rule ruleDef, List<CharacteristicDto> characteristicDtos) {
     RemediationFunction remediationFunction = ruleDef.remediationFunction();
 
     RuleDto ruleDto = new RuleDto()
@@ -192,7 +192,7 @@ public class RuleRegistration implements Startable {
     ruleDao.insert(ruleDto, sqlSession);
     buffer.add(ruleDto);
 
-    for (RuleDefinitions.Param param : ruleDef.params()) {
+    for (RulesDefinition.Param param : ruleDef.params()) {
       RuleParamDto paramDto = new RuleParamDto()
         .setRuleId(ruleDto.getId())
         .setDefaultValue(param.defaultValue())
@@ -206,7 +206,7 @@ public class RuleRegistration implements Startable {
     return ruleDto;
   }
 
-  private void enableAndUpdate(Buffer buffer, SqlSession sqlSession, RuleDefinitions.Rule ruleDef, RuleDto dto, List<CharacteristicDto> characteristicDtos) {
+  private void enableAndUpdate(Buffer buffer, SqlSession sqlSession, RulesDefinition.Rule ruleDef, RuleDto dto, List<CharacteristicDto> characteristicDtos) {
     if (mergeRule(buffer, ruleDef, dto, characteristicDtos)) {
       ruleDao.update(dto);
     }
@@ -215,7 +215,7 @@ public class RuleRegistration implements Startable {
     buffer.markProcessed(dto);
   }
 
-  private boolean mergeRule(Buffer buffer, RuleDefinitions.Rule def, RuleDto dto, List<CharacteristicDto> characteristicDtos) {
+  private boolean mergeRule(Buffer buffer, RulesDefinition.Rule def, RuleDto dto, List<CharacteristicDto> characteristicDtos) {
     boolean changed = false;
     if (!StringUtils.equals(dto.getName(), def.name())) {
       dto.setName(def.name());
@@ -255,7 +255,7 @@ public class RuleRegistration implements Startable {
     return changed;
   }
 
-  private boolean mergeDebtDefinitions(RuleDefinitions.Rule def, RuleDto dto, List<CharacteristicDto> characteristicDtos) {
+  private boolean mergeDebtDefinitions(RulesDefinition.Rule def, RuleDto dto, List<CharacteristicDto> characteristicDtos) {
     boolean changed = false;
 
     CharacteristicDto characteristic = findCharacteristic(characteristicDtos, def);
@@ -290,11 +290,11 @@ public class RuleRegistration implements Startable {
     return changed;
   }
 
-  private void mergeParams(Buffer buffer, SqlSession sqlSession, RuleDefinitions.Rule ruleDef, RuleDto dto) {
+  private void mergeParams(Buffer buffer, SqlSession sqlSession, RulesDefinition.Rule ruleDef, RuleDto dto) {
     Collection<RuleParamDto> paramDtos = buffer.paramsForRuleId(dto.getId());
     Set<String> persistedParamKeys = Sets.newHashSet();
     for (RuleParamDto paramDto : paramDtos) {
-      RuleDefinitions.Param paramDef = ruleDef.param(paramDto.getName());
+      RulesDefinition.Param paramDef = ruleDef.param(paramDto.getName());
       if (paramDef == null) {
         activeRuleDao.deleteParametersWithParamId(paramDto.getId(), sqlSession);
         ruleDao.deleteParam(paramDto, sqlSession);
@@ -307,7 +307,7 @@ public class RuleRegistration implements Startable {
         persistedParamKeys.add(paramDto.getName());
       }
     }
-    for (RuleDefinitions.Param param : ruleDef.params()) {
+    for (RulesDefinition.Param param : ruleDef.params()) {
       if (!persistedParamKeys.contains(param.key())) {
         RuleParamDto paramDto = new RuleParamDto()
           .setRuleId(dto.getId())
@@ -321,7 +321,7 @@ public class RuleRegistration implements Startable {
     }
   }
 
-  private boolean mergeParam(RuleParamDto paramDto, RuleDefinitions.Param paramDef) {
+  private boolean mergeParam(RuleParamDto paramDto, RulesDefinition.Param paramDef) {
     boolean changed = false;
     if (!StringUtils.equals(paramDto.getType(), paramDef.type().toString())) {
       paramDto.setType(paramDef.type().toString());
@@ -338,7 +338,7 @@ public class RuleRegistration implements Startable {
     return changed;
   }
 
-  private void mergeTags(Buffer buffer, SqlSession sqlSession, RuleDefinitions.Rule ruleDef, RuleDto dto) {
+  private void mergeTags(Buffer buffer, SqlSession sqlSession, RulesDefinition.Rule ruleDef, RuleDto dto) {
     Set<String> existingSystemTags = Sets.newHashSet();
 
     Collection<RuleRuleTagDto> tagDtos = ImmutableList.copyOf(buffer.tagsForRuleId(dto.getId()));
@@ -447,10 +447,10 @@ public class RuleRegistration implements Startable {
    * The side effect of this approach is that extended repositories will not be managed the same way.
    * If an extended repository do not exists anymore, then related active rules will be removed.
    */
-  private void removeActiveRulesOnStillExistingRepositories(List<RuleDto> removedRules, RuleDefinitions.Context context) {
-    List<String> repositoryKeys = newArrayList(Iterables.transform(context.repositories(), new Function<RuleDefinitions.Repository, String>() {
+  private void removeActiveRulesOnStillExistingRepositories(List<RuleDto> removedRules, RulesDefinition.Context context) {
+    List<String> repositoryKeys = newArrayList(Iterables.transform(context.repositories(), new Function<RulesDefinition.Repository, String>() {
       @Override
-      public String apply(RuleDefinitions.Repository input) {
+      public String apply(RulesDefinition.Repository input) {
         return input.key();
       }
     }
@@ -538,7 +538,7 @@ public class RuleRegistration implements Startable {
   }
 
   @CheckForNull
-  private CharacteristicDto findCharacteristic(List<CharacteristicDto> characteristicDtos, RuleDefinitions.Rule ruleDef) {
+  private CharacteristicDto findCharacteristic(List<CharacteristicDto> characteristicDtos, RulesDefinition.Rule ruleDef) {
     final String key = ruleDef.characteristicKey();
     if (key == null) {
       // Rule is not linked to a characteristic, nothing to do
index 0bb968d886f464dd3960105ef6a10ef301857635..91153922bab52aef53c0124531d85a5739f7f7d9 100644 (file)
@@ -24,9 +24,10 @@ import com.google.common.collect.ImmutableSetMultimap;
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.collect.SetMultimap;
 import org.sonar.api.ServerComponent;
-import org.sonar.api.server.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RulesDefinition;
 
 import javax.annotation.CheckForNull;
+
 import java.util.Collection;
 import java.util.Map;
 import java.util.SortedSet;
@@ -44,7 +45,7 @@ public class RuleRepositories implements ServerComponent {
   public static class Repository implements Comparable<Repository> {
     private final String key, name, language;
 
-    private Repository(RuleDefinitions.Repository repoDef) {
+    private Repository(RulesDefinition.Repository repoDef) {
       this.key = repoDef.key();
       this.name = repoDef.name();
       this.language = repoDef.language();
@@ -117,11 +118,11 @@ public class RuleRepositories implements ServerComponent {
   private Map<String, Repository> repositoriesByKey;
   private SetMultimap<String, Repository> repositoriesByLang;
 
-  void register(RuleDefinitions.Context context) {
+  void register(RulesDefinition.Context context) {
     ImmutableSortedSet.Builder<Repository> listBuilder = ImmutableSortedSet.naturalOrder();
     ImmutableSetMultimap.Builder<String, Repository> langBuilder = ImmutableSetMultimap.builder();
     ImmutableMap.Builder<String, Repository> keyBuilder = ImmutableMap.builder();
-    for (RuleDefinitions.Repository repoDef : context.repositories()) {
+    for (RulesDefinition.Repository repoDef : context.repositories()) {
       Repository repository = new Repository(repoDef);
       listBuilder.add(repository);
       langBuilder.put(repository.language(), repository);
diff --git a/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRuleDefinitionsTest.java b/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRuleDefinitionsTest.java
deleted file mode 100644 (file)
index d03dcef..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
-import org.sonar.api.rule.RemediationFunction;
-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.rule.RuleDefinitions;
-import org.sonar.core.i18n.RuleI18nManager;
-import org.sonar.core.technicaldebt.DebtRulesXMLImporter;
-import org.sonar.core.technicaldebt.TechnicalDebtModelRepository;
-
-import java.io.Reader;
-import java.util.Arrays;
-import java.util.List;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-@RunWith(MockitoJUnitRunner.class)
-public class DeprecatedRuleDefinitionsTest {
-
-  @Mock
-  RuleI18nManager i18n;
-
-  @Mock
-  TechnicalDebtModelRepository debtModelRepository;
-
-  @Mock
-  DebtRulesXMLImporter importer;
-
-  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", "security"});
-      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() throws Exception {
-    RuleDefinitions.Context context = new RuleDefinitions.Context();
-    new DeprecatedRuleDefinitions(i18n, new RuleRepository[]{new CheckstyleRules()}, debtModelRepository, importer).define(context);
-
-    assertThat(context.repositories()).hasSize(1);
-    RuleDefinitions.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);
-    RuleDefinitions.Rule rule = checkstyle.rule("ConstantName");
-    assertThat(rule).isNotNull();
-    assertThat(rule.key()).isEqualTo("ConstantName");
-    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", "security");
-    assertThat(rule.params()).hasSize(1);
-    RuleDefinitions.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() throws Exception {
-    RuleDefinitions.Context context = new RuleDefinitions.Context();
-
-    // no more RuleRepository !
-    new DeprecatedRuleDefinitions(i18n, debtModelRepository, importer);
-
-    assertThat(context.repositories()).isEmpty();
-  }
-
-  @Test
-  public void use_l10n_bundles() throws Exception {
-    RuleDefinitions.Context context = new RuleDefinitions.Context();
-    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 DeprecatedRuleDefinitions(i18n, new RuleRepository[]{new UseBundles()}, debtModelRepository, importer).define(context);
-
-    RuleDefinitions.Repository checkstyle = context.repository("checkstyle");
-    RuleDefinitions.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");
-    RuleDefinitions.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() throws Exception {
-    RuleDefinitions.Context context = new RuleDefinitions.Context();
-
-    List<DebtRulesXMLImporter.RuleDebt> ruleDebts = newArrayList(
-      new DebtRulesXMLImporter.RuleDebt()
-        .setCharacteristicKey("MEMORY_EFFICIENCY")
-        .setRuleKey(RuleKey.of("checkstyle", "ConstantName"))
-        .setFunction(RemediationFunction.LINEAR_OFFSET)
-        .setFactor("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))).thenReturn(ruleDebts);
-
-    new DeprecatedRuleDefinitions(i18n, new RuleRepository[]{new CheckstyleRules()}, debtModelRepository, importer).define(context);
-
-    assertThat(context.repositories()).hasSize(1);
-    RuleDefinitions.Repository checkstyle = context.repository("checkstyle");
-    assertThat(checkstyle.rules()).hasSize(1);
-
-    RuleDefinitions.Rule rule = checkstyle.rule("ConstantName");
-    assertThat(rule).isNotNull();
-    assertThat(rule.key()).isEqualTo("ConstantName");
-    assertThat(rule.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
-    assertThat(rule.remediationFunction()).isEqualTo(RemediationFunction.LINEAR_OFFSET);
-    assertThat(rule.remediationFactor()).isEqualTo("1d");
-    assertThat(rule.remediationOffset()).isEqualTo("10min");
-  }
-
-}
diff --git a/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionTest.java b/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionTest.java
new file mode 100644 (file)
index 0000000..6617c16
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.rule.RemediationFunction;
+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.rule.RulesDefinition;
+import org.sonar.core.i18n.RuleI18nManager;
+import org.sonar.core.technicaldebt.DebtRulesXMLImporter;
+import org.sonar.core.technicaldebt.TechnicalDebtModelRepository;
+
+import java.io.Reader;
+import java.util.Arrays;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DeprecatedRulesDefinitionTest {
+
+  @Mock
+  RuleI18nManager i18n;
+
+  @Mock
+  TechnicalDebtModelRepository debtModelRepository;
+
+  @Mock
+  DebtRulesXMLImporter importer;
+
+  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", "security"});
+      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() throws Exception {
+    RulesDefinition.Context context = new RulesDefinition.Context();
+    new DeprecatedRulesDefinition(i18n, new RuleRepository[]{new CheckstyleRules()}, debtModelRepository, importer).define(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.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", "security");
+    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() throws Exception {
+    RulesDefinition.Context context = new RulesDefinition.Context();
+
+    // no more RuleRepository !
+    new DeprecatedRulesDefinition(i18n, debtModelRepository, importer);
+
+    assertThat(context.repositories()).isEmpty();
+  }
+
+  @Test
+  public void use_l10n_bundles() throws Exception {
+    RulesDefinition.Context context = new RulesDefinition.Context();
+    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 DeprecatedRulesDefinition(i18n, new RuleRepository[]{new UseBundles()}, debtModelRepository, importer).define(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() throws Exception {
+    RulesDefinition.Context context = new RulesDefinition.Context();
+
+    List<DebtRulesXMLImporter.RuleDebt> ruleDebts = newArrayList(
+      new DebtRulesXMLImporter.RuleDebt()
+        .setCharacteristicKey("MEMORY_EFFICIENCY")
+        .setRuleKey(RuleKey.of("checkstyle", "ConstantName"))
+        .setFunction(RemediationFunction.LINEAR_OFFSET)
+        .setFactor("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))).thenReturn(ruleDebts);
+
+    new DeprecatedRulesDefinition(i18n, new RuleRepository[]{new CheckstyleRules()}, debtModelRepository, importer).define(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.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
+    assertThat(rule.remediationFunction()).isEqualTo(RemediationFunction.LINEAR_OFFSET);
+    assertThat(rule.remediationFactor()).isEqualTo("1d");
+    assertThat(rule.remediationOffset()).isEqualTo("10min");
+  }
+
+}
diff --git a/sonar-server/src/test/java/org/sonar/server/rule/RuleDefinitionsLoaderTest.java b/sonar-server/src/test/java/org/sonar/server/rule/RuleDefinitionsLoaderTest.java
deleted file mode 100644 (file)
index 037778a..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 org.junit.Test;
-import org.sonar.api.server.rule.RuleDefinitions;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class RuleDefinitionsLoaderTest {
-  @Test
-  public void no_definitions() {
-    RuleRepositories repositories = new RuleRepositories();
-
-    RuleDefinitions.Context context = new RuleDefinitionsLoader(repositories).load();
-
-    assertThat(context.repositories()).isEmpty();
-    assertThat(repositories.repositories()).isEmpty();
-  }
-
-  @Test
-  public void load_definitions() {
-    RuleRepositories repositories = new RuleRepositories();
-
-    RuleDefinitions.Context context = new RuleDefinitionsLoader(repositories, new RuleDefinitions[]{
-        new FindbugsDefinitions(), new SquidDefinitions()
-    }).load();
-
-    assertThat(context.repositories()).hasSize(2);
-    assertThat(context.repository("findbugs")).isNotNull();
-    assertThat(context.repository("squid")).isNotNull();
-    assertThat(repositories.repositories()).hasSize(2);
-    assertThat(repositories.repository("findbugs")).isNotNull();
-    assertThat(repositories.repository("squid")).isNotNull();
-  }
-
-  static class FindbugsDefinitions implements RuleDefinitions {
-    @Override
-    public void define(Context context) {
-      NewRepository repo = context.createRepository("findbugs", "java");
-      repo.setName("Findbugs");
-      repo.createRule("ABC")
-          .setName("ABC")
-          .setHtmlDescription("Description of ABC");
-      repo.done();
-    }
-  }
-
-  static class SquidDefinitions implements RuleDefinitions {
-    @Override
-    public void define(Context context) {
-      NewRepository repo = context.createRepository("squid", "java");
-      repo.setName("Squid");
-      repo.createRule("DEF")
-          .setName("DEF")
-          .setHtmlDescription("Description of DEF");
-      repo.done();
-    }
-  }
-}
index 5c07b56addd1f4d78d23c44ce9470af9cdd03422..417711e4cbfbb86887873536fd1820a3de45be67 100644 (file)
@@ -25,7 +25,7 @@ import org.junit.Test;
 import org.sonar.api.rule.RemediationFunction;
 import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.rule.Severity;
-import org.sonar.api.server.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RulesDefinition;
 import org.sonar.api.utils.MessageException;
 import org.sonar.core.persistence.AbstractDaoTestCase;
 import org.sonar.core.persistence.MyBatis;
@@ -67,7 +67,7 @@ public class RuleRegistrationTest extends AbstractDaoTestCase {
     activeRuleDao = new ActiveRuleDao(myBatis);
     ruleTagOperations = new RuleTagOperations(ruleTagDao, esRuleTags);
     characteristicDao = new CharacteristicDao(myBatis);
-    task = new RuleRegistration(new RuleDefinitionsLoader(mock(RuleRepositories.class), new RuleDefinitions[]{new FakeRepository()}),
+    task = new RuleRegistration(new RuleDefinitionsLoader(mock(RuleRepositories.class), new RulesDefinition[]{new FakeRepository()}),
       profilesManager, ruleRegistry, esRuleTags, ruleTagOperations, myBatis, ruleDao, ruleTagDao, activeRuleDao, characteristicDao, mock(RegisterDebtModel.class));
   }
 
@@ -214,7 +214,7 @@ public class RuleRegistrationTest extends AbstractDaoTestCase {
 
   @Test
   public void test_high_number_of_rules() {
-    task = new RuleRegistration(new RuleDefinitionsLoader(mock(RuleRepositories.class), new RuleDefinitions[]{new BigRepository()}),
+    task = new RuleRegistration(new RuleDefinitionsLoader(mock(RuleRepositories.class), new RulesDefinition[]{new BigRepository()}),
       profilesManager, ruleRegistry, esRuleTags, ruleTagOperations, myBatis, ruleDao, ruleTagDao, activeRuleDao, characteristicDao, mock(RegisterDebtModel.class));
 
     setupData("shared");
@@ -228,7 +228,7 @@ public class RuleRegistrationTest extends AbstractDaoTestCase {
 
   @Test
   public void insert_extended_repositories() {
-    task = new RuleRegistration(new RuleDefinitionsLoader(mock(RuleRepositories.class), new RuleDefinitions[]{
+    task = new RuleRegistration(new RuleDefinitionsLoader(mock(RuleRepositories.class), new RulesDefinition[]{
       new FindbugsRepository(), new FbContribRepository()}),
       profilesManager, ruleRegistry, esRuleTags, ruleTagOperations, myBatis, ruleDao, ruleTagDao, activeRuleDao, characteristicDao, mock(RegisterDebtModel.class));
 
@@ -238,7 +238,7 @@ public class RuleRegistrationTest extends AbstractDaoTestCase {
     checkTables("insert_extended_repositories", EXCLUDED_COLUMN_NAMES_INCLUDING_DEBT, "rules");
   }
 
-  static class FakeRepository implements RuleDefinitions {
+  static class FakeRepository implements RulesDefinition {
     @Override
     public void define(Context context) {
       NewRepository repo = context.createRepository("fake", "java");
@@ -266,7 +266,7 @@ public class RuleRegistrationTest extends AbstractDaoTestCase {
     }
   }
 
-  static class BigRepository implements RuleDefinitions {
+  static class BigRepository implements RulesDefinition {
     static final int SIZE = 500;
 
     @Override
@@ -288,7 +288,7 @@ public class RuleRegistrationTest extends AbstractDaoTestCase {
     }
   }
 
-  static class FindbugsRepository implements RuleDefinitions {
+  static class FindbugsRepository implements RulesDefinition {
     @Override
     public void define(Context context) {
       NewRepository repo = context.createRepository("findbugs", "java");
@@ -299,7 +299,7 @@ public class RuleRegistrationTest extends AbstractDaoTestCase {
     }
   }
 
-  static class FbContribRepository implements RuleDefinitions {
+  static class FbContribRepository implements RulesDefinition {
     @Override
     public void define(Context context) {
       NewExtendedRepository repo = context.extendRepository("findbugs", "java");
index 8e488f72974b87399ae7d369d60df5eb1c6f1e38..03d3d093c38d85d7777e61a82dbb9f9f80c8cb7c 100644 (file)
 package org.sonar.server.rule;
 
 import org.junit.Test;
-import org.sonar.api.server.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RulesDefinition;
 
 import static org.fest.assertions.Assertions.assertThat;
 
 public class RuleRepositoriesTest {
   @Test
   public void should_register_repositories() {
-    RuleDefinitions.Context context = new RuleDefinitions.Context();
+    RulesDefinition.Context context = new RulesDefinition.Context();
     new SquidDefinitions().define(context);
     new FindbugsDefinitions().define(context);
 
@@ -60,7 +60,7 @@ public class RuleRepositoriesTest {
     assertThat(findbugs).isEqualTo(findbugs).isNotEqualTo(squid).isNotEqualTo("findbugs").isNotEqualTo(null);
   }
 
-  static class FindbugsDefinitions implements RuleDefinitions {
+  static class FindbugsDefinitions implements RulesDefinition {
     @Override
     public void define(Context context) {
       NewRepository repo = context.createRepository("findbugs", "java");
@@ -72,7 +72,7 @@ public class RuleRepositoriesTest {
     }
   }
 
-  static class SquidDefinitions implements RuleDefinitions {
+  static class SquidDefinitions implements RulesDefinition {
     @Override
     public void define(Context context) {
       NewRepository repo = context.createRepository("squid", "java");
diff --git a/sonar-server/src/test/java/org/sonar/server/rule/RulesDefinitionLoaderTest.java b/sonar-server/src/test/java/org/sonar/server/rule/RulesDefinitionLoaderTest.java
new file mode 100644 (file)
index 0000000..53781d2
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 org.junit.Test;
+import org.sonar.api.server.rule.RulesDefinition;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class RulesDefinitionLoaderTest {
+  @Test
+  public void no_definitions() {
+    RuleRepositories repositories = new RuleRepositories();
+
+    RulesDefinition.Context context = new RuleDefinitionsLoader(repositories).load();
+
+    assertThat(context.repositories()).isEmpty();
+    assertThat(repositories.repositories()).isEmpty();
+  }
+
+  @Test
+  public void load_definitions() {
+    RuleRepositories repositories = new RuleRepositories();
+
+    RulesDefinition.Context context = new RuleDefinitionsLoader(repositories, new RulesDefinition[]{
+        new FindbugsDefinitions(), new SquidDefinitions()
+    }).load();
+
+    assertThat(context.repositories()).hasSize(2);
+    assertThat(context.repository("findbugs")).isNotNull();
+    assertThat(context.repository("squid")).isNotNull();
+    assertThat(repositories.repositories()).hasSize(2);
+    assertThat(repositories.repository("findbugs")).isNotNull();
+    assertThat(repositories.repository("squid")).isNotNull();
+  }
+
+  static class FindbugsDefinitions implements RulesDefinition {
+    @Override
+    public void define(Context context) {
+      NewRepository repo = context.createRepository("findbugs", "java");
+      repo.setName("Findbugs");
+      repo.createRule("ABC")
+          .setName("ABC")
+          .setHtmlDescription("Description of ABC");
+      repo.done();
+    }
+  }
+
+  static class SquidDefinitions implements RulesDefinition {
+    @Override
+    public void define(Context context) {
+      NewRepository repo = context.createRepository("squid", "java");
+      repo.setName("Squid");
+      repo.createRule("DEF")
+          .setName("DEF")
+          .setHtmlDescription("Description of DEF");
+      repo.done();
+    }
+  }
+}