Author: limpbizkit
Date: Mon Nov 3 18:52:54 2008
New Revision: 660
Modified:
trunk/src/com/google/inject/BindingProcessor.java
trunk/src/com/google/inject/ClassBindingImpl.java
trunk/src/com/google/inject/ConstructorInjector.java
trunk/src/com/google/inject/Initializer.java
trunk/src/com/google/inject/InjectorImpl.java
trunk/src/com/google/inject/internal/TypeResolver.java
trunk/src/com/google/inject/spi/InjectionPoint.java
trunk/test/com/google/inject/GenericInjectionTest.java
trunk/test/com/google/inject/ScopesTest.java
trunk/test/com/google/inject/TypeLiteralTest.java
trunk/test/com/google/inject/spi/InjectionPointTest.java
Log:
This change is big, and it makes Guice slower. I'll need to follow-up with
some optimizations that make TypeResolver quicker.
Suppose you have a parameterized class:
class Foo<T> {
@Inject Set<T> tees;
}
This change makes it so that as long as you have the required dependencies,
parameterized injection points will be resolved. For example:
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bind(new TypeLiteral<Set<String>>() {})
.toInstance(ImmutableSet.of("A", "B", "C"));
}
});
Foo<String> foo = injector.getInstance(new TypeLiteral<Foo<String>>() {});
assertEquals(ImmutableSet.of("A", "B", "C"), foo.tees);
This builds on my earlier work for TypeResolver. That class is currently
pretty slow (it builds a ton of HashMaps eagerly), but it shouldn't be too
hard to make it lazy - that way everything will work nice and fast if
you're not leveraging this feature.
Modified: trunk/src/com/google/inject/BindingProcessor.java
==============================================================================
--- trunk/src/com/google/inject/BindingProcessor.java (original)
+++ trunk/src/com/google/inject/BindingProcessor.java Mon Nov 3 18:52:54
2008
@@ -175,19 +175,16 @@
// Example: bind(Date.class).annotatedWith(Red.class);
// We can't assume abstract types aren't injectable. They may have
an
// @ImplementedBy annotation or something.
- if (key.hasAnnotationType() || !(type instanceof Class<?>)) {
+ if (key.hasAnnotationType()) {
errors.missingImplementation(key);
putBinding(invalidBinding(injector, key, source));
return null;
}
// This cast is safe after the preceeding check.
- @SuppressWarnings("unchecked")
- Class<T> clazz = (Class<T>) type;
final BindingImpl<T> binding;
try {
- binding = injector.createUnitializedBinding(
- key, clazz, scope, source, loadStrategy, errors);
+ binding = injector.createUnitializedBinding(key, scope, source,
loadStrategy, errors);
putBinding(binding);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
Modified: trunk/src/com/google/inject/ClassBindingImpl.java
==============================================================================
--- trunk/src/com/google/inject/ClassBindingImpl.java (original)
+++ trunk/src/com/google/inject/ClassBindingImpl.java Mon Nov 3 18:52:54
2008
@@ -43,7 +43,7 @@
}
@Override void initialize(InjectorImpl injector, Errors errors) throws
ErrorsException {
- lateBoundConstructor.bind(injector, getBoundClass(), errors);
+ lateBoundConstructor.bind(injector, getKey().getTypeLiteral(), errors);
injectionPoints =
lateBoundConstructor.constructorInjector.getInjectionPoints();
}
@@ -52,15 +52,9 @@
return visitor.visitConstructor(lateBoundConstructor.getConstructor(),
injectionPoints);
}
- @SuppressWarnings("unchecked")
- public Class<T> getBoundClass() {
- // T should always be the class itself.
- return (Class<T>) key.getRawType();
- }
-
@Override public String toString() {
return new ToStringBuilder(Binding.class)
- .add("class", getBoundClass())
+ .add("type", getKey().getTypeLiteral())
.add("scope", scope)
.add("source", source)
.toString();
Modified: trunk/src/com/google/inject/ConstructorInjector.java
==============================================================================
--- trunk/src/com/google/inject/ConstructorInjector.java (original)
+++ trunk/src/com/google/inject/ConstructorInjector.java Mon Nov 3
18:52:54 2008
@@ -31,18 +31,18 @@
*/
class ConstructorInjector<T> {
- final Class<T> implementation;
+ final TypeLiteral<T> implementation;
final InjectionPoint injectionPoint;
final ImmutableList<SingleMemberInjector> memberInjectors;
final ImmutableList<SingleParameterInjector<?>> parameterInjectors;
final ConstructionProxy<T> constructionProxy;
- ConstructorInjector(Errors errors, InjectorImpl injector, Class<T>
implementation)
+ ConstructorInjector(Errors errors, InjectorImpl injector, TypeLiteral<T>
implementation)
throws ErrorsException {
this.implementation = implementation;
try {
- this.injectionPoint =
InjectionPoint.forConstructorOf(implementation);
+ this.injectionPoint =
InjectionPoint.forConstructorOf(implementation.getType());
} catch (ConfigurationException e) {
throw errors.merge(e.getErrorMessages()).toException();
}
Modified: trunk/src/com/google/inject/Initializer.java
==============================================================================
--- trunk/src/com/google/inject/Initializer.java (original)
+++ trunk/src/com/google/inject/Initializer.java Mon Nov 3 18:52:54 2008
@@ -22,10 +22,9 @@
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
import com.google.inject.spi.InjectionPoint;
-import com.google.inject.SingleMemberInjector;
+import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
@@ -119,7 +118,8 @@
}
public void validate(Errors errors) throws ErrorsException {
- injectors = injector.injectors.get(instance.getClass(),
errors.withSource(source));
+ injectors = injector.injectors.get(
+ TypeLiteral.get(instance.getClass()), errors.withSource(source));
}
/**
Modified: trunk/src/com/google/inject/InjectorImpl.java
==============================================================================
--- trunk/src/com/google/inject/InjectorImpl.java (original)
+++ trunk/src/com/google/inject/InjectorImpl.java Mon Nov 3 18:52:54 2008
@@ -29,6 +29,7 @@
import com.google.inject.internal.ErrorsException;
import com.google.inject.internal.FailableCache;
import com.google.inject.internal.MatcherAndConverter;
+import com.google.inject.internal.MoreTypes;
import com.google.inject.internal.ToStringBuilder;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
@@ -340,72 +341,68 @@
/**
* Creates a binding for an injectable type with the given scope. Looks
for a scope on the type if
* none is specified.
- *
- * TODO(jessewilson): Fix raw types! this method makes a binding for
[EMAIL PROTECTED] Foo} from a request
- * for [EMAIL PROTECTED] Foo<String>}
- *
- * @param type the raw type for [EMAIL PROTECTED] key}
*/
- <T> BindingImpl<T> createUnitializedBinding(Key<T> key, Class<T> type,
Scope scope, Object source,
+ <T> BindingImpl<T> createUnitializedBinding(Key<T> key, Scope scope,
Object source,
LoadStrategy loadStrategy, Errors errors) throws ErrorsException {
+ Class<?> rawType =
MoreTypes.getRawType(key.getTypeLiteral().getType());
+
// Don't try to inject arrays, or enums.
- if (type.isArray() || type.isEnum()) {
+ if (rawType.isArray() || rawType.isEnum()) {
throw errors.missingImplementation(key).toException();
}
// Handle @ImplementedBy
- ImplementedBy implementedBy = type.getAnnotation(ImplementedBy.class);
+ ImplementedBy implementedBy =
rawType.getAnnotation(ImplementedBy.class);
if (implementedBy != null) {
- Annotations.checkForMisplacedScopeAnnotations(type, source, errors);
- return createImplementedByBinding(type, scope, implementedBy,
loadStrategy, errors);
+ Annotations.checkForMisplacedScopeAnnotations(rawType, source,
errors);
+ return createImplementedByBinding(key, scope, implementedBy,
loadStrategy, errors);
}
// Handle @ProvidedBy.
- ProvidedBy providedBy = type.getAnnotation(ProvidedBy.class);
+ ProvidedBy providedBy = rawType.getAnnotation(ProvidedBy.class);
if (providedBy != null) {
- Annotations.checkForMisplacedScopeAnnotations(type, source, errors);
- return createProvidedByBinding(type, scope, providedBy,
loadStrategy, errors);
+ Annotations.checkForMisplacedScopeAnnotations(rawType, source,
errors);
+ return createProvidedByBinding(key, scope, providedBy, loadStrategy,
errors);
}
// We can't inject abstract classes.
// TODO: Method interceptors could actually enable us to implement
// abstract types. Should we remove this restriction?
- if (Modifier.isAbstract(type.getModifiers())) {
+ if (Modifier.isAbstract(rawType.getModifiers())) {
throw errors.missingImplementation(key).toException();
}
// Error: Inner class.
- if (Classes.isInnerClass(type)) {
- throw errors.cannotInjectInnerClass(type).toException();
+ if (Classes.isInnerClass(rawType)) {
+ throw errors.cannotInjectInnerClass(rawType).toException();
}
if (scope == null) {
- Class<? extends Annotation> scopeAnnotation =
Annotations.findScopeAnnotation(errors, type);
+ Class<? extends Annotation> scopeAnnotation
+ = Annotations.findScopeAnnotation(errors, rawType);
if (scopeAnnotation != null) {
scope = state.getScope(scopeAnnotation);
if (scope == null) {
- errors.withSource(type).scopeNotFound(scopeAnnotation);
+ errors.withSource(rawType).scopeNotFound(scopeAnnotation);
}
}
}
- Key<T> keyForRawType = Key.get(type);
-
LateBoundConstructor<T> lateBoundConstructor = new
LateBoundConstructor<T>();
InternalFactory<? extends T> scopedFactory
- = Scopes.scope(keyForRawType, this, lateBoundConstructor, scope);
+ = Scopes.scope(key, this, lateBoundConstructor, scope);
return new ClassBindingImpl<T>(
- this, keyForRawType, source, scopedFactory, scope,
lateBoundConstructor, loadStrategy);
+ this, key, source, scopedFactory, scope, lateBoundConstructor,
loadStrategy);
}
static class LateBoundConstructor<T> implements InternalFactory<T> {
ConstructorInjector<T> constructorInjector;
@SuppressWarnings("unchecked") // the constructor T is the same as the
implementation T
- void bind(InjectorImpl injector, Class<T> implementation, Errors
errors)
+ void bind(InjectorImpl injector, TypeLiteral<T> implementation, Errors
errors)
throws ErrorsException {
- constructorInjector = (ConstructorInjector<T>)
injector.constructors.get(
- implementation, errors);
+ constructorInjector
+ = (ConstructorInjector<T>)
injector.constructors.get(implementation, errors);
}
public Constructor<T> getConstructor() {
@@ -426,12 +423,13 @@
}
/** Creates a binding for a type annotated with @ProvidedBy. */
- <T> BindingImpl<T> createProvidedByBinding(final Class<T> type, Scope
scope,
+ <T> BindingImpl<T> createProvidedByBinding(Key<T> key, Scope scope,
ProvidedBy providedBy, LoadStrategy loadStrategy, Errors errors)
throws ErrorsException {
+ final Class<?> rawType =
MoreTypes.getRawType(key.getTypeLiteral().getType());
final Class<? extends Provider<?>> providerType = providedBy.value();
// Make sure it's not the same type. TODO: Can we check for deeper
loops?
- if (providerType == type) {
+ if (providerType == rawType) {
throw errors.recursiveProviderType().toException();
}
@@ -449,8 +447,8 @@
Provider<?> provider = providerBinding.internalFactory.get(errors,
context, dependency);
try {
Object o = provider.get();
- if (o != null && !type.isInstance(o)) {
- throw errors.subtypeNotProvided(providerType,
type).toException();
+ if (o != null && !rawType.isInstance(o)) {
+ throw errors.subtypeNotProvided(providerType,
rawType).toException();
}
@SuppressWarnings("unchecked") // protected by isInstance()
check above
T t = (T) o;
@@ -461,11 +459,10 @@
}
};
- Key<T> key = Key.get(type);
return new LinkedProviderBindingImpl<T>(
this,
key,
- type,
+ rawType /* source */,
Scopes.<T>scope(key, this, internalFactory, scope),
scope,
providerKey,
@@ -473,19 +470,20 @@
}
/** Creates a binding for a type annotated with @ImplementedBy. */
- <T> BindingImpl<T> createImplementedByBinding(Class<T> type, Scope scope,
+ <T> BindingImpl<T> createImplementedByBinding(Key<T> key, Scope scope,
ImplementedBy implementedBy, LoadStrategy loadStrategy, Errors
errors)
throws ErrorsException {
+ Class<?> rawType =
MoreTypes.getRawType(key.getTypeLiteral().getType());
Class<?> implementationType = implementedBy.value();
// Make sure it's not the same type. TODO: Can we check for deeper
cycles?
- if (implementationType == type) {
+ if (implementationType == rawType) {
throw errors.recursiveImplementationType().toException();
}
// Make sure implementationType extends type.
- if (!type.isAssignableFrom(implementationType)) {
- throw errors.notASubtype(implementationType, type).toException();
+ if (!rawType.isAssignableFrom(implementationType)) {
+ throw errors.notASubtype(implementationType, rawType).toException();
}
@SuppressWarnings("unchecked") // After the preceding check, this cast
is safe.
@@ -502,13 +500,13 @@
}
};
- Key<T> key = Key.get(type);
return new LinkedBindingImpl<T>(
this,
key,
- type,
+ rawType /* source */,
Scopes.<T>scope(key, this, internalFactory, scope),
- scope, targetKey,
+ scope,
+ targetKey,
loadStrategy);
}
@@ -560,10 +558,8 @@
throw errors.missingImplementation(key).toException();
}
- // Create a binding based on the raw type.
- @SuppressWarnings("unchecked")
- Class<T> rawType = (Class<T>) key.getRawType();
- BindingImpl<T> binding = createUnitializedBinding(key, rawType, null
/* scope */, rawType,
+ Object source = MoreTypes.getRawType(key.getTypeLiteral().getType());
+ BindingImpl<T> binding = createUnitializedBinding(key, null /* scope
*/, source,
LoadStrategy.LAZY, errors);
initializeBinding(binding, errors);
return binding;
@@ -575,14 +571,14 @@
}
/** Cached field and method injectors for a type. */
- final FailableCache<Class<?>, ImmutableList<SingleMemberInjector>>
injectors
- = new FailableCache<Class<?>, ImmutableList<SingleMemberInjector>>()
{
- protected ImmutableList<SingleMemberInjector> create(Class<?> type,
Errors errors)
+ final FailableCache<TypeLiteral<?>, ImmutableList<SingleMemberInjector>>
injectors
+ = new FailableCache<TypeLiteral<?>,
ImmutableList<SingleMemberInjector>>() {
+ protected ImmutableList<SingleMemberInjector> create(TypeLiteral<?>
type, Errors errors)
throws ErrorsException {
int numErrorsBefore = errors.size();
List<InjectionPoint> injectionPoints = Lists.newArrayList();
try {
- InjectionPoint.addForInstanceMethodsAndFields(type,
injectionPoints);
+ InjectionPoint.addForInstanceMethodsAndFields(type.getType(),
injectionPoints);
} catch (ConfigurationException e) {
errors.merge(e.getErrorMessages());
}
@@ -672,10 +668,11 @@
}
/** Cached constructor injectors for each type */
- final FailableCache<Class<?>, ConstructorInjector<?>> constructors
- = new FailableCache<Class<?>, ConstructorInjector<?>>() {
+ final FailableCache<TypeLiteral<?>, ConstructorInjector<?>> constructors
+ = new FailableCache<TypeLiteral<?>, ConstructorInjector<?>>() {
@SuppressWarnings("unchecked")
- protected ConstructorInjector<?> create(Class<?> type, Errors errors)
throws ErrorsException {
+ protected ConstructorInjector<?> create(TypeLiteral<?> type, Errors
errors)
+ throws ErrorsException {
return new ConstructorInjector(errors, InjectorImpl.this, type);
}
};
@@ -695,7 +692,7 @@
// configuration/validation stuff throws ConfigurationException
List<SingleMemberInjector> injectors;
try {
- injectors = this.injectors.get(o.getClass(), errors);
+ injectors = this.injectors.get(TypeLiteral.get(o.getClass()),
errors);
} catch (ErrorsException e) {
throw new
ConfigurationException(errors.merge(e.getErrors()).getMessages());
}
Modified: trunk/src/com/google/inject/internal/TypeResolver.java
==============================================================================
--- trunk/src/com/google/inject/internal/TypeResolver.java (original)
+++ trunk/src/com/google/inject/internal/TypeResolver.java Mon Nov 3
18:52:54 2008
@@ -25,6 +25,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
@@ -51,7 +52,7 @@
public final Type type;
/** maps from type variables to the types they resolve to */
- private final Map<FqTypeVar, Type> index = Maps.newHashMap();
+ private final Map<TypeVariable, Type> index = Maps.newHashMap();
/** types to resolved types */
private final Map<Type, Type> cache = Maps.newHashMap();
@@ -102,9 +103,8 @@
Type[] arguments = parameterizedType.getActualTypeArguments();
Type[] resolvedArguments = new Type[typeVariables.length];
for (int v = 0; v < typeVariables.length; v++) {
- FqTypeVar typeVar = new FqTypeVar(rawType,
typeVariables[v].getName());
resolvedArguments[v] = resolve(arguments[v]);
- index.put(typeVar, resolvedArguments[v]);
+ index.put(typeVariables[v], resolvedArguments[v]);
}
Type resolvedRawType = resolve(rawType);
@@ -120,8 +120,7 @@
GenericDeclaration genericDeclaration =
typeVariable.getGenericDeclaration();
Type result = typeVariable;
if (genericDeclaration instanceof Class) {
- FqTypeVar fqTypeVar = new FqTypeVar((Class<?>) genericDeclaration,
typeVariable.getName());
- Type resolved = index.get(fqTypeVar);
+ Type resolved = index.get(typeVariable);
result = resolved != null ? resolved : result;
}
cache.put(type, result);
@@ -163,6 +162,13 @@
}
/**
+ * Returns the raw form of the type being resolved, such as [EMAIL
PROTECTED] List}.
+ */
+ public Class<?> getRawType() {
+ return MoreTypes.getRawType(type);
+ }
+
+ /**
* Returns the generic equivalent of [EMAIL PROTECTED] supertype}. For
example, if
this
* is a resolver of [EMAIL PROTECTED] ArrayList<String>}, this method
returns [EMAIL PROTECTED]
* Iterable<String>} given the input [EMAIL PROTECTED] Iterable.class}.
@@ -190,19 +196,6 @@
}
/**
- * Returns a list of the resolved generic parameter types of [EMAIL
PROTECTED]
- * constructor}.
- *
- * @param constructor a constructor defined by this resolver's type or
any of
- * its superclasses.
- */
- public List<Type> getParameterTypes(Constructor constructor) {
-
checkArgument(implementedTypes.containsKey(constructor.getDeclaringClass()),
- "%s does not construct a supertype of %s", constructor, type);
- return resolveAll(constructor.getGenericParameterTypes());
- }
-
- /**
* Returns a list of the resolved generic exception types of [EMAIL
PROTECTED]
* constructor}.
*
@@ -216,15 +209,31 @@
}
/**
- * Returns a list of the resolved generic parameter types of [EMAIL
PROTECTED]
method}.
+ * Returns a list of the resolved generic parameter types of [EMAIL
PROTECTED]
methodOrConstructor}.
*
- * @param method a method defined by this resolver's type, its
superclasses
- * or implemented interfaces.
+ * @param methodOrConstructor a method or constructor defined by this
resolver's type, its
+ * superclasses or implemented interfaces.
*/
- public List<Type> getParameterTypes(Method method) {
- checkArgument(implementedTypes.containsKey(method.getDeclaringClass()),
- "%s is not defined by a supertype of %s", method, type);
- return resolveAll(method.getGenericParameterTypes());
+ public List<Type> getParameterTypes(Member methodOrConstructor) {
+ Type[] genericParameterTypes;
+
+ if (methodOrConstructor instanceof Method) {
+ Method method = (Method) methodOrConstructor;
+
checkArgument(implementedTypes.containsKey(method.getDeclaringClass()),
+ "%s is not defined by a supertype of %s", method, type);
+ genericParameterTypes = method.getGenericParameterTypes();
+
+ } else if (methodOrConstructor instanceof Constructor) {
+ Constructor constructor = (Constructor) methodOrConstructor;
+
checkArgument(implementedTypes.containsKey(constructor.getDeclaringClass()),
+ "%s does not construct a supertype of %s", constructor, type);
+ genericParameterTypes = constructor.getGenericParameterTypes();
+
+ } else {
+ throw new IllegalArgumentException("Not a method or a constructor: "
+ methodOrConstructor);
+ }
+
+ return resolveAll(genericParameterTypes);
}
/**
@@ -262,32 +271,5 @@
@Override public String toString() {
return "Resolver:" + type;
- }
-
- /**
- * A fully-qualified type variable, such as the "E" in java.util.List.
- */
- private static class FqTypeVar {
- private final Class<?> rawType;
- private final String name;
-
- private FqTypeVar(Class<?> rawType, String name) {
- this.rawType = rawType;
- this.name = name;
- }
-
- @Override public boolean equals(Object o) {
- return o instanceof FqTypeVar
- && ((FqTypeVar) o).rawType == rawType
- && ((FqTypeVar) o).name.equals(name);
- }
-
- @Override public int hashCode() {
- return rawType.hashCode() ^ name.hashCode();
- }
-
- @Override public String toString() {
- return rawType + ":" + name;
- }
}
}
Modified: trunk/src/com/google/inject/spi/InjectionPoint.java
==============================================================================
--- trunk/src/com/google/inject/spi/InjectionPoint.java (original)
+++ trunk/src/com/google/inject/spi/InjectionPoint.java Mon Nov 3 18:52:54
2008
@@ -26,6 +26,7 @@
import com.google.inject.internal.ErrorsException;
import com.google.inject.internal.MoreTypes;
import com.google.inject.internal.Nullability;
+import com.google.inject.internal.TypeResolver;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.annotation.Annotation;
@@ -61,25 +62,22 @@
this.optional = optional;
}
- InjectionPoint(Method method) {
+ InjectionPoint(TypeResolver typeResolver, Method method) {
this.member = method;
Inject inject = method.getAnnotation(Inject.class);
this.optional = inject.optional();
- this.dependencies = forMember(method,
method.getGenericParameterTypes(),
- method.getParameterAnnotations());
+ this.dependencies = forMember(method, typeResolver,
method.getParameterAnnotations());
}
- InjectionPoint(Constructor<?> constructor) {
+ InjectionPoint(TypeResolver typeResolver, Constructor<?> constructor) {
this.member = constructor;
this.optional = false;
- // TODO(jessewilson): make sure that if @Inject it exists, its not
optional
- this.dependencies = forMember(constructor,
constructor.getGenericParameterTypes(),
- constructor.getParameterAnnotations());
+ this.dependencies = forMember(constructor, typeResolver,
constructor.getParameterAnnotations());
}
- InjectionPoint(Field field) {
+ InjectionPoint(TypeResolver typeResolver, Field field) {
this.member = field;
Inject inject = field.getAnnotation(Inject.class);
@@ -90,7 +88,7 @@
Errors errors = new Errors(field);
Key<?> key = null;
try {
- key = Annotations.getKey(field.getGenericType(), field, annotations,
errors);
+ key = Annotations.getKey(typeResolver.getFieldType(field), field,
annotations, errors);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
@@ -100,14 +98,15 @@
newDependency(key, Nullability.allowsNull(annotations), -1));
}
- private ImmutableList<Dependency<?>> forMember(Member member, Type[]
genericParameterTypes,
- Annotation[][] annotations) {
+ private ImmutableList<Dependency<?>> forMember(Member member,
TypeResolver typeResolver,
+ Annotation[][] paramterAnnotations) {
Errors errors = new Errors(member);
- Iterator<Annotation[]> annotationsIterator =
Arrays.asList(annotations).iterator();
+ Iterator<Annotation[]> annotationsIterator =
Arrays.asList(paramterAnnotations).iterator();
List<Dependency<?>> dependencies = Lists.newArrayList();
int index = 0;
- for (Type parameterType : genericParameterTypes) {
+
+ for (Type parameterType : typeResolver.getParameterTypes(member)) {
try {
Annotation[] parameterAnnotations = annotationsIterator.next();
Key<?> key = Annotations.getKey(parameterType, member,
parameterAnnotations, errors);
@@ -157,7 +156,7 @@
@Override public boolean equals(Object o) {
return o instanceof InjectionPoint
- && member == ((InjectionPoint) o).member;
+ && member.equals(((InjectionPoint) o).member);
}
@Override public int hashCode() {
@@ -178,15 +177,17 @@
*
* @param type a concrete type with exactly one constructor annotated
[EMAIL PROTECTED] @[EMAIL PROTECTED] Inject},
* or a no-arguments constructor that is not private.
- * @throws RuntimeException if there is no injectable constructor, more
than one injectable
+ * @throws ConfigurationException if there is no injectable constructor,
more than one injectable
* constructor, or if parameters of the injectable constructor are
malformed, such as a
* parameter with multiple binding annotations.
*/
- public static InjectionPoint forConstructorOf(Class<?> type) {
+ public static InjectionPoint forConstructorOf(Type type) {
Errors errors = new Errors(type);
+ TypeResolver typeResolver = new TypeResolver(type);
+ Class<?> rawType = typeResolver.getRawType();
Constructor<?> injectableConstructor = null;
- for (Constructor<?> constructor : type.getDeclaredConstructors()) {
+ for (Constructor<?> constructor : rawType.getDeclaredConstructors()) {
Inject inject = constructor.getAnnotation(Inject.class);
if (inject != null) {
if (inject.optional()) {
@@ -194,7 +195,7 @@
}
if (injectableConstructor != null) {
- errors.tooManyConstructors(type);
+ errors.tooManyConstructors(rawType);
}
injectableConstructor = constructor;
@@ -205,24 +206,24 @@
errors.throwConfigurationExceptionIfErrorsExist();
if (injectableConstructor != null) {
- return new InjectionPoint(injectableConstructor);
+ return new InjectionPoint(typeResolver, injectableConstructor);
}
// If no annotated constructor is found, look for a no-arg constructor
instead.
try {
- Constructor<?> noArgConstructor = type.getDeclaredConstructor();
+ Constructor<?> noArgConstructor = rawType.getDeclaredConstructor();
// Disallow private constructors on non-private classes (unless they
have @Inject)
if (Modifier.isPrivate(noArgConstructor.getModifiers())
- && !Modifier.isPrivate(type.getModifiers())) {
- errors.missingConstructor(type);
+ && !Modifier.isPrivate(rawType.getModifiers())) {
+ errors.missingConstructor(rawType);
throw new ConfigurationException(errors.getMessages());
}
checkForMisplacedBindingAnnotations(noArgConstructor, errors);
- return new InjectionPoint(noArgConstructor);
+ return new InjectionPoint(typeResolver, noArgConstructor);
} catch (NoSuchMethodException e) {
- errors.missingConstructor(type);
+ errors.missingConstructor(rawType);
throw new ConfigurationException(errors.getMessages());
}
}
@@ -232,11 +233,11 @@
* All fields are added first, and then all methods. Within the fields,
supertype fields are added
* before subtype fields. Similarly, supertype methods are added before
subtype methods.
*
- * @throws RuntimeException if there is a malformed injection point on
[EMAIL PROTECTED] type}, such as a
- * field with multiple binding annotations. When such an exception
is thrown, the valid
+ * @throws ConfigurationException if there is a malformed injection
point on [EMAIL PROTECTED] type}, such as
+ * a field with multiple binding annotations. When such an
exception is thrown, the valid
* injection points are still added to the collection.
*/
- public static void addForStaticMethodsAndFields(Class<?> type,
Collection<InjectionPoint> sink) {
+ public static void addForStaticMethodsAndFields(Type type,
Collection<InjectionPoint> sink) {
Errors errors = new Errors();
addInjectionPoints(type, Factory.FIELDS, true, sink, errors);
addInjectionPoints(type, Factory.METHODS, true, sink, errors);
@@ -248,12 +249,11 @@
* All fields are added first, and then all methods. Within the fields,
supertype fields are added
* before subtype fields. Similarly, supertype methods are added before
subtype methods.
*
- * @throws RuntimeException if there is a malformed injection point on
[EMAIL PROTECTED] type}, such as a
- * field with multiple binding annotations. When such an exception
is thrown, the valid
+ * @throws ConfigurationException if there is a malformed injection
point on [EMAIL PROTECTED] type}, such as
+ * a field with multiple binding annotations. When such an
exception is thrown, the valid
* injection points are still added to the collection.
*/
- public static void addForInstanceMethodsAndFields(Class<?> type,
- Collection<InjectionPoint> sink) {
+ public static void addForInstanceMethodsAndFields(Type type,
Collection<InjectionPoint> sink) {
// TODO (crazybob): Filter out overridden members.
Errors errors = new Errors();
addInjectionPoints(type, Factory.FIELDS, false, sink, errors);
@@ -269,24 +269,27 @@
}
}
- private static <M extends Member & AnnotatedElement> void
addInjectionPoints(Class<?> type,
+ private static <M extends Member & AnnotatedElement> void
addInjectionPoints(Type type,
Factory<M> factory, boolean statics, Collection<InjectionPoint>
injectionPoints,
Errors errors) {
if (type == Object.class) {
return;
}
+ TypeResolver typeResolver = new TypeResolver(type);
+
// Add injectors for superclass first.
- addInjectionPoints(type.getSuperclass(), factory, statics,
injectionPoints, errors);
+ Type superType =
typeResolver.getSupertype(MoreTypes.getRawType(type).getSuperclass());
+ addInjectionPoints(superType, factory, statics, injectionPoints,
errors);
// Add injectors for all members next
- addInjectorsForMembers(type, factory, statics, injectionPoints,
errors);
+ addInjectorsForMembers(typeResolver, factory, statics,
injectionPoints, errors);
}
- private static <M extends Member & AnnotatedElement> void
addInjectorsForMembers(Class<?> type,
- Factory<M> factory, boolean statics, Collection<InjectionPoint>
injectionPoints,
- Errors errors) {
- for (M member : factory.getMembers(type)) {
+ private static <M extends Member & AnnotatedElement> void
addInjectorsForMembers(
+ TypeResolver typeResolver, Factory<M> factory, boolean statics,
+ Collection<InjectionPoint> injectionPoints, Errors errors) {
+ for (M member : factory.getMembers(typeResolver.getRawType())) {
if (isStatic(member) != statics) {
continue;
}
@@ -297,7 +300,7 @@
}
try {
- injectionPoints.add(factory.create(member, errors));
+ injectionPoints.add(factory.create(typeResolver, member, errors));
} catch (ConfigurationException ignorable) {
if (!inject.optional()) {
errors.merge(ignorable.getErrorMessages());
@@ -315,8 +318,8 @@
public Field[] getMembers(Class<?> type) {
return type.getDeclaredFields();
}
- public InjectionPoint create(Field member, Errors errors) {
- return new InjectionPoint(member);
+ public InjectionPoint create(TypeResolver typeResolver, Field
member, Errors errors) {
+ return new InjectionPoint(typeResolver, member);
}
};
@@ -324,14 +327,14 @@
public Method[] getMembers(Class<?> type) {
return type.getDeclaredMethods();
}
- public InjectionPoint create(Method member, Errors errors) {
+ public InjectionPoint create(TypeResolver typeResolver, Method
member, Errors errors) {
checkForMisplacedBindingAnnotations(member, errors);
- return new InjectionPoint(member);
+ return new InjectionPoint(typeResolver, member);
}
};
M[] getMembers(Class<?> type);
- InjectionPoint create(M member, Errors errors);
+ InjectionPoint create(TypeResolver typeResolver, M member, Errors
errors);
}
private static final long serialVersionUID = 0;
Modified: trunk/test/com/google/inject/GenericInjectionTest.java
==============================================================================
--- trunk/test/com/google/inject/GenericInjectionTest.java (original)
+++ trunk/test/com/google/inject/GenericInjectionTest.java Mon Nov 3
18:52:54 2008
@@ -16,8 +16,14 @@
package com.google.inject;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.util.Modules;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import junit.framework.TestCase;
/**
@@ -69,5 +75,97 @@
static class Parameterized<T> {
@Inject
Parameterized() { }
+ }
+
+ public void testInjectingParameterizedDependenciesForImplicitBinding() {
+ assertParameterizedDepsInjected(new Key<ParameterizedDeps<String,
Integer>>() {},
+ Modules.EMPTY_MODULE);
+ }
+
+ public void testInjectingParameterizedDependenciesForBindingTarget() {
+ final TypeLiteral<ParameterizedDeps<String, Integer>> type
+ = new TypeLiteral<ParameterizedDeps<String, Integer>>() {};
+
+ assertParameterizedDepsInjected(Key.get(Object.class), new
AbstractModule() {
+ protected void configure() {
+ bind(Object.class).to(type);
+ }
+ });
+ }
+
+ public void testInjectingParameterizedDependenciesForBindingSource() {
+ final TypeLiteral<ParameterizedDeps<String, Integer>> type
+ = new TypeLiteral<ParameterizedDeps<String, Integer>>() {};
+
+ assertParameterizedDepsInjected(Key.get(type), new AbstractModule() {
+ protected void configure() {
+ bind(type);
+ }
+ });
+ }
+
+ public void testBindingToSubtype() {
+ final TypeLiteral<ParameterizedDeps<String, Integer>> type
+ = new TypeLiteral<ParameterizedDeps<String, Integer>>() {};
+
+ assertParameterizedDepsInjected(Key.get(type), new AbstractModule() {
+ protected void configure() {
+ bind(type).to(new TypeLiteral<SubParameterizedDeps<String, Long,
Integer>>() {});
+ }
+ });
+ }
+
+ public void testBindingSubtype() {
+ final TypeLiteral<SubParameterizedDeps<String, Long, Integer>> type
+ = new TypeLiteral<SubParameterizedDeps<String, Long, Integer>>()
{};
+
+ assertParameterizedDepsInjected(Key.get(type), new AbstractModule() {
+ protected void configure() {
+ bind(type);
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ public void assertParameterizedDepsInjected(Key<?> key, Module
bindingModule) {
+ Module bindDataModule = new AbstractModule() {
+ protected void configure() {}
+ @Provides Map<String, Integer> provideMap() {
+ return ImmutableMap.of("one", 1, "two", 2);
+ }
+ @Provides Set<String> provideSet(Map<String, Integer> map) {
+ return map.keySet();
+ }
+ @Provides Collection<Integer> provideCollection(Map<String, Integer>
map) {
+ return map.values();
+ }
+ };
+
+ Injector injector = Guice.createInjector(bindDataModule,
bindingModule);
+ ParameterizedDeps<String, Integer> parameterizedDeps
+ = (ParameterizedDeps<String, Integer>) injector.getInstance(key);
+ assertEquals(ImmutableMap.of("one", 1, "two", 2),
parameterizedDeps.map);
+ assertEquals(ImmutableSet.of("one", "two"), parameterizedDeps.keys);
+ assertEquals(ImmutableSet.of(1, 2),
ImmutableSet.copyOf(parameterizedDeps.values));
+ }
+
+ static class SubParameterizedDeps<A, B, C> extends ParameterizedDeps<A,
C> {
+ @Inject SubParameterizedDeps(Set<A> keys) {
+ super(keys);
+ }
+ }
+
+ static class ParameterizedDeps<K, V> {
+ @Inject private Map<K, V> map;
+ private Set<K> keys;
+ private Collection<V> values;
+
+ @Inject ParameterizedDeps(Set<K> keys) {
+ this.keys = keys;
+ }
+
+ @Inject void method(Collection<V> values) {
+ this.values = values;
+ }
}
}
Modified: trunk/test/com/google/inject/ScopesTest.java
==============================================================================
--- trunk/test/com/google/inject/ScopesTest.java (original)
+++ trunk/test/com/google/inject/ScopesTest.java Mon Nov 3 18:52:54 2008
@@ -306,7 +306,7 @@
} catch (ConfigurationException expected) {
assertContains(expected.getMessage(),
"1) More than one scope annotation was found: ",
- "while locating binding for " +
SingletonAndCustomScoped.class.getName());
+ "while locating " + SingletonAndCustomScoped.class.getName());
}
}
Modified: trunk/test/com/google/inject/TypeLiteralTest.java
==============================================================================
--- trunk/test/com/google/inject/TypeLiteralTest.java (original)
+++ trunk/test/com/google/inject/TypeLiteralTest.java Mon Nov 3 18:52:54
2008
@@ -87,6 +87,12 @@
assertEquals(arrayAsClass, arrayAsType);
}
+ public void testEqualityOfMultidimensionalGenericArrayAndClassArray() {
+ TypeLiteral<String[][][]> arrayAsClass =
TypeLiteral.get(String[][][].class);
+ TypeLiteral<String[][][]> arrayAsType = new
TypeLiteral<String[][][]>() {};
+ assertEquals(arrayAsClass, arrayAsType);
+ }
+
public void testTypeLiteralsMustHaveRawTypes() {
try {
TypeLiteral.get(Types.subtypeOf(Runnable.class));
Modified: trunk/test/com/google/inject/spi/InjectionPointTest.java
==============================================================================
--- trunk/test/com/google/inject/spi/InjectionPointTest.java (original)
+++ trunk/test/com/google/inject/spi/InjectionPointTest.java Mon Nov 3
18:52:54 2008
@@ -16,18 +16,25 @@
package com.google.inject.spi;
+import com.google.common.collect.ImmutableSet;
import static com.google.common.collect.Iterables.getOnlyElement;
+import com.google.common.collect.Sets;
import static com.google.inject.Asserts.assertEqualsBothWays;
import static com.google.inject.Asserts.assertSimilarWhenReserialized;
import com.google.inject.Inject;
import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
import com.google.inject.internal.ErrorsException;
+import com.google.inject.internal.TypeResolver;
import com.google.inject.name.Named;
-import com.google.inject.name.Names;
+import static com.google.inject.name.Names.named;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Map;
+import java.util.Set;
import junit.framework.Assert;
import junit.framework.TestCase;
@@ -44,13 +51,14 @@
}
public void testFieldInjectionPoint() throws NoSuchFieldException,
IOException, ErrorsException {
+ TypeResolver typeResolver = new TypeResolver(getClass());
Field fooField = getClass().getField("foo");
- InjectionPoint injectionPoint = new InjectionPoint(fooField);
+ InjectionPoint injectionPoint = new InjectionPoint(typeResolver,
fooField);
assertSame(fooField, injectionPoint.getMember());
assertFalse(injectionPoint.isOptional());
assertEquals(getClass().getName() + ".foo", injectionPoint.toString());
- assertEqualsBothWays(injectionPoint, new InjectionPoint(fooField));
+ assertEqualsBothWays(injectionPoint, new InjectionPoint(typeResolver,
fooField));
assertSimilarWhenReserialized(injectionPoint);
Dependency<?> dependency =
getOnlyElement(injectionPoint.getDependencies());
@@ -58,20 +66,22 @@
+ getClass().getName() + ".foo", dependency.toString());
assertEquals(fooField, dependency.getInjectionPoint().getMember());
assertEquals(-1, dependency.getParameterIndex());
- Assert.assertEquals(Key.get(String.class, Names.named("a")),
dependency.getKey());
+ Assert.assertEquals(Key.get(String.class, named("a")),
dependency.getKey());
assertEquals(false, dependency.isNullable());
assertSimilarWhenReserialized(dependency);
assertEqualsBothWays(dependency,
- getOnlyElement(new InjectionPoint(fooField).getDependencies()));
+ getOnlyElement(new InjectionPoint(typeResolver,
fooField).getDependencies()));
}
public void testMethodInjectionPoint() throws Exception {
+ TypeResolver typeResolver = new TypeResolver(getClass());
+
Method barMethod = getClass().getMethod("bar", String.class);
- InjectionPoint injectionPoint = new InjectionPoint(barMethod);
+ InjectionPoint injectionPoint = new InjectionPoint(typeResolver,
barMethod);
assertSame(barMethod, injectionPoint.getMember());
assertFalse(injectionPoint.isOptional());
assertEquals(getClass().getName() + ".bar()",
injectionPoint.toString());
- assertEqualsBothWays(injectionPoint, new InjectionPoint(barMethod));
+ assertEqualsBothWays(injectionPoint, new InjectionPoint(typeResolver,
barMethod));
assertSimilarWhenReserialized(injectionPoint);
Dependency<?> dependency =
getOnlyElement(injectionPoint.getDependencies());
@@ -79,21 +89,23 @@
+ getClass().getName() + ".bar()[0]", dependency.toString());
assertEquals(barMethod, dependency.getInjectionPoint().getMember());
assertEquals(0, dependency.getParameterIndex());
- assertEquals(Key.get(String.class, Names.named("b")),
dependency.getKey());
+ assertEquals(Key.get(String.class, named("b")), dependency.getKey());
assertEquals(false, dependency.isNullable());
assertSimilarWhenReserialized(dependency);
assertEqualsBothWays(dependency,
- getOnlyElement(new InjectionPoint(barMethod).getDependencies()));
+ getOnlyElement(new InjectionPoint(typeResolver,
barMethod).getDependencies()));
}
public void testConstructorInjectionPoint() throws
NoSuchMethodException, IOException,
ErrorsException {
+ TypeResolver typeResolver = new TypeResolver(Constructable.class);
+
Constructor<?> constructor =
Constructable.class.getConstructor(String.class);
- InjectionPoint injectionPoint = new InjectionPoint(constructor);
+ InjectionPoint injectionPoint = new InjectionPoint(typeResolver,
constructor);
assertSame(constructor, injectionPoint.getMember());
assertFalse(injectionPoint.isOptional());
assertEquals(Constructable.class.getName() + ".<init>()",
injectionPoint.toString());
- assertEqualsBothWays(injectionPoint, new InjectionPoint(constructor));
+ assertEqualsBothWays(injectionPoint, new InjectionPoint(typeResolver,
constructor));
assertSimilarWhenReserialized(injectionPoint);
Dependency<?> dependency =
getOnlyElement(injectionPoint.getDependencies());
@@ -101,22 +113,76 @@
+ Constructable.class.getName() + ".<init>()[0]",
dependency.toString());
assertEquals(constructor, dependency.getInjectionPoint().getMember());
assertEquals(0, dependency.getParameterIndex());
- assertEquals(Key.get(String.class, Names.named("c")),
dependency.getKey());
+ assertEquals(Key.get(String.class, named("c")), dependency.getKey());
assertEquals(false, dependency.isNullable());
assertSimilarWhenReserialized(dependency);
assertEqualsBothWays(dependency,
- getOnlyElement(new InjectionPoint(constructor).getDependencies()));
+ getOnlyElement(new InjectionPoint(typeResolver,
constructor).getDependencies()));
}
public void testUnattachedDependency() throws IOException {
- Dependency<String> dependency = Dependency.get(Key.get(String.class,
Names.named("d")));
+ Dependency<String> dependency = Dependency.get(Key.get(String.class,
named("d")));
assertEquals("Key[type=java.lang.String,
[EMAIL PROTECTED](value=d)]",
dependency.toString());
assertNull(dependency.getInjectionPoint());
assertEquals(-1, dependency.getParameterIndex());
- assertEquals(Key.get(String.class, Names.named("d")),
dependency.getKey());
+ assertEquals(Key.get(String.class, named("d")), dependency.getKey());
assertEquals(true, dependency.isNullable());
assertSimilarWhenReserialized(dependency);
- assertEqualsBothWays(dependency, Dependency.get(Key.get(String.class,
Names.named("d"))));
+ assertEqualsBothWays(dependency, Dependency.get(Key.get(String.class,
named("d"))));
+ }
+
+ public void testForConstructorOf() {
+ InjectionPoint injectionPoint =
InjectionPoint.forConstructorOf(Constructable.class);
+ assertEquals(Constructable.class.getName() + ".<init>()",
injectionPoint.toString());
+ }
+
+ public void testAddForInstanceMethodsAndFields() throws Exception {
+ Method instanceMethod =
HasInjections.class.getMethod("instanceMethod", String.class);
+ Field instanceField = HasInjections.class.getField("instanceField");
+
+ Set<InjectionPoint> sink = Sets.newHashSet();
+ InjectionPoint.addForInstanceMethodsAndFields(HasInjections.class,
sink);
+ assertEquals(ImmutableSet.of(
+ new InjectionPoint(new TypeResolver(HasInjections.class),
instanceMethod),
+ new InjectionPoint(new TypeResolver(HasInjections.class),
instanceField)),
+ sink);
+ }
+
+ public void testAddForStaticMethodsAndFields() throws Exception {
+ Method staticMethod = HasInjections.class.getMethod("staticMethod",
String.class);
+ Field staticField = HasInjections.class.getField("staticField");
+
+ Set<InjectionPoint> sink = Sets.newHashSet();
+ InjectionPoint.addForStaticMethodsAndFields(HasInjections.class, sink);
+ assertEquals(ImmutableSet.of(
+ new InjectionPoint(new TypeResolver(HasInjections.class),
staticMethod),
+ new InjectionPoint(new TypeResolver(HasInjections.class),
staticField)),
+ sink);
+ }
+
+ static class HasInjections {
+ @Inject public static void staticMethod(@Named("a") String a) {}
+ @Inject @Named("c") public static String staticField;
+ @Inject public void instanceMethod(@Named("d") String d) {}
+ @Inject @Named("f") public String instanceField;
+ }
+
+ public void testAddForParameterizedInjections() {
+ Set<InjectionPoint> sink = Sets.newHashSet();
+ Type type = new TypeLiteral<ParameterizedInjections<String>>()
{}.getType();
+
+ InjectionPoint constructor = InjectionPoint.forConstructorOf(type);
+ assertEquals(new Key<Map<String, String>>() {},
+ getOnlyElement(constructor.getDependencies()).getKey());
+
+ InjectionPoint.addForInstanceMethodsAndFields(type, sink);
+ InjectionPoint field = getOnlyElement(sink);
+ assertEquals(new Key<Set<String>>() {},
getOnlyElement(field.getDependencies()).getKey());
+ }
+
+ static class ParameterizedInjections<T> {
+ @Inject Set<T> setOfTees;
+ @Inject public ParameterizedInjections(Map<T, T> map) {}
}
}
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"google-guice-dev" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/google-guice-dev?hl=en
-~----------~----~----~----~------~----~------~--~---