io.codemodder.remediation.SearcherStrategyRemediator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of codemodder-base Show documentation
Show all versions of codemodder-base Show documentation
Base framework for writing codemods in Java
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.Triplet;
/**
* 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 withMatchAndFixStrategy(final MatchAndFixStrategy maf) {
Objects.requireNonNull(maf);
this.searcherRemediatorMap.put(
new FixCandidateSearcher.Builder().withMatcher(maf::match).build(), maf);
return this;
}
public Builder withMatchAndFixStrategyAndNodeMatcher(
final MatchAndFixStrategy maf, final NodePositionMatcher nodeMatcher) {
Objects.requireNonNull(maf);
this.searcherRemediatorMap.put(
new FixCandidateSearcher.Builder()
.withMatcher(maf::match)
.withNodePositionMatcher(nodeMatcher)
.build(),
maf);
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 Triplet, List, List> remediateWithStrategy(
final CompilationUnit cu,
final String path,
final DetectorRule detectorRule,
final List findingsForPath,
final Function findingIdExtractor,
final Function findingStartLineExtractor,
final Function> findingEndLineExtractor,
final Function> findingColumnExtractor,
final FixCandidateSearcher searcher,
final RemediationStrategy strategy) {
if (findingsForPath.isEmpty()) {
return Triplet.with(List.of(), List.of(), findingsForPath);
}
FixCandidateSearchResults results =
searcher.search(
cu,
path,
detectorRule,
findingsForPath,
findingIdExtractor,
findingStartLineExtractor,
findingEndLineExtractor,
findingColumnExtractor);
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 Triplet.with(changes, unfixedFindings, results.unmatchedIssues());
}
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> findingStartColumnExtractor) {
List findings = new ArrayList<>(findingsForPath);
List allChanges = new ArrayList<>();
List allUnfixed = new ArrayList<>();
for (var searcherAndStrategy : searcherRemediatorMap.entrySet()) {
var tripletResults =
remediateWithStrategy(
cu,
path,
detectorRule,
findings,
findingIdExtractor,
findingStartLineExtractor,
findingEndLineExtractor,
findingStartColumnExtractor,
searcherAndStrategy.getKey(),
searcherAndStrategy.getValue());
allChanges.addAll(tripletResults.getValue0());
allUnfixed.addAll(tripletResults.getValue1());
findings = tripletResults.getValue2();
}
// Any remaining, unmatched, findings are treated as unfixed
allUnfixed.addAll(
findings.stream()
.map(
f ->
new UnfixedFinding(
findingIdExtractor.apply(f),
detectorRule,
path,
findingStartLineExtractor.apply(f),
RemediationMessages.noNodesAtThatLocation))
.toList());
return CodemodFileScanningResult.from(allChanges, allUnfixed);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy