remove phases from BindingProcessor, refactor so that two different classes do the two different bits, with an abstract superclass managing the shared pieces.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@1527 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/core/src/com/google/inject/internal/AbstractBindingProcessor.java b/core/src/com/google/inject/internal/AbstractBindingProcessor.java
new file mode 100644
index 0000000..fdbc752
--- /dev/null
+++ b/core/src/com/google/inject/internal/AbstractBindingProcessor.java
@@ -0,0 +1,167 @@
+/**

+ * Copyright (C) 2011 Google Inc.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ * http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package com.google.inject.internal;

+

+import java.util.Set;

+

+import com.google.inject.AbstractModule;

+import com.google.inject.Binder;

+import com.google.inject.Binding;

+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.Scope;

+import com.google.inject.TypeLiteral;

+import com.google.inject.internal.util.ImmutableSet;

+import com.google.inject.spi.DefaultBindingTargetVisitor;

+

+/**

+ * Guarantees that processing of Binding elements happens in a sane way.

+ * 

+ * @author sameb@google.com (Sam Berlin)

+ */

+abstract class AbstractBindingProcessor extends AbstractProcessor {

+

+  // It's unfortunate that we have to maintain a blacklist of specific

+  // classes, but we can't easily block the whole package because of

+  // all our unit tests.

+  private static final Set<Class<?>> FORBIDDEN_TYPES = ImmutableSet.<Class<?>>of(

+      AbstractModule.class,

+      Binder.class,

+      Binding.class,

+      Injector.class,

+      Key.class,

+      MembersInjector.class,

+      Module.class,

+      Provider.class,

+      Scope.class,

+      TypeLiteral.class);

+  // TODO(jessewilson): fix BuiltInModule, then add Stage

+  

+  protected final ProcessedBindingData bindingData;

+  

+  AbstractBindingProcessor(Errors errors, ProcessedBindingData bindingData) {

+    super(errors);

+    this.bindingData = bindingData;

+  }

+

+  protected <T> UntargettedBindingImpl<T> invalidBinding(

+      InjectorImpl injector, Key<T> key, Object source) {

+    return new UntargettedBindingImpl<T>(injector, key, source);

+  }

+  

+  protected void putBinding(BindingImpl<?> binding) {

+    Key<?> key = binding.getKey();

+

+    Class<?> rawType = key.getTypeLiteral().getRawType();

+    if (FORBIDDEN_TYPES.contains(rawType)) {

+      errors.cannotBindToGuiceType(rawType.getSimpleName());

+      return;

+    }

+

+    BindingImpl<?> original = injector.getExistingBinding(key);

+    if (original != null) {

+      // If it failed because of an explicit duplicate binding...

+      if (injector.state.getExplicitBinding(key) != null) {

+        try {

+          if(!isOkayDuplicate(original, binding, injector.state)) {

+            errors.bindingAlreadySet(key, original.getSource());

+            return;

+          }

+        } catch(Throwable t) {

+          errors.errorCheckingDuplicateBinding(key, original.getSource(), t);

+          return;

+        }

+      } else {

+        // Otherwise, it failed because of a duplicate JIT binding

+        // in the parent

+        errors.jitBindingAlreadySet(key);

+        return;

+      }

+    }

+

+    // prevent the parent from creating a JIT binding for this key

+    injector.state.parent().blacklist(key, binding.getSource());

+    injector.state.putBinding(key, binding);

+  }

+

+  /**

+   * We tolerate duplicate bindings if one exposes the other or if the two bindings

+   * are considered duplicates (see {@link Bindings#areDuplicates(BindingImpl, BindingImpl)}.

+   *

+   * @param original the binding in the parent injector (candidate for an exposing binding)

+   * @param binding the binding to check (candidate for the exposed binding)

+   */

+  private boolean isOkayDuplicate(BindingImpl<?> original, BindingImpl<?> binding, State state) {

+    if (original instanceof ExposedBindingImpl) {

+      ExposedBindingImpl exposed = (ExposedBindingImpl) original;

+      InjectorImpl exposedFrom = (InjectorImpl) exposed.getPrivateElements().getInjector();

+      return (exposedFrom == binding.getInjector());

+    } else {

+      original = (BindingImpl<?>)state.getExplicitBindingsThisLevel().get(binding.getKey());

+      // If no original at this level, the original was on a parent, and we don't

+      // allow deduplication between parents & children.

+      if(original == null) {

+        return false;

+      } else {

+        return original.equals(binding);

+      }

+    }

+  }

+  

+  private <T> void validateKey(Object source, Key<T> key) {

+    Annotations.checkForMisplacedScopeAnnotations(

+        key.getTypeLiteral().getRawType(), source, errors);

+  }

+  

+  /** 

+   * Processor for visiting bindings.  Each overriden method that wants to

+   * actually process the binding should call prepareBinding first.

+   */

+  abstract class Processor<T, V> extends DefaultBindingTargetVisitor<T, V> {

+    final Object source;

+    final Key<T> key;

+    final Class<? super T> rawType;

+    Scoping scoping;

+    

+    Processor(BindingImpl<T> binding) {

+      source = binding.getSource();

+      key = binding.getKey();

+      rawType = key.getTypeLiteral().getRawType();

+      scoping = binding.getScoping();

+    }

+    

+    protected void prepareBinding() {      

+      validateKey(source, key);

+      scoping = Scoping.makeInjectable(scoping, injector, errors);

+    }

+

+    protected void scheduleInitialization(final BindingImpl<?> binding) {

+      bindingData.addUninitializedBinding(new Runnable() {

+        public void run() {

+          try {

+            binding.getInjector().initializeBinding(binding, errors.withSource(source));

+          } catch (ErrorsException e) {

+            errors.merge(e.getErrors());

+          }

+        }

+      });

+    }

+  }

+}

diff --git a/core/src/com/google/inject/internal/BindingProcessor.java b/core/src/com/google/inject/internal/BindingProcessor.java
index 28d04a6..a26fed3 100644
--- a/core/src/com/google/inject/internal/BindingProcessor.java
+++ b/core/src/com/google/inject/internal/BindingProcessor.java
@@ -16,19 +16,10 @@
 
 package com.google.inject.internal;
 
-import com.google.inject.AbstractModule;
 import com.google.inject.Binder;
 import com.google.inject.Binding;
-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.Scope;
-import com.google.inject.TypeLiteral;
-import com.google.inject.internal.util.ImmutableSet;
-import com.google.inject.internal.util.Lists;
-import com.google.inject.spi.BindingTargetVisitor;
 import com.google.inject.spi.ConstructorBinding;
 import com.google.inject.spi.ConvertedConstantBinding;
 import com.google.inject.spi.ExposedBinding;
@@ -40,7 +31,6 @@
 import com.google.inject.spi.ProviderInstanceBinding;
 import com.google.inject.spi.ProviderKeyBinding;
 import com.google.inject.spi.UntargettedBinding;
-import java.util.List;
 import java.util.Set;
 
 /**
@@ -49,28 +39,18 @@
  * @author crazybob@google.com (Bob Lee)
  * @author jessewilson@google.com (Jesse Wilson)
  */
-final class BindingProcessor extends AbstractProcessor {
+final class BindingProcessor extends AbstractBindingProcessor {
 
-  private final List<CreationListener> creationListeners = Lists.newArrayList();
   private final Initializer initializer;
-  private final List<Runnable> uninitializedBindings = Lists.newArrayList();
-  
-  enum Phase { PASS_ONE, PASS_TWO; }
-  private Phase phase;
 
-  BindingProcessor(Errors errors, Initializer initializer) {
-    super(errors);
+  BindingProcessor(Errors errors, Initializer initializer, ProcessedBindingData bindingData) {
+    super(errors, bindingData);
     this.initializer = initializer;
   }
-  
-  void setPhase(Phase phase) {
-    this.phase = phase;
-  }
 
   @Override public <T> Boolean visit(Binding<T> command) {
-    final Object source = command.getSource();
-
-    if (Void.class.equals(command.getKey().getTypeLiteral().getRawType())) {
+    Class<?> rawType = command.getKey().getTypeLiteral().getRawType();
+    if (Void.class.equals(rawType)) {
       if (command instanceof ProviderInstanceBinding
           && ((ProviderInstanceBinding) command).getProviderInstance() instanceof ProviderMethod) {
         errors.voidProviderMethod();
@@ -79,22 +59,15 @@
       }
       return true;
     }
-
-    final Key<T> key = command.getKey();
-    Class<? super T> rawType = key.getTypeLiteral().getRawType();
-
+    
     if (rawType == Provider.class) {
       errors.bindingToProvider();
       return true;
     }
-
-    validateKey(command.getSource(), command.getKey());
-
-    final Scoping scoping = Scoping.makeInjectable(
-        ((BindingImpl<?>) command).getScoping(), injector, errors);
-
-    return command.acceptTargetVisitor(new BindingTargetVisitor<T, Boolean>() {
+    
+    return command.acceptTargetVisitor(new Processor<T, Boolean>((BindingImpl<T>)command) {
       public Boolean visit(ConstructorBinding<? extends T> binding) {
+        prepareBinding();
         try {
           ConstructorBindingImpl<T> onInjector = ConstructorBindingImpl.create(injector, key, 
               binding.getConstructor(), source, scoping, errors, false);
@@ -108,6 +81,7 @@
       }
 
       public Boolean visit(InstanceBinding<? extends T> binding) {
+        prepareBinding();
         Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
         T instance = binding.getInstance();
         Initializable<T> ref = initializer.requestInjection(
@@ -121,6 +95,7 @@
       }
 
       public Boolean visit(ProviderInstanceBinding<? extends T> binding) {
+        prepareBinding();
         Provider<? extends T> provider = binding.getProviderInstance();
         Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
         Initializable<Provider<? extends T>> initializable = initializer
@@ -134,10 +109,11 @@
       }
 
       public Boolean visit(ProviderKeyBinding<? extends T> binding) {
+        prepareBinding();
         Key<? extends javax.inject.Provider<? extends T>> providerKey = binding.getProviderKey();
         BoundProviderFactory<T> boundProviderFactory
             = new BoundProviderFactory<T>(injector, providerKey, source);
-        creationListeners.add(boundProviderFactory);
+        bindingData.addCreationListener(boundProviderFactory);
         InternalFactory<? extends T> scopedFactory = Scoping.scope(
             key, injector, (InternalFactory<? extends T>) boundProviderFactory, source, scoping);
         putBinding(new LinkedProviderBindingImpl<T>(
@@ -146,13 +122,14 @@
       }
 
       public Boolean visit(LinkedKeyBinding<? extends T> binding) {
+        prepareBinding();
         Key<? extends T> linkedKey = binding.getLinkedKey();
         if (key.equals(linkedKey)) {
           errors.recursiveBinding();
         }
 
         FactoryProxy<T> factory = new FactoryProxy<T>(injector, key, linkedKey, source);
-        creationListeners.add(factory);
+        bindingData.addCreationListener(factory);
         InternalFactory<? extends T> scopedFactory
             = Scoping.scope(key, injector, factory, source, scoping);
         putBinding(
@@ -160,34 +137,8 @@
         return true;
       }
 
-      public Boolean visit(UntargettedBinding<? extends T> untargetted) {        
-        // Error: Missing implementation.
-        // Example: bind(Date.class).annotatedWith(Red.class);
-        // We can't assume abstract types aren't injectable. They may have an
-        // @ImplementedBy annotation or something.
-        if (key.getAnnotationType() != null) {
-          errors.missingImplementation(key);
-          putBinding(invalidBinding(injector, key, source));
-          return true;
-        }
-
-        // We want to do UntargettedBindings in the second pass.
-        if (phase == Phase.PASS_ONE) {
-          return false;
-        }
-
-        // This cast is safe after the preceeding check.
-        try {
-          BindingImpl<T> binding = injector.createUninitializedBinding(
-              key, scoping, source, errors, false);
-          scheduleInitialization(binding);
-          putBinding(binding);
-        } catch (ErrorsException e) {
-          errors.merge(e.getErrors());
-          putBinding(invalidBinding(injector, key, source));
-        }
-
-        return true;
+      public Boolean visit(UntargettedBinding<? extends T> untargetted) {
+        return false;
       }
 
       public Boolean visit(ExposedBinding<? extends T> binding) {
@@ -201,28 +152,15 @@
       public Boolean visit(ProviderBinding<? extends T> binding) {
         throw new IllegalArgumentException("Cannot apply a non-module element");
       }
-
-      private void scheduleInitialization(final BindingImpl<?> binding) {
-        uninitializedBindings.add(new Runnable() {
-          public void run() {
-            try {
-              binding.getInjector().initializeBinding(binding, errors.withSource(source));
-            } catch (ErrorsException e) {
-              errors.merge(e.getErrors());
-            }
-          }
-        });
+      
+      @Override
+      protected Boolean visitOther(Binding<? extends T> binding) {
+        throw new IllegalStateException("BindingProcessor should override all visitations");
       }
     });
   }
 
   @Override public Boolean visit(PrivateElements privateElements) {
-    // Because we do two passes, we have to ignore the PrivateElements in the second
-    // pass.  Otherwise we end up calling bindExposed twice for each one.
-    if (phase == Phase.PASS_TWO) {
-      return false;
-    }
-    
     for (Key<?> key : privateElements.getExposedKeys()) {
       bindExposed(privateElements, key);
     }
@@ -231,108 +169,8 @@
 
   private <T> void bindExposed(PrivateElements privateElements, Key<T> key) {
     ExposedKeyFactory<T> exposedKeyFactory = new ExposedKeyFactory<T>(key, privateElements);
-    creationListeners.add(exposedKeyFactory);
+    bindingData.addCreationListener(exposedKeyFactory);
     putBinding(new ExposedBindingImpl<T>(
         injector, privateElements.getExposedSource(key), key, exposedKeyFactory, privateElements));
   }
-
-  private <T> void validateKey(Object source, Key<T> key) {
-    Annotations.checkForMisplacedScopeAnnotations(
-        key.getTypeLiteral().getRawType(), source, errors);
-  }
-
-  <T> UntargettedBindingImpl<T> invalidBinding(InjectorImpl injector, Key<T> key, Object source) {
-    return new UntargettedBindingImpl<T>(injector, key, source);
-  }
-
-  public void initializeBindings() {
-    for (Runnable initializer : uninitializedBindings) {
-      initializer.run();
-    }
-  }
-
-  public void runCreationListeners() {
-    for (CreationListener creationListener : creationListeners) {
-      creationListener.notify(errors);
-    }
-  }
-
-  private void putBinding(BindingImpl<?> binding) {
-    Key<?> key = binding.getKey();
-
-    Class<?> rawType = key.getTypeLiteral().getRawType();
-    if (FORBIDDEN_TYPES.contains(rawType)) {
-      errors.cannotBindToGuiceType(rawType.getSimpleName());
-      return;
-    }
-
-    BindingImpl<?> original = injector.getExistingBinding(key);
-    if (original != null) {
-      // If it failed because of an explicit duplicate binding...
-      if (injector.state.getExplicitBinding(key) != null) {
-        try {
-          if(!isOkayDuplicate(original, binding, injector.state)) {
-            errors.bindingAlreadySet(key, original.getSource());
-            return;
-          }
-        } catch(Throwable t) {
-          errors.errorCheckingDuplicateBinding(key, original.getSource(), t);
-          return;
-        }
-      } else {
-        // Otherwise, it failed because of a duplicate JIT binding
-        // in the parent
-        errors.jitBindingAlreadySet(key);
-        return;
-      }
-    }
-
-    // prevent the parent from creating a JIT binding for this key
-    injector.state.parent().blacklist(key, binding.getSource());
-    injector.state.putBinding(key, binding);
-  }
-
-  /**
-   * We tolerate duplicate bindings if one exposes the other or if the two bindings
-   * are considered duplicates (see {@link Bindings#areDuplicates(BindingImpl, BindingImpl)}.
-   *
-   * @param original the binding in the parent injector (candidate for an exposing binding)
-   * @param binding the binding to check (candidate for the exposed binding)
-   */
-  private boolean isOkayDuplicate(BindingImpl<?> original, BindingImpl<?> binding, State state) {
-    if (original instanceof ExposedBindingImpl) {
-      ExposedBindingImpl exposed = (ExposedBindingImpl) original;
-      InjectorImpl exposedFrom = (InjectorImpl) exposed.getPrivateElements().getInjector();
-      return (exposedFrom == binding.getInjector());
-    } else {
-      original = (BindingImpl<?>)state.getExplicitBindingsThisLevel().get(binding.getKey());
-      // If no original at this level, the original was on a parent, and we don't
-      // allow deduplication between parents & children.
-      if(original == null) {
-        return false;
-      } else {
-        return original.equals(binding);
-      }
-    }
-  }
-
-  // It's unfortunate that we have to maintain a blacklist of specific
-  // classes, but we can't easily block the whole package because of
-  // all our unit tests.
-  private static final Set<Class<?>> FORBIDDEN_TYPES = ImmutableSet.<Class<?>>of(
-      AbstractModule.class,
-      Binder.class,
-      Binding.class,
-      Injector.class,
-      Key.class,
-      MembersInjector.class,
-      Module.class,
-      Provider.class,
-      Scope.class,
-      TypeLiteral.class);
-  // TODO(jessewilson): fix BuiltInModule, then add Stage
-
-  interface CreationListener {
-    void notify(Errors errors);
-  }
 }
diff --git a/core/src/com/google/inject/internal/BoundProviderFactory.java b/core/src/com/google/inject/internal/BoundProviderFactory.java
index 6599750..27f957e 100644
--- a/core/src/com/google/inject/internal/BoundProviderFactory.java
+++ b/core/src/com/google/inject/internal/BoundProviderFactory.java
@@ -17,7 +17,6 @@
 package com.google.inject.internal;
 
 import com.google.inject.Key;
-import com.google.inject.internal.BindingProcessor.CreationListener;
 import com.google.inject.internal.InjectorImpl.JitLimitation;
 import com.google.inject.spi.Dependency;
 
diff --git a/core/src/com/google/inject/internal/CreationListener.java b/core/src/com/google/inject/internal/CreationListener.java
new file mode 100644
index 0000000..533b14f
--- /dev/null
+++ b/core/src/com/google/inject/internal/CreationListener.java
@@ -0,0 +1,24 @@
+/**

+ * Copyright (C) 2011 Google Inc.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ * http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package com.google.inject.internal;

+

+/** Something that is notified upon creation. */

+interface CreationListener {

+

+  /** Notifies that creation should happen. */

+  void notify(Errors errors);

+}
\ No newline at end of file
diff --git a/core/src/com/google/inject/internal/ExposedKeyFactory.java b/core/src/com/google/inject/internal/ExposedKeyFactory.java
index 822282d..52bf7cb 100644
--- a/core/src/com/google/inject/internal/ExposedKeyFactory.java
+++ b/core/src/com/google/inject/internal/ExposedKeyFactory.java
@@ -24,7 +24,7 @@
  * This factory exists in a parent injector. When invoked, it retrieves its value from a child
  * injector.
  */
-final class ExposedKeyFactory<T> implements InternalFactory<T>, BindingProcessor.CreationListener {
+final class ExposedKeyFactory<T> implements InternalFactory<T>, CreationListener {
   private final Key<T> key;
   private final PrivateElements privateElements;
   private BindingImpl<T> delegate;
diff --git a/core/src/com/google/inject/internal/FactoryProxy.java b/core/src/com/google/inject/internal/FactoryProxy.java
index ca9a33c..c6d2f3b 100644
--- a/core/src/com/google/inject/internal/FactoryProxy.java
+++ b/core/src/com/google/inject/internal/FactoryProxy.java
@@ -25,7 +25,7 @@
  * A placeholder which enables us to swap in the real factory once the injector is created.
  * Used for a linked binding, so that getting the linked binding returns the link's factory.
  */
-final class FactoryProxy<T> implements InternalFactory<T>, BindingProcessor.CreationListener {
+final class FactoryProxy<T> implements InternalFactory<T>, CreationListener {
 
   private final InjectorImpl injector;
   private final Key<T> key;
diff --git a/core/src/com/google/inject/internal/InjectorShell.java b/core/src/com/google/inject/internal/InjectorShell.java
index 151a1a1..66ff334 100644
--- a/core/src/com/google/inject/internal/InjectorShell.java
+++ b/core/src/com/google/inject/internal/InjectorShell.java
@@ -24,7 +24,6 @@
 import static com.google.inject.Scopes.SINGLETON;
 import com.google.inject.Singleton;
 import com.google.inject.Stage;
-import com.google.inject.internal.BindingProcessor.Phase;
 import com.google.inject.internal.InjectorImpl.InjectorOptions;
 import com.google.inject.internal.util.ImmutableSet;
 import com.google.inject.internal.util.Lists;
@@ -51,10 +50,8 @@
 
   private final List<Element> elements;
   private final InjectorImpl injector;
-  private final PrivateElements privateElements;
 
   private InjectorShell(Builder builder, List<Element> elements, InjectorImpl injector) {
-    this.privateElements = builder.privateElements;
     this.elements = elements;
     this.injector = injector;
   }
@@ -120,8 +117,11 @@
      * returned if any modules contain {@link Binder#newPrivateBinder private environments}. The
      * primary injector will be first in the returned list.
      */
-    List<InjectorShell> build(BindingProcessor bindingProcessor,
-        Stopwatch stopwatch, Errors errors) {
+    List<InjectorShell> build(
+        Initializer initializer,
+        ProcessedBindingData bindingData,
+        Stopwatch stopwatch,
+        Errors errors) {
       checkState(stage != null, "Stage not initialized");
       checkState(privateElements == null || parent != null, "PrivateElements with no parent");
       checkState(state != null, "no state. Did you remember to lock() ?");
@@ -171,14 +171,11 @@
       bindInjector(injector);
       bindLogger(injector);
       
-      // Do two passes over our bindings -- first to get all non UntargettedBindings,
-      // then to get the only UntargettedBindings.  This is necessary because
-      // UntargettedBindings can create JIT bindings and need all their other
-      // dependencies set up ahead of time.
-      bindingProcessor.setPhase(Phase.PASS_ONE);
-      bindingProcessor.process(injector, elements);
-      bindingProcessor.setPhase(Phase.PASS_TWO);
-      bindingProcessor.process(injector, elements);
+      // Process all normal bindings, then UntargettedBindings.
+      // This is necessary because UntargettedBindings can create JIT bindings
+      // and need all their other dependencies set up ahead of time.
+      new BindingProcessor(errors, initializer, bindingData).process(injector, elements);
+      new UntargettedBindingProcessor(errors, bindingData).process(injector, elements);
       stopwatch.resetAndLog("Binding creation");
 
       List<InjectorShell> injectorShells = Lists.newArrayList();
@@ -188,7 +185,7 @@
       PrivateElementProcessor processor = new PrivateElementProcessor(errors);
       processor.process(injector, elements);
       for (Builder builder : processor.getInjectorShellBuilders()) {
-        injectorShells.addAll(builder.build(bindingProcessor, stopwatch, errors));
+        injectorShells.addAll(builder.build(initializer, bindingData, stopwatch, errors));
       }
       stopwatch.resetAndLog("Private environment creation");
 
diff --git a/core/src/com/google/inject/internal/InternalInjectorCreator.java b/core/src/com/google/inject/internal/InternalInjectorCreator.java
index 812062a..a2c79f9 100644
--- a/core/src/com/google/inject/internal/InternalInjectorCreator.java
+++ b/core/src/com/google/inject/internal/InternalInjectorCreator.java
@@ -61,7 +61,7 @@
   private final Errors errors = new Errors();
 
   private final Initializer initializer = new Initializer();
-  private final BindingProcessor bindingProcesor;
+  private final ProcessedBindingData bindingData;
   private final InjectionRequestProcessor injectionRequestProcessor;
 
   private final InjectorShell.Builder shellBuilder = new InjectorShell.Builder();
@@ -69,7 +69,7 @@
   
   public InternalInjectorCreator() {
     injectionRequestProcessor = new InjectionRequestProcessor(errors, initializer);
-    bindingProcesor = new BindingProcessor(errors, initializer);
+    bindingData = new ProcessedBindingData();
   }
   
   public InternalInjectorCreator stage(Stage stage) {
@@ -100,7 +100,7 @@
     // Synchronize while we're building up the bindings and other injector state. This ensures that
     // the JIT bindings in the parent injector don't change while we're being built
     synchronized (shellBuilder.lock()) {
-      shells = shellBuilder.build(bindingProcesor, stopwatch, errors);
+      shells = shellBuilder.build(initializer, bindingData, stopwatch, errors);
       stopwatch.resetAndLog("Injector construction");
 
       initializeStatically();
@@ -119,7 +119,7 @@
 
   /** Initialize and validate everything. */
   private void initializeStatically() {
-    bindingProcesor.initializeBindings();
+    bindingData.initializeBindings();
     stopwatch.resetAndLog("Binding initialization");
 
     for (InjectorShell shell : shells) {
@@ -130,7 +130,7 @@
     injectionRequestProcessor.process(shells);
     stopwatch.resetAndLog("Collecting injection requests");
 
-    bindingProcesor.runCreationListeners();
+    bindingData.runCreationListeners(errors);
     stopwatch.resetAndLog("Binding validation");
 
     injectionRequestProcessor.validate();
diff --git a/core/src/com/google/inject/internal/ProcessedBindingData.java b/core/src/com/google/inject/internal/ProcessedBindingData.java
new file mode 100644
index 0000000..cf311a7
--- /dev/null
+++ b/core/src/com/google/inject/internal/ProcessedBindingData.java
@@ -0,0 +1,54 @@
+/**

+ * Copyright (C) 2011 Google Inc.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ * http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package com.google.inject.internal;

+

+import java.util.List;

+

+import com.google.inject.internal.util.Lists;

+

+/**

+ * Keeps track of creation listeners & uninitialized bindings,

+ * so they can be processed after bindings are recorded.

+ * 

+ * @author sameb@google.com (Sam Berlin)

+ */

+class ProcessedBindingData {

+  

+  private final List<CreationListener> creationListeners = Lists.newArrayList();

+  private final List<Runnable> uninitializedBindings = Lists.newArrayList();

+  

+  void addCreationListener(CreationListener listener) {

+    creationListeners.add(listener);

+  }

+  

+  void addUninitializedBinding(Runnable runnable) {

+    uninitializedBindings.add(runnable);

+  }

+  

+  void initializeBindings() {

+    for (Runnable initializer : uninitializedBindings) {

+      initializer.run();

+    }

+  }

+

+  void runCreationListeners(Errors errors) {

+    for (CreationListener creationListener : creationListeners) {

+      creationListener.notify(errors);

+    }

+  }

+

+}

diff --git a/core/src/com/google/inject/internal/UntargettedBindingProcessor.java b/core/src/com/google/inject/internal/UntargettedBindingProcessor.java
new file mode 100644
index 0000000..d056127
--- /dev/null
+++ b/core/src/com/google/inject/internal/UntargettedBindingProcessor.java
@@ -0,0 +1,69 @@
+/**

+ * Copyright (C) 2011 Google Inc.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ * http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package com.google.inject.internal;

+

+import com.google.inject.Binding;

+import com.google.inject.spi.UntargettedBinding;

+

+/**

+ * Processes just UntargettedBindings.

+ *

+ * @author sameb@google.com (Sam Berlin)

+ */

+class UntargettedBindingProcessor extends AbstractBindingProcessor {

+  

+  UntargettedBindingProcessor(Errors errors, ProcessedBindingData bindingData) {

+    super(errors, bindingData);

+  }

+  

+  @Override

+  public <T> Boolean visit(Binding<T> binding) {

+    return binding.acceptTargetVisitor(new Processor<T, Boolean>((BindingImpl<T>)binding) {  

+      public Boolean visit(UntargettedBinding<? extends T> untargetted) {

+        prepareBinding();

+

+        // Error: Missing implementation.

+        // Example: bind(Date.class).annotatedWith(Red.class);

+        // We can't assume abstract types aren't injectable. They may have an

+        // @ImplementedBy annotation or something.

+        if (key.getAnnotationType() != null) {

+          errors.missingImplementation(key);

+          putBinding(invalidBinding(injector, key, source));

+          return true;

+        }

+    

+        // This cast is safe after the preceeding check.

+        try {

+          BindingImpl<T> binding = injector.createUninitializedBinding(

+              key, scoping, source, errors, false);

+          scheduleInitialization(binding);

+          putBinding(binding);

+        } catch (ErrorsException e) {

+          errors.merge(e.getErrors());

+          putBinding(invalidBinding(injector, key, source));

+        }

+    

+        return true;

+      }

+      

+      @Override

+      protected Boolean visitOther(Binding<? extends T> binding) {

+        return false;

+      }

+    });

+  }

+}