toConstructor() core implementation. This still requires test coverage, and maybe a review of the API.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@1022 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/internal/BindingProcessor.java b/src/com/google/inject/internal/BindingProcessor.java
index acacc87..103fd8b 100644
--- a/src/com/google/inject/internal/BindingProcessor.java
+++ b/src/com/google/inject/internal/BindingProcessor.java
@@ -85,6 +85,18 @@
((BindingImpl<?>) command).getScoping(), injector, errors);
command.acceptTargetVisitor(new BindingTargetVisitor<T, Void>() {
+ public Void visit(ConstructorBinding<? extends T> binding) {
+ try {
+ ConstructorBindingImpl<T> onInjector = ConstructorBindingImpl.create(injector, key,
+ binding.getConstructor(), source, scoping, errors);
+ scheduleInitialization(onInjector);
+ putBinding(onInjector);
+ } catch (ErrorsException e) {
+ errors.merge(e.getErrors());
+ putBinding(invalidBinding(injector, key, source));
+ }
+ return null;
+ }
public Void visit(InstanceBinding<? extends T> binding) {
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
@@ -148,26 +160,15 @@
}
// This cast is safe after the preceeding check.
- final BindingImpl<T> binding;
try {
- binding = injector.createUnitializedBinding(key, scoping, source, errors);
+ BindingImpl<T> binding = injector.createUnitializedBinding(key, scoping, source, errors);
+ scheduleInitialization(binding);
putBinding(binding);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
putBinding(invalidBinding(injector, key, source));
- return null;
}
- uninitializedBindings.add(new Runnable() {
- public void run() {
- try {
- binding.getInjector().initializeBinding(binding, errors.withSource(source));
- } catch (ErrorsException e) {
- errors.merge(e.getErrors());
- }
- }
- });
-
return null;
}
@@ -179,12 +180,20 @@
throw new IllegalArgumentException("Cannot apply a non-module element");
}
- public Void visit(ConstructorBinding<? extends T> binding) {
+ public Void visit(ProviderBinding<? extends T> binding) {
throw new IllegalArgumentException("Cannot apply a non-module element");
}
- public Void visit(ProviderBinding<? extends T> binding) {
- throw new IllegalArgumentException("Cannot apply a non-module element");
+ private void scheduleInitialization(final BindingImpl<?> binding) {
+ uninitializedBindings.add(new Runnable() {
+ public void run() {
+ try {
+ binding.getInjector().initializeBinding(binding, errors.withSource(source));
+ } catch (ErrorsException e) {
+ errors.merge(e.getErrors());
+ }
+ }
+ });
}
});
diff --git a/src/com/google/inject/internal/ConstructionContext.java b/src/com/google/inject/internal/ConstructionContext.java
index b5fd538..829f102 100644
--- a/src/com/google/inject/internal/ConstructionContext.java
+++ b/src/com/google/inject/internal/ConstructionContext.java
@@ -73,8 +73,7 @@
invocationHandlers = new ArrayList<DelegatingInvocationHandler<T>>();
}
- DelegatingInvocationHandler<T> invocationHandler
- = new DelegatingInvocationHandler<T>();
+ DelegatingInvocationHandler<T> invocationHandler = new DelegatingInvocationHandler<T>();
invocationHandlers.add(invocationHandler);
ClassLoader classLoader = BytecodeGen.getClassLoader(expectedType);
diff --git a/src/com/google/inject/internal/ConstructorBindingImpl.java b/src/com/google/inject/internal/ConstructorBindingImpl.java
index 91de6db..5154126 100644
--- a/src/com/google/inject/internal/ConstructorBindingImpl.java
+++ b/src/com/google/inject/internal/ConstructorBindingImpl.java
@@ -18,13 +18,17 @@
import com.google.inject.Binder;
import com.google.inject.Key;
+import com.google.inject.ConfigurationException;
import static com.google.inject.internal.Preconditions.checkState;
+import static com.google.inject.internal.Annotations.findScopeAnnotation;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.ConstructorBinding;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.InjectionPoint;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -32,34 +36,78 @@
final class ConstructorBindingImpl<T> extends BindingImpl<T> implements ConstructorBinding<T> {
private final Factory<T> factory;
+ private final InjectionPoint constructorInjectionPoint;
private ConstructorBindingImpl(InjectorImpl injector, Key<T> key, Object source,
- InternalFactory<? extends T> scopedFactory, Scoping scoping, Factory<T> factory) {
+ InternalFactory<? extends T> scopedFactory, Scoping scoping, Factory<T> factory,
+ InjectionPoint constructorInjectionPoint) {
super(injector, key, source, scopedFactory, scoping);
this.factory = factory;
+ this.constructorInjectionPoint = constructorInjectionPoint;
}
public ConstructorBindingImpl(Key<T> key, Object source, Scoping scoping,
- InjectionPoint constructorInjector, Set<InjectionPoint> injectionPoints) {
+ InjectionPoint constructorInjectionPoint, Set<InjectionPoint> injectionPoints) {
super(source, key, scoping);
this.factory = new Factory<T>();
ConstructionProxy<T> constructionProxy
- = new DefaultConstructionProxyFactory<T>(constructorInjector).create();
+ = new DefaultConstructionProxyFactory<T>(constructorInjectionPoint).create();
+ this.constructorInjectionPoint = constructorInjectionPoint;
factory.constructorInjector = new ConstructorInjector<T>(
injectionPoints, constructionProxy, null, null);
}
- static <T> ConstructorBindingImpl<T> create(
- InjectorImpl injector, Key<T> key, Object source, Scoping scoping) {
+ /**
+ * @param constructorInjector the constructor to use, or {@code null} to use the default.
+ */
+ static <T> ConstructorBindingImpl<T> create(InjectorImpl injector, Key<T> key,
+ InjectionPoint constructorInjector, Object source, Scoping scoping, Errors errors)
+ throws ErrorsException {
+ int numErrors = errors.size();
+ Class<? super T> rawType = key.getTypeLiteral().getRawType();
+
+ // We can't inject abstract classes.
+ if (Modifier.isAbstract(rawType.getModifiers())) {
+ errors.missingImplementation(key);
+ }
+
+ // Error: Inner class.
+ if (Classes.isInnerClass(rawType)) {
+ errors.cannotInjectInnerClass(rawType);
+ }
+
+ // if no scope is specified, look for a scoping annotation on the concrete class
+ if (!scoping.isExplicitlyScoped()) {
+ Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, rawType);
+ if (scopeAnnotation != null) {
+ scoping = Scoping.makeInjectable(Scoping.forAnnotation(scopeAnnotation),
+ injector, errors.withSource(rawType));
+ }
+ }
+
+ errors.throwIfNewErrors(numErrors);
+
Factory<T> factoryFactory = new Factory<T>();
InternalFactory<? extends T> scopedFactory
= Scoping.scope(key, injector, factoryFactory, scoping);
+
+ // Find a constructor annotated @Inject
+ if (constructorInjector == null) {
+ try {
+ constructorInjector = InjectionPoint.forConstructorOf(key.getTypeLiteral());
+ } catch (ConfigurationException e) {
+ throw errors.merge(e.getErrorMessages()).toException();
+ }
+ }
+
return new ConstructorBindingImpl<T>(
- injector, key, source, scopedFactory, scoping, factoryFactory);
+ injector, key, source, scopedFactory, scoping, factoryFactory, constructorInjector);
}
+ @SuppressWarnings("unchecked") // the result type always agrees with the ConstructorInjector type
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
- factory.constructorInjector = injector.constructors.get(getKey().getTypeLiteral(), errors);
+ factory.constructorInjector
+ = (ConstructorInjector<T>) injector.constructors.get(constructorInjectionPoint, errors);
}
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
@@ -93,12 +141,12 @@
@Override protected BindingImpl<T> withScoping(Scoping scoping) {
return new ConstructorBindingImpl<T>(
- null, getKey(), getSource(), factory, scoping, factory);
+ null, getKey(), getSource(), factory, scoping, factory, constructorInjectionPoint);
}
@Override protected BindingImpl<T> withKey(Key<T> key) {
return new ConstructorBindingImpl<T>(
- null, key, getSource(), factory, getScoping(), factory);
+ null, key, getSource(), factory, getScoping(), factory, constructorInjectionPoint);
}
public void applyTo(Binder binder) {
diff --git a/src/com/google/inject/internal/ConstructorInjectorStore.java b/src/com/google/inject/internal/ConstructorInjectorStore.java
index af0b07d..e1122a5 100644
--- a/src/com/google/inject/internal/ConstructorInjectorStore.java
+++ b/src/com/google/inject/internal/ConstructorInjectorStore.java
@@ -16,8 +16,6 @@
package com.google.inject.internal;
-import com.google.inject.ConfigurationException;
-import com.google.inject.TypeLiteral;
import static com.google.inject.internal.Iterables.concat;
import com.google.inject.spi.InjectionPoint;
@@ -29,12 +27,12 @@
final class ConstructorInjectorStore {
private final InjectorImpl injector;
- private final FailableCache<TypeLiteral<?>, ConstructorInjector<?>> cache
- = new FailableCache<TypeLiteral<?>, ConstructorInjector<?>> () {
+ private final FailableCache<InjectionPoint, ConstructorInjector<?>> cache
+ = new FailableCache<InjectionPoint, ConstructorInjector<?>> () {
@SuppressWarnings("unchecked")
- protected ConstructorInjector<?> create(TypeLiteral<?> type, Errors errors)
+ protected ConstructorInjector<?> create(InjectionPoint constructorInjector, Errors errors)
throws ErrorsException {
- return createConstructor(type, errors);
+ return createConstructor(constructorInjector, errors);
}
};
@@ -45,26 +43,21 @@
/**
* Returns a new complete constructor injector with injection listeners registered.
*/
- @SuppressWarnings("unchecked") // the ConstructorInjector type always agrees with the passed type
- public <T> ConstructorInjector<T> get(TypeLiteral<T> key, Errors errors) throws ErrorsException {
- return (ConstructorInjector<T>) cache.get(key, errors);
+ public ConstructorInjector<?> get(InjectionPoint constructorInjector, Errors errors)
+ throws ErrorsException {
+ return cache.get(constructorInjector, errors);
}
- private <T> ConstructorInjector<T> createConstructor(TypeLiteral<T> type, Errors errors)
+ private <T> ConstructorInjector<T> createConstructor(InjectionPoint injectionPoint, Errors errors)
throws ErrorsException {
int numErrorsBefore = errors.size();
- InjectionPoint injectionPoint;
- try {
- injectionPoint = InjectionPoint.forConstructorOf(type);
- } catch (ConfigurationException e) {
- errors.merge(e.getErrorMessages());
- throw errors.toException();
- }
-
SingleParameterInjector<?>[] constructorParameterInjectors
= injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
- MembersInjectorImpl<T> membersInjector = injector.membersInjectorStore.get(type, errors);
+
+ @SuppressWarnings("unchecked") // the injector type agrees with the injection point type
+ MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>) injector.membersInjectorStore
+ .get(injectionPoint.getDeclaringType(), errors);
/*if[AOP]*/
ImmutableList<MethodAspect> injectorAspects = injector.state.getMethodAspects();
diff --git a/src/com/google/inject/internal/InjectorImpl.java b/src/com/google/inject/internal/InjectorImpl.java
index bcd653b..0ec25d3 100644
--- a/src/com/google/inject/internal/InjectorImpl.java
+++ b/src/com/google/inject/internal/InjectorImpl.java
@@ -28,7 +28,6 @@
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.TypeLiteral;
-import static com.google.inject.internal.Annotations.findScopeAnnotation;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.ConvertedConstantBinding;
import com.google.inject.spi.Dependency;
@@ -36,10 +35,8 @@
import com.google.inject.spi.ProviderBinding;
import com.google.inject.spi.ProviderKeyBinding;
import com.google.inject.util.Providers;
-import java.lang.annotation.Annotation;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collections;
@@ -412,27 +409,7 @@
return createProvidedByBinding(key, scoping, providedBy, 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(rawType.getModifiers())) {
- throw errors.missingImplementation(key).toException();
- }
-
- // Error: Inner class.
- if (Classes.isInnerClass(rawType)) {
- throw errors.cannotInjectInnerClass(rawType).toException();
- }
-
- if (!scoping.isExplicitlyScoped()) {
- Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, rawType);
- if (scopeAnnotation != null) {
- scoping = Scoping.makeInjectable(Scoping.forAnnotation(scopeAnnotation),
- this, errors.withSource(rawType));
- }
- }
-
- return ConstructorBindingImpl.create(this, key, source, scoping);
+ return ConstructorBindingImpl.create(this, key, null, source, scoping, errors);
}
/**
diff --git a/src/com/google/inject/spi/InjectionPoint.java b/src/com/google/inject/spi/InjectionPoint.java
index 3792add..7ab3e1e 100644
--- a/src/com/google/inject/spi/InjectionPoint.java
+++ b/src/com/google/inject/spi/InjectionPoint.java
@@ -54,25 +54,30 @@
private final boolean optional;
private final Member member;
+ private final TypeLiteral<?> declaringType;
private final ImmutableList<Dependency<?>> dependencies;
- InjectionPoint(TypeLiteral<?> type, Method method) {
+ InjectionPoint(TypeLiteral<?> declaringType, Method method) {
this.member = method;
+ this.declaringType = declaringType;
Inject inject = method.getAnnotation(Inject.class);
this.optional = inject.optional();
- this.dependencies = forMember(method, type, method.getParameterAnnotations());
+ this.dependencies = forMember(method, declaringType, method.getParameterAnnotations());
}
- InjectionPoint(TypeLiteral<?> type, Constructor<?> constructor) {
+ InjectionPoint(TypeLiteral<?> declaringType, Constructor<?> constructor) {
this.member = constructor;
+ this.declaringType = declaringType;
this.optional = false;
- this.dependencies = forMember(constructor, type, constructor.getParameterAnnotations());
+ this.dependencies = forMember(
+ constructor, declaringType, constructor.getParameterAnnotations());
}
- InjectionPoint(TypeLiteral<?> type, Field field) {
+ InjectionPoint(TypeLiteral<?> declaringType, Field field) {
this.member = field;
+ this.declaringType = declaringType;
Inject inject = field.getAnnotation(Inject.class);
this.optional = inject.optional();
@@ -82,7 +87,7 @@
Errors errors = new Errors(field);
Key<?> key = null;
try {
- key = Annotations.getKey(type.getFieldType(field), field, annotations, errors);
+ key = Annotations.getKey(declaringType.getFieldType(field), field, annotations, errors);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
@@ -148,6 +153,15 @@
return optional;
}
+ /**
+ * Returns the generic type that defines this injection point. If the member exists on a
+ * parameterized type, the result will include more type information than the member's {@link
+ * Member#getDeclaringClass() raw declaring class}.
+ */
+ public TypeLiteral<?> getDeclaringType() {
+ return declaringType;
+ }
+
@Override public boolean equals(Object o) {
return o instanceof InjectionPoint
&& member.equals(((InjectionPoint) o).member);
@@ -162,14 +176,13 @@
}
/**
- * Returns a new injection point for the specified constructor. If any parameter of
- * {@code constructor} includes a type variable (such as {@code List<T>}), prefer the overload
- * that includes a type literal.
+ * Returns a new injection point for the specified constructor. If the declaring type of {@code
+ * constructor} is parameterized (such as {@code List<T>}), prefer the overload that includes a
+ * type literal.
*
* @param constructor any single constructor present on {@code type}.
*/
public static <T> InjectionPoint forConstructor(Constructor<T> constructor) {
- // TODO: verify that constructor is valid? (defined on a non-abstract class, etc.)
return new InjectionPoint(TypeLiteral.get(constructor.getDeclaringClass()), constructor);
}
@@ -187,7 +200,6 @@
.throwConfigurationExceptionIfErrorsExist();
}
- // TODO: verify that constructor is valid? (defined on a non-abstract class, etc.)
return new InjectionPoint(type, constructor);
}
diff --git a/test/com/google/inject/BindingTest.java b/test/com/google/inject/BindingTest.java
index 48feea2..2bca3f7 100644
--- a/test/com/google/inject/BindingTest.java
+++ b/test/com/google/inject/BindingTest.java
@@ -21,6 +21,7 @@
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import java.lang.reflect.Constructor;
import junit.framework.TestCase;
/**
@@ -206,4 +207,30 @@
@Inject TooManyConstructors(Injector i) {}
@Inject TooManyConstructors() {}
}
+
+ public void testToConstructorBindings() throws NoSuchMethodException {
+ final Constructor<C> constructor = C.class.getConstructor(Stage.class);
+
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ bind(C.class).toConstructor(constructor);
+ }
+ });
+
+ assertEquals(Stage.DEVELOPMENT, injector.getInstance(C.class).stage);
+ }
+
+ public static class C {
+ private final Stage stage;
+
+ public C(Stage stage) {
+ this.stage = stage;
+ }
+
+ @Inject C() {
+ this.stage = null;
+ }
+ }
+
+
}