package org.intellij.sonar.index;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.psi.PsiFile;
import org.intellij.sonar.console.SonarConsole;
import org.intellij.sonar.persistence.Settings;
import org.intellij.sonar.sonarreport.data.Issue;
import org.intellij.sonar.util.ProgressIndicatorUtil;
import org.intellij.sonar.util.SettingsUtil;
import org.intellij.sonar.util.SonarComponentToFileMatcher;
import org.sonar.wsclient.services.Resource;
public class IssuesByFileIndexer {
private final ImmutableList<PsiFile> files;
private ImmutableList<Issue> issues;
private SonarConsole sonarConsole;
public IssuesByFileIndexer(ImmutableList<PsiFile> files) {
this.files = files;
}
public void setIssues(ImmutableList<Issue> issues) {
this.issues = issues;
}
public IssuesByFileIndexer withSonarReportIssues(List<Issue> issues) {
setIssues(ImmutableList.copyOf(issues));
return this;
}
public IssuesByFileIndexer withSonarConsole(SonarConsole sonarConsole) {
this.sonarConsole = sonarConsole;
return this;
}
public IssuesByFileIndexer withSonarServerIssues(ImmutableList<org.sonar.wsclient.issue.Issue> issues) {
setIssues(
FluentIterable.from(issues)
.transform(
issue -> new Issue(
issue.key(),
issue.componentKey(),
issue.line(),
issue.message(),
issue.severity(),
issue.ruleKey(),
issue.status(),
false // issues from sonar server cannot be new
)
).toList()
);
return this;
}
public Map<String,Set<SonarIssue>> create() {
final Map<String,Set<SonarIssue>> index = Maps.newConcurrentMap();
final int filesCount = files.size();
final AtomicInteger fileIndex = new AtomicInteger(0);
final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
final int availableProcessors = Runtime.getRuntime().availableProcessors();
info(
String.format(
"Start processing %d files and %d issues with %d threads",
filesCount,
issues.size(),
availableProcessors
)
);
final ExecutorService executorService = Executors.newFixedThreadPool(availableProcessors);
final Iterable<List<PsiFile>> filePartitions = Iterables.partition(files,availableProcessors);
for (final List<PsiFile> partition : filePartitions) {
executorService.execute(
() -> {
for (PsiFile psiFile : partition) {
if (indicator.isCanceled()) break;
final int currentFileIndex = fileIndex.incrementAndGet();
ProgressIndicatorUtil.setFraction(indicator,1.0 * currentFileIndex / filesCount);
ProgressIndicatorUtil.setText2(indicator,psiFile.getName());
final String filesProgressMessage = String.format("%d / %d files processed",currentFileIndex,filesCount);
ProgressIndicatorUtil.setText(indicator,filesProgressMessage);
if (filesCount % currentFileIndex == 20) {
info(filesProgressMessage);
}
String fullFilePath = psiFile.getVirtualFile().getPath();
ImmutableSet.Builder<SonarIssue> entriesBuilder = ImmutableSet.builder();
final Settings settings = SettingsUtil.getSettingsFor(psiFile);
if (settings == null) continue;
final Collection<Resource> resources = settings.getResources();
if (resources == null || resources.isEmpty()) {
matchFileByResource(fullFilePath,entriesBuilder,new Resource());
} else {
for (Resource resource : resources) {
matchFileByResource(fullFilePath,entriesBuilder,resource);
}
}
final ImmutableSet<SonarIssue> sonarIssues = entriesBuilder.build();
if (!sonarIssues.isEmpty())
index.put(fullFilePath,sonarIssues);
}
}
);
}
executorService.shutdown();
try {
executorService.awaitTermination(Long.MAX_VALUE,TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
sonarConsole.error(Throwables.getStackTraceAsString(e));
}
return index;
}
private void info(String msg) {
if (sonarConsole != null) {
sonarConsole.info(msg);
}
}
private void matchFileByResource(
String fullFilePath,
ImmutableSet.Builder<SonarIssue> entriesBuilder,
Resource resource
) {
final String resourceKey = resource.getKey();
for (Issue issue : issues) {
String component = issue.getComponent();
if (SonarComponentToFileMatcher.match(component,resourceKey,fullFilePath)) {
entriesBuilder.add(
new SonarIssue(
issue.getKey(),
issue.getRule(),
issue.getLine(),
issue.getMessage(),
issue.getSeverity(),
issue.getIsNew()
)
);
}
}
}
}