io.codemodder.plugins.maven.CodeTFGenerator Maven / Gradle / Ivy
package io.codemodder.plugins.maven;
import com.github.difflib.DiffUtils;
import com.github.difflib.UnifiedDiffUtils;
import com.github.difflib.patch.AbstractDelta;
import com.github.difflib.patch.Patch;
import io.codemodder.DependencyDescriptor;
import io.codemodder.DependencyGAV;
import io.codemodder.codetf.CodeTFChange;
import io.codemodder.codetf.CodeTFChangesetEntry;
import io.codemodder.codetf.CodeTFDiffSide;
import io.codemodder.plugins.maven.operator.POMDocument;
import java.io.File;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Pattern;
/**
* CodeTFGenerator is responsible for generating {@link CodeTFChangesetEntry} for Maven POM updates.
*/
final class CodeTFGenerator {
private final ArtifactInjectionPositionFinder positionFinder;
private final DependencyDescriptor dependencyDescriptor;
/**
* Constructs a CodeTFGenerator with the specified artifact injection position finder and
* dependency descriptor.
*
* @param positionFinder The ArtifactInjectionPositionFinder for finding artifact positions.
* @param dependencyDescriptor The DependencyDescriptor for generating dependency descriptions.
*/
CodeTFGenerator(
final ArtifactInjectionPositionFinder positionFinder,
final DependencyDescriptor dependencyDescriptor) {
this.dependencyDescriptor = Objects.requireNonNull(dependencyDescriptor);
this.positionFinder = Objects.requireNonNull(positionFinder);
}
/**
* Get CodeTFChangesetEntry for Maven POM updates.
*
* @param projectDir The project directory where the POM is located.
* @param pomDocument The POMDocument representing the POM to be updated.
* @param newDependency The new dependency to be added to the POM.
* @return CodeTFChangesetEntry representing the POM update.
*/
CodeTFChangesetEntry getChanges(
final Path projectDir, final POMDocument pomDocument, final DependencyGAV newDependency) {
final List originalPomContents =
getLinesFrom(pomDocument, pomDocument.getOriginalPom());
final List finalPomContents =
getLinesFrom(pomDocument, pomDocument.getResultPomBytes());
final Patch patch = DiffUtils.diff(originalPomContents, finalPomContents);
final List> deltas = patch.getDeltas();
final int position = positionFinder.find(deltas, newDependency.artifact());
final Path pomDocumentPath = getPomDocumentPath(pomDocument);
final String relativePomPath = projectDir.relativize(pomDocumentPath).toString();
final String description = dependencyDescriptor.create(newDependency);
final Map properties = buildPropertiesMap(description);
// Use RIGHT side as the diff side for now, as we are only adding dependencies.
final CodeTFChange change =
new CodeTFChange(
position,
properties,
description,
CodeTFDiffSide.RIGHT,
List.of(),
List.of(),
List.of());
final List patchDiff =
UnifiedDiffUtils.generateUnifiedDiff(
relativePomPath, relativePomPath, originalPomContents, patch, 3);
final String diff = String.join(pomDocument.getEndl(), patchDiff);
return new CodeTFChangesetEntry(relativePomPath, diff, List.of(change));
}
private Path getPomDocumentPath(final POMDocument pomDocument) {
try {
return new File(pomDocument.getPomPath().toURI()).toPath();
} catch (URISyntaxException e) {
throw new MavenProvider.DependencyUpdateException(
"Failure on URI for " + pomDocument.getPomPath(), e);
}
}
private Map buildPropertiesMap(final String description) {
return description != null && !description.isBlank()
? Map.of("contextual_description", "true")
: Collections.emptyMap();
}
private List getLinesFrom(final POMDocument doc, final byte[] byteArray) {
return Arrays.asList(
new String(byteArray, doc.getCharset()).split(Pattern.quote(doc.getEndl())));
}
}