Author: limpbizkit
Date: Sun Jun 7 12:37:28 2009
New Revision: 1005
Modified:
trunk/src/com/google/inject/binder/LinkedBindingBuilder.java
trunk/src/com/google/inject/internal/BindingBuilder.java
trunk/src/com/google/inject/internal/ConstructorBindingImpl.java
trunk/src/com/google/inject/internal/ConstructorInjector.java
trunk/src/com/google/inject/internal/Errors.java
trunk/src/com/google/inject/spi/InjectionPoint.java
trunk/test/com/google/inject/spi/ElementsTest.java
trunk/test/com/google/inject/spi/InjectionPointTest.java
Log:
toConstructor SPI support. We can now create the module models for
toConstructor bindings. I haven't yet added the code that takes these
bindings and uses them when creating an Injector.
Modified: trunk/src/com/google/inject/binder/LinkedBindingBuilder.java
==============================================================================
--- trunk/src/com/google/inject/binder/LinkedBindingBuilder.java
(original)
+++ trunk/src/com/google/inject/binder/LinkedBindingBuilder.java Sun Jun
7
12:37:28 2009
@@ -19,6 +19,7 @@
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
+import java.lang.reflect.Constructor;
/**
* See the EDSL examples at {...@link com.google.inject.Binder}.
@@ -73,4 +74,9 @@
*/
ScopedBindingBuilder toProvider(
Key<? extends Provider<? extends T>> providerKey);
+
+ /**
+ * See the EDSL examples at {...@link com.google.inject.Binder}.
+ */
+ ScopedBindingBuilder toConstructor(Constructor<? extends T> constructor);
}
Modified: trunk/src/com/google/inject/internal/BindingBuilder.java
==============================================================================
--- trunk/src/com/google/inject/internal/BindingBuilder.java (original)
+++ trunk/src/com/google/inject/internal/BindingBuilder.java Sun Jun 7
12:37:28 2009
@@ -22,11 +22,13 @@
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.AnnotatedBindingBuilder;
+import com.google.inject.binder.ScopedBindingBuilder;
import static com.google.inject.internal.Preconditions.checkNotNull;
import com.google.inject.spi.Element;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.Message;
import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Set;
@@ -78,9 +80,7 @@
try {
injectionPoints =
InjectionPoint.forInstanceMethodsAndFields(instance.getClass());
} catch (ConfigurationException e) {
- for (Message message : e.getErrorMessages()) {
- binder.addError(message);
- }
+ copyErrorsToBinder(e);
injectionPoints = e.getPartialValue();
}
} else {
@@ -102,9 +102,7 @@
try {
injectionPoints =
InjectionPoint.forInstanceMethodsAndFields(provider.getClass());
} catch (ConfigurationException e) {
- for (Message message : e.getErrorMessages()) {
- binder.addError(message);
- }
+ copyErrorsToBinder(e);
injectionPoints = e.getPartialValue();
}
@@ -132,7 +130,44 @@
return this;
}
+ public ScopedBindingBuilder toConstructor(Constructor<? extends T>
constructor) {
+ checkNotNull(constructor, "constructor");
+ checkNotTargetted();
+
+ BindingImpl<T> base = getBinding();
+ TypeLiteral<T> keyType = base.getKey().getTypeLiteral();
+ TypeLiteral<? extends T> toConstruct =
(constructor.getDeclaringClass() == keyType.getRawType())
+ ? keyType
+ : TypeLiteral.get(constructor.getDeclaringClass());
+
+ Set<InjectionPoint> injectionPoints;
+ try {
+ injectionPoints =
InjectionPoint.forInstanceMethodsAndFields(toConstruct);
+ } catch (ConfigurationException e) {
+ copyErrorsToBinder(e);
+ injectionPoints = e.getPartialValue();
+ }
+
+ try {
+ @SuppressWarnings("unchecked") // safe; constructor is a subtype of
toConstruct
+ InjectionPoint constructorPoint =
InjectionPoint.forConstructor((Constructor) constructor,
+ toConstruct);
+ setBinding(new ConstructorBindingImpl<T>(base.getKey(),
base.getSource(), base.getScoping(),
+ constructorPoint, injectionPoints));
+ } catch (ConfigurationException e) {
+ copyErrorsToBinder(e);
+ }
+
+ return this;
+ }
+
@Override public String toString() {
return "BindingBuilder<" + getBinding().getKey().getTypeLiteral()
+ ">";
+ }
+
+ private void copyErrorsToBinder(ConfigurationException e) {
+ for (Message message : e.getErrorMessages()) {
+ binder.addError(message);
+ }
}
}
Modified: trunk/src/com/google/inject/internal/ConstructorBindingImpl.java
==============================================================================
--- trunk/src/com/google/inject/internal/ConstructorBindingImpl.java
(original)
+++ trunk/src/com/google/inject/internal/ConstructorBindingImpl.java Sun
Jun 7 12:37:28 2009
@@ -23,6 +23,7 @@
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.util.List;
import java.util.Map;
@@ -38,6 +39,16 @@
this.factory = factory;
}
+ public ConstructorBindingImpl(Key<T> key, Object source, Scoping scoping,
+ InjectionPoint constructorInjector, Set<InjectionPoint>
injectionPoints) {
+ super(source, key, scoping);
+ this.factory = new Factory<T>();
+ ConstructionProxy<T> constructionProxy
+ = new
DefaultConstructionProxyFactory<T>(constructorInjector).create();
+ factory.constructorInjector = new ConstructorInjector<T>(
+ injectionPoints, constructionProxy, null, null);
+ }
+
static <T> ConstructorBindingImpl<T> create(
InjectorImpl injector, Key<T> key, Object source, Scoping scoping) {
Factory<T> factoryFactory = new Factory<T>();
@@ -80,8 +91,19 @@
.build());
}
+ @Override protected BindingImpl<T> withScoping(Scoping scoping) {
+ return new ConstructorBindingImpl<T>(
+ null, getKey(), getSource(), factory, scoping, factory);
+ }
+
+ @Override protected BindingImpl<T> withKey(Key<T> key) {
+ return new ConstructorBindingImpl<T>(
+ null, key, getSource(), factory, getScoping(), factory);
+ }
+
public void applyTo(Binder binder) {
- throw new UnsupportedOperationException("This element represents a
synthetic binding.");
+ getScoping().applyTo(binder.withSource(getSource())
+ .bind(getKey()).toConstructor((Constructor)
getConstructor().getMember()));
}
@Override public String toString() {
Modified: trunk/src/com/google/inject/internal/ConstructorInjector.java
==============================================================================
--- trunk/src/com/google/inject/internal/ConstructorInjector.java
(original)
+++ trunk/src/com/google/inject/internal/ConstructorInjector.java Sun Jun
7 12:37:28 2009
@@ -18,6 +18,7 @@
import com.google.inject.spi.InjectionPoint;
import java.lang.reflect.InvocationTargetException;
+import java.util.Set;
/**
* Creates instances using an injectable constructor. After construction,
all injectable fields and
@@ -32,12 +33,11 @@
private final ConstructionProxy<T> constructionProxy;
private final MembersInjectorImpl<T> membersInjector;
- ConstructorInjector(ImmutableSet<InjectionPoint> injectableMembers,
+ ConstructorInjector(Set<InjectionPoint> injectableMembers,
ConstructionProxy<T> constructionProxy,
SingleParameterInjector<?>[] parameterInjectors,
- MembersInjectorImpl<T> membersInjector)
- throws ErrorsException {
- this.injectableMembers = injectableMembers;
+ MembersInjectorImpl<T> membersInjector) {
+ this.injectableMembers = ImmutableSet.copyOf(injectableMembers);
this.constructionProxy = constructionProxy;
this.parameterInjectors = parameterInjectors;
this.membersInjector = membersInjector;
Modified: trunk/src/com/google/inject/internal/Errors.java
==============================================================================
--- trunk/src/com/google/inject/internal/Errors.java (original)
+++ trunk/src/com/google/inject/internal/Errors.java Sun Jun 7 12:37:28
2009
@@ -224,6 +224,10 @@
+ CONSTRUCTOR_RULES, implementation);
}
+ public Errors constructorNotDefinedByType(Constructor<?> constructor,
TypeLiteral<?> type) {
+ return addMessage("%s does not define %s", type, constructor);
+ }
+
public Errors duplicateScopes(Scope existing,
Class<? extends Annotation> annotationType, Scope scope) {
return addMessage("Scope %s is already bound to %s. Cannot bind %s.",
existing,
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 Sun Jun 7 12:37:28
2009
@@ -56,13 +56,6 @@
private final Member member;
private final ImmutableList<Dependency<?>> dependencies;
- private InjectionPoint(Member member,
- ImmutableList<Dependency<?>> dependencies, boolean optional) {
- this.member = member;
- this.dependencies = dependencies;
- this.optional = optional;
- }
-
InjectionPoint(TypeLiteral<?> type, Method method) {
this.member = method;
@@ -166,6 +159,36 @@
@Override public String toString() {
return MoreTypes.toString(member);
+ }
+
+ /**
+ * 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.
+ *
+ * @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);
+ }
+
+ /**
+ * Returns a new injection point for the specified constructor of {...@code
type}.
+ *
+ * @param constructor any single constructor present on {...@code type}.
+ * @param type the concrete type that defines {...@code constructor}.
+ */
+ public static <T> InjectionPoint forConstructor(
+ Constructor<T> constructor, TypeLiteral<? extends T> type) {
+ if (type.getRawType() != constructor.getDeclaringClass()) {
+ new Errors(type)
+ .constructorNotDefinedByType(constructor, type)
+ .throwConfigurationExceptionIfErrorsExist();
+ }
+
+ // TODO: verify that constructor is valid? (defined on a non-abstract
class, etc.)
+ return new InjectionPoint(type, constructor);
}
/**
Modified: trunk/test/com/google/inject/spi/ElementsTest.java
==============================================================================
--- trunk/test/com/google/inject/spi/ElementsTest.java (original)
+++ trunk/test/com/google/inject/spi/ElementsTest.java Sun Jun 7 12:37:28
2009
@@ -35,9 +35,12 @@
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
import com.google.inject.binder.ConstantBindingBuilder;
import com.google.inject.binder.ScopedBindingBuilder;
+import com.google.inject.internal.ImmutableMap;
import com.google.inject.internal.ImmutableSet;
+import static com.google.inject.internal.Iterables.getOnlyElement;
import com.google.inject.matcher.Matcher;
import com.google.inject.matcher.Matchers;
+import com.google.inject.name.Named;
import com.google.inject.name.Names;
import com.google.inject.util.Providers;
import java.lang.annotation.Annotation;
@@ -45,6 +48,8 @@
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -1024,6 +1029,84 @@
);
}
+ public void testBindToConstructor() throws NoSuchMethodException,
NoSuchFieldException {
+ final Constructor<B> constructor =
B.class.getDeclaredConstructor(Object.class);
+ final Field field = B.class.getDeclaredField("stage");
+
+ checkModule(
+ new AbstractModule() {
+ protected void configure() {
+ bind(new TypeLiteral<B<Integer>>()
{}).toConstructor((Constructor) constructor)
+ .in(Singleton.class);
+ }
+ },
+
+ new FailingElementVisitor() {
+ @Override public <T> Void visit(Binding<T> binding) {
+ assertEquals(new Key<B<Integer>>() {}, binding.getKey());
+ binding.acceptScopingVisitor(new
FailingBindingScopingVisitor() {
+ @Override public Void visitScopeAnnotation(Class<? extends
Annotation> annotation) {
+ assertEquals(Singleton.class, annotation);
+ return null;
+ }
+ });
+
+ binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
+ @Override public Void visit(ConstructorBinding<? extends T>
constructorBinding) {
+ assertEquals(constructor,
constructorBinding.getConstructor().getMember());
+ assertEquals(Key.get(Integer.class),
+
getOnlyElement(constructorBinding.getConstructor().getDependencies()).getKey());
+ assertEquals(field,
+
getOnlyElement(constructorBinding.getInjectableMembers()).getMember());
+ assertEquals(2,
constructorBinding.getDependencies().size());
+ assertEquals(ImmutableMap.of(),
constructorBinding.getMethodInterceptors());
+ return null;
+ }
+ });
+ return null;
+ }
+ }
+ );
+ }
+
+ public void testBindToMalformedConstructor() throws
NoSuchMethodException, NoSuchFieldException {
+ final Constructor<C> constructor =
C.class.getDeclaredConstructor(Integer.class);
+
+ checkModule(
+ new AbstractModule() {
+ protected void configure() {
+ bind(C.class).toConstructor(constructor);
+ }
+ },
+
+ new FailingElementVisitor() {
+ @Override public <T> Void visit(Binding<T> binding) {
+ assertEquals(Key.get(C.class), binding.getKey());
+ assertTrue(binding instanceof UntargettedBinding);
+ return null;
+ }
+ },
+
+ new ExternalFailureVisitor() {
+ @Override public Void visit(Message message) {
+ assertContains(message.getMessage(),
+ C.class.getName() + ".a has more than one annotation ",
+ Named.class.getName(), SampleAnnotation.class.getName());
+ return null;
+ }
+ },
+
+ new ExternalFailureVisitor() {
+ @Override public Void visit(Message message) {
+ assertContains(message.getMessage(),
+ C.class.getName() + ".<init>() has more than one
annotation ",
+ Named.class.getName(), SampleAnnotation.class.getName());
+ return null;
+ }
+ }
+ );
+ }
+
// Business logic tests
public void testModulesAreInstalledAtMostOnce() {
@@ -1063,7 +1146,9 @@
for (int i = 0; i < visitors.length; i++) {
ElementVisitor<?> visitor = visitors[i];
Element element = elements.get(i);
- assertContains(element.getSource().toString(), "ElementsTest.java");
+ if (!(visitor instanceof ExternalFailureVisitor)) {
+
assertContains(element.getSource().toString(), "ElementsTest.java");
+ }
element.acceptVisitor(visitor);
}
}
@@ -1080,6 +1165,12 @@
}
}
+ /**
+ * By extending this interface rather than FailingElementVisitor, the
source of the error doesn't
+ * need to contain the string {...@code ElementsTest.java}.
+ */
+ abstract class ExternalFailureVisitor extends FailingElementVisitor {}
+
@Retention(RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@BindingAnnotation
@@ -1089,5 +1180,15 @@
static class A<T> {
@Inject Stage stage;
+ }
+
+ static class B<T> {
+ @Inject Stage stage;
+ B(T t) {}
+ }
+
+ static class C {
+ @Inject @Named("foo") @SampleAnnotation String a;
+ C(@Named("bar") @SampleAnnotation Integer b) {}
}
}
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 Sun Jun 7
12:37:28 2009
@@ -16,12 +16,15 @@
package com.google.inject.spi;
+import static com.google.inject.Asserts.assertContains;
import static com.google.inject.Asserts.assertEqualsBothWays;
import static com.google.inject.Asserts.assertNotSerializable;
+import com.google.inject.ConfigurationException;
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.ImmutableList;
import com.google.inject.internal.ImmutableSet;
import static com.google.inject.internal.Iterables.getOnlyElement;
import com.google.inject.name.Named;
@@ -30,6 +33,8 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import junit.framework.Assert;
@@ -128,7 +133,33 @@
assertNotSerializable(dependency);
assertEqualsBothWays(dependency, Dependency.get(Key.get(String.class,
named("d"))));
}
-
+
+ public void testForConstructor() throws NoSuchMethodException {
+ Constructor<HashSet> constructor = HashSet.class.getConstructor();
+ TypeLiteral<HashSet<String>> hashSet = new
TypeLiteral<HashSet<String>>() {};
+
+ InjectionPoint injectionPoint =
InjectionPoint.forConstructor(constructor, hashSet);
+ assertSame(constructor, injectionPoint.getMember());
+ assertEquals(ImmutableList.<Dependency>of(),
injectionPoint.getDependencies());
+ assertFalse(injectionPoint.isOptional());
+
+ try {
+ InjectionPoint.forConstructor(constructor, new
TypeLiteral<LinkedHashSet<String>>() {});
+ } catch (ConfigurationException expected) {
+
assertContains(expected.getMessage(),
"java.util.LinkedHashSet<java.lang.String>",
+ " does not define java.util.HashSet.<init>()",
+ " while locating java.util.LinkedHashSet<java.lang.String>");
+ }
+
+ try {
+ InjectionPoint.forConstructor((Constructor) constructor, new
TypeLiteral<Set<String>>() {});
+ } catch (ConfigurationException expected) {
+
assertContains(expected.getMessage(), "java.util.Set<java.lang.String>",
+ " does not define java.util.HashSet.<init>()",
+ " while locating java.util.Set<java.lang.String>");
+ }
+ }
+
public void testForConstructorOf() {
InjectionPoint injectionPoint =
InjectionPoint.forConstructorOf(Constructable.class);
assertEquals(Constructable.class.getName() + ".<init>()",
injectionPoint.toString());
--~--~---------~--~----~------------~-------~--~----~
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
-~----------~----~----~----~------~----~------~--~---