Modified scope binding to bind to scope annotations directly. No more strings.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@144 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/AbstractModule.java b/src/com/google/inject/AbstractModule.java
index a9c9209..b0a702e 100644
--- a/src/com/google/inject/AbstractModule.java
+++ b/src/com/google/inject/AbstractModule.java
@@ -72,10 +72,11 @@
   }
 
   /**
-   * @see ContainerBuilder#scope(String, Scope)
+   * @see ContainerBuilder#scope(Class, Scope)
    */
-  protected void scope(String name, Scope scope) {
-    builder.scope(name, scope);
+  protected void scope(Class<? extends Annotation> scopeAnnotation,
+      Scope scope) {
+    builder.scope(scopeAnnotation, scope);
   }
 
   /**
diff --git a/src/com/google/inject/ContainerBuilder.java b/src/com/google/inject/ContainerBuilder.java
index 18e7646..4062186 100644
--- a/src/com/google/inject/ContainerBuilder.java
+++ b/src/com/google/inject/ContainerBuilder.java
@@ -19,9 +19,6 @@
 import com.google.inject.ContainerImpl.Injector;
 import com.google.inject.Key.AnnotationStrategy;
 import static com.google.inject.Scopes.CONTAINER;
-import static com.google.inject.Scopes.CONTAINER_NAME;
-import static com.google.inject.Scopes.DEFAULT;
-import static com.google.inject.Scopes.DEFAULT_NAME;
 import com.google.inject.matcher.Matcher;
 import com.google.inject.spi.Message;
 import com.google.inject.spi.SourceConsumer;
@@ -71,7 +68,8 @@
       = new ArrayList<ConstantBindingBuilder>();
   final List<LinkedBindingBuilder<?>> linkedBindingBuilders
       = new ArrayList<LinkedBindingBuilder<?>>();
-  final Map<String, Scope> scopes = new HashMap<String, Scope>();
+  final Map<Class<? extends Annotation>, Scope> scopes =
+      new HashMap<Class<? extends Annotation>, Scope>();
 
   final List<StaticInjection> staticInjections
       = new ArrayList<StaticInjection>();
@@ -108,8 +106,7 @@
    * Constructs a new builder.
    */
   public ContainerBuilder() {
-    scope(DEFAULT_NAME, DEFAULT);
-    scope(CONTAINER_NAME, CONTAINER);
+    scope(ContainerScoped.class, CONTAINER);
 
     bind(Container.class).to(CONTAINER_FACTORY);
     bind(Logger.class).to(LOGGER_FACTORY);
@@ -141,17 +138,17 @@
   }
 
   /**
-   * Adds a new scope. Maps a {@link Scope} instance to a given scope name.
-   * Scopes should be mapped before used in bindings. {@link Scoped#value()}
-   * references this name.
+   * Adds a new scope. Maps a {@link Scope} instance to a given annotation.
    */
-  public void scope(String name, Scope scope) {
+  public void scope(Class<? extends Annotation> annotationType, Scope scope) {
     ensureNotCreated();
-    if (scopes.containsKey(nonNull(name, "name"))) {
-      addError(source(), ErrorMessages.DUPLICATE_SCOPES, name);
+    Scope existing = scopes.get(nonNull(annotationType, "annotation type"));
+    if (existing != null) {
+      addError(source(), ErrorMessages.DUPLICATE_SCOPES, existing,
+          annotationType, scope);
     }
     else {
-      scopes.put(nonNull(name, "name"), nonNull(scope, "scope"));
+      scopes.put(annotationType, nonNull(scope, "scope"));
     }
   }
 
@@ -505,8 +502,7 @@
     /**
      * Binds to instances of the given implementation class. The
      * {@link Container} will inject the implementation instances as well. Sets
-     * the scope based on the @{@link Scoped} annotation on the implementation
-     * class if present.
+     * the scope based on an annotation on the implementation class if present.
      */
     public <I extends T> BindingBuilder<T> to(Class<I> implementation) {
       return to(TypeLiteral.get(implementation));
@@ -515,8 +511,7 @@
     /**
      * Binds to instances of the given implementation type. The
      * {@link Container} will inject the implementation instances as well. Sets
-     * the scope based on the @{@link Scoped} annotation on the implementation
-     * class if present.
+     * the scope based on an annotation on the implementation class if present.
      */
     public <I extends T> BindingBuilder<T> to(
         final TypeLiteral<I> implementation) {
@@ -590,17 +585,18 @@
     }
 
     /**
-     * Specifies the scope. References the name passed to {@link #scope}.
+     * Specifies the scope. References the annotation passed to {@link
+     * ContainerBuilder#scope(Class, Scope)}.
      */
-    public BindingBuilder<T> in(String scopeName) {
+    public BindingBuilder<T> in(Class<? extends Annotation> scopeAnnotation) {
       ensureScopeNotSet();
 
       // We could defer this lookup to when we create the container, but this
       // is fine for now.
-      this.scope = scopes.get(nonNull(scopeName, "scope name"));
+      this.scope = scopes.get(nonNull(scopeAnnotation, "scope annotation"));
       if (this.scope == null) {
-        errorHandler.handle(ErrorMessages.SCOPE_NOT_FOUND, scopeName,
-            scopes.keySet());
+        errorHandler.handle(ErrorMessages.SCOPE_NOT_FOUND,
+            "@" + scopeAnnotation.getSimpleName());
       }
       return this;
     }
diff --git a/src/com/google/inject/ContainerImpl.java b/src/com/google/inject/ContainerImpl.java
index 00fde7e..c0980e3 100644
--- a/src/com/google/inject/ContainerImpl.java
+++ b/src/com/google/inject/ContainerImpl.java
@@ -88,12 +88,13 @@
   final ConstructionProxyFactory constructionProxyFactory;
   final Map<Key<?>, Binding<?>> bindings;
   final BindingsMultimap bindingsMultimap = new BindingsMultimap();
-  final Map<String, Scope> scopes;
+  final Map<Class<? extends Annotation>, Scope> scopes;
 
   ErrorHandler errorHandler = new InvalidErrorHandler();
 
   ContainerImpl(ConstructionProxyFactory constructionProxyFactory,
-      Map<Key<?>, Binding<?>> bindings, Map<String, Scope> scopes) {
+      Map<Key<?>, Binding<?>> bindings,
+      Map<Class<? extends Annotation>, Scope> scopes) {
     this.constructionProxyFactory = constructionProxyFactory;
     this.bindings = bindings;
     this.scopes = scopes;
@@ -119,7 +120,12 @@
   <T> List<String> getNamesOfBindingAnnotations(TypeLiteral<T> type) {
     List<String> names = new ArrayList<String>();
     for (Binding<T> binding : findBindingsByType(type)) {
-      names.add(binding.getKey().getAnnotationName());
+      Key<T> key = binding.getKey();
+      if (!key.hasAnnotationType()) {
+        names.add("[no annotation]");
+      } else {
+        names.add(key.getAnnotationName());
+      }
     }
     return names;
   }
diff --git a/src/com/google/inject/ContainerScoped.java b/src/com/google/inject/ContainerScoped.java
index 3b86898..49968fe 100644
--- a/src/com/google/inject/ContainerScoped.java
+++ b/src/com/google/inject/ContainerScoped.java
@@ -29,7 +29,4 @@
  */
 @Target(ElementType.TYPE)
 @Retention(RUNTIME)
-@Scoped(Scopes.CONTAINER_NAME)
-public @interface ContainerScoped {
-
-}
+public @interface ContainerScoped {}
diff --git a/src/com/google/inject/ErrorMessages.java b/src/com/google/inject/ErrorMessages.java
index 6454472..7809941 100644
--- a/src/com/google/inject/ErrorMessages.java
+++ b/src/com/google/inject/ErrorMessages.java
@@ -50,6 +50,8 @@
     }
   }
 
+  static final String SCOPE_NOT_FOUND = "No scope is bound to %s.";
+
   static final String SINGLE_INSTANCE_AND_SCOPE = "Setting the scope is not"
       + " permitted when binding to a single instance.";
 
@@ -63,7 +65,8 @@
   static final String TOO_MANY_CONSTRUCTORS = "More than one constructor"
       + " annotated with @Inject found in %s. " + CONSTRUCTOR_RULES;
 
-  static final String DUPLICATE_SCOPES = "A scope named '%s' already exists.";
+  static final String DUPLICATE_SCOPES = "Scope %s is already bound to %s."
+      + " Cannot bind %s.";
 
   static final String MISSING_CONSTANT_VALUE = "Missing constant value. Please"
       + " call to(...).";
@@ -82,17 +85,13 @@
   static final String IMPLEMENTATION_ALREADY_SET = "Implementation is set more"
       + " than once.";
 
-  static final String SCOPE_NOT_FOUND = "Scope named '%s' not found."
-      + " Available scope names: %s";
+  static final String SCOPE_ALREADY_SET = "Scope is set more than once.";
 
-  static final String SCOPE_ALREADY_SET = "Scope is set more than once."
-      + " You can set the scope by calling in(...), by annotating the"
-      + " implementation class with @Scoped, or by annotating the"
-      + " implementation with with an annotation which is annotated with"
-      + " @Scoped.";
+  static final String DUPLICATE_ANNOTATIONS = "Duplicate binding annotations"
+      + " found on %s: %s and %s";
 
-  static final String DUPLICATE_ANNOTATIONS = "Duplicate binding annotations found"
-      + " on %s: %s and %s";
+  public static final String DUPLICATE_SCOPE_ANNOTATIONS = "Duplicate scope"
+      + " annotations found on %s: %s and %s";
 
   static final String CONSTANT_VALUE_ALREADY_SET = "Constant value is set more"
       + " than once.";
diff --git a/src/com/google/inject/Scoped.java b/src/com/google/inject/Scoped.java
deleted file mode 100644
index b6da7a3..0000000
--- a/src/com/google/inject/Scoped.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * 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 java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-import java.lang.annotation.Target;
-
-/**
- * Annotates an implementation class with the name of its scope.
- *
- * <p>If you apply {@code Scoped} to another annotation, that annotation will
- * act as an alias and the user will not have to explicitly specify a name. See
- * {@link ContainerScoped} for an example.
- *
- * @author crazybob
- */
-@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
-@Retention(RUNTIME)
-public @interface Scoped {
-
-  /**
-   * Scope name.
-   */
-  String value();
-}
diff --git a/src/com/google/inject/Scopes.java b/src/com/google/inject/Scopes.java
index d8ee1b5..4f9b876 100644
--- a/src/com/google/inject/Scopes.java
+++ b/src/com/google/inject/Scopes.java
@@ -20,6 +20,7 @@
 import com.google.inject.util.DuplicateAnnotationException;
 
 import java.util.Map;
+import java.lang.annotation.Annotation;
 
 /**
  * Built in scope implementations.
@@ -31,11 +32,6 @@
   private Scopes() {}
 
   /**
-   * Name of the default scope.
-   */
-  public static final String DEFAULT_NAME = "DEFAULT";
-
-  /**
    * The default scope, one instance per injection.
    */
   public static final Scope DEFAULT = new Scope() {
@@ -44,16 +40,11 @@
     }
 
     public String toString() {
-      return DEFAULT_NAME;
+      return "Scopes.DEFAULT";
     }
   };
 
   /**
-   * Name of container scope.
-   */
-  public static final String CONTAINER_NAME = "CONTAINER";
-
-  /**
    * One instance per container.
    */
   public static final Scope CONTAINER = new Scope() {
@@ -88,7 +79,7 @@
     }
 
     public String toString() {
-      return CONTAINER_NAME;
+      return "Scopes.CONTAINER";
     }
   };
 
@@ -101,51 +92,21 @@
    * @param errorHandler handles errors
    */
   static Scope getScopeForType(Class<?> implementation,
-      Map<String, Scope> scopes, ErrorHandler errorHandler) {
-    return getScopeForName(
-        getScopeNameForType(implementation, errorHandler),
-        scopes,
-        errorHandler
-    );
-  }
-
-  /**
-   * Finds scope for the given name. Returns {@code null} if the name is
-   * {@code null}. Otherwise, records an error if a scope isn't found.
-   */
-  static Scope getScopeForName(String name, Map<String, Scope> scopes,
+      Map<Class<? extends Annotation>, Scope> scopes,
       ErrorHandler errorHandler) {
-    // None found.
-    if (name == null) {
-      return null;
+    Scope found = null;
+    for (Annotation annotation : implementation.getAnnotations()) {
+      Scope scope = scopes.get(annotation.annotationType());
+      if (scope != null) {
+        if (found != null) {
+          errorHandler.handle(ErrorMessages.DUPLICATE_SCOPE_ANNOTATIONS,
+              implementation, found, scope);
+        } else {
+          found = scope;
+        }
+      }
     }
-
-    // Look up scope for name.
-    Scope scope = scopes.get(name);
-    if (scope == null) {
-      errorHandler.handle(
-          ErrorMessages.SCOPE_NOT_FOUND, name, scopes.keySet());
-    }
-    return scope;
-  }
-
-  /**
-   * Gets the scope name from annotations on the given type. Records errors if
-   * multiple names are found.
-   */
-  static String getScopeNameForType(Class<?> implementation,
-      ErrorHandler errorHandler) {
-    try {
-      Scoped scoped =
-          SurrogateAnnotations.findAnnotation(Scoped.class, implementation);
-      return scoped == null ? null : scoped.value();
-    } catch (DuplicateAnnotationException e) {
-      // Scope already set.
-      errorHandler.handle(ErrorMessages.DUPLICATE_ANNOTATIONS,
-          Scoped.class.getSimpleName(), implementation, e.getFirst(),
-          e.getSecond());
-      return null;
-    }
+    return found;
   }
 
   /**
diff --git a/src/com/google/inject/servlet/RequestScoped.java b/src/com/google/inject/servlet/RequestScoped.java
index c863fe5..c0d89fd 100644
--- a/src/com/google/inject/servlet/RequestScoped.java
+++ b/src/com/google/inject/servlet/RequestScoped.java
@@ -16,8 +16,6 @@
 
 package com.google.inject.servlet;
 
-import com.google.inject.Scoped;
-
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -30,5 +28,4 @@
  */
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
-@Scoped(ServletScopes.REQUEST_NAME)
 public @interface RequestScoped {}
diff --git a/src/com/google/inject/servlet/ServletModule.java b/src/com/google/inject/servlet/ServletModule.java
index a71b842..4a3ca1b 100644
--- a/src/com/google/inject/servlet/ServletModule.java
+++ b/src/com/google/inject/servlet/ServletModule.java
@@ -42,8 +42,8 @@
 
   protected void configure() {
     // Scopes.
-    scope(REQUEST_NAME, REQUEST);
-    scope(SESSION_NAME, SESSION);
+    scope(RequestScoped.class, REQUEST);
+    scope(SessionScoped.class, SESSION);
 
     // Bind request.
     Factory<HttpServletRequest> requestFactory =
diff --git a/src/com/google/inject/servlet/SessionScoped.java b/src/com/google/inject/servlet/SessionScoped.java
index 2c931e6..8bd679c 100644
--- a/src/com/google/inject/servlet/SessionScoped.java
+++ b/src/com/google/inject/servlet/SessionScoped.java
@@ -16,8 +16,6 @@
 
 package com.google.inject.servlet;
 
-import com.google.inject.Scoped;
-
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -30,5 +28,4 @@
  */
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
-@Scoped(ServletScopes.SESSION_NAME)
 public @interface SessionScoped {}
diff --git a/test/com/google/inject/ErrorHandlingTest.java b/test/com/google/inject/ErrorHandlingTest.java
index b5c4d23..b4e97f9 100644
--- a/test/com/google/inject/ErrorHandlingTest.java
+++ b/test/com/google/inject/ErrorHandlingTest.java
@@ -69,7 +69,8 @@
       bind(Bar.class);
       bind(Tee.class);
       bind(new TypeLiteral<List<String>>() {});
-      bind(String.class).annotatedWith(Names.annotationFor("foo")).in("foo");
+      bind(String.class).annotatedWith(Names.annotationFor("foo")).in(
+          Named.class);
       link(Key.get(Runnable.class)).to(Key.get(Runnable.class));
       requestStaticInjection(ErrorHandlingTest.class);
     }
diff --git a/test/com/google/inject/ScopesTest.java b/test/com/google/inject/ScopesTest.java
index 52dbc9a..114d2d0 100644
--- a/test/com/google/inject/ScopesTest.java
+++ b/test/com/google/inject/ScopesTest.java
@@ -28,47 +28,6 @@
  */
 public class ScopesTest extends TestCase {
 
-  public void testScopedAnnotation() {
-    assertEquals("foo", Scopes.getScopeNameForType(AnnotatedWithScoped.class,
-        InvalidErrorHandler.INSTANCE));
-  }
-
-  @Scoped("foo")
-  static class AnnotatedWithScoped {}
-
-  public void testCustomAnnotation() {
-    assertEquals("custom", Scopes.getScopeNameForType(CustomAnnotated.class,
-        InvalidErrorHandler.INSTANCE));
-  }
-
-  @Target(ElementType.TYPE)
-  @Retention(RetentionPolicy.RUNTIME)
-  @Scoped("custom")
-  @interface CustomScoped {}
-
-  @CustomScoped
-  static class CustomAnnotated {}
-
-  public void testMultipleAnnotations() {
-    final String[] messageHolder = new String[1];
-    Scopes.getScopeNameForType(MultiplyAnnotated.class,
-        new AbstractErrorHandler() {
-          public void handle(String message) {
-            messageHolder[0] = message;
-          }
-
-          public void handle(Throwable t) {
-            fail();
-          }
-        });
-    String message = messageHolder[0];
-    assertNotNull(messageHolder[0]);
-  }
-
-  @Scoped("foo")
-  @CustomScoped
-  static class MultiplyAnnotated {}
-
   public void testContainerScopedAnnotation()
       throws ContainerCreationException {
     ContainerBuilder builder = new ContainerBuilder();