/*******************************************************************************
 * Copyright (c) 2010-2011 Sonatype, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 *   http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.sonatype.guice.plexus.binders;

import java.util.ArrayList;
import java.util.List;

import org.sonatype.guice.bean.inject.BeanBinder;
import org.sonatype.guice.bean.inject.PropertyBinder;
import org.sonatype.guice.plexus.config.PlexusBeanMetadata;
import org.sonatype.guice.plexus.config.PlexusBeanSource;

import com.google.inject.TypeLiteral;
import com.google.inject.spi.ProvisionListener;
import com.google.inject.spi.TypeEncounter;

/**
 * {@link BeanBinder} that binds bean properties according to Plexus metadata.
 */
final class PlexusBeanBinder
    implements BeanBinder, ProvisionListener
{
    // ----------------------------------------------------------------------
    // Implementation fields
    // ----------------------------------------------------------------------

    private static final ThreadLocal<List<Object>> PENDING = new ThreadLocal<List<Object>>()
    {
        @Override
        protected java.util.List<Object> initialValue()
        {
            return new ArrayList<Object>();
        }
    };

    private final PlexusBeanManager manager;

    private final PlexusBeanSource[] sources;

    // ----------------------------------------------------------------------
    // Constructors
    // ----------------------------------------------------------------------

    PlexusBeanBinder( final PlexusBeanManager manager, final List<PlexusBeanSource> sources )
    {
        this.manager = manager;
        this.sources = sources.toArray( new PlexusBeanSource[sources.size()] );
    }

    // ----------------------------------------------------------------------
    // Public methods
    // ----------------------------------------------------------------------

    public <B> PropertyBinder bindBean( final TypeLiteral<B> type, final TypeEncounter<B> encounter )
    {
        final Class<?> clazz = type.getRawType();
        for ( final PlexusBeanSource source : sources )
        {
            // use first source that has metadata for the given implementation
            final PlexusBeanMetadata metadata = source.getBeanMetadata( clazz );
            if ( metadata != null )
            {
                return new PlexusPropertyBinder( manager, encounter, metadata );
            }
        }
        return null; // no need to auto-bind
    }

    public <T> void onProvision( final ProvisionInvocation<T> pi )
    {
        final List<Object> pending = PENDING.get();
        final boolean isRoot = pending.isEmpty();
        if ( isRoot )
        {
            pending.add( null ); // must add NULL place-holder before scheduling starts
        }

        final Object bean = pi.provision(); // this may involve more onProvision calls

        if ( null != bean )
        {
            // only activate on unique implementation binding (avoids duplicates)
            final Class<?> boundType = pi.getKey().getTypeLiteral().getRawType();
            if ( boundType == bean.getClass() && manager.manage( bean.getClass() ) )
            {
                pending.add( bean );
            }
        }

        if ( isRoot )
        {
            // cache+clear to avoid blocking later on
            final Object[] beans = pending.toArray();
            pending.clear();

            // process in order of creation; but skip the NULL place-holder at the start
            for ( int i = 1; i < beans.length; i++ )
            {
                manager.manage( beans[i] ); // this may involve more onProvision calls
            }
        }
    }
}

@@ -803,16 +803,64 @@
 iteral;%0A
+import com.google.inject.spi.InjectionListener;%0A
 import c
@@ -1086,16 +1086,43 @@
 nBinder,
+ InjectionListener%3CObject%3E,
  Provisi
@@ -1455,26 +1455,16 @@
 otected 
-java.util.
 List%3CObj
@@ -2373,32 +2373,150 @@
 e.getRawType();%0A
+        if ( null != manager && manager.manage( clazz ) )%0A        %7B%0A            encounter.register( this );%0A        %7D%0A
         for ( fi
@@ -3069,69 +3069,30 @@
     
-final boolean isRoot = pending.isEmpty();%0A        if ( isRoot
+if ( pending.isEmpty()
  )%0A 
@@ -3174,15 +3174,17 @@
 ore 
-schedul
+provision
 ing 
@@ -3199,496 +3199,93 @@
     
-   %7D%0A%0A
        
- final Object bean = pi.provision(); // this may involve more onProvision calls%0A%0A        if ( null != bean )%0A        %7B%0A            // only activate on unique implementation binding (avoids duplicates)%0A            final Class%3C?%3E boundType = pi.getKey().getTypeLiteral().getRawType();%0A            if ( boundType == bean.getClass() && manager.manage( bean.getClass() ) )%0A            %7B%0A                pending.add( bean );%0A            %7D%0A        %7D%0A%0A        if ( isRoot )%0A        %7B
+pi.provision(); // because this step may involve further calls to onProvision%0A
 %0A   
@@ -3626,16 +3626,16 @@
  // 
-this may
+may also
  inv
@@ -3691,11 +3691,111 @@
  %7D%0A    %7D
+%0A%0A    public void afterInjection( final Object bean )%0A    %7B%0A        PENDING.get().add( bean );%0A    %7D
 %0A%7D%0A

Use mix of InjectionListener and ProvisionListener to simplify book-keeping