Revision: 1526
Author: sberlin
Date: Thu Mar 10 16:03:01 2011
Log: fix issue 614 -- admittedly not the prettiest solution, but it works.
http://code.google.com/p/google-guice/source/detail?r=1526

Modified:
 /trunk/core/src/com/google/inject/internal/BindingProcessor.java
 /trunk/core/src/com/google/inject/internal/InjectorShell.java
 /trunk/core/test/com/google/inject/BinderTest.java

=======================================
--- /trunk/core/src/com/google/inject/internal/BindingProcessor.java Mon Feb 21 13:06:40 2011 +++ /trunk/core/src/com/google/inject/internal/BindingProcessor.java Thu Mar 10 16:03:01 2011
@@ -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);
     }
=======================================
--- /trunk/core/src/com/google/inject/internal/InjectorShell.java Sun Dec 12 18:44:36 2010 +++ /trunk/core/src/com/google/inject/internal/InjectorShell.java Thu Mar 10 16:03:01 2011
@@ -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");

=======================================
--- /trunk/core/test/com/google/inject/BinderTest.java Sat Jul 3 08:51:31 2010 +++ /trunk/core/test/com/google/inject/BinderTest.java Thu Mar 10 16:03:01 2011
@@ -268,7 +268,82 @@
     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
@@ -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() {

--
You received this message because you are subscribed to the Google Groups 
"google-guice-dev" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/google-guice-dev?hl=en.

Reply via email to