Browse Source

SONAR-4908 add RuleDefinitions#loadXml() and depreciate org.sonar.api.rules.XMLRuleParser

tags/4.2
Simon Brandhof 10 years ago
parent
commit
7d6d0d00cd

sonar-plugin-api/src/main/java/org/sonar/api/rules/XMLRuleParser.java → sonar-deprecated/src/main/java/org/sonar/api/rules/XMLRuleParser.java View File

@@ -48,7 +48,9 @@ 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();


+ 15
- 4
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitions.java View 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

sonar-plugin-api/src/main/java/org/sonar/api/rule/AnnotationRuleDefinitions.java → sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitionsFromAnnotations.java View File

@@ -28,7 +28,6 @@ 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;
@@ -39,11 +38,12 @@ import java.util.List;
* </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 {
class RuleDefinitionsFromAnnotations {

private static final Logger LOG = LoggerFactory.getLogger(AnnotationRuleDefinitions.class);
private static final Logger LOG = LoggerFactory.getLogger(RuleDefinitionsFromAnnotations.class);

void loadRules(RuleDefinitions.NewRepository repo, Class... annotatedClasses) {
for (Class annotatedClass : annotatedClasses) {

+ 184
- 0
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleDefinitionsFromXml.java View File

@@ -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;
}
}

+ 22
- 3
sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleParamType.java View 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);
}

sonar-plugin-api/src/test/java/org/sonar/api/rule/AnnotationRuleDefinitionsTest.java → sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleDefinitionsFromAnnotationsTest.java View File

@@ -26,10 +26,10 @@ import org.sonar.check.Priority;

import static org.fest.assertions.Assertions.assertThat;

public class AnnotationRuleDefinitionsTest {
public class RuleDefinitionsFromAnnotationsTest {

@org.junit.Rule
public final ExpectedException exception = ExpectedException.none();
public final ExpectedException thrown = ExpectedException.none();

@Test
public void rule_with_property() {
@@ -74,22 +74,22 @@ public class AnnotationRuleDefinitionsTest {
@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]");
thrown.expect(IllegalArgumentException.class);
thrown.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);
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
@@ -116,7 +116,7 @@ public class AnnotationRuleDefinitionsTest {
private RuleDefinitions.Repository load(Class annotatedClass) {
RuleDefinitions.Context context = new RuleDefinitions.Context();
RuleDefinitions.NewRepository newRepository = context.newRepository("squid", "java");
new AnnotationRuleDefinitions().loadRules(newRepository, annotatedClass);
new RuleDefinitionsFromAnnotations().loadRules(newRepository, annotatedClass);
newRepository.done();
return context.repository("squid");
}

+ 122
- 0
sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleDefinitionsFromXmlTest.java View File

@@ -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();
}
}

+ 16
- 0
sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleParamTypeTest.java View 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");
}
}

+ 0
- 137
sonar-plugin-api/src/test/java/org/sonar/api/rules/XMLRuleParserTest.java View File

@@ -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"));
}
}

sonar-plugin-api/src/test/resources/org/sonar/api/rules/XMLRuleParserTest/deprecated.xml → sonar-plugin-api/src/test/resources/org/sonar/api/rule/RuleDefinitionsFromXmlTest/deprecated.xml View File

@@ -2,10 +2,9 @@
<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>
<description>Count methods</description>
<param key="minMethodsCount" type="i">
<description>Le nombre minimum de méthodes. 10 par défaut.</description>
<description>description of param</description>
</param>
</rule>
</rules>

sonar-plugin-api/src/test/resources/org/sonar/api/rules/XMLRuleParserTest/rules.xml → sonar-plugin-api/src/test/resources/org/sonar/api/rule/RuleDefinitionsFromXmlTest/rules.xml View File

@@ -1,15 +1,15 @@
<rules>
<rule>
<!-- with exhaustive fields -->
<key>com.puppycrawl.tools.checkstyle.checks.naming.LocalVariableNameCheck</key>
<name>Local Variable Name</name>
<key>complete</key>
<name>Complete</name>
<description>
<![CDATA[Checks that local, non-final variable names conform to a format specified by the format property.]]>
<![CDATA[Description of Complete]]>
</description>
<category name="Efficiency" />
<configKey>Checker/TreeWalker/LocalVariableName</configKey>
<priority>BLOCKER</priority>
<severity>BLOCKER</severity>
<cardinality>MULTIPLE</cardinality>
<status>BETA</status>
<param>
<key>tokens</key>
<description>
@@ -30,11 +30,10 @@

<rule>
<!-- with only required fields -->
<key>com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck</key>
<name>Magic Number</name>
<key>minimal</key>
<name>Minimal</name>
<description>
<![CDATA[Checks for magic numbers.]]>
<![CDATA[Description of Minimal]]>
</description>
<category name="Maintainability" />
</rule>
</rules>

sonar-plugin-api/src/test/resources/org/sonar/api/rules/XMLRuleParserTest/utf8.xml → sonar-plugin-api/src/test/resources/org/sonar/api/rule/RuleDefinitionsFromXmlTest/utf8.xml View File


+ 3
- 23
sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRuleDefinitions.java View 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);
}
}

Loading…
Cancel
Save