org.wildfly.channelplugin.manipulation.PomManipulator Maven / Gradle / Ivy
Show all versions of wildfly-channel-maven-plugin
package org.wildfly.channelplugin.manipulation;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Stack;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import org.apache.http.util.Asserts;
import org.codehaus.mojo.versions.api.PomHelper;
import org.codehaus.mojo.versions.rewriting.ModifiedPomXMLEventReader;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.WriterFactory;
import org.codehaus.stax2.XMLInputFactory2;
import org.commonjava.maven.atlas.ident.ref.ArtifactRef;
import org.commonjava.maven.atlas.ident.ref.ProjectRef;
import org.commonjava.maven.ext.common.model.Project;
/**
* Provides functionality to manipulate properties and dependencies in a POM file.
*/
public class PomManipulator {
private static final String DEPENDENCY_MANAGEMENT_PATH = "/project/dependencyManagement";
private static final String DEPENDENCY_MANAGEMENT_DEPENDENCIES_PATH = "/project/dependencyManagement/dependencies";
private static final String REPOSITORIES_PATH = "/project/repositories";
private static final String PLUGIN_REPOSITORIES_PATH = "/project/pluginRepositories";
private static final String PROJECT = "project";
private static final String DEPENDENCY_MANAGEMENT = "dependencyManagement";
private static final String DEPENDENCIES = "dependencies";
private static final String REPOSITORIES = "repositories";
private static final String PLUGIN_REPOSITORIES = "pluginRepositories";
private final Project project;
private final ModifiedPomXMLEventReader eventReader;
private final StringBuilder content;
private boolean closed = false;
/**
* @param project Project instance
*/
public PomManipulator(Project project) {
try {
this.project = project;
XMLInputFactory inputFactory = XMLInputFactory2.newInstance();
inputFactory.setProperty(XMLInputFactory2.P_PRESERVE_LOCATION, Boolean.TRUE);
this.content = PomHelper.readXmlFile(project.getPom());
this.eventReader = new ModifiedPomXMLEventReader(content, inputFactory, project.getPom().getPath());
} catch (IOException | XMLStreamException e) {
throw new RuntimeException("Couldn't initialize PomWriter instance", e);
}
}
public void overrideDependencyVersion(ArtifactRef d, String newVersion) throws XMLStreamException {
PomHelper.setDependencyVersion(eventReader, d.getGroupId(), d.getArtifactId(), d.getVersionString(),
newVersion, project.getModel());
}
public void overrideDependencyVersion(String groupId, String artifactId, String oldVersionString, String newVersion) throws XMLStreamException {
PomHelper.setDependencyVersion(eventReader, groupId, artifactId, oldVersionString, newVersion,
project.getModel());
}
public void overrideDependencyVersionWithComment(ArtifactRef d, String newVersion) throws XMLStreamException {
overrideDependencyVersionWithComment(eventReader, d.getGroupId(), d.getArtifactId(), d.getVersionString(), newVersion);
}
static void overrideDependencyVersionWithComment(final ModifiedPomXMLEventReader eventReader,
final String groupId, final String artifactId,
final String oldVersionString, final String newVersion)
throws XMLStreamException {
eventReader.rewind();
Stack stack = new Stack<>();
String path = "";
List locations = Arrays.asList(
"/project/dependencyManagement/dependencies/dependency",
"/project/dependencies/dependency"
);
boolean matchingGroupId = false;
boolean matchingArtifactId = false;
boolean hasVersion = false;
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
if (event.isStartElement()) {
String parentElementPath = path;
stack.push(path);
String elementName = event.asStartElement().getName().getLocalPart();
path = path + "/" + elementName;
if (locations.contains(parentElementPath)) {
switch (elementName) {
case "groupId":
matchingGroupId = groupId.equals(eventReader.getElementText().trim());
path = stack.pop();
break;
case "artifactId":
matchingArtifactId = artifactId.equals(eventReader.getElementText().trim());
path = stack.pop();
break;
case "version":
eventReader.mark(0);
break;
}
}
} else if (event.isEndElement()) {
String parentPath = stack.peek();
String elementName = event.asEndElement().getName().getLocalPart();
if (locations.contains(parentPath) || locations.contains(path)) {
switch (elementName) {
case "version":
eventReader.mark(1);
String versionString = eventReader.getBetween(0, 1);
hasVersion = !versionString.trim().isEmpty();
break;
case "dependency":
if (matchingGroupId && matchingArtifactId && hasVersion) {
eventReader.replaceMark(1, " ");
eventReader.replaceBetween(0, 1, newVersion);
}
matchingGroupId = false;
matchingArtifactId = false;
hasVersion = false;
eventReader.clearMark(0);
eventReader.clearMark(1);
break;
}
}
path = stack.pop();
}
}
}
public boolean overrideProperty(String propertyName, String propertyValue) throws XMLStreamException {
return PomHelper.setPropertyVersion(eventReader, null, propertyName, propertyValue);
}
public void injectManagedDependency(ArtifactRef dependency, Collection exclusions, String oldVersion)
throws XMLStreamException {
injectDependencyManagementSection(eventReader);
injectDependencyManagementDependenciesSection(eventReader);
injectManagedDependency(eventReader, dependency, exclusions, oldVersion);
}
static void injectRepositoriesSection(ModifiedPomXMLEventReader eventReader) throws XMLStreamException {
eventReader.rewind();
Stack stack = new Stack();
String path = "";
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
if (event.isStartElement()) {
if (path.equals("/project") && event.asStartElement().getName().getLocalPart().equals(REPOSITORIES)) {
// section is already present
return;
}
stack.push(path);
path = path + "/" + event.asStartElement().getName().getLocalPart();
} else if (event.isEndElement()) {
if (event.asEndElement().getName().getLocalPart().equals(PROJECT)) {
eventReader.mark(0);
eventReader.replaceMark(0, composeRepositoriesElementString()
+ ""
);
eventReader.clearMark(0);
return;
}
path = stack.pop();
}
}
}
static void injectPluginRepositoriesSection(ModifiedPomXMLEventReader eventReader) throws XMLStreamException {
eventReader.rewind();
Stack stack = new Stack();
String path = "";
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
if (event.isStartElement()) {
if (path.equals("/project") && event.asStartElement().getName().getLocalPart().equals(PLUGIN_REPOSITORIES)) {
// section is already present
return;
}
stack.push(path);
path = path + "/" + event.asStartElement().getName().getLocalPart();
} else if (event.isEndElement()) {
if (event.asEndElement().getName().getLocalPart().equals(PROJECT)) {
eventReader.mark(0);
eventReader.replaceMark(0, composePluginRepositoriesElementString()
+ ""
);
eventReader.clearMark(0);
return;
}
path = stack.pop();
}
}
}
public void injectRepository(String id, String url) throws XMLStreamException {
injectRepository(eventReader, id, url);
}
public void injectPluginRepository(String id, String url) throws XMLStreamException {
injectPluginRepository(eventReader, id, url);
}
static void injectRepository(ModifiedPomXMLEventReader eventReader, String id, String url) throws XMLStreamException {
injectRepositoriesSection(eventReader);
eventReader.rewind();
Stack stack = new Stack();
String path = "";
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
if (event.isStartElement()) {
stack.push(path);
path = path + "/" + event.asStartElement().getName().getLocalPart();
} else if (event.isEndElement()) {
// replaces "" end element with new repository + the end element
String elementName = event.asEndElement().getName().getLocalPart();
if (elementName.equals(REPOSITORIES) && path.equals(REPOSITORIES_PATH)) {
eventReader.mark(0);
eventReader.replaceMark(0, composeRepositoryElementString(id, url)
+ " "
);
eventReader.clearMark(0);
break;
}
path = stack.pop();
}
}
}
static void injectPluginRepository(ModifiedPomXMLEventReader eventReader, String id, String url) throws XMLStreamException {
injectPluginRepositoriesSection(eventReader);
eventReader.rewind();
Stack stack = new Stack<>();
String path = "";
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
if (event.isStartElement()) {
stack.push(path);
path = path + "/" + event.asStartElement().getName().getLocalPart();
} else if (event.isEndElement()) {
// replaces "" end element with new repository + the end element
String elementName = event.asEndElement().getName().getLocalPart();
if (elementName.equals(PLUGIN_REPOSITORIES) && path.equals(PLUGIN_REPOSITORIES_PATH)) {
eventReader.mark(0);
eventReader.replaceMark(0, composePluginRepositoryElementString(id, url)
+ " "
);
eventReader.clearMark(0);
break;
}
path = stack.pop();
}
}
}
public void injectProperty(String key, String version) throws XMLStreamException {
injectProperty(eventReader, key, version);
}
/**
* Writes the updated POM file.
*/
public void writePom() {
assertOpen();
try (Writer writer = WriterFactory.newXmlWriter(project.getPom())) {
closed = true;
IOUtil.copy(content.toString(), writer);
eventReader.close();
} catch (IOException e) {
throw new RuntimeException("Failed to write to the pom file", e);
} catch (XMLStreamException e) {
throw new RuntimeException("Couldn't close event reader", e);
}
}
private void assertOpen() {
Asserts.check(!closed, "This instance cannot be used repeatedly.");
}
/**
* This method attempts to inject new dependency into at the end of the dependencyManagement section.
*
* The dependencyManagement section must be already present in the POM.
*/
static void injectManagedDependency(ModifiedPomXMLEventReader eventReader, ArtifactRef dependency,
Collection exclusions, String oldVersion) throws XMLStreamException {
eventReader.rewind();
Stack stack = new Stack<>();
String path = "";
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
if (event.isStartElement()) {
stack.push(path);
path = path + "/" + event.asStartElement().getName().getLocalPart();
} else if (event.isEndElement()) {
if (event.asEndElement().getName().getLocalPart().equals(DEPENDENCIES)
&& path.equals(DEPENDENCY_MANAGEMENT_DEPENDENCIES_PATH)) {
eventReader.mark(0);
eventReader.replaceMark(0, composeDependencyElementString(dependency, exclusions, oldVersion)
+ " "
);
eventReader.clearMark(0);
return;
}
path = stack.pop();
}
}
}
/**
* Inserts dependencyManagement section if it's not already present.
*/
static void injectDependencyManagementSection(ModifiedPomXMLEventReader eventReader) throws XMLStreamException {
eventReader.rewind();
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
if (event.isStartElement()) {
if (event.asStartElement().getName().getLocalPart().equals(DEPENDENCY_MANAGEMENT)) {
// dependency management is already present
return;
}
} else if (event.isEndElement()) {
if (event.asEndElement().getName().getLocalPart().equals(PROJECT)) {
eventReader.mark(0);
eventReader.replaceMark(0, composeDependencyManagementElementString()
+ ""
);
eventReader.clearMark(0);
return;
}
}
}
}
static void injectDependencyManagementDependenciesSection(ModifiedPomXMLEventReader eventReader)
throws XMLStreamException {
eventReader.rewind();
Stack stack = new Stack<>();
String path = "";
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
if (event.isStartElement()) {
if (event.asStartElement().getName().getLocalPart().equals(DEPENDENCIES)
&& path.equals(DEPENDENCY_MANAGEMENT_PATH)) {
// dependencies section is already present
return;
}
stack.push(path);
path = path + "/" + event.asStartElement().getName().getLocalPart();
} else if (event.isEndElement()) {
if (event.asEndElement().getName().getLocalPart().equals(DEPENDENCY_MANAGEMENT)) {
eventReader.mark(0);
eventReader.replaceMark(0, composeDependenciesElementString()
+ " "
);
eventReader.clearMark(0);
return;
}
path = stack.pop();
}
}
}
/**
* This method attempts to inject new property at the end of the properties section.
*
* The properties section must be already present in the POM.
*/
static void injectProperty(ModifiedPomXMLEventReader eventReader, String key, String version) throws XMLStreamException {
eventReader.rewind();
Stack stack = new Stack<>();
String path = "";
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
if (event.isStartElement()) {
stack.push(path);
path = path + "/" + event.asStartElement().getName().getLocalPart();
} else if (event.isEndElement()) {
if (event.asEndElement().getName().getLocalPart().equals("properties")
&& path.equals("/project/properties")) {
eventReader.mark(0);
eventReader.replaceMark(0, String.format(" <%s>%s%s>\n", key, version, key)
+ " "
);
eventReader.clearMark(0);
break;
}
path = stack.pop();
}
}
}
private static String composeDependencyManagementElementString() {
return " \n"
+ " \n";
}
private static String composeDependenciesElementString() {
return " \n"
+ " \n";
}
private static String composeRepositoriesElementString() {
return " \n"
+ " \n";
}
private static String composePluginRepositoriesElementString() {
return " \n"
+ " \n";
}
private static String composeDependencyElementString(ArtifactRef artifact, Collection exclusions, String oldVersion) {
StringBuilder sb = new StringBuilder();
sb.append(" \n");
sb.append(String.format(" %s \n", artifact.getGroupId()));
sb.append(String.format(" %s \n", artifact.getArtifactId()));
sb.append(String.format(" %s \n", artifact.getVersionString(), oldVersion));
if (artifact.getClassifier() != null) {
sb.append(String.format(" %s \n", artifact.getClassifier()));
}
if (!"jar".equals(artifact.getType())) {
sb.append(String.format(" %s \n", artifact.getType()));
}
if (exclusions != null && !exclusions.isEmpty()) {
sb.append(" \n");
for (ProjectRef e: exclusions) {
sb.append(" \n");
sb.append(" " + e.getGroupId() + " \n");
sb.append(" " + e.getArtifactId() + " \n");
sb.append(" \n");
}
sb.append(" \n");
}
sb.append(" \n");
return sb.toString();
}
private static String composeRepositoryElementString(String id, String url) {
StringBuilder sb = new StringBuilder();
sb.append(" \n");
sb.append(String.format(" %s \n", id));
sb.append(String.format(" %s \n", url));
sb.append(" \n");
sb.append(" true \n");
sb.append(" always \n");
sb.append(" \n");
sb.append(" \n");
sb.append(" true \n");
sb.append(" always \n");
sb.append(" \n");
sb.append(" \n");
return sb.toString();
}
private static String composePluginRepositoryElementString(String id, String url) {
StringBuilder sb = new StringBuilder();
sb.append(" \n");
sb.append(String.format(" %s \n", id));
sb.append(String.format(" %s \n", url));
sb.append(" \n");
sb.append(" true \n");
sb.append(" always \n");
sb.append(" \n");
sb.append(" \n");
sb.append(" true \n");
sb.append(" always \n");
sb.append(" \n");
sb.append(" \n");
return sb.toString();
}
}