cleanup related to issue 395.  rather than using Guice.createInjectorBuilder, use "new InjectorBuilder()".  deprecated methods in Guice that took the Stage parameter.  (didn't deprecate other methods because it's still a good easy-to-use way to start getting into Guice.)  added more tests to JitBindingsTest for @Provides, child injectors & private modules (private modules was broken!). rolled passing around Injector options (stage, jitDisabled) into a new InjectorOptions class to prevent those things from breaking again in the future.  renamed InjectorBuilderImpl to InternalInjectorCreator because it has to be public for InjectorBuilder to use it, and didn't want to confuse auto-complete IDEs into offering InjectorBuilderImpl as an option.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@1142 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/Guice.java b/src/com/google/inject/Guice.java
index b0bd1bc..f13a20f 100644
--- a/src/com/google/inject/Guice.java
+++ b/src/com/google/inject/Guice.java
@@ -18,12 +18,10 @@
 
 import java.util.Arrays;
 
-import com.google.inject.internal.InjectorBuilderImpl;
-
 
 /**
  * The entry point to the Guice framework. Creates {@link Injector}s from
- * {@link Module}s.
+ * {@link Module}s.  For advanced usage, see {@link InjectorBuilder}.
  *
  * <p>Guice supports a model of development that draws clear boundaries between
  * APIs, Implementations of these APIs, Modules which configure these
@@ -54,6 +52,9 @@
 
   /**
    * Creates an injector for the given set of modules.
+   * 
+   * To create an Injector with a {@link Stage} or other options, see
+   * {@link InjectorBuilder}.
    *
    * @throws CreationException if one or more errors occur during Injector
    *     construction
@@ -64,6 +65,9 @@
 
   /**
    * Creates an injector for the given set of modules.
+   * 
+   * To create an Injector with a {@link Stage} or other options, see
+   * {@link InjectorBuilder}.
    *
    * @throws CreationException if one or more errors occur during Injector
    *     creation
@@ -77,8 +81,11 @@
    * stage.
    *
    * @throws CreationException if one or more errors occur during Injector
-   *     creation
+   *     creation.
+   *     
+   * @deprecated Use {@link InjectorBuilder} for advanced Injector creation.
    */
+  @Deprecated
   public static Injector createInjector(Stage stage, Module... modules) {
     return createInjector(stage, Arrays.asList(modules));
   }
@@ -89,21 +96,15 @@
    *
    * @throws CreationException if one or more errors occur during Injector
    *     construction
+   *     
+   * @deprecated Use {@link InjectorBuilder} for advanced Injector creation.
    */
+  @Deprecated
   public static Injector createInjector(Stage stage,
       Iterable<? extends Module> modules) {
-    return new InjectorBuilderImpl()
+    return new InjectorBuilder()
         .stage(stage)
         .addModules(modules)
         .build();
   }
-  
-  /** 
-   * Creates an {@link InjectorBuilder} which can be used to create an injector.
-   *
-   * @since 2.1
-   */
-  public static InjectorBuilder createInjectorBuilder() {
-    return new InjectorBuilderImpl().stage(Stage.DEVELOPMENT);
-  }
 }
diff --git a/src/com/google/inject/Injector.java b/src/com/google/inject/Injector.java
index 43f9723..a74b4c8 100644
--- a/src/com/google/inject/Injector.java
+++ b/src/com/google/inject/Injector.java
@@ -223,13 +223,6 @@
    * @since 2.0
    */
   Injector createChildInjector(Module... modules);
-  
-  /**
-   * Creates an InjectorBuilder for a child injector.
-   *
-   * @since 2.1
-   */
-  InjectorBuilder createChildInjectorBuilder();
 
   /**
    * Returns a map containing all scopes in the injector. The maps keys are scoping annotations
diff --git a/src/com/google/inject/InjectorBuilder.java b/src/com/google/inject/InjectorBuilder.java
index 9bbfc38..7694094 100644
--- a/src/com/google/inject/InjectorBuilder.java
+++ b/src/com/google/inject/InjectorBuilder.java
@@ -1,40 +1,93 @@
 package com.google.inject;
 
+import java.util.Arrays;
+
+import com.google.inject.internal.InternalInjectorCreator;
+
 /**
- * Allows Injectors to be built with many different parameters.
+ * The advanced entry point to the Guice framework. Creates {@link Injector}s from
+ * {@link Module}s, allowing many options to be configured for the Injector.
+ *
+ * <p>Guice supports a model of development that draws clear boundaries between
+ * APIs, Implementations of these APIs, Modules which configure these
+ * implementations, and finally Applications which consist of a collection of
+ * Modules. It is the Application, which typically defines your {@code main()}
+ * method, that bootstraps the Guice Injector using the {@code Guice} class, as
+ * in this example:
+ * <pre>
+ *     public class FooApplication {
+ *       public static void main(String[] args) {
+ *         Injector injector = new InjectorBuilder().
+ *             .stage(Stage.PRODUCTION)
+ *             . . . 
+ *             .addModules(
+ *                new ModuleA(),
+ *                new ModuleB(),
+ *                . . .
+ *                new FooApplicationFlagsModule(args)
+ *             )
+ *             .build();
+ *         );
+ *
+ *         // Now just bootstrap the application and you're done
+ *         FooStarter starter = injector.getInstance(FooStarter.class);
+ *         starter.runApplication();
+ *       }
+ *     }
+ * </pre>
  * 
  * @since 2.1
  */
-public interface InjectorBuilder {
-
+public class InjectorBuilder {
+  
+  private final InternalInjectorCreator creator = new InternalInjectorCreator();
+  
+  private Stage stage = Stage.DEVELOPMENT;
+  private boolean jitDisabled;
+  
   /**
-   * Sets the stage for the created injector. If the stage is {@link Stage#PRODUCTION}, 
-   * singletons will be eagerly loaded in when the Injector is built.
+   * Sets the stage for the injector. If the stage is {@link Stage#PRODUCTION}, 
+   * singletons will be eagerly loaded when the Injector is built.
    */
-  InjectorBuilder stage(Stage stage);
+  public InjectorBuilder stage(Stage stage) {
+    this.stage = stage;
+    return this;
+  }
 
   /**
-   * If explicit bindings are required, then classes cannot inject classes
-   * that are not explicitly bound in a module.  Bindings created through
-   * a linked binding <code>bind(Foo.class).to(FooImpl.class)</code> are allowed,
-   * but the implicit binding (FooImpl) cannot directly injected unless it is
+   * If explicit bindings are required, then classes that are not explicitly
+   * bound in a module cannot be injected. Bindings created through a linked
+   * binding (<code>bind(Foo.class).to(FooImpl.class)</code>) are allowed, but
+   * the implicit binding (FooImpl) cannot be directly injected unless it is
    * also explicitly bound.
    * 
    * Tools can still retrieve bindings for implicit bindings (bindings created
-   * through a linked binding) if explicit bindings are required, however 
-   * the {@link Binding#getProvider} method of the binding cannot be used.
+   * through a linked binding) if explicit bindings are required, however
+   * {@link Binding#getProvider} cannot be used.
    * 
    * By default, explicit bindings are not required.
    */
-  InjectorBuilder requireExplicitBindings();
+  InjectorBuilder requireExplicitBindings() {
+    this.jitDisabled = true;
+    return this;
+  }
+
+  /** Adds more modules that will be used when the Injector is created. */
+  InjectorBuilder addModules(Iterable<? extends Module> modules) {
+    creator.addModules(modules);
+    return this;
+  }
   
   /** Adds more modules that will be used when the Injector is created. */
-  InjectorBuilder addModules(Iterable<? extends Module> modules);
-  
-  /** Adds more modules that will be used when the Injector is created. */
-  InjectorBuilder addModules(Module... modules);
+  InjectorBuilder addModules(Module... modules) {
+    creator.addModules(Arrays.asList(modules));
+    return this;
+  }
 
   /** Builds the injector. */
-  Injector build();
+  Injector build() {
+    creator.injectorOptions(new InternalInjectorCreator.InjectorOptions(stage, jitDisabled));
+    return creator.build();
+  }
 
 }
\ No newline at end of file
diff --git a/src/com/google/inject/Scopes.java b/src/com/google/inject/Scopes.java
index d2e0bd4..660e89c 100644
--- a/src/com/google/inject/Scopes.java
+++ b/src/com/google/inject/Scopes.java
@@ -19,7 +19,7 @@
 import java.lang.annotation.Annotation;
 
 import com.google.inject.internal.CircularDependencyProxy;
-import com.google.inject.internal.InjectorBuilderImpl;
+import com.google.inject.internal.InternalInjectorCreator;
 import com.google.inject.internal.LinkedBindingImpl;
 import com.google.inject.spi.BindingScopingVisitor;
 
@@ -59,7 +59,7 @@
              *
              * This block is re-entrant for circular dependencies.
              */
-            synchronized (InjectorBuilderImpl.class) {
+            synchronized (InternalInjectorCreator.class) {
               if (instance == null) {
                 T provided = creator.get();
 
diff --git a/src/com/google/inject/internal/Initializer.java b/src/com/google/inject/internal/Initializer.java
index 35e21ea..ee15fc4 100644
--- a/src/com/google/inject/internal/Initializer.java
+++ b/src/com/google/inject/internal/Initializer.java
@@ -142,7 +142,7 @@
       if (pendingInjection.remove(instance) != null) {
         // if in Stage.TOOL, we only want to inject & notify toolable injection points.
         // (otherwise we'll inject all of them)
-        membersInjector.injectAndNotify(instance, errors.withSource(source), injector.stage == Stage.TOOL);
+        membersInjector.injectAndNotify(instance, errors.withSource(source), injector.options.stage == Stage.TOOL);
       }
 
       return instance;
diff --git a/src/com/google/inject/internal/InjectionRequestProcessor.java b/src/com/google/inject/internal/InjectionRequestProcessor.java
index 3c5433a..aff4f6f 100644
--- a/src/com/google/inject/internal/InjectionRequestProcessor.java
+++ b/src/com/google/inject/internal/InjectionRequestProcessor.java
@@ -110,7 +110,7 @@
             for (SingleMemberInjector memberInjector : memberInjectors) {
               // Run injections if we're not in tool stage (ie, PRODUCTION or DEV),
               // or if we are in tool stage and the injection point is toolable.
-              if(injector.stage != Stage.TOOL || memberInjector.getInjectionPoint().isToolable()) {
+              if(injector.options.stage != Stage.TOOL || memberInjector.getInjectionPoint().isToolable()) {
                 memberInjector.inject(errors, context, null);
               }
             }
diff --git a/src/com/google/inject/internal/InjectorImpl.java b/src/com/google/inject/internal/InjectorImpl.java
index b24e074..58996c2 100644
--- a/src/com/google/inject/internal/InjectorImpl.java
+++ b/src/com/google/inject/internal/InjectorImpl.java
@@ -29,8 +29,8 @@
 import com.google.inject.Provider;
 import com.google.inject.ProvisionException;
 import com.google.inject.Scope;
-import com.google.inject.Stage;
 import com.google.inject.TypeLiteral;
+import com.google.inject.internal.InternalInjectorCreator.InjectorOptions;
 import com.google.inject.spi.BindingTargetVisitor;
 import com.google.inject.spi.ConvertedConstantBinding;
 import com.google.inject.spi.Dependency;
@@ -70,19 +70,17 @@
   final State state;
   final InjectorImpl parent;
   final BindingsMultimap bindingsMultimap = new BindingsMultimap();
-  final Stage stage;
-  final boolean jitDisabled;
+  final InjectorOptions options;
 
   /** Just-in-time binding cache. Guarded by state.lock() */
   final Map<Key<?>, BindingImpl<?>> jitBindings = Maps.newHashMap();
 
   Lookups lookups = new DeferredLookups(this);
 
-  InjectorImpl(@Nullable InjectorImpl parent, State state, Stage stage, boolean jitDisabled) {
+  InjectorImpl(@Nullable InjectorImpl parent, State state, InjectorOptions injectorOptions) {
     this.parent = parent;
     this.state = state;
-    this.stage = stage;
-    this.jitDisabled = jitDisabled;
+    this.options = injectorOptions;
 
     if (parent != null) {
       localContext = parent.localContext;
@@ -149,7 +147,7 @@
   }
 
   public Injector createChildInjector(Iterable<? extends Module> modules) {
-    return new InjectorBuilderImpl()
+    return new InternalInjectorCreator()
         .parentInjector(this)
         .addModules(modules)
         .build();
@@ -158,12 +156,6 @@
   public Injector createChildInjector(Module... modules) {
     return createChildInjector(ImmutableList.of(modules));
   }
-  
-  @Override
-  public InjectorBuilder createChildInjectorBuilder() {
-    return new InjectorBuilderImpl()
-      .parentInjector(this);
-  }
 
   /**
    * Returns a just-in-time binding for {@code key}, creating it if necessary.
@@ -174,7 +166,7 @@
       throws ErrorsException {
 
 
-    if(jitDisabled && jitType == JitLimitation.NO_JIT && !isProvider(key)) {
+    if(options.jitDisabled && jitType == JitLimitation.NO_JIT && !isProvider(key)) {
       throw errors.jitDisabled(key).toException();
     }
     
@@ -189,7 +181,7 @@
         }
       }
       
-      if(jitDisabled && jitType != JitLimitation.NEW_OR_EXISTING_JIT && !isProvider(key)) {
+      if(options.jitDisabled && jitType != JitLimitation.NEW_OR_EXISTING_JIT && !isProvider(key)) {
         throw errors.jitDisabled(key).toException();
       } else {
         return createJustInTimeBindingRecursive(key, errors);
@@ -454,7 +446,7 @@
     }
 
     
-    return ConstructorBindingImpl.create(this, key, null, source, scoping, errors, jitBinding && jitDisabled);
+    return ConstructorBindingImpl.create(this, key, null, source, scoping, errors, jitBinding && options.jitDisabled);
   }
 
   /**
@@ -584,7 +576,7 @@
   private <T> BindingImpl<T> createJustInTimeBindingRecursive(Key<T> key, Errors errors)
       throws ErrorsException {
     // ask the parent to create the JIT binding
-    if (parent != null && !parent.jitDisabled) {
+    if (parent != null && !parent.options.jitDisabled) {
       try {
         return parent.createJustInTimeBindingRecursive(key, new Errors());
       } catch (ErrorsException ignored) {
diff --git a/src/com/google/inject/internal/InjectorShell.java b/src/com/google/inject/internal/InjectorShell.java
index c12bcab..172a170 100644
--- a/src/com/google/inject/internal/InjectorShell.java
+++ b/src/com/google/inject/internal/InjectorShell.java
@@ -26,6 +26,8 @@
 import com.google.inject.Stage;
 import static com.google.inject.internal.Preconditions.checkNotNull;
 import static com.google.inject.internal.Preconditions.checkState;
+
+import com.google.inject.internal.InternalInjectorCreator.InjectorOptions;
 import com.google.inject.spi.Dependency;
 import com.google.inject.spi.Element;
 import com.google.inject.spi.Elements;
@@ -69,8 +71,7 @@
     private State state;
 
     private InjectorImpl parent;
-    private Stage stage;
-    private boolean jitDisabled;
+    private InjectorOptions options;
 
     /** null unless this exists in a {@link Binder#newPrivateBinder private environment} */
     private PrivateElementsImpl privateElements;
@@ -78,16 +79,12 @@
     Builder parent(InjectorImpl parent) {
       this.parent = parent;
       this.state = new InheritingState(parent.state);
-      return this;
-    }
-
-    Builder stage(Stage stage) {
-      this.stage = stage;
+      this.options = parent.options;
       return this;
     }
     
-    Builder jitDisabled(boolean jitDisabled) {
-      this.jitDisabled = jitDisabled;
+    Builder setInjectorOptions(InjectorOptions options) {
+      this.options = options;
       return this;
     }
 
@@ -102,6 +99,10 @@
         this.modules.add(module);
       }
     }
+    
+    InjectorOptions getInjectorOptions() {
+      return options;
+    }
 
     /** Synchronize on this before calling {@link #build}. */
     Object lock() {
@@ -115,22 +116,23 @@
      */
     List<InjectorShell> build(BindingProcessor bindingProcessor,
         Stopwatch stopwatch, Errors errors) {
-      checkState(stage != null, "Stage not initialized");
+      checkState(options != null, "Options not initialized");
+      checkState(options.stage != null, "Stage not initialized");
       checkState(privateElements == null || parent != null, "PrivateElements with no parent");
       checkState(state != null, "no state. Did you remember to lock() ?");
 
-      InjectorImpl injector = new InjectorImpl(parent, state, stage, jitDisabled);
+      InjectorImpl injector = new InjectorImpl(parent, state, options);
       if (privateElements != null) {
         privateElements.initInjector(injector);
       }
 
       // bind Stage and Singleton if this is a top-level injector
       if (parent == null) {
-        modules.add(0, new RootModule(stage));
+        modules.add(0, new RootModule(options.stage));
         new TypeConverterBindingProcessor(errors).prepareBuiltInConverters(injector);
       }
 
-      elements.addAll(Elements.getElements(stage, modules));
+      elements.addAll(Elements.getElements(options.stage, modules));
       stopwatch.resetAndLog("Module execution");
 
       new MessageProcessor(errors).process(injector, elements);
@@ -161,7 +163,7 @@
       injectorShells.add(new InjectorShell(this, elements, injector));
 
       // recursively build child shells
-      PrivateElementProcessor processor = new PrivateElementProcessor(errors, stage);
+      PrivateElementProcessor processor = new PrivateElementProcessor(errors, options);
       processor.process(injector, elements);
       for (Builder builder : processor.getInjectorShellBuilders()) {
         injectorShells.addAll(builder.build(bindingProcessor, stopwatch, errors));
diff --git a/src/com/google/inject/internal/InjectorBuilderImpl.java b/src/com/google/inject/internal/InternalInjectorCreator.java
similarity index 89%
rename from src/com/google/inject/internal/InjectorBuilderImpl.java
rename to src/com/google/inject/internal/InternalInjectorCreator.java
index 6444b36..624887b 100644
--- a/src/com/google/inject/internal/InjectorBuilderImpl.java
+++ b/src/com/google/inject/internal/InternalInjectorCreator.java
@@ -18,7 +18,6 @@
 
 import com.google.inject.Binding;
 import com.google.inject.Injector;
-import com.google.inject.InjectorBuilder;
 import com.google.inject.Key;
 import com.google.inject.MembersInjector;
 import com.google.inject.Module;
@@ -28,7 +27,6 @@
 import com.google.inject.Scope;
 import com.google.inject.spi.Dependency;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -54,57 +52,51 @@
  * @author crazybob@google.com (Bob Lee)
  * @author jessewilson@google.com (Jesse Wilson)
  */
-public final class InjectorBuilderImpl implements InjectorBuilder {
+public final class InternalInjectorCreator {
 
   private final Stopwatch stopwatch = new Stopwatch();
   private final Errors errors = new Errors();
 
-  private Stage stage;
-
   private final Initializer initializer = new Initializer();
   private final BindingProcessor bindingProcesor;
   private final InjectionRequestProcessor injectionRequestProcessor;
 
   private final InjectorShell.Builder shellBuilder = new InjectorShell.Builder();
   private List<InjectorShell> shells;
+  
+  public static class InjectorOptions {
+    final Stage stage;
+    final boolean jitDisabled;
+    
+    public InjectorOptions(Stage stage, boolean jitDisabled) {
+      this.stage = stage;
+      this.jitDisabled = jitDisabled;
+    }
+  }
 
-  public InjectorBuilderImpl() {
+  public InternalInjectorCreator() {
     injectionRequestProcessor = new InjectionRequestProcessor(errors, initializer);
     bindingProcesor = new BindingProcessor(errors, initializer);
   }
-
-  /**
-   * Sets the stage for the created injector. If the stage is {@link Stage#PRODUCTION}, this class
-   * will eagerly load singletons.
-   */
-  public InjectorBuilder stage(Stage stage) {
-    shellBuilder.stage(stage);
-    this.stage = stage;
-    return this;
-  }
   
-  public InjectorBuilder requireExplicitBindings() {
-    shellBuilder.jitDisabled(true);
+  public InternalInjectorCreator injectorOptions(InjectorOptions options) {
+    shellBuilder.setInjectorOptions(options);
     return this;
   }  
 
   /**
-   * Sets the parent of the injector to-be-constructed. As a side effect, this sets this injector's
-   * stage to the stage of {@code parent}.
+   * Sets the parent of the injector to-be-constructed.As a side effect, this sets this injector's
+   * stage to the stage of {@code parent} and sets {@link #requireExplicitBindings()} if the parent
+   * injector also required them.
    */
-  public InjectorBuilder parentInjector(InjectorImpl parent) {
+  public InternalInjectorCreator parentInjector(InjectorImpl parent) {
     shellBuilder.parent(parent);
-    return stage(parent.getInstance(Stage.class));
-  }
-
-  public InjectorBuilder addModules(Iterable<? extends Module> modules) {
-    shellBuilder.addModules(modules);
+    shellBuilder.setInjectorOptions(parent.options);
     return this;
   }
-  
-  @Override
-  public InjectorBuilder addModules(Module... modules) {
-    shellBuilder.addModules(Arrays.asList(modules));
+
+  public InternalInjectorCreator addModules(Iterable<? extends Module> modules) {
+    shellBuilder.addModules(modules);
     return this;
   }
 
@@ -124,7 +116,7 @@
 
     injectDynamically();
 
-    if (stage == Stage.TOOL) {
+    if (shellBuilder.getInjectorOptions().stage == Stage.TOOL) {
       // wrap the primaryInjector in a ToolStageInjector
       // to prevent non-tool-friendy methods from being called.
       return new ToolStageInjector(primaryInjector());
@@ -190,9 +182,9 @@
     stopwatch.resetAndLog("Instance injection");
     errors.throwCreationExceptionIfErrorsExist();
 
-    if(stage != Stage.TOOL) {
+    if(shellBuilder.getInjectorOptions().stage != Stage.TOOL) {
       for (InjectorShell shell : shells) {
-        loadEagerSingletons(shell.getInjector(), stage, errors);
+        loadEagerSingletons(shell.getInjector(), shellBuilder.getInjectorOptions().stage, errors);
       }
       stopwatch.resetAndLog("Preloading singletons");
     }
@@ -285,9 +277,6 @@
     public Injector createChildInjector(Module... modules) {
       return delegateInjector.createChildInjector(modules);
     }
-    public InjectorBuilder createChildInjectorBuilder() {
-      return delegateInjector.createChildInjectorBuilder();
-    }
     public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
       return delegateInjector.getScopeBindings();
     }
diff --git a/src/com/google/inject/internal/PrivateElementProcessor.java b/src/com/google/inject/internal/PrivateElementProcessor.java
index 33d2fbe..21883fb 100644
--- a/src/com/google/inject/internal/PrivateElementProcessor.java
+++ b/src/com/google/inject/internal/PrivateElementProcessor.java
@@ -16,7 +16,7 @@
 
 package com.google.inject.internal;
 
-import com.google.inject.Stage;
+import com.google.inject.internal.InternalInjectorCreator.InjectorOptions;
 import com.google.inject.spi.PrivateElements;
 import java.util.List;
 
@@ -27,18 +27,18 @@
  */
 final class PrivateElementProcessor extends AbstractProcessor {
 
-  private final Stage stage;
+  private final InjectorOptions options;
   private final List<InjectorShell.Builder> injectorShellBuilders = Lists.newArrayList();
 
-  PrivateElementProcessor(Errors errors, Stage stage) {
+  PrivateElementProcessor(Errors errors, InjectorOptions options) {
     super(errors);
-    this.stage = stage;
+    this.options = options;
   }
 
   @Override public Boolean visit(PrivateElements privateElements) {
     InjectorShell.Builder builder = new InjectorShell.Builder()
         .parent(injector)
-        .stage(stage)
+        .setInjectorOptions(options)
         .privateElements(privateElements);
     injectorShellBuilders.add(builder);
     return true;
diff --git a/test/com/google/inject/JitBindingsTest.java b/test/com/google/inject/JitBindingsTest.java
index d6e6a17..a5676f1 100644
--- a/test/com/google/inject/JitBindingsTest.java
+++ b/test/com/google/inject/JitBindingsTest.java
@@ -1,5 +1,6 @@
 package com.google.inject;
 
+import static com.google.inject.Asserts.assertContains;
 import junit.framework.TestCase;
 
 /**
@@ -18,7 +19,7 @@
   }
   
   public void testLinkedBindingWorks() {
-    Injector injector = Guice.createInjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
+    Injector injector = new InjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
       @Override
       protected void configure() {
         bind(Foo.class).to(FooImpl.class);
@@ -33,7 +34,7 @@
   }
   
   public void testMoreBasicsWork() {
-    Injector injector = Guice.createInjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
+    Injector injector = new InjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
       @Override
       protected void configure() {
         bind(Foo.class).to(FooImpl.class);
@@ -50,7 +51,7 @@
   }
   
   public void testLinkedProviderBindingWorks() {
-    Injector injector = Guice.createInjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
+    Injector injector = new InjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
       @Override
       protected void configure() {
         bind(Foo.class).toProvider(FooProvider.class);
@@ -65,17 +66,17 @@
   
   public void testJitGetFails() {
     try {
-      Guice.createInjectorBuilder().requireExplicitBindings().build().getInstance(Bar.class);
+      new InjectorBuilder().requireExplicitBindings().build().getInstance(Bar.class);
       fail("should have failed");
     } catch(ConfigurationException expected) {
-      Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(Bar.class));
+      assertContains(expected.getMessage(), "1) " + jitFailed(Bar.class));
       assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
     }
   }
   
   public void testJitInjectionFails() {
     try {
-      Guice.createInjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
+      new InjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
         @Override
         protected void configure() {
           bind(Foo.class).to(FooImpl.class);
@@ -84,24 +85,24 @@
       }).build();
       fail("should have failed");
     } catch (CreationException expected) {
-      Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(Bar.class));
+      assertContains(expected.getMessage(), "1) " + jitFailed(Bar.class));
       assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
     }
   }
 
   public void testJitProviderGetFails() {
     try {
-      Guice.createInjectorBuilder().requireExplicitBindings().build().getProvider(Bar.class);
+      new InjectorBuilder().requireExplicitBindings().build().getProvider(Bar.class);
       fail("should have failed");
     } catch (ConfigurationException expected) {
-      Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(Bar.class));
+      assertContains(expected.getMessage(), "1) " + jitFailed(Bar.class));
       assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
     }
   }
 
   public void testJitProviderInjectionFails() {
     try {
-      Guice.createInjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
+      new InjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
         @Override
         protected void configure() {
           bind(Foo.class).to(FooImpl.class);
@@ -110,13 +111,13 @@
       }).build();
       fail("should have failed");
     } catch (CreationException expected) {
-      Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(Bar.class));
+      assertContains(expected.getMessage(), "1) " + jitFailed(Bar.class));
       assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
     }
   }
   
   public void testImplementedBy() {
-    Injector injector = Guice.createInjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
+    Injector injector = new InjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
       @Override
       protected void configure() {
         bind(ImplBy.class);
@@ -127,7 +128,7 @@
   }
   
   public void testProvidedBy() {
-    Injector injector = Guice.createInjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
+    Injector injector = new InjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
       @Override
       protected void configure() {
         bind(ProvBy.class);
@@ -137,6 +138,80 @@
     ensureFails(injector, true, ProvByProvider.class);
   }
   
+  public void testProviderMethods() {
+    Injector injector = new InjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
+      @Override protected void configure() {}
+      @SuppressWarnings("unused") @Provides Foo foo() { return new FooImpl(); }
+    }).build();
+    ensureWorks(injector, Foo.class);
+  }
+  
+  public void testChildInjectors() {
+    Injector parent = new InjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Bar.class);
+      }
+    }).build();
+    ensureWorks(parent, Bar.class);
+    ensureFails(parent, false, FooImpl.class, FooBar.class, Foo.class);
+    
+    try {
+      parent.createChildInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          bind(FooBar.class);
+        }
+      });
+      fail("should have failed");
+    } catch(CreationException expected) {
+      assertContains(expected.getMessage(), "1) " + jitFailed(Foo.class));
+      assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
+    }
+    
+    Injector child = parent.createChildInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Foo.class).to(FooImpl.class);
+      }
+    });
+    ensureWorks(child, Foo.class, Bar.class);
+    ensureFails(child, true, FooImpl.class);
+    ensureFails(parent, false, FooImpl.class, FooBar.class, Foo.class); // parent still doesn't have these
+    
+    Injector grandchild = child.createChildInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(FooBar.class);
+      }
+    });
+    ensureWorks(grandchild, FooBar.class, Foo.class, Bar.class);
+    ensureFails(grandchild, true, FooImpl.class);
+    ensureFails(child, true, FooImpl.class);
+    ensureFails(parent, false, FooImpl.class, FooBar.class, Foo.class); // parent still doesn't have these    
+  }
+
+  public void testPrivateModules() {
+    try {
+      new InjectorBuilder().requireExplicitBindings().addModules(new AbstractModule() {
+        protected void configure() {
+          bind(Foo.class).to(FooImpl.class);
+  
+          install(new PrivateModule() {
+            public void configure() {
+              bind(FooBar.class);
+              expose(FooBar.class);
+            }
+          });
+        }
+      }).build();
+      fail("should have failed");
+    } catch(CreationException expected) {
+      assertContains(expected.getMessage(), "1) " + jitFailed(Bar.class));
+      assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
+    }
+  }
+  
   private void ensureWorks(Injector injector, Class<?>... classes) {
     for(int i = 0; i < classes.length; i++) {
       injector.getInstance(classes[i]);
@@ -151,7 +226,7 @@
         injector.getInstance(classes[i]);
         fail("should have failed");
       } catch(ConfigurationException expected) {
-        Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(classes[i]));
+        assertContains(expected.getMessage(), "1) " + jitFailed(classes[i]));
         assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
       }
       
@@ -159,7 +234,7 @@
         injector.getProvider(classes[i]);
         fail("should have failed");
       } catch(ConfigurationException expected) {
-        Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(classes[i]));
+        assertContains(expected.getMessage(), "1) " + jitFailed(classes[i]));
         assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
       }
       
@@ -169,7 +244,7 @@
           binding.getProvider();
           fail("should have failed");
         } catch(ConfigurationException expected) {
-          Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(classes[i]));
+          assertContains(expected.getMessage(), "1) " + jitFailed(classes[i]));
           assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
         }
       } else {
@@ -177,7 +252,7 @@
           injector.getBinding(classes[i]);
           fail("should have failed");          
         } catch(ConfigurationException expected) {
-          Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(classes[i]));
+          assertContains(expected.getMessage(), "1) " + jitFailed(classes[i]));
           assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
         }
       }
@@ -196,7 +271,6 @@
     @SuppressWarnings("unused") @Inject Provider<Bar> bar;
   }
   private static class FooProvider implements Provider<Foo> {
-    @Override
     public Foo get() {
       return new FooImpl();
     }
@@ -209,7 +283,6 @@
   @ProvidedBy(ProvByProvider.class)
   private static interface ProvBy {}
   private static class ProvByProvider implements Provider<ProvBy> {
-    @Override
     public ProvBy get() {
       return new ProvBy() {};
     }