io.codemodder.plugins.maven.operator.QueryByParsing Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of codemodder-plugin-maven Show documentation
Show all versions of codemodder-plugin-maven Show documentation
Plugin for providing Maven dependency management functions to codemods.
package io.codemodder.plugins.maven.operator;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Parses a Maven POM focusing on extracting dependency information without relying on any Maven
* infrastructure.
*/
class QueryByParsing extends AbstractQueryCommand {
private final Set dependencies = new LinkedHashSet<>();
private final Set dependencyManagement =
new TreeSet<>(
new Comparator() {
@Override
public int compare(Dependency o1, Dependency o2) {
if (o1 == o2) return 0;
if (o1 == null) return 1;
if (o2 == null) return -1;
return new CompareToBuilder()
.append(o1.getGroupId(), o2.getGroupId())
.append(o1.getArtifactId(), o2.getArtifactId())
.toComparison();
}
});
private final Map properties = new LinkedHashMap<>();
private final StrSubstitutor strSubstitutor = new StrSubstitutor(properties);
@Override
public void extractDependencyTree(Path outputPath, Path pomFilePath, ProjectModel c) {
// Not implemented
}
/**
* Executes the parsing strategy to extract dependency information from POM files.
*
* @param pm The ProjectModel containing input parameters for the operation.
* @return `true` if the operation succeeds; otherwise, `false`.
*/
@Override
public boolean execute(ProjectModel pm) {
/** Enlist all pom files given an hierarchy */
List pomFilesByHierarchy = pm.allPomFiles();
Collections.reverse(pomFilesByHierarchy);
for (POMDocument pomDocument : pomFilesByHierarchy) {
updateProperties(pomDocument);
updateDependencyManagement(pomDocument);
updateDependencies(pomDocument);
}
this.result = dependencies;
return true;
}
private void updateDependencyManagement(POMDocument pomDocument) {
Collection dependencyManagementDependenciesToAdd = new ArrayList<>();
Element dependencyManagementElement =
pomDocument.getPomDocument().getRootElement().element("dependencyManagement");
if (dependencyManagementElement != null) {
List dependencyElements =
dependencyManagementElement.element("dependencies").elements("dependency");
for (Element dependencyElement : dependencyElements) {
String groupId = getElementTextOrNull(dependencyElement, "groupId");
String artifactId = getElementTextOrNull(dependencyElement, "artifactId");
String version =
Optional.ofNullable(dependencyElement.elementText("version")).orElse(UNKNOWN);
String classifier = getElementTextOrNull(dependencyElement, "classifier");
String packaging = getElementTextOrNull(dependencyElement, "packaging");
try {
version = strSubstitutor.replace(version);
} catch (IllegalStateException e) {
logger.warn("while interpolating version", e);
version = UNKNOWN;
}
Dependency dependency =
new Dependency(groupId, artifactId, version, classifier, packaging, null);
dependencyManagementDependenciesToAdd.add(dependency);
}
}
this.dependencyManagement.addAll(dependencyManagementDependenciesToAdd);
}
private Dependency lookForDependencyManagement(String groupId, String artifactId) {
for (Dependency dependency : dependencyManagement) {
if (Objects.equals(dependency.getGroupId(), groupId)
&& Objects.equals(dependency.getArtifactId(), artifactId)) {
return dependency;
}
}
return null;
}
private void updateDependencies(POMDocument pomDocument) {
Collection dependenciesToAdd = new ArrayList<>();
Element dependenciesElement =
pomDocument.getPomDocument().getRootElement().element("dependencies");
if (dependenciesElement != null) {
List dependencyElements = dependenciesElement.elements("dependency");
for (Element dependencyElement : dependencyElements) {
String groupId = getElementTextOrNull(dependencyElement, "groupId");
String artifactId = getElementTextOrNull(dependencyElement, "artifactId");
String version =
Optional.ofNullable(dependencyElement.elementText("version")).orElse(UNKNOWN);
Dependency proposedDependency = lookForDependencyManagement(groupId, artifactId);
if (proposedDependency != null) {
dependenciesToAdd.add(proposedDependency);
} else {
String classifier = getElementTextOrNull(dependencyElement, "classifier");
String packaging = getElementTextOrNull(dependencyElement, "packaging");
try {
version = strSubstitutor.replace(version);
} catch (IllegalStateException e) {
logger.warn("while interpolating version", e);
version = UNKNOWN;
}
Dependency dependency =
new Dependency(groupId, artifactId, version, classifier, packaging, null);
dependenciesToAdd.add(dependency);
}
}
}
this.dependencies.addAll(dependenciesToAdd);
}
private String getElementTextOrNull(Element parent, String elementName) {
Element child = parent.element(elementName);
return child != null ? child.getText() : null;
}
/** Updates the Properties member variable based on whats on the POMDocument */
private void updateProperties(POMDocument pomDocument) {
Map propsDefined = ProjectModel.propertiesDefinedOnPomDocument(pomDocument);
for (Map.Entry entry : propsDefined.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (!value.matches(RE_INTERPOLATION.pattern())) {
properties.put(key, value);
}
if (!value.matches(RE_INTERPOLATION.pattern())) {
String newValue;
try {
Matcher matcher = RE_INTERPOLATION.matcher(value);
StringBuffer resultBuffer = new StringBuffer();
while (matcher.find()) {
String variable = matcher.group();
String replacement = strSubstitutor.replace(variable);
matcher.appendReplacement(resultBuffer, Matcher.quoteReplacement(replacement));
}
matcher.appendTail(resultBuffer);
newValue = resultBuffer.toString();
} catch (IllegalStateException e) {
LOGGER.warn("while replacing variables: ", e);
newValue = value;
}
properties.put(key, newValue);
}
}
}
private static final Pattern RE_INTERPOLATION =
Pattern.compile(".*\\$\\{[\\p{Alnum}.\\-_]+\\}.*");
private static final Logger logger = LoggerFactory.getLogger(QueryByParsing.class);
private static final String UNKNOWN = "UNKNOWN";
}