o Extended plugin to support expressions like "project/dependencies/*/artifactId" to iterate collections

git-svn-id: https://svn.apache.org/repos/asf/maven/core-integration-testing/trunk@949790 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Benjamin Bentmann 2010-05-31 16:16:22 +00:00
parent 43b2be7947
commit aafe8e9c67
3 changed files with 103 additions and 60 deletions

View File

@ -26,6 +26,7 @@ import org.apache.maven.plugin.MojoFailureException;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
@ -51,7 +52,7 @@ import java.util.Properties;
* </pre>
*
* Expressions that reference non-existing objects or use invalid collection/array indices silently resolve to
* <code>null</code>.
* <code>null</code>. For collections and arrays, the special index "*" can be used to iterate all elements.
*
* @goal eval
* @phase initialize
@ -176,8 +177,13 @@ public class EvalMojo
for ( int i = 0; i < expressions.length; i++ )
{
Object value = ExpressionUtil.evaluate( expressions[i], contexts );
PropertyUtil.store( expressionProperties, expressions[i].replace( '/', '.' ), value );
Map values = ExpressionUtil.evaluate( expressions[i], contexts );
for ( Iterator it = values.keySet().iterator(); it.hasNext(); )
{
Object key = it.next();
Object value = values.get( key );
PropertyUtil.store( expressionProperties, key.toString().replace( '/', '.' ), value );
}
}
}

View File

@ -24,6 +24,9 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -48,65 +51,56 @@ class ExpressionUtil
* Evaluates the specified expression. Expressions are composed of segments which are separated by a forward slash
* ('/'). Each segment specifies a (public) bean property of the current object and drives the evaluation further
* down the object graph. For lists, arrays and maps segments can additionally specify the index/key of an element.
* The initial segment denotes the root object and the parameter <code>contexts</code> is used to specify which
* root objects are available. For instance, if <code>contexts</code> maps the token "project" to a Maven project
* The initial segment denotes the root object and the parameter <code>contexts</code> is used to specify which root
* objects are available. For instance, if <code>contexts</code> maps the token "project" to a Maven project
* instance, the expression "project/build/resources/0/directory" specifies the first resource directory of the
* project.
*
* @param expression The expression to evaluate, may be <code>null</code>.
* @param contexts The possible root objects for the expression evaluation, indexed by their identifying token, must
* not be <code>null</code>.
* @return The value of the expression or <code>null</code> if the expression could not be evaluated.
* @param context The object to start expression evaluation at, must not be <code>null</code>.
* @return The values of the evaluation, indexed by expression, or an empty map if the segments could not be
* evaluated.
*/
public static Object evaluate( String expression, Map contexts )
public static Map evaluate( String expression, Object context )
{
Object value = null;
Map values = Collections.EMPTY_MAP;
if ( expression != null && expression.length() > 0 )
{
List segments = Arrays.asList( expression.split( "/", 0 ) );
if ( !segments.isEmpty() )
{
Object context = contexts.get( segments.get( 0 ) );
if ( context != null )
{
value = evaluate( context, segments.subList( 1, segments.size() ) );
}
}
values = evaluate( "", segments, context );
}
return value;
return values;
}
/**
* Evaluates the given expression segments against the specified object.
*
* @param context The object to evaluate the segments against, may be <code>null</code>.
* @param prefix The expression prefix that led to the current context, must not be <code>null</code>.
* @param segments The expression segments to evaluate, must not be <code>null</code>.
* @return The value of the evaluation or <code>null</code> if the segments could not be evaluated.
* @param context The object to evaluate the segments against, may be <code>null</code>.
* @return The values of the evaluation, indexed by expression, or an empty map if the segments could not be
* evaluated.
*/
private static Object evaluate( Object context, List segments )
private static Map evaluate( String prefix, List segments, Object context )
{
Object value = null;
Map values = Collections.EMPTY_MAP;
if ( segments.isEmpty() )
{
value = context;
values = Collections.singletonMap( prefix, context );
}
else if ( context != null )
{
Object target = null;
Map targets = Collections.EMPTY_MAP;
String segment = (String) segments.get( 0 );
if ( segment.length() <= 0 )
{
value = context;
}
else if ( context.getClass().isArray() && Character.isDigit( segment.charAt( 0 ) ) )
if ( context.getClass().isArray() && Character.isDigit( segment.charAt( 0 ) ) )
{
try
{
int index = Integer.parseInt( segment );
target = Array.get( context, index );
targets = Collections.singletonMap( segment, Array.get( context, index ) );
}
catch ( RuntimeException e )
{
@ -118,21 +112,51 @@ class ExpressionUtil
try
{
int index = Integer.parseInt( segment );
target = ( (List) context ).get( index );
targets = Collections.singletonMap( segment, ( (List) context ).get( index ) );
}
catch ( RuntimeException e )
{
// invalid index, just ignore
}
}
else if ( ( context instanceof Collection ) && "*".equals( segment ) )
{
targets = new LinkedHashMap();
int index = 0;
for ( Iterator it = ( (Collection) context ).iterator(); it.hasNext(); index++ )
{
targets.put( Integer.toString( index ), it.next() );
}
}
else if ( context.getClass().isArray() && "*".equals( segment ) )
{
targets = new LinkedHashMap();
for ( int index = 0, n = Array.getLength( context ); index < n; index++ )
{
targets.put( Integer.toString( index ), Array.get( context, index ) );
}
}
else
{
target = getProperty( context, segment );
targets = Collections.singletonMap( segment, getProperty( context, segment ) );
}
values = new LinkedHashMap();
for ( Iterator it = targets.keySet().iterator(); it.hasNext(); )
{
Object key = it.next();
Object target = targets.get( key );
values.putAll( evaluate( concat( prefix, String.valueOf( key ) ),
segments.subList( 1, segments.size() ), target ) );
}
value = evaluate( target, segments.subList( 1, segments.size() ) );
}
return value;
return values;
}
private static String concat( String prefix, String segment )
{
return ( prefix == null || prefix.length() <= 0 ) ? segment : ( prefix + '/' + segment );
}
/**

View File

@ -47,35 +47,48 @@ public class ExpressionUtilTest
contexts.put( "map", map );
contexts.put( "bean", bean );
assertSame( array, ExpressionUtil.evaluate( "array", contexts ) );
assertSame( array, ExpressionUtil.evaluate( "array/", contexts ) );
assertSame( list, ExpressionUtil.evaluate( "list", contexts ) );
assertSame( map, ExpressionUtil.evaluate( "map", contexts ) );
assertSame( bean, ExpressionUtil.evaluate( "bean", contexts ) );
assertNull( ExpressionUtil.evaluate( "no-root", contexts ) );
assertSame( array, evaluate( "array", contexts ) );
assertSame( array, ExpressionUtil.evaluate( "array/", contexts ).get( "array" ) );
assertSame( list, evaluate( "list", contexts ) );
assertSame( map, evaluate( "map", contexts ) );
assertSame( bean, evaluate( "bean", contexts ) );
assertNull( evaluate( "no-root", contexts ) );
assertEquals( new Integer( 3 ), ExpressionUtil.evaluate( "array/length", contexts ) );
assertEquals( "three", ExpressionUtil.evaluate( "array/2", contexts ) );
assertEquals( new Integer( 5 ), ExpressionUtil.evaluate( "array/2/length", contexts ) );
assertNull( ExpressionUtil.evaluate( "array/invalid", contexts ) );
assertNull( ExpressionUtil.evaluate( "array/-1", contexts ) );
assertNull( ExpressionUtil.evaluate( "array/999", contexts ) );
assertEquals( new Integer( 3 ), evaluate( "array/length", contexts ) );
assertEquals( "three", evaluate( "array/2", contexts ) );
assertEquals( new Integer( 5 ), evaluate( "array/2/length", contexts ) );
assertNull( evaluate( "array/invalid", contexts ) );
assertNull( evaluate( "array/-1", contexts ) );
assertNull( evaluate( "array/999", contexts ) );
assertEquals( 3, ExpressionUtil.evaluate( "array/*", contexts ).size() );
assertEquals( "one", ExpressionUtil.evaluate( "array/*", contexts ).get( "array/0" ) );
assertEquals( "two", ExpressionUtil.evaluate( "array/*", contexts ).get( "array/1" ) );
assertEquals( "three", ExpressionUtil.evaluate( "array/*", contexts ).get( "array/2" ) );
assertEquals( new Integer( 3 ), ExpressionUtil.evaluate( "list/size", contexts ) );
assertEquals( "-2", ExpressionUtil.evaluate( "list/2", contexts ) );
assertNull( ExpressionUtil.evaluate( "list/invalid", contexts ) );
assertNull( ExpressionUtil.evaluate( "list/-1", contexts ) );
assertNull( ExpressionUtil.evaluate( "list/999", contexts ) );
assertEquals( new Integer( 3 ), evaluate( "list/size", contexts ) );
assertEquals( "-2", evaluate( "list/2", contexts ) );
assertNull( evaluate( "list/invalid", contexts ) );
assertNull( evaluate( "list/-1", contexts ) );
assertNull( evaluate( "list/999", contexts ) );
assertEquals( 3, ExpressionUtil.evaluate( "list/*", contexts ).size() );
assertEquals( "0", ExpressionUtil.evaluate( "list/*", contexts ).get( "list/0" ) );
assertEquals( "-1", ExpressionUtil.evaluate( "list/*", contexts ).get( "list/1" ) );
assertEquals( "-2", ExpressionUtil.evaluate( "list/*", contexts ).get( "list/2" ) );
assertEquals( new Integer( 1 ), ExpressionUtil.evaluate( "map/size", contexts ) );
assertEquals( "value", ExpressionUtil.evaluate( "map/some.key", contexts ) );
assertNull( ExpressionUtil.evaluate( "map/invalid", contexts ) );
assertEquals( new Integer( 1 ), evaluate( "map/size", contexts ) );
assertEquals( "value", evaluate( "map/some.key", contexts ) );
assertNull( evaluate( "map/invalid", contexts ) );
assertEquals( "field", ExpressionUtil.evaluate( "bean/field", contexts ) );
assertNull( ExpressionUtil.evaluate( "bean/invalid", contexts ) );
assertEquals( "prop", ExpressionUtil.evaluate( "bean/bean/prop", contexts ) );
assertEquals( "flag", ExpressionUtil.evaluate( "bean/bean/flag", contexts ) );
assertEquals( "arg", ExpressionUtil.evaluate( "bean/bean/arg", contexts ) );
assertEquals( "field", evaluate( "bean/field", contexts ) );
assertNull( evaluate( "bean/invalid", contexts ) );
assertEquals( "prop", evaluate( "bean/bean/prop", contexts ) );
assertEquals( "flag", evaluate( "bean/bean/flag", contexts ) );
assertEquals( "arg", evaluate( "bean/bean/arg", contexts ) );
}
private static Object evaluate( String expression, Object context )
{
return ExpressionUtil.evaluate( expression, context ).get( expression );
}
public void testGetProperty()