Skip to content

Commit

Permalink
Resolves mojohaus#474: Add property support for UseDepVersion
Browse files Browse the repository at this point in the history
- adding a new parameter, processProperties, which will also update property values if dependencies are controlled by properties
  • Loading branch information
andrzejj0 committed Dec 23, 2022
1 parent 2bed457 commit a244930
Show file tree
Hide file tree
Showing 33 changed files with 1,483 additions and 122 deletions.
2 changes: 0 additions & 2 deletions versions-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,9 @@
<version>${mavenVersion}</version>
<scope>provided</scope>
</dependency>
<!-- woodstox only used for unit tests -->
<dependency>
<groupId>com.fasterxml.woodstox</groupId>
<artifactId>woodstox-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* under the License.
*/

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;

Expand All @@ -28,7 +29,10 @@
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
Expand All @@ -37,6 +41,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.Stack;
Expand All @@ -45,6 +50,7 @@
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
Expand All @@ -65,13 +71,15 @@
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.project.ProjectBuildingResult;
import org.codehaus.mojo.versions.rewriting.ModifiedPomXMLEventReader;
import org.codehaus.mojo.versions.utils.ModelNode;
import org.codehaus.mojo.versions.utils.RegexUtils;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.codehaus.stax2.XMLInputFactory2;

import static java.util.Collections.singletonMap;
import static java.util.stream.IntStream.range;
Expand All @@ -88,7 +96,7 @@ public class PomHelper {
/**
* Gets the raw model before any interpolation what-so-ever.
*
* @param project The project to get the raw model for.
* @param project The project to getModel the raw model for.
* @return The raw model.
* @throws IOException if the file is not found or if the file does not parse.
*/
Expand All @@ -99,7 +107,7 @@ public static Model getRawModel(MavenProject project) throws IOException {
/**
* Gets the raw model before any interpolation what-so-ever.
*
* @param moduleProjectFile The project file to get the raw model for.
* @param moduleProjectFile The project file to getModel the raw model for.
* @return The raw model.
* @throws IOException if the file is not found or if the file does not parse.
*/
Expand All @@ -115,21 +123,23 @@ public static Model getRawModel(File moduleProjectFile) throws IOException {
/**
* Gets the current raw model before any interpolation what-so-ever.
*
* @param modifiedPomXMLEventReader The {@link ModifiedPomXMLEventReader} to get the raw model for.
* @param modifiedPomXMLEventReader The {@link ModifiedPomXMLEventReader} to getModel the raw model for.
* @return The raw model.
* @throws IOException if the file is not found or if the file does not parse.
*/
public static Model getRawModel(ModifiedPomXMLEventReader modifiedPomXMLEventReader) throws IOException {
try (Reader reader =
new StringReader(modifiedPomXMLEventReader.asStringBuilder().toString())) {
return getRawModel(reader);
Model result = getRawModel(reader);
result.setPomFile(new File(modifiedPomXMLEventReader.getPath()));
return result;
}
}

/**
* Gets the current raw model before any interpolation what-so-ever.
*
* @param reader The {@link Reader} to get the raw model for.
* @param reader The {@link Reader} to getModel the raw model for.
* @return The raw model.
* @throws IOException if the file is not found or if the file does not parse.
*/
Expand Down Expand Up @@ -433,7 +443,7 @@ public static boolean setProjectParentVersion(final ModifiedPomXMLEventReader po
* @param artifactId The artifactId of the dependency.
* @param oldVersion The old version of the dependency.
* @param newVersion The new version of the dependency.
* @param model The model to get the project properties from.
* @param model The model to getModel the project properties from.
* @return <code>true</code> if a replacement was made.
* @throws XMLStreamException if something went wrong.
*/
Expand Down Expand Up @@ -595,65 +605,67 @@ public static String evaluate(String expr, Map<String, String> properties) {
return null;
}

String expression = stripTokens(expr);
if (expression.equals(expr)) {
int index = expr.indexOf("${");
if (index >= 0) {
int lastIndex = expr.indexOf("}", index);
if (lastIndex >= 0) {
String retVal = expr.substring(0, index);
return extractExpression(expr)
.map(expression -> {
String value = properties.get(expression);

if (value != null) {
int exprStartDelimiter = value.indexOf("${");

if (index > 0 && expr.charAt(index - 1) == '$') {
retVal += expr.substring(index + 1, lastIndex + 1);
if (exprStartDelimiter >= 0) {
if (exprStartDelimiter > 0) {
value = value.substring(0, exprStartDelimiter)
+ evaluate(value.substring(exprStartDelimiter), properties);
} else {
value = evaluate(value.substring(exprStartDelimiter), properties);
}
}
} else {
retVal += evaluate(expr.substring(index, lastIndex + 1), properties);
// TODO find a way to log that and not use this System.out!!
// this class could be a component with logger injected !!
System.out.println("expression: " + expression + " no value ");
}
return value == null ? expr : value;
})
.orElseGet(() -> {
int index = expr.indexOf("${");
if (index >= 0) {
int lastIndex = expr.indexOf("}", index);
if (lastIndex >= 0) {
String retVal = expr.substring(0, index);

if (index > 0 && expr.charAt(index - 1) == '$') {
retVal += expr.substring(index + 1, lastIndex + 1);
} else {
retVal += evaluate(expr.substring(index, lastIndex + 1), properties);
}

retVal += evaluate(expr.substring(lastIndex + 1), properties);
return retVal;
}
}

retVal += evaluate(expr.substring(lastIndex + 1), properties);
return retVal;
}
}

// Was not an expression
if (expression.contains("$$")) {
return expression.replaceAll("\\$\\$", "\\$");
} else {
return expression;
}
}

String value = properties.get(expression);

if (value != null) {
int exprStartDelimiter = value.indexOf("${");

if (exprStartDelimiter >= 0) {
if (exprStartDelimiter > 0) {
value = value.substring(0, exprStartDelimiter)
+ evaluate(value.substring(exprStartDelimiter), properties);
} else {
value = evaluate(value.substring(exprStartDelimiter), properties);
}
}
} else {
// TODO find a way to log that and not use this System.out!!
// this class could be a component with logger injected !!
System.out.println("expression: " + expression + " no value ");
}
return value == null ? expr : value;
// Was not an expression
if (expr.contains("$$")) {
return expr.replaceAll("\\$\\$", "\\$");
} else {
return expr;
}
});
}

/**
* Strips the expression token markers from the start and end of the string.
*
* @param expr the string (perhaps with token markers)
* @return the string (definately without token markers)
* @return the string (without token markers) if a property has been found, {@link Optional#empty()}
* otherwise
*/
private static String stripTokens(String expr) {
if (expr.startsWith("${") && expr.indexOf("}") == expr.length() - 1) {
expr = expr.substring(2, expr.length() - 1);
}
return expr;
public static Optional<String> extractExpression(String expr) {
return Optional.ofNullable(expr)
.map(String::trim)
.filter(e -> e.startsWith("${") && e.indexOf("}") == e.length() - 1)
.map(e -> e.substring(2, e.length() - 1));
}

/**
Expand Down Expand Up @@ -1179,7 +1191,7 @@ public static Set<String> getAllChildModules(MavenProject project, Log logger) {
* @return the set of all child modules of the project.
*/
public static Set<String> getAllChildModules(Model model, Log logger) {
logger.debug("Finding child modules...");
logger.debug("Finding child modules of " + model);
Set<String> childModules = new TreeSet<>(model.getModules());
model.getProfiles().forEach(profile -> childModules.addAll(profile.getModules()));
debugModules(logger, "Child modules:", childModules);
Expand Down Expand Up @@ -1326,6 +1338,64 @@ public static ProjectBuildingRequest createProjectBuilderRequest(
};
}

/**
* Builds a {@link ModelNode} tree of raw models keyed by module path and returns a list of all nodes,
* ordered depth-first visiting order. The root node is always the first node of the list.
*
* @param rootNode The root node of the reactor
* @param logger logger to log parsing errors to
* @return the root node of the {@link ModelNode} of raw models relative to the project's basedir.
* @throws UncheckedIOException thrown if reading one of the models down the tree fails
*/
public static List<ModelNode> getRawModelTree(ModelNode rootNode, Log logger) throws UncheckedIOException {
XMLInputFactory inputFactory = XMLInputFactory2.newInstance();
inputFactory.setProperty(XMLInputFactory2.P_PRESERVE_LOCATION, Boolean.TRUE);
return getRawModelTree(rootNode, logger, inputFactory);
}

private static List<ModelNode> getRawModelTree(ModelNode rootNode, Log logger, XMLInputFactory inputFactory)
throws UncheckedIOException {
Path baseDir = rootNode.getModel().getPomFile().getParentFile().toPath();
List<ModelNode> result = new ArrayList<>();
result.add(rootNode);
result.addAll(getAllChildModules(rootNode.getModel(), logger).stream()
.map(baseDir::resolve)
.map(path -> Files.isDirectory(path) ? path.resolve("pom.xml") : path)
.map(pomFile -> {
try {
ModifiedPomXMLEventReader pom = new ModifiedPomXMLEventReader(
new StringBuilder(new String(Files.readAllBytes(pomFile))),
inputFactory,
pomFile.toString());
return new ModelNode(rootNode, getRawModel(pom), pom);
} catch (IOException e) {
throw new UncheckedIOException("Could not open " + pomFile, e);
} catch (XMLStreamException e) {
throw new RuntimeException("Could not parse " + pomFile, e);
}
})
.flatMap(node -> getRawModelTree(node, logger, inputFactory).stream())
.collect(Collectors.toList()));
return result;
}

/**
* Traverses the module tree upwards searching for the closest definition of a property with the given name.
*
* @param propertyName name of the property to be found
* @param node model tree node at which the search should be started
* @return {@link Optional} object containing the model tree node containing the closest
* property definition, or {@link Optional#empty()} if none has been found
*/
public static Optional<ModelNode> findProperty(String propertyName, ModelNode node) {
if (Optional.ofNullable(node.getModel().getProperties())
.map(properties -> properties.getProperty(propertyName))
.isPresent()) {
return Optional.of(node);
}
return node.getParent().flatMap(parent -> findProperty(propertyName, parent));
}

/**
* Builds a map of raw models keyed by module path.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,4 +514,8 @@ public Model parse() throws IOException, XmlPullParserException {
MavenXpp3Reader reader = new MavenXpp3Reader();
return reader.read(new StringReader(pom.toString()));
}

public String getPath() {
return path;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public static Set<Dependency> extractDependenciesFromDependencyManagement(
log.debug("dependency from pom: " + dependency.getGroupId() + ":" + dependency.getArtifactId() + ":"
+ dependency.getVersion() + ":" + dependency.getScope());
if (dependency.getVersion() == null) {
// get parent and get the information from there.
// getModel parent and getModel the information from there.
if (project.hasParent()) {
log.debug("Reading parent dependencyManagement information");
DependencyManagement parentProjectDependencyManagement = processDependencyManagementTransitive
Expand All @@ -112,10 +112,10 @@ public static Set<Dependency> extractDependenciesFromDependencyManagement(
}
}
} else {
String message = "We can't get the version for the dependency " + dependency.getGroupId() + ":"
+ dependency.getArtifactId() + " because there does not exist a parent.";
String message = "We can't getModel the version for the dependency " + dependency.getGroupId()
+ ":" + dependency.getArtifactId() + " because there does not exist a parent.";
log.error(message);
// Throw error because we will not able to get a version for a dependency.
// Throw error because we will not able to getModel a version for a dependency.
throw new VersionRetrievalException(message);
}
} else {
Expand Down
Loading

0 comments on commit a244930

Please sign in to comment.