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 java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
@ -51,7 +52,7 @@
* </pre> * </pre>
* *
* Expressions that reference non-existing objects or use invalid collection/array indices silently resolve to * 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 * @goal eval
* @phase initialize * @phase initialize
@ -176,8 +177,13 @@ public void execute()
for ( int i = 0; i < expressions.length; i++ ) for ( int i = 0; i < expressions.length; i++ )
{ {
Object value = ExpressionUtil.evaluate( expressions[i], contexts ); Map values = ExpressionUtil.evaluate( expressions[i], contexts );
PropertyUtil.store( expressionProperties, expressions[i].replace( '/', '.' ), value ); 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.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; 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 * 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 * ('/'). 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. * 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 * The initial segment denotes the root object and the parameter <code>contexts</code> is used to specify which root
* root objects are available. For instance, if <code>contexts</code> maps the token "project" to a Maven project * 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 * instance, the expression "project/build/resources/0/directory" specifies the first resource directory of the
* project. * project.
* *
* @param expression The expression to evaluate, may be <code>null</code>. * @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 * @param context The object to start expression evaluation at, must not be <code>null</code>.
* not be <code>null</code>. * @return The values of the evaluation, indexed by expression, or an empty map if the segments could not be
* @return The value of the expression or <code>null</code> if the expression could not be evaluated. * 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 ) if ( expression != null && expression.length() > 0 )
{ {
List segments = Arrays.asList( expression.split( "/", 0 ) ); List segments = Arrays.asList( expression.split( "/", 0 ) );
if ( !segments.isEmpty() ) values = evaluate( "", segments, context );
{
Object context = contexts.get( segments.get( 0 ) );
if ( context != null )
{
value = evaluate( context, segments.subList( 1, segments.size() ) );
}
}
} }
return value; return values;
} }
/** /**
* Evaluates the given expression segments against the specified object. * 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>. * @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() ) if ( segments.isEmpty() )
{ {
value = context; values = Collections.singletonMap( prefix, context );
} }
else if ( context != null ) else if ( context != null )
{ {
Object target = null; Map targets = Collections.EMPTY_MAP;
String segment = (String) segments.get( 0 ); String segment = (String) segments.get( 0 );
if ( segment.length() <= 0 ) if ( context.getClass().isArray() && Character.isDigit( segment.charAt( 0 ) ) )
{
value = context;
}
else if ( context.getClass().isArray() && Character.isDigit( segment.charAt( 0 ) ) )
{ {
try try
{ {
int index = Integer.parseInt( segment ); int index = Integer.parseInt( segment );
target = Array.get( context, index ); targets = Collections.singletonMap( segment, Array.get( context, index ) );
} }
catch ( RuntimeException e ) catch ( RuntimeException e )
{ {
@ -118,21 +112,51 @@ else if ( ( context instanceof List ) && Character.isDigit( segment.charAt( 0 )
try try
{ {
int index = Integer.parseInt( segment ); int index = Integer.parseInt( segment );
target = ( (List) context ).get( index ); targets = Collections.singletonMap( segment, ( (List) context ).get( index ) );
} }
catch ( RuntimeException e ) catch ( RuntimeException e )
{ {
// invalid index, just ignore // 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 else
{ {
target = getProperty( context, segment ); targets = Collections.singletonMap( segment, getProperty( context, segment ) );
}
value = evaluate( target, segments.subList( 1, segments.size() ) );
} }
return value; 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 ) );
}
}
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 void testEvaluate()
contexts.put( "map", map ); contexts.put( "map", map );
contexts.put( "bean", bean ); contexts.put( "bean", bean );
assertSame( array, ExpressionUtil.evaluate( "array", contexts ) ); assertSame( array, evaluate( "array", contexts ) );
assertSame( array, ExpressionUtil.evaluate( "array/", contexts ) ); assertSame( array, ExpressionUtil.evaluate( "array/", contexts ).get( "array" ) );
assertSame( list, ExpressionUtil.evaluate( "list", contexts ) ); assertSame( list, evaluate( "list", contexts ) );
assertSame( map, ExpressionUtil.evaluate( "map", contexts ) ); assertSame( map, evaluate( "map", contexts ) );
assertSame( bean, ExpressionUtil.evaluate( "bean", contexts ) ); assertSame( bean, evaluate( "bean", contexts ) );
assertNull( ExpressionUtil.evaluate( "no-root", contexts ) ); assertNull( evaluate( "no-root", contexts ) );
assertEquals( new Integer( 3 ), ExpressionUtil.evaluate( "array/length", contexts ) ); assertEquals( new Integer( 3 ), evaluate( "array/length", contexts ) );
assertEquals( "three", ExpressionUtil.evaluate( "array/2", contexts ) ); assertEquals( "three", evaluate( "array/2", contexts ) );
assertEquals( new Integer( 5 ), ExpressionUtil.evaluate( "array/2/length", contexts ) ); assertEquals( new Integer( 5 ), evaluate( "array/2/length", contexts ) );
assertNull( ExpressionUtil.evaluate( "array/invalid", contexts ) ); assertNull( evaluate( "array/invalid", contexts ) );
assertNull( ExpressionUtil.evaluate( "array/-1", contexts ) ); assertNull( evaluate( "array/-1", contexts ) );
assertNull( ExpressionUtil.evaluate( "array/999", 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( new Integer( 3 ), evaluate( "list/size", contexts ) );
assertEquals( "-2", ExpressionUtil.evaluate( "list/2", contexts ) ); assertEquals( "-2", evaluate( "list/2", contexts ) );
assertNull( ExpressionUtil.evaluate( "list/invalid", contexts ) ); assertNull( evaluate( "list/invalid", contexts ) );
assertNull( ExpressionUtil.evaluate( "list/-1", contexts ) ); assertNull( evaluate( "list/-1", contexts ) );
assertNull( ExpressionUtil.evaluate( "list/999", 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( new Integer( 1 ), evaluate( "map/size", contexts ) );
assertEquals( "value", ExpressionUtil.evaluate( "map/some.key", contexts ) ); assertEquals( "value", evaluate( "map/some.key", contexts ) );
assertNull( ExpressionUtil.evaluate( "map/invalid", contexts ) ); assertNull( evaluate( "map/invalid", contexts ) );
assertEquals( "field", ExpressionUtil.evaluate( "bean/field", contexts ) ); assertEquals( "field", evaluate( "bean/field", contexts ) );
assertNull( ExpressionUtil.evaluate( "bean/invalid", contexts ) ); assertNull( evaluate( "bean/invalid", contexts ) );
assertEquals( "prop", ExpressionUtil.evaluate( "bean/bean/prop", contexts ) ); assertEquals( "prop", evaluate( "bean/bean/prop", contexts ) );
assertEquals( "flag", ExpressionUtil.evaluate( "bean/bean/flag", contexts ) ); assertEquals( "flag", evaluate( "bean/bean/flag", contexts ) );
assertEquals( "arg", ExpressionUtil.evaluate( "bean/bean/arg", 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() public void testGetProperty()