
org.sonar.java.model.DefaultJavaFileScannerContext Maven / Gradle / Ivy
/*
* SonarQube Java
* Copyright (C) 2012-2023 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.java.model;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.sonar.api.batch.fs.InputComponent;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.plugins.java.api.internal.EndOfAnalysis;
import org.sonar.java.SonarComponents;
import org.sonar.java.ast.visitors.ComplexityVisitor;
import org.sonar.java.metrics.MetricsComputer;
import org.sonar.java.metrics.MetricsScannerContext;
import org.sonar.java.regex.RegexCache;
import org.sonar.java.regex.RegexCheck;
import org.sonar.java.regex.RegexScannerContext;
import org.sonar.java.reporting.AnalyzerMessage;
import org.sonar.java.reporting.FluentReporting;
import org.sonar.java.reporting.InternalJavaIssueBuilder;
import org.sonar.plugins.java.api.JavaCheck;
import org.sonar.plugins.java.api.JavaFileScannerContext;
import org.sonar.plugins.java.api.JavaVersion;
import org.sonar.plugins.java.api.SourceMap;
import org.sonar.plugins.java.api.caching.CacheContext;
import org.sonar.plugins.java.api.tree.CompilationUnitTree;
import org.sonar.plugins.java.api.tree.LiteralTree;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonarsource.analyzer.commons.regex.RegexParseResult;
import org.sonarsource.analyzer.commons.regex.ast.FlagSet;
import org.sonarsource.analyzer.commons.regex.ast.RegexSyntaxElement;
public class DefaultJavaFileScannerContext extends DefaultInputFileScannerContext implements JavaFileScannerContext, RegexScannerContext, FluentReporting, MetricsScannerContext {
private final JavaTree.CompilationUnitTreeImpl tree;
private final boolean semanticEnabled;
private final ComplexityVisitor complexityVisitor;
private final RegexCache regexCache;
private final MetricsComputer metricsComputer;
private final boolean fileParsed;
private List lines = null;
private String content;
public DefaultJavaFileScannerContext(CompilationUnitTree tree, InputFile inputFile, Sema semanticModel,
@Nullable SonarComponents sonarComponents, JavaVersion javaVersion,
boolean fileParsed, boolean inAndroidContext, @Nullable CacheContext cacheContext) {
super(sonarComponents, inputFile, javaVersion, inAndroidContext, cacheContext);
this.tree = (JavaTree.CompilationUnitTreeImpl) tree;
this.semanticEnabled = semanticModel != null;
this.complexityVisitor = new ComplexityVisitor();
this.regexCache = new RegexCache();
this.metricsComputer = new MetricsComputer();
this.fileParsed = fileParsed;
}
public DefaultJavaFileScannerContext(CompilationUnitTree tree, InputFile inputFile, Sema semanticModel,
@Nullable SonarComponents sonarComponents, JavaVersion javaVersion,
boolean fileParsed, boolean inAndroidContext) {
this(tree,inputFile, semanticModel, sonarComponents, javaVersion, fileParsed, inAndroidContext, null);
}
@Override
public CompilationUnitTree getTree() {
return tree;
}
@Override
@Nullable
public Object getSemanticModel() {
if (!semanticEnabled) {
return null;
}
return tree.sema;
}
@Override
public boolean fileParsed() {
return fileParsed;
}
@Override
public void reportIssue(JavaCheck javaCheck, Tree tree, String message) {
reportIssue(javaCheck, tree, message, Collections.emptyList(), null);
}
@Override
public void reportIssue(RegexCheck regexCheck, RegexSyntaxElement regexTree, String message, @Nullable Integer cost, List secondaries) {
List completedSecondaries = new ArrayList<>();
List mainLocations = new RegexCheck.RegexIssueLocation(regexTree, message).toSingleLocationItems();
if (mainLocations.size() > 1) {
// handle other main locations as secondaries with same message
mainLocations.subList(1, mainLocations.size())
.stream()
.forEach(completedSecondaries::add);
}
completedSecondaries.addAll(secondaries);
reportIssue(regexCheck, mainLocations.get(0).locations().get(0), message, cost, completedSecondaries);
}
@Override
public void reportIssue(RegexCheck regexCheck, Tree javaSyntaxElement, String message, @Nullable Integer cost, List secondaries) {
reportIssue(regexCheck, AnalyzerMessage.textSpanFor(javaSyntaxElement), message, cost, secondaries);
}
private void reportIssue(RegexCheck regexCheck, AnalyzerMessage.TextSpan mainLocation, String message, @Nullable Integer cost, List secondaries) {
List> secondariesAsFlows = new ArrayList<>();
secondaries.stream()
.flatMap(regexIssueLocation -> regexIssueLocation.toSingleLocationItems().stream())
.map(Collections::singletonList)
.forEach(secondariesAsFlows::add);
AnalyzerMessage analyzerMessage = new AnalyzerMessage(regexCheck, inputFile, mainLocation, message, cost != null ? cost : 0);
completeAnalyzerMessageWithFlows(analyzerMessage, secondariesAsFlows, ril -> ril.locations().get(0), RegexCheck.RegexIssueLocation::message);
reportIssue(analyzerMessage);
}
@Override
public RegexParseResult regexForLiterals(FlagSet initialFlags, LiteralTree... stringLiterals) {
return regexCache.getRegexForLiterals(initialFlags, stringLiterals);
}
@Override
public void reportIssue(JavaCheck javaCheck, Tree syntaxNode, String message, List secondary, @Nullable Integer cost) {
List> flows = secondary.stream().map(Collections::singletonList).collect(Collectors.toList());
reportIssueWithFlow(javaCheck, syntaxNode, message, flows, cost);
}
@Override
public void reportIssueWithFlow(JavaCheck javaCheck, Tree syntaxNode, String message, Iterable> flows, @Nullable Integer cost) {
throwIfEndOfAnalysisCheck(javaCheck);
reportIssue(createAnalyzerMessage(inputFile, javaCheck, syntaxNode, null, message, flows, cost));
}
@Override
public void reportIssue(JavaCheck javaCheck, Tree startTree, Tree endTree, String message) {
reportIssue(javaCheck, startTree, endTree, message, Collections.emptyList(), null);
}
@Override
public void reportIssue(JavaCheck javaCheck, Tree startTree, Tree endTree, String message, List secondary, @Nullable Integer cost) {
throwIfEndOfAnalysisCheck(javaCheck);
List> flows = secondary.stream().map(Collections::singletonList).collect(Collectors.toList());
reportIssue(createAnalyzerMessage(inputFile, javaCheck, startTree, endTree, message, flows, cost));
}
@Override
public List getFileLines() {
if (lines == null) {
lines = Collections.unmodifiableList(sonarComponents.fileLines(inputFile));
}
return lines;
}
@Override
public String getFileContent() {
if (content == null) {
content = sonarComponents.inputFileContents(inputFile);
}
return content;
}
public AnalyzerMessage createAnalyzerMessage(JavaCheck javaCheck, Tree startTree, String message) {
return createAnalyzerMessage(inputFile, javaCheck, startTree, null, message, Collections.emptyList(), null);
}
protected static AnalyzerMessage createAnalyzerMessage(InputFile inputFile, JavaCheck javaCheck, Tree startTree, @Nullable Tree endTree, String message,
Iterable> flows, @Nullable Integer cost) {
AnalyzerMessage.TextSpan location = endTree != null ? AnalyzerMessage.textSpanBetween(startTree, endTree) : AnalyzerMessage.textSpanFor(startTree);
AnalyzerMessage analyzerMessage = new AnalyzerMessage(javaCheck, inputFile, location, message, cost != null ? cost : 0);
completeAnalyzerMessageWithFlows(analyzerMessage, flows, loc -> AnalyzerMessage.textSpanFor(loc.syntaxNode), loc -> loc.msg);
return analyzerMessage;
}
private static void completeAnalyzerMessageWithFlows(
AnalyzerMessage analyzerMessage,
Iterable> flows,
Function flowItemLocationProdivder,
Function flowItemMessageProvider) {
JavaCheck check = analyzerMessage.getCheck();
InputComponent component = analyzerMessage.getInputComponent();
for (List flow : flows) {
List sonarqubeFlow = flow.stream()
.map(l -> new AnalyzerMessage(check, component, flowItemLocationProdivder.apply(l), flowItemMessageProvider.apply(l),0))
.collect(Collectors.toList());
analyzerMessage.flows.add(sonarqubeFlow);
}
}
@Override
public List getComplexityNodes(Tree tree) {
return complexityVisitor.getNodes(tree);
}
protected static void throwIfEndOfAnalysisCheck(JavaCheck javaCheck) {
if (javaCheck instanceof EndOfAnalysis) {
throw new UnsupportedOperationException("EndOfAnalysisCheck must only call reportIssue with AnalyzerMessage and must never pass a Tree reference.");
}
}
@Override
public Optional sourceMap() {
if (inputFile instanceof GeneratedFile) {
return Optional.of(((GeneratedFile) inputFile).sourceMap());
}
return Optional.empty();
}
@Override
public JavaIssueBuilder newIssue() {
return new InternalJavaIssueBuilder(inputFile, sonarComponents);
}
@Override
public MetricsComputer getMetricsComputer() {
return metricsComputer;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy