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