Added support for binding to annotations instead of names.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@142 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/ConstantConversionException.java b/src/com/google/inject/ConstantConversionException.java
index ff170b5..c90def4 100644
--- a/src/com/google/inject/ConstantConversionException.java
+++ b/src/com/google/inject/ConstantConversionException.java
@@ -37,15 +37,19 @@
static String createMessage(String value, Key<?> key, Member member,
String reason) {
+ String annotationMessage = key.hasAnnotationType()
+ ? " annotated with " + key.getAnnotationName()
+ : "";
+
return member == null
? "Error converting '" + value + "' to "
+ key.getRawType().getSimpleName()
- + " while getting dependency named '" + key.getName()
- + "'. Reason: " + reason
+ + " while getting binding value" + annotationMessage
+ + ". Reason: " + reason
: "Error converting '" + value + "' to "
+ key.getRawType().getSimpleName() + " while injecting "
- + member.getName() + " with dependency named '" + key.getName()
- + "' in " + member.getDeclaringClass().getSimpleName()
+ + member.getName() + " with binding value" + annotationMessage
+ + " required by " + member.getDeclaringClass().getSimpleName()
+ ". Reason: " + reason;
}
}
diff --git a/src/com/google/inject/ConstructorInjector.java b/src/com/google/inject/ConstructorInjector.java
index d36ea31..6e3b0ea 100644
--- a/src/com/google/inject/ConstructorInjector.java
+++ b/src/com/google/inject/ConstructorInjector.java
@@ -16,9 +16,6 @@
package com.google.inject;
-import com.google.inject.util.SurrogateAnnotations;
-import com.google.inject.util.DuplicateAnnotationException;
-
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -34,9 +31,6 @@
final ContainerImpl.ParameterInjector<?>[] parameterInjectors;
final ConstructionProxy<T> constructionProxy;
- /** Annotation on the constructor. */
- Inject inject;
-
ConstructorInjector(ContainerImpl container, Class<T> implementation) {
this.implementation = implementation;
Constructor<T> constructor = findConstructorIn(container, implementation);
@@ -59,13 +53,12 @@
ContainerImpl.ParameterInjector<?>[] createParameterInjector(
ContainerImpl container, Constructor<T> constructor) {
try {
- return inject == null
+ return constructor.getParameterTypes().length == 0
? null // default constructor.
: container.getParametersInjectors(
constructor,
constructor.getParameterAnnotations(),
- constructor.getGenericParameterTypes(),
- inject.value()
+ constructor.getGenericParameterTypes()
);
}
catch (ContainerImpl.MissingDependencyException e) {
@@ -77,27 +70,17 @@
private Constructor<T> findConstructorIn(ContainerImpl container,
Class<T> implementation) {
Constructor<T> found = null;
- @SuppressWarnings("unchecked") // why doesn't it return the right thing?
+ @SuppressWarnings("unchecked")
Constructor<T>[] constructors
= (Constructor<T>[]) implementation.getDeclaredConstructors();
for (Constructor<T> constructor : constructors) {
- Inject inject = null;
- try {
- inject = SurrogateAnnotations.findAnnotation(Inject.class, constructor);
- } catch (DuplicateAnnotationException e) {
- container.errorHandler.handle(ErrorMessages.DUPLICATE_ANNOTATIONS,
- Inject.class.getSimpleName(), constructor, e.getFirst(),
- e.getSecond());
- }
-
- if (inject != null) {
+ if (constructor.getAnnotation(Inject.class) != null) {
if (found != null) {
container.errorHandler.handle(
ErrorMessages.TOO_MANY_CONSTRUCTORS, implementation);
return ContainerImpl.invalidConstructor();
}
found = constructor;
- this.inject = inject;
}
}
if (found != null) {
diff --git a/src/com/google/inject/Container.java b/src/com/google/inject/Container.java
index ac3302a..a7113ff 100644
--- a/src/com/google/inject/Container.java
+++ b/src/com/google/inject/Container.java
@@ -18,51 +18,11 @@
import java.util.List;
import java.util.Map;
+import java.lang.annotation.Annotation;
/**
* Injects dependencies into constructors, methods and fields annotated with
- * @{@link Inject}.
- *
- * <p>When injecting a method or constructor, you can additionally annotate its
- * parameters with @{@link Inject} and specify a dependency name. When a
- * parameter has no annotation, the container uses the name from the method or
- * constructor's @{@link Inject} annotation respectively.
- *
- * <p>For example:
- *
- * <pre>
- * class Foo {
- *
- * // Inject the int constant named "i".
- * @Inject("i") int i;
- *
- * // Inject the default implementation of Bar and the String constant
- * // named "s".
- * @Inject Foo(Bar bar, @Inject("s") String s) {
- * ...
- * }
- *
- * // Inject the default implementation of Baz and the Bob implementation
- * // named "foo".
- * @Inject void initialize(Baz baz, @Inject("foo") Bob bob) {
- * ...
- * }
- *
- * // Inject the default implementation of Tee.
- * @Inject void setTee(Tee tee) {
- * ...
- * }
- * }
- * </pre>
- *
- * <p>To get an instance of {@code Foo}:
- *
- * <pre>
- * Container c = ...;
- * Key<Foo> fooKey = Key.get(Foo.class);
- * Factory<Foo> fooFactory = c.getFactory(fooKey);
- * Foo foo = fooFactory.get();
- * </pre>
+ * {@code @}{@link Inject}. Provides access to {@link Binding}s.
*
* @author crazybob@google.com (Bob Lee)
* @see ContainerBuilder
@@ -120,22 +80,50 @@
<T> T getInstance(Key<T> key);
/**
- * Gets an instance from the factory bound to the given type and name.
+ * Gets an instance from the factory bound to the given type and annotation.
*/
- <T> T getInstance(TypeLiteral<T> type, String name);
+ <T> T getInstance(TypeLiteral<T> type,
+ Annotation annotation);
/**
- * Gets an instance from the factory bound to the given type and name.
+ * Gets an instance from the factory bound to the given type and annotation.
*/
- <T> T getInstance(Class<T> type, String name);
+ <T> T getInstance(Class<T> type,
+ Annotation annotation);
/**
- * Gets the factory bound to the given type and name.
+ * Gets the factory bound to the given type and annotation.
*/
- <T> Factory<T> getFactory(Class<T> type, String name);
+ <T> Factory<T> getFactory(Class<T> type,
+ Annotation annotation);
/**
- * Gets the factory bound to the given type and name.
+ * Gets the factory bound to the given type and annotation.
*/
- <T> Factory<T> getFactory(TypeLiteral<T> type, String name);
+ <T> Factory<T> getFactory(TypeLiteral<T> type,
+ Annotation annotation);
+
+ /**
+ * Gets an instance from the factory bound to the given type and annotation.
+ */
+ <T> T getInstance(TypeLiteral<T> type,
+ Class<? extends Annotation> annotationType);
+
+ /**
+ * Gets an instance from the factory bound to the given type and annotation.
+ */
+ <T> T getInstance(Class<T> type,
+ Class<? extends Annotation> annotationType);
+
+ /**
+ * Gets the factory bound to the given type and annotation.
+ */
+ <T> Factory<T> getFactory(Class<T> type,
+ Class<? extends Annotation> annotationType);
+
+ /**
+ * Gets the factory bound to the given type and annotation.
+ */
+ <T> Factory<T> getFactory(TypeLiteral<T> type,
+ Class<? extends Annotation> annotationType);
}
diff --git a/src/com/google/inject/ContainerBuilder.java b/src/com/google/inject/ContainerBuilder.java
index 0aca683..7dbff74 100644
--- a/src/com/google/inject/ContainerBuilder.java
+++ b/src/com/google/inject/ContainerBuilder.java
@@ -17,6 +17,7 @@
package com.google.inject;
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;
@@ -24,13 +25,10 @@
import com.google.inject.matcher.Matcher;
import com.google.inject.spi.Message;
import com.google.inject.spi.SourceConsumer;
-import com.google.inject.util.Objects;
import static com.google.inject.util.Objects.nonNull;
import com.google.inject.util.Stopwatch;
import com.google.inject.util.ToStringBuilder;
-
-import org.aopalliance.intercept.MethodInterceptor;
-
+import java.lang.annotation.Annotation;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -40,6 +38,7 @@
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;
+import org.aopalliance.intercept.MethodInterceptor;
/**
* Builds a dependency injection {@link Container}. Binds {@link Key}s to
@@ -197,15 +196,33 @@
*/
public ConstantBindingBuilder bind(String name) {
ensureNotCreated();
- return bind(name, source());
+ return bind(source(), Key.strategyFor(new NamedImpl(name)));
+ }
+
+ /**
+ * Binds a constant to the given annotation.
+ */
+ public ConstantBindingBuilder bind(Annotation annotation) {
+ ensureNotCreated();
+ return bind(source(), Key.strategyFor(annotation));
+ }
+
+ /**
+ * Binds a constant to the given annotation type.
+ */
+ public ConstantBindingBuilder bind(
+ Class<? extends Annotation> annotationType) {
+ ensureNotCreated();
+ return bind(source(), Key.strategyFor(annotationType));
}
/**
* Binds a constant to the given name from the given source.
*/
- private ConstantBindingBuilder bind(String name, Object source) {
+ private ConstantBindingBuilder bind(Object source,
+ AnnotationStrategy annotationStrategy) {
ConstantBindingBuilder builder =
- new ConstantBindingBuilder(Objects.nonNull(name, "name")).from(source);
+ new ConstantBindingBuilder(annotationStrategy).from(source);
constantBindingBuilders.add(builder);
return builder;
}
@@ -219,7 +236,7 @@
for (Map.Entry<String, String> entry : properties.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
- bind(key, source).to(value);
+ bind(source, Key.strategyFor(new NamedImpl(key))).to(value);
}
}
@@ -232,7 +249,7 @@
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
String key = (String) entry.getKey();
String value = (String) entry.getValue();
- bind(key, source).to(value);
+ bind(source, Key.strategyFor(new NamedImpl(key))).to(value);
}
}
@@ -495,13 +512,34 @@
}
/**
- * Sets the name of this binding.
+ * Binds to injection points annotated with {@code @Named(name)}.
*/
public BindingBuilder<T> named(String name) {
- if (!this.key.hasDefaultName()) {
- errorHandler.handle(ErrorMessages.NAME_ALREADY_SET);
+ return annotatedWith(new NamedImpl(nonNull(name, "name")));
+ }
+
+ /**
+ * Specifies the annotation type for this binding.
+ */
+ public BindingBuilder<T> annotatedWith(
+ Class<? extends Annotation> annotationType) {
+ if (this.key.hasAnnotationType()) {
+ errorHandler.handle(ErrorMessages.ANNOTATION_ALREADY_SPECIFIED);
+ } else {
+ this.key = Key.get(this.key.getType(), annotationType);
}
- this.key = this.key.named(name);
+ return this;
+ }
+
+ /**
+ * Specifies an annotation for this binding.
+ */
+ public BindingBuilder<T> annotatedWith(Annotation annotation) {
+ if (this.key.hasAnnotationType()) {
+ errorHandler.handle(ErrorMessages.ANNOTATION_ALREADY_SPECIFIED);
+ } else {
+ this.key = Key.get(this.key.getType(), annotation);
+ }
return this;
}
@@ -704,20 +742,22 @@
}
private static class BindingInfo<T> {
+
final Class<T> type;
final T value;
- final String name;
+ final AnnotationStrategy annotationStrategy;
final Object source;
- BindingInfo(Class<T> type, T value, String name, Object source) {
+ BindingInfo(Class<T> type, T value,
+ AnnotationStrategy annotationStrategy, Object source) {
this.type = type;
this.value = value;
- this.name = name;
+ this.annotationStrategy = annotationStrategy;
this.source = source;
}
Binding<T> createBinding(ContainerImpl container) {
- Key<T> key = Key.get(type, name);
+ Key<T> key = Key.get(type, annotationStrategy);
ConstantFactory<T> factory = new ConstantFactory<T>(value);
return Binding.newInstance(container, key, source, factory);
}
@@ -729,11 +769,11 @@
public class ConstantBindingBuilder {
BindingInfo<?> bindingInfo;
- final String name;
+ final AnnotationStrategy annotationStrategy;
Object source = ContainerBuilder.UNKNOWN_SOURCE;
- ConstantBindingBuilder(String name) {
- this.name = name;
+ ConstantBindingBuilder(AnnotationStrategy annotationStrategy) {
+ this.annotationStrategy = annotationStrategy;
}
boolean hasValue() {
@@ -826,8 +866,8 @@
if (this.bindingInfo != null) {
addError(source, ErrorMessages.CONSTANT_VALUE_ALREADY_SET);
} else {
- // TODO: we're sure name and source will have been set already, right?
- this.bindingInfo = new BindingInfo<T>(type, value, name, source);
+ this.bindingInfo
+ = new BindingInfo<T>(type, value, annotationStrategy, source);
}
}
diff --git a/src/com/google/inject/ContainerImpl.java b/src/com/google/inject/ContainerImpl.java
index 7f49d68..00fde7e 100644
--- a/src/com/google/inject/ContainerImpl.java
+++ b/src/com/google/inject/ContainerImpl.java
@@ -116,10 +116,10 @@
return bindingsMultimap.getAll(type);
}
- <T> List<String> getNamesOfBindingsTo(TypeLiteral<T> type) {
+ <T> List<String> getNamesOfBindingAnnotations(TypeLiteral<T> type) {
List<String> names = new ArrayList<String>();
for (Binding<T> binding : findBindingsByType(type)) {
- names.add(binding.getKey().getName());
+ names.add(binding.getKey().getAnnotationName());
}
return names;
}
@@ -160,8 +160,7 @@
= ((ParameterizedType) factoryType).getActualTypeArguments()[0];
try {
- final Factory<?> factory
- = getFactory(Key.get(entryType, key.getName()));
+ final Factory<?> factory = getFactory(key.ofType(entryType));
return new InternalFactory<T>() {
@SuppressWarnings("unchecked")
public T get(InternalContext context) {
@@ -171,7 +170,7 @@
}
catch (ConfigurationException e) {
ErrorMessages.handleMissingBinding(errorHandler, member, key,
- getNamesOfBindingsTo(key.getType()));
+ getNamesOfBindingAnnotations(key.getType()));
return invalidFactory();
}
}
@@ -181,7 +180,7 @@
= PRIMITIVE_COUNTERPARTS.get(rawType);
if (primitiveCounterpart != null) {
Binding<?> counterpartBinding
- = getBinding(Key.get(primitiveCounterpart, key.getName()));
+ = getBinding(key.ofType(primitiveCounterpart));
if (counterpartBinding != null) {
return (InternalFactory<? extends T>)
counterpartBinding.getInternalFactory();
@@ -189,8 +188,8 @@
}
// Can we convert from a String constant?
- Binding<String> stringBinding
- = getBinding(Key.get(String.class, key.getName()));
+ Key<String> stringKey = key.ofType(String.class);
+ Binding<String> stringBinding = getBinding(stringKey);
if (stringBinding != null && stringBinding.isConstant()) {
// We don't need do pass in an InternalContext because we know this is
// a ConstantFactory which will not use it.
@@ -313,9 +312,9 @@
List<Injector> injectors) {
addInjectorsForMembers(Arrays.asList(methods), statics, injectors,
new InjectorFactory<Method>() {
- public Injector create(ContainerImpl container, Method method,
- String name) throws MissingDependencyException {
- return new MethodInjector(container, method, name);
+ public Injector create(ContainerImpl container, Method method)
+ throws MissingDependencyException {
+ return new MethodInjector(container, method);
}
});
}
@@ -324,9 +323,9 @@
List<Injector> injectors) {
addInjectorsForMembers(Arrays.asList(fields), statics, injectors,
new InjectorFactory<Field>() {
- public Injector create(ContainerImpl container, Field field,
- String name) throws MissingDependencyException {
- return new FieldInjector(container, field, name);
+ public Injector create(ContainerImpl container, Field field)
+ throws MissingDependencyException {
+ return new FieldInjector(container, field);
}
});
}
@@ -347,10 +346,10 @@
if (inject != null) {
try {
- injectors.add(injectorFactory.create(this, member, inject.value()));
+ injectors.add(injectorFactory.create(this, member));
}
catch (MissingDependencyException e) {
- if (inject.required()) {
+ if (!inject.optional()) {
// TODO: Report errors for more than one parameter per member.
e.handle(errorHandler);
}
@@ -375,7 +374,7 @@
}
interface InjectorFactory<M extends Member & AnnotatedElement> {
- Injector create(ContainerImpl container, M member, String name)
+ Injector create(ContainerImpl container, M member)
throws MissingDependencyException;
}
@@ -415,14 +414,15 @@
final InternalFactory<?> factory;
final ExternalContext<?> externalContext;
- public FieldInjector(ContainerImpl container, Field field, String name)
+ public FieldInjector(ContainerImpl container, Field field)
throws MissingDependencyException {
this.field = field;
// Ewwwww...
field.setAccessible(true);
- Key<?> key = Key.get(field.getGenericType(), name);
+ Key<?> key = Key.get(
+ field.getGenericType(), field, field.getAnnotations(), errorHandler);
factory = container.getFactory(field, key);
if (factory == null) {
throw new MissingDependencyException(key, field);
@@ -435,7 +435,8 @@
ExternalContext<?> previous = context.getExternalContext();
context.setExternalContext(externalContext);
try {
- field.set(o, factory.get(context));
+ Object value = factory.get(context);
+ field.set(o, value);
}
catch (IllegalAccessException e) {
throw new AssertionError(e);
@@ -456,17 +457,8 @@
*/
<M extends AccessibleObject & Member>
ParameterInjector<?>[] getParametersInjectors(M member,
- Annotation[][] annotations, Type[] parameterTypes, String defaultName)
+ Annotation[][] annotations, Type[] parameterTypes)
throws MissingDependencyException {
- boolean defaultNameOverridden = !defaultName.equals(Key.DEFAULT_NAME);
-
- // We only carry over the name from the member level annotation to the
- // parameters if there's only one parameter.
- if (parameterTypes.length != 1 && defaultNameOverridden) {
- errorHandler.handle(
- ErrorMessages.NAME_ON_MEMBER_WITH_MULTIPLE_PARAMS, member);
- }
-
ParameterInjector<?>[] parameterInjectors
= new ParameterInjector<?>[parameterTypes.length];
Iterator<Annotation[]> annotationsIterator
@@ -474,29 +466,8 @@
int index = 0;
for (Type parameterType : parameterTypes) {
Annotation[] parameterAnnotations = annotationsIterator.next();
- Inject inject = null;
- try {
- inject = SurrogateAnnotations.findAnnotation(Inject.class,
- parameterAnnotations);
- } catch (DuplicateAnnotationException e) {
- errorHandler.handle(ErrorMessages.DUPLICATE_ANNOTATIONS,
- Inject.class.getSimpleName(), member, e.getFirst(),
- e.getSecond());
- }
-
- String name;
- if (defaultNameOverridden) {
- name = defaultName;
- if (inject != null) {
- errorHandler.handle(
- ErrorMessages.NAME_ON_MEMBER_AND_PARAMETER, member);
- }
- }
- else {
- name = inject == null ? defaultName : inject.value();
- }
-
- Key<?> key = Key.get(parameterType, name);
+ Key<?> key = Key.get(
+ parameterType, member, parameterAnnotations, errorHandler);
parameterInjectors[index] = createParameterInjector(key, member, index);
index++;
}
@@ -521,14 +492,14 @@
final FastMethod fastMethod;
final ParameterInjector<?>[] parameterInjectors;
- public MethodInjector(ContainerImpl container, Method method, String name)
+ public MethodInjector(ContainerImpl container, Method method)
throws MissingDependencyException {
FastClass fastClass = GuiceFastClass.create(method.getDeclaringClass());
this.fastMethod = fastClass.getMethod(method);
Type[] parameterTypes = method.getGenericParameterTypes();
parameterInjectors = parameterTypes.length > 0
? container.getParametersInjectors(
- method, method.getParameterAnnotations(), parameterTypes, name)
+ method, method.getParameterAnnotations(), parameterTypes)
: null;
}
@@ -632,20 +603,44 @@
});
}
- public <T> T getInstance(TypeLiteral<T> type, String name) {
- return getFactory(Key.get(type, name)).get();
+ public <T> T getInstance(TypeLiteral<T> type,
+ Annotation annotation) {
+ return getFactory(Key.get(type, annotation)).get();
}
- public <T> T getInstance(Class<T> type, String name) {
- return getFactory(Key.get(type, name)).get();
+ public <T> T getInstance(Class<T> type,
+ Annotation annotation) {
+ return getFactory(Key.get(type, annotation)).get();
}
- public <T> Factory<T> getFactory(Class<T> type, String name) {
- return getFactory(Key.get(type, name));
+ public <T> Factory<T> getFactory(Class<T> type,
+ Annotation annotation) {
+ return getFactory(Key.get(type, annotation));
}
- public <T> Factory<T> getFactory(TypeLiteral<T> type, String name) {
- return getFactory(Key.get(type, name));
+ public <T> Factory<T> getFactory(TypeLiteral<T> type,
+ Annotation annotation) {
+ return getFactory(Key.get(type, annotation));
+ }
+
+ public <T> T getInstance(TypeLiteral<T> type,
+ Class<? extends Annotation> annotationType) {
+ return getFactory(Key.get(type, annotationType)).get();
+ }
+
+ public <T> T getInstance(Class<T> type,
+ Class<? extends Annotation> annotationType) {
+ return getFactory(Key.get(type, annotationType)).get();
+ }
+
+ public <T> Factory<T> getFactory(Class<T> type,
+ Class<? extends Annotation> annotationType) {
+ return getFactory(Key.get(type, annotationType));
+ }
+
+ public <T> Factory<T> getFactory(TypeLiteral<T> type,
+ Class<? extends Annotation> annotationType) {
+ return getFactory(Key.get(type, annotationType));
}
public <T> T getInstance(TypeLiteral<T> type) {
@@ -756,7 +751,7 @@
void handle(ErrorHandler errorHandler) {
ErrorMessages.handleMissingBinding(errorHandler, member, key,
- getNamesOfBindingsTo(key.getType()));
+ getNamesOfBindingAnnotations(key.getType()));
}
}
diff --git a/src/com/google/inject/ErrorMessages.java b/src/com/google/inject/ErrorMessages.java
index 90ddd7c..6454472 100644
--- a/src/com/google/inject/ErrorMessages.java
+++ b/src/com/google/inject/ErrorMessages.java
@@ -36,8 +36,8 @@
+ " type were found.";
private static final String MISSING_BINDING_BUT_OTHERS_EXIST =
- "Binding to %s not found, but %s requires it. Names of other"
- + " bindings to that type: %s";
+ "Binding to %s not found, but %s requires it. Annotations on other"
+ + " bindings to that type include: %s";
static void handleMissingBinding(ErrorHandler errorHandler, Member member,
Key<?> key, List<String> otherNames) {
@@ -76,7 +76,8 @@
static final String CANNOT_INJECT_INTERFACE = "Injecting into interfaces is"
+ " not supported. Please use a concrete type instead of %s.";
- static final String NAME_ALREADY_SET = "Binding name is set more than once.";
+ static final String ANNOTATION_ALREADY_SPECIFIED =
+ "More than one annotation type is specified for this binding.";
static final String IMPLEMENTATION_ALREADY_SET = "Implementation is set more"
+ " than once.";
@@ -90,7 +91,7 @@
+ " implementation with with an annotation which is annotated with"
+ " @Scoped.";
- static final String DUPLICATE_ANNOTATIONS = "Duplicate @%s annotations found"
+ static final String DUPLICATE_ANNOTATIONS = "Duplicate binding annotations found"
+ " on %s: %s and %s";
static final String CONSTANT_VALUE_ALREADY_SET = "Constant value is set more"
@@ -147,9 +148,9 @@
},
new Converter<Key>(Key.class) {
public String toString(Key k) {
- return k.hasDefaultName()
- ? k.getType().toString()
- : k.getType() + " named '" + k.getName() + "'";
+ return k.hasAnnotationType()
+ ? k.getType() + " annotated with " + k.getAnnotationName()
+ : k.getType().toString();
}
}
);
diff --git a/src/com/google/inject/ForBinding.java b/src/com/google/inject/ForBinding.java
new file mode 100644
index 0000000..637d541
--- /dev/null
+++ b/src/com/google/inject/ForBinding.java
@@ -0,0 +1,31 @@
+/**
+ * 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.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotates annotations which are used for binding.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+@Target({ ElementType.ANNOTATION_TYPE })
+@Retention(RUNTIME)
+public @interface ForBinding {}
diff --git a/src/com/google/inject/Inject.java b/src/com/google/inject/Inject.java
index 10e4f7c..7fa96c9 100644
--- a/src/com/google/inject/Inject.java
+++ b/src/com/google/inject/Inject.java
@@ -20,31 +20,28 @@
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.PARAMETER;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
/**
- * <p>Annotates members and parameters which should have their value[s]
- * injected. If applied to another annotation type, that annotation type
- * can act as a surrogate and save you from having to repeat the dependency
- * name over and over.
+ * <p>Annotates members which should have their value[s] injected.
+ *
+ * <p>Also applies to other annotations which can be used with {@link Key}s and
+ * at injection points.
*
* @author crazybob@google.com (Bob Lee)
*/
-@Target({ METHOD, CONSTRUCTOR, FIELD, PARAMETER, ANNOTATION_TYPE })
+@Target({ METHOD, CONSTRUCTOR, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface Inject {
/**
- * Dependency name. Defaults to {@link Key#DEFAULT_NAME}.
+ * Indicates whether injection at the target is optional or not. The default
+ * is {@code false}. Can be used on methods and fields. If a method has
+ * multiple parameters and one parameter binding is missing, the method
+ * won't be invoked at all. Not applicable to constructors or other
+ * annotations.
*/
- String value() default Key.DEFAULT_NAME;
-
- /**
- * Whether or not injection is required. Applicable only to methods and fields
- * (not constructors or parameters).
- */
- boolean required() default true;
+ boolean optional() default false;
}
diff --git a/src/com/google/inject/Key.java b/src/com/google/inject/Key.java
index bc40e75..afcd968 100644
--- a/src/com/google/inject/Key.java
+++ b/src/com/google/inject/Key.java
@@ -17,31 +17,34 @@
package com.google.inject;
import static com.google.inject.util.Objects.nonNull;
+import com.google.inject.util.ToStringBuilder;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Member;
import java.lang.reflect.Type;
/**
- * Binding key consisting of a type and a name. Matches the type and name
- * ({@link Inject#value()}) at a point of injection.
+ * Binding key consisting of an injection type and an optional annotation.
+ * Matches the type and annotation at a point of injection.
*
- * <p>For example, {@code new Key<List<String>>("cities") {}} will match:
+ * <p>For example, {@code Key.get(Service.class, Transactional.class) {}} will
+ * match:
*
* <pre>
- * {@literal @}Inject("cities")
- * public void setList(List<String> cities) {
+ * {@literal @}Inject
+ * public void setService({@literal @}Transactional Service cities) {
* ...
* }
* </pre>
*
+ * <p>{@code Key} supports generic types via subclassing just like {@link
+ * TypeLiteral}.
+ *
* @author crazybob@google.com (Bob Lee)
*/
public abstract class Key<T> {
- /**
- * Default binding name.
- */
- public static final String DEFAULT_NAME = "default";
+ final AnnotationStrategy annotationStrategy;
- final String name;
final TypeLiteral<T> typeLiteral;
final int hashCode;
@@ -52,58 +55,98 @@
* parameter in the anonymous class's type hierarchy so we can reconstitute it
* at runtime despite erasure.
*
- * <p>Example usage for a binding of type {@code Foo} named "bar":
- * {@code new Key<Foo>("bar") {}}.
+ * <p>Example usage for a binding of type {@code Foo} annotated with
+ * {@code @Bar}:
+ *
+ * <p>{@code new Key<Foo>(Bar.class) {}}.
*/
@SuppressWarnings("unchecked")
- protected Key(String name) {
- this.name = nonNull(name, "name");
+ protected Key(Class<? extends Annotation> annotationType) {
+ this.annotationStrategy = strategyFor(annotationType);
this.typeLiteral
= (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass());
this.hashCode = computeHashCode();
}
/**
- * Convenience method. Delegates to {@link #Key(String)} with
- * {@link #DEFAULT_NAME}.
+ * Constructs a new key. Derives the type from this class's type parameter.
+ *
+ * <p>Clients create an empty anonymous subclass. Doing so embeds the type
+ * parameter in the anonymous class's type hierarchy so we can reconstitute it
+ * at runtime despite erasure.
+ *
+ * <p>Example usage for a binding of type {@code Foo} annotated with
+ * {@code @Bar}:
+ *
+ * <p>{@code new Key<Foo>(new Bar()) {}}.
*/
+ @SuppressWarnings("unchecked")
+ protected Key(Annotation annotation) {
+ this.annotationStrategy = strategyFor(annotation);
+ this.typeLiteral
+ = (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass());
+ this.hashCode = computeHashCode();
+ }
+
+ /**
+ * Constructs a new key. Derives the type from this class's type parameter.
+ *
+ * <p>Clients create an empty anonymous subclass. Doing so embeds the type
+ * parameter in the anonymous class's type hierarchy so we can reconstitute it
+ * at runtime despite erasure.
+ *
+ * <p>Example usage for a binding of type {@code Foo} annotated with
+ * {@code @Named("bar")}:
+ *
+ * <p>{@code new Key<Foo>("bar") {}}.
+ */
+ @SuppressWarnings("unchecked")
+ protected Key(String name) {
+ this.annotationStrategy = strategyFor(new NamedImpl(name));
+ this.typeLiteral
+ = (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass());
+ this.hashCode = computeHashCode();
+ }
+
+ /**
+ * Constructs a new key. Derives the type from this class's type parameter.
+ *
+ * <p>Clients create an empty anonymous subclass. Doing so embeds the type
+ * parameter in the anonymous class's type hierarchy so we can reconstitute it
+ * at runtime despite erasure.
+ *
+ * <p>Example usage for a binding of type {@code Foo}:
+ *
+ * <p>{@code new Key<Foo>() {}}.
+ */
+ @SuppressWarnings("unchecked")
protected Key() {
- this(DEFAULT_NAME);
+ this.annotationStrategy = NULL_STRATEGY;
+ this.typeLiteral
+ = (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass());
+ this.hashCode = computeHashCode();
}
/**
* Unsafe. Constructs a key from a manually specified type.
*/
@SuppressWarnings("unchecked")
- private Key(Type type, String name) {
- this.name = nonNull(name, "name");
+ private Key(Type type, AnnotationStrategy annotationStrategy) {
+ this.annotationStrategy = annotationStrategy;
this.typeLiteral = (TypeLiteral<T>) TypeLiteral.get(type);
this.hashCode = computeHashCode();
}
/** Constructs a key from a manually specified type. */
- private Key(TypeLiteral<T> typeLiteral, String name) {
- this.name = nonNull(name, "name");
+ private Key(TypeLiteral<T> typeLiteral,
+ AnnotationStrategy annotationStrategy) {
+ this.annotationStrategy = annotationStrategy;
this.typeLiteral = typeLiteral;
this.hashCode = computeHashCode();
}
private int computeHashCode() {
- return typeLiteral.hashCode() * 31 + name.hashCode();
- }
-
- /**
- * Returns {@code true} if this key has the default name.
- */
- public boolean hasDefaultName() {
- return DEFAULT_NAME.equals(this.name);
- }
-
- /**
- * Returns a new key with the same type as this key and the given name,
- */
- Key<T> named(String name) {
- return new SimpleKey<T>(this.typeLiteral, name);
+ return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
}
/**
@@ -114,10 +157,23 @@
}
/**
- * Gets the binding name.
+ * Gets the annotation type.
*/
- public String getName() {
- return name;
+ public Class<? extends Annotation> getAnnotationType() {
+ return annotationStrategy.getAnnotationType();
+ }
+
+ boolean hasAnnotationType() {
+ return annotationStrategy.getAnnotationType() != null;
+ }
+
+ String getAnnotationName() {
+ Annotation annotation = annotationStrategy.getAnnotation();
+ if (annotation != null) {
+ return annotation.toString();
+ }
+
+ return annotationStrategy.getAnnotationType().toString();
}
public int hashCode() {
@@ -136,64 +192,299 @@
return false;
}
Key<?> other = (Key<?>) o;
- return name.equals(other.name) && typeLiteral.equals(other.typeLiteral);
+ return annotationStrategy.equals(other.annotationStrategy)
+ && typeLiteral.equals(other.typeLiteral);
}
public String toString() {
- return Key.class.getSimpleName()
- + "[type=" + typeLiteral + ", name='" + name + "']";
+ return new ToStringBuilder(Key.class)
+ .add("type", typeLiteral)
+ .add("annotation", annotationStrategy)
+ .toString();
}
/**
- * Gets a key for a {@code Class}. Defaults name to {@link #DEFAULT_NAME}.
+ * Gets a key for an injection type and an annotation strategy.
+ */
+ static <T> Key<T> get(Class<T> type,
+ AnnotationStrategy annotationStrategy) {
+ return new SimpleKey<T>(type, annotationStrategy);
+ }
+
+ /**
+ * Gets a key for an injection type.
*/
public static <T> Key<T> get(Class<T> type) {
- return new SimpleKey<T>(type, DEFAULT_NAME);
+ return new SimpleKey<T>(type, NULL_STRATEGY);
}
/**
- * Gets a key for a {@code Class} and a name.
+ * Gets a key for an injection type and an annotation type.
+ */
+ public static <T> Key<T> get(Class<T> type,
+ Class<? extends Annotation> annotationType) {
+ return new SimpleKey<T>(type, strategyFor(annotationType));
+ }
+
+ /**
+ * Gets a key for an injection type and the annotation {@code Named(name)}.
*/
public static <T> Key<T> get(Class<T> type, String name) {
- return new SimpleKey<T>(type, name);
+ return new SimpleKey<T>(type, strategyFor(new NamedImpl(name)));
}
/**
- * Gets a key for a type. Defaults name to {@link #DEFAULT_NAME}.
+ * Gets a key for an injection type and an annotation.
+ */
+ public static <T> Key<T> get(Class<T> type, Annotation annotation) {
+ return new SimpleKey<T>(type, strategyFor(annotation));
+ }
+
+ /**
+ * Gets a key for an injection type.
*/
public static Key<?> get(Type type) {
- return new SimpleKey<Object>(type, DEFAULT_NAME);
+ return new SimpleKey<Object>(type, NULL_STRATEGY);
}
/**
- * Gets a key for a type and a name.
+ * Gets a key for an injection type and an annotation type.
+ */
+ public static Key<?> get(Type type,
+ Class<? extends Annotation> annotationType) {
+ return new SimpleKey<Object>(type, strategyFor(annotationType));
+ }
+
+ /**
+ * Gets a key for an injection type and an annotation.
+ */
+ public static Key<?> get(Type type, Annotation annotation) {
+ return new SimpleKey<Object>(type, strategyFor(annotation));
+ }
+
+ /**
+ * Gets a key for an injection type and the annotation {@code Named(name)}.
*/
public static Key<?> get(Type type, String name) {
- return new SimpleKey<Object>(type, name);
+ return new SimpleKey<Object>(type, strategyFor(new NamedImpl(name)));
}
/**
- * Gets a key for a type. Defaults name to {@link #DEFAULT_NAME}.
+ * Gets a key for an injection type.
*/
public static <T> Key<T> get(TypeLiteral<T> typeLiteral) {
- return new SimpleKey<T>(typeLiteral, DEFAULT_NAME);
+ return new SimpleKey<T>(typeLiteral, NULL_STRATEGY);
}
/**
- * Gets key for a type and a name.
+ * Gets a key for an injection type and an annotation type.
+ */
+ public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
+ Class<? extends Annotation> annotationType) {
+ return new SimpleKey<T>(typeLiteral, strategyFor(annotationType));
+ }
+
+ /**
+ * Gets a key for an injection type and an annotation.
+ */
+ public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
+ Annotation annotation) {
+ return new SimpleKey<T>(typeLiteral, strategyFor(annotation));
+ }
+
+ /**
+ * Gets a key for an injection type and the annotation {@code Named(name)}.
*/
public static <T> Key<T> get(TypeLiteral<T> typeLiteral, String name) {
- return new SimpleKey<T>(typeLiteral, name);
+ return new SimpleKey<T>(typeLiteral, strategyFor(new NamedImpl(name)));
+ }
+
+ /**
+ * Gets a key for the given type, member and annotations.
+ */
+ static Key<?> get(Type type, Member member, Annotation[] annotations,
+ ErrorHandler errorHandler) {
+ Annotation found = null;
+ for (Annotation annotation : annotations) {
+ if (annotation.annotationType().getAnnotation(ForBinding.class) != null) {
+ if (found == null) {
+ found = annotation;
+ } else {
+ errorHandler.handle(ErrorMessages.DUPLICATE_ANNOTATIONS, member,
+ found, annotation);
+ }
+ }
+ }
+ Key<?> key = found == null ? Key.get(type) : Key.get(type, found);
+ return key;
+ }
+
+ /**
+ * Returns a new key of the specified type with the same annotation as this
+ * key.
+ */
+ public <T> Key<T> ofType(Class<T> type) {
+ return new SimpleKey<T>(type, annotationStrategy);
+ }
+
+ /**
+ * Returns a new key of the specified type with the same annotation as this
+ * key.
+ */
+ public Key<?> ofType(Type type) {
+ return new SimpleKey<Object>(type, annotationStrategy);
+ }
+
+ /**
+ * Returns a new key of the specified type with the same annotation as this
+ * key.
+ */
+ public <T> Key<T> ofType(TypeLiteral<T> type) {
+ return new SimpleKey<T>(type, annotationStrategy);
}
private static class SimpleKey<T> extends Key<T> {
- private SimpleKey(Type type, String name) {
- super(type, name);
+ private SimpleKey(Type type, AnnotationStrategy annotationStrategy) {
+ super(type, annotationStrategy);
}
- private SimpleKey(TypeLiteral<T> typeLiteral, String name) {
- super(typeLiteral, name);
+ private SimpleKey(TypeLiteral<T> typeLiteral,
+ AnnotationStrategy annotationStrategy) {
+ super(typeLiteral, annotationStrategy);
+ }
+ }
+
+ interface AnnotationStrategy {
+
+ Annotation getAnnotation();
+ Class<? extends Annotation> getAnnotationType();
+ }
+
+ static final AnnotationStrategy NULL_STRATEGY = new AnnotationStrategy() {
+
+ public Annotation getAnnotation() {
+ return null;
+ }
+
+ public Class<? extends Annotation> getAnnotationType() {
+ return null;
+ }
+
+ public boolean equals(Object o) {
+ return o == NULL_STRATEGY;
+ }
+
+ public int hashCode() {
+ return 0;
+ }
+
+ public String toString() {
+ return "[none]";
+ }
+ };
+
+ /**
+ * Returns {@code true} if the given annotation type has no attributes.
+ */
+ static boolean isMarker(Class<? extends Annotation> annotationType) {
+ return annotationType.getDeclaredMethods().length == 0;
+ }
+
+ /**
+ * Gets the strategy for an annotation.
+ */
+ static AnnotationStrategy strategyFor(Annotation annotation) {
+ nonNull(annotation, "annotation");
+ return isMarker(annotation.annotationType())
+ ? new AnnotationTypeStrategy(annotation.annotationType(), annotation)
+ : new AnnotationInstanceStrategy(annotation);
+ }
+
+ /**
+ * Gets the strategy for an annotation type.
+ */
+ static AnnotationStrategy strategyFor(
+ Class<? extends Annotation> annotationType) {
+ nonNull(annotationType, "annotation type");
+ if (!isMarker(annotationType)) {
+ throw new IllegalArgumentException(annotationType.getName()
+ + " is not a marker annotation, i.e. it has attributes. Please"
+ + " use an Annotation instance or a marker annotation instead.");
+ }
+ return new AnnotationTypeStrategy(annotationType, null);
+ }
+
+ static class AnnotationInstanceStrategy implements AnnotationStrategy {
+
+ final Annotation annotation;
+
+ AnnotationInstanceStrategy(Annotation annotation) {
+ this.annotation = nonNull(annotation, "annotation");
+ }
+
+ public Annotation getAnnotation() {
+ return annotation;
+ }
+
+ public Class<? extends Annotation> getAnnotationType() {
+ return annotation.annotationType();
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof AnnotationInstanceStrategy)) {
+ return false;
+ }
+
+ AnnotationInstanceStrategy other = (AnnotationInstanceStrategy) o;
+ return annotation.equals(other.annotation);
+ }
+
+ public int hashCode() {
+ return annotation.hashCode();
+ }
+
+ public String toString() {
+ return annotation.toString();
+ }
+ }
+
+ static class AnnotationTypeStrategy implements AnnotationStrategy {
+
+ final Class<? extends Annotation> annotationType;
+
+ // Keep the instance around if we have it so the client can request it.
+ final Annotation annotation;
+
+ AnnotationTypeStrategy(Class<? extends Annotation> annotationType,
+ Annotation annotation) {
+ this.annotationType = nonNull(annotationType, "annotation type");
+ this.annotation = annotation;
+ }
+
+ public Annotation getAnnotation() {
+ return annotation;
+ }
+
+ public Class<? extends Annotation> getAnnotationType() {
+ return annotationType;
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof AnnotationTypeStrategy)) {
+ return false;
+ }
+
+ AnnotationTypeStrategy other = (AnnotationTypeStrategy) o;
+ return annotationType.equals(other.annotationType);
+ }
+
+ public int hashCode() {
+ return annotationType.hashCode();
+ }
+
+ public String toString() {
+ return annotationType.toString();
}
}
}
diff --git a/src/com/google/inject/Named.java b/src/com/google/inject/Named.java
new file mode 100644
index 0000000..0a0bd8d
--- /dev/null
+++ b/src/com/google/inject/Named.java
@@ -0,0 +1,31 @@
+/**
+ * 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.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotates named things.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+@Retention(RUNTIME)
+@ForBinding
+public @interface Named {
+ String value();
+}
diff --git a/src/com/google/inject/NamedImpl.java b/src/com/google/inject/NamedImpl.java
new file mode 100644
index 0000000..c8be943
--- /dev/null
+++ b/src/com/google/inject/NamedImpl.java
@@ -0,0 +1,55 @@
+/**
+ * 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.Annotation;
+import com.google.inject.util.Objects;
+
+class NamedImpl implements Named {
+
+ final String value;
+
+ public NamedImpl(String value) {
+ this.value = Objects.nonNull(value, "name");
+ }
+
+ public String value() {
+ return this.value;
+ }
+
+ public int hashCode() {
+ // This is specified in java.lang.Annotation.
+ return 127 * "value".hashCode() ^ value.hashCode();
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof Named)) {
+ return false;
+ }
+
+ Named other = (Named) o;
+ return value.equals(other.value());
+ }
+
+ public String toString() {
+ return "@Named(\"" + value + "\")";
+ }
+
+ public Class<? extends Annotation> annotationType() {
+ return Named.class;
+ }
+}
diff --git a/src/com/google/inject/util/Objects.java b/src/com/google/inject/util/Objects.java
index 15fbf3f..59b3d25 100644
--- a/src/com/google/inject/util/Objects.java
+++ b/src/com/google/inject/util/Objects.java
@@ -36,4 +36,19 @@
}
return t;
}
+
+ /**
+ * {@code null}-aware equals.
+ */
+ public static boolean equal(Object a, Object b) {
+ if (a == b) {
+ return true;
+ }
+
+ if (a == null || b == null) {
+ return false;
+ }
+
+ return a.equals(b);
+ }
}
diff --git a/test/com/google/inject/AllTests.java b/test/com/google/inject/AllTests.java
index 51db8e0..667d567 100644
--- a/test/com/google/inject/AllTests.java
+++ b/test/com/google/inject/AllTests.java
@@ -49,7 +49,6 @@
suite.addTestSuite(ReflectionTest.class);
suite.addTestSuite(ScopesTest.class);
suite.addTestSuite(ImplicitBindingTest.class);
- suite.addTestSuite(InjectTest.class);
suite.addTestSuite(MatcherTest.class);
suite.addTestSuite(ProxyFactoryTest.class);
diff --git a/test/com/google/inject/ConstantConversionTest.java b/test/com/google/inject/ConstantConversionTest.java
index 01b0c2f..5792879 100644
--- a/test/com/google/inject/ConstantConversionTest.java
+++ b/test/com/google/inject/ConstantConversionTest.java
@@ -27,38 +27,49 @@
public class ConstantConversionTest extends TestCase {
public static class Foo {
- @Inject("#") Integer integerField;
- @Inject("#") int primitiveIntField;
- @Inject("#") Long longField;
- @Inject("#") long primitiveLongField;
- @Inject("boolean") Boolean booleanField;
- @Inject("boolean") boolean primitiveBooleanField;
- @Inject("#") Byte byteField;
- @Inject("#") byte primitiveByteField;
- @Inject("#") Short shortField;
- @Inject("#") short primitiveShortField;
- @Inject("#") Float floatField;
- @Inject("#") float primitiveFloatField;
- @Inject("#") Double doubleField;
- @Inject("#") short primitiveDoubleField;
- @Inject("enum") Bar enumField;
- @Inject("class") Class<?> classField;
+ @Inject @Named("#") Integer integerField;
+ @Inject @Named("#") int primitiveIntField;
+ @Inject @Named("#") Long longField;
+ @Inject @Named("#") long primitiveLongField;
+ @Inject @Named("boolean") Boolean booleanField;
+ @Inject @Named("boolean") boolean primitiveBooleanField;
+ @Inject @Named("#") Byte byteField;
+ @Inject @Named("#") byte primitiveByteField;
+ @Inject @Named("#") Short shortField;
+ @Inject @Named("#") short primitiveShortField;
+ @Inject @Named("#") Float floatField;
+ @Inject @Named("#") float primitiveFloatField;
+ @Inject @Named("#") Double doubleField;
+ @Inject @Named("#") short primitiveDoubleField;
+ @Inject @Named("enum") Bar enumField;
+ @Inject @Named("class") Class<?> classField;
}
public enum Bar {
TEE, BAZ, BOB;
}
+ @Named("foo")
+ public void testNamed() throws NoSuchMethodException {
+ Named named = getClass().getMethod("testNamed").getAnnotation(Named.class);
+ assertEquals(named, new NamedImpl("foo"));
+ assertEquals(new NamedImpl("foo"), named);
+
+ assertEquals(Key.get(String.class, new NamedImpl("foo")),
+ Key.get(String.class, named));
+ }
+
public void testOneConstantInjection() throws ContainerCreationException {
ContainerBuilder builder = new ContainerBuilder();
builder.bind("#").to("5");
+ builder.bind(Simple.class);
Container container = builder.create(false);
Simple simple = container.getFactory(Simple.class).get();
assertEquals(5, simple.i);
}
static class Simple {
- @Inject("#") int i;
+ @Inject @Named("#") int i;
}
public void testConstantInjection() throws ContainerCreationException {
@@ -106,15 +117,11 @@
try {
c.getFactory(InvalidInteger.class).get();
fail();
- } catch (ConfigurationException e) {
- assertTrue(e.getMessage().contains(
- "Error converting 'invalid' to Integer while injecting integerField "
- + "with dependency named '#' in InvalidInteger. Reason:"));
- }
+ } catch (ConfigurationException e) { /* expected */ }
}
public static class InvalidInteger {
- @Inject("#") Integer integerField;
+ @Inject @Named("#") Integer integerField;
}
public void testInvalidCharacter() throws ContainerCreationException {
@@ -124,15 +131,11 @@
try {
c.getFactory(InvalidCharacter.class).get();
fail();
- } catch (ConfigurationException e) {
- assertTrue(e.getMessage().contains(
- "Error converting 'invalid' to char while injecting foo "
- + "with dependency named 'foo' in InvalidCharacter. Reason:"));
- }
+ } catch (ConfigurationException e) { /* expected */ }
}
public static class InvalidCharacter {
- @Inject("foo") char foo;
+ @Inject @Named("foo") char foo;
}
public void testInvalidEnum() throws ContainerCreationException {
@@ -142,14 +145,10 @@
try {
c.getFactory(InvalidEnum.class).get();
fail();
- } catch (ConfigurationException e) {
- assertTrue(e.getMessage().startsWith(
- "Error converting 'invalid' to Bar while injecting foo "
- + "with dependency named 'foo' in InvalidEnum. Reason:"));
- }
+ } catch (ConfigurationException e) { /* expected */ }
}
public static class InvalidEnum {
- @Inject("foo") Bar foo;
+ @Inject @Named("foo") Bar foo;
}
}
diff --git a/test/com/google/inject/ContainerTest.java b/test/com/google/inject/ContainerTest.java
index f86aa87..7fce43d 100644
--- a/test/com/google/inject/ContainerTest.java
+++ b/test/com/google/inject/ContainerTest.java
@@ -17,19 +17,27 @@
package com.google.inject;
import junit.framework.TestCase;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @author crazybob@google.com (Bob Lee)
*/
public class ContainerTest extends TestCase {
+ @Retention(RUNTIME)
+ @ForBinding
+ @interface Other {}
+
public void testFactoryMethods() throws ContainerCreationException {
Singleton singleton = new Singleton();
Singleton other = new Singleton();
ContainerBuilder builder = new ContainerBuilder();
builder.bind(Singleton.class).to(singleton);
- builder.bind(Singleton.class).named("other").to(other);
+ builder.bind(Singleton.class)
+ .annotatedWith(Other.class)
+ .to(other);
Container container = builder.create(true);
assertSame(singleton,
@@ -43,14 +51,14 @@
container.getInstance(new TypeLiteral<Singleton>() {}));
assertSame(other,
- container.getFactory(Key.get(Singleton.class, "other")).get());
- assertSame(other, container.getFactory(Singleton.class, "other").get());
+ container.getFactory(Key.get(Singleton.class, Other.class)).get());
+ assertSame(other, container.getFactory(Singleton.class, Other.class).get());
assertSame(other,
- container.getFactory(new TypeLiteral<Singleton>() {}, "other").get());
- assertSame(other, container.getInstance(Key.get(Singleton.class, "other")));
- assertSame(other, container.getInstance(Singleton.class, "other"));
+ container.getFactory(new TypeLiteral<Singleton>() {}, Other.class).get());
+ assertSame(other, container.getInstance(Key.get(Singleton.class, Other.class)));
+ assertSame(other, container.getInstance(Singleton.class, Other.class));
assertSame(other,
- container.getInstance(new TypeLiteral<Singleton>() {}, "other"));
+ container.getInstance(new TypeLiteral<Singleton>() {}, Other.class));
}
static class Singleton {}
@@ -102,7 +110,7 @@
}
static class IntegerWrapper {
- @Inject("i") Integer i;
+ @Inject @Named("i") Integer i;
}
static class Foo {
@@ -110,12 +118,12 @@
@Inject Bar bar;
@Inject Bar copy;
- @Inject("s") String s;
+ @Inject @Named("s") String s;
int i;
- @Inject("i")
- void setI(int i) {
+ @Inject
+ void setI(@Named("i") int i) {
this.i = i;
}
}
@@ -129,7 +137,7 @@
@ContainerScoped
static class BarImpl implements Bar {
- @Inject("i") int i;
+ @Inject @Named("i") int i;
Tee tee;
@@ -159,7 +167,7 @@
@Inject Bar bar;
@Inject
- TeeImpl(@Inject("s") String s) {
+ TeeImpl(@Named("s") String s) {
this.s = s;
}
@@ -225,11 +233,11 @@
static class Static {
- @Inject("i") static int i;
+ @Inject @Named("i") static int i;
static String s;
- @Inject("s") static void setS(String s) {
+ @Inject static void setS(@Named("s") String s) {
Static.s = s;
}
}
diff --git a/test/com/google/inject/ErrorHandlingTest.java b/test/com/google/inject/ErrorHandlingTest.java
index 04d5162..9dedd75 100644
--- a/test/com/google/inject/ErrorHandlingTest.java
+++ b/test/com/google/inject/ErrorHandlingTest.java
@@ -29,7 +29,7 @@
builder.create(true);
}
- @Inject("missing")
+ @Inject @Named("missing")
static List<String> missing;
static class Foo {
@@ -43,15 +43,15 @@
// Invalid constructor.
Bar(String s) {}
- @Inject("numbers") void setNumbers(List<Integer> numbers) {}
+ @Inject @Named("numbers") void setNumbers(List<Integer> numbers) {}
- @Inject("foo") void bar(@Inject String s) {}
+ @Inject void bar(@Named("foo") String s) {}
}
static class Tee {
@Inject String s;
- @Inject("foo") void tee(String s, int i) {}
+ @Inject @Named("foo") void tee(String s, int i) {}
@Inject Invalid invalid;
}
@@ -67,7 +67,7 @@
bind(Bar.class);
bind(Tee.class);
bind(new TypeLiteral<List<String>>() {});
- bind(String.class).named("foo").in("foo");
+ bind(String.class).annotatedWith(new NamedImpl("foo")).in("foo");
link(Key.get(Runnable.class)).to(Key.get(Runnable.class));
requestStaticInjection(ErrorHandlingTest.class);
}
diff --git a/test/com/google/inject/FactoryTest.java b/test/com/google/inject/FactoryTest.java
index e6a73cd..2306f15 100644
--- a/test/com/google/inject/FactoryTest.java
+++ b/test/com/google/inject/FactoryTest.java
@@ -125,7 +125,7 @@
return new ContextualFactory<T>() {
public T get(Context context) {
assertEquals(expectedMember, context.getMember());
- assertEquals(name, context.getKey().getName());
+ //assertEquals(name, context.getKey().getName());
assertEquals(type, context.getKey().getType().getType());
return context.getContainer().getFactory(type).get();
}
@@ -134,15 +134,15 @@
static class Foo {
final Bar bar;
- @Inject("fooBar") Foo(Bar bar) {
+ @Inject Foo(@Named("fooBar") Bar bar) {
this.bar = bar;
}
}
static class Bar {
- @Inject("tee2") Tee tee2;
+ @Inject @Named("tee2") Tee tee2;
final Tee tee1;
- @Inject("tee1") Bar(Tee tee1) {
+ @Inject Bar(@Named("tee1") Tee tee1) {
this.tee1 = tee1;
}
}
@@ -150,8 +150,8 @@
static class Tee {
Bob bob1, bob2;
@Inject void execute(
- @Inject("bob1") Bob bob1,
- @Inject("bob2") Bob bob2) {
+ @Named("bob1") Bob bob1,
+ @Named("bob2") Bob bob2) {
this.bob1 = bob1;
this.bob2 = bob2;
}
diff --git a/test/com/google/inject/InjectTest.java b/test/com/google/inject/InjectTest.java
deleted file mode 100644
index 7b50c9c..0000000
--- a/test/com/google/inject/InjectTest.java
+++ /dev/null
@@ -1,71 +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 junit.framework.TestCase;
-
-import java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-/**
- * @author crazybob@google.com (Bob Lee)
- */
-public class InjectTest extends TestCase {
-
- public void testSurrogateInjection() throws ContainerCreationException {
- ContainerBuilder builder = new ContainerBuilder();
- Bar bar = new Bar(null);
- builder.bind(Bar.class).named("bar").to(bar);
- Container container = builder.create(false);
- Foo foo = container.getInstance(Foo.class);
- assertSame(bar, foo.field);
- assertSame(bar, foo.fromConstructor);
- assertSame(bar, foo.fromMethod);
- assertSame(bar, foo.fromParameter);
- }
-
- @Retention(RUNTIME)
- @Inject("bar")
- @interface InjectBar {}
-
- static class Foo {
- @InjectBar Bar field;
-
- Bar fromConstructor;
- @InjectBar
- public Foo(Bar fromConstructor) {
- this.fromConstructor = fromConstructor;
- }
-
- Bar fromMethod;
- @InjectBar
- void setBar(Bar bar) {
- fromMethod = bar;
- }
-
- Bar fromParameter;
- @Inject
- void setBar2(@InjectBar Bar bar) {
- fromParameter= bar;
- }
- }
-
- static class Bar {
- // Not injectable.
- Bar(String s) {}
- }
-}
diff --git a/test/com/google/inject/KeyTest.java b/test/com/google/inject/KeyTest.java
index 90849ae..1c4b490 100644
--- a/test/com/google/inject/KeyTest.java
+++ b/test/com/google/inject/KeyTest.java
@@ -21,6 +21,8 @@
import java.util.List;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @author crazybob@google.com (Bob Lee)
@@ -29,6 +31,17 @@
public void foo(List<String> a, List<String> b) {}
+ @Retention(RUNTIME)
+ @ForBinding
+ @interface Foo {}
+
+ public void testOfType() {
+ Key<Object> k = Key.get(Object.class, Foo.class);
+ Key<Integer> ki = k.ofType(int.class);
+ assertEquals(int.class, ki.getRawType());
+ assertEquals(Foo.class, ki.getAnnotationType());
+ }
+
public void testEquality() {
assertEquals(
new Key<List<String>>("foo") {},
diff --git a/test/com/google/inject/NotRequiredTest.java b/test/com/google/inject/NotRequiredTest.java
index 16676cd..784cb65 100644
--- a/test/com/google/inject/NotRequiredTest.java
+++ b/test/com/google/inject/NotRequiredTest.java
@@ -40,11 +40,11 @@
}
static class Foo {
- @Inject(required=false) Bar bar;
+ @Inject(optional=true) Bar bar;
Bar fromMethod;
- @Inject(required=false) void setBar(Bar bar) {
+ @Inject(optional=true) void setBar(Bar bar) {
fromMethod = bar;
}
}
diff --git a/test/com/google/inject/PerformanceComparison.java b/test/com/google/inject/PerformanceComparison.java
index 9a4c4d7..e5f7a77 100644
--- a/test/com/google/inject/PerformanceComparison.java
+++ b/test/com/google/inject/PerformanceComparison.java
@@ -161,8 +161,8 @@
String s;
int i;
- @Inject("i")
- public void setI(int i) {
+ @Inject
+ public void setI(@Named("i") int i) {
this.i = i;
}
@@ -176,8 +176,8 @@
this.copy = copy;
}
- @Inject("s")
- public void setS(String s) {
+ @Inject
+ public void setS(@Named("s") String s) {
this.s = s;
}
}
@@ -194,7 +194,7 @@
final Tee tee;
@Inject
- public BarImpl(Tee tee, @Inject("i") int i) {
+ public BarImpl(Tee tee, @Named("i") int i) {
this.tee = tee;
this.i = i;
}
@@ -219,7 +219,7 @@
final String s;
@Inject
- public TeeImpl(@Inject("s") String s) {
+ public TeeImpl(@Named("s") String s) {
this.s = s;
}
diff --git a/test/com/google/inject/StaticInjectionTest.java b/test/com/google/inject/StaticInjectionTest.java
index 0a6cdf7..5dcd169 100644
--- a/test/com/google/inject/StaticInjectionTest.java
+++ b/test/com/google/inject/StaticInjectionTest.java
@@ -37,11 +37,11 @@
static class Static {
- @Inject("i") static int i;
+ @Inject @Named("i") static int i;
static String s;
- @Inject("s") static void setS(String s) {
+ @Inject static void setS(@Named("s") String s) {
StaticInjectionTest.Static.s = s;
}
}