]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4908 add template rules and param types
authorSimon Brandhof <simon.brandhof@gmail.com>
Mon, 13 Jan 2014 16:22:14 +0000 (17:22 +0100)
committerSimon Brandhof <simon.brandhof@gmail.com>
Mon, 13 Jan 2014 16:22:43 +0000 (17:22 +0100)
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitions.java
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleParamType.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleDefinitionsTest.java
sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleParamTypeTest.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 [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRuleDefinitionsTest.java [new file with mode: 0644]

index b83339481339262b4832a2c67ad07e93c6eff0d4..939377f2d4037caecd33a173db5e1521c42e2c42 100644 (file)
@@ -168,6 +168,7 @@ public interface RuleDefinitions extends ServerExtension {
       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(newRule));
       }
       this.rulesByKey = ruleBuilder.build();
@@ -221,22 +222,25 @@ public interface RuleDefinitions extends ServerExtension {
   static class NewRule {
     private final String repoKey, key;
     private String name, htmlDescription, metadata, defaultSeverity = Severity.MAJOR;
+    private boolean template;
     private final Set<String> tags = Sets.newHashSet();
     private final Map<String, NewParam> paramsByKey = Maps.newHashMap();
 
     private NewRule(String repoKey, String key) {
       this.repoKey = repoKey;
-      this.key = this.name = key;
+      this.key = key;
     }
 
     public NewRule setName(String s) {
-      if (StringUtils.isBlank(s)) {
-        throw new IllegalArgumentException(String.format("Name of rule %s is blank", this));
-      }
       this.name = s;
       return this;
     }
 
+    public NewRule setTemplate(boolean template) {
+      this.template = template;
+      return this;
+    }
+
     public NewRule setDefaultSeverity(String s) {
       if (!Severity.ALL.contains(s)) {
         throw new IllegalArgumentException(String.format("Default severity of rule %s is not correct: %s", this, s));
@@ -246,9 +250,6 @@ public interface RuleDefinitions extends ServerExtension {
     }
 
     public NewRule setHtmlDescription(String s) {
-      if (StringUtils.isBlank(s)) {
-        throw new IllegalArgumentException(String.format("HTML description of rule %s is blank", this));
-      }
       this.htmlDescription = s;
       return this;
     }
@@ -292,6 +293,15 @@ public interface RuleDefinitions extends ServerExtension {
       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);
@@ -300,6 +310,7 @@ public interface RuleDefinitions extends ServerExtension {
 
   static class Rule {
     private final String repoKey, key, name, htmlDescription, metadata, defaultSeverity;
+    private final boolean template;
     private final Set<String> tags;
     private final Map<String, Param> params;
 
@@ -310,6 +321,7 @@ public interface RuleDefinitions extends ServerExtension {
       this.htmlDescription = newRule.htmlDescription;
       this.metadata = newRule.metadata;
       this.defaultSeverity = newRule.defaultSeverity;
+      this.template = newRule.template;
       this.tags = ImmutableSet.copyOf(newRule.tags);
       ImmutableMap.Builder<String, Param> paramsBuilder = ImmutableMap.builder();
       for (NewParam newParam : newRule.paramsByKey.values()) {
@@ -335,6 +347,10 @@ public interface RuleDefinitions extends ServerExtension {
       return htmlDescription;
     }
 
+    public boolean template() {
+      return template;
+    }
+
     @CheckForNull
     public Param param(String key) {
       return params.get(key);
@@ -385,7 +401,7 @@ public interface RuleDefinitions extends ServerExtension {
   static class NewParam {
     private final String key;
     private String name, description, defaultValue;
-    // TODO type
+    private RuleParamType type = RuleParamType.STRING;
 
     private NewParam(String key) {
       this.key = this.name = key;
@@ -397,6 +413,11 @@ public interface RuleDefinitions extends ServerExtension {
       return this;
     }
 
+    public NewParam setType(RuleParamType t) {
+      this.type = t;
+      return this;
+    }
+
     /**
      * Plain-text description. Can be null.
      */
@@ -413,12 +434,14 @@ public interface RuleDefinitions extends ServerExtension {
 
   static 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() {
@@ -439,6 +462,10 @@ public interface RuleDefinitions extends ServerExtension {
       return defaultValue;
     }
 
+    public RuleParamType type() {
+      return type;
+    }
+
     @Override
     public boolean equals(Object o) {
       if (this == o) {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleParamType.java b/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleParamType.java
new file mode 100644 (file)
index 0000000..5b9599d
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.rule;
+
+/**
+ * @since 4.2
+ */
+public final class RuleParamType {
+
+  public static final RuleParamType STRING = new RuleParamType("STRING");
+  public static final RuleParamType TEXT = new RuleParamType("TEXT");
+  public static final RuleParamType BOOLEAN = new RuleParamType("BOOLEAN");
+  public static final RuleParamType INTEGER = new RuleParamType("INTEGER");
+  public static final RuleParamType REGULAR_EXPRESSION = new RuleParamType("REGULAR_EXPRESSION");
+
+  private final String key;
+
+  private RuleParamType(String key) {
+    this.key = key;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    RuleParamType that = (RuleParamType) o;
+    return key.equals(that.key);
+  }
+
+  @Override
+  public int hashCode() {
+    return key.hashCode();
+  }
+
+  @Override
+  public String toString() {
+    return key;
+  }
+}
index 4f391609c513e580713a43c9882d76c448e9825b..87b891d04ac14ee3840a9c0c94ef48616caa3f71 100644 (file)
@@ -62,12 +62,13 @@ public class RuleDefinitionsTest {
     RuleDefinitions.NewRepository newFindbugs = context.newRepository("findbugs", "java");
     newFindbugs.newRule("NPE")
       .setName("Detect NPE")
+      .setHtmlDescription("Detect <code>NPE</code>")
       .setHtmlDescription("Detect <code>java.lang.NullPointerException</code>")
       .setDefaultSeverity(Severity.BLOCKER)
       .setMetadata("/something")
       .setTags("one", "two")
       .addTags("two", "three", "four");
-    newFindbugs.newRule("ABC");
+    newFindbugs.newRule("ABC").setName("ABC").setHtmlDescription("ABC");
     newFindbugs.done();
 
     RuleDefinitions.Repository findbugs = context.repository("findbugs");
@@ -81,6 +82,7 @@ public class RuleDefinitionsTest {
     assertThat(npeRule.tags()).containsOnly("one", "two", "three", "four");
     assertThat(npeRule.params()).isEmpty();
     assertThat(npeRule.metadata()).isEqualTo("/something");
+    assertThat(npeRule.template()).isFalse();
     assertThat(npeRule.toString()).isEqualTo("[repository=findbugs, key=NPE]");
 
     // test equals() and hashCode()
@@ -92,14 +94,12 @@ public class RuleDefinitionsTest {
   @Test
   public void define_rule_with_default_fields() {
     RuleDefinitions.NewRepository newFindbugs = context.newRepository("findbugs", "java");
-    newFindbugs.newRule("NPE");
+    newFindbugs.newRule("NPE").setName("NPE").setHtmlDescription("NPE");
     newFindbugs.done();
 
     RuleDefinitions.Rule rule = context.repository("findbugs").rule("NPE");
     assertThat(rule.key()).isEqualTo("NPE");
-    assertThat(rule.name()).isEqualTo("NPE");
     assertThat(rule.defaultSeverity()).isEqualTo(Severity.MAJOR);
-    assertThat(rule.htmlDescription()).isNull();
     assertThat(rule.params()).isEmpty();
     assertThat(rule.metadata()).isNull();
     assertThat(rule.tags()).isEmpty();
@@ -108,8 +108,8 @@ public class RuleDefinitionsTest {
   @Test
   public void define_rule_parameters() {
     RuleDefinitions.NewRepository newFindbugs = context.newRepository("findbugs", "java");
-    RuleDefinitions.NewRule newNpe = newFindbugs.newRule("NPE");
-    newNpe.newParam("level").setDefaultValue("LOW").setName("Level").setDescription("The level");
+    RuleDefinitions.NewRule newNpe = newFindbugs.newRule("NPE").setName("NPE").setHtmlDescription("NPE");
+    newNpe.newParam("level").setDefaultValue("LOW").setName("Level").setDescription("The level").setType(RuleParamType.INTEGER);
     newNpe.newParam("effort");
     newFindbugs.done();
 
@@ -121,11 +121,13 @@ public class RuleDefinitionsTest {
     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);
@@ -138,7 +140,7 @@ public class RuleDefinitionsTest {
 
     // for example fb-contrib
     RuleDefinitions.NewExtendedRepository newFindbugs = context.extendRepository("findbugs");
-    newFindbugs.newRule("NPE");
+    newFindbugs.newRule("NPE").setName("NPE").setHtmlDescription("NPE");
     newFindbugs.done();
 
     assertThat(context.repositories()).isEmpty();
@@ -187,11 +189,13 @@ public class RuleDefinitionsTest {
 
   @Test
   public void fail_if_blank_rule_name() {
+    RuleDefinitions.NewRepository newRepository = context.newRepository("findbugs", "java");
+    newRepository.newRule("NPE").setName(null).setHtmlDescription("NPE");
     try {
-      context.newRepository("findbugs", "java").newRule("NPE").setName(null);
+      newRepository.done();
       fail();
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessage("Name of rule [repository=findbugs, key=NPE] is blank");
+    } catch (IllegalStateException e) {
+      assertThat(e).hasMessage("Name of rule [repository=findbugs, key=NPE] is empty");
     }
   }
 
@@ -208,11 +212,13 @@ public class RuleDefinitionsTest {
 
   @Test
   public void fail_if_blank_rule_html_description() {
+    RuleDefinitions.NewRepository newRepository = context.newRepository("findbugs", "java");
+    newRepository.newRule("NPE").setName("NPE").setHtmlDescription(null);
     try {
-      context.newRepository("findbugs", "java").newRule("NPE").setHtmlDescription(null);
+      newRepository.done();
       fail();
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessage("HTML description of rule [repository=findbugs, key=NPE] is blank");
+    } catch (IllegalStateException e) {
+      assertThat(e).hasMessage("HTML description of rule [repository=findbugs, key=NPE] is empty");
     }
   }
 
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleParamTypeTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleParamTypeTest.java
new file mode 100644 (file)
index 0000000..722c672
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.rule;
+
+import org.junit.Test;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class RuleParamTypeTest {
+  @Test
+  public void testEquals() throws Exception {
+    assertThat(RuleParamType.INTEGER)
+      .isEqualTo(RuleParamType.INTEGER)
+      .isNotEqualTo(RuleParamType.STRING)
+      .isNotEqualTo("INTEGER")
+      .isNotEqualTo(null);
+  }
+
+  @Test
+  public void testHashCode() throws Exception {
+    assertThat(RuleParamType.INTEGER.hashCode()).isEqualTo(RuleParamType.INTEGER.hashCode());
+  }
+
+  @Test
+  public void testToString() throws Exception {
+    assertThat(RuleParamType.INTEGER.toString()).isEqualTo("INTEGER");
+  }
+}
index fb1028555551c565a1d74031c3e36fe582fdc8f7..7dcb10367ab195b9190f26670d367b6ca25f18f5 100644 (file)
@@ -91,6 +91,7 @@ import org.sonar.server.permission.InternalPermissionTemplateService;
 import org.sonar.server.permission.PermissionFinder;
 import org.sonar.server.plugins.*;
 import org.sonar.server.qualityprofile.*;
+import org.sonar.server.rule.DeprecatedRuleDefinitions;
 import org.sonar.server.rule.ProfileRules;
 import org.sonar.server.rule.RubyRuleService;
 import org.sonar.server.rule.RuleRegistry;
@@ -368,6 +369,7 @@ public final class Platform {
     ComponentContainer startupContainer = servicesContainer.createChild();
     startupContainer.addSingleton(GwtPublisher.class);
     startupContainer.addSingleton(RegisterMetrics.class);
+    startupContainer.addSingleton(DeprecatedRuleDefinitions.class);
     startupContainer.addSingleton(RegisterRules.class);
     startupContainer.addSingleton(RegisterNewProfiles.class);
     startupContainer.addSingleton(JdbcDriverDeployer.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
new file mode 100644 (file)
index 0000000..cc61dfa
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.apache.commons.lang.StringUtils;
+import org.sonar.api.rule.RuleDefinitions;
+import org.sonar.api.rules.RuleParam;
+import org.sonar.api.rules.RuleRepository;
+import org.sonar.check.Cardinality;
+import org.sonar.core.i18n.RuleI18nManager;
+
+import javax.annotation.CheckForNull;
+
+/**
+ * Inject deprecated RuleRepository into RuleDefinitions for backward-compatibility.
+ * @since 4.2
+ */
+public class DeprecatedRuleDefinitions implements RuleDefinitions {
+  private final RuleI18nManager i18n;
+  private final RuleRepository[] repositories;
+
+  public DeprecatedRuleDefinitions(RuleI18nManager i18n, RuleRepository[] repositories) {
+    this.i18n = i18n;
+    this.repositories = repositories;
+  }
+
+  public DeprecatedRuleDefinitions(RuleI18nManager i18n) {
+    this(i18n, new RuleRepository[0]);
+  }
+
+  @Override
+  public void define(Context context) {
+    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.newRepository(repository.getKey(), repository.getLanguage());
+        newRepository.setName(repository.getName());
+      } else {
+        newRepository = (NewRepository) context.extendRepository(repository.getKey());
+      }
+      for (org.sonar.api.rules.Rule rule : repository.createRules()) {
+        // TODO remove org.sonar.api.rules.Rule#tags
+        NewRule newRule = newRepository.newRule(rule.getKey());
+        newRule.setName(ruleName(repository.getKey(), rule));
+        newRule.setHtmlDescription(ruleDescription(repository.getKey(), rule));
+        newRule.setMetadata(rule.getConfigKey());
+        newRule.setTemplate(Cardinality.MULTIPLE.equals(rule.getCardinality()));
+        newRule.setDefaultSeverity(rule.getSeverity().toString());
+        for (RuleParam param : rule.getParams()) {
+          NewParam newParam = newRule.newParam(param.getKey());
+          newParam.setDefaultValue(param.getDefaultValue());
+          newParam.setDescription(paramDescription(repository.getKey(), rule.getKey(), param));
+        }
+      }
+      newRepository.done();
+    }
+  }
+
+  @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);
+  }
+}
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
new file mode 100644 (file)
index 0000000..e199c47
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.rule.RuleDefinitions;
+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.core.i18n.RuleI18nManager;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DeprecatedRuleDefinitionsTest {
+
+  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.createParameter("format").setDescription("Regular expression").setDefaultValue("A-Z").setType("REGULAR_EXPRESSION");
+      return Arrays.asList(rule);
+    }
+  }
+
+  @Test
+  public void wrap_deprecated_rule_repositories() throws Exception {
+    RuleDefinitions.Context context = new RuleDefinitions.Context();
+    RuleI18nManager i18n = mock(RuleI18nManager.class);
+
+    new DeprecatedRuleDefinitions(i18n, new RuleRepository[]{new CheckstyleRules()}).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.defaultSeverity()).isEqualTo(Severity.BLOCKER);
+    assertThat(rule.metadata()).isEqualTo("Checker/TreeWalker/ConstantName");
+    assertThat(rule.tags()).isEmpty();
+    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();
+    RuleI18nManager i18n = mock(RuleI18nManager.class);
+
+    // no more RuleRepository !
+    new DeprecatedRuleDefinitions(i18n);
+
+    assertThat(context.repositories()).isEmpty();
+  }
+
+
+  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 use_l10n_bundles() throws Exception {
+    RuleDefinitions.Context context = new RuleDefinitions.Context();
+    RuleI18nManager i18n = mock(RuleI18nManager.class);
+    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()}).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");
+  }
+}