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