--- /dev/null
+/*
+ * 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 + "]");
+ }
+
+}
+++ /dev/null
-/*
- * 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);
- }
-}
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;
*/
void loadAnnotatedClasses(Class... classes);
+ /**
+ * Reads definitions of rules from a XML file. Format is :
+ * <pre>
+ *
+ * </pre>
+ */
+ void loadXml(InputStream xmlInput, String encoding);
+
void done();
}
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
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
import org.apache.commons.lang.StringUtils;
import org.sonar.api.PropertyType;
+import javax.annotation.Nullable;
+
/**
* @since 4.2
*/
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);
}
+++ /dev/null
-/*
- * 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 + "]");
- }
-
-}
+++ /dev/null
-/*
- * 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;
- }
-}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
package org.sonar.api.rule;
import org.junit.Test;
+import org.sonar.api.PropertyType;
import static org.fest.assertions.Assertions.assertThat;
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");
+ }
}
+++ /dev/null
-/*
- * 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"));
- }
-}
--- /dev/null
+<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
--- /dev/null
+<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
--- /dev/null
+<rules>
+ <rule>
+ <key>com.puppycrawl.tools.checkstyle.checks.naming.LocalVariableNameCheck</key>
+ <priority>BLOCKER</priority>
+ <configKey>Checker/TreeWalker/LocalVariableName</configKey>
+ <name>M & M</name>
+ <description>
+ <![CDATA[éà&]]>
+ </description>
+ </rule>
+</rules>
+++ /dev/null
-<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
+++ /dev/null
-<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
+++ /dev/null
-<rules>
- <rule>
- <key>com.puppycrawl.tools.checkstyle.checks.naming.LocalVariableNameCheck</key>
- <priority>BLOCKER</priority>
- <configKey>Checker/TreeWalker/LocalVariableName</configKey>
- <name>M & M</name>
- <description>
- <![CDATA[éà&]]>
- </description>
- </rule>
-</rules>
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 {
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();
);
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);
- }
}