Injector.getAllBindings(), feature for issue 389.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@1040 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/Injector.java b/src/com/google/inject/Injector.java
index e49540c..6ff563c 100644
--- a/src/com/google/inject/Injector.java
+++ b/src/com/google/inject/Injector.java
@@ -90,18 +90,31 @@
   <T> MembersInjector<T> getMembersInjector(Class<T> type);
 
   /**
-   * Returns all explicit bindings.
+   * Returns this injector's <strong>explicit</strong> bindings.
    *
    * <p>The returned map does not include bindings inherited from a {@link #getParent() parent
    * injector}, should one exist. The returned map is guaranteed to iterate (for example, with
-   * its {@link java.util.Map#entrySet()} iterator) in the order of insertion. In other words,
-   * the order in which bindings appear in user Modules.
+   * its {@link Map#entrySet()} iterator) in the order of insertion. In other words, the order in
+   * which bindings appear in user Modules.
    *
    * <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
    */
   Map<Key<?>, Binding<?>> getBindings();
 
   /**
+   * Returns a snapshot of this injector's bindings, <strong>both explicit and
+   * just-in-time</strong>. The returned map is immutable; it contains only the bindings that were
+   * present when {@code getAllBindings()} was invoked. Subsequent calls may return a map with
+   * additional just-in-time bindings.
+   *
+   * <p>The returned map does not include bindings inherited from a {@link #getParent() parent
+   * injector}, should one exist.
+   *
+   * <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
+   */
+  Map<Key<?>, Binding<?>> getAllBindings();
+
+  /**
    * Returns the binding for the given injection key. This will be an explicit bindings if the key
    * was bound explicitly by a module, or an implicit binding otherwise. The implicit binding will
    * be created if necessary.
diff --git a/src/com/google/inject/internal/InjectorBuilder.java b/src/com/google/inject/internal/InjectorBuilder.java
index 0f80f41..67d0450 100644
--- a/src/com/google/inject/internal/InjectorBuilder.java
+++ b/src/com/google/inject/internal/InjectorBuilder.java
@@ -245,6 +245,9 @@
     public Map<Key<?>, Binding<?>> getBindings() {
       return this.delegateInjector.getBindings();
     }
+    public Map<Key<?>, Binding<?>> getAllBindings() {
+      return this.delegateInjector.getAllBindings();
+    }
     public <T> Binding<T> getBinding(Key<T> key) {
       return this.delegateInjector.getBinding(key);
     }
diff --git a/src/com/google/inject/internal/InjectorImpl.java b/src/com/google/inject/internal/InjectorImpl.java
index b55014e..9419935 100644
--- a/src/com/google/inject/internal/InjectorImpl.java
+++ b/src/com/google/inject/internal/InjectorImpl.java
@@ -625,11 +625,19 @@
     return getBindingOrThrow(key, errors).getInternalFactory();
   }
 
-  // not test-covered
   public Map<Key<?>, Binding<?>> getBindings() {
     return state.getExplicitBindingsThisLevel();
   }
 
+  public Map<Key<?>, Binding<?>> getAllBindings() {
+    synchronized (state.lock()) {
+      return new ImmutableMap.Builder<Key<?>, Binding<?>>()
+          .putAll(state.getExplicitBindingsThisLevel())
+          .putAll(jitBindings)
+          .build();
+    }
+  }
+
   private static class BindingsMultimap {
     final Map<TypeLiteral<?>, List<Binding<?>>> multimap = Maps.newHashMap();
 
diff --git a/test/com/google/inject/BindingTest.java b/test/com/google/inject/BindingTest.java
index fe5a40a..725a35a 100644
--- a/test/com/google/inject/BindingTest.java
+++ b/test/com/google/inject/BindingTest.java
@@ -26,8 +26,10 @@
 import java.lang.reflect.Constructor;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
 import junit.framework.TestCase;
 import org.aopalliance.intercept.MethodInterceptor;
 import org.aopalliance.intercept.MethodInvocation;
@@ -363,6 +365,37 @@
         heardTypes);
   }
 
+  public void testGetAllBindings() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(D.class).toInstance(new D(Stage.PRODUCTION));
+        bind(Object.class).to(D.class);
+        getProvider(new Key<C<Stage>>() {});
+      }
+    });
+
+    Map<Key<?>,Binding<?>> bindings = injector.getAllBindings();
+    assertEquals(ImmutableSet.of(Key.get(Injector.class), Key.get(Stage.class), Key.get(D.class),
+        Key.get(Logger.class), Key.get(Object.class), new Key<C<Stage>>() {}),
+        bindings.keySet());
+
+    // add a JIT binding
+    injector.getInstance(F.class);
+
+    Map<Key<?>,Binding<?>> bindings2 = injector.getAllBindings();
+    assertEquals(ImmutableSet.of(Key.get(Injector.class), Key.get(Stage.class), Key.get(D.class),
+        Key.get(Logger.class), Key.get(Object.class), new Key<C<Stage>>() {}, Key.get(F.class)),
+        bindings2.keySet());
+
+    // the original map shouldn't have changed
+    assertEquals(ImmutableSet.of(Key.get(Injector.class), Key.get(Stage.class), Key.get(D.class),
+        Key.get(Logger.class), Key.get(Object.class), new Key<C<Stage>>() {}),
+        bindings.keySet());
+
+    // check the bindings' values
+    assertEquals(injector, bindings.get(Key.get(Injector.class)).getProvider().get());
+  }
+
   public static class C<T> {
     private Stage stage;
     private T t;