/*
* SonarQube
* Copyright (C) 2009-2017 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.sonar.scanner.issue;
import com.google.common.base.Strings;
import org.sonar.api.batch.fs.TextRange;
import org.sonar.api.batch.fs.internal.DefaultInputComponent;
import org.sonar.api.batch.rule.ActiveRule;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.rule.Rule;
import org.sonar.api.batch.rule.Rules;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.Issue.Flow;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.MessageException;
import org.sonar.scanner.protocol.Constants.Severity;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReport.IssueLocation;
import org.sonar.scanner.report.ReportPublisher;
/**
* Initialize the issues raised during scan.
*/
public class ModuleIssues {
private final ActiveRules activeRules;
private final Rules rules;
private final IssueFilters filters;
private final ReportPublisher reportPublisher;
public ModuleIssues(ActiveRules activeRules, Rules rules, IssueFilters filters, ReportPublisher reportPublisher) {
this.activeRules = activeRules;
this.rules = rules;
this.filters = filters;
this.reportPublisher = reportPublisher;
}
public boolean initAndAddIssue(Issue issue) {
DefaultInputComponent inputComponent = (DefaultInputComponent) issue.primaryLocation().inputComponent();
Rule rule = validateRule(issue);
ActiveRule activeRule = activeRules.find(issue.ruleKey());
if (activeRule == null) {
// rule does not exist or is not enabled -> ignore the issue
return false;
}
String primaryMessage = Strings.isNullOrEmpty(issue.primaryLocation().message()) ? rule.name() : issue.primaryLocation().message();
org.sonar.api.batch.rule.Severity overriddenSeverity = issue.overriddenSeverity();
Severity severity = overriddenSeverity != null ? Severity.valueOf(overriddenSeverity.name()) : Severity.valueOf(activeRule.severity());
ScannerReport.Issue.Builder builder = ScannerReport.Issue.newBuilder();
ScannerReport.IssueLocation.Builder locationBuilder = IssueLocation.newBuilder();
ScannerReport.TextRange.Builder textRangeBuilder = ScannerReport.TextRange.newBuilder();
// non-null fields
builder.setSeverity(severity);
builder.setRuleRepository(issue.ruleKey().repository());
builder.setRuleKey(issue.ruleKey().rule());
builder.setMsg(primaryMessage);
locationBuilder.setMsg(primaryMessage);
locationBuilder.setComponentRef(inputComponent.batchId());
TextRange primaryTextRange = issue.primaryLocation().textRange();
if (primaryTextRange != null) {
builder.setTextRange(toProtobufTextRange(textRangeBuilder, primaryTextRange));
}
Double gap = issue.gap();
if (gap != null) {
builder.setGap(gap);
}
applyFlows(builder, locationBuilder, textRangeBuilder, issue);
ScannerReport.Issue rawIssue = builder.build();
if (filters.accept(inputComponent.key(), rawIssue)) {
write(inputComponent.batchId(), rawIssue);
return true;
}
return false;
}
private static void applyFlows(ScannerReport.Issue.Builder builder, ScannerReport.IssueLocation.Builder locationBuilder,
ScannerReport.TextRange.Builder textRangeBuilder, Issue issue) {
ScannerReport.Flow.Builder flowBuilder = ScannerReport.Flow.newBuilder();
for (Flow flow : issue.flows()) {
if (flow.locations().isEmpty()) {
return;
}
flowBuilder.clear();
for (org.sonar.api.batch.sensor.issue.IssueLocation location : flow.locations()) {
locationBuilder.clear();
locationBuilder.setComponentRef(((DefaultInputComponent) location.inputComponent()).batchId());
String message = location.message();
if (message != null) {
locationBuilder.setMsg(message);
}
TextRange textRange = location.textRange();
if (textRange != null) {
locationBuilder.setTextRange(toProtobufTextRange(textRangeBuilder, textRange));
}
flowBuilder.addLocation(locationBuilder.build());
}
builder.addFlow(flowBuilder.build());
}
}
private static ScannerReport.TextRange toProtobufTextRange(ScannerReport.TextRange.Builder textRangeBuilder, TextRange primaryTextRange) {
textRangeBuilder.clear();
textRangeBuilder.setStartLine(primaryTextRange.start().line());
textRangeBuilder.setStartOffset(primaryTextRange.start().lineOffset());
textRangeBuilder.setEndLine(primaryTextRange.end().line());
textRangeBuilder.setEndOffset(primaryTextRange.end().lineOffset());
return textRangeBuilder.build();
}
private Rule validateRule(Issue issue) {
RuleKey ruleKey = issue.ruleKey();
Rule rule = rules.find(ruleKey);
if (rule == null) {
throw MessageException.of(String.format("The rule '%s' does not exist.", ruleKey));
}
if (Strings.isNullOrEmpty(rule.name()) && Strings.isNullOrEmpty(issue.primaryLocation().message())) {
throw MessageException.of(String.format("The rule '%s' has no name and the related issue has no message.", ruleKey));
}
return rule;
}
public void write(int batchId, ScannerReport.Issue rawIssue) {
reportPublisher.getWriter().appendComponentIssue(batchId, rawIssue);
}
}