/*******************************************************************************
 * Copyright (c) 2010, 2013 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
 *
 * Contributors:
 *    Stuart McCulloch (Sonatype, Inc.) - initial API and implementation
 *******************************************************************************/
package org.eclipse.sisu.space;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Qualifier;

import org.eclipse.sisu.BeanScanning;

import com.google.inject.Binder;
import com.google.inject.Module;
import com.google.inject.spi.Element;
import com.google.inject.spi.Elements;
import com.google.inject.spi.MembersInjectorLookup;
import com.google.inject.spi.ProviderLookup;

/**
 * Guice {@link Module} that automatically binds types annotated with {@link Qualifier} annotations.
 */
public class SpaceModule
    implements Module
{
    // ----------------------------------------------------------------------
    // Implementation fields
    // ----------------------------------------------------------------------

    private static Map<String, List<Element>> cachedElementsMap;

    final ClassSpace space;

    private final BeanScanning scanning;

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

    public SpaceModule( final ClassSpace space )
    {
        this( space, BeanScanning.ON );
    }

    public SpaceModule( final ClassSpace space, final BeanScanning scanning )
    {
        this.space = space;
        this.scanning = scanning;
    }

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

    public final void configure( final Binder binder )
    {
        binder.bind( ClassSpace.class ).toInstance( space );

        final ClassSpaceScanner scanner;
        switch ( scanning )
        {
            default:
            case ON:
                scanner = new ClassSpaceScanner( space );
                break;
            case INDEX:
                scanner = new ClassSpaceScanner( new SisuIndexClassFinder( false ), space );
                break;
            case GLOBAL_INDEX:
                scanner = new ClassSpaceScanner( new SisuIndexClassFinder( true ), space );
                break;
            case CACHE:
                replayCachedElements( binder );
                return;
            case OFF:
                return;
        }

        scanner.accept( visitor( binder ) );
    }

    // ----------------------------------------------------------------------
    // Customizable methods
    // ----------------------------------------------------------------------

    protected ClassSpaceVisitor visitor( final Binder binder )
    {
        return new QualifiedTypeVisitor( new QualifiedTypeBinder( binder ) );
    }

    // ----------------------------------------------------------------------
    // Implementation methods
    // ----------------------------------------------------------------------

    private final synchronized void replayCachedElements( final Binder binder )
    {
        if ( null == cachedElementsMap )
        {
            cachedElementsMap = new HashMap<String, List<Element>>();
        }
        /*
         * Scan and cache elements
         */
        final String key = space.toString();
        List<Element> elements = cachedElementsMap.get( key );
        boolean replaying = true;
        if ( null == elements )
        {
            replaying = false;
            elements = Elements.getElements( new Module()
            {
                public void configure( final Binder recorder )
                {
                    new ClassSpaceScanner( space ).accept( visitor( recorder ) );
                }
            } );
            cachedElementsMap.put( key, elements );
        }
        /*
         * Replay cached elements
         */
        for ( final Element e : elements )
        {
            if ( replaying )
            {
                // lookups have state so we replace them with duplicates when replaying...
                if ( e instanceof ProviderLookup<?> )
                {
                    binder.getProvider( ( (ProviderLookup<?>) e ).getKey() );
                    continue;
                }
                if ( e instanceof MembersInjectorLookup<?> )
                {
                    binder.getMembersInjector( ( (MembersInjectorLookup<?>) e ).getType() );
                    continue;
                }
            }
            e.applyTo( binder );
        }
    }
}

@@ -1373,72 +1373,97 @@
     
-final ClassSpace space;%0A%0A    private final BeanScanning scanning
+private final boolean caching;%0A%0A    final ClassSpace space;%0A%0A    final ClassFinder finder
 ;%0A%0A 
@@ -1798,448 +1798,287 @@
 nal 
-BeanScanning scanning )%0A    %7B%0A        this.space = space;%0A        this.scanning = scanning;%0A    %7D%0A%0A    // ----------------------------------------------------------------------%0A    // Public methods%0A    // ----------------------------------------------------------------------%0A%0A    public final void configure( final Binder binder )%0A    %7B%0A        binder.bind( ClassSpace.class ).toInstance( space );%0A%0A        final ClassSpaceScanner scanner
+ClassFinder finder )%0A    %7B%0A        caching = false;%0A%0A        this.space = space;%0A        this.finder = finder;%0A    %7D%0A%0A    public SpaceModule( final ClassSpace space, final BeanScanning scanning )%0A    %7B%0A        caching = BeanScanning.CACHE == scanning;%0A%0A        this.space = space
 ;%0A  
@@ -2117,29 +2117,8 @@
   %7B%0A
-            default:%0A
     
@@ -2131,17 +2131,18 @@
   case O
-N
+FF
 :%0A      
@@ -2155,48 +2155,21 @@
     
-scann
+find
 er = n
-ew ClassSpaceScanner( space )
+ull
 ;%0A  
@@ -2233,40 +2233,16 @@
     
-scann
+find
 er =
- new ClassSpaceScanner(
  new
@@ -2270,25 +2270,16 @@
 ( false 
-), space 
 );%0A     
@@ -2347,40 +2347,16 @@
     
-scann
+find
 er =
- new ClassSpaceScanner(
  new
@@ -2387,17 +2387,8 @@
 rue 
-), space 
 );%0A 
@@ -2425,18 +2425,15 @@
     
-case CACHE
+default
 :%0A  
@@ -2450,37 +2450,40 @@
     
-replayCachedElements( b
+finder = new DefaultClassF
 inder
- 
+(
 );%0A 
@@ -2501,28 +2501,280 @@
     
+b
 re
-turn;
+ak;%0A        %7D%0A    %7D%0A%0A    // ----------------------------------------------------------------------%0A    // Public methods%0A    // ----------------------------------------------------------------------%0A%0A    public final void configure( final Binder binder )
 %0A    
+%7B%0A
         
 case
@@ -2773,68 +2773,244 @@
     
-case OFF:%0A                return;%0A        %7D%0A%0A        scanner
+binder.bind( ClassSpace.class ).toInstance( space );%0A%0A        if ( caching )%0A        %7B%0A            replayCachedElements( binder );%0A        %7D%0A        else if ( null != finder )%0A        %7B%0A            new ClassSpaceScanner( finder, space )
 .acc
@@ -3027,32 +3027,42 @@
 or( binder ) );%0A
+        %7D%0A
     %7D%0A%0A    // --
@@ -4263,16 +4263,24 @@
 Scanner(
+ finder,
  space )

Refactor SpaceModule to support custom ClassFinder