fix issue 614 -- admittedly not the prettiest solution, but it works.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@1526 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/core/src/com/google/inject/internal/BindingProcessor.java b/core/src/com/google/inject/internal/BindingProcessor.java
index 6de190f..28d04a6 100644
--- a/core/src/com/google/inject/internal/BindingProcessor.java
+++ b/core/src/com/google/inject/internal/BindingProcessor.java
@@ -54,11 +54,18 @@
   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);
     this.initializer = initializer;
   }
+  
+  void setPhase(Phase phase) {
+    this.phase = phase;
+  }
 
   @Override public <T> Boolean visit(Binding<T> command) {
     final Object source = command.getSource();
@@ -86,8 +93,8 @@
     final Scoping scoping = Scoping.makeInjectable(
         ((BindingImpl<?>) command).getScoping(), injector, errors);
 
-    command.acceptTargetVisitor(new BindingTargetVisitor<T, Void>() {
-      public Void visit(ConstructorBinding<? extends T> binding) {
+    return command.acceptTargetVisitor(new BindingTargetVisitor<T, Boolean>() {
+      public Boolean visit(ConstructorBinding<? extends T> binding) {
         try {
           ConstructorBindingImpl<T> onInjector = ConstructorBindingImpl.create(injector, key, 
               binding.getConstructor(), source, scoping, errors, false);
@@ -97,10 +104,10 @@
           errors.merge(e.getErrors());
           putBinding(invalidBinding(injector, key, source));
         }
-        return null;
+        return true;
       }
 
-      public Void visit(InstanceBinding<? extends T> binding) {
+      public Boolean visit(InstanceBinding<? extends T> binding) {
         Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
         T instance = binding.getInstance();
         Initializable<T> ref = initializer.requestInjection(
@@ -110,10 +117,10 @@
             = Scoping.scope(key, injector, factory, source, scoping);
         putBinding(new InstanceBindingImpl<T>(injector, key, source, scopedFactory, injectionPoints,
             instance));
-        return null;
+        return true;
       }
 
-      public Void visit(ProviderInstanceBinding<? extends T> binding) {
+      public Boolean visit(ProviderInstanceBinding<? extends T> binding) {
         Provider<? extends T> provider = binding.getProviderInstance();
         Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
         Initializable<Provider<? extends T>> initializable = initializer
@@ -123,10 +130,10 @@
             = Scoping.scope(key, injector, factory, source, scoping);
         putBinding(new ProviderInstanceBindingImpl<T>(injector, key, source, scopedFactory, scoping,
             provider, injectionPoints));
-        return null;
+        return true;
       }
 
-      public Void visit(ProviderKeyBinding<? extends T> binding) {
+      public Boolean visit(ProviderKeyBinding<? extends T> binding) {
         Key<? extends javax.inject.Provider<? extends T>> providerKey = binding.getProviderKey();
         BoundProviderFactory<T> boundProviderFactory
             = new BoundProviderFactory<T>(injector, providerKey, source);
@@ -135,10 +142,10 @@
             key, injector, (InternalFactory<? extends T>) boundProviderFactory, source, scoping);
         putBinding(new LinkedProviderBindingImpl<T>(
             injector, key, source, scopedFactory, scoping, providerKey));
-        return null;
+        return true;
       }
 
-      public Void visit(LinkedKeyBinding<? extends T> binding) {
+      public Boolean visit(LinkedKeyBinding<? extends T> binding) {
         Key<? extends T> linkedKey = binding.getLinkedKey();
         if (key.equals(linkedKey)) {
           errors.recursiveBinding();
@@ -150,10 +157,10 @@
             = Scoping.scope(key, injector, factory, source, scoping);
         putBinding(
             new LinkedBindingImpl<T>(injector, key, source, scopedFactory, scoping, linkedKey));
-        return null;
+        return true;
       }
 
-      public Void visit(UntargettedBinding<? extends T> untargetted) {
+      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
@@ -161,7 +168,12 @@
         if (key.getAnnotationType() != null) {
           errors.missingImplementation(key);
           putBinding(invalidBinding(injector, key, source));
-          return null;
+          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.
@@ -175,18 +187,18 @@
           putBinding(invalidBinding(injector, key, source));
         }
 
-        return null;
+        return true;
       }
 
-      public Void visit(ExposedBinding<? extends T> binding) {
+      public Boolean visit(ExposedBinding<? extends T> binding) {
         throw new IllegalArgumentException("Cannot apply a non-module element");
       }
 
-      public Void visit(ConvertedConstantBinding<? extends T> binding) {
+      public Boolean visit(ConvertedConstantBinding<? extends T> binding) {
         throw new IllegalArgumentException("Cannot apply a non-module element");
       }
 
-      public Void visit(ProviderBinding<? extends T> binding) {
+      public Boolean visit(ProviderBinding<? extends T> binding) {
         throw new IllegalArgumentException("Cannot apply a non-module element");
       }
 
@@ -202,11 +214,15 @@
         });
       }
     });
-
-    return true;
   }
 
   @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);
     }
diff --git a/core/src/com/google/inject/internal/InjectorShell.java b/core/src/com/google/inject/internal/InjectorShell.java
index 9086b9a..151a1a1 100644
--- a/core/src/com/google/inject/internal/InjectorShell.java
+++ b/core/src/com/google/inject/internal/InjectorShell.java
@@ -24,6 +24,7 @@
 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;
@@ -169,6 +170,14 @@
 
       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);
       stopwatch.resetAndLog("Binding creation");
 
diff --git a/core/test/com/google/inject/BinderTest.java b/core/test/com/google/inject/BinderTest.java
index 6f97a03..435ab1b 100644
--- a/core/test/com/google/inject/BinderTest.java
+++ b/core/test/com/google/inject/BinderTest.java
@@ -268,8 +268,83 @@
     assertSame(strings, injector.getInstance(new Key<String[]>() {}));
     assertSame(strings, injector.getInstance(String[].class));
   }
+  
+  /**
+   * Binding something to two different things should give an error.
+   */
+  public void testSettingBindingTwice() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          bind(String.class).toInstance("foo");
+          bind(String.class).toInstance("bar");
+        }
+      });
+      fail();
+    } catch(CreationException expected) {
+      assertContains(expected.getMessage(),
+        "1) A binding to java.lang.String was already configured at " + getClass().getName(),
+        "at " + getClass().getName(), ".configure(BinderTest.java:");
+      assertContains(expected.getMessage(), "1 error");
+    }
+  }
+  
+  /**
+   * Binding an @ImplementedBy thing to something else should also fail.
+   */
+  public void testSettingAtImplementedByTwice() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          bind(HasImplementedBy1.class);
+          bind(HasImplementedBy1.class).toInstance(new HasImplementedBy1() {});
+        }
+      });
+      fail();
+    } catch(CreationException expected) {
+      expected.printStackTrace();
+      assertContains(expected.getMessage(),
+        "1) A binding to " + HasImplementedBy1.class.getName()
+        + " was already configured at " + getClass().getName(),
+        "at " + getClass().getName(), ".configure(BinderTest.java:");
+      assertContains(expected.getMessage(), "1 error");
+    }
+  }
 
   /**
+   * See issue 614, Problem One
+   * http://code.google.com/p/google-guice/issues/detail?id=614
+   */
+  public void testJitDependencyDoesntBlockOtherExplicitBindings() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(HasImplementedByThatNeedsAnotherImplementedBy.class);
+        bind(HasImplementedBy1.class).toInstance(new HasImplementedBy1() {});
+      }
+    });
+    injector.getAllBindings(); // just validate it doesn't throw.
+    // Also validate that we're using the explicit (and not @ImplementedBy) implementation
+    assertFalse(injector.getInstance(HasImplementedBy1.class) instanceof ImplementsHasImplementedBy1);
+  }
+  
+  /**
+   * See issue 614, Problem Two
+   * http://code.google.com/p/google-guice/issues/detail?id=614
+   */
+  public void testJitDependencyCanUseExplicitDependencies() {
+    Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(HasImplementedByThatWantsExplicit.class);
+        bind(JustAnInterface.class).toInstance(new JustAnInterface() {});
+      }
+    });
+  }
+  
+  /**
    * Untargetted bindings should follow @ImplementedBy and @ProvidedBy
    * annotations if they exist. Otherwise the class should be constructed
    * directly.
@@ -423,6 +498,28 @@
   static class ExtendsHasImplementedBy2 extends HasImplementedBy2 {}
 
   static class JustAClass {}
+  
+  @ImplementedBy(ImplementsHasImplementedByThatNeedsAnotherImplementedBy.class)
+  static interface HasImplementedByThatNeedsAnotherImplementedBy {
+  }
+  
+  static class ImplementsHasImplementedByThatNeedsAnotherImplementedBy
+    implements HasImplementedByThatNeedsAnotherImplementedBy {
+    @Inject 
+    ImplementsHasImplementedByThatNeedsAnotherImplementedBy(
+        HasImplementedBy1 h1n1) {}                             
+  }
+  
+  @ImplementedBy(ImplementsHasImplementedByThatWantsExplicit.class)
+  static interface HasImplementedByThatWantsExplicit {    
+  }
+  
+  static class ImplementsHasImplementedByThatWantsExplicit
+      implements HasImplementedByThatWantsExplicit {
+    @Inject ImplementsHasImplementedByThatWantsExplicit(JustAnInterface jai) {}
+  }
+  
+  static interface JustAnInterface {}
 
 
 //  public void testBindInterfaceWithoutImplementation() {