Added Binding.getProviderBinding()

git-svn-id: https://google-guice.googlecode.com/svn/trunk@361 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/Binding.java b/src/com/google/inject/Binding.java
index 5dcd6f3..d4a21d8 100644
--- a/src/com/google/inject/Binding.java
+++ b/src/com/google/inject/Binding.java
@@ -17,6 +17,7 @@
 package com.google.inject;
 
 import com.google.inject.spi.BindingVisitor;
+import com.google.inject.spi.ProviderBinding;
 
 /**
  * A mapping from a key (type and optional annotation) to a provider of
@@ -50,6 +51,11 @@
   Provider<T> getProvider();
 
   /**
+   * Gets the synthetic binding to this binding's Provider.
+   */
+  ProviderBinding<T> getProviderBinding();
+
+  /**
    * Returns the scope applied by this binding.
    */
   Scope getScope();
diff --git a/src/com/google/inject/BindingImpl.java b/src/com/google/inject/BindingImpl.java
index 9b3d729..acf4ae3 100644
--- a/src/com/google/inject/BindingImpl.java
+++ b/src/com/google/inject/BindingImpl.java
@@ -17,6 +17,7 @@
 package com.google.inject;
 
 import com.google.inject.internal.ToStringBuilder;
+import com.google.inject.spi.ProviderBinding;
 
 /**
  * @author crazybob@google.com (Bob Lee)
@@ -55,6 +56,13 @@
     return provider;
   }
 
+  @SuppressWarnings("unchecked")
+  public ProviderBinding<T> getProviderBinding() {
+    // ProviderBinding is the only type of binding that can be generated for
+    // a Provider<T>.
+    return (ProviderBinding<T>) injector.getBinding(key.providerKey());
+  }
+
   InternalFactory<? extends T> getInternalFactory() {
     return internalFactory;
   }
diff --git a/src/com/google/inject/Injector.java b/src/com/google/inject/Injector.java
index 771d1e8..ede77eb 100644
--- a/src/com/google/inject/Injector.java
+++ b/src/com/google/inject/Injector.java
@@ -76,6 +76,16 @@
   <T> Binding<T> getBinding(Key<T> key);
 
   /**
+   * Gets a binding for the given type, or null if no binding for this type is
+   * found. Returns explicit bindings as well as those synthesized by the
+   * container such as bindings for converted constants, etc.
+   *
+   * <p>This method is part of the Injector Introspection API and is
+   * primarily intended for use by tools.
+   */
+  <T> Binding<T> getBinding(Class<T> type);
+
+  /**
    * Finds all bindings to the given type. This method is part of the Injector
    * Introspection API and is primarily intended for use by tools.
    */
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index 2f694bd..0e9cda8 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -161,6 +161,10 @@
     return getJitBindingImpl(key);
   }
 
+  public <T> Binding<T> getBinding(Class<T> type) {
+    return getBinding(Key.get(type));
+  }
+
   /**
    * Gets a binding which was specified explicitly in a module.
    */
@@ -535,7 +539,9 @@
 
     @SuppressWarnings("unchecked")
     public T get(InternalContext context) {
-      // We know this cast is safe (assuming getConstructor() is safe).
+      // This may not actually be safe because it could return a super type
+      // of T (if that's all the client needs), but it should be OK in
+      // practice thanks to the wonders of erasure.
       return (T) constructorInjector.construct(
           context, context.getExpectedType());
     }
diff --git a/src/com/google/inject/Key.java b/src/com/google/inject/Key.java
index c5b4621..aba867f 100644
--- a/src/com/google/inject/Key.java
+++ b/src/com/google/inject/Key.java
@@ -175,6 +175,13 @@
     return typeLiteral.getRawType();
   }
 
+  /**
+   * Gets the key of this key's provider.
+   */
+  Key<Provider<T>> providerKey() {
+    return ofType(typeLiteral.providerType());
+  }
+
   public boolean equals(Object o) {
     if (o == this) {
       return true;
@@ -306,6 +313,14 @@
   }
 
   /**
+   * Returns a new key of the specified type with the same annotation as this
+   * key.
+   */
+  <T> Key<T> ofType(TypeLiteral<T> type) {
+    return new SimpleKey<T>(type, annotationStrategy);
+  }
+
+  /**
    * Returns true if this key has annotation attributes.
    * @return
    */
diff --git a/src/com/google/inject/TypeLiteral.java b/src/com/google/inject/TypeLiteral.java
index 6044350..a945a57 100644
--- a/src/com/google/inject/TypeLiteral.java
+++ b/src/com/google/inject/TypeLiteral.java
@@ -131,6 +131,30 @@
     return type;
   }
 
+  /**
+   * Gets the type of this type's provider.
+   */
+  @SuppressWarnings("unchecked")
+  TypeLiteral<Provider<T>> providerType() {
+    final Type[] typeParameters = new Type[] { getType() };
+
+    // This cast is safe and wouldn't generate a warning if Type had a type
+    // parameter.
+    return (TypeLiteral<Provider<T>>) get(new ParameterizedType() {
+      public Type[] getActualTypeArguments() {
+        return typeParameters;
+      }
+
+      public Type getRawType() {
+        return Provider.class;
+      }
+
+      public Type getOwnerType() {
+        return null;
+      }
+    });
+  }
+
   public int hashCode() {
     return this.hashCode;
   }
diff --git a/test/com/google/inject/BindingTest.java b/test/com/google/inject/BindingTest.java
index 545c6ed..f4ec2b7 100644
--- a/test/com/google/inject/BindingTest.java
+++ b/test/com/google/inject/BindingTest.java
@@ -33,6 +33,20 @@
  */
 public class BindingTest extends TestCase {
 
+  public void testProviderBinding() {
+    Injector injector = Guice.createInjector();
+    Binding<Bob> bobBinding = injector.getBinding(Bob.class);
+    assertTrue(bobBinding.getProvider().get() instanceof Bob);
+    Binding<Provider<Bob>> bobProviderBinding = bobBinding.getProviderBinding();
+    assertTrue(bobProviderBinding.getProvider().get().get() instanceof Bob);
+    Binding<Provider<Provider<Bob>>> bobProviderProviderBinding
+        = bobProviderBinding.getProviderBinding();
+    assertTrue(bobProviderProviderBinding.getProvider().get().get().get()
+        instanceof Bob);
+  }
+
+  static class Bob {}
+
   public void testVisitor() {
     MyVisitor myVisitor = new MyVisitor();