component selection still not working...at least, in tests. Needs more work

git-svn-id: https://svn.apache.org/repos/asf/maven/sandbox/trunk/mae@1159745 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/mae-api/src/main/java/org/apache/maven/mae/internal/container/ComponentKey.java b/mae-api/src/main/java/org/apache/maven/mae/internal/container/ComponentKey.java
index 5630ce3..8e24258 100644
--- a/mae-api/src/main/java/org/apache/maven/mae/internal/container/ComponentKey.java
+++ b/mae-api/src/main/java/org/apache/maven/mae/internal/container/ComponentKey.java
@@ -21,6 +21,14 @@
 
 import static org.codehaus.plexus.util.StringUtils.isBlank;
 
+import java.lang.reflect.Field;
+
+import org.codehaus.plexus.component.annotations.Component;
+import org.codehaus.plexus.component.annotations.Requirement;
+import org.sonatype.guice.plexus.config.Roles;
+
+import com.google.inject.Key;
+
 public class ComponentKey<T>
 {
 
@@ -44,6 +52,35 @@
         hint = DEFAULT_HINT;
     }
 
+    @SuppressWarnings( "unchecked" )
+    public ComponentKey( final Component comp )
+    {
+        roleClass = (Class<T>) comp.role();
+
+        String h = comp.hint();
+        hint = ( isBlank( h ) || DEFAULT_HINT.equals( h ) ? DEFAULT_HINT : h );
+    }
+
+    @SuppressWarnings( { "unchecked", "rawtypes" } )
+    public ComponentKey( final Requirement req, final Field field )
+    {
+        if ( req.hints() != null && req.hints().length > 0 )
+        {
+            throw new IllegalArgumentException( "Cannot construct ComponentKey for requirement listing multiple hints." );
+        }
+
+        Class role = req.role();
+        if ( role == null || role.equals( Object.class ) )
+        {
+            role = field.getType();
+        }
+
+        roleClass = role;
+
+        String h = req.hint();
+        hint = ( isBlank( h ) || DEFAULT_HINT.equals( h ) ? DEFAULT_HINT : h );
+    }
+
     public String getRole()
     {
         return roleClass.getName();
@@ -56,7 +93,22 @@
 
     public String key()
     {
-        return roleClass + ( DEFAULT_HINT.equals( hint ) ? "" : "#" + hint );
+        return roleClass.getName() + ( DEFAULT_HINT.equals( hint ) ? "" : "#" + hint );
+    }
+
+    public Key<T> componentKey()
+    {
+        return Roles.componentKey( roleClass, hint );
+    }
+
+    public Key<T> literalComponentKey()
+    {
+        return isLiteral() ? componentKey() : Roles.componentKey( roleClass, hint + LITERAL_SUFFIX );
+    }
+
+    public Key<T> rawComponentKey()
+    {
+        return Roles.componentKey( roleClass, getLiteralHint( hint ) );
     }
 
     @Override
@@ -115,6 +167,11 @@
         return instance == null ? null : roleClass.cast( instance );
     }
 
+    public boolean isLiteral()
+    {
+        return isLiteral( hint );
+    }
+
     public static boolean isLiteral( final String value )
     {
         return value != null && value.length() > 1 && value.endsWith( LITERAL_SUFFIX );
diff --git a/mae-api/src/main/java/org/apache/maven/mae/internal/container/ComponentSelector.java b/mae-api/src/main/java/org/apache/maven/mae/internal/container/ComponentSelector.java
index 663cab4..cff1227 100644
--- a/mae-api/src/main/java/org/apache/maven/mae/internal/container/ComponentSelector.java
+++ b/mae-api/src/main/java/org/apache/maven/mae/internal/container/ComponentSelector.java
@@ -60,6 +60,11 @@
         return remappedComponentHints.isEmpty();
     }
 
+    public boolean hasOverride( final ComponentKey<?> key )
+    {
+        return remappedComponentHints.containsKey( key );
+    }
+
     public <T> boolean hasOverride( final Class<T> role, final String hint )
     {
         final ComponentKey<T> check = new ComponentKey<T>( role, hint );
@@ -121,4 +126,10 @@
         return (ComponentKey<T>) remappedComponentHints.get( new ComponentKey<T>( role, hint ) );
     }
 
+    @SuppressWarnings( "unchecked" )
+    public <T> ComponentKey<T> getOverride( final ComponentKey<T> key )
+    {
+        return (ComponentKey<T>) remappedComponentHints.get( key );
+    }
+
 }
diff --git a/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/ComponentSelectionModule.java b/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/ComponentSelectionModule.java
index 1f6a6e8..35c3607 100644
--- a/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/ComponentSelectionModule.java
+++ b/mae-booter/src/main/java/org/apache/maven/mae/boot/embed/ComponentSelectionModule.java
@@ -1,23 +1,26 @@
 package org.apache.maven.mae.boot.embed;
 
 import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
 
 import org.apache.maven.mae.internal.container.ComponentKey;
 import org.apache.maven.mae.internal.container.ComponentSelector;
+import org.codehaus.plexus.component.annotations.Component;
 import org.codehaus.plexus.component.annotations.Requirement;
-import org.sonatype.guice.plexus.config.Hints;
-import org.sonatype.guice.plexus.config.Roles;
 
 import com.google.inject.Binder;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.google.inject.MembersInjector;
 import com.google.inject.Module;
+import com.google.inject.Provider;
 import com.google.inject.TypeLiteral;
 import com.google.inject.matcher.Matchers;
+import com.google.inject.spi.Dependency;
+import com.google.inject.spi.InjectionListener;
+import com.google.inject.spi.InjectionPoint;
 import com.google.inject.spi.TypeEncounter;
 import com.google.inject.spi.TypeListener;
 
@@ -36,7 +39,43 @@
     @Override
     public void configure( final Binder binder )
     {
-        binder.bindListener( Matchers.any(), new ComponentSelectingListener( selector ) );
+        // binder.bindListener( Matchers.any(), new SysoutTypeListener() );
+        binder.bindListener( Matchers.any(), new ComponentSelectingListener( selector, binder ) );
+    }
+
+    @SuppressWarnings( "unused" )
+    private static final class SysoutTypeListener
+        implements TypeListener
+    {
+
+        @Override
+        public <I> void hear( final TypeLiteral<I> type, final TypeEncounter<I> encounter )
+        {
+            Set<InjectionPoint> injectionPoints = InjectionPoint.forInstanceMethodsAndFields( type.getRawType() );
+            if ( injectionPoints != null && !injectionPoints.isEmpty() )
+            {
+                System.out.println( type.getRawType().getName() );
+
+                for ( InjectionPoint ip : injectionPoints )
+                {
+                    Dependency<?> dep = ip.getDependencies().get( 0 );
+                    System.out.printf( "%s --> %s", ip.getMember(), dep.getKey() );
+                }
+            }
+
+            Component comp = type.getRawType().getAnnotation( Component.class );
+            if ( comp != null )
+            {
+                ComponentKey<I> found = new ComponentKey<I>( comp );
+
+                System.out.printf( "%s [Provider: %s]\n", found, encounter.getProvider( found.componentKey() ) );
+            }
+            else
+            {
+                System.out.printf( "RAW: %s\n", type.getRawType().getName() );
+            }
+        }
+
     }
 
     private static final class ComponentSelectingListener
@@ -45,93 +84,155 @@
 
         private final ComponentSelector selector;
 
-        public ComponentSelectingListener( final ComponentSelector selector )
+        private final Binder binder;
+
+        public ComponentSelectingListener( final ComponentSelector selector, final Binder binder )
         {
             this.selector = selector;
+            this.binder = binder;
         }
 
+        @SuppressWarnings( { "unchecked", "rawtypes" } )
         @Override
         public <I> void hear( final TypeLiteral<I> type, final TypeEncounter<I> encounter )
         {
-            List<Field> requirementFields = new ArrayList<Field>();
+            Map<Field, Provider<?>> requirementFields = new HashMap<Field, Provider<?>>();
+
+            // Component comp = type.getRawType().getAnnotation( Component.class );
+            // Set<InjectionPoint> injectionPoints = InjectionPoint.forInstanceMethodsAndFields( type.getRawType() );
+            // if ( comp != null || ( injectionPoints != null && !injectionPoints.isEmpty() ) )
+            // {
+            // encounter.register( null )
+            // }
+
             for ( Field field : type.getRawType().getDeclaredFields() )
             {
-                if ( field.getAnnotation( Requirement.class ) != null )
+                Requirement req = field.getAnnotation( Requirement.class );
+                if ( req != null )
                 {
-                    requirementFields.add( field );
+                    // FIXME: Collections!
+                    if ( !field.getType().equals( req.role() ) )
+                    {
+                        // System.out.printf( "Found collection? %s\n", field );
+                    }
+                    else
+                    {
+                        ComponentKey key = new ComponentKey( req, field );
+
+                        Key componentKey = key.componentKey();
+                        if ( key.isLiteral() )
+                        {
+                            componentKey = key.rawComponentKey();
+                            // System.out.printf( "%s ------> %s\n", field, componentKey );
+                        }
+                        else if ( selector.hasOverride( key ) )
+                        {
+                            binder.bind( key.literalComponentKey() ).toProvider( encounter.getProvider( key.componentKey() ) );
+                            componentKey = selector.getOverride( key ).componentKey();
+                            // System.out.printf( "%s ------> %s\n", field, componentKey );
+                        }
+
+                        requirementFields.put( field, encounter.getProvider( componentKey ) );
+                    }
                 }
             }
 
             if ( !requirementFields.isEmpty() )
             {
-                encounter.register( new ComponentSelectionInjector<I>( selector, requirementFields ) );
+                final ComponentSelectionInjector<I> injector = new ComponentSelectionInjector<I>( requirementFields );
+                encounter.register( new MembersInjector<I>()
+                {
+                    @Override
+                    public void injectMembers( final I instance )
+                    {
+                        injector.inject( instance );
+                    }
+                } );
+
+                encounter.register( new InjectionListener<I>()
+                {
+                    @Override
+                    public void afterInjection( final I instance )
+                    {
+                        injector.inject( instance );
+                    }
+                } );
             }
         }
     }
 
     private static final class ComponentSelectionInjector<T>
-        implements MembersInjector<T>
     {
-        // FIXME: This is NOT being injected!!
-        @Inject
-        private Injector injector;
+        private final Map<Field, Provider<?>> fields;
 
-        private final ComponentSelector selector;
-
-        private final List<Field> fields;
-
-        public ComponentSelectionInjector( final ComponentSelector selector, final List<Field> fields )
+        public ComponentSelectionInjector( final Map<Field, Provider<?>> fields )
         {
-            this.selector = selector;
             this.fields = fields;
         }
 
-        @SuppressWarnings( { "rawtypes", "unchecked" } )
-        @Override
-        public void injectMembers( final T instance )
+        public void inject( final T instance )
         {
-            for ( Field field : fields )
+            for ( Map.Entry<Field, Provider<?>> entry : fields.entrySet() )
             {
-                Requirement req = field.getAnnotation( Requirement.class );
-                Class role = req.role();
-                if ( role == null )
+                Field field = entry.getKey();
+                Provider<?> provider = entry.getValue();
+                boolean acc = field.isAccessible();
+                if ( !acc )
                 {
-                    role = field.getType();
+                    field.setAccessible( true );
                 }
 
-                // FIXME: What about req.hints() ???
-                String hint = req.hint();
-                if ( hint == null )
+                try
                 {
-                    hint = Hints.DEFAULT_HINT;
+                    Object value = provider.get();
+                    System.out.printf( "Setting %s to: %s\n", field, value );
+                    field.set( instance, value );
+                }
+                catch ( IllegalArgumentException e )
+                {
+                    throw new RuntimeException( e );
+                }
+                catch ( IllegalAccessException e )
+                {
+                    throw new RuntimeException( e );
+                }
+                finally
+                {
+                    field.setAccessible( acc );
                 }
 
-                ComponentKey key;
-                if ( ComponentKey.isLiteral( hint ) )
-                {
-                    key = new ComponentKey( role, ComponentKey.getLiteralHint( hint ) );
-                }
-                else if ( selector.hasOverride( role, hint ) )
-                {
-                    key = selector.getOverride( role, hint );
-                }
-                else
-                {
-                    key = new ComponentKey( role, hint );
-                }
-
-                set( field, instance, key );
+                // FieldSetter setter = new FieldSetter( entry.getKey(), entry.getValue(), instance );
+                // AccessController.doPrivileged( setter );
             }
         }
 
-        @SuppressWarnings( { "rawtypes", "unchecked" } )
-        private void set( final Field field, final Object instance, final ComponentKey key )
+    }
+
+    @SuppressWarnings( "unused" )
+    private static final class FieldSetter<T>
+        implements PrivilegedAction<T>
+    {
+
+        private final Field field;
+
+        private final Provider<?> provider;
+
+        private final T instance;
+
+        FieldSetter( final Field field, final Provider<?> provider, final T instance )
         {
-            Key componentKey = Roles.componentKey( key.getRoleClass(), key.getHint() );
-            Object value = injector.getInstance( componentKey );
+            this.field = field;
+            this.provider = provider;
+            this.instance = instance;
+
+        }
+
+        @Override
+        public T run()
+        {
             try
             {
-                field.set( instance, value );
+                field.set( instance, provider.get() );
             }
             catch ( IllegalArgumentException e )
             {
@@ -141,6 +242,8 @@
             {
                 throw new RuntimeException( e );
             }
+
+            return null;
         }
 
     }
diff --git a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/MAEApplicationTest.java b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/MAEApplicationTest.java
index 457c6ee..8e1a66b 100644
--- a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/MAEApplicationTest.java
+++ b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/MAEApplicationTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.maven.mae.internal.container;
 
+import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.junit.Assert.assertThat;
@@ -28,6 +29,9 @@
 import org.apache.maven.mae.boot.embed.MAEEmbedderBuilder;
 import org.apache.maven.mae.internal.container.fixture.ContainerOwner;
 import org.apache.maven.mae.internal.container.fixture.DefaultSingletonOwner;
+import org.apache.maven.mae.internal.container.fixture.NonSimplePart;
+import org.apache.maven.mae.internal.container.fixture.Part;
+import org.apache.maven.mae.internal.container.fixture.SimplePart;
 import org.apache.maven.mae.internal.container.fixture.SingletonLiteralOwner;
 import org.apache.maven.mae.internal.container.fixture.SingletonOwner;
 import org.junit.Test;
@@ -91,7 +95,10 @@
         throws Throwable
     {
         ContainerOwner owner = new ContainerOwner();
-        new TestApplication().load();
+        new TestApplication().withInstance( owner ).load();
+
+        assertThat( owner.container, notNullValue() );
+
         final DefaultSingletonOwner single = owner.container.lookup( DefaultSingletonOwner.class );
 
         assertThat( single.singleton(), notNullValue() );
@@ -102,21 +109,47 @@
         throws Throwable
     {
         ContainerOwner owner = new ContainerOwner();
-        new TestApplication().load();
+        new TestApplication().withInstance( owner ).load();
+
+        assertThat( owner.container, notNullValue() );
+
         final SingletonOwner single = owner.container.lookup( SingletonOwner.class );
 
         assertThat( single.singleton(), notNullValue() );
     }
 
     @Test
+    public void singletonSelectedRequirement()
+        throws Throwable
+    {
+        ContainerOwner owner = new ContainerOwner();
+        new TestApplication().withInstance( owner ).withComponentSelection( new ComponentKey<Part>( Part.class,
+                                                                                                    "simple" ),
+                                                                            "non-simple" ).load();
+
+        assertThat( owner.container, notNullValue() );
+
+        final SingletonOwner single = owner.container.lookup( SingletonOwner.class );
+
+        assertThat( single.singleton(), notNullValue() );
+        assertThat( single.singleton(), instanceOf( NonSimplePart.class ) );
+    }
+
+    @Test
     public void singletonLiteralRequirement()
         throws Throwable
     {
         ContainerOwner owner = new ContainerOwner();
-        new TestApplication().load();
+        new TestApplication().withInstance( owner ).withComponentSelection( new ComponentKey<Part>( Part.class,
+                                                                                                    "simple" ),
+                                                                            "non-simple" ).load();
+
+        assertThat( owner.container, notNullValue() );
+
         final SingletonLiteralOwner single = owner.container.lookup( SingletonLiteralOwner.class );
 
         assertThat( single.singletonLiteral(), notNullValue() );
+        assertThat( single.singletonLiteral(), instanceOf( SimplePart.class ) );
     }
 
     //
@@ -136,11 +169,25 @@
     {
         private final String name;
 
+        private ComponentSelector selector;
+
         TestApplication()
         {
             name = new Exception().getStackTrace()[1].getMethodName();
         }
 
+        public synchronized AbstractMAEApplication withComponentSelection( final ComponentKey<?> intercept,
+                                                                           final String newHint )
+        {
+            if ( selector == null )
+            {
+                selector = new ComponentSelector();
+            }
+
+            selector.setSelection( intercept, newHint );
+            return this;
+        }
+
         public final TestApplication withInstance( final Object instance )
         {
             withComponentInstance( instance );
@@ -167,6 +214,12 @@
             super.configureBuilder( builder );
         }
 
+        @Override
+        public ComponentSelector getComponentSelector()
+        {
+            return selector;
+        }
+
     }
 
 }
diff --git a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/MapOwner.java b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/MapOwner.java
index eb7809c..141b979 100644
--- a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/MapOwner.java
+++ b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/MapOwner.java
@@ -28,10 +28,10 @@
 public class MapOwner
 {
 
-    @Requirement( role = Child.class )
-    private Map<String, Child> members;
+    @Requirement( role = SimplePart.class )
+    private Map<String, SimplePart> members;
 
-    public Map<String, Child> members()
+    public Map<String, SimplePart> members()
     {
         return members;
     }
diff --git a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/Child.java b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/NonSimplePart.java
similarity index 83%
copy from mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/Child.java
copy to mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/NonSimplePart.java
index e752bbe..ab6d34c 100644
--- a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/Child.java
+++ b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/NonSimplePart.java
@@ -21,8 +21,15 @@
 
 import org.codehaus.plexus.component.annotations.Component;
 
-@Component( role = Child.class, hint = "simple" )
-public class Child
+@Component( role = Part.class, hint = "non-simple" )
+public class NonSimplePart
+    implements Part
 {
 
+    @Override
+    public String getType()
+    {
+        return "simple";
+    }
+
 }
diff --git a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/Part.java b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/Part.java
new file mode 100644
index 0000000..45624b6
--- /dev/null
+++ b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/Part.java
@@ -0,0 +1,8 @@
+package org.apache.maven.mae.internal.container.fixture;
+
+public interface Part
+{
+
+    String getType();
+
+}
\ No newline at end of file
diff --git a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/Child.java b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/SimplePart.java
similarity index 84%
rename from mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/Child.java
rename to mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/SimplePart.java
index e752bbe..e8a52a1 100644
--- a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/Child.java
+++ b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/SimplePart.java
@@ -21,8 +21,15 @@
 
 import org.codehaus.plexus.component.annotations.Component;
 
-@Component( role = Child.class, hint = "simple" )
-public class Child
+@Component( role = Part.class, hint = "simple" )
+public class SimplePart
+    implements Part
 {
 
+    @Override
+    public String getType()
+    {
+        return "simple";
+    }
+
 }
diff --git a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/SingletonLiteralOwner.java b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/SingletonLiteralOwner.java
index b984673..c2b330b 100644
--- a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/SingletonLiteralOwner.java
+++ b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/SingletonLiteralOwner.java
@@ -26,10 +26,10 @@
 public class SingletonLiteralOwner
 {
 
-    @Requirement( role = Child.class, hint = "simple_" )
-    private Child singletonLiteral;
+    @Requirement( role = Part.class, hint = "simple_" )
+    private Part singletonLiteral;
 
-    public Child singletonLiteral()
+    public Part singletonLiteral()
     {
         return singletonLiteral;
     }
diff --git a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/SingletonOwner.java b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/SingletonOwner.java
index 3c4ef93..4b6cc67 100644
--- a/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/SingletonOwner.java
+++ b/mae-booter/src/test/java/org/apache/maven/mae/internal/container/fixture/SingletonOwner.java
@@ -26,10 +26,10 @@
 public class SingletonOwner
 {
 
-    @Requirement( role = Child.class, hint = "simple" )
-    private Child singleton;
+    @Requirement( role = Part.class, hint = "simple" )
+    private Part singleton;
 
-    public Child singleton()
+    public Part singleton()
     {
         return singleton;
     }