Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.sonar.server.debt.DebtRulesXMLImporter Maven / Gradle / Ivy
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program 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.
*
* This program 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.server.debt;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import java.io.Reader;
import java.io.StringReader;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.codehaus.stax2.XMLInputFactory2;
import org.codehaus.staxmate.SMInputFactory;
import org.codehaus.staxmate.in.SMHierarchicCursor;
import org.codehaus.staxmate.in.SMInputCursor;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.server.ServerSide;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.ValidationMessages;
import org.sonar.server.debt.DebtModelXMLExporter.RuleDebt;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static javax.xml.stream.XMLInputFactory.IS_COALESCING;
import static javax.xml.stream.XMLInputFactory.IS_NAMESPACE_AWARE;
import static javax.xml.stream.XMLInputFactory.IS_VALIDATING;
import static javax.xml.stream.XMLInputFactory.SUPPORT_DTD;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import static org.sonar.api.server.debt.DebtRemediationFunction.Type.CONSTANT_ISSUE;
import static org.sonar.api.server.debt.DebtRemediationFunction.Type.LINEAR;
import static org.sonar.api.utils.Duration.MINUTE;
import static org.sonar.server.debt.DebtModelXMLExporter.CHARACTERISTIC;
import static org.sonar.server.debt.DebtModelXMLExporter.PROPERTY;
import static org.sonar.server.debt.DebtModelXMLExporter.PROPERTY_COEFFICIENT;
import static org.sonar.server.debt.DebtModelXMLExporter.PROPERTY_FUNCTION;
import static org.sonar.server.debt.DebtModelXMLExporter.PROPERTY_KEY;
import static org.sonar.server.debt.DebtModelXMLExporter.PROPERTY_OFFSET;
import static org.sonar.server.debt.DebtModelXMLExporter.PROPERTY_TEXT_VALUE;
import static org.sonar.server.debt.DebtModelXMLExporter.PROPERTY_VALUE;
import static org.sonar.server.debt.DebtModelXMLExporter.REPOSITORY_KEY;
import static org.sonar.server.debt.DebtModelXMLExporter.RULE_KEY;
/**
* Import rules debt definitions from an XML
*/
@ServerSide
@ComputeEngineSide
public class DebtRulesXMLImporter {
public List importXML(String xml, ValidationMessages validationMessages) {
return importXML(new StringReader(xml), validationMessages);
}
public List importXML(Reader xml, ValidationMessages validationMessages) {
List ruleDebts = newArrayList();
try {
SMInputFactory inputFactory = initStax();
SMHierarchicCursor cursor = inputFactory.rootElementCursor(xml);
// advance to
cursor.advance();
SMInputCursor rootCursor = cursor.childElementCursor(CHARACTERISTIC);
while (rootCursor.getNext() != null) {
process(ruleDebts, validationMessages, rootCursor);
}
cursor.getStreamReader().closeCompletely();
} catch (XMLStreamException e) {
throw new IllegalStateException("XML is not valid", e);
}
return ruleDebts;
}
private static SMInputFactory initStax() {
XMLInputFactory xmlFactory = XMLInputFactory2.newInstance();
xmlFactory.setProperty(IS_COALESCING, TRUE);
xmlFactory.setProperty(IS_NAMESPACE_AWARE, FALSE);
xmlFactory.setProperty(SUPPORT_DTD, FALSE);
xmlFactory.setProperty(IS_VALIDATING, FALSE);
return new SMInputFactory(xmlFactory);
}
private static void process(List ruleDebts,
ValidationMessages validationMessages, SMInputCursor chcCursor) throws XMLStreamException {
SMInputCursor cursor = chcCursor.childElementCursor();
while (cursor.getNext() != null) {
String node = cursor.getLocalName();
if (StringUtils.equals(node, CHARACTERISTIC)) {
process(ruleDebts, validationMessages, cursor);
} else if (StringUtils.equals(node, REPOSITORY_KEY)) {
RuleDebt ruleDebt = processRule(validationMessages, cursor);
if (ruleDebt != null) {
ruleDebts.add(ruleDebt);
}
}
}
}
@CheckForNull
private static RuleDebt processRule(ValidationMessages validationMessages, SMInputCursor cursor) throws XMLStreamException {
String ruleRepositoryKey = cursor.collectDescendantText().trim();
String ruleKey = null;
Properties properties = new Properties();
while (cursor.getNext() != null) {
String node = cursor.getLocalName();
if (StringUtils.equals(node, PROPERTY)) {
properties.add(processProperty(validationMessages, cursor));
} else if (StringUtils.equals(node, RULE_KEY)) {
ruleKey = cursor.collectDescendantText().trim();
}
}
if (isNotBlank(ruleRepositoryKey) && isNotBlank(ruleKey)) {
return createRule(RuleKey.of(ruleRepositoryKey, ruleKey), properties, validationMessages);
}
return null;
}
private static Property processProperty(ValidationMessages validationMessages, SMInputCursor cursor) throws XMLStreamException {
SMInputCursor c = cursor.childElementCursor();
String key = null;
int value = 0;
String textValue = null;
while (c.getNext() != null) {
String node = c.getLocalName();
if (StringUtils.equals(node, PROPERTY_KEY)) {
key = c.collectDescendantText().trim();
} else if (StringUtils.equals(node, PROPERTY_VALUE)) {
String s = c.collectDescendantText().trim();
try {
Double valueDouble = NumberUtils.createDouble(s);
value = valueDouble.intValue();
} catch (NumberFormatException ex) {
validationMessages.addErrorText(String.format("Cannot import value '%s' for field %s - Expected a numeric value instead", s, key));
}
} else if (StringUtils.equals(node, PROPERTY_TEXT_VALUE)) {
textValue = c.collectDescendantText().trim();
textValue = "mn".equals(textValue) ? MINUTE : textValue;
}
}
return new Property(key, value, textValue);
}
@CheckForNull
private static RuleDebt createRule(RuleKey ruleKey, Properties properties, ValidationMessages validationMessages) {
Property function = properties.function();
Property coefficientProperty = properties.coefficient();
String coefficient = coefficientProperty == null ? null : coefficientProperty.toDuration();
Property offsetProperty = properties.offset();
String offset = offsetProperty == null ? null : offsetProperty.toDuration();
if (function != null && (coefficient != null || offset != null)) {
return createRuleDebt(ruleKey, function.getTextValue(), coefficient, offset, validationMessages);
}
return null;
}
@CheckForNull
private static RuleDebt createRuleDebt(RuleKey ruleKey, String function, @Nullable String coefficient, @Nullable String offset, ValidationMessages validationMessages) {
if ("constant_resource".equals(function)) {
validationMessages.addWarningText(String.format("Constant/file function is no longer used, technical debt definitions on '%s' are ignored.", ruleKey));
return null;
}
if ("linear_threshold".equals(function) && coefficient != null) {
validationMessages.addWarningText(String.format("Linear with threshold function is no longer used, remediation function of '%s' is replaced by linear.", ruleKey));
return createRuleDebt(ruleKey, LINEAR.name(), coefficient, null, validationMessages);
}
if (CONSTANT_ISSUE.name().equalsIgnoreCase(function) && coefficient != null && offset == null) {
return createRuleDebt(ruleKey, CONSTANT_ISSUE.name(), null, coefficient, validationMessages);
}
return new RuleDebt().setRuleKey(ruleKey).setFunction(function.toUpperCase()).setCoefficient(coefficient).setOffset(offset);
}
private static class Properties {
List list;
public Properties() {
this.list = newArrayList();
}
public Properties add(Property property) {
this.list.add(property);
return this;
}
public Property function() {
return find(PROPERTY_FUNCTION);
}
public Property coefficient() {
return find(PROPERTY_COEFFICIENT);
}
public Property offset() {
return find(PROPERTY_OFFSET);
}
private Property find(String key) {
return Iterables.find(list, new PropertyMatchKey(key), null);
}
}
private static class Property {
String key;
int value;
String textValue;
private Property(String key, int value, String textValue) {
this.key = key;
this.value = value;
this.textValue = textValue;
}
private String getKey() {
return key;
}
private int getValue() {
return value;
}
private String getTextValue() {
return textValue;
}
@CheckForNull
public String toDuration() {
if (key != null && getValue() > 0) {
String duration = Integer.toString(getValue());
duration += !Strings.isNullOrEmpty(getTextValue()) ? getTextValue() : Duration.DAY;
return duration;
}
return null;
}
}
private static class PropertyMatchKey implements Predicate {
private final String key;
public PropertyMatchKey(String key) {
this.key = key;
}
@Override
public boolean apply(@Nonnull Property input) {
return input.getKey().equals(key);
}
}
}