io.codemodder.providers.sonar.SonarModule Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of codemodder-plugin-sonar Show documentation
Show all versions of codemodder-plugin-sonar Show documentation
Plugin to enable the use of Sonar in codemods
package io.codemodder.providers.sonar;
import com.google.inject.AbstractModule;
import io.codemodder.CodeChanger;
import io.codemodder.sonar.model.Hotspot;
import io.codemodder.sonar.model.Issue;
import io.codemodder.sonar.model.SonarFinding;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ClassInfoList;
import io.github.classgraph.ScanResult;
import java.lang.reflect.Executable;
import java.lang.reflect.Parameter;
import java.nio.file.Path;
import java.util.*;
import javax.inject.Inject;
final class SonarModule extends AbstractModule {
private final List> codemodTypes;
private final Path repository;
private final List sonarFindings;
private final Class extends RuleFinding> ruleFindingClass;
SonarModule(
final List> codemodTypes,
final Path repository,
final List findings,
final Class extends RuleFinding> ruleFindingClass) {
this.codemodTypes = Objects.requireNonNull(codemodTypes);
this.repository = Objects.requireNonNull(repository);
this.sonarFindings = findings;
this.ruleFindingClass = ruleFindingClass;
}
@Override
protected void configure() {
Map> findingsByRuleMap = groupFindingsByRule(sonarFindings);
Set packagesScanned = new HashSet<>();
for (final Class extends CodeChanger> codemodType : codemodTypes) {
String packageName = codemodType.getPackageName();
if (!packagesScanned.contains(packageName)) {
packagesScanned.add(packageName);
bindAnnotationsForPackage(packageName, findingsByRuleMap);
}
}
}
private Map> groupFindingsByRule(List findings) {
Map> findingsByRuleMap = new HashMap<>();
findings.forEach(
finding -> {
String rule = finding.rule();
List perRuleList = findingsByRuleMap.computeIfAbsent(rule, k -> new ArrayList<>());
perRuleList.add(finding);
});
return findingsByRuleMap;
}
private void bindAnnotationsForPackage(
String packageName, Map> findingsByRuleMap) {
try (ScanResult scan =
new ClassGraph()
.enableAllInfo()
.acceptPackagesNonRecursive(packageName)
.removeTemporaryFilesAfterScan()
.scan()) {
List injectableParams = getInjectableParameters(scan);
// Outside the loop, create a map to track already bound ruleIds
final Map boundRuleIds = new HashMap<>();
injectableParams.forEach(
param -> {
ProvidedSonarScan annotation = param.getAnnotation(ProvidedSonarScan.class);
if (annotation != null && ruleFindingClass.equals(param.getType())) {
bindParam(annotation, param, findingsByRuleMap, boundRuleIds);
}
});
}
}
private List getInjectableParameters(ScanResult scan) {
ClassInfoList classesWithMethodAnnotation = scan.getClassesWithMethodAnnotation(Inject.class);
List> injectableClasses = classesWithMethodAnnotation.loadClasses();
return injectableClasses.stream()
.map(Class::getDeclaredConstructors)
.flatMap(Arrays::stream)
.filter(constructor -> constructor.isAnnotationPresent(Inject.class))
.map(Executable::getParameters)
.flatMap(Arrays::stream)
.toList();
}
private void bindParam(
ProvidedSonarScan annotation,
Parameter param,
Map> findingsByRuleMap,
Map boundRuleIds) {
// Ensure the parameter type is valid
if (!RuleFinding.class.isAssignableFrom(param.getType())) {
throw new IllegalArgumentException(
"can't use @ProvidedSonarScan on anything except RuleFinding (see "
+ param.getDeclaringExecutable().getDeclaringClass().getName()
+ ")");
}
final String ruleId = annotation.ruleId();
// Check if the ruleId has already been bound
if (boundRuleIds.containsKey(ruleId)) {
return; // Skip binding if already bound
}
// Retrieve findings for the ruleId
List findings = findingsByRuleMap.getOrDefault(ruleId, Collections.emptyList());
if (RuleIssue.class.equals(ruleFindingClass)) {
if (findings.isEmpty()) {
bind(RuleIssue.class)
.annotatedWith(annotation)
.toInstance(new RuleIssue(List.of(), repository));
} else {
bind(RuleIssue.class)
.annotatedWith(annotation)
.toInstance(new RuleIssue((List) findings, repository));
}
} else if (RuleHotspot.class.equals(ruleFindingClass)) {
if (findings.isEmpty()) {
bind(RuleHotspot.class)
.annotatedWith(annotation)
.toInstance(new RuleHotspot(List.of(), repository));
} else {
bind(RuleHotspot.class)
.annotatedWith(annotation)
.toInstance(new RuleHotspot((List) findings, repository));
}
}
// Mark the ruleId as bound
boundRuleIds.put(ruleId, true);
}
}