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
-~----------~----~----~----~------~----~------~--~---

Reply via email to