]> source.dussan.org Git - sonarqube.git/commitdiff
Refactor package names of API introduced in 4.2
authorSimon Brandhof <simon.brandhof@gmail.com>
Mon, 20 Jan 2014 15:33:42 +0000 (16:33 +0100)
committerSimon Brandhof <simon.brandhof@gmail.com>
Mon, 20 Jan 2014 15:33:42 +0000 (16:33 +0100)
* org.sonar.api.batch.<domain>
* org.sonar.api.server.<domain>

66 files changed:
plugins/sonar-xoo-plugin/src/main/java/org/sonar/plugins/xoo/rules/XooRuleDefinitions.java
plugins/sonar-xoo-plugin/src/test/java/org/sonar/plugins/xoo/rules/XooRuleDefinitionsTest.java
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitions.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitionsFromAnnotations.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitionsFromXml.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleParamType.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleTagFormat.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleDefinitions.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleDefinitionsFromAnnotations.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleDefinitionsFromXml.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleParamType.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleTagFormat.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/package-info.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/RequestHandler.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Response.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleRequest.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleResponse.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/package-info.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/web/ws/Request.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/web/ws/RequestHandler.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/web/ws/Response.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/web/ws/SimpleRequest.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/web/ws/SimpleResponse.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/web/ws/WebService.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/web/ws/package-info.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleDefinitionsFromAnnotationsTest.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleDefinitionsFromXmlTest.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleDefinitionsTest.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleParamTypeTest.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleTagFormatTest.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleDefinitionsFromAnnotationsTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleDefinitionsTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleParamTypeTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleTagFormatTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/server/ws/SimpleRequestTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/server/ws/WebServiceTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/web/ws/SimpleRequestTest.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/web/ws/WebServiceTest.java [deleted file]
sonar-plugin-api/src/test/resources/org/sonar/api/rule/RuleDefinitionsFromXmlTest/deprecated.xml [deleted file]
sonar-plugin-api/src/test/resources/org/sonar/api/rule/RuleDefinitionsFromXmlTest/rules.xml [deleted file]
sonar-plugin-api/src/test/resources/org/sonar/api/rule/RuleDefinitionsFromXmlTest/utf8.xml [deleted file]
sonar-plugin-api/src/test/resources/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest/deprecated.xml [new file with mode: 0644]
sonar-plugin-api/src/test/resources/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest/rules.xml [new file with mode: 0644]
sonar-plugin-api/src/test/resources/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest/utf8.xml [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperations.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRule.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRuleParam.java
sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRuleDefinitions.java
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/main/java/org/sonar/server/rule/RuleWebService.java
sonar-server/src/main/java/org/sonar/server/ws/ListingWebService.java
sonar-server/src/main/java/org/sonar/server/ws/ServletRequest.java
sonar-server/src/main/java/org/sonar/server/ws/ServletResponse.java
sonar-server/src/main/java/org/sonar/server/ws/WebServiceEngine.java
sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRuleDefinitionsTest.java
sonar-server/src/test/java/org/sonar/server/rule/RuleDefinitionsLoaderTest.java
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/RuleWebServiceTest.java
sonar-server/src/test/java/org/sonar/server/ws/ListingWebServiceTest.java
sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java

index 8b628604345eecdac18d61f124acf6c56809a65d..ba60b4bbbdcf641f5cc72d801c82849202ae26ea 100644 (file)
@@ -19,8 +19,8 @@
  */
 package org.sonar.plugins.xoo.rules;
 
-import org.sonar.api.rule.RuleDefinitions;
-import org.sonar.api.rule.RuleParamType;
+import org.sonar.api.server.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RuleParamType;
 import org.sonar.plugins.xoo.base.XooConstants;
 import org.sonar.plugins.xoo.base.XooRuleKeys;
 
index 5b77d0de4ec18745a1324f04bf73de3bc0349327..5ca6394ab1f4c1e3d6fcc98c7d7e32e189f67a01 100644 (file)
@@ -20,8 +20,8 @@
 package org.sonar.plugins.xoo.rules;
 
 import org.junit.Test;
-import org.sonar.api.rule.RuleDefinitions;
-import org.sonar.api.rule.RuleDefinitions.Repository;
+import org.sonar.api.server.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RuleDefinitions.Repository;
 
 import static org.fest.assertions.Assertions.assertThat;
 
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitions.java b/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitions.java
deleted file mode 100644 (file)
index 85bb7bd..0000000
+++ /dev/null
@@ -1,569 +0,0 @@
-/*
- * 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 com.google.common.collect.*;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.ServerExtension;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import java.io.InputStream;
-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.2
- */
-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 newRepository(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 newRule(String ruleKey);
-
-    /**
-     * Reads definitions of rules from the annotations provided by the library sonar-check-api.
-     */
-    void 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;configKey&gt;Checker/TreeWalker/LocalVariableName&lt;/configKey&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;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;priority&gt;BLOCKER&lt;/priority&gt;
-     &lt;/rule&gt;
-   &lt;/rules&gt;
-
-     * </pre>
-     */
-    void loadXml(InputStream xmlInput, String encoding);
-
-    void done();
-  }
-
-  interface NewRepository extends NewExtendedRepository {
-    NewRepository setName(String s);
-  }
-
-  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 newRule(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;
-    }
-
-    @Override
-    public void loadAnnotatedClasses(Class... classes) {
-      new RuleDefinitionsFromAnnotations().loadRules(this, classes);
-    }
-
-    @Override
-    public void loadXml(InputStream xmlInput, String encoding) {
-      new RuleDefinitionsFromXml().loadRules(this, xmlInput, encoding);
-    }
-
-    @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();
-  }
-
-  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, metadata, defaultSeverity = Severity.MAJOR;
-    private boolean template;
-    private Status status = Status.READY;
-    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 NewRule setName(String s) {
-      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));
-      }
-      this.defaultSeverity = s;
-      return this;
-    }
-
-    public NewRule setHtmlDescription(String s) {
-      this.htmlDescription = s;
-      return this;
-    }
-
-    public NewRule setStatus(Status status) {
-      this.status = status;
-      return this;
-    }
-
-    public NewParam newParam(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;
-    }
-
-    /**
-     * @see org.sonar.api.rule.RuleTagFormat
-     */
-    public NewRule addTags(String... list) {
-      for (String tag : list) {
-        RuleTagFormat.validate(tag);
-        tags.add(tag);
-      }
-      return this;
-    }
-
-    /**
-     * @see org.sonar.api.rule.RuleTagFormat
-     */
-    public NewRule setTags(String... list) {
-      tags.clear();
-      addTags(list);
-      return this;
-    }
-
-    /**
-     * Optional metadata 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 setMetadata(@Nullable String s) {
-      this.metadata = 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);
-    }
-  }
-
-  enum Status {
-    BETA, DEPRECATED, READY
-  }
-
-  class Rule {
-    private final Repository repository;
-    private final String repoKey, key, name, htmlDescription, metadata, defaultSeverity;
-    private final boolean template;
-    private final Set<String> tags;
-    private final Map<String, Param> params;
-    private final Status 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.metadata = newRule.metadata;
-      this.defaultSeverity = newRule.defaultSeverity;
-      this.template = newRule.template;
-      this.status = newRule.status;
-      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 defaultSeverity() {
-      return defaultSeverity;
-    }
-
-    @CheckForNull
-    public String htmlDescription() {
-      return htmlDescription;
-    }
-
-    public boolean template() {
-      return template;
-    }
-
-    public Status status() {
-      return status;
-    }
-
-    @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 org.sonar.api.rule.RuleDefinitions.NewRule#setMetadata(String)
-     */
-    @CheckForNull
-    public String metadata() {
-      return metadata;
-    }
-
-    @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;
-    }
-  }
-
-  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/main/java/org/sonar/api/rule/RuleDefinitionsFromAnnotations.java b/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitionsFromAnnotations.java
deleted file mode 100644 (file)
index 94a8f5b..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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 com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
-import com.google.common.base.Functions;
-import com.google.common.collect.ImmutableMap;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.utils.AnnotationUtils;
-import org.sonar.api.utils.FieldUtils2;
-import org.sonar.check.Cardinality;
-
-import java.lang.reflect.Field;
-import java.util.List;
-
-/**
- * Read definitions of rules based on the annotations provided by sonar-check-api.
- * </p>
- * It is internally used by {@link org.sonar.api.rule.RuleDefinitions} and can't be directly
- * used by plugins.
- *
- * @since 4.2
- */
-class RuleDefinitionsFromAnnotations {
-
-  private static final Logger LOG = LoggerFactory.getLogger(RuleDefinitionsFromAnnotations.class);
-
-  void loadRules(RuleDefinitions.NewRepository repo, Class... annotatedClasses) {
-    for (Class annotatedClass : annotatedClasses) {
-      loadRule(repo, annotatedClass);
-    }
-  }
-
-  private void loadRule(RuleDefinitions.NewRepository repo, Class clazz) {
-    org.sonar.check.Rule ruleAnnotation = AnnotationUtils.getAnnotation(clazz, org.sonar.check.Rule.class);
-    if (ruleAnnotation != null) {
-      loadRule(repo, clazz, ruleAnnotation);
-    } else {
-      LOG.warn("The class " + clazz.getCanonicalName() + " should be annotated with " + org.sonar.check.Rule.class);
-    }
-  }
-
-  private void loadRule(RuleDefinitions.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.newRule(ruleKey);
-    rule.setName(ruleName).setHtmlDescription(description);
-    rule.setDefaultSeverity(ruleAnnotation.priority().name());
-    rule.setTemplate(ruleAnnotation.cardinality() == Cardinality.MULTIPLE);
-    rule.setStatus(RuleDefinitions.Status.valueOf(ruleAnnotation.status()));
-
-    List<Field> fields = FieldUtils2.getFields(clazz, true);
-    for (Field field : fields) {
-      loadParameters(rule, field);
-    }
-  }
-
-  private void loadParameters(RuleDefinitions.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.newParam(fieldKey)
-        .setDescription(propertyAnnotation.description())
-        .setDefaultValue(propertyAnnotation.defaultValue());
-
-      if (!StringUtils.isBlank(propertyAnnotation.type())) {
-        try {
-          param.setType(RuleParamType.parse(propertyAnnotation.type().trim()));
-        } catch (IllegalArgumentException e) {
-          throw new IllegalArgumentException("Invalid property type [" + propertyAnnotation.type() + "]", e);
-        }
-      } else {
-        param.setType(guessType(field.getType()));
-      }
-    }
-  }
-
-  private static final Function<Class<?>, RuleParamType> TYPE_FOR_CLASS = Functions.forMap(
-    ImmutableMap.<Class<?>, RuleParamType>builder()
-      .put(Integer.class, RuleParamType.INTEGER)
-      .put(int.class, RuleParamType.INTEGER)
-      .put(Float.class, RuleParamType.FLOAT)
-      .put(float.class, RuleParamType.FLOAT)
-      .put(Boolean.class, RuleParamType.BOOLEAN)
-      .put(boolean.class, RuleParamType.BOOLEAN)
-      .build(),
-    RuleParamType.STRING);
-
-  @VisibleForTesting
-  static RuleParamType guessType(Class<?> type) {
-    return TYPE_FOR_CLASS.apply(type);
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitionsFromXml.java b/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitionsFromXml.java
deleted file mode 100644 (file)
index b53904c..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * 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 com.google.common.io.Closeables;
-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.check.Cardinality;
-
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.XMLStreamException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @since 4.2
- */
-class RuleDefinitionsFromXml {
-
-  void loadRules(RuleDefinitions.NewRepository repo, InputStream input, String encoding) {
-    Reader reader = null;
-    try {
-      reader = new InputStreamReader(input, encoding);
-      loadRules(repo, reader);
-
-    } catch (IOException e) {
-      throw new IllegalStateException("Fail to load XML file", e);
-
-    } finally {
-      Closeables.closeQuietly(reader);
-    }
-  }
-
-  void loadRules(RuleDefinitions.NewRepository repo, Reader reader) {
-    XMLInputFactory xmlFactory = XMLInputFactory.newInstance();
-    xmlFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
-    xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
-    // just so it won't try to load DTD in if there's DOCTYPE
-    xmlFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
-    xmlFactory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE);
-    SMInputFactory inputFactory = new SMInputFactory(xmlFactory);
-    try {
-      SMHierarchicCursor rootC = inputFactory.rootElementCursor(reader);
-      rootC.advance(); // <rules>
-
-      SMInputCursor rulesC = rootC.childElementCursor("rule");
-      while (rulesC.getNext() != null) {
-        // <rule>
-        processRule(repo, rulesC);
-      }
-
-    } catch (XMLStreamException e) {
-      throw new IllegalStateException("XML is not valid", e);
-    }
-  }
-
-  private void processRule(RuleDefinitions.NewRepository repo, SMInputCursor ruleC) throws XMLStreamException {
-    String key = null, name = null, description = null, metadata = null, severity = Severity.MAJOR, status = null;
-    Cardinality cardinality = Cardinality.SINGLE;
-    List<ParamStruct> params = new ArrayList<ParamStruct>();
-
-    /* BACKWARD COMPATIBILITY WITH VERY OLD FORMAT */
-    String keyAttribute = ruleC.getAttrValue("key");
-    if (StringUtils.isNotBlank(keyAttribute)) {
-      key = StringUtils.trim(keyAttribute);
-    }
-    String priorityAttribute = ruleC.getAttrValue("priority");
-    if (StringUtils.isNotBlank(priorityAttribute)) {
-      severity = StringUtils.trim(priorityAttribute);
-    }
-
-    SMInputCursor cursor = ruleC.childElementCursor();
-    while (cursor.getNext() != null) {
-      String nodeName = cursor.getLocalName();
-
-      if (StringUtils.equalsIgnoreCase("name", nodeName)) {
-        name = StringUtils.trim(cursor.collectDescendantText(false));
-
-      } else if (StringUtils.equalsIgnoreCase("description", nodeName)) {
-        description = StringUtils.trim(cursor.collectDescendantText(false));
-
-      } else if (StringUtils.equalsIgnoreCase("key", nodeName)) {
-        key = StringUtils.trim(cursor.collectDescendantText(false));
-
-      } else if (StringUtils.equalsIgnoreCase("configKey", nodeName)) {
-        metadata = StringUtils.trim(cursor.collectDescendantText(false));
-
-      } else if (StringUtils.equalsIgnoreCase("priority", nodeName)) {
-        // deprecated field, replaced by severity
-        severity = StringUtils.trim(cursor.collectDescendantText(false));
-
-      } else if (StringUtils.equalsIgnoreCase("severity", nodeName)) {
-        severity = StringUtils.trim(cursor.collectDescendantText(false));
-
-      } else if (StringUtils.equalsIgnoreCase("cardinality", nodeName)) {
-        cardinality = Cardinality.valueOf(StringUtils.trim(cursor.collectDescendantText(false)));
-
-      } else if (StringUtils.equalsIgnoreCase("status", nodeName)) {
-        status = StringUtils.trim(cursor.collectDescendantText(false));
-
-      } else if (StringUtils.equalsIgnoreCase("param", nodeName)) {
-        params.add(processParameter(cursor));
-      }
-    }
-    RuleDefinitions.NewRule rule = repo.newRule(key)
-      .setHtmlDescription(description)
-      .setDefaultSeverity(severity)
-      .setName(name)
-      .setMetadata(metadata)
-      .setTemplate(cardinality == Cardinality.MULTIPLE);
-    if (status != null) {
-      rule.setStatus(RuleDefinitions.Status.valueOf(status));
-    }
-    for (ParamStruct param : params) {
-      rule.newParam(param.key)
-        .setDefaultValue(param.defaultValue)
-        .setType(param.type)
-        .setDescription(param.description);
-    }
-  }
-
-  private static class ParamStruct {
-    String key, description, defaultValue;
-    RuleParamType type = RuleParamType.STRING;
-  }
-
-  private ParamStruct processParameter(SMInputCursor ruleC) throws XMLStreamException {
-    ParamStruct param = new ParamStruct();
-
-    // BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT
-    String keyAttribute = ruleC.getAttrValue("key");
-    if (StringUtils.isNotBlank(keyAttribute)) {
-      param.key = StringUtils.trim(keyAttribute);
-    }
-
-    // BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT
-    String typeAttribute = ruleC.getAttrValue("type");
-    if (StringUtils.isNotBlank(typeAttribute)) {
-      param.type = RuleParamType.parse(typeAttribute);
-    }
-
-    SMInputCursor paramC = ruleC.childElementCursor();
-    while (paramC.getNext() != null) {
-      String propNodeName = paramC.getLocalName();
-      String propText = StringUtils.trim(paramC.collectDescendantText(false));
-      if (StringUtils.equalsIgnoreCase("key", propNodeName)) {
-        param.key = propText;
-
-      } else if (StringUtils.equalsIgnoreCase("description", propNodeName)) {
-        param.description = propText;
-
-      } else if (StringUtils.equalsIgnoreCase("type", propNodeName)) {
-        param.type = RuleParamType.parse(propText);
-
-      } else if (StringUtils.equalsIgnoreCase("defaultValue", propNodeName)) {
-        param.defaultValue = propText;
-      }
-    }
-    return param;
-  }
-}
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
deleted file mode 100644 (file)
index 8b9b4fe..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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.apache.commons.lang.StringEscapeUtils;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.PropertyType;
-
-import java.util.List;
-
-import static com.google.common.collect.Lists.newArrayList;
-
-/**
- * @since 4.2
- */
-public final class RuleParamType {
-
-  private static final String FIELD_SEPARATOR = "|";
-  private static final String OPTION_SEPARATOR = ",";
-
-  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 FLOAT = new RuleParamType("FLOAT");
-
-  private final String type;
-  private final List<String> options;
-
-  // format is "type|comma-separated list of options", for example "INTEGER" or "SINGLE_SELECT_LIST|foo=one,bar,baz=two"
-  private final String key;
-
-  private RuleParamType(String type, String... options) {
-    this.type = type;
-    this.options = newArrayList(options);
-    StringBuilder sb = new StringBuilder();
-    sb.append(type);
-    if (options.length > 0) {
-      sb.append(FIELD_SEPARATOR);
-      for (String option : options) {
-        sb.append(StringEscapeUtils.escapeCsv(option));
-        sb.append(OPTION_SEPARATOR);
-      }
-    }
-    this.key = sb.toString();
-  }
-
-  public String type() {
-    return type;
-  }
-
-  public List<String> options() {
-    return options;
-  }
-
-  public static RuleParamType ofValues(String... acceptedValues) {
-    // reuse the same type as plugin properties in order to
-    // benefit from shared helpers (validation, HTML component)
-    String type = PropertyType.SINGLE_SELECT_LIST.name();
-    return new RuleParamType(type, acceptedValues);
-  }
-
-  // TODO validate format
-  public static RuleParamType parse(String s) {
-    // deprecated formats
-    if ("i".equals(s) || "i{}".equals(s)) {
-      return INTEGER;
-    }
-    if ("s".equals(s) || "s{}".equals(s) || "r".equals(s) || "REGULAR_EXPRESSION".equals(s)) {
-      return STRING;
-    }
-    if ("b".equals(s)) {
-      return BOOLEAN;
-    }
-    if (s.startsWith("s[")) {
-      String values = StringUtils.substringBetween(s, "[", "]");
-      return ofValues(StringUtils.split(values, ','));
-    }
-
-    // standard format
-    String format = StringUtils.substringBefore(s, FIELD_SEPARATOR);
-    String options = StringUtils.substringAfter(s, FIELD_SEPARATOR);
-    if (StringUtils.isBlank(options)) {
-      return new RuleParamType(format);
-    }
-    return new RuleParamType(format, options.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"));
-  }
-
-  @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;
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleTagFormat.java b/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleTagFormat.java
deleted file mode 100644 (file)
index 998a319..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.apache.commons.lang.StringUtils;
-
-/**
- * @since 4.2
- */
-public class RuleTagFormat {
-
-  // Allowed characters are the same as those on StackOverflow
-  // see http://meta.stackoverflow.com/questions/22624/what-symbols-characters-are-not-allowed-in-tags
-  private static final String VALID_CHARACTERS_REGEX = "^[a-z0-9\\+#\\-\\.]+$";
-
-  private RuleTagFormat() {
-    // only static methods
-  }
-
-  public static boolean isValid(String tag) {
-    return StringUtils.isNotBlank(tag) && tag.matches(VALID_CHARACTERS_REGEX);
-  }
-
-  public static void validate(String tag) {
-    if (!isValid(tag)) {
-      throw new IllegalArgumentException(String.format("Whitespaces are not allowed in rule tags: '%s'", tag));
-    }
-  }
-}
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
new file mode 100644 (file)
index 0000000..253e234
--- /dev/null
@@ -0,0 +1,570 @@
+/*
+ * 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.server.rule;
+
+import com.google.common.collect.*;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.ServerExtension;
+import org.sonar.api.rule.Severity;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import java.io.InputStream;
+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.2
+ */
+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 newRepository(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 newRule(String ruleKey);
+
+    /**
+     * Reads definitions of rules from the annotations provided by the library sonar-check-api.
+     */
+    void 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;configKey&gt;Checker/TreeWalker/LocalVariableName&lt;/configKey&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;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;priority&gt;BLOCKER&lt;/priority&gt;
+     &lt;/rule&gt;
+   &lt;/rules&gt;
+
+     * </pre>
+     */
+    void loadXml(InputStream xmlInput, String encoding);
+
+    void done();
+  }
+
+  interface NewRepository extends NewExtendedRepository {
+    NewRepository setName(String s);
+  }
+
+  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 newRule(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;
+    }
+
+    @Override
+    public void loadAnnotatedClasses(Class... classes) {
+      new RuleDefinitionsFromAnnotations().loadRules(this, classes);
+    }
+
+    @Override
+    public void loadXml(InputStream xmlInput, String encoding) {
+      new RuleDefinitionsFromXml().loadRules(this, xmlInput, encoding);
+    }
+
+    @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();
+  }
+
+  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, metadata, defaultSeverity = Severity.MAJOR;
+    private boolean template;
+    private Status status = Status.READY;
+    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 NewRule setName(String s) {
+      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));
+      }
+      this.defaultSeverity = s;
+      return this;
+    }
+
+    public NewRule setHtmlDescription(String s) {
+      this.htmlDescription = s;
+      return this;
+    }
+
+    public NewRule setStatus(Status status) {
+      this.status = status;
+      return this;
+    }
+
+    public NewParam newParam(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;
+    }
+
+    /**
+     * @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 metadata 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 setMetadata(@Nullable String s) {
+      this.metadata = 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);
+    }
+  }
+
+  enum Status {
+    BETA, DEPRECATED, READY
+  }
+
+  class Rule {
+    private final Repository repository;
+    private final String repoKey, key, name, htmlDescription, metadata, defaultSeverity;
+    private final boolean template;
+    private final Set<String> tags;
+    private final Map<String, Param> params;
+    private final Status 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.metadata = newRule.metadata;
+      this.defaultSeverity = newRule.defaultSeverity;
+      this.template = newRule.template;
+      this.status = newRule.status;
+      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 defaultSeverity() {
+      return defaultSeverity;
+    }
+
+    @CheckForNull
+    public String htmlDescription() {
+      return htmlDescription;
+    }
+
+    public boolean template() {
+      return template;
+    }
+
+    public Status status() {
+      return status;
+    }
+
+    @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#setMetadata(String)
+     */
+    @CheckForNull
+    public String metadata() {
+      return metadata;
+    }
+
+    @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;
+    }
+  }
+
+  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/main/java/org/sonar/api/server/rule/RuleDefinitionsFromAnnotations.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleDefinitionsFromAnnotations.java
new file mode 100644 (file)
index 0000000..fbc8c23
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.server.rule;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.utils.AnnotationUtils;
+import org.sonar.api.utils.FieldUtils2;
+import org.sonar.check.Cardinality;
+
+import java.lang.reflect.Field;
+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
+ * used by plugins.
+ *
+ * @since 4.2
+ */
+class RuleDefinitionsFromAnnotations {
+
+  private static final Logger LOG = LoggerFactory.getLogger(RuleDefinitionsFromAnnotations.class);
+
+  void loadRules(RuleDefinitions.NewRepository repo, Class... annotatedClasses) {
+    for (Class annotatedClass : annotatedClasses) {
+      loadRule(repo, annotatedClass);
+    }
+  }
+
+  private void loadRule(RuleDefinitions.NewRepository repo, Class clazz) {
+    org.sonar.check.Rule ruleAnnotation = AnnotationUtils.getAnnotation(clazz, org.sonar.check.Rule.class);
+    if (ruleAnnotation != null) {
+      loadRule(repo, clazz, ruleAnnotation);
+    } else {
+      LOG.warn("The class " + clazz.getCanonicalName() + " should be annotated with " + org.sonar.check.Rule.class);
+    }
+  }
+
+  private void loadRule(RuleDefinitions.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.newRule(ruleKey);
+    rule.setName(ruleName).setHtmlDescription(description);
+    rule.setDefaultSeverity(ruleAnnotation.priority().name());
+    rule.setTemplate(ruleAnnotation.cardinality() == Cardinality.MULTIPLE);
+    rule.setStatus(RuleDefinitions.Status.valueOf(ruleAnnotation.status()));
+
+    List<Field> fields = FieldUtils2.getFields(clazz, true);
+    for (Field field : fields) {
+      loadParameters(rule, field);
+    }
+  }
+
+  private void loadParameters(RuleDefinitions.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.newParam(fieldKey)
+        .setDescription(propertyAnnotation.description())
+        .setDefaultValue(propertyAnnotation.defaultValue());
+
+      if (!StringUtils.isBlank(propertyAnnotation.type())) {
+        try {
+          param.setType(RuleParamType.parse(propertyAnnotation.type().trim()));
+        } catch (IllegalArgumentException e) {
+          throw new IllegalArgumentException("Invalid property type [" + propertyAnnotation.type() + "]", e);
+        }
+      } else {
+        param.setType(guessType(field.getType()));
+      }
+    }
+  }
+
+  private static final Function<Class<?>, RuleParamType> TYPE_FOR_CLASS = Functions.forMap(
+    ImmutableMap.<Class<?>, RuleParamType>builder()
+      .put(Integer.class, RuleParamType.INTEGER)
+      .put(int.class, RuleParamType.INTEGER)
+      .put(Float.class, RuleParamType.FLOAT)
+      .put(float.class, RuleParamType.FLOAT)
+      .put(Boolean.class, RuleParamType.BOOLEAN)
+      .put(boolean.class, RuleParamType.BOOLEAN)
+      .build(),
+    RuleParamType.STRING);
+
+  @VisibleForTesting
+  static RuleParamType guessType(Class<?> type) {
+    return TYPE_FOR_CLASS.apply(type);
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleDefinitionsFromXml.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleDefinitionsFromXml.java
new file mode 100644 (file)
index 0000000..a035baf
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * 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.server.rule;
+
+import com.google.common.io.Closeables;
+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.check.Cardinality;
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @since 4.2
+ */
+class RuleDefinitionsFromXml {
+
+  void loadRules(RuleDefinitions.NewRepository repo, InputStream input, String encoding) {
+    Reader reader = null;
+    try {
+      reader = new InputStreamReader(input, encoding);
+      loadRules(repo, reader);
+
+    } catch (IOException e) {
+      throw new IllegalStateException("Fail to load XML file", e);
+
+    } finally {
+      Closeables.closeQuietly(reader);
+    }
+  }
+
+  void loadRules(RuleDefinitions.NewRepository repo, Reader reader) {
+    XMLInputFactory xmlFactory = XMLInputFactory.newInstance();
+    xmlFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
+    xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
+    // just so it won't try to load DTD in if there's DOCTYPE
+    xmlFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
+    xmlFactory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE);
+    SMInputFactory inputFactory = new SMInputFactory(xmlFactory);
+    try {
+      SMHierarchicCursor rootC = inputFactory.rootElementCursor(reader);
+      rootC.advance(); // <rules>
+
+      SMInputCursor rulesC = rootC.childElementCursor("rule");
+      while (rulesC.getNext() != null) {
+        // <rule>
+        processRule(repo, rulesC);
+      }
+
+    } catch (XMLStreamException e) {
+      throw new IllegalStateException("XML is not valid", e);
+    }
+  }
+
+  private void processRule(RuleDefinitions.NewRepository repo, SMInputCursor ruleC) throws XMLStreamException {
+    String key = null, name = null, description = null, metadata = null, severity = Severity.MAJOR, status = null;
+    Cardinality cardinality = Cardinality.SINGLE;
+    List<ParamStruct> params = new ArrayList<ParamStruct>();
+
+    /* BACKWARD COMPATIBILITY WITH VERY OLD FORMAT */
+    String keyAttribute = ruleC.getAttrValue("key");
+    if (StringUtils.isNotBlank(keyAttribute)) {
+      key = StringUtils.trim(keyAttribute);
+    }
+    String priorityAttribute = ruleC.getAttrValue("priority");
+    if (StringUtils.isNotBlank(priorityAttribute)) {
+      severity = StringUtils.trim(priorityAttribute);
+    }
+
+    SMInputCursor cursor = ruleC.childElementCursor();
+    while (cursor.getNext() != null) {
+      String nodeName = cursor.getLocalName();
+
+      if (StringUtils.equalsIgnoreCase("name", nodeName)) {
+        name = StringUtils.trim(cursor.collectDescendantText(false));
+
+      } else if (StringUtils.equalsIgnoreCase("description", nodeName)) {
+        description = StringUtils.trim(cursor.collectDescendantText(false));
+
+      } else if (StringUtils.equalsIgnoreCase("key", nodeName)) {
+        key = StringUtils.trim(cursor.collectDescendantText(false));
+
+      } else if (StringUtils.equalsIgnoreCase("configKey", nodeName)) {
+        metadata = StringUtils.trim(cursor.collectDescendantText(false));
+
+      } else if (StringUtils.equalsIgnoreCase("priority", nodeName)) {
+        // deprecated field, replaced by severity
+        severity = StringUtils.trim(cursor.collectDescendantText(false));
+
+      } else if (StringUtils.equalsIgnoreCase("severity", nodeName)) {
+        severity = StringUtils.trim(cursor.collectDescendantText(false));
+
+      } else if (StringUtils.equalsIgnoreCase("cardinality", nodeName)) {
+        cardinality = Cardinality.valueOf(StringUtils.trim(cursor.collectDescendantText(false)));
+
+      } else if (StringUtils.equalsIgnoreCase("status", nodeName)) {
+        status = StringUtils.trim(cursor.collectDescendantText(false));
+
+      } else if (StringUtils.equalsIgnoreCase("param", nodeName)) {
+        params.add(processParameter(cursor));
+      }
+    }
+    RuleDefinitions.NewRule rule = repo.newRule(key)
+      .setHtmlDescription(description)
+      .setDefaultSeverity(severity)
+      .setName(name)
+      .setMetadata(metadata)
+      .setTemplate(cardinality == Cardinality.MULTIPLE);
+    if (status != null) {
+      rule.setStatus(RuleDefinitions.Status.valueOf(status));
+    }
+    for (ParamStruct param : params) {
+      rule.newParam(param.key)
+        .setDefaultValue(param.defaultValue)
+        .setType(param.type)
+        .setDescription(param.description);
+    }
+  }
+
+  private static class ParamStruct {
+    String key, description, defaultValue;
+    RuleParamType type = RuleParamType.STRING;
+  }
+
+  private ParamStruct processParameter(SMInputCursor ruleC) throws XMLStreamException {
+    ParamStruct param = new ParamStruct();
+
+    // BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT
+    String keyAttribute = ruleC.getAttrValue("key");
+    if (StringUtils.isNotBlank(keyAttribute)) {
+      param.key = StringUtils.trim(keyAttribute);
+    }
+
+    // BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT
+    String typeAttribute = ruleC.getAttrValue("type");
+    if (StringUtils.isNotBlank(typeAttribute)) {
+      param.type = RuleParamType.parse(typeAttribute);
+    }
+
+    SMInputCursor paramC = ruleC.childElementCursor();
+    while (paramC.getNext() != null) {
+      String propNodeName = paramC.getLocalName();
+      String propText = StringUtils.trim(paramC.collectDescendantText(false));
+      if (StringUtils.equalsIgnoreCase("key", propNodeName)) {
+        param.key = propText;
+
+      } else if (StringUtils.equalsIgnoreCase("description", propNodeName)) {
+        param.description = propText;
+
+      } else if (StringUtils.equalsIgnoreCase("type", propNodeName)) {
+        param.type = RuleParamType.parse(propText);
+
+      } else if (StringUtils.equalsIgnoreCase("defaultValue", propNodeName)) {
+        param.defaultValue = propText;
+      }
+    }
+    return param;
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleParamType.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleParamType.java
new file mode 100644 (file)
index 0000000..9174845
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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.server.rule;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.PropertyType;
+
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * @since 4.2
+ */
+public final class RuleParamType {
+
+  private static final String FIELD_SEPARATOR = "|";
+  private static final String OPTION_SEPARATOR = ",";
+
+  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 FLOAT = new RuleParamType("FLOAT");
+
+  private final String type;
+  private final List<String> options;
+
+  // format is "type|comma-separated list of options", for example "INTEGER" or "SINGLE_SELECT_LIST|foo=one,bar,baz=two"
+  private final String key;
+
+  private RuleParamType(String type, String... options) {
+    this.type = type;
+    this.options = newArrayList(options);
+    StringBuilder sb = new StringBuilder();
+    sb.append(type);
+    if (options.length > 0) {
+      sb.append(FIELD_SEPARATOR);
+      for (String option : options) {
+        sb.append(StringEscapeUtils.escapeCsv(option));
+        sb.append(OPTION_SEPARATOR);
+      }
+    }
+    this.key = sb.toString();
+  }
+
+  public String type() {
+    return type;
+  }
+
+  public List<String> options() {
+    return options;
+  }
+
+  public static RuleParamType ofValues(String... acceptedValues) {
+    // reuse the same type as plugin properties in order to
+    // benefit from shared helpers (validation, HTML component)
+    String type = PropertyType.SINGLE_SELECT_LIST.name();
+    return new RuleParamType(type, acceptedValues);
+  }
+
+  // TODO validate format
+  public static RuleParamType parse(String s) {
+    // deprecated formats
+    if ("i".equals(s) || "i{}".equals(s)) {
+      return INTEGER;
+    }
+    if ("s".equals(s) || "s{}".equals(s) || "r".equals(s) || "REGULAR_EXPRESSION".equals(s)) {
+      return STRING;
+    }
+    if ("b".equals(s)) {
+      return BOOLEAN;
+    }
+    if (s.startsWith("s[")) {
+      String values = StringUtils.substringBetween(s, "[", "]");
+      return ofValues(StringUtils.split(values, ','));
+    }
+
+    // standard format
+    String format = StringUtils.substringBefore(s, FIELD_SEPARATOR);
+    String options = StringUtils.substringAfter(s, FIELD_SEPARATOR);
+    if (StringUtils.isBlank(options)) {
+      return new RuleParamType(format);
+    }
+    return new RuleParamType(format, options.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"));
+  }
+
+  @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;
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleTagFormat.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleTagFormat.java
new file mode 100644 (file)
index 0000000..987b5f1
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.server.rule;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * @since 4.2
+ */
+public class RuleTagFormat {
+
+  // Allowed characters are the same as those on StackOverflow
+  // see http://meta.stackoverflow.com/questions/22624/what-symbols-characters-are-not-allowed-in-tags
+  private static final String VALID_CHARACTERS_REGEX = "^[a-z0-9\\+#\\-\\.]+$";
+
+  private RuleTagFormat() {
+    // only static methods
+  }
+
+  public static boolean isValid(String tag) {
+    return StringUtils.isNotBlank(tag) && tag.matches(VALID_CHARACTERS_REGEX);
+  }
+
+  public static void validate(String tag) {
+    if (!isValid(tag)) {
+      throw new IllegalArgumentException(String.format("Whitespaces are not allowed in rule tags: '%s'", tag));
+    }
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/package-info.java
new file mode 100644 (file)
index 0000000..de7f133
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.api.server.rule;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java
new file mode 100644 (file)
index 0000000..327c055
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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.server.ws;
+
+import javax.annotation.CheckForNull;
+
+/**
+ * @since 4.2
+ */
+public abstract class Request {
+
+  @CheckForNull
+  public abstract String param(String key);
+
+  public abstract String mediaType();
+
+  public abstract boolean isPost();
+
+  @CheckForNull
+  public Integer intParam(String key) {
+    String s = param(key);
+    return s == null ? null : Integer.parseInt(s);
+  }
+
+  public int intParam(String key, int defaultValue) {
+    String s = param(key);
+    return s == null ? defaultValue : Integer.parseInt(s);
+  }
+
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/RequestHandler.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/RequestHandler.java
new file mode 100644 (file)
index 0000000..48f7692
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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.server.ws;
+
+import org.sonar.api.ServerExtension;
+
+/**
+ * @since 4.2
+ */
+public interface RequestHandler extends ServerExtension {
+
+  void handle(Request request, Response response) throws Exception;
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Response.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Response.java
new file mode 100644 (file)
index 0000000..59209cb
--- /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.server.ws;
+
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.utils.text.XmlWriter;
+
+import java.io.OutputStream;
+import java.io.Writer;
+
+/**
+ * HTTP response
+ *
+ * @since 4.2
+ */
+public interface Response {
+
+  int status();
+
+  Response setStatus(int httpStatus);
+
+  JsonWriter newJsonWriter();
+
+  XmlWriter newXmlWriter();
+
+  OutputStream output();
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleRequest.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleRequest.java
new file mode 100644 (file)
index 0000000..9840b8a
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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.server.ws;
+
+import com.google.common.collect.Maps;
+
+import javax.annotation.CheckForNull;
+import java.util.Map;
+
+public class SimpleRequest extends Request {
+  private Map<String, String> params = Maps.newHashMap();
+
+  private String mediaType = "application/json";
+  private boolean post = false;
+
+  public SimpleRequest() {
+  }
+
+  public SimpleRequest setParams(Map<String, String> m) {
+    this.params = m;
+    return this;
+  }
+
+  public SimpleRequest setParams(String key, String value) {
+    this.params.put(key, value);
+    return this;
+  }
+
+  @Override
+  @CheckForNull
+  public String param(String key) {
+    return params.get(key);
+  }
+
+  @Override
+  public String mediaType() {
+    return mediaType;
+  }
+
+  public SimpleRequest setMediaType(String s) {
+    this.mediaType = s;
+    return this;
+  }
+
+  @Override
+  public boolean isPost() {
+    return post;
+  }
+
+  public SimpleRequest setPost(boolean b) {
+    this.post = b;
+    return this;
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleResponse.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleResponse.java
new file mode 100644 (file)
index 0000000..c6ae01d
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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.server.ws;
+
+import org.apache.commons.io.Charsets;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.utils.text.XmlWriter;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+public class SimpleResponse implements Response {
+  private int httpStatus = 200;
+  private final ByteArrayOutputStream output = new ByteArrayOutputStream();
+
+  @Override
+  public JsonWriter newJsonWriter() {
+    return JsonWriter.of(new OutputStreamWriter(output, Charsets.UTF_8));
+  }
+
+  @Override
+  public XmlWriter newXmlWriter() {
+    return XmlWriter.of(new OutputStreamWriter(output, Charsets.UTF_8));
+  }
+
+  @Override
+  public OutputStream output() {
+    return output;
+  }
+
+  // for unit testing
+  public String outputAsString() {
+    return new String(output.toByteArray(), Charsets.UTF_8);
+  }
+
+  @Override
+  public int status() {
+    return httpStatus;
+  }
+
+  @Override
+  public Response setStatus(int httpStatus) {
+    this.httpStatus = httpStatus;
+    return this;
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java
new file mode 100644 (file)
index 0000000..94758d4
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * 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.server.ws;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.ServerExtension;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @since 4.2
+ */
+public interface WebService extends ServerExtension {
+
+  class Context {
+    private final Map<String, Controller> controllers = Maps.newHashMap();
+
+    public NewController newController(String path) {
+      return new NewController(this, path);
+    }
+
+    private void register(NewController newController) {
+      if (controllers.containsKey(newController.path)) {
+        throw new IllegalStateException(
+          String.format("The web service '%s' is defined multiple times", newController.path)
+        );
+      }
+      controllers.put(newController.path, new Controller(newController));
+    }
+
+    @CheckForNull
+    public Controller controller(String key) {
+      return controllers.get(key);
+    }
+
+    public List<Controller> controllers() {
+      return ImmutableList.copyOf(controllers.values());
+    }
+  }
+
+  class NewController {
+    private final Context context;
+    private final String path;
+    private String description, since;
+    private final Map<String, NewAction> actions = Maps.newHashMap();
+
+    private NewController(Context context, String path) {
+      if (StringUtils.isBlank(path)) {
+        throw new IllegalArgumentException("Web service path can't be empty");
+      }
+      this.context = context;
+      this.path = path;
+    }
+
+    public void done() {
+      context.register(this);
+    }
+
+    public NewController setDescription(@Nullable String s) {
+      this.description = s;
+      return this;
+    }
+
+    public NewController setSince(@Nullable String s) {
+      this.since = s;
+      return this;
+    }
+
+    public NewAction newAction(String actionKey) {
+      if (actions.containsKey(actionKey)) {
+        throw new IllegalStateException(
+          String.format("The action '%s' is defined multiple times in the web service '%s'", actionKey, path)
+        );
+      }
+      NewAction action = new NewAction(actionKey);
+      actions.put(actionKey, action);
+      return action;
+    }
+  }
+
+  class Controller {
+    private final String path, description, since;
+    private final Map<String, Action> actions;
+
+    private Controller(NewController newController) {
+      if (newController.actions.isEmpty()) {
+        throw new IllegalStateException(
+          String.format("At least one action must be declared in the web service '%s'", newController.path)
+        );
+      }
+      this.path = newController.path;
+      this.description = newController.description;
+      this.since = newController.since;
+      ImmutableMap.Builder<String, Action> mapBuilder = ImmutableMap.builder();
+      for (NewAction newAction : newController.actions.values()) {
+        mapBuilder.put(newAction.key, new Action(this, newAction));
+      }
+      this.actions = mapBuilder.build();
+    }
+
+    public String path() {
+      return path;
+    }
+
+    @CheckForNull
+    public String description() {
+      return description;
+    }
+
+    public boolean isApi() {
+      return path.startsWith("api/");
+    }
+
+    @CheckForNull
+    public String since() {
+      return since;
+    }
+
+    @CheckForNull
+    public Action action(String actionKey) {
+      return actions.get(actionKey);
+    }
+
+    public Collection<Action> actions() {
+      return actions.values();
+    }
+  }
+
+  // TODO define supported parameters
+  class NewAction {
+    private final String key;
+    private String description, since;
+    private boolean post = false;
+    private RequestHandler handler;
+
+    private NewAction(String key) {
+      this.key = key;
+    }
+
+    public NewAction setDescription(@Nullable String s) {
+      this.description = s;
+      return this;
+    }
+
+    public NewAction setSince(@Nullable String s) {
+      this.since = s;
+      return this;
+    }
+
+    public NewAction setPost(boolean b) {
+      this.post = b;
+      return this;
+    }
+
+    public NewAction setHandler(RequestHandler h) {
+      this.handler = h;
+      return this;
+    }
+  }
+
+  class Action {
+    private final String key, path, description, since;
+    private final boolean post;
+    private final RequestHandler handler;
+
+    private Action(Controller controller, NewAction newAction) {
+      this.key = newAction.key;
+      this.path = String.format("%s/%s", controller.path(), key);
+      this.description = newAction.description;
+      this.since = StringUtils.defaultIfBlank(newAction.since, controller.since);
+      this.post = newAction.post;
+      if (newAction.handler == null) {
+        throw new IllegalStateException("RequestHandler is not set on action " + path);
+      }
+      this.handler = newAction.handler;
+    }
+
+    public String key() {
+      return key;
+    }
+
+    public String path() {
+      return path;
+    }
+
+    @CheckForNull
+    public String description() {
+      return description;
+    }
+
+    /**
+     * Set if different than controller.
+     */
+    @CheckForNull
+    public String since() {
+      return since;
+    }
+
+    public boolean isPost() {
+      return post;
+    }
+
+    public RequestHandler handler() {
+      return handler;
+    }
+
+    @Override
+    public String toString() {
+      return path;
+    }
+  }
+
+  /**
+   * Executed at server startup.
+   */
+  void define(Context context);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/package-info.java
new file mode 100644 (file)
index 0000000..0e1d131
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.api.server.ws;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/ws/Request.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/ws/Request.java
deleted file mode 100644 (file)
index 0aac4f1..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.web.ws;
-
-import javax.annotation.CheckForNull;
-
-/**
- * @since 4.2
- */
-public abstract class Request {
-
-  @CheckForNull
-  public abstract String param(String key);
-
-  public abstract String mediaType();
-
-  public abstract boolean isPost();
-
-  @CheckForNull
-  public Integer intParam(String key) {
-    String s = param(key);
-    return s == null ? null : Integer.parseInt(s);
-  }
-
-  public int intParam(String key, int defaultValue) {
-    String s = param(key);
-    return s == null ? defaultValue : Integer.parseInt(s);
-  }
-
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/ws/RequestHandler.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/ws/RequestHandler.java
deleted file mode 100644 (file)
index c7cc37a..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.web.ws;
-
-import org.sonar.api.ServerExtension;
-
-/**
- * @since 4.2
- */
-public interface RequestHandler extends ServerExtension {
-
-  void handle(Request request, Response response) throws Exception;
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/ws/Response.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/ws/Response.java
deleted file mode 100644 (file)
index 6bcb3e7..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.web.ws;
-
-import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.api.utils.text.XmlWriter;
-
-import java.io.OutputStream;
-import java.io.Writer;
-
-/**
- * HTTP response
- *
- * @since 4.2
- */
-public interface Response {
-
-  int status();
-
-  Response setStatus(int httpStatus);
-
-  JsonWriter newJsonWriter();
-
-  XmlWriter newXmlWriter();
-
-  OutputStream output();
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/ws/SimpleRequest.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/ws/SimpleRequest.java
deleted file mode 100644 (file)
index 5a89ca4..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.web.ws;
-
-import com.google.common.collect.Maps;
-
-import javax.annotation.CheckForNull;
-import java.util.Map;
-
-public class SimpleRequest extends Request {
-  private Map<String, String> params = Maps.newHashMap();
-
-  private String mediaType = "application/json";
-  private boolean post = false;
-
-  public SimpleRequest() {
-  }
-
-  public SimpleRequest setParams(Map<String, String> m) {
-    this.params = m;
-    return this;
-  }
-
-  public SimpleRequest setParams(String key, String value) {
-    this.params.put(key, value);
-    return this;
-  }
-
-  @Override
-  @CheckForNull
-  public String param(String key) {
-    return params.get(key);
-  }
-
-  @Override
-  public String mediaType() {
-    return mediaType;
-  }
-
-  public SimpleRequest setMediaType(String s) {
-    this.mediaType = s;
-    return this;
-  }
-
-  @Override
-  public boolean isPost() {
-    return post;
-  }
-
-  public SimpleRequest setPost(boolean b) {
-    this.post = b;
-    return this;
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/ws/SimpleResponse.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/ws/SimpleResponse.java
deleted file mode 100644 (file)
index 0e70a57..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.web.ws;
-
-import org.apache.commons.io.Charsets;
-import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.api.utils.text.XmlWriter;
-
-import java.io.ByteArrayOutputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-
-public class SimpleResponse implements Response {
-  private int httpStatus = 200;
-  private final ByteArrayOutputStream output = new ByteArrayOutputStream();
-
-  @Override
-  public JsonWriter newJsonWriter() {
-    return JsonWriter.of(new OutputStreamWriter(output, Charsets.UTF_8));
-  }
-
-  @Override
-  public XmlWriter newXmlWriter() {
-    return XmlWriter.of(new OutputStreamWriter(output, Charsets.UTF_8));
-  }
-
-  @Override
-  public OutputStream output() {
-    return output;
-  }
-
-  // for unit testing
-  public String outputAsString() {
-    return new String(output.toByteArray(), Charsets.UTF_8);
-  }
-
-  @Override
-  public int status() {
-    return httpStatus;
-  }
-
-  @Override
-  public Response setStatus(int httpStatus) {
-    this.httpStatus = httpStatus;
-    return this;
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/ws/WebService.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/ws/WebService.java
deleted file mode 100644 (file)
index 0b8c680..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * 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.web.ws;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.ServerExtension;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @since 4.2
- */
-public interface WebService extends ServerExtension {
-
-  class Context {
-    private final Map<String, Controller> controllers = Maps.newHashMap();
-
-    public NewController newController(String path) {
-      return new NewController(this, path);
-    }
-
-    private void register(NewController newController) {
-      if (controllers.containsKey(newController.path)) {
-        throw new IllegalStateException(
-          String.format("The web service '%s' is defined multiple times", newController.path)
-        );
-      }
-      controllers.put(newController.path, new Controller(newController));
-    }
-
-    @CheckForNull
-    public Controller controller(String key) {
-      return controllers.get(key);
-    }
-
-    public List<Controller> controllers() {
-      return ImmutableList.copyOf(controllers.values());
-    }
-  }
-
-  class NewController {
-    private final Context context;
-    private final String path;
-    private String description, since;
-    private final Map<String, NewAction> actions = Maps.newHashMap();
-
-    private NewController(Context context, String path) {
-      if (StringUtils.isBlank(path)) {
-        throw new IllegalArgumentException("Web service path can't be empty");
-      }
-      this.context = context;
-      this.path = path;
-    }
-
-    public void done() {
-      context.register(this);
-    }
-
-    public NewController setDescription(@Nullable String s) {
-      this.description = s;
-      return this;
-    }
-
-    public NewController setSince(@Nullable String s) {
-      this.since = s;
-      return this;
-    }
-
-    public NewAction newAction(String actionKey) {
-      if (actions.containsKey(actionKey)) {
-        throw new IllegalStateException(
-          String.format("The action '%s' is defined multiple times in the web service '%s'", actionKey, path)
-        );
-      }
-      NewAction action = new NewAction(actionKey);
-      actions.put(actionKey, action);
-      return action;
-    }
-  }
-
-  class Controller {
-    private final String path, description, since;
-    private final Map<String, Action> actions;
-
-    private Controller(NewController newController) {
-      if (newController.actions.isEmpty()) {
-        throw new IllegalStateException(
-          String.format("At least one action must be declared in the web service '%s'", newController.path)
-        );
-      }
-      this.path = newController.path;
-      this.description = newController.description;
-      this.since = newController.since;
-      ImmutableMap.Builder<String, Action> mapBuilder = ImmutableMap.builder();
-      for (NewAction newAction : newController.actions.values()) {
-        mapBuilder.put(newAction.key, new Action(this, newAction));
-      }
-      this.actions = mapBuilder.build();
-    }
-
-    public String path() {
-      return path;
-    }
-
-    @CheckForNull
-    public String description() {
-      return description;
-    }
-
-    public boolean isApi() {
-      return path.startsWith("api/");
-    }
-
-    @CheckForNull
-    public String since() {
-      return since;
-    }
-
-    @CheckForNull
-    public Action action(String actionKey) {
-      return actions.get(actionKey);
-    }
-
-    public Collection<Action> actions() {
-      return actions.values();
-    }
-  }
-
-  // TODO define supported parameters
-  class NewAction {
-    private final String key;
-    private String description, since;
-    private boolean post = false;
-    private RequestHandler handler;
-
-    private NewAction(String key) {
-      this.key = key;
-    }
-
-    public NewAction setDescription(@Nullable String s) {
-      this.description = s;
-      return this;
-    }
-
-    public NewAction setSince(@Nullable String s) {
-      this.since = s;
-      return this;
-    }
-
-    public NewAction setPost(boolean b) {
-      this.post = b;
-      return this;
-    }
-
-    public NewAction setHandler(RequestHandler h) {
-      this.handler = h;
-      return this;
-    }
-  }
-
-  class Action {
-    private final String key, path, description, since;
-    private final boolean post;
-    private final RequestHandler handler;
-
-    private Action(Controller controller, NewAction newAction) {
-      this.key = newAction.key;
-      this.path = String.format("%s/%s", controller.path(), key);
-      this.description = newAction.description;
-      this.since = StringUtils.defaultIfBlank(newAction.since, controller.since);
-      this.post = newAction.post;
-      if (newAction.handler == null) {
-        throw new IllegalStateException("RequestHandler is not set on action " + path);
-      }
-      this.handler = newAction.handler;
-    }
-
-    public String key() {
-      return key;
-    }
-
-    public String path() {
-      return path;
-    }
-
-    @CheckForNull
-    public String description() {
-      return description;
-    }
-
-    /**
-     * Set if different than controller.
-     */
-    @CheckForNull
-    public String since() {
-      return since;
-    }
-
-    public boolean isPost() {
-      return post;
-    }
-
-    public RequestHandler handler() {
-      return handler;
-    }
-
-    @Override
-    public String toString() {
-      return path;
-    }
-  }
-
-  /**
-   * Executed at server startup.
-   */
-  void define(Context context);
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/ws/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/ws/package-info.java
deleted file mode 100644 (file)
index b5fcdf6..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.api.web.ws;
-
-import javax.annotation.ParametersAreNonnullByDefault;
-
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleDefinitionsFromAnnotationsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleDefinitionsFromAnnotationsTest.java
deleted file mode 100644 (file)
index e3007cc..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * 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.Ignore;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-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(RuleDefinitions.Status.BETA);
-    assertThat(rule.name()).isEqualTo("bar");
-    assertThat(rule.htmlDescription()).isEqualTo("Foo Bar");
-    assertThat(rule.defaultSeverity()).isEqualTo(Severity.BLOCKER);
-    assertThat(rule.status()).isEqualTo(RuleDefinitions.Status.BETA);
-    assertThat(rule.params()).hasSize(1);
-
-    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 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
-  @Ignore("TODO list supported types in RuleParamType")
-  public void should_reject_invalid_property_types() {
-    thrown.expect(IllegalArgumentException.class);
-    thrown.expectMessage("Invalid property type [INVALID]");
-
-    load(RuleWithInvalidPropertyType.class);
-  }
-
-  @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 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.defaultSeverity()).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.NewRepository newRepository = context.newRepository("squid", "java");
-    new RuleDefinitionsFromAnnotations().loadRules(newRepository, annotatedClass);
-    newRepository.done();
-    return context.repository("squid");
-  }
-
-  @org.sonar.check.Rule(name = "foo", description = "Foo")
-  static class RuleWithoutKey {
-  }
-
-  @org.sonar.check.Rule(key = "foo")
-  static class RuleWithoutNameNorDescription {
-  }
-
-  @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;
-  }
-}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleDefinitionsFromXmlTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleDefinitionsFromXmlTest.java
deleted file mode 100644 (file)
index 97fa34f..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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 com.google.common.base.Charsets;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-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.newRepository("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/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.defaultSeverity()).isEqualTo(Severity.BLOCKER);
-    assertThat(rule.template()).isTrue();
-    assertThat(rule.status()).isEqualTo(RuleDefinitions.Status.BETA);
-    assertThat(rule.metadata()).isEqualTo("Checker/TreeWalker/LocalVariableName");
-    assertThat(rule.status()).isEqualTo(RuleDefinitions.Status.BETA);
-
-    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(RuleDefinitions.Status.READY);
-    assertThat(rule.defaultSeverity()).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/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/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.defaultSeverity()).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/rule/RuleDefinitionsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleDefinitionsTest.java
deleted file mode 100644 (file)
index 9245e41..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * 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;
-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.newRepository("findbugs", "java").setName("Findbugs").done();
-    context.newRepository("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.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")
-      .setStatus(RuleDefinitions.Status.BETA)
-      .setTags("one", "two")
-      .addTags("two", "three", "four");
-    newFindbugs.newRule("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.defaultSeverity()).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.metadata()).isEqualTo("/something");
-    assertThat(npeRule.template()).isFalse();
-    assertThat(npeRule.status()).isEqualTo(RuleDefinitions.Status.BETA);
-    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.newRepository("findbugs", "java");
-    newFindbugs.newRule("NPE").setName("NPE").setHtmlDescription("NPE");
-    newFindbugs.done();
-
-    RuleDefinitions.Rule rule = context.repository("findbugs").rule("NPE");
-    assertThat(rule.key()).isEqualTo("NPE");
-    assertThat(rule.defaultSeverity()).isEqualTo(Severity.MAJOR);
-    assertThat(rule.params()).isEmpty();
-    assertThat(rule.metadata()).isNull();
-    assertThat(rule.status()).isEqualTo(RuleDefinitions.Status.READY);
-    assertThat(rule.tags()).isEmpty();
-  }
-
-  @Test
-  public void define_rule_parameters() {
-    RuleDefinitions.NewRepository newFindbugs = context.newRepository("findbugs", "java");
-    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();
-
-    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 extend_repository() {
-    assertThat(context.extendedRepositories()).isEmpty();
-
-    // for example fb-contrib
-    RuleDefinitions.NewExtendedRepository newFindbugs = context.extendRepository("findbugs", "java");
-    newFindbugs.newRule("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.newRepository("findbugs", "java").setName(null).done();
-
-    assertThat(context.repository("findbugs").name()).isEqualTo("findbugs");
-  }
-
-  @Test
-  public void fail_if_duplicated_repo_keys() {
-    context.newRepository("findbugs", "java").done();
-    try {
-      context.newRepository("findbugs", "whatever_the_language").done();
-      fail();
-    } catch (IllegalStateException e) {
-      assertThat(e).hasMessage("The rule repository 'findbugs' is defined several times");
-    }
-  }
-
-  @Test
-  public void warning_if_duplicated_rule_keys() {
-    RuleDefinitions.NewRepository findbugs = context.newRepository("findbugs", "java");
-    findbugs.newRule("NPE");
-    findbugs.newRule("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.newRepository("findbugs", "java").newRule("NPE");
-    rule.newParam("level");
-    try {
-      rule.newParam("level");
-      fail();
-    } catch (IllegalArgumentException e) {
-      assertThat(e).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.newRepository("findbugs", "java");
-    newRepository.newRule("NPE").setName(null).setHtmlDescription("NPE");
-    try {
-      newRepository.done();
-      fail();
-    } catch (IllegalStateException e) {
-      assertThat(e).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.newRepository("findbugs", "java").newRule("NPE").setTags("coding style");
-      fail();
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessage("Whitespaces are not allowed in rule tags: 'coding style'");
-    }
-  }
-
-  @Test
-  public void fail_if_blank_rule_html_description() {
-    RuleDefinitions.NewRepository newRepository = context.newRepository("findbugs", "java");
-    newRepository.newRule("NPE").setName("NPE").setHtmlDescription(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.newRepository("findbugs", "java").newRule("NPE").setDefaultSeverity("VERY HIGH");
-      fail();
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessage("Default severity of rule [repository=findbugs, key=NPE] is not correct: VERY HIGH");
-    }
-  }
-}
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
deleted file mode 100644 (file)
index 0df7557..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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 org.sonar.api.PropertyType;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class RuleParamTypeTest {
-
-  @Test
-  public void testEquals() throws Exception {
-    RuleParamType noOptions = RuleParamType.INTEGER;
-    RuleParamType withOptions1 = RuleParamType.ofValues("one", "two");
-    RuleParamType withOptions2 = RuleParamType.ofValues("three", "four");
-
-    assertThat(RuleParamType.INTEGER)
-      .isEqualTo(RuleParamType.INTEGER)
-      .isNotEqualTo(RuleParamType.STRING)
-      .isNotEqualTo("INTEGER")
-      .isNotEqualTo(withOptions1)
-      .isNotEqualTo(null);
-
-    assertThat(withOptions1)
-      .isEqualTo(withOptions1)
-      .isNotEqualTo(noOptions)
-      .isNotEqualTo(withOptions2)
-      .isNotEqualTo("SINGLE_SELECT_LIST|one,two,")
-      .isNotEqualTo(null);
-  }
-
-  @Test
-  public void testHashCode() throws Exception {
-    assertThat(RuleParamType.INTEGER.hashCode()).isEqualTo(RuleParamType.INTEGER.hashCode());
-  }
-
-  @Test
-  public void testInteger() throws Exception {
-    RuleParamType type = RuleParamType.INTEGER;
-    assertThat(type.toString()).isEqualTo("INTEGER");
-    assertThat(RuleParamType.parse(type.toString()).type()).isEqualTo("INTEGER");
-    assertThat(RuleParamType.parse(type.toString()).options()).isEmpty();
-    assertThat(RuleParamType.parse(type.toString()).toString()).isEqualTo("INTEGER");
-  }
-
-  @Test
-  public void testListOfValues() throws Exception {
-    RuleParamType selectList = RuleParamType.parse("SINGLE_SELECT_LIST|foo,bar,");
-    assertThat(selectList.type()).isEqualTo("SINGLE_SELECT_LIST");
-    assertThat(selectList.options()).containsOnly("foo", "bar");
-    assertThat(selectList.toString()).isEqualTo("SINGLE_SELECT_LIST|foo,bar,");
-
-    // escape values
-    selectList = RuleParamType.ofValues("foo", "one,two|three,four");
-    assertThat(selectList.type()).isEqualTo("SINGLE_SELECT_LIST");
-    assertThat(selectList.options()).containsOnly("foo", "one,two|three,four");
-    assertThat(selectList.toString()).isEqualTo("SINGLE_SELECT_LIST|foo,\"one,two|three,four\",");
-  }
-
-  @Test
-  public void support_deprecated_formats() throws Exception {
-    assertThat(RuleParamType.parse("b")).isEqualTo(RuleParamType.BOOLEAN);
-    assertThat(RuleParamType.parse("i")).isEqualTo(RuleParamType.INTEGER);
-    assertThat(RuleParamType.parse("i{}")).isEqualTo(RuleParamType.INTEGER);
-    assertThat(RuleParamType.parse("s")).isEqualTo(RuleParamType.STRING);
-    assertThat(RuleParamType.parse("s{}")).isEqualTo(RuleParamType.STRING);
-    assertThat(RuleParamType.parse("r")).isEqualTo(RuleParamType.STRING);
-    assertThat(RuleParamType.parse("TEXT")).isEqualTo(RuleParamType.TEXT);
-    assertThat(RuleParamType.parse("STRING")).isEqualTo(RuleParamType.STRING);
-    assertThat(RuleParamType.parse("REGULAR_EXPRESSION")).isEqualTo(RuleParamType.STRING);
-    RuleParamType list = RuleParamType.parse("s[FOO,BAR]");
-    assertThat(list.type()).isEqualTo("SINGLE_SELECT_LIST");
-    assertThat(list.options()).containsOnly("FOO", "BAR");
-  }
-}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleTagFormatTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleTagFormatTest.java
deleted file mode 100644 (file)
index 3197d4e..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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;
-import static org.fest.assertions.Fail.fail;
-
-public class RuleTagFormatTest {
-  @Test
-  public void isValid() {
-    assertThat(RuleTagFormat.isValid(null)).isFalse();
-    assertThat(RuleTagFormat.isValid("")).isFalse();
-    assertThat(RuleTagFormat.isValid(" ")).isFalse();
-    assertThat(RuleTagFormat.isValid("coding style")).isFalse();
-    assertThat(RuleTagFormat.isValid("Style")).isFalse();
-    assertThat(RuleTagFormat.isValid("sTyle")).isFalse();
-    assertThat(RuleTagFormat.isValid("@style")).isFalse();
-
-    assertThat(RuleTagFormat.isValid("style")).isTrue();
-    assertThat(RuleTagFormat.isValid("c++")).isTrue();
-    assertThat(RuleTagFormat.isValid("f#")).isTrue();
-    assertThat(RuleTagFormat.isValid("c++11")).isTrue();
-  }
-
-  @Test
-  public void validate() {
-    RuleTagFormat.validate("style");
-    // no error
-  }
-
-  @Test
-  public void validate_and_fail() {
-    try {
-      RuleTagFormat.validate("  ");
-      fail();
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessage("Whitespaces are not allowed in rule tags: '  '");
-    }
-  }
-}
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
new file mode 100644 (file)
index 0000000..f07d998
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * 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.server.rule;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.server.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RuleDefinitionsFromAnnotations;
+import org.sonar.api.server.rule.RuleParamType;
+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(RuleDefinitions.Status.BETA);
+    assertThat(rule.name()).isEqualTo("bar");
+    assertThat(rule.htmlDescription()).isEqualTo("Foo Bar");
+    assertThat(rule.defaultSeverity()).isEqualTo(Severity.BLOCKER);
+    assertThat(rule.status()).isEqualTo(RuleDefinitions.Status.BETA);
+    assertThat(rule.params()).hasSize(1);
+
+    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 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
+  @Ignore("TODO list supported types in RuleParamType")
+  public void should_reject_invalid_property_types() {
+    thrown.expect(IllegalArgumentException.class);
+    thrown.expectMessage("Invalid property type [INVALID]");
+
+    load(RuleWithInvalidPropertyType.class);
+  }
+
+  @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 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.defaultSeverity()).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.NewRepository newRepository = context.newRepository("squid", "java");
+    new RuleDefinitionsFromAnnotations().loadRules(newRepository, annotatedClass);
+    newRepository.done();
+    return context.repository("squid");
+  }
+
+  @org.sonar.check.Rule(name = "foo", description = "Foo")
+  static class RuleWithoutKey {
+  }
+
+  @org.sonar.check.Rule(key = "foo")
+  static class RuleWithoutNameNorDescription {
+  }
+
+  @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;
+  }
+}
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
new file mode 100644 (file)
index 0000000..a9e71d8
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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.server.rule;
+
+import com.google.common.base.Charsets;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+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 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.newRepository("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.defaultSeverity()).isEqualTo(Severity.BLOCKER);
+    assertThat(rule.template()).isTrue();
+    assertThat(rule.status()).isEqualTo(RuleDefinitions.Status.BETA);
+    assertThat(rule.metadata()).isEqualTo("Checker/TreeWalker/LocalVariableName");
+    assertThat(rule.status()).isEqualTo(RuleDefinitions.Status.BETA);
+
+    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(RuleDefinitions.Status.READY);
+    assertThat(rule.defaultSeverity()).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.defaultSeverity()).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
new file mode 100644 (file)
index 0000000..88a5ca3
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * 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.server.rule;
+
+import org.junit.Test;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.server.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RuleParamType;
+
+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.newRepository("findbugs", "java").setName("Findbugs").done();
+    context.newRepository("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.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")
+      .setStatus(RuleDefinitions.Status.BETA)
+      .setTags("one", "two")
+      .addTags("two", "three", "four");
+    newFindbugs.newRule("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.defaultSeverity()).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.metadata()).isEqualTo("/something");
+    assertThat(npeRule.template()).isFalse();
+    assertThat(npeRule.status()).isEqualTo(RuleDefinitions.Status.BETA);
+    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.newRepository("findbugs", "java");
+    newFindbugs.newRule("NPE").setName("NPE").setHtmlDescription("NPE");
+    newFindbugs.done();
+
+    RuleDefinitions.Rule rule = context.repository("findbugs").rule("NPE");
+    assertThat(rule.key()).isEqualTo("NPE");
+    assertThat(rule.defaultSeverity()).isEqualTo(Severity.MAJOR);
+    assertThat(rule.params()).isEmpty();
+    assertThat(rule.metadata()).isNull();
+    assertThat(rule.status()).isEqualTo(RuleDefinitions.Status.READY);
+    assertThat(rule.tags()).isEmpty();
+  }
+
+  @Test
+  public void define_rule_parameters() {
+    RuleDefinitions.NewRepository newFindbugs = context.newRepository("findbugs", "java");
+    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();
+
+    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 extend_repository() {
+    assertThat(context.extendedRepositories()).isEmpty();
+
+    // for example fb-contrib
+    RuleDefinitions.NewExtendedRepository newFindbugs = context.extendRepository("findbugs", "java");
+    newFindbugs.newRule("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.newRepository("findbugs", "java").setName(null).done();
+
+    assertThat(context.repository("findbugs").name()).isEqualTo("findbugs");
+  }
+
+  @Test
+  public void fail_if_duplicated_repo_keys() {
+    context.newRepository("findbugs", "java").done();
+    try {
+      context.newRepository("findbugs", "whatever_the_language").done();
+      fail();
+    } catch (IllegalStateException e) {
+      assertThat(e).hasMessage("The rule repository 'findbugs' is defined several times");
+    }
+  }
+
+  @Test
+  public void warning_if_duplicated_rule_keys() {
+    RuleDefinitions.NewRepository findbugs = context.newRepository("findbugs", "java");
+    findbugs.newRule("NPE");
+    findbugs.newRule("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.newRepository("findbugs", "java").newRule("NPE");
+    rule.newParam("level");
+    try {
+      rule.newParam("level");
+      fail();
+    } catch (IllegalArgumentException e) {
+      assertThat(e).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.newRepository("findbugs", "java");
+    newRepository.newRule("NPE").setName(null).setHtmlDescription("NPE");
+    try {
+      newRepository.done();
+      fail();
+    } catch (IllegalStateException e) {
+      assertThat(e).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.newRepository("findbugs", "java").newRule("NPE").setTags("coding style");
+      fail();
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessage("Whitespaces are not allowed in rule tags: 'coding style'");
+    }
+  }
+
+  @Test
+  public void fail_if_blank_rule_html_description() {
+    RuleDefinitions.NewRepository newRepository = context.newRepository("findbugs", "java");
+    newRepository.newRule("NPE").setName("NPE").setHtmlDescription(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.newRepository("findbugs", "java").newRule("NPE").setDefaultSeverity("VERY HIGH");
+      fail();
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessage("Default severity of rule [repository=findbugs, key=NPE] is not correct: VERY HIGH");
+    }
+  }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleParamTypeTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleParamTypeTest.java
new file mode 100644 (file)
index 0000000..2b7558a
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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.server.rule;
+
+import org.junit.Test;
+import org.sonar.api.server.rule.RuleParamType;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class RuleParamTypeTest {
+
+  @Test
+  public void testEquals() throws Exception {
+    RuleParamType noOptions = RuleParamType.INTEGER;
+    RuleParamType withOptions1 = RuleParamType.ofValues("one", "two");
+    RuleParamType withOptions2 = RuleParamType.ofValues("three", "four");
+
+    assertThat(RuleParamType.INTEGER)
+      .isEqualTo(RuleParamType.INTEGER)
+      .isNotEqualTo(RuleParamType.STRING)
+      .isNotEqualTo("INTEGER")
+      .isNotEqualTo(withOptions1)
+      .isNotEqualTo(null);
+
+    assertThat(withOptions1)
+      .isEqualTo(withOptions1)
+      .isNotEqualTo(noOptions)
+      .isNotEqualTo(withOptions2)
+      .isNotEqualTo("SINGLE_SELECT_LIST|one,two,")
+      .isNotEqualTo(null);
+  }
+
+  @Test
+  public void testHashCode() throws Exception {
+    assertThat(RuleParamType.INTEGER.hashCode()).isEqualTo(RuleParamType.INTEGER.hashCode());
+  }
+
+  @Test
+  public void testInteger() throws Exception {
+    RuleParamType type = RuleParamType.INTEGER;
+    assertThat(type.toString()).isEqualTo("INTEGER");
+    assertThat(RuleParamType.parse(type.toString()).type()).isEqualTo("INTEGER");
+    assertThat(RuleParamType.parse(type.toString()).options()).isEmpty();
+    assertThat(RuleParamType.parse(type.toString()).toString()).isEqualTo("INTEGER");
+  }
+
+  @Test
+  public void testListOfValues() throws Exception {
+    RuleParamType selectList = RuleParamType.parse("SINGLE_SELECT_LIST|foo,bar,");
+    assertThat(selectList.type()).isEqualTo("SINGLE_SELECT_LIST");
+    assertThat(selectList.options()).containsOnly("foo", "bar");
+    assertThat(selectList.toString()).isEqualTo("SINGLE_SELECT_LIST|foo,bar,");
+
+    // escape values
+    selectList = RuleParamType.ofValues("foo", "one,two|three,four");
+    assertThat(selectList.type()).isEqualTo("SINGLE_SELECT_LIST");
+    assertThat(selectList.options()).containsOnly("foo", "one,two|three,four");
+    assertThat(selectList.toString()).isEqualTo("SINGLE_SELECT_LIST|foo,\"one,two|three,four\",");
+  }
+
+  @Test
+  public void support_deprecated_formats() throws Exception {
+    assertThat(RuleParamType.parse("b")).isEqualTo(RuleParamType.BOOLEAN);
+    assertThat(RuleParamType.parse("i")).isEqualTo(RuleParamType.INTEGER);
+    assertThat(RuleParamType.parse("i{}")).isEqualTo(RuleParamType.INTEGER);
+    assertThat(RuleParamType.parse("s")).isEqualTo(RuleParamType.STRING);
+    assertThat(RuleParamType.parse("s{}")).isEqualTo(RuleParamType.STRING);
+    assertThat(RuleParamType.parse("r")).isEqualTo(RuleParamType.STRING);
+    assertThat(RuleParamType.parse("TEXT")).isEqualTo(RuleParamType.TEXT);
+    assertThat(RuleParamType.parse("STRING")).isEqualTo(RuleParamType.STRING);
+    assertThat(RuleParamType.parse("REGULAR_EXPRESSION")).isEqualTo(RuleParamType.STRING);
+    RuleParamType list = RuleParamType.parse("s[FOO,BAR]");
+    assertThat(list.type()).isEqualTo("SINGLE_SELECT_LIST");
+    assertThat(list.options()).containsOnly("FOO", "BAR");
+  }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleTagFormatTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleTagFormatTest.java
new file mode 100644 (file)
index 0000000..735f3f3
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.server.rule;
+
+import org.junit.Test;
+import org.sonar.api.server.rule.RuleTagFormat;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+
+public class RuleTagFormatTest {
+  @Test
+  public void isValid() {
+    assertThat(RuleTagFormat.isValid(null)).isFalse();
+    assertThat(RuleTagFormat.isValid("")).isFalse();
+    assertThat(RuleTagFormat.isValid(" ")).isFalse();
+    assertThat(RuleTagFormat.isValid("coding style")).isFalse();
+    assertThat(RuleTagFormat.isValid("Style")).isFalse();
+    assertThat(RuleTagFormat.isValid("sTyle")).isFalse();
+    assertThat(RuleTagFormat.isValid("@style")).isFalse();
+
+    assertThat(RuleTagFormat.isValid("style")).isTrue();
+    assertThat(RuleTagFormat.isValid("c++")).isTrue();
+    assertThat(RuleTagFormat.isValid("f#")).isTrue();
+    assertThat(RuleTagFormat.isValid("c++11")).isTrue();
+  }
+
+  @Test
+  public void validate() {
+    RuleTagFormat.validate("style");
+    // no error
+  }
+
+  @Test
+  public void validate_and_fail() {
+    try {
+      RuleTagFormat.validate("  ");
+      fail();
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessage("Whitespaces are not allowed in rule tags: '  '");
+    }
+  }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/SimpleRequestTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/SimpleRequestTest.java
new file mode 100644 (file)
index 0000000..d9e5bca
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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.server.ws;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Test;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class SimpleRequestTest {
+  @Test
+  public void string_params() {
+    Request request = new SimpleRequest().setParams(ImmutableMap.of("foo", "bar"));
+    assertThat(request.param("none")).isNull();
+    assertThat(request.param("foo")).isEqualTo("bar");
+  }
+
+  @Test
+  public void int_params() {
+    Request request = new SimpleRequest().setParams(ImmutableMap.of("foo", "123"));
+    assertThat(request.intParam("none")).isNull();
+    assertThat(request.intParam("foo")).isEqualTo(123);
+
+    assertThat(request.intParam("none", 456)).isEqualTo(456);
+    assertThat(request.intParam("foo", 456)).isEqualTo(123);
+  }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/WebServiceTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/WebServiceTest.java
new file mode 100644 (file)
index 0000000..d962f6b
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * 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.server.ws;
+
+import org.junit.Test;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+import static org.mockito.Mockito.mock;
+
+public class WebServiceTest {
+
+  static class MetricWebService implements WebService {
+    boolean showCalled = false, createCalled = false;
+    @Override
+    public void define(Context context) {
+      NewController newController = context.newController("api/metric")
+        .setDescription("Metrics")
+        .setSince("3.2");
+      newController.newAction("show")
+          .setDescription("Show metric")
+          .setHandler(new RequestHandler() {
+            @Override
+            public void handle(Request request, Response response) {
+              show(request, response);
+            }
+          });
+      newController.newAction("create")
+        .setDescription("Create metric")
+        .setSince("4.1")
+        .setPost(true)
+        .setHandler(new RequestHandler() {
+          @Override
+          public void handle(Request request, Response response) {
+            create(request, response);
+          }
+        });
+      newController.done();
+    }
+
+    void show(Request request, Response response) {
+      showCalled = true;
+    }
+
+    void create(Request request, Response response) {
+      createCalled = true;
+    }
+  }
+
+
+  WebService.Context context = new WebService.Context();
+
+  @Test
+  public void no_web_services_by_default() {
+    assertThat(context.controllers()).isEmpty();
+    assertThat(context.controller("metric")).isNull();
+  }
+
+  @Test
+  public void define_web_service() {
+    MetricWebService metricWs = new MetricWebService();
+
+    metricWs.define(context);
+
+    WebService.Controller controller = context.controller("api/metric");
+    assertThat(controller).isNotNull();
+    assertThat(controller.path()).isEqualTo("api/metric");
+    assertThat(controller.description()).isEqualTo("Metrics");
+    assertThat(controller.since()).isEqualTo("3.2");
+    assertThat(controller.isApi()).isTrue();
+    assertThat(controller.actions()).hasSize(2);
+    WebService.Action showAction = controller.action("show");
+    assertThat(showAction).isNotNull();
+    assertThat(showAction.key()).isEqualTo("show");
+    assertThat(showAction.description()).isEqualTo("Show metric");
+    assertThat(showAction.handler()).isNotNull();
+    // same as controller
+    assertThat(showAction.since()).isEqualTo("3.2");
+    assertThat(showAction.isPost()).isFalse();
+    assertThat(showAction.path()).isEqualTo("api/metric/show");
+    WebService.Action createAction = controller.action("create");
+    assertThat(createAction).isNotNull();
+    assertThat(createAction.key()).isEqualTo("create");
+    assertThat(createAction.toString()).isEqualTo("api/metric/create");
+    // overrides controller version
+    assertThat(createAction.since()).isEqualTo("4.1");
+    assertThat(createAction.isPost()).isTrue();
+  }
+
+  @Test
+  public void non_api_ws() {
+    new WebService() {
+      @Override
+      public void define(Context context) {
+        NewController controller = context.newController("rule");
+        controller.newAction("index").setHandler(mock(RequestHandler.class));
+        controller.done();
+      }
+    }.define(context);
+    assertThat(context.controller("rule").isApi()).isFalse();
+  }
+
+  @Test
+  public void fail_if_duplicated_ws_keys() {
+    MetricWebService metricWs = new MetricWebService();
+    metricWs.define(context);
+    try {
+      new WebService() {
+        @Override
+        public void define(Context context) {
+          NewController newController = context.newController("api/metric");
+          newController.newAction("delete");
+          newController.done();
+        }
+      }.define(context);
+      fail();
+    } catch (IllegalStateException e) {
+      assertThat(e).hasMessage("The web service 'api/metric' is defined multiple times");
+    }
+  }
+
+  @Test
+  public void fail_if_no_action_handler() {
+    try {
+      new WebService() {
+        @Override
+        public void define(Context context) {
+          NewController controller = context.newController("rule");
+          controller.newAction("show");
+          controller.done();
+        }
+      }.define(context);
+      fail();
+    } catch (IllegalStateException e) {
+      assertThat(e).hasMessage("RequestHandler is not set on action rule/show");
+    }
+  }
+
+  @Test
+  public void fail_if_duplicated_action_keys() {
+    try {
+      new WebService() {
+        @Override
+        public void define(Context context) {
+          NewController newController = context.newController("rule");
+          newController.newAction("create");
+          newController.newAction("delete");
+          newController.newAction("delete");
+          newController.done();
+        }
+      }.define(context);
+      fail();
+    } catch (IllegalStateException e) {
+      assertThat(e).hasMessage("The action 'delete' is defined multiple times in the web service 'rule'");
+    }
+  }
+
+  @Test
+  public void fail_if_no_actions() {
+    try {
+      new WebService() {
+        @Override
+        public void define(Context context) {
+          context.newController("rule").done();
+        }
+      }.define(context);
+      fail();
+    } catch (IllegalStateException e) {
+      assertThat(e).hasMessage("At least one action must be declared in the web service 'rule'");
+    }
+  }
+
+  @Test
+  public void fail_if_no_ws_path() {
+    try {
+      new WebService() {
+        @Override
+        public void define(Context context) {
+          context.newController(null).done();
+        }
+      }.define(context);
+      fail();
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessage("Web service path can't be empty");
+    }
+  }
+
+  @Test
+  public void handle_request() throws Exception {
+    MetricWebService metricWs = new MetricWebService();
+    metricWs.define(context);
+
+    assertThat(metricWs.showCalled).isFalse();
+    assertThat(metricWs.createCalled).isFalse();
+    context.controller("api/metric").action("show").handler().handle(mock(Request.class), mock(Response.class));
+    assertThat(metricWs.showCalled).isTrue();
+    assertThat(metricWs.createCalled).isFalse();
+    context.controller("api/metric").action("create").handler().handle(mock(Request.class), mock(Response.class));
+    assertThat(metricWs.createCalled).isTrue();
+  }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/web/ws/SimpleRequestTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/web/ws/SimpleRequestTest.java
deleted file mode 100644 (file)
index b6120c5..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.web.ws;
-
-import com.google.common.collect.ImmutableMap;
-import org.junit.Test;
-import org.sonar.api.web.ws.Request;
-import org.sonar.api.web.ws.SimpleRequest;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class SimpleRequestTest {
-  @Test
-  public void string_params() {
-    Request request = new SimpleRequest().setParams(ImmutableMap.of("foo", "bar"));
-    assertThat(request.param("none")).isNull();
-    assertThat(request.param("foo")).isEqualTo("bar");
-  }
-
-  @Test
-  public void int_params() {
-    Request request = new SimpleRequest().setParams(ImmutableMap.of("foo", "123"));
-    assertThat(request.intParam("none")).isNull();
-    assertThat(request.intParam("foo")).isEqualTo(123);
-
-    assertThat(request.intParam("none", 456)).isEqualTo(456);
-    assertThat(request.intParam("foo", 456)).isEqualTo(123);
-  }
-}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/web/ws/WebServiceTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/web/ws/WebServiceTest.java
deleted file mode 100644 (file)
index 0485597..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * 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.web.ws;
-
-import org.junit.Test;
-import org.sonar.api.web.ws.Request;
-import org.sonar.api.web.ws.RequestHandler;
-import org.sonar.api.web.ws.Response;
-import org.sonar.api.web.ws.WebService;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.fest.assertions.Fail.fail;
-import static org.mockito.Mockito.mock;
-
-public class WebServiceTest {
-
-  static class MetricWebService implements WebService {
-    boolean showCalled = false, createCalled = false;
-    @Override
-    public void define(Context context) {
-      NewController newController = context.newController("api/metric")
-        .setDescription("Metrics")
-        .setSince("3.2");
-      newController.newAction("show")
-          .setDescription("Show metric")
-          .setHandler(new RequestHandler() {
-            @Override
-            public void handle(Request request, Response response) {
-              show(request, response);
-            }
-          });
-      newController.newAction("create")
-        .setDescription("Create metric")
-        .setSince("4.1")
-        .setPost(true)
-        .setHandler(new RequestHandler() {
-          @Override
-          public void handle(Request request, Response response) {
-            create(request, response);
-          }
-        });
-      newController.done();
-    }
-
-    void show(Request request, Response response) {
-      showCalled = true;
-    }
-
-    void create(Request request, Response response) {
-      createCalled = true;
-    }
-  }
-
-
-  WebService.Context context = new WebService.Context();
-
-  @Test
-  public void no_web_services_by_default() {
-    assertThat(context.controllers()).isEmpty();
-    assertThat(context.controller("metric")).isNull();
-  }
-
-  @Test
-  public void define_web_service() {
-    MetricWebService metricWs = new MetricWebService();
-
-    metricWs.define(context);
-
-    WebService.Controller controller = context.controller("api/metric");
-    assertThat(controller).isNotNull();
-    assertThat(controller.path()).isEqualTo("api/metric");
-    assertThat(controller.description()).isEqualTo("Metrics");
-    assertThat(controller.since()).isEqualTo("3.2");
-    assertThat(controller.isApi()).isTrue();
-    assertThat(controller.actions()).hasSize(2);
-    WebService.Action showAction = controller.action("show");
-    assertThat(showAction).isNotNull();
-    assertThat(showAction.key()).isEqualTo("show");
-    assertThat(showAction.description()).isEqualTo("Show metric");
-    assertThat(showAction.handler()).isNotNull();
-    // same as controller
-    assertThat(showAction.since()).isEqualTo("3.2");
-    assertThat(showAction.isPost()).isFalse();
-    assertThat(showAction.path()).isEqualTo("api/metric/show");
-    WebService.Action createAction = controller.action("create");
-    assertThat(createAction).isNotNull();
-    assertThat(createAction.key()).isEqualTo("create");
-    assertThat(createAction.toString()).isEqualTo("api/metric/create");
-    // overrides controller version
-    assertThat(createAction.since()).isEqualTo("4.1");
-    assertThat(createAction.isPost()).isTrue();
-  }
-
-  @Test
-  public void non_api_ws() {
-    new WebService() {
-      @Override
-      public void define(Context context) {
-        NewController controller = context.newController("rule");
-        controller.newAction("index").setHandler(mock(RequestHandler.class));
-        controller.done();
-      }
-    }.define(context);
-    assertThat(context.controller("rule").isApi()).isFalse();
-  }
-
-  @Test
-  public void fail_if_duplicated_ws_keys() {
-    MetricWebService metricWs = new MetricWebService();
-    metricWs.define(context);
-    try {
-      new WebService() {
-        @Override
-        public void define(Context context) {
-          NewController newController = context.newController("api/metric");
-          newController.newAction("delete");
-          newController.done();
-        }
-      }.define(context);
-      fail();
-    } catch (IllegalStateException e) {
-      assertThat(e).hasMessage("The web service 'api/metric' is defined multiple times");
-    }
-  }
-
-  @Test
-  public void fail_if_no_action_handler() {
-    try {
-      new WebService() {
-        @Override
-        public void define(Context context) {
-          NewController controller = context.newController("rule");
-          controller.newAction("show");
-          controller.done();
-        }
-      }.define(context);
-      fail();
-    } catch (IllegalStateException e) {
-      assertThat(e).hasMessage("RequestHandler is not set on action rule/show");
-    }
-  }
-
-  @Test
-  public void fail_if_duplicated_action_keys() {
-    try {
-      new WebService() {
-        @Override
-        public void define(Context context) {
-          NewController newController = context.newController("rule");
-          newController.newAction("create");
-          newController.newAction("delete");
-          newController.newAction("delete");
-          newController.done();
-        }
-      }.define(context);
-      fail();
-    } catch (IllegalStateException e) {
-      assertThat(e).hasMessage("The action 'delete' is defined multiple times in the web service 'rule'");
-    }
-  }
-
-  @Test
-  public void fail_if_no_actions() {
-    try {
-      new WebService() {
-        @Override
-        public void define(Context context) {
-          context.newController("rule").done();
-        }
-      }.define(context);
-      fail();
-    } catch (IllegalStateException e) {
-      assertThat(e).hasMessage("At least one action must be declared in the web service 'rule'");
-    }
-  }
-
-  @Test
-  public void fail_if_no_ws_path() {
-    try {
-      new WebService() {
-        @Override
-        public void define(Context context) {
-          context.newController(null).done();
-        }
-      }.define(context);
-      fail();
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessage("Web service path can't be empty");
-    }
-  }
-
-  @Test
-  public void handle_request() throws Exception {
-    MetricWebService metricWs = new MetricWebService();
-    metricWs.define(context);
-
-    assertThat(metricWs.showCalled).isFalse();
-    assertThat(metricWs.createCalled).isFalse();
-    context.controller("api/metric").action("show").handler().handle(mock(Request.class), mock(Response.class));
-    assertThat(metricWs.showCalled).isTrue();
-    assertThat(metricWs.createCalled).isFalse();
-    context.controller("api/metric").action("create").handler().handle(mock(Request.class), mock(Response.class));
-    assertThat(metricWs.createCalled).isTrue();
-  }
-}
diff --git a/sonar-plugin-api/src/test/resources/org/sonar/api/rule/RuleDefinitionsFromXmlTest/deprecated.xml b/sonar-plugin-api/src/test/resources/org/sonar/api/rule/RuleDefinitionsFromXmlTest/deprecated.xml
deleted file mode 100644 (file)
index 2938ea7..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<rules>
-  <rule key="org.sonar.it.checkstyle.MethodsCountCheck" priority="CRITICAL">
-    <name>Methods Count Check</name>
-    <configKey>Checker/TreeWalker/org.sonar.it.checkstyle.MethodsCountCheck</configKey>
-    <description>Count methods</description>
-    <param key="minMethodsCount" type="i">
-      <description>description of param</description>
-    </param>
-  </rule>
-</rules>
\ No newline at end of file
diff --git a/sonar-plugin-api/src/test/resources/org/sonar/api/rule/RuleDefinitionsFromXmlTest/rules.xml b/sonar-plugin-api/src/test/resources/org/sonar/api/rule/RuleDefinitionsFromXmlTest/rules.xml
deleted file mode 100644 (file)
index 6be8619..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<rules>
-  <rule>
-    <!-- with exhaustive fields -->
-    <key>complete</key>
-    <name>Complete</name>
-    <description>
-      <![CDATA[Description of Complete]]>
-    </description>
-    <configKey>Checker/TreeWalker/LocalVariableName</configKey>
-    <severity>BLOCKER</severity>
-    <cardinality>MULTIPLE</cardinality>
-    <status>BETA</status>
-    <param>
-      <key>tokens</key>
-      <description>
-        <![CDATA[
-          Controls whether the check applies to variable declarations or catch clause parameters
-          ]]>
-      </description>
-    </param>
-    <param>
-      <key>ignore</key>
-      <description>
-        Ignore ?
-      </description>
-      <defaultValue>false</defaultValue>
-    </param>
-  </rule>
-
-
-  <rule>
-    <!-- with only required fields -->
-    <key>minimal</key>
-    <name>Minimal</name>
-    <description>
-      <![CDATA[Description of Minimal]]>
-    </description>
-  </rule>
-</rules>
\ No newline at end of file
diff --git a/sonar-plugin-api/src/test/resources/org/sonar/api/rule/RuleDefinitionsFromXmlTest/utf8.xml b/sonar-plugin-api/src/test/resources/org/sonar/api/rule/RuleDefinitionsFromXmlTest/utf8.xml
deleted file mode 100644 (file)
index 6197e03..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<rules>
-  <rule>
-    <key>com.puppycrawl.tools.checkstyle.checks.naming.LocalVariableNameCheck</key>
-    <priority>BLOCKER</priority>
-    <configKey>Checker/TreeWalker/LocalVariableName</configKey>
-    <name>M &amp; M</name>
-    <description>
-      <![CDATA[éà&]]>
-    </description>
-  </rule>
-</rules>
diff --git a/sonar-plugin-api/src/test/resources/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest/deprecated.xml b/sonar-plugin-api/src/test/resources/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest/deprecated.xml
new file mode 100644 (file)
index 0000000..2938ea7
--- /dev/null
@@ -0,0 +1,10 @@
+<rules>
+  <rule key="org.sonar.it.checkstyle.MethodsCountCheck" priority="CRITICAL">
+    <name>Methods Count Check</name>
+    <configKey>Checker/TreeWalker/org.sonar.it.checkstyle.MethodsCountCheck</configKey>
+    <description>Count methods</description>
+    <param key="minMethodsCount" type="i">
+      <description>description of param</description>
+    </param>
+  </rule>
+</rules>
\ No newline at end of file
diff --git a/sonar-plugin-api/src/test/resources/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest/rules.xml b/sonar-plugin-api/src/test/resources/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest/rules.xml
new file mode 100644 (file)
index 0000000..6be8619
--- /dev/null
@@ -0,0 +1,39 @@
+<rules>
+  <rule>
+    <!-- with exhaustive fields -->
+    <key>complete</key>
+    <name>Complete</name>
+    <description>
+      <![CDATA[Description of Complete]]>
+    </description>
+    <configKey>Checker/TreeWalker/LocalVariableName</configKey>
+    <severity>BLOCKER</severity>
+    <cardinality>MULTIPLE</cardinality>
+    <status>BETA</status>
+    <param>
+      <key>tokens</key>
+      <description>
+        <![CDATA[
+          Controls whether the check applies to variable declarations or catch clause parameters
+          ]]>
+      </description>
+    </param>
+    <param>
+      <key>ignore</key>
+      <description>
+        Ignore ?
+      </description>
+      <defaultValue>false</defaultValue>
+    </param>
+  </rule>
+
+
+  <rule>
+    <!-- with only required fields -->
+    <key>minimal</key>
+    <name>Minimal</name>
+    <description>
+      <![CDATA[Description of Minimal]]>
+    </description>
+  </rule>
+</rules>
\ No newline at end of file
diff --git a/sonar-plugin-api/src/test/resources/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest/utf8.xml b/sonar-plugin-api/src/test/resources/org/sonar/api/server/rule/RuleDefinitionsFromXmlTest/utf8.xml
new file mode 100644 (file)
index 0000000..6197e03
--- /dev/null
@@ -0,0 +1,11 @@
+<rules>
+  <rule>
+    <key>com.puppycrawl.tools.checkstyle.checks.naming.LocalVariableNameCheck</key>
+    <priority>BLOCKER</priority>
+    <configKey>Checker/TreeWalker/LocalVariableName</configKey>
+    <name>M &amp; M</name>
+    <description>
+      <![CDATA[éà&]]>
+    </description>
+  </rule>
+</rules>
index 497aa1bf371fc2fc560fc6ee98a79abd868e1878..f35dfc24f850d69b645b430f8d64900832c0fb36 100644 (file)
@@ -26,7 +26,7 @@ import org.apache.ibatis.session.SqlSession;
 import org.elasticsearch.common.base.Predicate;
 import org.elasticsearch.common.collect.Iterables;
 import org.sonar.api.ServerComponent;
-import org.sonar.api.rule.RuleParamType;
+import org.sonar.api.server.rule.RuleParamType;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.rules.RulePriority;
 import org.sonar.api.utils.System2;
index 67dcd03c4bfaa70a512f7b28388f302829bd5b89..1b22e99c01fa614d94d4dde5c2a8f80aba7df3d5 100644 (file)
@@ -23,7 +23,7 @@ import org.apache.commons.lang.builder.ReflectionToStringBuilder;
 import org.elasticsearch.common.collect.Lists;
 import org.elasticsearch.common.collect.Maps;
 import org.elasticsearch.common.joda.time.format.ISODateTimeFormat;
-import org.sonar.api.rule.RuleParamType;
+import org.sonar.api.server.rule.RuleParamType;
 import org.sonar.check.Cardinality;
 import org.sonar.server.rule.ActiveRuleDocument;
 import org.sonar.server.rule.RuleDocument;
index 36285357f0f395d3d10112c792b5415dca104850..f46d52e3907b696d1349555e6f324bd9fa915aa9 100644 (file)
@@ -20,7 +20,7 @@
 package org.sonar.server.qualityprofile;
 
 import org.apache.commons.lang.builder.ReflectionToStringBuilder;
-import org.sonar.api.rule.RuleParamType;
+import org.sonar.api.server.rule.RuleParamType;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
index d8d373e639fed1a380278f58965312e12d687e8c..6ec08b88d2e7a9b9fe44d12db99cca98728f9535 100644 (file)
@@ -20,8 +20,8 @@
 package org.sonar.server.rule;
 
 import org.apache.commons.lang.StringUtils;
-import org.sonar.api.rule.RuleDefinitions;
-import org.sonar.api.rule.RuleParamType;
+import org.sonar.api.server.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RuleParamType;
 import org.sonar.api.rules.RuleParam;
 import org.sonar.api.rules.RuleRepository;
 import org.sonar.check.Cardinality;
index a9f1bc71b77f7e37e9f2b8f3599f8b7f5a48f91e..9cfe6da71a59a020db5617dfcea0d5d3bbcd9001 100644 (file)
@@ -20,7 +20,7 @@
 package org.sonar.server.rule;
 
 import org.sonar.api.ServerComponent;
-import org.sonar.api.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RuleDefinitions;
 
 /**
  * Loads all instances of RuleDefinitions and initializes RuleRepositories. Used at server startup.
index dca8898555e90a2e7754eea5f1ea597eaddc040a..56501c76cb1dc29ed12eb3fcf726e44fc073d0c6 100644 (file)
@@ -26,7 +26,7 @@ import org.apache.ibatis.session.SqlSession;
 import org.picocontainer.Startable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.sonar.api.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RuleDefinitions;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.rules.RulePriority;
index 8a4882fc91a46306188049845bcb728d9816b2f9..ee71287ff67ad1a6e54ac24bfe3194a5229ce337 100644 (file)
@@ -24,7 +24,7 @@ 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.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RuleDefinitions;
 
 import javax.annotation.CheckForNull;
 import java.util.Collection;
index e40c69a89b3b999c00ef954a9e2a772f97c17d52..de35e668469679771685da9b50fc47753f8b2c67 100644 (file)
  */
 package org.sonar.server.rule;
 
-import org.sonar.api.web.ws.Request;
-import org.sonar.api.web.ws.RequestHandler;
-import org.sonar.api.web.ws.Response;
-import org.sonar.api.web.ws.WebService;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
 
 public class RuleWebService implements WebService {
 
index 9d5afcbc9959f68522e9d43a04d8973da266cc8d..407d1dcc7628228df7e9a804e4e53ee955f2732c 100644 (file)
 package org.sonar.server.ws;
 
 import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.api.web.ws.Request;
-import org.sonar.api.web.ws.RequestHandler;
-import org.sonar.api.web.ws.Response;
-import org.sonar.api.web.ws.WebService;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
 
 import java.util.List;
 
index e38e53975b3eb0eeb6309b8da0b0258d260c1751..d8c62790c921bbfce66e542cf487a04edd080eaf 100644 (file)
@@ -19,7 +19,7 @@
  */
 package org.sonar.server.ws;
 
-import org.sonar.api.web.ws.Request;
+import org.sonar.api.server.ws.Request;
 
 import javax.servlet.http.HttpServletRequest;
 
index 97c03a98989d4563c434c7b3ec46b2df90f7d0ab..448bf213b6f258424d8a3fc5f393092e7fa49a93 100644 (file)
@@ -21,7 +21,7 @@ package org.sonar.server.ws;
 
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.api.utils.text.XmlWriter;
-import org.sonar.api.web.ws.Response;
+import org.sonar.api.server.ws.Response;
 
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
index 84037f4e460752286fff915d6bb140f8b0821d2c..b1556e57094cce7edb77089a3a91af974aba0e6c 100644 (file)
@@ -22,9 +22,9 @@ package org.sonar.server.ws;
 import org.picocontainer.Startable;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.api.web.ws.Request;
-import org.sonar.api.web.ws.Response;
-import org.sonar.api.web.ws.WebService;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
 
 import javax.servlet.http.HttpServletResponse;
 import java.util.List;
index ff92599b5cef189616050431ace323efc3b33d3d..d44951a21a1f35ba06ced201b4d9646d256ab3d0 100644 (file)
@@ -20,7 +20,7 @@
 package org.sonar.server.rule;
 
 import org.junit.Test;
-import org.sonar.api.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RuleDefinitions;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.rules.RulePriority;
index 8433bce0713c942fcb4b83ff4c2cc4f11926e13e..3743c6991fda43b497eaf784edd507d28e0365e8 100644 (file)
@@ -20,7 +20,7 @@
 package org.sonar.server.rule;
 
 import org.junit.Test;
-import org.sonar.api.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RuleDefinitions;
 
 import static org.fest.assertions.Assertions.assertThat;
 
index 4975e8476d34e67da235c5c4686810a7b0ffbd70..6bb253002c3be80ca645d3383403d3da93e314a8 100644 (file)
@@ -22,7 +22,7 @@ package org.sonar.server.rule;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.sonar.api.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RuleDefinitions;
 import org.sonar.api.rule.Severity;
 import org.sonar.core.persistence.AbstractDaoTestCase;
 import org.sonar.core.persistence.MyBatis;
index 3113e1a5f41fa3b8d05a285c0a1babf0e0913217..5d2fcff8d0b248639767ae06f4952621123c92a4 100644 (file)
@@ -20,7 +20,7 @@
 package org.sonar.server.rule;
 
 import org.junit.Test;
-import org.sonar.api.rule.RuleDefinitions;
+import org.sonar.api.server.rule.RuleDefinitions;
 
 import static org.fest.assertions.Assertions.assertThat;
 
index 0700df795160ffc5f1b8b6359312e17efb874012..69e9c4bdc5dc17cbd5e7ac58d093158bfc708d25 100644 (file)
@@ -22,10 +22,10 @@ package org.sonar.server.rule;
 import org.apache.commons.io.IOUtils;
 import org.junit.Test;
 import org.skyscreamer.jsonassert.JSONAssert;
-import org.sonar.api.web.ws.RequestHandler;
-import org.sonar.api.web.ws.SimpleRequest;
-import org.sonar.api.web.ws.SimpleResponse;
-import org.sonar.api.web.ws.WebService;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.SimpleRequest;
+import org.sonar.api.server.ws.SimpleResponse;
+import org.sonar.api.server.ws.WebService;
 
 import static org.fest.assertions.Assertions.assertThat;
 
index 95227dd8dc6225c75834b91d3370abb5230b6c8b..915f2edca5d0eb4189868b7c0efedce0f724dd7a 100644 (file)
@@ -22,7 +22,7 @@ package org.sonar.server.ws;
 import org.apache.commons.io.IOUtils;
 import org.junit.Test;
 import org.skyscreamer.jsonassert.JSONAssert;
-import org.sonar.api.web.ws.*;
+import org.sonar.api.server.ws.*;
 
 import static org.fest.assertions.Assertions.assertThat;
 
index 5565f7e159895cd37b720b96bde2feb257a67113..727f07d2627bc31ec5309e1620f04968889d060b 100644 (file)
@@ -22,7 +22,7 @@ package org.sonar.server.ws;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.sonar.api.web.ws.*;
+import org.sonar.api.server.ws.*;
 
 import static org.fest.assertions.Assertions.assertThat;