Author: krosenvold
Date: Tue Oct 14 06:52:39 2014
New Revision: 1631643

URL: http://svn.apache.org/r1631643
Log:
[MSHARED-313] Port dotted expression parser fixes.

Patch by  Igor Fedorenko, adapted to msu by me

Modified:
    
maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractor.java
    
maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractorTest.java

Modified: 
maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractor.java
URL: 
http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractor.java?rev=1631643&r1=1631642&r2=1631643&view=diff
==============================================================================
--- 
maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractor.java
 (original)
+++ 
maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractor.java
 Tue Oct 14 06:52:39 2014
@@ -19,6 +19,7 @@ package org.apache.maven.shared.utils.in
  * under the License.
  */
 
+import java.lang.reflect.Array;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Arrays;
@@ -28,6 +29,7 @@ import java.util.StringTokenizer;
 import java.util.WeakHashMap;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+
 import org.apache.maven.shared.utils.StringUtils;
 import 
org.apache.maven.shared.utils.introspection.MethodMap.AmbiguousException;
 
@@ -46,292 +48,291 @@ import javax.annotation.Nullable;
  * @version $Id$
  * @see <a 
href="http://struts.apache.org/1.x/struts-taglib/indexedprops.html";>http://struts.apache.org/1.x/struts-taglib/indexedprops.html</a>
  */
-public class ReflectionValueExtractor
-{
-    private static final Class<?>[] CLASS_ARGS = new Class[0];
-
-    private static final Object[] OBJECT_ARGS = new Object[0];
-
-    /**
-     * Use a WeakHashMap here, so the keys (Class objects) can be garbage 
collected.
-     * This approach prevents permgen space overflows due to retention of 
discarded
-     * classloaders.
-     */
-    private static final Map<Class<?>, ClassMap> classMaps = new 
WeakHashMap<Class<?>, ClassMap>();
-
-    /**
-     * Indexed properties pattern, ie <code>(\\w+)\\[(\\d+)\\]</code>
-     */
-    private static final Pattern INDEXED_PROPS = Pattern.compile( 
"(\\w+)\\[(\\d+)\\]" );
-
-    /**
-     * Indexed properties pattern, ie <code>(\\w+)\\((.+)\\)</code>
-     */
-    private static final Pattern MAPPED_PROPS = Pattern.compile( 
"(\\w+)\\((.+)\\)" );
-
-    private ReflectionValueExtractor()
-    {
-    }
+public class ReflectionValueExtractor {
+       private static final Class<?>[] CLASS_ARGS = new Class[0];
 
-    /**
-     * <p>The implementation supports indexed, nested and mapped 
properties.</p>
-     * <p/>
-     * <ul>
-     * <li>nested properties should be defined by a dot, i.e. 
"user.address.street"</li>
-     * <li>indexed properties (java.util.List or array instance) should be 
contains <code>(\\w+)\\[(\\d+)\\]</code>
-     * pattern, i.e. "user.addresses[1].street"</li>
-     * <li>mapped properties should be contains <code>(\\w+)\\((.+)\\)</code> 
pattern, i.e. "user.addresses(myAddress).street"</li>
-     * <ul>
-     *
-     * @param expression not null expression
-     * @param root       not null object
-     * @return the object defined by the expression
-     * @throws IntrospectionException if any
-     */
-    public static Object evaluate( @Nonnull String expression, @Nullable 
Object root )
-        throws IntrospectionException
-    {
-        return evaluate( expression, root, true );
-    }
+       private static final Object[] OBJECT_ARGS = new Object[0];
 
-    /**
-     * <p>The implementation supports indexed, nested and mapped 
properties.</p>
-     * <p/>
-     * <ul>
-     * <li>nested properties should be defined by a dot, i.e. 
"user.address.street"</li>
-     * <li>indexed properties (java.util.List or array instance) should be 
contains <code>(\\w+)\\[(\\d+)\\]</code>
-     * pattern, i.e. "user.addresses[1].street"</li>
-     * <li>mapped properties should be contains <code>(\\w+)\\((.+)\\)</code> 
pattern, i.e. "user.addresses(myAddress).street"</li>
-     * <ul>
-     *
-     * @param expression not null expression
-     * @param root       not null object
-     * @return the object defined by the expression
-     * @throws IntrospectionException if any
-     */
-    public static Object evaluate( @Nonnull String expression, @Nullable 
Object root, boolean trimRootToken )
-        throws IntrospectionException
-    {
-        // if the root token refers to the supplied root object parameter, 
remove it.
-        if ( trimRootToken )
-        {
-            expression = expression.substring( expression.indexOf( '.' ) + 1 );
+       /**
+        * Use a WeakHashMap here, so the keys (Class objects) can be garbage 
collected.
+        * This approach prevents permgen space overflows due to retention of 
discarded
+        * classloaders.
+        */
+       private static final Map<Class<?>, ClassMap> classMaps = new 
WeakHashMap<Class<?>, ClassMap>();
+
+
+       static final int EOF = -1;
+
+       static final char PROPERTY_START = '.';
+
+       static final char INDEXED_START = '[';
+
+       static final char INDEXED_END = ']';
+
+       static final char MAPPED_START = '(';
+
+       static final char MAPPED_END = ')';
+
+       static class Tokenizer {
+               final String expression;
+
+               int idx;
+
+               public Tokenizer(String expression) {
+                       this.expression = expression;
+               }
+
+               public int peekChar() {
+                       return idx < expression.length() ? 
expression.charAt(idx) : EOF;
+               }
+
+               public int skipChar() {
+                       return idx < expression.length() ? 
expression.charAt(idx++) : EOF;
+               }
+
+               public String nextToken(char delimiter) {
+                       int start = idx;
+
+                       while (idx < expression.length() && delimiter != 
expression.charAt(idx)) {
+                               idx++;
+                       }
+
+                       // delimiter MUST be present
+                       if (idx <= start || idx >= expression.length()) {
+                               return null;
+                       }
+
+                       return expression.substring(start, idx++);
+               }
+
+               public String nextPropertyName() {
+                       final int start = idx;
+
+                       while (idx < expression.length() && 
Character.isJavaIdentifierPart(expression.charAt(idx))) {
+                               idx++;
+                       }
+
+                       // property name does not require delimiter
+                       if (idx <= start || idx > expression.length()) {
+                               return null;
+                       }
+
+                       return expression.substring(start, idx);
+               }
+
+               public int getPosition() {
+                       return idx < expression.length() ? idx : EOF;
+               }
+
+               // to make tokenizer look pretty in debugger
+               @Override
+               public String toString() {
+                       return idx < expression.length() ? 
expression.substring(idx) : "<EOF>";
+               }
+       }
+
+       private ReflectionValueExtractor() {
+       }
+
+       /**
+        * <p>The implementation supports indexed, nested and mapped 
properties.</p>
+        * <p/>
+        * <ul>
+        * <li>nested properties should be defined by a dot, i.e. 
"user.address.street"</li>
+        * <li>indexed properties (java.util.List or array instance) should be 
contains <code>(\\w+)\\[(\\d+)\\]</code>
+        * pattern, i.e. "user.addresses[1].street"</li>
+        * <li>mapped properties should be contains 
<code>(\\w+)\\((.+)\\)</code> pattern, i.e. 
"user.addresses(myAddress).street"</li>
+        * <ul>
+        *
+        * @param expression not null expression
+        * @param root       not null object
+        * @return the object defined by the expression
+        * @throws IntrospectionException if any
+        */
+       public static Object evaluate(@Nonnull String expression, @Nullable 
Object root)
+                       throws IntrospectionException {
+               return evaluate(expression, root, true);
+       }
+
+       /**
+        * <p>The implementation supports indexed, nested and mapped 
properties.</p>
+        * <p/>
+        * <ul>
+        * <li>nested properties should be defined by a dot, i.e. 
"user.address.street"</li>
+        * <li>indexed properties (java.util.List or array instance) should be 
contains <code>(\\w+)\\[(\\d+)\\]</code>
+        * pattern, i.e. "user.addresses[1].street"</li>
+        * <li>mapped properties should be contains 
<code>(\\w+)\\((.+)\\)</code> pattern, i.e. 
"user.addresses(myAddress).street"</li>
+        * <ul>
+        *
+        * @param expression not null expression
+        * @param root       not null object
+        * @return the object defined by the expression
+        * @throws IntrospectionException if any
+        */
+       public static Object evaluate(@Nonnull String expression, @Nullable 
Object root, boolean trimRootToken)
+                       throws IntrospectionException {
+               Object value = root;
+
+               // 
----------------------------------------------------------------------
+               // Walk the dots and retrieve the ultimate value desired from 
the
+               // MavenProject instance.
+               // 
----------------------------------------------------------------------
+
+               if (org.codehaus.plexus.util.StringUtils.isEmpty(expression) || 
!Character.isJavaIdentifierStart(expression.charAt(0))) {
+                       return null;
+               }
+
+               final Tokenizer tokenizer;
+               if (trimRootToken) {
+                       tokenizer = new Tokenizer(expression);
+                       tokenizer.nextPropertyName();
+                       if (tokenizer.getPosition() == EOF) {
+                               return null;
+                       }
+               } else {
+                       tokenizer = new Tokenizer("." + expression);
+               }
+
+               int propertyPosition = tokenizer.getPosition();
+               while (value != null && tokenizer.peekChar() != EOF) {
+                       switch (tokenizer.skipChar()) {
+                               case INDEXED_START:
+                                       value =
+                                                       
getIndexedValue(expression, propertyPosition, tokenizer.getPosition(), value,
+                                                                       
tokenizer.nextToken(INDEXED_END));
+                                       break;
+                               case MAPPED_START:
+                                       value =
+                                                       
getMappedValue(expression, propertyPosition, tokenizer.getPosition(), value,
+                                                                       
tokenizer.nextToken(MAPPED_END));
+                                       break;
+                               case PROPERTY_START:
+                                       propertyPosition = 
tokenizer.getPosition();
+                                       value = getPropertyValue(value, 
tokenizer.nextPropertyName());
+                                       break;
+                               default:
+                                       // could not parse expression
+                                       return null;
+                       }
+               }
+
+               return value;
+       }
+
+       private static Object getMappedValue(final String expression, final int 
from, final int to, final Object value,
+                       final String key)
+                       throws IntrospectionException {
+               if (value == null || key == null) {
+                       return null;
+               }
+
+               if (value instanceof Map) {
+                       Object[] localParams = new Object[] { key };
+                       ClassMap classMap = getClassMap(value.getClass());
+                       try {
+                               Method method = classMap.findMethod("get", 
localParams);
+                               return method.invoke(value, localParams);
+                       } catch (AmbiguousException e) {
+                               throw new IntrospectionException(e);
+                       } catch (IllegalAccessException e) {
+                               throw new IntrospectionException(e);
+                       } catch (InvocationTargetException e) {
+                               throw new 
IntrospectionException(e.getTargetException());
+                       }
+
+               }
+
+               final String message =
+                               String.format("The token '%s' at position '%d' 
refers to a java.util.Map, but the value seems is an instance of '%s'",
+                                               expression.subSequence(from, 
to), from, value.getClass());
+
+               throw new IntrospectionException(message);
+       }
+
+       private static Object getIndexedValue(final String expression, final 
int from, final int to, final Object value,
+                       final String indexStr)
+                       throws IntrospectionException {
+               try {
+                       int index = Integer.parseInt(indexStr);
+
+                       if (value.getClass().isArray()) {
+                               return Array.get(value, index);
+                       }
+
+                       if (value instanceof List) {
+                               ClassMap classMap = 
getClassMap(value.getClass());
+                               // use get method on List interface
+                               Object[] localParams = new Object[] { index };
+                               Method method = null;
+                               try {
+                                       method = classMap.findMethod("get", 
localParams);
+                                       return method.invoke(value, 
localParams);
+                               } catch (AmbiguousException e) {
+                                       throw new IntrospectionException(e);
+                               } catch (IllegalAccessException e) {
+                                       throw new IntrospectionException(e);
+                               }
+                       }
+               } catch (NumberFormatException e) {
+                       return null;
+               } catch (InvocationTargetException e) {
+                       // catch array index issues gracefully, otherwise 
release
+                       if (e.getCause() instanceof IndexOutOfBoundsException) {
+                               return null;
+                       }
+
+                       throw new 
IntrospectionException(e.getTargetException());
+               }
+
+               final String message =
+                               String.format("The token '%s' at position '%d' 
refers to a java.util.List or an array, but the value seems is an instance of 
'%s'",
+                                               expression.subSequence(from, 
to), from, value.getClass());
+
+               throw new IntrospectionException(message);
+       }
+
+       private static Object getPropertyValue(Object value, String property)
+                       throws IntrospectionException {
+               if (value == null || property == null) {
+                       return null;
+               }
+
+               ClassMap classMap = getClassMap(value.getClass());
+               String methodBase = 
org.codehaus.plexus.util.StringUtils.capitalizeFirstLetter(property);
+               String methodName = "get" + methodBase;
+        try {
+               Method method = classMap.findMethod(methodName, CLASS_ARGS);
+
+               if (method == null) {
+                       // perhaps this is a boolean property??
+                       methodName = "is" + methodBase;
+
+                       method = classMap.findMethod(methodName, CLASS_ARGS);
+               }
+
+               if (method == null) {
+                       return null;
+               }
+
+                       return method.invoke(value, OBJECT_ARGS);
+               } catch (InvocationTargetException e) {
+                       throw new 
IntrospectionException(e.getTargetException());
+               } catch (AmbiguousException e) {
+            throw new IntrospectionException(e);
+        } catch (IllegalAccessException e) {
+            throw new IntrospectionException(e);
         }
-
-        Object value = root;
-
-        // 
----------------------------------------------------------------------
-        // Walk the dots and retrieve the ultimate value desired from the
-        // MavenProject instance.
-        // 
----------------------------------------------------------------------
-
-        StringTokenizer parser = new StringTokenizer( expression, "." );
-
-        while ( parser.hasMoreTokens() )
-        {
-            // if we have nothing, stop now
-            if ( value == null )
-            {
-                return null;
-            }
-
-            String token = parser.nextToken();
-
-            ClassMap classMap = getClassMap( value.getClass() );
-
-            Method method;
-            Object[] localParams = OBJECT_ARGS;
-
-            // do we have an indexed property?
-            Matcher matcher = INDEXED_PROPS.matcher( token );
-            if ( matcher.find() )
-            {
-                String methodBase = StringUtils.capitalizeFirstLetter( 
matcher.group( 1 ) );
-                String methodName = "get" + methodBase;
-                try
-                {
-                    method = classMap.findMethod( methodName, CLASS_ARGS );
-                }
-                catch ( AmbiguousException e )
-                {
-                    throw new IntrospectionException( e );
-                }
-                
-                try
-                {
-                    value = method.invoke( value, OBJECT_ARGS );
-                }
-                catch ( IllegalArgumentException e )
-                {
-                    throw new IntrospectionException( e );
-                }
-                catch ( IllegalAccessException e )
-                {
-                    throw new IntrospectionException( e );
-                }
-                catch ( InvocationTargetException e )
-                {
-                    throw new IntrospectionException( e );
-                }                
-                
-                classMap = getClassMap( value.getClass() );
-
-                if ( classMap.getCachedClass().isArray() )
-                {
-                    value = Arrays.asList( (Object[]) value );
-                    classMap = getClassMap( value.getClass() );
-                }
-
-                if ( value instanceof List )
-                {
-                    // use get method on List interface
-                    localParams = new Object[1];
-                    localParams[0] = Integer.valueOf( matcher.group( 2 ) );
-                    try
-                    {
-                        method = classMap.findMethod( "get", localParams );
-                    }
-                    catch ( AmbiguousException e )
-                    {
-                        throw new IntrospectionException( e );
-                    }
-                }
-                else
-                {
-                    throw new IntrospectionException( "The token '" + token
-                                             + "' refers to a java.util.List 
or an array, but the value seems is an instance of '"
-                                             + value.getClass() + "'." );
-                }
-            }
-            else
-            {
-                // do we have a mapped property?
-                matcher = MAPPED_PROPS.matcher( token );
-                if ( matcher.find() )
-                {
-                    String methodBase = StringUtils.capitalizeFirstLetter( 
matcher.group( 1 ) );
-                    String methodName = "get" + methodBase;
-                    try
-                    {
-                        method = classMap.findMethod( methodName, CLASS_ARGS );
-                    }
-                    catch ( AmbiguousException e )
-                    {
-                        throw new IntrospectionException( e );
-                    }
-                    
-                    try
-                    {
-                        value = method.invoke( value, OBJECT_ARGS );
-                    }
-                    catch ( IllegalArgumentException e )
-                    {
-                        throw new IntrospectionException( e );
-                    }
-                    catch ( IllegalAccessException e )
-                    {
-                        throw new IntrospectionException( e );
-                    }
-                    catch ( InvocationTargetException e )
-                    {
-                        throw new IntrospectionException( e );
-                    }
-                    classMap = getClassMap( value.getClass() );
-
-                    if ( value instanceof Map )
-                    {
-                        // use get method on List interface
-                        localParams = new Object[1];
-                        localParams[0] = matcher.group( 2 );
-                        try
-                        {
-                            method = classMap.findMethod( "get", localParams );
-                        }
-                        catch ( AmbiguousException e )
-                        {
-                            throw new IntrospectionException( e );
-                        }
-                    }
-                    else
-                    {
-                        throw new IntrospectionException( "The token '" + token
-                                                 + "' refers to a 
java.util.Map, but the value seems is an instance of '"
-                                                 + value.getClass() + "'." );
-                    }
-                }
-                else
-                {
-                    String methodBase = StringUtils.capitalizeFirstLetter( 
token );
-                    String methodName = "get" + methodBase;
-                    try
-                    {
-                        method = classMap.findMethod( methodName, CLASS_ARGS );
-                    }
-                    catch ( AmbiguousException e )
-                    {
-                        throw new IntrospectionException( e );
-                    }
-
-                    if ( method == null )
-                    {
-                        // perhaps this is a boolean property??
-                        methodName = "is" + methodBase;
-
-                        try
-                        {
-                            method = classMap.findMethod( methodName, 
CLASS_ARGS );
-                        }
-                        catch ( AmbiguousException e )
-                        {
-                            throw new IntrospectionException( e );
-                        }
-                    }
-                }
-            }
-
-            if ( method == null )
-            {
-                return null;
-            }
-
-            try
-            {
-                value = method.invoke( value, localParams );
-            }
-            catch ( InvocationTargetException e )
-            {
-                // catch array index issues gracefully, otherwise release
-                if ( e.getCause() instanceof IndexOutOfBoundsException )
-                {
-                    return null;
-                }
-
-                throw new IntrospectionException( e );
-            }
-            catch ( IllegalArgumentException e )
-            {
-                throw new IntrospectionException( e );
-            }
-            catch ( IllegalAccessException e )
-            {
-                throw new IntrospectionException( e );
-            }
-        }
-
-        return value;
     }
 
-    private static ClassMap getClassMap( Class<?> clazz )
-    {
-        ClassMap classMap = classMaps.get( clazz );
-
-        if ( classMap == null )
-        {
-            classMap = new ClassMap( clazz );
+       private static ClassMap getClassMap(Class<?> clazz) {
+               ClassMap classMap = classMaps.get(clazz);
 
-            classMaps.put( clazz, classMap );
-        }
+               if (classMap == null) {
+                       classMap = new ClassMap(clazz);
 
-        return classMap;
-    }
+                       classMaps.put(clazz, classMap);
+               }
+
+               return classMap;
+       }
 }

Modified: 
maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractorTest.java
URL: 
http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractorTest.java?rev=1631643&r1=1631642&r2=1631643&view=diff
==============================================================================
--- 
maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractorTest.java
 (original)
+++ 
maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractorTest.java
 Tue Oct 14 06:52:39 2014
@@ -24,6 +24,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+
 import junit.framework.Assert;
 import junit.framework.TestCase;
 
@@ -57,6 +58,11 @@ public class ReflectionValueExtractorTes
         project.addDependency( dependency1 );
         project.addDependency( dependency2 );
         project.setBuild( new Build() );
+
+        // Build up an artifactMap
+        project.addArtifact( new Artifact("g0","a0","v0","e0","c0") );
+        project.addArtifact( new Artifact("g1","a1","v1","e1","c1") );
+        project.addArtifact( new Artifact("g2","a2","v2","e2","c2") );
     }
 
     public void testValueExtraction()
@@ -148,6 +154,185 @@ public class ReflectionValueExtractorTes
         Assert.assertNull( ReflectionValueExtractor.evaluate( 
"project.dependencies[0].foo", project ) );
     }
 
+    public void testMappedDottedKey()
+            throws Exception
+    {
+        Map<String, String> map = new HashMap<String, String>();
+        map.put( "a.b", "a.b-value" );
+
+        Assert.assertEquals( "a.b-value", 
ReflectionValueExtractor.evaluate("h.value(a.b)", new ValueHolder(map)) );
+    }
+
+    public void testIndexedMapped()
+            throws Exception
+    {
+        Map<Object, Object> map = new HashMap<Object, Object>();
+        map.put( "a", "a-value" );
+        List<Object> list = new ArrayList<Object>();
+        list.add( map );
+
+        Assert.assertEquals( "a-value", 
ReflectionValueExtractor.evaluate("h.value[0](a)", new ValueHolder(list)) );
+    }
+
+    public void testMappedIndexed()
+            throws Exception
+    {
+        List<Object> list = new ArrayList<Object>();
+        list.add( "a-value" );
+        Map<Object, Object> map = new HashMap<Object, Object>();
+        map.put( "a", list );
+        Assert.assertEquals( "a-value", 
ReflectionValueExtractor.evaluate("h.value(a)[0]", new ValueHolder(map)) );
+    }
+
+    public void testMappedMissingDot()
+            throws Exception
+    {
+        Map<Object, Object> map = new HashMap<Object, Object>();
+        map.put( "a", new ValueHolder( "a-value" ) );
+        Assert.assertNull( 
ReflectionValueExtractor.evaluate("h.value(a)value", new ValueHolder(map)) );
+    }
+
+    public void testIndexedMissingDot()
+            throws Exception
+    {
+        List<Object> list = new ArrayList<Object>();
+        list.add( new ValueHolder( "a-value" ) );
+        Assert.assertNull( 
ReflectionValueExtractor.evaluate("h.value[0]value", new ValueHolder(list)) );
+    }
+
+    public void testDotDot()
+            throws Exception
+    {
+        Assert.assertNull( ReflectionValueExtractor.evaluate("h..value", new 
ValueHolder("value")) );
+    }
+
+    public void testBadIndexedSyntax()
+            throws Exception
+    {
+        List<Object> list = new ArrayList<Object>();
+        list.add( "a-value" );
+        Object value = new ValueHolder( list );
+
+        Assert.assertNull( ReflectionValueExtractor.evaluate("h.value[", 
value) );
+        Assert.assertNull( ReflectionValueExtractor.evaluate("h.value[]", 
value) );
+        Assert.assertNull( ReflectionValueExtractor.evaluate("h.value[a]", 
value) );
+        Assert.assertNull( ReflectionValueExtractor.evaluate("h.value[0", 
value) );
+        Assert.assertNull( ReflectionValueExtractor.evaluate("h.value[0)", 
value) );
+        Assert.assertNull( ReflectionValueExtractor.evaluate("h.value[-1]", 
value) );
+    }
+
+    public void testBadMappedSyntax()
+            throws Exception
+    {
+        Map<Object, Object> map = new HashMap<Object, Object>();
+        map.put( "a", "a-value" );
+        Object value = new ValueHolder( map );
+
+        Assert.assertNull( ReflectionValueExtractor.evaluate("h.value(", 
value) );
+        Assert.assertNull( ReflectionValueExtractor.evaluate("h.value()", 
value) );
+        Assert.assertNull( ReflectionValueExtractor.evaluate("h.value(a", 
value) );
+        Assert.assertNull( ReflectionValueExtractor.evaluate("h.value(a]", 
value) );
+    }
+
+    public void testIllegalIndexedType()
+            throws Exception
+    {
+        try
+        {
+            ReflectionValueExtractor.evaluate("h.value[1]", new 
ValueHolder("string"));
+        }
+        catch ( Exception e )
+        {
+            // TODO assert exception message
+        }
+    }
+
+    public void testIllegalMappedType()
+            throws Exception
+    {
+        try
+        {
+            ReflectionValueExtractor.evaluate("h.value(key)", new 
ValueHolder("string"));
+        }
+        catch ( Exception e )
+        {
+            // TODO assert exception message
+        }
+    }
+
+    public void testTrimRootToken()
+            throws Exception
+    {
+        Assert.assertNull( ReflectionValueExtractor.evaluate("project", 
project, true) );
+    }
+
+    public void testArtifactMap()
+            throws Exception
+    {
+        assertEquals( "g0", ((Artifact) 
ReflectionValueExtractor.evaluate("project.artifactMap(g0:a0:c0)", 
project)).getGroupId() );
+        assertEquals( "a1", ((Artifact) 
ReflectionValueExtractor.evaluate("project.artifactMap(g1:a1:c1)", 
project)).getArtifactId() );
+        assertEquals( "c2", ((Artifact) 
ReflectionValueExtractor.evaluate("project.artifactMap(g2:a2:c2)", 
project)).getClassifier() );
+    }
+
+    public static class Artifact
+    {
+        private String groupId;
+        private String artifactId;
+        private String version;
+        private String extension;
+        private String classifier;
+
+        public Artifact( String groupId, String artifactId, String version, 
String extension, String classifier )
+        {
+            this.groupId = groupId;
+            this.artifactId = artifactId;
+            this.version = version;
+            this.extension = extension;
+            this.classifier = classifier;
+        }
+
+        public String getGroupId()
+        {
+            return groupId;
+        }
+        public void setGroupId( String groupId )
+        {
+            this.groupId = groupId;
+        }
+        public String getArtifactId()
+        {
+            return artifactId;
+        }
+        public void setArtifactId( String artifactId )
+        {
+            this.artifactId = artifactId;
+        }
+        public String getVersion()
+        {
+            return version;
+        }
+        public void setVersion( String version )
+        {
+            this.version = version;
+        }
+        public String getExtension()
+        {
+            return extension;
+        }
+        public void setExtension( String extension )
+        {
+            this.extension = extension;
+        }
+        public String getClassifier()
+        {
+            return classifier;
+        }
+        public void setClassifier( String classifier )
+        {
+            this.classifier = classifier;
+        }
+    }
+
     public static class Project
     {
         private String modelVersion;
@@ -166,6 +351,8 @@ public class ReflectionValueExtractorTes
 
         private String version;
 
+        private Map<String,Artifact> artifactMap = new 
HashMap<String,Artifact>();
+
         public void setModelVersion( String modelVersion )
         {
             this.modelVersion = modelVersion;
@@ -262,8 +449,21 @@ public class ReflectionValueExtractorTes
             }
             return ret;
         }
+
+
+        // ${project.artifactMap(g:a:v)}
+        public void addArtifact(Artifact a)
+        {
+            artifactMap.put( a.getGroupId() + ":" + a.getArtifactId() + ":" + 
a.getClassifier(), a );
+        }
+
+        public Map<String,Artifact> getArtifactMap()
+        {
+            return artifactMap;
+        }
     }
 
+
     public static class Build
     {
 
@@ -298,4 +498,19 @@ public class ReflectionValueExtractorTes
             return connection;
         }
     }
+
+    public static class ValueHolder
+    {
+        private final Object value;
+
+        public ValueHolder( Object value )
+        {
+            this.value = value;
+        }
+
+        public Object getValue()
+        {
+            return value;
+        }
+    }
 }


Reply via email to