001/*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019 */
020package org.sonar.plugins.pmd;
021
022import com.google.common.annotations.VisibleForTesting;
023import org.jdom.CDATA;
024import org.jdom.Document;
025import org.jdom.Element;
026import org.jdom.output.Format;
027import org.jdom.output.XMLOutputter;
028import org.sonar.api.profiles.ProfileExporter;
029import org.sonar.api.profiles.RulesProfile;
030import org.sonar.api.resources.Java;
031import org.sonar.api.rules.ActiveRule;
032import org.sonar.api.rules.ActiveRuleParam;
033import org.sonar.api.utils.SonarException;
034import org.sonar.plugins.pmd.xml.PmdProperty;
035import org.sonar.plugins.pmd.xml.PmdRule;
036import org.sonar.plugins.pmd.xml.PmdRuleset;
037
038import java.io.IOException;
039import java.io.StringWriter;
040import java.io.Writer;
041import java.util.ArrayList;
042import java.util.List;
043
044public class PmdProfileExporter extends ProfileExporter {
045  public PmdProfileExporter() {
046    super(PmdConstants.REPOSITORY_KEY, PmdConstants.PLUGIN_NAME);
047    setSupportedLanguages(Java.KEY);
048    setMimeType("application/xml");
049  }
050
051  @Override
052  public void exportProfile(RulesProfile profile, Writer writer) {
053    try {
054      String xmlModules = exportProfile(PmdConstants.REPOSITORY_KEY, profile);
055      writer.append(xmlModules);
056    } catch (IOException e) {
057      throw new SonarException("Fail to export the profile " + profile, e);
058    }
059  }
060
061  public String exportProfile(String repositoryKey, RulesProfile profile) {
062    PmdRuleset tree = createPmdRuleset(repositoryKey, profile.getActiveRulesByRepository(repositoryKey), profile.getName());
063    return exportPmdRulesetToXml(tree);
064  }
065
066  private PmdRuleset createPmdRuleset(String repositoryKey, List<ActiveRule> activeRules, String profileName) {
067    PmdRuleset ruleset = new PmdRuleset(profileName);
068    for (ActiveRule activeRule : activeRules) {
069      if (activeRule.getRule().getRepositoryKey().equals(repositoryKey)) {
070        String configKey = activeRule.getRule().getConfigKey();
071        PmdRule rule = new PmdRule(configKey, PmdLevelUtils.toLevel(activeRule.getSeverity()));
072        if ((activeRule.getActiveRuleParams() != null) && !activeRule.getActiveRuleParams().isEmpty()) {
073          List<PmdProperty> properties = new ArrayList<PmdProperty>();
074          for (ActiveRuleParam activeRuleParam : activeRule.getActiveRuleParams()) {
075            properties.add(new PmdProperty(activeRuleParam.getRuleParam().getKey(), activeRuleParam.getValue()));
076          }
077          rule.setProperties(properties);
078        }
079        ruleset.addRule(rule);
080        processXPathRule(activeRule.getRuleKey(), rule);
081      }
082    }
083    return ruleset;
084  }
085
086  @VisibleForTesting
087  void processXPathRule(String sonarRuleKey, PmdRule rule) {
088    if (PmdConstants.XPATH_CLASS.equals(rule.getRef())) {
089      rule.setRef(null);
090      PmdProperty xpathMessage = rule.getProperty(PmdConstants.XPATH_MESSAGE_PARAM);
091      if (xpathMessage == null) {
092        throw new SonarException("Property '" + PmdConstants.XPATH_MESSAGE_PARAM + "' should be set for PMD rule " + sonarRuleKey);
093      }
094      rule.setMessage(xpathMessage.getValue());
095      rule.removeProperty(PmdConstants.XPATH_MESSAGE_PARAM);
096      PmdProperty xpathExp = rule.getProperty(PmdConstants.XPATH_EXPRESSION_PARAM);
097      if (xpathExp == null) {
098        throw new SonarException("Property '" + PmdConstants.XPATH_EXPRESSION_PARAM + "' should be set for PMD rule " + sonarRuleKey);
099      }
100      xpathExp.setCdataValue(xpathExp.getValue());
101      rule.setClazz(PmdConstants.XPATH_CLASS);
102      rule.setName(sonarRuleKey);
103    }
104  }
105
106  private String exportPmdRulesetToXml(PmdRuleset pmdRuleset) {
107    Element eltRuleset = new Element("ruleset");
108    for (PmdRule pmdRule : pmdRuleset.getPmdRules()) {
109      Element eltRule = new Element("rule");
110      addAttribute(eltRule, "ref", pmdRule.getRef());
111      addAttribute(eltRule, "class", pmdRule.getClazz());
112      addAttribute(eltRule, "message", pmdRule.getMessage());
113      addAttribute(eltRule, "name", pmdRule.getName());
114      addChild(eltRule, "priority", pmdRule.getPriority());
115      if (pmdRule.hasProperties()) {
116        Element eltProperties = new Element("properties");
117        eltRule.addContent(eltProperties);
118        for (PmdProperty prop : pmdRule.getProperties()) {
119          Element eltProperty = new Element("property");
120          eltProperty.setAttribute("name", prop.getName());
121          if (prop.isCdataValue()) {
122            Element eltValue = new Element("value");
123            eltValue.addContent(new CDATA(prop.getCdataValue()));
124            eltProperty.addContent(eltValue);
125          } else {
126            eltProperty.setAttribute("value", prop.getValue());
127          }
128          eltProperties.addContent(eltProperty);
129        }
130      }
131      eltRuleset.addContent(eltRule);
132    }
133    XMLOutputter serializer = new XMLOutputter(Format.getPrettyFormat());
134    StringWriter xml = new StringWriter();
135    try {
136      serializer.output(new Document(eltRuleset), xml);
137    } catch (IOException e) {
138      throw new SonarException("A exception occured while generating the PMD configuration file.", e);
139    }
140    return xml.toString();
141  }
142
143  private void addChild(Element elt, String name, String text) {
144    if (text != null) {
145      elt.addContent(new Element(name).setText(text));
146    }
147  }
148
149  private void addAttribute(Element elt, String name, String value) {
150    if (value != null) {
151      elt.setAttribute(name, value);
152    }
153  }
154}