]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4908 add RuleDefinitions#loadXml() and depreciate org.sonar.api.rules.XMLRuleParser
authorSimon Brandhof <simon.brandhof@gmail.com>
Wed, 15 Jan 2014 21:22:50 +0000 (22:22 +0100)
committerSimon Brandhof <simon.brandhof@gmail.com>
Wed, 15 Jan 2014 21:22:50 +0000 (22:22 +0100)
19 files changed:
sonar-deprecated/src/main/java/org/sonar/api/rules/XMLRuleParser.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/rule/AnnotationRuleDefinitions.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitions.java
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitionsFromAnnotations.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitionsFromXml.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleParamType.java
sonar-plugin-api/src/main/java/org/sonar/api/rules/XMLRuleParser.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/rule/AnnotationRuleDefinitionsTest.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleDefinitionsFromAnnotationsTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleDefinitionsFromXmlTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleParamTypeTest.java
sonar-plugin-api/src/test/java/org/sonar/api/rules/XMLRuleParserTest.java [deleted file]
sonar-plugin-api/src/test/resources/org/sonar/api/rule/RuleDefinitionsFromXmlTest/deprecated.xml [new file with mode: 0644]
sonar-plugin-api/src/test/resources/org/sonar/api/rule/RuleDefinitionsFromXmlTest/rules.xml [new file with mode: 0644]
sonar-plugin-api/src/test/resources/org/sonar/api/rule/RuleDefinitionsFromXmlTest/utf8.xml [new file with mode: 0644]
sonar-plugin-api/src/test/resources/org/sonar/api/rules/XMLRuleParserTest/deprecated.xml [deleted file]
sonar-plugin-api/src/test/resources/org/sonar/api/rules/XMLRuleParserTest/rules.xml [deleted file]
sonar-plugin-api/src/test/resources/org/sonar/api/rules/XMLRuleParserTest/utf8.xml [deleted file]
sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRuleDefinitions.java

diff --git a/sonar-deprecated/src/main/java/org/sonar/api/rules/XMLRuleParser.java b/sonar-deprecated/src/main/java/org/sonar/api/rules/XMLRuleParser.java
new file mode 100644 (file)
index 0000000..17a60af
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * 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.rules;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Strings;
+import com.google.common.collect.Maps;
+import com.google.common.io.Closeables;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.CharEncoding;
+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.PropertyType;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.utils.SonarException;
+import org.sonar.check.Cardinality;
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @since 2.3
+ * @deprecated in 4.2. Replaced by org.sonar.api.rule.RuleDefinitions#loadXml()
+ */
+@Deprecated
+public final class XMLRuleParser implements ServerComponent {
+  private static final Map<String, String> TYPE_MAP = typeMapWithDeprecatedValues();
+
+  public List<Rule> parse(File file) {
+    Reader reader = null;
+    try {
+      reader = new InputStreamReader(FileUtils.openInputStream(file), CharEncoding.UTF_8);
+      return parse(reader);
+
+    } catch (IOException e) {
+      throw new SonarException("Fail to load the file: " + file, e);
+
+    } finally {
+      Closeables.closeQuietly(reader);
+    }
+  }
+
+  /**
+   * Warning : the input stream is closed in this method
+   */
+  public List<Rule> parse(InputStream input) {
+    Reader reader = null;
+    try {
+      reader = new InputStreamReader(input, CharEncoding.UTF_8);
+      return parse(reader);
+
+    } catch (IOException e) {
+      throw new SonarException("Fail to load the xml stream", e);
+
+    } finally {
+      Closeables.closeQuietly(reader);
+    }
+  }
+
+  public List<Rule> parse(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>
+      List<Rule> rules = new ArrayList<Rule>();
+
+      SMInputCursor rulesC = rootC.childElementCursor("rule");
+      while (rulesC.getNext() != null) {
+        // <rule>
+        Rule rule = Rule.create();
+        rules.add(rule);
+
+        processRule(rule, rulesC);
+      }
+      return rules;
+
+    } catch (XMLStreamException e) {
+      throw new SonarException("XML is not valid", e);
+    }
+  }
+
+  private static void processRule(Rule rule, SMInputCursor ruleC) throws XMLStreamException {
+    /* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
+    String keyAttribute = ruleC.getAttrValue("key");
+    if (StringUtils.isNotBlank(keyAttribute)) {
+      rule.setKey(StringUtils.trim(keyAttribute));
+    }
+
+    /* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
+    String priorityAttribute = ruleC.getAttrValue("priority");
+    if (StringUtils.isNotBlank(priorityAttribute)) {
+      rule.setSeverity(RulePriority.valueOf(StringUtils.trim(priorityAttribute)));
+    }
+
+    SMInputCursor cursor = ruleC.childElementCursor();
+
+    while (cursor.getNext() != null) {
+      String nodeName = cursor.getLocalName();
+
+      if (StringUtils.equalsIgnoreCase("name", nodeName)) {
+        rule.setName(StringUtils.trim(cursor.collectDescendantText(false)));
+
+      } else if (StringUtils.equalsIgnoreCase("description", nodeName)) {
+        rule.setDescription(StringUtils.trim(cursor.collectDescendantText(false)));
+
+      } else if (StringUtils.equalsIgnoreCase("key", nodeName)) {
+        rule.setKey(StringUtils.trim(cursor.collectDescendantText(false)));
+
+      } else if (StringUtils.equalsIgnoreCase("configKey", nodeName)) {
+        rule.setConfigKey(StringUtils.trim(cursor.collectDescendantText(false)));
+
+      } else if (StringUtils.equalsIgnoreCase("priority", nodeName)) {
+        rule.setSeverity(RulePriority.valueOf(StringUtils.trim(cursor.collectDescendantText(false))));
+
+      } else if (StringUtils.equalsIgnoreCase("cardinality", nodeName)) {
+        rule.setCardinality(Cardinality.valueOf(StringUtils.trim(cursor.collectDescendantText(false))));
+
+      } else if (StringUtils.equalsIgnoreCase("status", nodeName)) {
+        rule.setStatus(StringUtils.trim(cursor.collectDescendantText(false)));
+
+      } else if (StringUtils.equalsIgnoreCase("param", nodeName)) {
+        processParameter(rule, cursor);
+      }
+    }
+    if (Strings.isNullOrEmpty(rule.getKey())) {
+      throw new SonarException("Node <key> is missing in <rule>");
+    }
+  }
+
+  private static void processParameter(Rule rule, SMInputCursor ruleC) throws XMLStreamException {
+    RuleParam param = rule.createParameter();
+
+    String keyAttribute = ruleC.getAttrValue("key");
+    if (StringUtils.isNotBlank(keyAttribute)) {
+      /* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
+      param.setKey(StringUtils.trim(keyAttribute));
+    }
+
+    String typeAttribute = ruleC.getAttrValue("type");
+    if (StringUtils.isNotBlank(typeAttribute)) {
+      /* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
+      param.setType(type(StringUtils.trim(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.setKey(propText);
+
+      } else if (StringUtils.equalsIgnoreCase("description", propNodeName)) {
+        param.setDescription(propText);
+
+      } else if (StringUtils.equalsIgnoreCase("type", propNodeName)) {
+        param.setType(type(propText));
+
+      } else if (StringUtils.equalsIgnoreCase("defaultValue", propNodeName)) {
+        param.setDefaultValue(propText);
+      }
+    }
+    if (Strings.isNullOrEmpty(param.getKey())) {
+      throw new SonarException("Node <key> is missing in <param>");
+    }
+  }
+
+  private static Map<String, String> typeMapWithDeprecatedValues() {
+    Map<String, String> map = Maps.newHashMap();
+    map.put("i", PropertyType.INTEGER.name());
+    map.put("s", PropertyType.STRING.name());
+    map.put("b", PropertyType.BOOLEAN.name());
+    map.put("r", PropertyType.REGULAR_EXPRESSION.name());
+    map.put("s{}", "s{}");
+    map.put("i{}", "i{}");
+    for (PropertyType propertyType : PropertyType.values()) {
+      map.put(propertyType.name(), propertyType.name());
+    }
+    return map;
+  }
+
+  @VisibleForTesting
+  static String type(String type) {
+    String validType = TYPE_MAP.get(type);
+    if (null != validType) {
+      return validType;
+    }
+
+    if (type.matches(".\\[.+\\]")) {
+      return type;
+    }
+    throw new SonarException("Invalid property type [" + type + "]");
+  }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rule/AnnotationRuleDefinitions.java b/sonar-plugin-api/src/main/java/org/sonar/api/rule/AnnotationRuleDefinitions.java
deleted file mode 100644 (file)
index 4fda654..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.api.utils.SonarException;
-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 AnnotationRuleDefinitions {
-
-  private static final Logger LOG = LoggerFactory.getLogger(AnnotationRuleDefinitions.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);
-  }
-}
index 071d83c7ce6bd35a1046c39e9c3b443bb7c36e63..98d07f53cfb7d951b9a9cbc9ecb8d1eaaa1c5270 100644 (file)
@@ -26,6 +26,7 @@ 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;
@@ -92,6 +93,14 @@ public interface RuleDefinitions extends ServerExtension {
      */
     void loadAnnotatedClasses(Class... classes);
 
+    /**
+     * Reads definitions of rules from a XML file. Format is :
+     * <pre>
+     *
+     * </pre>
+     */
+    void loadXml(InputStream xmlInput, String encoding);
+
     void done();
   }
 
@@ -133,12 +142,14 @@ public interface RuleDefinitions extends ServerExtension {
       return newRule;
     }
 
-    /**
-     * Load definitions from classes annotated with #{@link org.sonar.check.Rule} of library sonar-check-api
-     */
     @Override
     public void loadAnnotatedClasses(Class... classes) {
-      new AnnotationRuleDefinitions().loadRules(this, classes);
+      new RuleDefinitionsFromAnnotations().loadRules(this, classes);
+    }
+
+    @Override
+    public void loadXml(InputStream xmlInput, String encoding) {
+      new RuleDefinitionsFromXml().loadRules(this, xmlInput, encoding);
     }
 
     @Override
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
new file mode 100644 (file)
index 0000000..94a8f5b
--- /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.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
new file mode 100644 (file)
index 0000000..b53904c
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * 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;
+  }
+}
index 0330cab1aa5d387da7b312d0ee67a5f258443ab6..953962c5fc27dba5b72143310b8fbb3b949ada3a 100644 (file)
@@ -23,6 +23,8 @@ import org.apache.commons.lang.StringEscapeUtils;
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.PropertyType;
 
+import javax.annotation.Nullable;
+
 /**
  * @since 4.2
  */
@@ -73,9 +75,26 @@ public final class RuleParamType {
     return new RuleParamType(type, acceptedValues);
   }
 
-  public static RuleParamType parse(String key) {
-    String format = StringUtils.substringBefore(key, FIELD_SEPARATOR);
-    String options = StringUtils.substringAfter(key, FIELD_SEPARATOR);
+  // 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)) {
+      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);
     }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/XMLRuleParser.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/XMLRuleParser.java
deleted file mode 100644 (file)
index f4dcc06..0000000
+++ /dev/null
@@ -1,226 +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.rules;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Strings;
-import com.google.common.collect.Maps;
-import com.google.common.io.Closeables;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang.CharEncoding;
-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.PropertyType;
-import org.sonar.api.ServerComponent;
-import org.sonar.api.utils.SonarException;
-import org.sonar.check.Cardinality;
-
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.XMLStreamException;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @since 2.3
- */
-public final class XMLRuleParser implements ServerComponent {
-  private static final Map<String, String> TYPE_MAP = typeMapWithDeprecatedValues();
-
-  public List<Rule> parse(File file) {
-    Reader reader = null;
-    try {
-      reader = new InputStreamReader(FileUtils.openInputStream(file), CharEncoding.UTF_8);
-      return parse(reader);
-
-    } catch (IOException e) {
-      throw new SonarException("Fail to load the file: " + file, e);
-
-    } finally {
-      Closeables.closeQuietly(reader);
-    }
-  }
-
-  /**
-   * Warning : the input stream is closed in this method
-   */
-  public List<Rule> parse(InputStream input) {
-    Reader reader = null;
-    try {
-      reader = new InputStreamReader(input, CharEncoding.UTF_8);
-      return parse(reader);
-
-    } catch (IOException e) {
-      throw new SonarException("Fail to load the xml stream", e);
-
-    } finally {
-      Closeables.closeQuietly(reader);
-    }
-  }
-
-  public List<Rule> parse(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>
-      List<Rule> rules = new ArrayList<Rule>();
-
-      SMInputCursor rulesC = rootC.childElementCursor("rule");
-      while (rulesC.getNext() != null) {
-        // <rule>
-        Rule rule = Rule.create();
-        rules.add(rule);
-
-        processRule(rule, rulesC);
-      }
-      return rules;
-
-    } catch (XMLStreamException e) {
-      throw new SonarException("XML is not valid", e);
-    }
-  }
-
-  private static void processRule(Rule rule, SMInputCursor ruleC) throws XMLStreamException {
-    /* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
-    String keyAttribute = ruleC.getAttrValue("key");
-    if (StringUtils.isNotBlank(keyAttribute)) {
-      rule.setKey(StringUtils.trim(keyAttribute));
-    }
-
-    /* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
-    String priorityAttribute = ruleC.getAttrValue("priority");
-    if (StringUtils.isNotBlank(priorityAttribute)) {
-      rule.setSeverity(RulePriority.valueOf(StringUtils.trim(priorityAttribute)));
-    }
-
-    SMInputCursor cursor = ruleC.childElementCursor();
-
-    while (cursor.getNext() != null) {
-      String nodeName = cursor.getLocalName();
-
-      if (StringUtils.equalsIgnoreCase("name", nodeName)) {
-        rule.setName(StringUtils.trim(cursor.collectDescendantText(false)));
-
-      } else if (StringUtils.equalsIgnoreCase("description", nodeName)) {
-        rule.setDescription(StringUtils.trim(cursor.collectDescendantText(false)));
-
-      } else if (StringUtils.equalsIgnoreCase("key", nodeName)) {
-        rule.setKey(StringUtils.trim(cursor.collectDescendantText(false)));
-
-      } else if (StringUtils.equalsIgnoreCase("configKey", nodeName)) {
-        rule.setConfigKey(StringUtils.trim(cursor.collectDescendantText(false)));
-
-      } else if (StringUtils.equalsIgnoreCase("priority", nodeName)) {
-        rule.setSeverity(RulePriority.valueOf(StringUtils.trim(cursor.collectDescendantText(false))));
-
-      } else if (StringUtils.equalsIgnoreCase("cardinality", nodeName)) {
-        rule.setCardinality(Cardinality.valueOf(StringUtils.trim(cursor.collectDescendantText(false))));
-
-      } else if (StringUtils.equalsIgnoreCase("status", nodeName)) {
-        rule.setStatus(StringUtils.trim(cursor.collectDescendantText(false)));
-
-      } else if (StringUtils.equalsIgnoreCase("param", nodeName)) {
-        processParameter(rule, cursor);
-      }
-    }
-    if (Strings.isNullOrEmpty(rule.getKey())) {
-      throw new SonarException("Node <key> is missing in <rule>");
-    }
-  }
-
-  private static void processParameter(Rule rule, SMInputCursor ruleC) throws XMLStreamException {
-    RuleParam param = rule.createParameter();
-
-    String keyAttribute = ruleC.getAttrValue("key");
-    if (StringUtils.isNotBlank(keyAttribute)) {
-      /* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
-      param.setKey(StringUtils.trim(keyAttribute));
-    }
-
-    String typeAttribute = ruleC.getAttrValue("type");
-    if (StringUtils.isNotBlank(typeAttribute)) {
-      /* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
-      param.setType(type(StringUtils.trim(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.setKey(propText);
-
-      } else if (StringUtils.equalsIgnoreCase("description", propNodeName)) {
-        param.setDescription(propText);
-
-      } else if (StringUtils.equalsIgnoreCase("type", propNodeName)) {
-        param.setType(type(propText));
-
-      } else if (StringUtils.equalsIgnoreCase("defaultValue", propNodeName)) {
-        param.setDefaultValue(propText);
-      }
-    }
-    if (Strings.isNullOrEmpty(param.getKey())) {
-      throw new SonarException("Node <key> is missing in <param>");
-    }
-  }
-
-  private static Map<String, String> typeMapWithDeprecatedValues() {
-    Map<String, String> map = Maps.newHashMap();
-    map.put("i", PropertyType.INTEGER.name());
-    map.put("s", PropertyType.STRING.name());
-    map.put("b", PropertyType.BOOLEAN.name());
-    map.put("r", PropertyType.REGULAR_EXPRESSION.name());
-    map.put("s{}", "s{}");
-    map.put("i{}", "i{}");
-    for (PropertyType propertyType : PropertyType.values()) {
-      map.put(propertyType.name(), propertyType.name());
-    }
-    return map;
-  }
-
-  @VisibleForTesting
-  static String type(String type) {
-    String validType = TYPE_MAP.get(type);
-    if (null != validType) {
-      return validType;
-    }
-
-    if (type.matches(".\\[.+\\]")) {
-      return type;
-    }
-    throw new SonarException("Invalid property type [" + type + "]");
-  }
-
-}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/rule/AnnotationRuleDefinitionsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/rule/AnnotationRuleDefinitionsTest.java
deleted file mode 100644 (file)
index 754878c..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 AnnotationRuleDefinitionsTest {
-
-  @org.junit.Rule
-  public final ExpectedException exception = 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() {
-    exception.expect(IllegalArgumentException.class);
-    exception.expectMessage("Invalid property type [INVALID]");
-
-    load(RuleWithInvalidPropertyType.class);
-  }
-
-  @Test
-  public void should_recognize_type() {
-    assertThat(AnnotationRuleDefinitions.guessType(Integer.class)).isEqualTo(RuleParamType.INTEGER);
-    assertThat(AnnotationRuleDefinitions.guessType(int.class)).isEqualTo(RuleParamType.INTEGER);
-    assertThat(AnnotationRuleDefinitions.guessType(Float.class)).isEqualTo(RuleParamType.FLOAT);
-    assertThat(AnnotationRuleDefinitions.guessType(float.class)).isEqualTo(RuleParamType.FLOAT);
-    assertThat(AnnotationRuleDefinitions.guessType(Boolean.class)).isEqualTo(RuleParamType.BOOLEAN);
-    assertThat(AnnotationRuleDefinitions.guessType(boolean.class)).isEqualTo(RuleParamType.BOOLEAN);
-    assertThat(AnnotationRuleDefinitions.guessType(String.class)).isEqualTo(RuleParamType.STRING);
-    assertThat(AnnotationRuleDefinitions.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 AnnotationRuleDefinitions().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/RuleDefinitionsFromAnnotationsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleDefinitionsFromAnnotationsTest.java
new file mode 100644 (file)
index 0000000..e3007cc
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * 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
new file mode 100644 (file)
index 0000000..97fa34f
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * 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();
+  }
+}
index f78bdba7ffa69444d9470cf51b0eb98ea874c044..b4da1f212744c7a435b3854a747a8b4488a5a4cb 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.api.rule;
 
 import org.junit.Test;
+import org.sonar.api.PropertyType;
 
 import static org.fest.assertions.Assertions.assertThat;
 
@@ -73,4 +74,19 @@ public class RuleParamTypeTest {
     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);
+    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/rules/XMLRuleParserTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/rules/XMLRuleParserTest.java
deleted file mode 100644 (file)
index 5dc4796..0000000
+++ /dev/null
@@ -1,137 +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.rules;
-
-import org.hamcrest.core.Is;
-import org.junit.Test;
-import org.sonar.api.PropertyType;
-import org.sonar.api.utils.SonarException;
-import org.sonar.check.Cardinality;
-
-import java.io.File;
-import java.io.StringReader;
-import java.util.List;
-
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static org.fest.assertions.Assertions.assertThat;
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsNot.not;
-import static org.hamcrest.core.IsNull.nullValue;
-import static org.junit.Assert.assertThat;
-
-public class XMLRuleParserTest {
-
-  @Test
-  public void should_parse_xml() throws Exception {
-    File file = new File(getClass().getResource("/org/sonar/api/rules/XMLRuleParserTest/rules.xml").toURI());
-    List<Rule> rules = new XMLRuleParser().parse(file);
-    assertThat(rules.size(), is(2));
-
-    Rule rule = rules.get(0);
-    assertThat(rule.getName(), is("Local Variable Name"));
-    assertThat(rule.getDescription(), is("Checks that local, non-final variable names conform to a format specified by the format property."));
-    assertThat(rule.getSeverity(), Is.is(RulePriority.BLOCKER));
-    assertThat(rule.getCardinality(), Is.is(Cardinality.MULTIPLE));
-    assertThat(rule.getConfigKey(), is("Checker/TreeWalker/LocalVariableName"));
-    assertThat(rule.getStatus(), is("READY"));
-
-    assertThat(rule.getParams().size(), is(2));
-    RuleParam prop = rule.getParam("ignore");
-    assertThat(prop.getKey(), is("ignore"));
-    assertThat(prop.getDescription(), is("Ignore ?"));
-    assertThat(prop.getDefaultValue(), is("false"));
-
-    Rule minimalRule = rules.get(1);
-    assertThat(minimalRule.getKey(), is("com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck"));
-    assertThat(minimalRule.getParams().size(), is(0));
-
-  }
-
-  @Test(expected = SonarException.class)
-  public void should_fail_if_missing_rule_key() {
-    new XMLRuleParser().parse(new StringReader("<rules><rule><name>Foo</name></rule></rules>"));
-  }
-
-  @Test
-  public void should_rule_name_should_be_optional() {
-    List<Rule> rules = new XMLRuleParser().parse(new StringReader("<rules><rule><key>foo</key></rule></rules>"));
-    assertThat(rules.get(0).getName(), nullValue());
-  }
-
-  @Test(expected = SonarException.class)
-  public void should_fail_if_missing_property_key() {
-    new XMLRuleParser().parse(new StringReader("<rules><rule><key>foo</key><name>Foo</name><param></param></rule></rules>"));
-  }
-
-  @Test
-  public void should_read_rule_parameter_type() {
-    assertThat(typeOf("<rules><rule><key>foo</key><name>Foo</name><param><key>key</key><type>STRING</type></param></rule></rules>")).isEqualTo(PropertyType.STRING.name());
-    assertThat(typeOf("<rules><rule><key>foo</key><name>Foo</name><param><key>key</key><type>INTEGER</type></param></rule></rules>")).isEqualTo(PropertyType.INTEGER.name());
-    assertThat(typeOf("<rules><rule><key>foo</key><name>Foo</name><param><key>key</key><type>s</type></param></rule></rules>")).isEqualTo(PropertyType.STRING.name());
-    assertThat(typeOf("<rules><rule><key>foo</key><name>Foo</name><param><key>key</key><type>s{}</type></param></rule></rules>")).isEqualTo("s{}");
-    assertThat(typeOf("<rules><rule><key>foo</key><name>Foo</name><param><key>key</key><type>i{}</type></param></rule></rules>")).isEqualTo("i{}");
-    assertThat(typeOf("<rules><rule><key>foo</key><name>Foo</name><param><key>key</key><type>s[foo,bar]</type></param></rule></rules>")).isEqualTo("s[foo,bar]");
-  }
-
-  static String typeOf(String xml) {
-    return getOnlyElement(new XMLRuleParser().parse(new StringReader(xml))).getParam("key").getType();
-  }
-
-  @Test(expected = SonarException.class)
-  public void should_fail_on_invalid_rule_parameter_type() {
-    new XMLRuleParser().parse(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() {
-    List<Rule> rules = new XMLRuleParser().parse(getClass().getResourceAsStream("/org/sonar/api/rules/XMLRuleParserTest/utf8.xml"));
-    assertThat(rules.size(), is(1));
-    Rule rule = rules.get(0);
-    assertThat(rule.getKey(), is("com.puppycrawl.tools.checkstyle.checks.naming.LocalVariableNameCheck"));
-    assertThat(rule.getName(), is("M & M"));
-    assertThat(rule.getDescription().charAt(0), is('\u00E9'));
-    assertThat(rule.getDescription().charAt(1), is('\u00E0'));
-    assertThat(rule.getDescription().charAt(2), is('\u0026'));
-  }
-
-  @Test
-  public void should_support_deprecated_format() {
-    // the deprecated format uses some attributes instead of nodes
-    List<Rule> rules = new XMLRuleParser().parse(getClass().getResourceAsStream("/org/sonar/api/rules/XMLRuleParserTest/deprecated.xml"));
-    assertThat(rules.size(), is(1));
-    Rule rule = rules.get(0);
-    assertThat(rule.getSeverity(), Is.is(RulePriority.CRITICAL));
-    assertThat(rule.getKey(), is("org.sonar.it.checkstyle.MethodsCountCheck"));
-    assertThat(rule.getParam("minMethodsCount"), not(nullValue()));
-  }
-
-  @Test
-  public void should_read_rule_status() {
-    List<Rule> rules = new XMLRuleParser().parse(new StringReader(
-        "<rules>"+
-            "<rule><key>foo</key><status>READY</status></rule>"+
-            "<rule><key>foo</key><status>BETA</status></rule>"+
-            "<rule><key>foo</key><status>DEPRECATED</status></rule>"+
-            "</rules>"));
-    assertThat(rules.get(0).getStatus(), is("READY"));
-    assertThat(rules.get(1).getStatus(), is("BETA"));
-    assertThat(rules.get(2).getStatus(), is("DEPRECATED"));
-  }
-}
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
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/rule/RuleDefinitionsFromXmlTest/rules.xml b/sonar-plugin-api/src/test/resources/org/sonar/api/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/rule/RuleDefinitionsFromXmlTest/utf8.xml b/sonar-plugin-api/src/test/resources/org/sonar/api/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>
diff --git a/sonar-plugin-api/src/test/resources/org/sonar/api/rules/XMLRuleParserTest/deprecated.xml b/sonar-plugin-api/src/test/resources/org/sonar/api/rules/XMLRuleParserTest/deprecated.xml
deleted file mode 100644 (file)
index 44cf56d..0000000
+++ /dev/null
@@ -1,11 +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>
-    <category name="Usability"/>
-    <description>Count methods.</description>
-    <param key="minMethodsCount" type="i">
-      <description>Le nombre minimum de méthodes. 10 par défaut.</description>
-    </param>
-  </rule>
-</rules>
\ No newline at end of file
diff --git a/sonar-plugin-api/src/test/resources/org/sonar/api/rules/XMLRuleParserTest/rules.xml b/sonar-plugin-api/src/test/resources/org/sonar/api/rules/XMLRuleParserTest/rules.xml
deleted file mode 100644 (file)
index 3ae3d4d..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-<rules>
-  <rule>
-    <!-- with exhaustive fields -->
-    <key>com.puppycrawl.tools.checkstyle.checks.naming.LocalVariableNameCheck</key>
-    <name>Local Variable Name</name>
-    <description>
-      <![CDATA[Checks that local, non-final variable names conform to a format specified by the format property.]]>
-    </description>
-    <category name="Efficiency" />
-    <configKey>Checker/TreeWalker/LocalVariableName</configKey>
-    <priority>BLOCKER</priority>
-    <cardinality>MULTIPLE</cardinality>
-    <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>com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck</key>
-    <name>Magic Number</name>
-    <description>
-      <![CDATA[Checks for magic numbers.]]>
-    </description>
-    <category name="Maintainability" />
-  </rule>
-</rules>
\ No newline at end of file
diff --git a/sonar-plugin-api/src/test/resources/org/sonar/api/rules/XMLRuleParserTest/utf8.xml b/sonar-plugin-api/src/test/resources/org/sonar/api/rules/XMLRuleParserTest/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>
index 901811551525065094b3f3bca8a0217e71c284b1..d8d373e639fed1a380278f58965312e12d687e8c 100644 (file)
@@ -28,10 +28,10 @@ import org.sonar.check.Cardinality;
 import org.sonar.core.i18n.RuleI18nManager;
 
 import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
 
 /**
  * Inject deprecated RuleRepository into RuleDefinitions for backward-compatibility.
+ *
  * @since 4.2
  */
 public class DeprecatedRuleDefinitions implements RuleDefinitions {
@@ -65,12 +65,12 @@ public class DeprecatedRuleDefinitions implements RuleDefinitions {
         newRule.setMetadata(rule.getConfigKey());
         newRule.setTemplate(Cardinality.MULTIPLE.equals(rule.getCardinality()));
         newRule.setDefaultSeverity(rule.getSeverity().toString());
-        newRule.setStatus(rule.getStatus()==null ? Status.READY : Status.valueOf(rule.getStatus()));
+        newRule.setStatus(rule.getStatus() == null ? Status.READY : Status.valueOf(rule.getStatus()));
         for (RuleParam param : rule.getParams()) {
           NewParam newParam = newRule.newParam(param.getKey());
           newParam.setDefaultValue(param.getDefaultValue());
           newParam.setDescription(paramDescription(repository.getKey(), rule.getKey(), param));
-          newParam.setType(paramType(param.getType()));
+          newParam.setType(RuleParamType.parse(param.getType()));
         }
       }
       newRepository.done();
@@ -103,24 +103,4 @@ public class DeprecatedRuleDefinitions implements RuleDefinitions {
     );
     return StringUtils.defaultIfBlank(desc, null);
   }
-
-  private RuleParamType paramType(@Nullable String s) {
-    if (StringUtils.isBlank(s)) {
-      return RuleParamType.STRING;
-    }
-    if ("i".equals(s) || "i{}".equals(s)) {
-      return RuleParamType.INTEGER;
-    }
-    if ("s".equals(s) || "s{}".equals(s) || "r".equals(s)) {
-      return RuleParamType.STRING;
-    }
-    if ("b".equals(s)) {
-      return RuleParamType.BOOLEAN;
-    }
-    if (s.startsWith("s[")) {
-      String values = StringUtils.substringBetween(s, "[", "]");
-      return RuleParamType.ofValues(StringUtils.split(values, ','));
-    }
-    return RuleParamType.parse(s);
-  }
 }