PR: MNG-2339 - ${project.*} evaluated badly

This commit makes sure that pom.* and project.* are resolved using the model reflector,
but if the prefix isn't pom or project, context and model properties are consulted.
If the value still isn't found, the model reflector is used as a legacy fallback,
and a warning is printed.

Also, project.* is deprecated in favour of pom.*.

TODO: env.*.

N.B.: there's loads of warnings about ${artifactId}. I can turn these off,
but if you specify -DartifactId on the commandline, all hell breaks loose.

See http://docs.codehaus.org/display/MAVEN/Refactoring+Interpolation


git-svn-id: https://svn.apache.org/repos/asf/maven/components/trunk@549786 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Kenney Westerhof 2007-06-22 11:34:53 +00:00
parent 87f98299ac
commit 99ba1e7e85
2 changed files with 126 additions and 23 deletions

View File

@ -31,7 +31,9 @@ import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -67,6 +69,7 @@ public class RegexBasedModelInterpolator
*
* @param model The inbound Model instance, to serialize and reference for expression resolution
* @param context The other context map to be used during resolution
* @param strict This parameter is ignored!
* @return The resolved instance of the inbound Model. This is a different instance!
*/
public Model interpolate( Model model, Map context, boolean strict )
@ -108,58 +111,94 @@ public class RegexBasedModelInterpolator
return model;
}
/**
* Interpolates all expressions in the src parameter.
* <p>
* The algorithm used for each expression is:
* <ul>
* <li>If it starts with either "pom." or "project.", the expression is evaluated against the model.</li>
* <li>If the value is null, get the value from the context.</li>
* <li>If the value is null, but the context contains the expression, don't replace the expression string
* with the value, and continue to find other expressions.</li>
* <li>If the value is null, get it from the model properties.</li>
* <li>
*
*
*/
private String interpolateInternal( String src, Model model, Map context )
throws ModelInterpolationException
{
Logger logger = getLogger();
String result = src;
Matcher matcher = EXPRESSION_PATTERN.matcher( result );
while ( matcher.find() )
{
String wholeExpr = matcher.group( 0 );
String prefix = matcher.group( 1 );
String realExpr = matcher.group( 2 );
Object value = context.get( realExpr );
prefix = prefix == null ? "" : prefix;
Object value = null;
boolean isPomExpression = "pom.".equals( prefix ) || "project.".equals( prefix );
if ( logger != null && "project.".equals( prefix ) )
{
logger.warn( "Deprecated expression: " + wholeExpr + " - 'project.' prefix is deprecated."
+ " Use 'pom.': ${pom." + realExpr + "} (model: " + model.getId() + ")" );
}
if ( isPomExpression )
{
value = getValueFromModel( realExpr, model, wholeExpr, logger );
}
if ( value == null )
{
value = context.get( realExpr );
// This may look out of place, but its here for the MNG-2124/MNG-1927 fix described in the project builder
if ( context.containsKey( realExpr ) )
if ( value == null && context.containsKey( realExpr ) )
{
// It existed, but was null. Leave it alone.
continue;
}
value = model.getProperties().getProperty( realExpr );
}
if ( value == null )
{
try
{
// NOTE: We've already trimmed off any leading expression parts like 'project.'
// or 'pom.', and now we have to ensure that the ReflectionValueExtractor
// doesn't try to do it again.
value = ReflectionValueExtractor.evaluate( realExpr, model, false );
}
catch ( Exception e )
{
Logger logger = getLogger();
if ( logger != null )
{
logger.debug( "POM interpolation cannot proceed with expression: " + wholeExpr + ". Skipping...", e );
}
}
value = model.getProperties().getProperty( realExpr );
}
// if the expression refers to itself, skip it.
if ( String.valueOf( value ).indexOf( wholeExpr ) > -1 )
// Any expression, not just artifactId, version etc., but also scm.repository
// were evaluated against the model, even if there is no prefix.
// If the 2.1 strategy fails, try the legacy approach. If it yields a result, warn for it.
if ( value == null && prefix.length() == 0 )
{
throw new ModelInterpolationException( wholeExpr, "Expression value '" + value + "' references itself in '" + model.getId() + "'." );
value = getValueFromModel( realExpr, model, wholeExpr, logger );
if ( value != null && logger != null )
{
logger.warn( "Deprecated expression: " + wholeExpr + " - missing prefix. Use ${pom."
+ realExpr + "} (model: " + model.getId() + ")" );
}
}
if ( value != null )
{
result = StringUtils.replace( result, wholeExpr, String.valueOf( value ) );
// if the expression refers to itself, skip it.
// replace project. expressions with pom. expressions to circumvent self-referencing expressions using
// the 2 different model expressions.
if ( StringUtils.replace( value.toString(), "${project.", "${pom." ).indexOf(
StringUtils.replace( wholeExpr, "${project.", "${pom." ) ) > -1 )
{
throw new ModelInterpolationException( wholeExpr, "Expression value '" + value
+ "' references itself in '" + model.getId() + "'." );
}
result = StringUtils.replace( result, wholeExpr, value.toString() );
// could use:
// result = matcher.replaceFirst( stringValue );
// but this could result in multiple lookups of stringValue, and replaceAll is not correct behaviour
@ -180,4 +219,23 @@ public class RegexBasedModelInterpolator
return result;
}
private static Object getValueFromModel( String realExpr, Model model, String wholeExpr, Logger logger )
{
try
{
// NOTE: We've already trimmed off any leading expression parts like 'project.'
// or 'pom.', and now we have to ensure that the ReflectionValueExtractor
// doesn't try to do it again.
return ReflectionValueExtractor.evaluate( realExpr, model, false );
}
catch ( Exception e )
{
if ( logger != null )
{
logger.debug( "POM interpolation cannot proceed with expression: " + wholeExpr + ". Skipping...", e );
}
return null;
}
}
}

View File

@ -129,11 +129,16 @@ public class RegexBasedModelInterpolatorTest
Dependency dep = new Dependency();
dep.setVersion( "${version}" );
Dependency dep2 = new Dependency();
dep2.setVersion( "${pom.version}" );
model.addDependency( dep );
model.addDependency( dep2 );
Model out = new RegexBasedModelInterpolator().interpolate( model, context );
assertEquals( "3.8.1", ( (Dependency) out.getDependencies().get( 0 ) ).getVersion() );
assertEquals( "3.8.1", ( (Dependency) out.getDependencies().get( 1 ) ).getVersion() );
}
public void testShouldNotInterpolateDependencyVersionWithInvalidReference()
@ -177,11 +182,16 @@ public class RegexBasedModelInterpolatorTest
Dependency dep = new Dependency();
dep.setVersion( "${artifactId}-${version}" );
Dependency dep2 = new Dependency();
dep2.setVersion( "${pom.artifactId}-${pom.version}" );
model.addDependency( dep );
model.addDependency( dep2 );
Model out = new RegexBasedModelInterpolator().interpolate( model, context );
assertEquals( "foo-3.8.1", ( (Dependency) out.getDependencies().get( 0 ) ).getVersion() );
assertEquals( "foo-3.8.1", ( (Dependency) out.getDependencies().get( 1 ) ).getVersion() );
}
public void testBasedir()
@ -197,6 +207,8 @@ public class RegexBasedModelInterpolatorTest
model.addRepository( repository );
assertNotNull( context.get( "basedir" ) );
Model out = new RegexBasedModelInterpolator().interpolate( model, context );
assertEquals( "file://localhost/myBasedir/temp-repo", ( (Repository) out.getRepositories().get( 0 ) ).getUrl() );
@ -217,4 +229,37 @@ public class RegexBasedModelInterpolatorTest
assertEquals( out.getProperties().getProperty( "outputDirectory" ), "${DOES_NOT_EXIST}" );
}
public void testPOMExpressionDoesNotUseSystemProperty()
throws Exception
{
Model model = new Model();
model.setVersion( "1.0" );
Properties modelProperties = new Properties();
modelProperties.setProperty( "version", "prop version" );
modelProperties.setProperty( "foo.version", "prop foo.version" );
modelProperties.setProperty( "pom.version", "prop pom.version" );
modelProperties.setProperty( "project.version", "prop project.version" );
model.setProperties( modelProperties );
Dependency dep = new Dependency();
model.addDependency( dep );
checkDep( "prop version", "${version}", model );
checkDep( "1.0", "${pom.version}", model );
checkDep( "1.0", "${project.version}", model );
checkDep( "prop foo.version", "${foo.version}", model );
}
private void checkDep( String expectedVersion, String depVersionExpr, Model model )
throws Exception
{
( (Dependency) model.getDependencies().get( 0 ) ).setVersion( depVersionExpr );
Model out = new RegexBasedModelInterpolator().interpolate( model, context );
String result = ( (Dependency) out.getDependencies().get( 0 ) ).getVersion();
assertEquals( "Expected '" + expectedVersion + "' for version expression '" + depVersionExpr + "', but was '" + result + "'", expectedVersion, result );
}
}