Package org.sonar.server.debt

Source Code of org.sonar.server.debt.DebtRulesXMLImporter$Property

/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 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.server.debt;

import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
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.ServerComponent;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.ValidationMessages;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;

import java.io.Reader;
import java.io.StringReader;
import java.util.List;

import static com.google.common.collect.Lists.newArrayList;
import static org.sonar.server.debt.DebtModelXMLExporter.*;

/**
* Import rules debt definitions from an XML
*/
public class DebtRulesXMLImporter implements ServerComponent {

  public List<RuleDebt> importXML(String xml, ValidationMessages validationMessages) {
    return importXML(new StringReader(xml), validationMessages);
  }

  public List<RuleDebt> importXML(Reader xml, ValidationMessages validationMessages) {
    List<RuleDebt> ruleDebts = newArrayList();
    try {
      SMInputFactory inputFactory = initStax();
      SMHierarchicCursor cursor = inputFactory.rootElementCursor(xml);

      // advance to <sqale>
      cursor.advance();
      SMInputCursor rootCursor = cursor.childElementCursor(CHARACTERISTIC);
      while (rootCursor.getNext() != null) {
        process(ruleDebts, null, null, validationMessages, rootCursor);
      }

      cursor.getStreamReader().closeCompletely();
    } catch (XMLStreamException e) {
      throw new IllegalStateException("XML is not valid", e);
    }
    return ruleDebts;
  }

  private SMInputFactory initStax() {
    XMLInputFactory xmlFactory = XMLInputFactory2.newInstance();
    xmlFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
    xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
    xmlFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
    xmlFactory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE);
    return new SMInputFactory(xmlFactory);
  }

  private void process(List<RuleDebt> ruleDebts, @Nullable String rootKey, @Nullable String parentKey,
                       ValidationMessages validationMessages, SMInputCursor chcCursor) throws XMLStreamException {
    String currentCharacteristicKey = null;
    SMInputCursor cursor = chcCursor.childElementCursor();
    while (cursor.getNext() != null) {
      String node = cursor.getLocalName();
      if (StringUtils.equals(node, CHARACTERISTIC_KEY)) {
        currentCharacteristicKey = cursor.collectDescendantText().trim();
      } else if (StringUtils.equals(node, CHARACTERISTIC)) {
        process(ruleDebts, parentKey, currentCharacteristicKey, validationMessages, cursor);
      } else if (StringUtils.equals(node, REPOSITORY_KEY)) {
        RuleDebt ruleDebt = processRule(validationMessages, cursor);
        if (ruleDebt != null && parentKey != null) {
          if (rootKey != null) {
            ruleDebt.setSubCharacteristicKey(DebtCharacteristicsXMLImporter.convertKey(parentKey));
            ruleDebts.add(ruleDebt);
          } else {
            validationMessages.addWarningText("Rule '" + ruleDebt.ruleKey() + "' is ignored because it's defined directly under a root characteristic.");
          }
        }
      }
    }
  }

  @CheckForNull
  private 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 (StringUtils.isNotBlank(ruleRepositoryKey) && StringUtils.isNotBlank(ruleKey)) {
      return createRule(RuleKey.of(ruleRepositoryKey, ruleKey), properties, validationMessages);
    }
    return null;
  }

  private 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) ? Duration.MINUTE : textValue;
      }
    }
    return new Property(key, value, textValue);
  }

  @CheckForNull
  private RuleDebt createRule(RuleKey ruleKey, Properties properties, ValidationMessages validationMessages) {
    Property function = properties.function();
    if (function != null) {

      Property coefficientProperty = properties.coefficient();
      String coefficient = coefficientProperty != null ? coefficientProperty.toDuration() : null;
      Property offsetProperty = properties.offset();
      String offset = offsetProperty != null ? offsetProperty.toDuration() : null;

      return createRuleDebt(ruleKey, function.getTextValue(), coefficient, offset, validationMessages);
    }
    return null;
  }

  @CheckForNull
  private RuleDebt createRuleDebt(RuleKey ruleKey, String function, @Nullable String coefficient, @Nullable String offset, ValidationMessages validationMessages) {
    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 new RuleDebt().setRuleKey(ruleKey).setFunction(DebtRemediationFunction.Type.LINEAR.name()).setCoefficient(coefficient);
    } else if ("constant_resource".equals(function)) {
      validationMessages.addWarningText(String.format("Constant/file function is no longer used, technical debt definitions on '%s' are ignored.", ruleKey));
    } else if (DebtRemediationFunction.Type.CONSTANT_ISSUE.name().equalsIgnoreCase(function) && coefficient != null && offset == null) {
      return new RuleDebt().setRuleKey(ruleKey).setFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE.name()).setOffset(coefficient);
    } else {
      return new RuleDebt().setRuleKey(ruleKey).setFunction(function.toUpperCase()).setCoefficient(coefficient).setOffset(offset);
    }
    return null;
  }

  private static class Properties {
    List<Property> properties;

    public Properties() {
      this.properties = newArrayList();
    }

    public Properties add(Property property) {
      this.properties.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(final String key) {
      return Iterables.find(properties, new Predicate<Property>() {
        @Override
        public boolean apply(Property input) {
          return input.getKey().equals(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;
    }
  }

}
TOP

Related Classes of org.sonar.server.debt.DebtRulesXMLImporter$Property

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.