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.sonarsource.dotnet.shared.plugins.RoslynProfileExporter Maven / Gradle / Ivy
/*
* SonarSource :: .NET :: Shared library
* Copyright (C) 2014-2024 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.sonarsource.dotnet.shared.plugins;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import org.sonar.api.config.Configuration;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.profiles.ProfileExporter;
import org.sonar.api.profiles.RulesProfile;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.ActiveRule;
import org.sonar.api.rules.ActiveRuleParam;
import org.sonar.api.rules.RuleParam;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.api.server.rule.RulesDefinition.Context;
import org.sonar.api.server.rule.RulesDefinition.Repository;
import org.sonar.api.server.rule.RulesDefinition.Rule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This profile exporter was build to be used by the SonarQube SonarScanner for .NET (S4MSB) during the Begin step,
* to download the SonarLint.xml.
*
* However, starting 2016, it is not used anymore. See commit ce945d inside the S4MSB repo.
*
* To see the defaults of the settings, see AbstractPropertyDefinitions.java
*
* See https://github.com/SonarSource/sonar-scanner-msbuild/blob/4.6.1.2049/src/SonarScanner.MSBuild.PreProcessor/Roslyn/RoslynAnalyzerProvider.cs#L150
*
*/
public class RoslynProfileExporter extends ProfileExporter {
private static final Logger LOG = LoggerFactory.getLogger(RoslynProfileExporter.class);
private static final String ROSLYN_REPOSITORY_PREFIX = "roslyn.";
private final DotNetPluginMetadata pluginMetadata;
private final Configuration configuration;
private final RulesDefinition[] rulesDefinitions;
public RoslynProfileExporter(DotNetPluginMetadata pluginMetadata, Configuration configuration, RulesDefinition[] rulesDefinitions) {
super(profileKey(pluginMetadata), "Technical exporter for the MSBuild SonarQube Scanner");
this.pluginMetadata = pluginMetadata;
this.configuration = configuration;
this.rulesDefinitions = rulesDefinitions;
setSupportedLanguages(pluginMetadata.languageKey());
}
private static String sonarAnalyzerPartialRepoKey(DotNetPluginMetadata pluginMetadata) {
return "sonaranalyzer-" + pluginMetadata.languageKey();
}
private static String profileKey(DotNetPluginMetadata pluginMetadata) {
return "roslyn-" + pluginMetadata.languageKey();
}
public static List sonarLintRepositoryProperties(DotNetPluginMetadata pluginMetadata) {
String analyzerVersion = getAnalyzerVersion();
return Arrays.asList(
PropertyDefinition.builder(pluginKeyPropertyKey(sonarAnalyzerPartialRepoKey(pluginMetadata)))
.defaultValue(pluginMetadata.pluginKey())
.hidden()
.build(),
PropertyDefinition.builder(pluginVersionPropertyKey(sonarAnalyzerPartialRepoKey(pluginMetadata)))
.defaultValue(analyzerVersion)
.hidden()
.build(),
PropertyDefinition.builder(staticResourceNamePropertyKey(sonarAnalyzerPartialRepoKey(pluginMetadata)))
.defaultValue("SonarAnalyzer-" + analyzerVersion + ".zip")
.hidden()
.build(),
PropertyDefinition.builder(analyzerIdPropertyKey(sonarAnalyzerPartialRepoKey(pluginMetadata)))
.defaultValue(pluginMetadata.sonarAnalyzerName())
.hidden()
.build(),
PropertyDefinition.builder(ruleNamespacePropertyKey(sonarAnalyzerPartialRepoKey(pluginMetadata)))
.defaultValue(pluginMetadata.sonarAnalyzerName())
.hidden()
.build(),
PropertyDefinition.builder(nugetPackageIdPropertyKey(sonarAnalyzerPartialRepoKey(pluginMetadata)))
.defaultValue(pluginMetadata.sonarAnalyzerName())
.hidden()
.build(),
PropertyDefinition.builder(nugetPackageVersionPropertyKey(sonarAnalyzerPartialRepoKey(pluginMetadata)))
.defaultValue(analyzerVersion)
.hidden()
.build());
}
private static String getAnalyzerVersion() {
try {
return new BufferedReader(new InputStreamReader(RoslynProfileExporter.class.getResourceAsStream("/static/version.txt"), StandardCharsets.UTF_8)).readLine();
} catch (IOException e) {
throw new IllegalStateException("Couldn't read C# analyzer version number from '/static/version.txt'", e);
}
}
@Override
public void exportProfile(RulesProfile rulesProfile, Writer writer) {
try {
Map> activeRoslynRulesByPartialRepoKey = activeRoslynRulesByPartialRepoKey(pluginMetadata, rulesProfile.getActiveRules()
.stream()
.map(r -> RuleKey.of(r.getRepositoryKey(), r.getRuleKey()))
.toList());
appendLine(writer, "");
appendLine(writer, "");
appendLine(writer, " ");
appendLine(writer, " ");
for (Map.Entry> partialRepoEntry : activeRoslynRulesByPartialRepoKey.entrySet()) {
writeRepoRuleSet(partialRepoEntry.getKey(), partialRepoEntry.getValue(), writer);
}
appendLine(writer, " ");
appendLine(writer, " ");
String sonarlintParameters = analysisSettings(rulesProfile);
java.util.Base64.Encoder encoder = java.util.Base64.getEncoder();
String base64 = new String(encoder.encode(sonarlintParameters.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
appendLine(writer, " " + base64 + " ");
appendLine(writer, " ");
appendLine(writer, " ");
appendLine(writer, " ");
appendLine(writer, " ");
for (String partialRepoKey : activeRoslynRulesByPartialRepoKey.keySet()) {
String pluginKey = mandatoryPropertyValue(pluginKeyPropertyKey(partialRepoKey));
String pluginVersion = mandatoryPropertyValue(pluginVersionPropertyKey(partialRepoKey));
String staticResourceName = mandatoryPropertyValue(staticResourceNamePropertyKey(partialRepoKey));
appendLine(writer,
" ");
}
appendLine(writer, " ");
appendLine(writer, " ");
for (String partialRepoKey : activeRoslynRulesByPartialRepoKey.keySet()) {
String packageId = mandatoryPropertyValue(nugetPackageIdPropertyKey(partialRepoKey));
String packageVersion = mandatoryPropertyValue(nugetPackageVersionPropertyKey(partialRepoKey));
appendLine(writer, " ");
}
appendLine(writer, " ");
appendLine(writer, " ");
appendLine(writer, " ");
} catch (Exception e) {
LOG.error(String.format("Error exporting profile '%s' for language '%s'", rulesProfile.getName(), rulesProfile.getLanguage()), e);
throw e;
}
}
private void writeRepoRuleSet(String partialRepoKey, Collection ruleKeys, Writer writer) {
String analyzerId = mandatoryPropertyValue(analyzerIdPropertyKey(partialRepoKey));
String ruleNamespace = mandatoryPropertyValue(ruleNamespacePropertyKey(partialRepoKey));
appendLine(writer, " ");
Set activeRules = new LinkedHashSet<>();
String repositoryKey = null;
for (RuleKey activeRuleKey : ruleKeys) {
if (repositoryKey == null) {
repositoryKey = activeRuleKey.repository();
}
String ruleKey = activeRuleKey.rule();
activeRules.add(ruleKey);
appendLine(writer, " ");
}
List allRuleKeys = allRuleKeysByRepositoryKey(repositoryKey);
for (String ruleKey : allRuleKeys) {
if (!activeRules.contains(ruleKey)) {
appendLine(writer, " ");
}
}
appendLine(writer, " ");
}
private String analysisSettings(RulesProfile ruleProfile) {
StringBuilder sb = new StringBuilder();
appendLine(sb, "");
appendLine(sb, "");
appendLine(sb, " ");
for (ActiveRule activeRule : ruleProfile.getActiveRulesByRepository(pluginMetadata.repositoryKey())) {
appendLine(sb, " ");
appendLine(sb, " " + escapeXml(activeRule.getRuleKey()) + " ");
appendParameters(sb, effectiveParameters(activeRule));
appendLine(sb, " ");
}
appendLine(sb, " ");
appendLine(sb, " ");
appendLine(sb, " ");
appendLine(sb, " ");
return sb.toString();
}
private static void appendParameters(StringBuilder sb, Map parameters) {
if (!parameters.isEmpty()) {
appendLine(sb, " ");
for (Map.Entry parameter : parameters.entrySet()) {
appendLine(sb, " ");
appendLine(sb, " " + escapeXml(parameter.getKey()) + " ");
appendLine(sb, " " + escapeXml(parameter.getValue()) + " ");
appendLine(sb, " ");
}
appendLine(sb, " ");
}
}
private static Map effectiveParameters(ActiveRule activeRule) {
Map result = new HashMap<>();
if (activeRule.getRule().getTemplate() != null) {
result.put("RuleKey", activeRule.getRuleKey());
}
for (ActiveRuleParam param : activeRule.getActiveRuleParams()) {
result.put(param.getKey(), param.getValue() == null ? "" : param.getValue());
}
for (RuleParam param : activeRule.getRule().getParams()) {
if (!result.containsKey(param.getKey())) {
result.put(param.getKey(), param.getDefaultValue() == null ? "" : param.getDefaultValue());
}
}
return result;
}
public static Map> activeRoslynRulesByPartialRepoKey(DotNetPluginMetadata pluginMetadata, Iterable activeRules) {
Map> result = new LinkedHashMap<>();
for (RuleKey activeRule : activeRules) {
if (activeRule.repository().startsWith(ROSLYN_REPOSITORY_PREFIX)) {
String pluginKey = activeRule.repository().substring(ROSLYN_REPOSITORY_PREFIX.length());
result.putIfAbsent(pluginKey, new ArrayList<>());
result.get(pluginKey).add(activeRule);
} else if (pluginMetadata.repositoryKey().equals(activeRule.repository())) {
result.putIfAbsent(sonarAnalyzerPartialRepoKey(pluginMetadata), new ArrayList<>());
result.get(sonarAnalyzerPartialRepoKey(pluginMetadata)).add(activeRule);
}
}
return result;
}
private List allRuleKeysByRepositoryKey(@Nullable String repositoryKey) {
List result = new ArrayList<>();
if (repositoryKey == null) {
return result;
}
for (RulesDefinition rulesDefinition : rulesDefinitions) {
Context context = new Context();
rulesDefinition.define(context);
Repository repo = context.repository(repositoryKey);
if (repo != null) {
for (Rule rule : repo.rules()) {
result.add(rule.key());
}
}
}
return result;
}
private String mandatoryPropertyValue(String propertyKey) {
return configuration.get(propertyKey).orElseThrow(() -> new IllegalStateException("The mandatory property \"" + propertyKey + "\" must be set by the Roslyn plugin."));
}
private static String pluginKeyPropertyKey(String partialRepoKey) {
return partialRepoKey + ".pluginKey";
}
private static String pluginVersionPropertyKey(String partialRepoKey) {
return partialRepoKey + ".pluginVersion";
}
private static String staticResourceNamePropertyKey(String partialRepoKey) {
return partialRepoKey + ".staticResourceName";
}
private static String analyzerIdPropertyKey(String partialRepoKey) {
return partialRepoKey + ".analyzerId";
}
private static String ruleNamespacePropertyKey(String partialRepoKey) {
return partialRepoKey + ".ruleNamespace";
}
private static String nugetPackageIdPropertyKey(String partialRepoKey) {
return partialRepoKey + ".nuget.packageId";
}
private static String nugetPackageVersionPropertyKey(String partialRepoKey) {
return partialRepoKey + ".nuget.packageVersion";
}
private static String escapeXml(String str) {
return str.replace("&", "&").replace("\"", """).replace("'", "'").replace("<", "<").replace(">", ">");
}
private static void appendLine(Writer writer, String line) {
try {
writer.write(line);
writer.write("\r\n");
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private static void appendLine(StringBuilder sb, String line) {
sb.append(line);
sb.append("\r\n");
}
}