Bug 394734: support custom extender strategies
diff --git a/org.eclipse.sisu.inject/src/org/eclipse/sisu/launch/BundleScanner.java b/org.eclipse.sisu.inject/src/org/eclipse/sisu/launch/BundleScanner.java
index f6d6a45..091792d 100644
--- a/org.eclipse.sisu.inject/src/org/eclipse/sisu/launch/BundleScanner.java
+++ b/org.eclipse.sisu.inject/src/org/eclipse/sisu/launch/BundleScanner.java
@@ -12,8 +12,8 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Dictionary;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -22,6 +22,7 @@
 import org.eclipse.sisu.inject.InjectorPublisher;
 import org.eclipse.sisu.inject.MutableBeanLocator;
 import org.eclipse.sisu.inject.Weak;
+import org.eclipse.sisu.space.BundleClassSpace;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleEvent;
@@ -64,6 +65,11 @@
     private static final Map<Long, Object> bundleInjectors = Collections.synchronizedMap( Weak.<Long, Object> values() );
 
     /**
+     * Custom strategies; contributed by attaching fragments to extender.
+     */
+    protected final List<Strategy> strategies;
+
+    /**
      * Mask of bundle states being tracked.
      */
     protected final int stateMask;
@@ -91,6 +97,7 @@
     public BundleScanner( final BundleContext context, final int stateMask, final MutableBeanLocator locator )
     {
         super( context, stateMask, null );
+        strategies = SisuExtensions.local( new BundleClassSpace( context.getBundle() ) ).create( Strategy.class );
         this.stateMask = stateMask;
         this.locator = locator;
     }
@@ -110,9 +117,10 @@
     @Override
     public final Object addingBundle( final Bundle bundle, final BundleEvent event )
     {
-        if ( injectBundle( bundle ) )
+        final Strategy strategy = findStrategy( bundle );
+        if ( null != strategy )
         {
-            return addBundleInjector( bundle );
+            return addBundleInjector( bundle, strategy );
         }
         return null; // don't bother tracking this bundle
     }
@@ -142,46 +150,90 @@
     }
 
     // ----------------------------------------------------------------------
+    // Public types
+    // ----------------------------------------------------------------------
+
+    public interface Strategy
+    {
+        /**
+         * Returns {@code true} if strategy applies to the bundle; otherwise {@code false}.
+         */
+        boolean matches( Bundle bundle );
+
+        /**
+         * Scans the given bundle and returns a {@link Module} of the resulting bindings.
+         * 
+         * @param bundle The bundle
+         * @return Scanned bindings
+         */
+        Module scan( Bundle bundle );
+
+        /**
+         * Default scanning strategy; scan any bundles that contain JSR330 annotated components.
+         */
+        Strategy DEFAULT = new Strategy()
+        {
+            public boolean matches( final Bundle bundle )
+            {
+                final String imports = (String) bundle.getHeaders().get( Constants.IMPORT_PACKAGE );
+                if ( null != imports )
+                {
+                    return imports.contains( "javax.inject" ) || imports.contains( "com.google.inject" );
+                }
+                return false; // doesn't import any interesting packages
+            }
+
+            public Module scan( final Bundle bundle )
+            {
+                return new BundleModule( bundle );
+            }
+        };
+    }
+
+    // ----------------------------------------------------------------------
     // Customizable methods
     // ----------------------------------------------------------------------
 
     /**
-     * Determines whether we should create an {@link Injector} for the given bundle.
+     * Finds the appropriate scanning strategy for the given bundle.
      * 
      * @param bundle The bundle
-     * @return {@code true} if an injector should be created; otherwise {@code false}
+     * @return The chosen strategy; {@code null} if it shouldn't be scanned
      */
-    protected boolean injectBundle( final Bundle bundle )
+    protected Strategy findStrategy( final Bundle bundle )
     {
         final String symbolicName = bundle.getSymbolicName();
         if ( SUPPORT_BUNDLE_NAMES.contains( symbolicName ) )
         {
-            return false; // ignore our main support bundles
+            return null; // ignore our main support bundles
         }
-        final Dictionary<?, ?> headers = bundle.getHeaders();
-        final String host = (String) headers.get( Constants.FRAGMENT_HOST );
-        if ( null != host )
+        if ( null != bundle.getHeaders().get( Constants.FRAGMENT_HOST ) )
         {
-            return false; // fragment, we'll scan it when we process the host
+            return null; // fragment, we'll scan it when we process the host
         }
-        final String imports = (String) headers.get( Constants.IMPORT_PACKAGE );
-        if ( null == imports )
+        // check for any attached strategies; latest first
+        for ( int i = strategies.size() - 1; i >= 0; i-- )
         {
-            return false; // doesn't import any interesting packages
+            final Strategy strategy = strategies.get( i );
+            if ( strategy.matches( bundle ) )
+            {
+                return strategy; // apply custom strategy
+            }
         }
-        return imports.contains( "javax.inject" ) || imports.contains( "com.google.inject" );
+        return Strategy.DEFAULT.matches( bundle ) ? Strategy.DEFAULT : null;
     }
 
     /**
      * Creates a new {@link Injector} for the given bundle.
      * 
      * @param bundle The bundle
+     * @param strategy The strategy
      * @return New injector
      */
-    protected Injector createInjector( final Bundle bundle )
+    protected Injector createInjector( final Bundle bundle, final Strategy strategy )
     {
         // the locatorModule will auto-register the injector as a publisher
-        return Guice.createInjector( locatorModule, new BundleModule( bundle ) );
+        return Guice.createInjector( locatorModule, strategy.scan( bundle ) );
     }
 
     /**
@@ -210,7 +262,7 @@
     // Implementation methods
     // ----------------------------------------------------------------------
 
-    private Object addBundleInjector( final Bundle bundle )
+    private Object addBundleInjector( final Bundle bundle, final Strategy strategy )
     {
         @SuppressWarnings( "boxing" )
         final Long bundleId = bundle.getBundleId();
@@ -218,7 +270,7 @@
         {
             // protect against nested activation calls
             bundleInjectors.put( bundleId, PLACEHOLDER );
-            final Injector injector = createInjector( bundle );
+            final Injector injector = createInjector( bundle, strategy );
             bundleInjectors.put( bundleId, injector );
         }
         return bundle;