Added support for @DefaultImplementation and @DefaulProvider.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@251 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/BindingBuilderImpl.java b/src/com/google/inject/BindingBuilderImpl.java
index e760af5..7299d59 100644
--- a/src/com/google/inject/BindingBuilderImpl.java
+++ b/src/com/google/inject/BindingBuilderImpl.java
@@ -245,40 +245,6 @@
     return this.scope == Scopes.SINGLETON;
   }
 
-  /**
-   * Delegates to a custom factory which is also bound in the injector.
-   */
-  private static class BoundProviderFactory<T>
-      implements InternalFactory<T>, CreationListener {
-
-    final Key<? extends Provider<? extends T>> providerKey;
-    final Object source;
-    private InternalFactory<? extends Provider<? extends T>> providerFactory;
-
-    public BoundProviderFactory(
-        Key<? extends Provider<? extends T>> providerKey,
-        Object source) {
-      this.providerKey = providerKey;
-      this.source = source;
-    }
-
-    public void notify(final InjectorImpl injector) {
-      injector.withDefaultSource(source, new Runnable() {
-        public void run() {
-          providerFactory = injector.getInternalFactory(null, providerKey);
-        }
-      });
-    }
-
-    public String toString() {
-      return providerKey.toString();
-    }
-
-    public T get(InternalContext context) {
-      return providerFactory.get(context).get();
-    }
-  }
-
   void registerInstanceForInjection(final Object o) {
     binder.creationListeners.add(new CreationListener() {
       public void notify(InjectorImpl injector) {
diff --git a/src/com/google/inject/BoundProviderFactory.java b/src/com/google/inject/BoundProviderFactory.java
new file mode 100644
index 0000000..aa89f98
--- /dev/null
+++ b/src/com/google/inject/BoundProviderFactory.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (C) 2006 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject;
+
+import com.google.inject.BinderImpl.CreationListener;
+
+/**
+ * Delegates to a custom factory which is also bound in the injector.
+ */
+class BoundProviderFactory<T>
+    implements InternalFactory<T>, CreationListener {
+
+  final Key<? extends Provider<? extends T>> providerKey;
+  final Object source;
+  private InternalFactory<? extends Provider<? extends T>> providerFactory;
+
+  BoundProviderFactory(
+      Key<? extends Provider<? extends T>> providerKey,
+      Object source) {
+    this.providerKey = providerKey;
+    this.source = source;
+  }
+
+  BoundProviderFactory(
+      Key<? extends Provider<? extends T>> providerKey,
+      InternalFactory<? extends Provider<? extends T>> providerFactory,
+      Object source) {
+    this.providerKey = providerKey;
+    this.providerFactory = providerFactory;
+    this.source = source;
+  }
+
+  public void notify(final InjectorImpl injector) {
+    injector.withDefaultSource(source, new Runnable() {
+      public void run() {
+        providerFactory = injector.getInternalFactory(null, providerKey);
+      }
+    });
+  }
+
+  public String toString() {
+    return providerKey.toString();
+  }
+
+  public T get(InternalContext context) {
+    return providerFactory.get(context).get();
+  }
+}
diff --git a/src/com/google/inject/DefaultImplementation.java b/src/com/google/inject/DefaultImplementation.java
index 7191bf7..31bea9c 100644
--- a/src/com/google/inject/DefaultImplementation.java
+++ b/src/com/google/inject/DefaultImplementation.java
@@ -16,11 +16,18 @@
 
 package com.google.inject;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
 /**
  * A pointer to the default implementation of a type.
  *
  * @author crazybob@google.com (Bob Lee)
  */
+@Retention(RUNTIME)
+@Target(TYPE)
 public @interface DefaultImplementation {
 
   /**
diff --git a/src/com/google/inject/DefaultProvider.java b/src/com/google/inject/DefaultProvider.java
index 7128ed2..7d72b5f 100644
--- a/src/com/google/inject/DefaultProvider.java
+++ b/src/com/google/inject/DefaultProvider.java
@@ -16,15 +16,22 @@
 
 package com.google.inject;
 
+import static java.lang.annotation.ElementType.TYPE;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+
 /**
- * A pointer to the default provider implementation for a type.
+ * A pointer to the default provider  for a type.
  *
  * @author crazybob@google.com (Bob Lee)
  */
+@Retention(RUNTIME)
+@Target(TYPE)
 public @interface DefaultProvider {
 
   /**
    * The implementation type.
    */
-  Class<?> value();
+  Class<? extends Provider<?>> value();
 }
diff --git a/src/com/google/inject/ErrorMessages.java b/src/com/google/inject/ErrorMessages.java
index 6c6e229..1bfd9e0 100644
--- a/src/com/google/inject/ErrorMessages.java
+++ b/src/com/google/inject/ErrorMessages.java
@@ -52,6 +52,17 @@
     }
   }
 
+  static final String SUBTYPE_NOT_PROVIDED
+      = "%s doesn't provide instances of %s.";
+
+  static final String NOT_A_SUBTYPE = "%s doesn't extend %s.";
+
+  static final String RECURSIVE_IMPLEMENTATION_TYPE = "@DefaultImplementation"
+      + " points to the same class it annotates.";
+
+  static final String RECURSIVE_PROVIDER_TYPE = "@DefaultProvider"
+      + " points to the same class it annotates.";
+
   static final String ERROR_INJECTING_MEMBERS = "An error of type %s occurred"
       + " while injecting members of %s. Error message: %s";
 
@@ -109,8 +120,9 @@
 
   static final String LINK_DESTINATION_NOT_FOUND = "Binding to %s not found.";
 
-  static final String CANNOT_INJECT_INTERFACE = "Injecting into interfaces is"
-      + " not supported. Please use a concrete type instead of %s.";
+  static final String CANNOT_INJECT_ABSTRACT_TYPE =
+      "Injecting into abstract types is not supported. Please use a concrete"
+          + " type instead of %s.";
 
   static final String ANNOTATION_ALREADY_SPECIFIED =
       "More than one annotation type is specified for this binding.";
@@ -142,7 +154,7 @@
       + " instance%n  bound to %s%n  for member at %s";
   
   static final String NULL_PROVIDED = "Null value returned by custom provider"
-      + " at %s";
+      + " bound at %s";
 
   static Object convert(Object o) {
     for (Converter<?> converter : converters) {
diff --git a/src/com/google/inject/Guice.java b/src/com/google/inject/Guice.java
index f06e5e3..f221605 100644
--- a/src/com/google/inject/Guice.java
+++ b/src/com/google/inject/Guice.java
@@ -27,6 +27,20 @@
   private Guice() {}
 
   /**
+   * Creates an injector with no explicit bindings.
+   */
+  public static Injector createInjector() {
+    try {
+      return createInjector(new Module() {
+        public void configure(Binder binder) {}
+      });
+    }
+    catch (CreationException e) {
+      throw new AssertionError(e);
+    }
+  }
+
+  /**
    * Creates an injector for the given set of modules.
    */
   public static Injector createInjector(Module... modules)
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index 2f0ef89..cc04d95 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -261,11 +261,9 @@
       }
     }
 
-    // Don't try to inject primitives, arrays, enums, interfaces or abstract
-    // classes.
+    // Don't try to inject primitives, arrays, or enums.
     int modifiers = rawType.getModifiers();
-    if (rawType.isArray() || rawType.isEnum()
-        || Modifier.isAbstract(modifiers) || rawType.isPrimitive()) {
+    if (rawType.isArray() || rawType.isEnum() || rawType.isPrimitive()) {
       // Look for a factory bound to a key without annotation attributes if
       // necessary.
       if (key.hasAttributes()) {
@@ -299,14 +297,15 @@
       };
       try {
         // note: intelliJ thinks this cast is superfluous, but is it?
-        return (InternalFactory<? extends T>) getImplicitBinding(rawType);
+        return (InternalFactory<? extends T>) getImplicitBinding(member,
+            rawType);
       }
       finally {
         this.errorHandler = previous;
       }
     }
     // note: intelliJ thinks this cast is superfluous, but is it?
-    return (InternalFactory<? extends T>) getImplicitBinding(rawType);
+    return (InternalFactory<? extends T>) getImplicitBinding(member, rawType);
   }
 
   private <T> InternalFactory<T> handleConstantConversionError(
@@ -594,7 +593,7 @@
     protected ConstructorInjector<?> create(Class<?> implementation) {
       if (implementation.isInterface()) {
         errorHandler.handle(defaultSource,
-            ErrorMessages.CANNOT_INJECT_INTERFACE, implementation);
+            ErrorMessages.CANNOT_INJECT_ABSTRACT_TYPE, implementation);
         return ConstructorInjector.invalidConstructor();
       }
 
@@ -873,7 +872,72 @@
    * was not made. Uses synchronization here so it's not necessary in the
    * factory itself. Returns {@code null} if the type isn't injectable.
    */
-  <T> InternalFactory<? extends T> getImplicitBinding(Class<T> type) {
+  <T> InternalFactory<? extends T> getImplicitBinding(Member member,
+      final Class<T> type) {
+    // Look for @DefaultImplementation.
+    DefaultImplementation defaultImplementation =
+        type.getAnnotation(DefaultImplementation.class);
+    if (defaultImplementation != null) {
+      Class<?> implementationType = defaultImplementation.value();
+
+      // Make sure it's not the same type. TODO: Can we check for deeper loops?
+      if (implementationType == type) {
+        errorHandler.handle(StackTraceElements.forType(type),
+            ErrorMessages.RECURSIVE_IMPLEMENTATION_TYPE, type);
+        return invalidFactory();
+      }
+
+      // Make sure implementationType extends type.
+      if (!type.isAssignableFrom(implementationType)) {
+        errorHandler.handle(StackTraceElements.forType(type),
+            ErrorMessages.NOT_A_SUBTYPE, implementationType, type);
+        return invalidFactory();
+      }
+
+      return (InternalFactory<T>) getInternalFactory(
+          member, Key.get(implementationType));      
+    }
+
+    // Look for @DefaultProvider.
+    DefaultProvider defaultProvider = type.getAnnotation(DefaultProvider.class);
+    if (defaultProvider != null) {
+      final Class<? extends Provider<?>> providerType = defaultProvider.value();
+
+      // Make sure it's not the same type. TODO: Can we check for deeper loops?
+      if (providerType == type) {
+        errorHandler.handle(StackTraceElements.forType(type),
+            ErrorMessages.RECURSIVE_PROVIDER_TYPE, type);
+        return invalidFactory();
+      }
+
+      // TODO: Make sure the provided type extends type. We at least check
+      // the type at runtime below.
+
+      InternalFactory<? extends Provider<?>> providerFactory
+          = getInternalFactory(member, Key.get(providerType));
+      Key<? extends Provider<?>> providerKey = Key.get(providerType);
+      return (InternalFactory<T>) new BoundProviderFactory(
+          providerKey, providerFactory, StackTraceElements.forType(type)) {
+        public Object get(InternalContext context) {
+          Object o = super.get(context);
+          try {
+            return type.cast(o);
+          } catch (ClassCastException e) {
+            errorHandler.handle(StackTraceElements.forType(type),
+                ErrorMessages.SUBTYPE_NOT_PROVIDED, providerType, type);
+            throw new AssertionError();
+          }
+        }
+      };
+    }
+
+    // TODO: Method interceptors could actually enable us to implement
+    // abstract types. Should we remove this restriction?
+    if (Modifier.isAbstract(type.getModifiers())) {
+      return null;
+    }
+
+    // Inject the class itself.
     synchronized (implicitBindings) {
       @SuppressWarnings("unchecked")
       InternalFactory<T> factory =
@@ -919,11 +983,11 @@
 
   static class ImplicitBinding<T> implements InternalFactory<T> {
 
-    final Class<T> type;
+    final Class<T> implementation;
     ConstructorInjector<T> constructorInjector;
 
-    ImplicitBinding(Class<T> type) {
-      this.type = type;
+    ImplicitBinding(Class<T> implementation) {
+      this.implementation = implementation;
     }
 
     void setConstructorInjector(
@@ -932,7 +996,7 @@
     }
 
     public T get(InternalContext context) {
-      return constructorInjector.construct(context, type);
+      return constructorInjector.construct(context, implementation);
     }
   }