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();