[MNG-6437] Better support for path and uri in property interpolation (#812)

This commit is contained in:
Guillaume Nodet 2023-10-17 19:55:08 +02:00 committed by GitHub
parent 32d67322ef
commit 92a82dcefd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 365 additions and 27 deletions

View File

@ -19,9 +19,11 @@
package org.apache.maven.plugin;
import java.io.File;
import java.nio.file.Path;
import java.util.Properties;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.interpolation.reflection.ReflectionValueExtractor;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.project.MavenProject;
@ -174,7 +176,13 @@ public Object evaluate(String expr, Class<?> type) throws ExpressionEvaluationEx
if (pathSeparator > 0) {
String pathExpression = expression.substring(0, pathSeparator);
value = ReflectionValueExtractor.evaluate(pathExpression, session);
value = value + expression.substring(pathSeparator);
if (pathSeparator < expression.length() - 1) {
if (value instanceof Path) {
value = ((Path) value).resolve(expression.substring(pathSeparator + 1));
} else {
value = value + expression.substring(pathSeparator);
}
}
} else {
value = ReflectionValueExtractor.evaluate(expression, session);
}

View File

@ -28,6 +28,7 @@
import org.apache.maven.api.Session;
import org.apache.maven.internal.impl.DefaultMojoExecution;
import org.apache.maven.internal.impl.DefaultSession;
import org.apache.maven.model.interpolation.reflection.ReflectionValueExtractor;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
@ -177,7 +178,13 @@ public Object evaluate(String expr, Class<?> type) throws ExpressionEvaluationEx
if (pathSeparator > 0) {
String pathExpression = expression.substring(0, pathSeparator);
value = ReflectionValueExtractor.evaluate(pathExpression, session);
value = value + expression.substring(pathSeparator);
if (pathSeparator < expression.length() - 1) {
if (value instanceof Path) {
value = ((Path) value).resolve(expression.substring(pathSeparator + 1));
} else {
value = value + expression.substring(pathSeparator);
}
}
} else {
value = ReflectionValueExtractor.evaluate(expression, session);
}

View File

@ -21,12 +21,14 @@
import javax.inject.Inject;
import java.io.File;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import org.apache.maven.AbstractCoreMavenComponentTestCase;
@ -34,6 +36,7 @@
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.bridge.MavenRepositorySystem;
import org.apache.maven.configuration.internal.EnhancedComponentConfigurator;
import org.apache.maven.execution.DefaultMavenExecutionRequest;
import org.apache.maven.execution.DefaultMavenExecutionResult;
import org.apache.maven.execution.MavenExecutionRequest;
@ -41,6 +44,8 @@
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.building.DefaultModelBuildingRequest;
import org.apache.maven.model.interpolation.reflection.IntrospectionException;
import org.apache.maven.model.root.RootLocator;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
@ -50,6 +55,8 @@
import org.codehaus.plexus.MutablePlexusContainer;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
import org.codehaus.plexus.configuration.DefaultPlexusConfiguration;
import org.codehaus.plexus.util.Os;
import org.junit.jupiter.api.Test;
import static org.codehaus.plexus.testing.PlexusExtension.getTestFile;
@ -386,6 +393,63 @@ void testRootDirectory() throws Exception {
assertInstanceOf(Path.class, ee.evaluate("${session.rootDirectory}"));
}
@Test
public void testUri() throws Exception {
Path path = Paths.get("").toAbsolutePath();
MavenSession mavenSession = createMavenSession(null);
mavenSession.getRequest().setTopDirectory(path);
mavenSession.getRequest().setRootDirectory(path);
Object result = new PluginParameterExpressionEvaluatorV4(mavenSession.getSession(), null)
.evaluate("${session.rootDirectory.uri}");
assertEquals(path.toUri(), result);
}
@Test
public void testPath() throws Exception {
Path path = Paths.get("").toAbsolutePath();
MavenSession mavenSession = createMavenSession(null);
mavenSession.getRequest().setTopDirectory(path);
mavenSession.getRequest().setRootDirectory(path);
Object result = new PluginParameterExpressionEvaluatorV4(mavenSession.getSession(), null)
.evaluate("${session.rootDirectory/target}");
assertEquals(path.resolve("target"), result);
}
@Test
public void testPluginInjection() throws Exception {
Path path = Paths.get("rép➜α").toAbsolutePath();
MavenSession mavenSession = createMavenSession(null);
mavenSession.getRequest().setTopDirectory(path);
mavenSession.getRequest().setRootDirectory(path);
DefaultModelBuildingRequest mbr = new DefaultModelBuildingRequest();
PluginParameterExpressionEvaluatorV4 evaluator =
new PluginParameterExpressionEvaluatorV4(mavenSession.getSession(), null);
DefaultPlexusConfiguration configuration = new DefaultPlexusConfiguration("config");
configuration.addChild("uri", "${session.rootDirectory.uri}");
configuration.addChild("path", "${session.rootDirectory}");
configuration.addChild("uriString", "${session.rootDirectory.uri.string}");
configuration.addChild("uriAsciiString", "${session.rootDirectory.uri.ASCIIString}");
configuration.addChild("pathString", "${session.rootDirectory.string}");
PluginParameterExpressionEvaluatorV4Test.Mojo mojo = new PluginParameterExpressionEvaluatorV4Test.Mojo();
new EnhancedComponentConfigurator().configureComponent(mojo, configuration, evaluator, null);
assertEquals(
Objects.equals(path.toUri().toString(), path.toUri().toASCIIString()), !Os.isFamily(Os.FAMILY_WINDOWS));
assertEquals(mojo.uri, path.toUri());
assertEquals(mojo.path, path);
assertEquals(mojo.uriString, path.toUri().toString());
assertEquals(mojo.uriAsciiString, path.toUri().toASCIIString());
assertEquals(mojo.pathString, path.toString());
}
private MavenProject createDefaultProject() {
return new MavenProject(new Model());
}
@ -442,4 +506,12 @@ protected String getProjectsDirectory() {
// TODO Auto-generated method stub
return null;
}
public static class Mojo {
URI uri;
Path path;
String uriString;
String uriAsciiString;
String pathString;
}
}

View File

@ -21,12 +21,14 @@
import javax.inject.Inject;
import java.io.File;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import org.apache.maven.AbstractCoreMavenComponentTestCase;
@ -35,6 +37,7 @@
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.bridge.MavenRepositorySystem;
import org.apache.maven.configuration.internal.EnhancedComponentConfigurator;
import org.apache.maven.execution.DefaultMavenExecutionRequest;
import org.apache.maven.execution.DefaultMavenExecutionResult;
import org.apache.maven.execution.MavenExecutionRequest;
@ -44,6 +47,8 @@
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.building.DefaultModelBuildingRequest;
import org.apache.maven.model.interpolation.reflection.IntrospectionException;
import org.apache.maven.model.root.RootLocator;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
@ -53,6 +58,8 @@
import org.codehaus.plexus.MutablePlexusContainer;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
import org.codehaus.plexus.configuration.DefaultPlexusConfiguration;
import org.codehaus.plexus.util.Os;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.internal.impl.DefaultRepositorySystem;
import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
@ -458,9 +465,74 @@ private MavenSession newMavenSession() throws Exception {
return createMavenSession(null);
}
@Test
public void testUri() throws Exception {
Path path = Paths.get("").toAbsolutePath();
MavenSession mavenSession = createMavenSession(null);
mavenSession.getRequest().setTopDirectory(path);
mavenSession.getRequest().setRootDirectory(path);
Object result = new PluginParameterExpressionEvaluatorV4(mavenSession.getSession(), null)
.evaluate("${session.rootDirectory.uri}");
assertEquals(path.toUri(), result);
}
@Test
public void testPath() throws Exception {
Path path = Paths.get("").toAbsolutePath();
MavenSession mavenSession = createMavenSession(null);
mavenSession.getRequest().setTopDirectory(path);
mavenSession.getRequest().setRootDirectory(path);
Object result = new PluginParameterExpressionEvaluatorV4(mavenSession.getSession(), null)
.evaluate("${session.rootDirectory/target}");
assertEquals(path.resolve("target"), result);
}
@Test
public void testPluginInjection() throws Exception {
Path path = Paths.get("rép➜α").toAbsolutePath();
MavenSession mavenSession = createMavenSession(null);
mavenSession.getRequest().setTopDirectory(path);
mavenSession.getRequest().setRootDirectory(path);
DefaultModelBuildingRequest mbr = new DefaultModelBuildingRequest();
PluginParameterExpressionEvaluatorV4 evaluator =
new PluginParameterExpressionEvaluatorV4(mavenSession.getSession(), null);
DefaultPlexusConfiguration configuration = new DefaultPlexusConfiguration("config");
configuration.addChild("uri", "${session.rootDirectory.uri}");
configuration.addChild("path", "${session.rootDirectory}");
configuration.addChild("uriString", "${session.rootDirectory.uri.string}");
configuration.addChild("uriAsciiString", "${session.rootDirectory.uri.ASCIIString}");
configuration.addChild("pathString", "${session.rootDirectory.string}");
Mojo mojo = new Mojo();
new EnhancedComponentConfigurator().configureComponent(mojo, configuration, evaluator, null);
assertEquals(
Objects.equals(path.toUri().toString(), path.toUri().toASCIIString()), !Os.isFamily(Os.FAMILY_WINDOWS));
assertEquals(mojo.uri, path.toUri());
assertEquals(mojo.path, path);
assertEquals(mojo.uriString, path.toUri().toString());
assertEquals(mojo.uriAsciiString, path.toUri().toASCIIString());
assertEquals(mojo.pathString, path.toString());
}
@Override
protected String getProjectsDirectory() {
// TODO Auto-generated method stub
return null;
}
public static class Mojo {
URI uri;
Path path;
String uriString;
String uriAsciiString;
String pathString;
}
}

View File

@ -21,6 +21,7 @@
import javax.inject.Inject;
import java.io.File;
import java.net.URI;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
@ -39,9 +40,7 @@
import org.codehaus.plexus.interpolation.AbstractValueSource;
import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
import org.codehaus.plexus.interpolation.MapBasedValueSource;
import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper;
import org.codehaus.plexus.interpolation.RecursionInterceptor;
import org.codehaus.plexus.interpolation.ValueSource;
@ -140,7 +139,11 @@ protected List<ValueSource> createValueSources(
@Override
public Object getValue(String expression) {
if ("basedir".equals(expression)) {
return projectDir.getAbsolutePath();
return projectDir.getAbsoluteFile().toPath().toString();
} else if (expression.startsWith("basedir.")) {
Path basedir = projectDir.getAbsoluteFile().toPath();
return new ObjectBasedValueSource(basedir)
.getValue(expression.substring("basedir.".length()));
}
return null;
}
@ -159,6 +162,11 @@ public Object getValue(String expression) {
.toPath()
.toUri()
.toASCIIString();
} else if (expression.startsWith("baseUri.")) {
URI baseUri =
projectDir.getAbsoluteFile().toPath().toUri();
return new ObjectBasedValueSource(baseUri)
.getValue(expression.substring("baseUri.".length()));
}
return null;
}
@ -177,6 +185,11 @@ public Object getValue(String expression) {
Path base = projectDir != null ? projectDir.toPath() : null;
Path root = rootLocator.findMandatoryRoot(base);
return root.toFile().getPath();
} else if (expression.startsWith("rootDirectory.")) {
Path base = projectDir != null ? projectDir.toPath() : null;
Path root = rootLocator.findMandatoryRoot(base);
return new ObjectBasedValueSource(root)
.getValue(expression.substring("rootDirectory.".length()));
}
return null;
}

View File

@ -0,0 +1,84 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.model.interpolation;
/*
* Copyright 2007 The Codehaus Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.apache.maven.model.interpolation.reflection.ReflectionValueExtractor;
import org.codehaus.plexus.interpolation.AbstractValueSource;
/**
* Wraps an object, providing reflective access to the object graph of which the
* supplied object is the root. Expressions like 'child.name' will translate into
* 'rootObject.getChild().getName()' for non-boolean properties, and
* 'rootObject.getChild().isName()' for boolean properties.
*/
public class ObjectBasedValueSource extends AbstractValueSource {
private final Object root;
/**
* Construct a new value source, using the supplied object as the root from
* which to start, and using expressions split at the dot ('.') to navigate
* the object graph beneath this root.
* @param root the root of the graph.
*/
public ObjectBasedValueSource(Object root) {
super(true);
this.root = root;
}
/**
* <p>Split the expression into parts, tokenized on the dot ('.') character. Then,
* starting at the root object contained in this value source, apply each part
* to the object graph below this root, using either 'getXXX()' or 'isXXX()'
* accessor types to resolve the value for each successive expression part.
* Finally, return the result of the last expression part's resolution.</p>
*
* <p><b>NOTE:</b> The object-graph nagivation actually takes place via the
* {@link ReflectionValueExtractor} class.</p>
*/
public Object getValue(String expression) {
if (expression == null || expression.trim().isEmpty()) {
return null;
}
try {
return ReflectionValueExtractor.evaluate(expression, root, false);
} catch (Exception e) {
addFeedback("Failed to extract \'" + expression + "\' from: " + root, e);
}
return null;
}
}

View File

@ -0,0 +1,62 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.model.interpolation;
import java.util.List;
import org.codehaus.plexus.interpolation.AbstractDelegatingValueSource;
import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper;
import org.codehaus.plexus.interpolation.QueryEnabledValueSource;
/**
* Wraps an arbitrary object with an {@link ObjectBasedValueSource} instance, then
* wraps that source with a {@link PrefixedValueSourceWrapper} instance, to which
* this class delegates all of its calls.
*/
public class PrefixedObjectValueSource extends AbstractDelegatingValueSource implements QueryEnabledValueSource {
/**
* Wrap the specified root object, allowing the specified expression prefix.
* @param prefix the prefix.
* @param root the root of the graph.
*/
public PrefixedObjectValueSource(String prefix, Object root) {
super(new PrefixedValueSourceWrapper(new ObjectBasedValueSource(root), prefix));
}
/**
* Wrap the specified root object, allowing the specified list of expression
* prefixes and setting whether the {@link PrefixedValueSourceWrapper} allows
* unprefixed expressions.
* @param possiblePrefixes The possible prefixes.
* @param root The root of the graph.
* @param allowUnprefixedExpressions if we allow undefined expressions or not.
*/
public PrefixedObjectValueSource(List<String> possiblePrefixes, Object root, boolean allowUnprefixedExpressions) {
super(new PrefixedValueSourceWrapper(
new ObjectBasedValueSource(root), possiblePrefixes, allowUnprefixedExpressions));
}
/**
* {@inheritDoc}
*/
public String getLastExpression() {
return ((QueryEnabledValueSource) getDelegate()).getLastExpression();
}
}

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.plugin;
package org.apache.maven.model.interpolation.reflection;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

View File

@ -16,9 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.plugin;
package org.apache.maven.model.interpolation.reflection;
class IntrospectionException extends Exception {
public class IntrospectionException extends Exception {
/**
*

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.plugin;
package org.apache.maven.model.interpolation.reflection;
import java.lang.reflect.Method;
import java.util.ArrayList;

View File

@ -16,26 +16,30 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.plugin;
package org.apache.maven.model.interpolation.reflection;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.annotations.Nullable;
import org.apache.maven.plugin.MethodMap.AmbiguousException;
import org.apache.maven.model.interpolation.reflection.MethodMap.AmbiguousException;
/**
* Using simple dotted expressions to extract the values from an Object instance using JSP-like expressions
* such as {@code project.build.sourceDirectory}.
* <p>
* In addition to usual getters using {@code getXxx} or {@code isXxx} suffixes, accessors
* using {@code asXxx} or {@code toXxx} prefixes are also supported.
*/
class ReflectionValueExtractor {
public class ReflectionValueExtractor {
private static final Object[] OBJECT_ARGS = new Object[0];
/**
@ -263,22 +267,14 @@ private static Object getPropertyValue(Object value, String property) throws Int
ClassMap classMap = getClassMap(value.getClass());
String methodBase = Character.toTitleCase(property.charAt(0)) + property.substring(1);
String methodName = "get" + methodBase;
try {
Method method = classMap.findMethod(methodName);
if (method == null) {
// perhaps this is a boolean property??
methodName = "is" + methodBase;
method = classMap.findMethod(methodName);
for (String prefix : Arrays.asList("get", "is", "to", "as")) {
Method method = classMap.findMethod(prefix + methodBase);
if (method != null) {
return method.invoke(value, OBJECT_ARGS);
}
}
if (method == null) {
return null;
}
return method.invoke(value, OBJECT_ARGS);
return null;
} catch (InvocationTargetException e) {
throw new IntrospectionException(e.getTargetException());
} catch (AmbiguousException | IllegalAccessException e) {

View File

@ -331,6 +331,30 @@ void testRootDirectory() throws Exception {
assertEquals("file:myRootDirectory/temp-repo", (out.getRepositories().get(0)).getUrl());
}
@Test
void testRootDirectoryWithUri() throws Exception {
Path rootDirectory = Paths.get("myRootDirectory");
Model model = Model.newBuilder()
.version("3.8.1")
.artifactId("foo")
.repositories(Collections.singletonList(Repository.newBuilder()
.url("${project.rootDirectory.uri}/temp-repo")
.build()))
.build();
ModelInterpolator interpolator = createInterpolator();
final SimpleProblemCollector collector = new SimpleProblemCollector();
Model out = interpolator.interpolateModel(
model, rootDirectory.toFile(), createModelBuildingRequest(context), collector);
assertProblemFree(collector);
assertEquals(
rootDirectory.resolve("temp-repo").toUri().toString(),
(out.getRepositories().get(0)).getUrl());
}
@Test
void testRootDirectoryWithNull() throws Exception {
Model model = Model.newBuilder()

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.plugin;
package org.apache.maven.model.interpolation.reflection;
/*
* Copyright The Codehaus Foundation.