All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.codemodder.providers.sonar.SonarModule Maven / Gradle / Ivy

There is a newer version: 0.97.5
Show newest version
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> ruleFindingClass;

  SonarModule(
      final List> codemodTypes,
      final Path repository,
      final List findings,
      final Class> 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 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);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy