aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-plugin-api
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2021-05-27 15:10:28 +0200
committersonartech <sonartech@sonarsource.com>2021-05-28 20:21:38 +0000
commit95d416a5748d0dd9255d730b6ebf357c5edc628e (patch)
tree4d6a6d5771494d9d59f81e0a544ad1d4f4719bdf /sonar-plugin-api
parent2fb8b360e3eb2252ea9c2e3f984f1ddd0130fc90 (diff)
downloadsonarqube-95d416a5748d0dd9255d730b6ebf357c5edc628e.tar.gz
sonarqube-95d416a5748d0dd9255d730b6ebf357c5edc628e.zip
SONAR-14882 Remove Staxmate and Woodstox dependencies from plugin API
Diffstat (limited to 'sonar-plugin-api')
-rw-r--r--sonar-plugin-api/build.gradle4
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileParser.java192
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/XMLRuleParser.java185
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinitionXmlLoader.java221
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/text/XmlWriter.java2
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/utils/text/XmlWriterTest.java10
6 files changed, 357 insertions, 257 deletions
diff --git a/sonar-plugin-api/build.gradle b/sonar-plugin-api/build.gradle
index bff7eec5a3b..04612e58427 100644
--- a/sonar-plugin-api/build.gradle
+++ b/sonar-plugin-api/build.gradle
@@ -16,10 +16,6 @@ dependencies {
// shaded, but not relocated
compile project(':sonar-check-api')
- shadow 'org.codehaus.staxmate:staxmate'
- shadow 'org.codehaus.woodstox:stax2-api'
- shadow 'org.codehaus.woodstox:woodstox-core-lgpl'
-
compileOnly 'ch.qos.logback:logback-classic'
compileOnly 'com.google.code.findbugs:jsr305'
compileOnly 'javax.servlet:javax.servlet-api'
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileParser.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileParser.java
index d2459c75785..bf78c8dd0df 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileParser.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileParser.java
@@ -25,12 +25,13 @@ import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
+import javax.annotation.Nullable;
+import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
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.ce.ComputeEngineSide;
import org.sonar.api.rules.ActiveRule;
import org.sonar.api.rules.Rule;
@@ -49,6 +50,11 @@ import org.sonar.api.utils.ValidationMessages;
@ComputeEngineSide
public class XMLProfileParser {
+ private static final String ELEMENT_PROFILE = "profile";
+ private static final String ELEMENT_RULES = "rules";
+ private static final String ELEMENT_RULE = "rule";
+ private static final String ELEMENT_PARAMETERS = "parameters";
+ private static final String ELEMENT_PARAMETER = "parameter";
private final RuleFinder ruleFinder;
/**
@@ -70,24 +76,16 @@ public class XMLProfileParser {
}
}
- public RulesProfile parse(Reader reader, ValidationMessages messages) {
+ public RulesProfile parse(Reader inputReader, ValidationMessages messages) {
RulesProfile profile = RulesProfile.create();
- SMInputFactory inputFactory = initStax();
+ XMLInputFactory inputFactory = initStax();
try {
- SMHierarchicCursor rootC = inputFactory.rootElementCursor(reader);
- rootC.advance(); // <profile>
- SMInputCursor cursor = rootC.childElementCursor();
- while (cursor.getNext() != null) {
- String nodeName = cursor.getLocalName();
- if (StringUtils.equals("rules", nodeName)) {
- SMInputCursor rulesCursor = cursor.childElementCursor("rule");
- processRules(rulesCursor, profile, messages);
-
- } else if (StringUtils.equals("name", nodeName)) {
- profile.setName(StringUtils.trim(cursor.collectDescendantText(false)));
-
- } else if (StringUtils.equals("language", nodeName)) {
- profile.setLanguage(StringUtils.trim(cursor.collectDescendantText(false)));
+ final XMLEventReader reader = inputFactory.createXMLEventReader(inputReader);
+ while (reader.hasNext()) {
+ final XMLEvent event = reader.nextEvent();
+ if (event.isStartElement() && event.asStartElement().getName()
+ .getLocalPart().equals(ELEMENT_PROFILE)) {
+ parseProfile(profile, reader, messages);
}
}
} catch (XMLStreamException e) {
@@ -97,6 +95,42 @@ public class XMLProfileParser {
return profile;
}
+ private void parseProfile(RulesProfile profile, final XMLEventReader reader, ValidationMessages messages) throws XMLStreamException {
+ while (reader.hasNext()) {
+ final XMLEvent event = reader.nextEvent();
+ if (event.isEndElement() && event.asEndElement().getName().getLocalPart().equals(ELEMENT_PROFILE)) {
+ return;
+ }
+ if (event.isStartElement()) {
+ final StartElement element = event.asStartElement();
+ final String elementName = element.getName().getLocalPart();
+ if (ELEMENT_RULES.equals(elementName)) {
+ parseRules(profile, reader, messages);
+ } else if ("name".equals(elementName)) {
+ profile.setName(StringUtils.trim(reader.getElementText()));
+ } else if ("language".equals(elementName)) {
+ profile.setLanguage(StringUtils.trim(reader.getElementText()));
+ }
+ }
+ }
+ }
+
+ private void parseRules(RulesProfile profile, XMLEventReader reader, ValidationMessages messages) throws XMLStreamException {
+ while (reader.hasNext()) {
+ final XMLEvent event = reader.nextEvent();
+ if (event.isEndElement() && event.asEndElement().getName().getLocalPart().equals(ELEMENT_RULES)) {
+ return;
+ }
+ if (event.isStartElement()) {
+ final StartElement element = event.asStartElement();
+ final String elementName = element.getName().getLocalPart();
+ if (ELEMENT_RULE.equals(elementName)) {
+ parseRule(profile, reader, messages);
+ }
+ }
+ }
+ }
+
private static void checkProfile(RulesProfile profile, ValidationMessages messages) {
if (StringUtils.isBlank(profile.getName())) {
messages.addErrorText("The mandatory node <name> is missing.");
@@ -106,56 +140,55 @@ public class XMLProfileParser {
}
}
- private static SMInputFactory initStax() {
+ private static XMLInputFactory initStax() {
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);
- return new SMInputFactory(xmlFactory);
+ return xmlFactory;
}
- private void processRules(SMInputCursor rulesCursor, RulesProfile profile, ValidationMessages messages) throws XMLStreamException {
+ private void parseRule(RulesProfile profile, XMLEventReader reader, ValidationMessages messages) throws XMLStreamException {
Map<String, String> parameters = new HashMap<>();
- while (rulesCursor.getNext() != null) {
- SMInputCursor ruleCursor = rulesCursor.childElementCursor();
-
- String repositoryKey = null;
- String key = null;
- RulePriority priority = null;
- parameters.clear();
-
- while (ruleCursor.getNext() != null) {
- String nodeName = ruleCursor.getLocalName();
-
- if (StringUtils.equals("repositoryKey", nodeName)) {
- repositoryKey = StringUtils.trim(ruleCursor.collectDescendantText(false));
-
- } else if (StringUtils.equals("key", nodeName)) {
- key = StringUtils.trim(ruleCursor.collectDescendantText(false));
-
- } else if (StringUtils.equals("priority", nodeName)) {
- priority = RulePriority.valueOf(StringUtils.trim(ruleCursor.collectDescendantText(false)));
+ String repositoryKey = null;
+ String key = null;
+ RulePriority priority = null;
+ while (reader.hasNext()) {
+ final XMLEvent event = reader.nextEvent();
+ if (event.isEndElement() && event.asEndElement().getName().getLocalPart().equals(ELEMENT_RULE)) {
+ buildRule(profile, messages, parameters, repositoryKey, key, priority);
+ return;
+ }
- } else if (StringUtils.equals("parameters", nodeName)) {
- SMInputCursor propsCursor = ruleCursor.childElementCursor("parameter");
- processParameters(propsCursor, parameters);
+ if (event.isStartElement()) {
+ final StartElement element = event.asStartElement();
+ final String elementName = element.getName().getLocalPart();
+ if ("repositoryKey".equals(elementName)) {
+ repositoryKey = StringUtils.trim(reader.getElementText());
+ } else if ("key".equals(elementName)) {
+ key = StringUtils.trim(reader.getElementText());
+ } else if ("priority".equals(elementName)) {
+ priority = RulePriority.valueOf(StringUtils.trim(reader.getElementText()));
+ } else if (ELEMENT_PARAMETERS.equals(elementName)) {
+ processParameters(parameters, reader);
}
}
+ }
+ }
- Rule rule = ruleFinder.findByKey(repositoryKey, key);
- if (rule == null) {
- messages.addWarningText("Rule not found: " + ruleToString(repositoryKey, key));
-
- } else {
- ActiveRule activeRule = profile.activateRule(rule, priority);
- for (Map.Entry<String, String> entry : parameters.entrySet()) {
- if (rule.getParam(entry.getKey()) == null) {
- messages.addWarningText("The parameter '" + entry.getKey() + "' does not exist in the rule: " + ruleToString(repositoryKey, key));
- } else {
- activeRule.setParameter(entry.getKey(), entry.getValue());
- }
+ private void buildRule(RulesProfile profile, ValidationMessages messages, Map<String, String> parameters, String repositoryKey, String key, @Nullable RulePriority priority) {
+ Rule rule = ruleFinder.findByKey(repositoryKey, key);
+ if (rule == null) {
+ messages.addWarningText("Rule not found: " + ruleToString(repositoryKey, key));
+ } else {
+ ActiveRule activeRule = profile.activateRule(rule, priority);
+ for (Map.Entry<String, String> entry : parameters.entrySet()) {
+ if (rule.getParam(entry.getKey()) == null) {
+ messages.addWarningText("The parameter '" + entry.getKey() + "' does not exist in the rule: " + ruleToString(repositoryKey, key));
+ } else {
+ activeRule.setParameter(entry.getKey(), entry.getValue());
}
}
}
@@ -165,22 +198,41 @@ public class XMLProfileParser {
return "[repository=" + repositoryKey + ", key=" + key + "]";
}
- private static void processParameters(SMInputCursor propsCursor, Map<String, String> parameters) throws XMLStreamException {
- while (propsCursor.getNext() != null) {
- SMInputCursor propCursor = propsCursor.childElementCursor();
- String key = null;
- String value = null;
- while (propCursor.getNext() != null) {
- String nodeName = propCursor.getLocalName();
- if (StringUtils.equals("key", nodeName)) {
- key = StringUtils.trim(propCursor.collectDescendantText(false));
-
- } else if (StringUtils.equals("value", nodeName)) {
- value = StringUtils.trim(propCursor.collectDescendantText(false));
+ private static void processParameters(Map<String, String> parameters, XMLEventReader reader) throws XMLStreamException {
+ while (reader.hasNext()) {
+ final XMLEvent event = reader.nextEvent();
+ if (event.isEndElement() && event.asEndElement().getName().getLocalPart().equals(ELEMENT_PARAMETERS)) {
+ return;
+ }
+ if (event.isStartElement()) {
+ final StartElement element = event.asStartElement();
+ final String elementName = element.getName().getLocalPart();
+ if (ELEMENT_PARAMETER.equals(elementName)) {
+ processParameter(parameters, reader);
}
}
- if (key != null) {
- parameters.put(key, value);
+ }
+ }
+
+ private static void processParameter(Map<String, String> parameters, XMLEventReader reader) throws XMLStreamException {
+ String key = null;
+ String value = null;
+ while (reader.hasNext()) {
+ final XMLEvent event = reader.nextEvent();
+ if (event.isEndElement() && event.asEndElement().getName().getLocalPart().equals(ELEMENT_PARAMETER)) {
+ if (key != null) {
+ parameters.put(key, value);
+ }
+ return;
+ }
+ if (event.isStartElement()) {
+ final StartElement element = event.asStartElement();
+ final String elementName = element.getName().getLocalPart();
+ if ("key".equals(elementName)) {
+ key = StringUtils.trim(reader.getElementText());
+ } else if ("value".equals(elementName)) {
+ value = StringUtils.trim(reader.getElementText());
+ }
}
}
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/XMLRuleParser.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/XMLRuleParser.java
index 374d9ba9aad..466b3e4fbec 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/rules/XMLRuleParser.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/XMLRuleParser.java
@@ -28,13 +28,16 @@ import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
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.ce.ComputeEngineSide;
import org.sonar.api.server.ServerSide;
@@ -52,6 +55,9 @@ import static java.nio.charset.StandardCharsets.UTF_8;
@ComputeEngineSide
public final class XMLRuleParser {
private static final Map<String, String> TYPE_MAP = typeMapWithDeprecatedValues();
+ private static final String ELEMENT_RULES = "rules";
+ private static final String ELEMENT_RULE = "rule";
+ private static final String ELEMENT_PARAM = "param";
public List<Rule> parse(File file) {
try (Reader reader = new InputStreamReader(Files.newInputStream(file.toPath()), UTF_8)) {
@@ -74,26 +80,22 @@ public final class XMLRuleParser {
}
}
- public List<Rule> parse(Reader reader) {
+ public List<Rule> parse(Reader inputReader) {
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>
+ final XMLEventReader reader = xmlFactory.createXMLEventReader(inputReader);
List<Rule> rules = new ArrayList<>();
-
- SMInputCursor rulesC = rootC.childElementCursor("rule");
- while (rulesC.getNext() != null) {
- // <rule>
- Rule rule = Rule.create();
- rules.add(rule);
-
- processRule(rule, rulesC);
+ while (reader.hasNext()) {
+ final XMLEvent event = reader.nextEvent();
+ if (event.isStartElement() && event.asStartElement().getName()
+ .getLocalPart().equals(ELEMENT_RULES)) {
+ parseRules(rules, reader);
+ }
}
return rules;
@@ -102,51 +104,77 @@ public final class XMLRuleParser {
}
}
- private static void processRule(Rule rule, SMInputCursor ruleC) throws XMLStreamException {
+ private void parseRules(List<Rule> rules, XMLEventReader reader) throws XMLStreamException {
+ while (reader.hasNext()) {
+ final XMLEvent event = reader.nextEvent();
+ if (event.isEndElement() && event.asEndElement().getName().getLocalPart().equals(ELEMENT_RULES)) {
+ return;
+ }
+ if (event.isStartElement()) {
+ final StartElement element = event.asStartElement();
+ final String elementName = element.getName().getLocalPart();
+ switch (elementName) {
+ case ELEMENT_RULE:
+ Rule rule = Rule.create();
+ rules.add(rule);
+ parseRule(rule, element, reader);
+ break;
+ }
+ }
+ }
+ }
+
+ private static void parseRule(Rule rule, StartElement ruleElement, XMLEventReader reader) throws XMLStreamException {
/* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
- String keyAttribute = ruleC.getAttrValue("key");
- if (StringUtils.isNotBlank(keyAttribute)) {
- rule.setKey(StringUtils.trim(keyAttribute));
+ Attribute keyAttribute = ruleElement.getAttributeByName(new QName("key"));
+ if (keyAttribute != null && StringUtils.isNotBlank(keyAttribute.getValue())) {
+ rule.setKey(StringUtils.trim(keyAttribute.getValue()));
}
/* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
- String priorityAttribute = ruleC.getAttrValue("priority");
- if (StringUtils.isNotBlank(priorityAttribute)) {
- rule.setSeverity(RulePriority.valueOf(StringUtils.trim(priorityAttribute)));
+ Attribute priorityAttribute = ruleElement.getAttributeByName(new QName("priority"));
+ if (priorityAttribute != null && StringUtils.isNotBlank(priorityAttribute.getValue())) {
+ rule.setSeverity(RulePriority.valueOf(StringUtils.trim(priorityAttribute.getValue())));
}
List<String> tags = new ArrayList<>();
- 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);
-
- } else if (StringUtils.equalsIgnoreCase("tag", nodeName)) {
- tags.add(StringUtils.trim(cursor.collectDescendantText(false)));
+ while (reader.hasNext()) {
+ final XMLEvent event = reader.nextEvent();
+ if (event.isEndElement() && event.asEndElement().getName().getLocalPart().equals(ELEMENT_RULE)) {
+ return;
+ }
+ if (event.isStartElement()) {
+ final StartElement element = event.asStartElement();
+ final String elementName = element.getName().getLocalPart().toLowerCase(Locale.ENGLISH);
+ switch (elementName) {
+ case "name":
+ rule.setName(StringUtils.trim(reader.getElementText()));
+ break;
+ case "description":
+ rule.setDescription(StringUtils.trim(reader.getElementText()));
+ break;
+ case "key":
+ rule.setKey(StringUtils.trim(reader.getElementText()));
+ break;
+ case "configkey":
+ rule.setConfigKey(StringUtils.trim(reader.getElementText()));
+ break;
+ case "priority":
+ rule.setSeverity(RulePriority.valueOf(StringUtils.trim(reader.getElementText())));
+ break;
+ case "cardinality":
+ rule.setCardinality(Cardinality.valueOf(StringUtils.trim(reader.getElementText())));
+ break;
+ case "status":
+ rule.setStatus(StringUtils.trim(reader.getElementText()));
+ break;
+ case ELEMENT_PARAM:
+ processParameter(rule, element, reader);
+ break;
+ case "tag":
+ tags.add(StringUtils.trim(reader.getElementText()));
+ break;
+ }
}
}
if (rule.getKey() == null || rule.getKey().isEmpty()) {
@@ -155,36 +183,43 @@ public final class XMLRuleParser {
rule.setTags(tags.toArray(new String[tags.size()]));
}
- private static void processParameter(Rule rule, SMInputCursor ruleC) throws XMLStreamException {
+ private static void processParameter(Rule rule, StartElement paramElement, XMLEventReader reader) throws XMLStreamException {
RuleParam param = rule.createParameter();
- String keyAttribute = ruleC.getAttrValue("key");
- if (StringUtils.isNotBlank(keyAttribute)) {
+ Attribute keyAttribute = paramElement.getAttributeByName(new QName("key"));
+ if (keyAttribute != null && StringUtils.isNotBlank(keyAttribute.getValue())) {
/* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
- param.setKey(StringUtils.trim(keyAttribute));
+ param.setKey(StringUtils.trim(keyAttribute.getValue()));
}
- String typeAttribute = ruleC.getAttrValue("type");
- if (StringUtils.isNotBlank(typeAttribute)) {
+ Attribute typeAttribute = paramElement.getAttributeByName(new QName("type"));
+ if (typeAttribute != null && StringUtils.isNotBlank(typeAttribute.getValue())) {
/* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
- param.setType(type(StringUtils.trim(typeAttribute)));
+ param.setType(type(StringUtils.trim(typeAttribute.getValue())));
}
- 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);
+ while (reader.hasNext()) {
+ final XMLEvent event = reader.nextEvent();
+ if (event.isEndElement() && event.asEndElement().getName().getLocalPart().equals(ELEMENT_PARAM)) {
+ return;
+ }
+ if (event.isStartElement()) {
+ final StartElement element = event.asStartElement();
+ final String elementName = element.getName().getLocalPart().toLowerCase(Locale.ENGLISH);
+ switch (elementName) {
+ case "key":
+ param.setKey(StringUtils.trim(reader.getElementText()));
+ break;
+ case "description":
+ param.setDescription(StringUtils.trim(reader.getElementText()));
+ break;
+ case "type":
+ param.setType(type(StringUtils.trim(reader.getElementText())));
+ break;
+ case "defaultvalue":
+ param.setDefaultValue(StringUtils.trim(reader.getElementText()));
+ break;
+ }
}
}
if (StringUtils.isEmpty(param.getKey())) {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinitionXmlLoader.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinitionXmlLoader.java
index 33c178bb5b0..fb5e0494766 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinitionXmlLoader.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinitionXmlLoader.java
@@ -27,11 +27,14 @@ import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
-import org.codehaus.staxmate.SMInputFactory;
-import org.codehaus.staxmate.in.SMHierarchicCursor;
-import org.codehaus.staxmate.in.SMInputCursor;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+import org.apache.commons.lang.StringUtils;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rule.Severity;
@@ -42,7 +45,6 @@ import org.sonar.check.Cardinality;
import org.sonarsource.api.sonarlint.SonarLintSide;
import static java.lang.String.format;
-import static org.apache.commons.lang.StringUtils.equalsIgnoreCase;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import static org.apache.commons.lang.StringUtils.trim;
@@ -184,6 +186,10 @@ import static org.apache.commons.lang.StringUtils.trim;
@SonarLintSide
public class RulesDefinitionXmlLoader {
+ private static final String ELEMENT_RULES = "rules";
+ private static final String ELEMENT_RULE = "rule";
+ private static final String ELEMENT_PARAM = "param";
+
private enum DescriptionFormat {
HTML, MARKDOWN
}
@@ -191,6 +197,7 @@ public class RulesDefinitionXmlLoader {
/**
* Loads rules by reading the XML input stream. The input stream is not always closed by the method, so it
* should be handled by the caller.
+ *
* @since 4.3
*/
public void load(RulesDefinition.NewRepository repo, InputStream input, String encoding) {
@@ -211,32 +218,47 @@ public class RulesDefinitionXmlLoader {
/**
* Loads rules by reading the XML input stream. The reader is not closed by the method, so it
* should be handled by the caller.
+ *
* @since 4.3
*/
- public void load(RulesDefinition.NewRepository repo, Reader reader) {
+ public void load(RulesDefinition.NewRepository repo, Reader inputReader) {
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);
+ final XMLEventReader reader = xmlFactory.createXMLEventReader(inputReader);
+ while (reader.hasNext()) {
+ final XMLEvent event = reader.nextEvent();
+ if (event.isStartElement() && event.asStartElement().getName()
+ .getLocalPart().equals(ELEMENT_RULES)) {
+ parseRules(repo, reader);
+ }
}
-
} catch (XMLStreamException e) {
throw new IllegalStateException("XML is not valid", e);
}
}
- private static void processRule(RulesDefinition.NewRepository repo, SMInputCursor ruleC) throws XMLStreamException {
+ private static void parseRules(RulesDefinition.NewRepository repo, XMLEventReader reader) throws XMLStreamException {
+ while (reader.hasNext()) {
+ final XMLEvent event = reader.nextEvent();
+ if (event.isEndElement() && event.asEndElement().getName().getLocalPart().equals(ELEMENT_RULES)) {
+ return;
+ }
+ if (event.isStartElement()) {
+ final StartElement element = event.asStartElement();
+ final String elementName = element.getName().getLocalPart();
+ if (ELEMENT_RULE.equals(elementName)) {
+ processRule(repo, element, reader);
+ }
+ }
+ }
+ }
+
+ private static void processRule(RulesDefinition.NewRepository repo, StartElement ruleElement, XMLEventReader reader) throws XMLStreamException {
String key = null;
String name = null;
String description = null;
@@ -255,74 +277,71 @@ public class RulesDefinitionXmlLoader {
List<String> tags = new ArrayList<>();
/* BACKWARD COMPATIBILITY WITH VERY OLD FORMAT */
- String keyAttribute = ruleC.getAttrValue("key");
- if (isNotBlank(keyAttribute)) {
- key = trim(keyAttribute);
+ Attribute keyAttribute = ruleElement.getAttributeByName(new QName("key"));
+ if (keyAttribute != null && StringUtils.isNotBlank(keyAttribute.getValue())) {
+ key = trim(keyAttribute.getValue());
}
- String priorityAttribute = ruleC.getAttrValue("priority");
- if (isNotBlank(priorityAttribute)) {
- severity = trim(priorityAttribute);
+ Attribute priorityAttribute = ruleElement.getAttributeByName(new QName("priority"));
+ if (priorityAttribute != null && StringUtils.isNotBlank(priorityAttribute.getValue())) {
+ severity = trim(priorityAttribute.getValue());
}
- SMInputCursor cursor = ruleC.childElementCursor();
- while (cursor.getNext() != null) {
- String nodeName = cursor.getLocalName();
-
- if (equalsIgnoreCase("name", nodeName)) {
- name = nodeValue(cursor);
-
- } else if (equalsIgnoreCase("type", nodeName)) {
- type = nodeValue(cursor);
-
- } else if (equalsIgnoreCase("description", nodeName)) {
- description = nodeValue(cursor);
-
- } else if (equalsIgnoreCase("descriptionFormat", nodeName)) {
- descriptionFormat = nodeValue(cursor);
-
- } else if (equalsIgnoreCase("key", nodeName)) {
- key = nodeValue(cursor);
-
- } else if (equalsIgnoreCase("configKey", nodeName)) {
- // deprecated field, replaced by internalKey
- internalKey = nodeValue(cursor);
-
- } else if (equalsIgnoreCase("internalKey", nodeName)) {
- internalKey = nodeValue(cursor);
-
- } else if (equalsIgnoreCase("priority", nodeName) || equalsIgnoreCase("severity", nodeName)) {
- // "priority" is deprecated field and has been replaced by "severity"
- severity = nodeValue(cursor);
-
- } else if (equalsIgnoreCase("cardinality", nodeName)) {
- template = Cardinality.MULTIPLE == Cardinality.valueOf(nodeValue(cursor));
-
- } else if (equalsIgnoreCase("gapDescription", nodeName) || equalsIgnoreCase("effortToFixDescription", nodeName)) {
- gapDescription = nodeValue(cursor);
-
- } else if (equalsIgnoreCase("remediationFunction", nodeName) || equalsIgnoreCase("debtRemediationFunction", nodeName)) {
- debtRemediationFunction = nodeValue(cursor);
-
- } else if (equalsIgnoreCase("remediationFunctionBaseEffort", nodeName) || equalsIgnoreCase("debtRemediationFunctionOffset", nodeName)) {
- debtRemediationFunctionGapMultiplier = nodeValue(cursor);
-
- } else if (equalsIgnoreCase("remediationFunctionGapMultiplier", nodeName) || equalsIgnoreCase("debtRemediationFunctionCoefficient", nodeName)) {
- debtRemediationFunctionBaseEffort = nodeValue(cursor);
-
- } else if (equalsIgnoreCase("status", nodeName)) {
- String s = nodeValue(cursor);
- if (s != null) {
- status = RuleStatus.valueOf(s);
+ while (reader.hasNext()) {
+ final XMLEvent event = reader.nextEvent();
+ if (event.isEndElement() && event.asEndElement().getName().getLocalPart().equals(ELEMENT_RULE)) {
+ buildRule(repo, key, name, description, descriptionFormat, internalKey, severity, type, status, template,
+ gapDescription, debtRemediationFunction, debtRemediationFunctionGapMultiplier, debtRemediationFunctionBaseEffort, params, tags);
+ return;
+ }
+ if (event.isStartElement()) {
+ final StartElement element = event.asStartElement();
+ final String elementName = element.getName().getLocalPart();
+ if ("name".equalsIgnoreCase(elementName)) {
+ name = StringUtils.trim(reader.getElementText());
+ } else if ("type".equalsIgnoreCase(elementName)) {
+ type = StringUtils.trim(reader.getElementText());
+ } else if ("description".equalsIgnoreCase(elementName)) {
+ description = StringUtils.trim(reader.getElementText());
+ } else if ("descriptionFormat".equalsIgnoreCase(elementName)) {
+ descriptionFormat = StringUtils.trim(reader.getElementText());
+ } else if ("key".equalsIgnoreCase(elementName)) {
+ key = StringUtils.trim(reader.getElementText());
+ } else if ("configKey".equalsIgnoreCase(elementName)) {
+ // deprecated field, replaced by internalKey
+ internalKey = StringUtils.trim(reader.getElementText());
+ } else if ("internalKey".equalsIgnoreCase(elementName)) {
+ internalKey = StringUtils.trim(reader.getElementText());
+ } else if ("priority".equalsIgnoreCase(elementName) || "severity".equalsIgnoreCase(elementName)) {
+ // "priority" is deprecated field and has been replaced by "severity"
+ severity = StringUtils.trim(reader.getElementText());
+ } else if ("cardinality".equalsIgnoreCase(elementName)) {
+ template = Cardinality.MULTIPLE == Cardinality.valueOf(StringUtils.trim(reader.getElementText()));
+ } else if ("gapDescription".equalsIgnoreCase(elementName) || "effortToFixDescription".equalsIgnoreCase(elementName)) {
+ gapDescription = StringUtils.trim(reader.getElementText());
+ } else if ("remediationFunction".equalsIgnoreCase(elementName) || "debtRemediationFunction".equalsIgnoreCase(elementName)) {
+ debtRemediationFunction = StringUtils.trim(reader.getElementText());
+ } else if ("remediationFunctionBaseEffort".equalsIgnoreCase(elementName) || "debtRemediationFunctionOffset".equalsIgnoreCase(elementName)) {
+ debtRemediationFunctionGapMultiplier = StringUtils.trim(reader.getElementText());
+ } else if ("remediationFunctionGapMultiplier".equalsIgnoreCase(elementName) || "debtRemediationFunctionCoefficient".equalsIgnoreCase(elementName)) {
+ debtRemediationFunctionBaseEffort = StringUtils.trim(reader.getElementText());
+ } else if ("status".equalsIgnoreCase(elementName)) {
+ String s = StringUtils.trim(reader.getElementText());
+ if (s != null) {
+ status = RuleStatus.valueOf(s);
+ }
+ } else if (ELEMENT_PARAM.equalsIgnoreCase(elementName)) {
+ params.add(processParameter(element, reader));
+ } else if ("tag".equalsIgnoreCase(elementName)) {
+ tags.add(StringUtils.trim(reader.getElementText()));
}
-
- } else if (equalsIgnoreCase("param", nodeName)) {
- params.add(processParameter(cursor));
-
- } else if (equalsIgnoreCase("tag", nodeName)) {
- tags.add(nodeValue(cursor));
}
}
+ }
+ private static void buildRule(RulesDefinition.NewRepository repo, String key, String name, @Nullable String description,
+ String descriptionFormat, @Nullable String internalKey, String severity, @Nullable String type, RuleStatus status,
+ boolean template, @Nullable String gapDescription, @Nullable String debtRemediationFunction, @Nullable String debtRemediationFunctionGapMultiplier,
+ @Nullable String debtRemediationFunctionBaseEffort, List<ParamStruct> params, List<String> tags) {
try {
RulesDefinition.NewRule rule = repo.createRule(key)
.setSeverity(severity)
@@ -375,10 +394,6 @@ public class RulesDefinitionXmlLoader {
}
}
- private static String nodeValue(SMInputCursor cursor) throws XMLStreamException {
- return trim(cursor.collectDescendantText(false));
- }
-
private static class ParamStruct {
String key;
@@ -387,36 +402,38 @@ public class RulesDefinitionXmlLoader {
RuleParamType type = RuleParamType.STRING;
}
- private static ParamStruct processParameter(SMInputCursor ruleC) throws XMLStreamException {
+ private static ParamStruct processParameter(StartElement paramElement, XMLEventReader reader) throws XMLStreamException {
ParamStruct param = new ParamStruct();
// BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT
- String keyAttribute = ruleC.getAttrValue("key");
- if (isNotBlank(keyAttribute)) {
- param.key = trim(keyAttribute);
+ Attribute keyAttribute = paramElement.getAttributeByName(new QName("key"));
+ if (keyAttribute != null && StringUtils.isNotBlank(keyAttribute.getValue())) {
+ param.key = StringUtils.trim(keyAttribute.getValue());
}
// BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT
- String typeAttribute = ruleC.getAttrValue("type");
- if (isNotBlank(typeAttribute)) {
- param.type = RuleParamType.parse(typeAttribute);
+ Attribute typeAttribute = paramElement.getAttributeByName(new QName("type"));
+ if (typeAttribute != null && StringUtils.isNotBlank(typeAttribute.getValue())) {
+ param.type = RuleParamType.parse(StringUtils.trim(typeAttribute.getValue()));
}
- SMInputCursor paramC = ruleC.childElementCursor();
- while (paramC.getNext() != null) {
- String propNodeName = paramC.getLocalName();
- String propText = nodeValue(paramC);
- if (equalsIgnoreCase("key", propNodeName)) {
- param.key = propText;
-
- } else if (equalsIgnoreCase("description", propNodeName)) {
- param.description = propText;
-
- } else if (equalsIgnoreCase("type", propNodeName)) {
- param.type = RuleParamType.parse(propText);
-
- } else if (equalsIgnoreCase("defaultValue", propNodeName)) {
- param.defaultValue = propText;
+ while (reader.hasNext()) {
+ final XMLEvent event = reader.nextEvent();
+ if (event.isEndElement() && event.asEndElement().getName().getLocalPart().equals(ELEMENT_PARAM)) {
+ return param;
+ }
+ if (event.isStartElement()) {
+ final StartElement element = event.asStartElement();
+ final String elementName = element.getName().getLocalPart();
+ if ("key".equalsIgnoreCase(elementName)) {
+ param.key = StringUtils.trim(reader.getElementText());
+ } else if ("description".equalsIgnoreCase(elementName)) {
+ param.description = StringUtils.trim(reader.getElementText());
+ } else if ("type".equalsIgnoreCase(elementName)) {
+ param.type = RuleParamType.parse(StringUtils.trim(reader.getElementText()));
+ } else if ("defaultValue".equalsIgnoreCase(elementName)) {
+ param.defaultValue = StringUtils.trim(reader.getElementText());
+ }
}
}
return param;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/text/XmlWriter.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/text/XmlWriter.java
index ad30878cb6b..4c4489dd32a 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/text/XmlWriter.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/text/XmlWriter.java
@@ -47,7 +47,7 @@ public class XmlWriter {
public XmlWriter declaration() {
try {
- stream.writeStartDocument();
+ stream.writeStartDocument("UTF-8", "1.0");
return this;
} catch (XMLStreamException e) {
throw rethrow(e);
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/text/XmlWriterTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/text/XmlWriterTest.java
index 2be3eebfc9f..ba0a3d4127c 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/utils/text/XmlWriterTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/utils/text/XmlWriterTest.java
@@ -41,25 +41,25 @@ public class XmlWriterTest {
@Test
public void declaration() {
writer.declaration().begin("foo").end().close();
- expect("<?xml version='1.0' encoding='UTF-8'?><foo/>");
+ expect("<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo></foo>");
}
@Test
public void end_with_unused_parameter() {
writer.begin("foo").end("foo").close();
- expect("<foo/>");
+ expect("<foo></foo>");
}
@Test
public void only_root() {
writer.begin("foo").end().close();
- expect("<foo/>");
+ expect("<foo></foo>");
}
@Test
public void escape_value() {
writer.prop("foo", "1<2 & 2>=2").close();
- expect("<foo>1&lt;2 &amp; 2>=2</foo>");
+ expect("<foo>1&lt;2 &amp; 2&gt;=2</foo>");
}
@Test
@@ -74,7 +74,7 @@ public class XmlWriterTest {
.prop("nullNumber", (Number) null)
.prop("nullString", (String) null)
.end().close();
- expect("<root/>");
+ expect("<root></root>");
}
@Test