issue 610 - let requireExplicitBindings still allow TypeConverters to create ConvertedConstantBindings.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@1512 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/core/src/com/google/inject/internal/InjectorImpl.java b/core/src/com/google/inject/internal/InjectorImpl.java
index 6f332fb..929e3b7 100644
--- a/core/src/com/google/inject/internal/InjectorImpl.java
+++ b/core/src/com/google/inject/internal/InjectorImpl.java
@@ -232,11 +232,7 @@
   private <T> BindingImpl<T> getJustInTimeBinding(Key<T> key, Errors errors, JitLimitation jitType)
       throws ErrorsException {
 
-    boolean jitOverride = isProvider(key) || isTypeLiteral(key) || isMembersInjector(key);
-    if(options.jitDisabled && jitType == JitLimitation.NO_JIT && !jitOverride) {
-      throw errors.jitDisabled(key).toException();
-    }
-    
+    boolean jitOverride = isProvider(key) || isTypeLiteral(key) || isMembersInjector(key);    
     synchronized (state.lock()) {
       // first try to find a JIT binding that we've already created
       for (InjectorImpl injector = this; injector != null; injector = injector.parent) {
@@ -244,15 +240,20 @@
         BindingImpl<T> binding = (BindingImpl<T>) injector.jitBindings.get(key);
 
         if (binding != null) {
-          return binding;
+          // If we found a JIT binding and we don't allow them,
+          // fail.  (But allow bindings created through TypeConverters.)
+          if (options.jitDisabled
+              && jitType == JitLimitation.NO_JIT
+              && !jitOverride
+              && !(binding instanceof ConvertedConstantBindingImpl)) {
+            throw errors.jitDisabled(key).toException();
+          } else {
+            return binding;
+          }
         }
       }
       
-      if(options.jitDisabled && jitType != JitLimitation.NEW_OR_EXISTING_JIT && !jitOverride) {
-        throw errors.jitDisabled(key).toException();
-      } else {
-        return createJustInTimeBindingRecursive(key, errors);
-      }
+      return createJustInTimeBindingRecursive(key, errors, options.jitDisabled, jitType);
     }
   }
 
@@ -752,12 +753,13 @@
    * Attempts to create a just-in-time binding for {@code key} in the root injector, falling back to
    * other ancestor injectors until this injector is tried.
    */
-  private <T> BindingImpl<T> createJustInTimeBindingRecursive(Key<T> key, Errors errors)
-      throws ErrorsException {
+  private <T> BindingImpl<T> createJustInTimeBindingRecursive(Key<T> key, Errors errors,
+      boolean jitDisabled, JitLimitation jitType) throws ErrorsException {
     // ask the parent to create the JIT binding
-    if (parent != null && !parent.options.jitDisabled) {
+    if (parent != null) {
       try {
-        return parent.createJustInTimeBindingRecursive(key, new Errors());
+        return parent.createJustInTimeBindingRecursive(key, new Errors(), jitDisabled,
+            parent.options.jitDisabled ? JitLimitation.NO_JIT : jitType);
       } catch (ErrorsException ignored) {
       }
     }
@@ -767,7 +769,7 @@
       throw errors.childBindingAlreadySet(key, sources).toException();
     }
 
-    BindingImpl<T> binding = createJustInTimeBinding(key, errors);
+    BindingImpl<T> binding = createJustInTimeBinding(key, errors, jitDisabled, jitType);
     state.parent().blacklist(key, binding.getSource());
     jitBindings.put(key, binding);
     return binding;
@@ -786,8 +788,8 @@
    *
    * @throws com.google.inject.internal.ErrorsException if the binding cannot be created.
    */
-  private <T> BindingImpl<T> createJustInTimeBinding(Key<T> key, Errors errors)
-      throws ErrorsException {
+  private <T> BindingImpl<T> createJustInTimeBinding(Key<T> key, Errors errors,
+      boolean jitDisabled, JitLimitation jitType) throws ErrorsException {
     int numErrorsBefore = errors.size();
 
     if (state.isBlacklisted(key)) {
@@ -818,6 +820,12 @@
     if (convertedBinding != null) {
       return convertedBinding;
     }
+    
+    if (!isTypeLiteral(key) 
+        && jitDisabled
+        && jitType != JitLimitation.NEW_OR_EXISTING_JIT) {
+      throw errors.jitDisabled(key).toException();
+    }
 
     // If the key has an annotation...
     if (key.getAnnotationType() != null) {
diff --git a/core/test/com/google/inject/JitBindingsTest.java b/core/test/com/google/inject/JitBindingsTest.java
index 8a7f9b4..500c9dc 100644
--- a/core/test/com/google/inject/JitBindingsTest.java
+++ b/core/test/com/google/inject/JitBindingsTest.java
@@ -41,6 +41,12 @@
     return "Explicit bindings are required and " + clazz + " is not explicitly bound.";
   }
   
+  private String inChildMessage(Class<?> clazz) {
+    return "Unable to create binding for "
+        + clazz.getName()
+        + ". It was already configured on one or more child injectors or private modules";
+  }
+  
   public void testLinkedBindingWorks() {
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override
@@ -317,7 +323,7 @@
     });
     ensureWorks(child, Foo.class, Bar.class);
     ensureFails(child, ALLOW_BINDING, FooImpl.class);
-    ensureFails(parent, FAIL_ALL, FooImpl.class, FooBar.class, Foo.class); // parent still doesn't have these
+    ensureInChild(parent, FooImpl.class, FooBar.class, Foo.class);
     
     Injector grandchild = child.createChildInjector(new AbstractModule() {
       @Override
@@ -328,7 +334,7 @@
     ensureWorks(grandchild, FooBar.class, Foo.class, Bar.class);
     ensureFails(grandchild, ALLOW_BINDING, FooImpl.class);
     ensureFails(child, ALLOW_BINDING, FooImpl.class);
-    ensureFails(parent, FAIL_ALL, FooImpl.class, FooBar.class, Foo.class); // parent still doesn't have these    
+    ensureInChild(parent, FooImpl.class, FooBar.class, Foo.class);    
   }
   
   public void testChildInjectorAddsOption() {
@@ -418,6 +424,20 @@
       assertContains(expected.getMessage(), "1) " + jitFailed(Bar.class));
       assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
     }
+    
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        binder().requireExplicitBindings();
+
+        install(new PrivateModule() {
+          public void configure() {
+            bind(Foo.class).to(FooImpl.class);
+            expose(Foo.class);
+          }
+        });
+      }
+    });
+    ensureInChild(injector, FooImpl.class);
   }
   
   public void testPrivateModuleAddsOption() {
@@ -553,6 +573,34 @@
     }
   }
   
+  private void ensureInChild(Injector injector, Class<?>... classes) {
+    for(int i = 0; i < classes.length; i++) {      
+      try { 
+        injector.getInstance(classes[i]);
+        fail("should have failed tring to retrieve class: " + classes[i]);
+      } catch(ConfigurationException expected) {
+        assertContains(expected.getMessage(), "1) " + inChildMessage(classes[i]));
+        assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
+      }
+      
+      try { 
+        injector.getProvider(classes[i]);
+        fail("should have failed tring to retrieve class: " + classes[i]);
+      } catch(ConfigurationException expected) {
+        assertContains(expected.getMessage(), "1) " + inChildMessage(classes[i]));
+        assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
+      }
+      
+      try {
+        injector.getBinding(classes[i]);
+        fail("should have failed tring to retrieve class: " + classes[i]);          
+      } catch(ConfigurationException expected) {
+        assertContains(expected.getMessage(), "1) " + inChildMessage(classes[i]));
+        assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
+      }
+    }
+  }
+  
   private static interface Foo {}
   private static class FooImpl implements Foo {}
   @Singleton private static class ScopedFooImpl implements Foo {}
diff --git a/core/test/com/google/inject/TypeConversionTest.java b/core/test/com/google/inject/TypeConversionTest.java
index 3d57d31..31f651d 100644
--- a/core/test/com/google/inject/TypeConversionTest.java
+++ b/core/test/com/google/inject/TypeConversionTest.java
@@ -114,6 +114,39 @@
     assertEquals(Bar.TEE, foo.enumField);
     assertEquals(Foo.class, foo.classField);
   }
+  
+  public void testConstantInjectionWithExplicitBindingsRequired() throws CreationException {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        binder().requireExplicitBindings();
+        bind(Foo.class);
+        bindConstant().annotatedWith(NumericValue.class).to("5");
+        bindConstant().annotatedWith(BooleanValue.class).to("true");
+        bindConstant().annotatedWith(EnumValue.class).to("TEE");
+        bindConstant().annotatedWith(ClassName.class).to(Foo.class.getName());
+      }
+    });
+
+    Foo foo = injector.getInstance(Foo.class);
+
+    checkNumbers(
+      foo.integerField,
+      foo.primitiveIntField,
+      foo.longField,
+      foo.primitiveLongField,
+      foo.byteField,
+      foo.primitiveByteField,
+      foo.shortField,
+      foo.primitiveShortField,
+      foo.floatField,
+      foo.primitiveFloatField,
+      foo.doubleField,
+      foo.primitiveDoubleField
+    );
+
+    assertEquals(Bar.TEE, foo.enumField);
+    assertEquals(Foo.class, foo.classField);
+  }
 
   void checkNumbers(Number... ns) {
     for (Number n : ns) {