Revision: 1271
Author: sberlin
Date: Wed Sep 22 20:16:22 2010
Log: new AssistedInject factories now implement HasDependencies (like the old assistedinject factories)
http://code.google.com/p/google-guice/source/detail?r=1271

Modified:
/trunk/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java /trunk/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryModuleBuilderTest.java

=======================================
--- /trunk/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java Sat Jul 31 09:08:27 2010 +++ /trunk/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java Wed Sep 22 20:16:22 2010
@@ -34,6 +34,7 @@
 import com.google.inject.internal.util.Classes;
 import com.google.inject.internal.util.ImmutableList;
 import com.google.inject.internal.util.ImmutableMap;
+import com.google.inject.internal.util.ImmutableSet;
 import com.google.inject.internal.util.Iterables;
 import com.google.inject.internal.util.ToStringBuilder;

@@ -42,6 +43,7 @@
 import static com.google.inject.internal.util.Preconditions.checkState;

 import com.google.inject.spi.Dependency;
+import com.google.inject.spi.HasDependencies;
 import com.google.inject.spi.InjectionPoint;
 import com.google.inject.spi.Message;
 import com.google.inject.spi.Toolable;
@@ -55,8 +57,10 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;

 /**
* The newer implementation of factory provider. This implementation uses a child injector to
@@ -67,7 +71,7 @@
  * @author [email protected] (Peter Schmitt)
  * @author [email protected] (Sam Berlin)
  */
-final class FactoryProvider2<F> implements InvocationHandler, Provider<F> {
+final class FactoryProvider2<F> implements InvocationHandler, Provider<F>, HasDependencies {

/** if a factory method parameter isn't annotated, it gets this annotation. */
   static final Assisted DEFAULT_ANNOTATION = new Assisted() {
@@ -102,6 +106,9 @@
     /** the parameters in the factory method associated with this data. */
     final ImmutableList<Key<?>> paramTypes;

+    /** All non-assisted dependencies required by this method. */
+    final Set<Dependency<?>> dependencies;
+
     /** true if {...@link #validForOptimizedAssistedInject} returned true. */
     final boolean optimized;
     /** the list of optimized providers, empty if not optimized. */
@@ -111,12 +118,14 @@

     AssistData(Constructor<?> constructor, Key<?> returnType,
         ImmutableList<Key<?>> paramTypes, boolean optimized,
-        List<ThreadLocalProvider> providers) {
+        List<ThreadLocalProvider> providers,
+        Set<Dependency<?>> dependencies) {
       this.constructor = constructor;
       this.returnType = returnType;
       this.paramTypes = paramTypes;
       this.optimized = optimized;
       this.providers = providers;
+      this.dependencies = dependencies;
     }

     @Override
@@ -128,6 +137,7 @@
         .add("optimized", optimized)
         .add("providers", providers)
         .add("cached binding", cachedBinding)
+        .add("dependencies", dependencies)
         .toString();

     }
@@ -206,13 +216,14 @@

Constructor<?> constructor = (Constructor)ctorInjectionPoint.getMember();
         List<ThreadLocalProvider> providers = Collections.emptyList();
+ Set<Dependency<?>> deps = getDependencies(ctorInjectionPoint, implementation);
         boolean optimized = false;
// Now go through all dependencies of the implementation and see if it is OK to // use an optimized form of assistedinject2. The optimized form requires that // all injections directly inject the object itself (and not a Provider of the object, // or an Injector), because it caches a single child injector and mutates the Provider
         // of the arguments in a ThreadLocal.
- if(validForOptimizedAssistedInject(ctorInjectionPoint, implementation)) {
+        if(isValidForOptimizedAssistedInject(deps)) {
ImmutableList.Builder<ThreadLocalProvider> providerListBuilder = ImmutableList.builder();
           for(int i = 0; i < params.size(); i++) {
             providerListBuilder.add(new ThreadLocalProvider());
@@ -220,7 +231,9 @@
           providers = providerListBuilder.build();
           optimized = true;
         }
- assistDataBuilder.put(method, new AssistData(constructor, returnType, immutableParamList, optimized, providers));
+        assistDataBuilder.put(method,
+            new AssistData(constructor, returnType, immutableParamList,
+                optimized, providers, removeAssistedDeps(deps)));
       }

// If we generated any errors (from finding matching constructors, for instance), throw an exception.
@@ -240,6 +253,14 @@
   public F get() {
     return factory;
   }
+
+  public Set<Dependency<?>> getDependencies() {
+    Set<Dependency<?>> combinedDeps = new HashSet<Dependency<?>>();
+    for(AssistData data : assistDataByMethod.values()) {
+      combinedDeps.addAll(data.dependencies);
+    }
+    return ImmutableSet.copyOf(combinedDeps);
+  }

   /**
* Returns true if the ConfigurationException is due to an error of TypeLiteral not being fully
@@ -370,30 +391,41 @@
     // All @Assisted params match up to the method's parameters.
     return true;
   }
-
-  /**
-   * Returns true if the implementation & constructor are suitable for an
-   * optimized version of AssistedInject. The optimized version caches the
-   * binding & uses a ThreadLocal Provider, so can only be applied if the
- * assisted bindings are immediately provided. This looks for hints that the - * values may be lazily retrieved, by looking for injections of Injector or a
-   * Provider for the assisted values.
-   */
- private boolean validForOptimizedAssistedInject(InjectionPoint ctorPoint, TypeLiteral<?> implementation) {
-    if(ctorPoint != null) {
-      for(Dependency<?> dep : ctorPoint.getDependencies()) {
-        if(isInjectorOrAssistedProvider(dep)) {
-          return false;
-        }
+
+ /** Calculates all dependencies required by the implementation and constructor. */ + private Set<Dependency<?>> getDependencies(InjectionPoint ctorPoint, TypeLiteral<?> implementation) {
+    ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
+    builder.addAll(ctorPoint.getDependencies());
+    if (!implementation.getRawType().isInterface()) {
+ for (InjectionPoint ip : InjectionPoint.forInstanceMethodsAndFields(implementation)) {
+        builder.addAll(ip.getDependencies());
+      }
+    }
+    return builder.build();
+  }
+
+  /** Return all non-assisted dependencies. */
+  private Set<Dependency<?>> removeAssistedDeps(Set<Dependency<?>> deps) {
+    ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
+    for(Dependency<?> dep : deps) {
+      Class annotationType = dep.getKey().getAnnotationType();
+ if (annotationType == null | | !annotationType.equals(Assisted.class)) {
+        builder.add(dep);
       }
     }
-    if(!implementation.getRawType().isInterface()) {
- for(InjectionPoint ip : InjectionPoint.forInstanceMethodsAndFields(implementation)) {
-        for(Dependency<?> dep : ip.getDependencies()) {
-          if(isInjectorOrAssistedProvider(dep)) {
-            return false;
-          }
-        }
+    return builder.build();
+  }
+
+  /**
+ * Returns true if all dependencies are suitable for the optimized version of AssistedInject. The + * optimized version caches the binding & uses a ThreadLocal Provider, so can only be applied if + * the assisted bindings are immediately provided. This looks for hints that the values may be + * lazily retrieved, by looking for injections of Injector or a Provider for the assisted values.
+   */
+ private boolean isValidForOptimizedAssistedInject(Set<Dependency<?>> dependencies) {
+    for (Dependency<?> dep : dependencies) {
+      if (isInjectorOrAssistedProvider(dep)) {
+        return false;
       }
     }
     return true;
=======================================
--- /trunk/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryModuleBuilderTest.java Sat Jul 17 09:39:00 2010 +++ /trunk/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryModuleBuilderTest.java Wed Sep 22 20:16:22 2010
@@ -17,25 +17,37 @@
 package com.google.inject.assistedinject;

 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.name.Names.named;
+
+import java.awt.Color;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import junit.framework.TestCase;

 import com.google.inject.AbstractModule;
+import com.google.inject.Binding;
 import com.google.inject.CreationException;
 import com.google.inject.Guice;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.google.inject.Key;
+import com.google.inject.Module;
 import com.google.inject.Provides;
+import com.google.inject.Stage;
 import com.google.inject.TypeLiteral;
+import com.google.inject.internal.util.ImmutableSet;
 import com.google.inject.internal.util.Iterables;
 import com.google.inject.name.Named;
 import com.google.inject.name.Names;
+import com.google.inject.spi.Dependency;
+import com.google.inject.spi.Element;
+import com.google.inject.spi.Elements;
+import com.google.inject.spi.HasDependencies;
 import com.google.inject.spi.Message;

-import junit.framework.TestCase;
-
-import java.awt.*;
-import java.util.Collection;
-
 public class FactoryModuleBuilderTest extends TestCase {

   public void testImplicitForwardingAssistedBindingFailsWithInterface() {
@@ -341,6 +353,85 @@
     AbstractCar create(Color color);
   }
   public static class ArtCar extends AbstractCar {}
-
-
-}
+
+  public void testFactoryBindingDependencies() {
+    // validate dependencies work in all stages & as a raw element,
+    // and that dependencies work for methods, fields, constructors,
+    // and for @AssistedInject constructors too.
+    Module module = new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Integer.class).toInstance(42);
+        bind(Double.class).toInstance(4.2d);
+        bind(Float.class).toInstance(4.2f);
+        bind(String.class).annotatedWith(named("dog")).toInstance("dog");
+        bind(String.class).annotatedWith(named("cat1")).toInstance("cat1");
+        bind(String.class).annotatedWith(named("cat2")).toInstance("cat2");
+        bind(String.class).annotatedWith(named("cat3")).toInstance("cat3");
+ bind(String.class).annotatedWith(named("arbitrary")).toInstance("fail!");
+        install(new FactoryModuleBuilder()
+                .implement(Animal.class, Dog.class)
+                .build(AnimalHouse.class));
+      }
+    };
+
+    Set<Key<?>> expectedKeys = ImmutableSet.<Key<?>>of(
+        Key.get(Integer.class),
+        Key.get(Double.class),
+        Key.get(Float.class),
+        Key.get(String.class, named("dog")),
+        Key.get(String.class, named("cat1")),
+        Key.get(String.class, named("cat2")),
+        Key.get(String.class, named("cat3"))
+    );
+
+    Injector injector = Guice.createInjector(module);
+ validateDependencies(expectedKeys, injector.getBinding(AnimalHouse.class));
+
+    injector = Guice.createInjector(Stage.TOOL, module);
+ validateDependencies(expectedKeys, injector.getBinding(AnimalHouse.class));
+
+    List<Element> elements = Elements.getElements(module);
+    boolean found = false;
+    for(Element element : elements) {
+      if(element instanceof Binding) {
+        Binding binding = (Binding)element;
+        if(binding.getKey().equals(Key.get(AnimalHouse.class))) {
+          found = true;
+          validateDependencies(expectedKeys, binding);
+          break;
+        }
+      }
+    }
+    assertTrue(found);
+  }
+
+ private void validateDependencies(Set<Key<?>> expectedKeys, Binding<?> binding) { + Set<Dependency<?>> dependencies = ((HasDependencies)binding).getDependencies();
+    Set<Key<?>> actualKeys = new HashSet<Key<?>>();
+    for(Dependency dependency : dependencies) {
+      actualKeys.add(dependency.getKey());
+    }
+    assertEquals(expectedKeys, actualKeys);
+  }
+
+  interface AnimalHouse {
+    Animal createAnimal(String name);
+    Cat createCat(String name);
+    Cat createCat(int age);
+  }
+
+  interface Animal {}
+  private static class Dog implements Animal {
+    @Inject int a;
+    @Inject Dog(@Assisted String a, double b) {}
+    @Inject void register(@Named("dog") String a) {}
+  }
+  private static class Cat implements Animal {
+    @Inject float a;
+    @AssistedInject Cat(@Assisted String a, @Named("cat1") String b) {}
+    @AssistedInject Cat(@Assisted int a, @Named("cat2") String b) {}
+ @AssistedInject Cat(@Assisted byte a, @Named("catfail") String b) {} // not a dependency!
+    @Inject void register(@Named("cat3") String a) {}
+  }
+}

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