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.checkstyle;
021
022import com.google.common.collect.ArrayListMultimap;
023import com.google.common.collect.ListMultimap;
024import org.apache.commons.configuration.BaseConfiguration;
025import org.apache.commons.configuration.Configuration;
026import org.apache.commons.lang.StringEscapeUtils;
027import org.apache.commons.lang.StringUtils;
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.RuleParam;
033import org.sonar.api.utils.SonarException;
034
035import java.io.IOException;
036import java.io.Writer;
037import java.util.List;
038
039public class CheckstyleProfileExporter extends ProfileExporter {
040
041  static final String DOCTYPE_DECLARATION = "<!DOCTYPE module PUBLIC \"-//Puppy Crawl//DTD Check Configuration 1.2//EN\" \"http://www.puppycrawl.com/dtds/configuration_1_2.dtd\">";
042  private Configuration conf;
043
044  public CheckstyleProfileExporter(Configuration conf) {
045    super(CheckstyleConstants.REPOSITORY_KEY, CheckstyleConstants.PLUGIN_NAME);
046    this.conf = conf;
047    setSupportedLanguages(Java.KEY);
048    setMimeType("application/xml");
049  }
050
051  /**
052   * for unit tests
053   */
054  CheckstyleProfileExporter() {
055    this(new BaseConfiguration());
056  }
057
058  @Override
059  public void exportProfile(RulesProfile profile, Writer writer) {
060    try {
061      ListMultimap<String, ActiveRule> activeRulesByConfigKey = arrangeByConfigKey(profile.getActiveRulesByRepository(CheckstyleConstants.REPOSITORY_KEY));
062      generateXML(writer, activeRulesByConfigKey);
063
064    } catch (IOException e) {
065      throw new SonarException("Fail to export the profile " + profile, e);
066    }
067
068  }
069
070  private void generateXML(Writer writer, ListMultimap<String, ActiveRule> activeRulesByConfigKey) throws IOException {
071    appendXmlHeader(writer);
072    appendCustomFilters(writer);
073    appendCheckerModules(writer, activeRulesByConfigKey);
074    appendTreeWalker(writer, activeRulesByConfigKey);
075    appendXmlFooter(writer);
076  }
077
078  private void appendXmlHeader(Writer writer) throws IOException {
079    writer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
080        + DOCTYPE_DECLARATION
081        + "<!-- Generated by Sonar -->"
082        + "<module name=\"Checker\">");
083  }
084
085  private void appendCustomFilters(Writer writer) throws IOException {
086    String filtersXML = conf.getString(CheckstyleConstants.FILTERS_KEY, CheckstyleConstants.FILTERS_DEFAULT_VALUE);
087    if (StringUtils.isNotBlank(filtersXML)) {
088      writer.append(filtersXML);
089    }
090  }
091
092  private void appendCheckerModules(Writer writer, ListMultimap<String, ActiveRule> activeRulesByConfigKey) throws IOException {
093    for (String configKey : activeRulesByConfigKey.keySet()) {
094      if (!isInTreeWalker(configKey)) {
095        List<ActiveRule> activeRules = activeRulesByConfigKey.get(configKey);
096        for (ActiveRule activeRule : activeRules) {
097          appendModule(writer, activeRule);
098        }
099      }
100    }
101  }
102
103  private void appendTreeWalker(Writer writer, ListMultimap<String, ActiveRule> activeRulesByConfigKey) throws IOException {
104    writer.append("<module name=\"TreeWalker\">");
105    writer.append("<module name=\"FileContentsHolder\"/> ");
106    for (String configKey : activeRulesByConfigKey.keySet()) {
107      if (isInTreeWalker(configKey)) {
108        List<ActiveRule> activeRules = activeRulesByConfigKey.get(configKey);
109        for (ActiveRule activeRule : activeRules) {
110          appendModule(writer, activeRule);
111        }
112      }
113    }
114    writer.append("</module>");
115  }
116
117  private void appendXmlFooter(Writer writer) throws IOException {
118    writer.append("</module>");
119  }
120
121  static boolean isInTreeWalker(String configKey) {
122    return StringUtils.startsWithIgnoreCase(configKey, "Checker/TreeWalker/");
123  }
124
125  private static ListMultimap<String, ActiveRule> arrangeByConfigKey(List<ActiveRule> activeRules) {
126    ListMultimap<String, ActiveRule> result = ArrayListMultimap.create();
127    if (activeRules != null) {
128      for (ActiveRule activeRule : activeRules) {
129        result.put(activeRule.getConfigKey(), activeRule);
130      }
131    }
132    return result;
133  }
134
135  private void appendModule(Writer writer, ActiveRule activeRule) throws IOException {
136    String moduleName = StringUtils.substringAfterLast(activeRule.getConfigKey(), "/");
137    writer.append("<module name=\"");
138    StringEscapeUtils.escapeXml(writer, moduleName);
139    writer.append("\">");
140    if (activeRule.getRule().getParent() != null) {
141      appendModuleProperty(writer, "id", activeRule.getRuleKey());
142    }
143    appendModuleProperty(writer, "severity", CheckstyleSeverityUtils.toSeverity(activeRule.getSeverity()));
144    appendRuleParameters(writer, activeRule);
145    writer.append("</module>");
146  }
147
148  private void appendRuleParameters(Writer writer, ActiveRule activeRule) throws IOException {
149    for (RuleParam ruleParam : activeRule.getRule().getParams()) {
150      String value = activeRule.getParameter(ruleParam.getKey());
151      if (StringUtils.isNotBlank(value)) {
152        appendModuleProperty(writer, ruleParam.getKey(), value);
153      }
154    }
155  }
156
157  private void appendModuleProperty(Writer writer, String propertyKey, String propertyValue) throws IOException {
158    if (StringUtils.isNotBlank(propertyValue)) {
159      writer.append("<property name=\"");
160      StringEscapeUtils.escapeXml(writer, propertyKey);
161      writer.append("\" value=\"");
162      StringEscapeUtils.escapeXml(writer, propertyValue);
163      writer.append("\"/>");
164    }
165  }
166
167}