io.codemodder.plugins.maven.operator.POMOperator Maven / Gradle / Ivy
Show all versions of codemodder-plugin-maven Show documentation
package io.codemodder.plugins.maven.operator;
import com.github.zafarkhaja.semver.Version;
import io.codemodder.DependencyGAV;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.*;
import javax.xml.stream.XMLStreamException;
import org.dom4j.DocumentException;
import org.jetbrains.annotations.NotNull;
/** Facade for the POM Operator, providing methods for modifying and querying POM files. */
public class POMOperator {
private final POMScanner pomScanner;
public POMOperator(final Path pomFile, final Path projectDir) {
this.pomScanner = new POMScanner(pomFile, projectDir);
}
public POMScanner getPomScanner() {
return pomScanner;
}
/**
* Modifies and retrieves the ProjectModel with a new dependency.
*
* @param newDependencyGAV The new DependencyGAV to add to the POM.
* @return The modified ProjectModel, or null if the modification was unsuccessful.
* @throws XMLStreamException If an error occurs during XML stream processing.
* @throws URISyntaxException If there is an issue with the URI syntax.
* @throws IOException If an I/O error occurs.
* @throws DocumentException If an error occurs while parsing the document.
*/
public ProjectModel addDependency(final DependencyGAV newDependencyGAV)
throws XMLStreamException, URISyntaxException, IOException, DocumentException {
final Dependency newDependency = new Dependency(newDependencyGAV);
final ProjectModel projectModel =
pomScanner
.scanFrom()
.withDependency(newDependency)
.withSkipIfNewer(true)
.withUseProperties(true)
.withRepositoryPath(FileUtils.createTempDirectoryWithPermissions())
.build();
return modify(projectModel) ? projectModel : null;
}
/**
* Retrieves all found dependencies in the POM.
*
* @return A collection of DependencyGAV objects representing the found dependencies.
* @throws DocumentException If an error occurs while parsing the document.
* @throws IOException If an I/O error occurs.
* @throws URISyntaxException If there is an issue with the URI syntax.
* @throws XMLStreamException If an error occurs during XML stream processing.
*/
@NotNull
public Collection getAllFoundDependencies()
throws DocumentException, IOException, URISyntaxException, XMLStreamException {
final ProjectModel originalProjectModel =
pomScanner
.scanFrom()
.withSafeQueryType()
.withRepositoryPath(FileUtils.createTempDirectoryWithPermissions())
.build();
final Collection foundDependencies = queryDependency(originalProjectModel);
return foundDependencies.stream()
.map(
dependency ->
DependencyGAV.createDefault(
dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion()))
.toList();
}
/**
* Bump a Dependency Version on a POM.
*
* @param projectModel Project Model (Context) class
* @return true if the modification was successful; otherwise, false.
* @throws URISyntaxException If there is an issue with the URI syntax.
* @throws IOException If an I/O error occurs.
* @throws XMLStreamException If an error occurs while handling XML streams.
*/
static boolean modify(ProjectModel projectModel)
throws URISyntaxException, IOException, XMLStreamException {
return CommandChain.modifyDependency().execute(projectModel);
}
/**
* Method to insert only a dependency onto a POM
*
* @param projectModel Project Model (Context) class
* @return true if the modification was successful; otherwise, false.
* @throws URISyntaxException If there is an issue with the URI syntax.
* @throws IOException If an I/O error occurs.
* @throws XMLStreamException If an error occurs while handling XML streams.
*/
static boolean insertOnly(ProjectModel projectModel)
throws URISyntaxException, IOException, XMLStreamException {
return CommandChain.insertDependency().execute(projectModel);
}
/**
* Method to update only a dependency (its version) onto a POM
*
* @param projectModel Project Model (Context) class
* @return true if the modification was successful; otherwise, false.
* @throws URISyntaxException If there is an issue with the URI syntax.
* @throws IOException If an I/O error occurs.
* @throws XMLStreamException If an error occurs while handling XML streams.
*/
static boolean updateOnly(ProjectModel projectModel)
throws URISyntaxException, IOException, XMLStreamException {
return CommandChain.updateDependency().execute(projectModel);
}
/**
* Public API - Query for all the artifacts referenced inside a POM File.
*
* @param projectModel Project Model (Context) Class
* @return a collection of Dependency objects representing the artifacts referenced in the POM.
* @throws URISyntaxException If there is an issue with the URI syntax.
* @throws IOException If an I/O error occurs.
* @throws XMLStreamException If an error occurs while handling XML streams.
*/
static Collection queryDependency(ProjectModel projectModel)
throws URISyntaxException, IOException, XMLStreamException {
return queryDependency(projectModel, Collections.emptyList());
}
/**
* Public API - Query for all the versions mentioned inside a POM File.
*
* @param projectModel Project Model (Context) Class
* @return an optional VersionQueryResponse object containing source and target versions, if
* found.
* @throws URISyntaxException If there is an issue with the URI syntax.
* @throws IOException If an I/O error occurs.
* @throws XMLStreamException If an error occurs while handling XML streams.
*/
static Optional queryVersions(ProjectModel projectModel)
throws URISyntaxException, IOException, XMLStreamException {
Set queryVersionResult =
queryVersions(projectModel, Collections.emptyList());
/*
* Likely Source / Target
*/
if (queryVersionResult.size() == 2) {
/*
* but if there's `release` we`ll throw an exception
*/
if (queryVersionResult.stream().anyMatch(it -> it.getKind() == Kind.RELEASE)) {
throw new IllegalStateException(
"Unexpected queryVersionResult Combination: " + queryVersionResult);
}
VersionDefinition queryVersionSource =
queryVersionResult.stream()
.filter(it -> it.getKind() == Kind.SOURCE)
.findFirst()
.orElseThrow(() -> new IllegalStateException("Missing source version"));
VersionDefinition queryVersionTarget =
queryVersionResult.stream()
.filter(it -> it.getKind() == Kind.TARGET)
.findFirst()
.orElseThrow(() -> new IllegalStateException("Missing target version"));
Version mappedSourceVersion = mapVersion(queryVersionSource.getValue());
Version mappedTargetVersion = mapVersion(queryVersionTarget.getValue());
return Optional.of(new VersionQueryResponse(mappedSourceVersion, mappedTargetVersion));
}
/** Could be either source, target or release - we pick the value anyway */
if (queryVersionResult.size() == 1) {
List queryVersionResultList =
queryVersionResult != null && !queryVersionResult.isEmpty()
? queryVersionResult.stream().toList()
: Collections.emptyList();
Version mappedVersion = mapVersion(queryVersionResultList.get(0).getValue());
VersionQueryResponse returnValue = new VersionQueryResponse(mappedVersion, mappedVersion);
return Optional.of(returnValue);
}
return Optional.empty();
}
/**
* Given a version string, formats and returns it as a semantic version object.
*
* Versions starting with "1." are appended with ".0".
*
*
Other versions are appended with ".0.0".
*
* @param version The version string to map.
* @return the mapped semantic version.
*/
private static Version mapVersion(String version) {
String fixedVersion = version + (version.startsWith("1.") ? ".0" : ".0.0");
return Version.valueOf(fixedVersion);
}
/**
* Internal Use (package-wide) - Query for dependencies mentioned on a POM.
*
* @param projectModel Project Model (Context) class
* @param commandList do not use (required for tests)
*/
static Collection queryDependency(
ProjectModel projectModel, List commandList)
throws URISyntaxException, IOException, XMLStreamException {
CommandChain chain = CommandChain.createForDependencyQuery(projectModel.getQueryType());
executeChain(commandList, chain, projectModel);
AbstractQueryCommand lastCommand = null;
for (int i = chain.getCommandList().size() - 1; i >= 0; i--) {
if (chain.getCommandList().get(i) instanceof AbstractQueryCommand) {
lastCommand = (AbstractQueryCommand) chain.getCommandList().get(i);
if (lastCommand.getResult() != null) {
break;
}
}
}
if (lastCommand == null) {
return Collections.emptyList();
}
return lastCommand.getResult();
}
/**
* Internal Use (package-wide) - Query for versions mentioned on a POM.
*
* @param projectModel Project Model (Context) class
* @param commandList do not use (required for tests)
*/
static Set queryVersions(ProjectModel projectModel, List commandList)
throws URISyntaxException, IOException, XMLStreamException {
CommandChain chain = CommandChain.createForVersionQuery(projectModel.getQueryType());
executeChain(commandList, chain, projectModel);
AbstractVersionCommand lastCommand = null;
for (int i = chain.getCommandList().size() - 1; i >= 0; i--) {
if (chain.getCommandList().get(i) instanceof AbstractVersionCommand) {
lastCommand = (AbstractVersionCommand) chain.getCommandList().get(i);
if (lastCommand.result != null && !lastCommand.result.isEmpty()) {
break;
}
}
}
if (lastCommand == null) {
return Collections.emptySet();
}
return lastCommand.result;
}
private static void executeChain(
List commandList, CommandChain chain, ProjectModel projectModel)
throws URISyntaxException, IOException, XMLStreamException {
if (!commandList.isEmpty()) {
chain.getCommandList().clear();
chain.getCommandList().addAll(commandList);
}
chain.execute(projectModel);
}
}