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

io.codemodder.remediation.SearcherStrategyRemediator Maven / Gradle / Ivy

package io.codemodder.remediation;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import io.codemodder.CodemodChange;
import io.codemodder.CodemodFileScanningResult;
import io.codemodder.codetf.DetectorRule;
import io.codemodder.codetf.FixedFinding;
import io.codemodder.codetf.UnfixedFinding;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import org.javatuples.Pair;

/**
 * Remediates issues with pairs of searchers and strategies. Searchers will associate an issue with
 * a node from the AST, while a strategy will try to fix an issue.
 *
 * @param 
 */
public class SearcherStrategyRemediator implements Remediator {

  public static final class Builder {
    private final Map, RemediationStrategy> searcherRemediatorMap;

    public Builder() {
      this.searcherRemediatorMap = new HashMap<>();
    }

    public Builder withSearcherStrategyPair(
        final FixCandidateSearcher searcher, final RemediationStrategy strategy) {
      this.searcherRemediatorMap.put(
          Objects.requireNonNull(searcher), Objects.requireNonNull(strategy));
      return this;
    }

    public Builder withFunctions(
        final Predicate searcherMatcher,
        final BiFunction fixer) {
      this.searcherRemediatorMap.put(
          new FixCandidateSearcher.Builder()
              .withMatcher(Objects.requireNonNull(searcherMatcher))
              .build(),
          new ModularRemediationStrategy(fixer));
      return this;
    }

    public SearcherStrategyRemediator build() {
      return new SearcherStrategyRemediator<>(searcherRemediatorMap);
    }
  }

  private final Map, RemediationStrategy> searcherRemediatorMap;

  protected SearcherStrategyRemediator(
      Map, RemediationStrategy> searcherRemediatorMap) {
    this.searcherRemediatorMap = searcherRemediatorMap;
  }

  /** Remediate with a chosen searcher-strategy pair. */
  private Pair, List> remediateWithStrategy(
      final CompilationUnit cu,
      final String path,
      final DetectorRule detectorRule,
      final Collection findingsForPath,
      final Function findingIdExtractor,
      final Function findingStartLineExtractor,
      final Function> findingEndLineExtractor,
      final Function> findingColumnExtractor,
      final FixCandidateSearcher searcher,
      final RemediationStrategy strategy) {

    FixCandidateSearchResults results =
        searcher.search(
            cu,
            path,
            detectorRule,
            new ArrayList<>(findingsForPath),
            findingIdExtractor,
            findingStartLineExtractor,
            findingEndLineExtractor,
            findingColumnExtractor);

    if (findingsForPath.isEmpty()) {
      return Pair.with(List.of(), List.of());
    }

    final List unfixedFindings = new ArrayList<>(results.unfixableFindings());
    final List changes = new ArrayList<>();

    for (FixCandidate fixCandidate : results.fixCandidates()) {
      List issues = fixCandidate.issues();
      Integer line = findingStartLineExtractor.apply(issues.get(0));

      if (line == null) {
        issues.forEach(
            issue -> {
              final String id = findingIdExtractor.apply(issue);
              final UnfixedFinding unfixableFinding =
                  new UnfixedFinding(id, detectorRule, path, null, "No line number provided");
              unfixedFindings.add(unfixableFinding);
            });
        continue;
      }

      // Try to fix, gathering the added dependencies if successful
      var maybeDeps = strategy.fix(cu, fixCandidate.node());
      if (maybeDeps.isSuccess()) {
        List fixedFindings =
            issues.stream()
                .map(findingIdExtractor)
                .map(findingId -> new FixedFinding(findingId, detectorRule))
                .toList();

        changes.add(CodemodChange.from(line, maybeDeps.getDependencies(), fixedFindings));
      } else {
        issues.forEach(
            issue -> {
              final String id = findingIdExtractor.apply(issue);
              final UnfixedFinding unfixableFinding =
                  new UnfixedFinding(id, detectorRule, path, line, maybeDeps.getReason());
              unfixedFindings.add(unfixableFinding);
            });
      }
    }
    return Pair.with(changes, unfixedFindings);
  }

  public CodemodFileScanningResult remediateAll(
      final CompilationUnit cu,
      final String path,
      final DetectorRule detectorRule,
      final Collection findingsForPath,
      final Function findingIdExtractor,
      final Function findingStartLineExtractor,
      final Function> findingEndLineExtractor,
      final Function> findingColumnExtractor) {

    List allChanges = new ArrayList<>();
    List allUnfixed = new ArrayList<>();

    for (var searcherAndStrategy : searcherRemediatorMap.entrySet()) {
      var pairResult =
          remediateWithStrategy(
              cu,
              path,
              detectorRule,
              findingsForPath,
              findingIdExtractor,
              findingStartLineExtractor,
              findingEndLineExtractor,
              findingColumnExtractor,
              searcherAndStrategy.getKey(),
              searcherAndStrategy.getValue());
      allChanges.addAll(pairResult.getValue0());
      allUnfixed.addAll(pairResult.getValue1());
    }
    return CodemodFileScanningResult.from(allChanges, allUnfixed);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy