Revision: 1553
Author:   sberlin
Date:     Sun Jun 12 14:54:43 2011
Log:      issue 631 -- expose dependency hierarchy in ProvisionListener.
http://code.google.com/p/google-guice/source/detail?r=1553

Added:
 /trunk/core/src/com/google/inject/spi/DependencyAndSource.java
Modified:
 /trunk/core/src/com/google/inject/internal/BindingProcessor.java
 /trunk/core/src/com/google/inject/internal/BoundProviderFactory.java
 /trunk/core/src/com/google/inject/internal/ConstructorInjector.java
 /trunk/core/src/com/google/inject/internal/FactoryProxy.java
 /trunk/core/src/com/google/inject/internal/Initializer.java
 /trunk/core/src/com/google/inject/internal/InjectionRequestProcessor.java
 /trunk/core/src/com/google/inject/internal/InjectorImpl.java
 /trunk/core/src/com/google/inject/internal/InternalContext.java
/trunk/core/src/com/google/inject/internal/InternalFactoryToProviderAdapter.java
 /trunk/core/src/com/google/inject/internal/InternalInjectorCreator.java
 /trunk/core/src/com/google/inject/internal/MembersInjectorImpl.java
 /trunk/core/src/com/google/inject/internal/ProvidedByInternalFactory.java
 /trunk/core/src/com/google/inject/internal/ProviderInternalFactory.java
/trunk/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java
 /trunk/core/src/com/google/inject/internal/SingleFieldInjector.java
 /trunk/core/src/com/google/inject/internal/SingleParameterInjector.java
 /trunk/core/src/com/google/inject/spi/ProvisionListener.java
 /trunk/core/test/com/google/inject/ProvisionListenerTest.java

=======================================
--- /dev/null
+++ /trunk/core/src/com/google/inject/spi/DependencyAndSource.java Sun Jun 12 14:54:43 2011
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.inject.spi;
+
+import java.lang.reflect.Member;
+
+import com.google.inject.Binding;
+import com.google.inject.internal.util.StackTraceElements;
+
+/**
+ * A combination of a {@link Dependency} and the {@link Binding#getSource()
+ * source} where the dependency was bound.
+ *
+ * @author [email protected] (Sam Berlin)
+ */
+public final class DependencyAndSource {
+  private final Dependency<?> dependency;
+  private final Object source;
+
+  public DependencyAndSource(Dependency<?> dependency, Object source) {
+    this.dependency = dependency;
+    this.source = source;
+  }
+
+  /**
+ * Returns the Dependency, if one exists. For anything that can be referenced + * by {@link Injector#getBinding}, a dependency exists. A dependency will not
+   * exist (and this will return null) for types initialized with
+ * {@link Binder#requestInjection} or {@link Injector#injectMembers(Object),
+   * nor will it exist for objects injected into Providers bound with
+   * LinkedBindingBuilder#toProvider(Provider).
+   */
+  public Dependency<?> getDependency() {
+    return dependency;
+  }
+
+  /**
+ * Returns a string describing where this dependency was bound. If the binding + * was just-in-time, there is no valid binding source, so this describes the
+   * class in question.
+   */
+  public String getBindingSource() {
+    if (source instanceof Class) {
+      return StackTraceElements.forType((Class) source).toString();
+    } else if (source instanceof Member) {
+      return StackTraceElements.forMember((Member) source).toString();
+    } else {
+      return source.toString();
+    }
+  }
+
+  @Override
+  public String toString() {
+    Dependency<?> dep = getDependency();
+    Object source = getBindingSource();
+    if (dep != null) {
+      return "Dependency: " + dep + ", source: " + source;
+    } else {
+      return "Source: " + source;
+    }
+  }
+}
=======================================
--- /trunk/core/src/com/google/inject/internal/BindingProcessor.java Sun Jun 5 11:32:05 2011 +++ /trunk/core/src/com/google/inject/internal/BindingProcessor.java Sun Jun 12 14:54:43 2011
@@ -85,7 +85,7 @@
         Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
         T instance = binding.getInstance();
         Initializable<T> ref = initializer.requestInjection(
-            injector, instance, source, injectionPoints);
+            injector, instance, key, source, injectionPoints);
         ConstantFactory<? extends T> factory = new ConstantFactory<T>(ref);
         InternalFactory<? extends T> scopedFactory
             = Scoping.scope(key, injector, factory, source, scoping);
@@ -99,7 +99,7 @@
         Provider<? extends T> provider = binding.getProviderInstance();
         Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
         Initializable<Provider<? extends T>> initializable = initializer
- .<Provider<? extends T>>requestInjection(injector, provider, source, injectionPoints); + .<Provider<? extends T>>requestInjection(injector, provider, null, source, injectionPoints); InternalFactory<T> factory = new InternalFactoryToInitializableAdapter<T>( initializable, source, !injector.options.disableCircularProxies,
             injector.provisionListenerStore.get(key));
=======================================
--- /trunk/core/src/com/google/inject/internal/BoundProviderFactory.java Sun Jun 5 11:32:05 2011 +++ /trunk/core/src/com/google/inject/internal/BoundProviderFactory.java Sun Jun 12 14:54:43 2011
@@ -52,9 +52,14 @@

public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
       throws ErrorsException {
-    errors = errors.withSource(providerKey);
- javax.inject.Provider<? extends T> provider = providerFactory.get(errors, context, dependency, true);
-    return circularGet(provider, errors, context, dependency, linked);
+    context.pushState(providerKey, source);
+    try {
+      errors = errors.withSource(providerKey);
+ javax.inject.Provider<? extends T> provider = providerFactory.get(errors, context, dependency, true);
+      return circularGet(provider, errors, context, dependency, linked);
+    } finally {
+      context.popState();
+    }
   }

   @Override
=======================================
--- /trunk/core/src/com/google/inject/internal/ConstructorInjector.java Sun Jun 5 11:32:05 2011 +++ /trunk/core/src/com/google/inject/internal/ConstructorInjector.java Sun Jun 12 14:54:43 2011
@@ -85,7 +85,7 @@
       if (!provisionCallback.hasListeners()) {
         return provision(errors, context, constructionContext);
       } else {
- return provisionCallback.provision(errors, new ProvisionCallback<T>() { + return provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
           public T call() throws ErrorsException {
             return provision(errors, context, constructionContext);
           }
=======================================
--- /trunk/core/src/com/google/inject/internal/FactoryProxy.java Thu Mar 10 19:02:39 2011 +++ /trunk/core/src/com/google/inject/internal/FactoryProxy.java Sun Jun 12 14:54:43 2011
@@ -51,7 +51,12 @@

public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
       throws ErrorsException {
- return targetFactory.get(errors.withSource(targetKey), context, dependency, true);
+    context.pushState(targetKey, source);
+    try {
+ return targetFactory.get(errors.withSource(targetKey), context, dependency, true);
+    } finally {
+      context.popState();
+    }
   }

   @Override public String toString() {
=======================================
--- /trunk/core/src/com/google/inject/internal/Initializer.java Sat Jul 3 08:51:31 2010 +++ /trunk/core/src/com/google/inject/internal/Initializer.java Sun Jun 12 14:54:43 2011
@@ -16,6 +16,7 @@

 package com.google.inject.internal;

+import com.google.inject.Key;
 import com.google.inject.Stage;
 import com.google.inject.TypeLiteral;
 import com.google.inject.internal.util.Lists;
@@ -48,10 +49,11 @@
    *
* @param instance an instance that optionally has members to be injected (each annotated with
    *      @Inject).
+   * @param key a key to use for keeping the state of the dependency chain
    * @param source the source location that this injection was requested
    */
- <T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Object source,
-      Set<InjectionPoint> injectionPoints) {
+ <T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Key<T> key,
+      Object source, Set<InjectionPoint> injectionPoints) {
     checkNotNull(source);

     // short circuit if the object has no injections
@@ -60,7 +62,7 @@
       return Initializables.of(instance);
     }

- InjectableReference<T> initializable = new InjectableReference<T>(injector, instance, source); + InjectableReference<T> initializable = new InjectableReference<T>(injector, instance, key, source);
     pendingInjection.put(instance, initializable);
     return initializable;
   }
@@ -106,10 +108,12 @@
     private final InjectorImpl injector;
     private final T instance;
     private final Object source;
+    private final Key<T> key;
     private MembersInjectorImpl<T> membersInjector;

- public InjectableReference(InjectorImpl injector, T instance, Object source) { + public InjectableReference(InjectorImpl injector, T instance, Key<T> key, Object source) {
       this.injector = injector;
+      this.key = key; // possibly null!
       this.instance = checkNotNull(instance, "instance");
       this.source = checkNotNull(source, "source");
     }
@@ -144,7 +148,8 @@
       if (pendingInjection.remove(instance) != null) {
// if in Stage.TOOL, we only want to inject & notify toolable injection points.
         // (otherwise we'll inject all of them)
- membersInjector.injectAndNotify(instance, errors.withSource(source), injector.options.stage == Stage.TOOL); + membersInjector.injectAndNotify(instance, errors.withSource(source), key, source,
+            injector.options.stage == Stage.TOOL);
       }

       return instance;
=======================================
--- /trunk/core/src/com/google/inject/internal/InjectionRequestProcessor.java Sat Jul 3 08:51:31 2010 +++ /trunk/core/src/com/google/inject/internal/InjectionRequestProcessor.java Sun Jun 12 14:54:43 2011
@@ -58,7 +58,7 @@
     }

     initializer.requestInjection(
- injector, request.getInstance(), request.getSource(), injectionPoints); + injector, request.getInstance(), null, request.getSource(), injectionPoints);
     return true;
   }

=======================================
--- /trunk/core/src/com/google/inject/internal/InjectorImpl.java Sun Jun 5 11:32:05 2011 +++ /trunk/core/src/com/google/inject/internal/InjectorImpl.java Sun Jun 12 14:54:43 2011
@@ -716,8 +716,13 @@
     InternalFactory<T> internalFactory = new InternalFactory<T>() {
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
           throws ErrorsException {
-        return targetBinding.getInternalFactory().get(
-            errors.withSource(targetKey), context, dependency, true);
+        context.pushState(targetKey, targetBinding.getSource());
+        try {
+          return targetBinding.getInternalFactory().get(
+              errors.withSource(targetKey), context, dependency, true);
+        } finally {
+          context.popState();
+        }
       }
     };

@@ -904,8 +909,8 @@

<T> SingleParameterInjector<T> createParameterInjector(final Dependency<T> dependency,
       final Errors errors) throws ErrorsException {
- InternalFactory<? extends T> factory = getInternalFactory(dependency.getKey(), errors, JitLimitation.NO_JIT);
-    return new SingleParameterInjector<T>(dependency, factory);
+ BindingImpl<? extends T> binding = getBindingOrThrow(dependency.getKey(), errors, JitLimitation.NO_JIT);
+    return new SingleParameterInjector<T>(dependency, binding);
   }

   /** Invokes a method. */
@@ -947,9 +952,7 @@
   }

<T> Provider<T> getProviderOrThrow(final Key<T> key, Errors errors) throws ErrorsException {
-
-
- final InternalFactory<? extends T> factory = getInternalFactory(key, errors, JitLimitation.NO_JIT); + final BindingImpl<? extends T> binding = getBindingOrThrow(key, errors, JitLimitation.NO_JIT);
     final Dependency<T> dependency = Dependency.get(key);

     return new Provider<T>() {
@@ -958,11 +961,11 @@
         try {
           T t = callInContext(new ContextualCallable<T>() {
             public T call(InternalContext context) throws ErrorsException {
-              Dependency previous = context.setDependency(dependency);
+ Dependency previous = context.pushDependency(dependency, binding.getSource());
               try {
-                return factory.get(errors, context, dependency, false);
+ return binding.getInternalFactory().get(errors, context, dependency, false);
               } finally {
-                context.setDependency(previous);
+                context.popStateAndSetDependency(previous);
               }
             }
           });
@@ -974,7 +977,7 @@
       }

       @Override public String toString() {
-        return factory.toString();
+        return binding.getInternalFactory().toString();
       }
     };
   }
=======================================
--- /trunk/core/src/com/google/inject/internal/InternalContext.java Thu Oct 21 21:13:25 2010 +++ /trunk/core/src/com/google/inject/internal/InternalContext.java Sun Jun 12 14:54:43 2011
@@ -16,8 +16,14 @@

 package com.google.inject.internal;

+import com.google.inject.Key;
+import com.google.inject.internal.util.ImmutableList;
 import com.google.inject.internal.util.Maps;
 import com.google.inject.spi.Dependency;
+import com.google.inject.spi.DependencyAndSource;
+
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;

 /**
@@ -29,7 +35,10 @@
 final class InternalContext {

private Map<Object, ConstructionContext<?>> constructionContexts = Maps.newHashMap(); + /** Keeps track of the type that is currently being requested for injection. */
   private Dependency dependency;
+  /** Keeps track of the hierarchy of types needed during injection. */
+ private LinkedList<DependencyAndSource> state = new LinkedList<DependencyAndSource>();

   @SuppressWarnings("unchecked")
   public <T> ConstructionContext<T> getConstructionContext(Object key) {
@@ -46,9 +55,32 @@
     return dependency;
   }

-  public Dependency setDependency(Dependency dependency) {
+  /** Sets the new current dependency & adds it to the state. */
+  public Dependency pushDependency(Dependency dependency, Object source) {
     Dependency previous = this.dependency;
     this.dependency = dependency;
+    state.addLast(new DependencyAndSource(dependency, source));
     return previous;
   }
-}
+
+  /** Pops the current state & sets the new dependency. */
+  public void popStateAndSetDependency(Dependency newDependency) {
+    popState();
+    this.dependency = newDependency;
+  }
+
+  /** Adds to the state without setting the dependency. */
+  public void pushState(Key<?> key, Object source) {
+ state.addLast(new DependencyAndSource(key == null ? null : Dependency.get(key), source));
+  }
+
+  /** Pops from the state without setting a dependency. */
+  public void popState() {
+    state.removeLast();
+  }
+
+  /** Returns the current dependency chain (all the state). */
+  public List<DependencyAndSource> getDependencyChain() {
+    return ImmutableList.copyOf(state);
+  }
+}
=======================================
--- /trunk/core/src/com/google/inject/internal/InternalFactoryToProviderAdapter.java Sat Apr 30 08:38:05 2011 +++ /trunk/core/src/com/google/inject/internal/InternalFactoryToProviderAdapter.java Sun Jun 12 14:54:43 2011
@@ -35,6 +35,7 @@

public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
       throws ErrorsException {
+    // TODO(sameb): Does this need to push state into the context?
     try {
       return errors.checkForNull(provider.get(), source, dependency);
     } catch (RuntimeException userException) {
=======================================
--- /trunk/core/src/com/google/inject/internal/InternalInjectorCreator.java Thu Mar 10 19:02:39 2011 +++ /trunk/core/src/com/google/inject/internal/InternalInjectorCreator.java Sun Jun 12 14:54:43 2011
@@ -198,14 +198,14 @@
           injector.callInContext(new ContextualCallable<Void>() {
             Dependency<?> dependency = Dependency.get(binding.getKey());
             public Void call(InternalContext context) {
-              Dependency previous = context.setDependency(dependency);
+ Dependency previous = context.pushDependency(dependency, binding.getSource());
               Errors errorsForBinding = errors.withSource(dependency);
               try {
binding.getInternalFactory().get(errorsForBinding, context, dependency, false);
               } catch (ErrorsException e) {
                 errorsForBinding.merge(e.getErrors());
               } finally {
-                context.setDependency(previous);
+                context.popStateAndSetDependency(previous);
               }

               return null;
=======================================
--- /trunk/core/src/com/google/inject/internal/MembersInjectorImpl.java Sat Jul 3 08:51:31 2010 +++ /trunk/core/src/com/google/inject/internal/MembersInjectorImpl.java Sun Jun 12 14:54:43 2011
@@ -16,6 +16,7 @@

 package com.google.inject.internal;

+import com.google.inject.Key;
 import com.google.inject.MembersInjector;
 import com.google.inject.TypeLiteral;
 import com.google.inject.internal.util.ImmutableList;
@@ -57,7 +58,7 @@
   public void injectMembers(T instance) {
     Errors errors = new Errors(typeLiteral);
     try {
-      injectAndNotify(instance, errors, false);
+      injectAndNotify(instance, errors, null, typeLiteral, false);
     } catch (ErrorsException e) {
       errors.merge(e.getErrors());
     }
@@ -65,14 +66,21 @@
     errors.throwProvisionExceptionIfErrorsExist();
   }

- void injectAndNotify(final T instance, final Errors errors, final boolean toolableOnly) throws ErrorsException {
+  void injectAndNotify(final T instance, final Errors errors,
+      final Key<T> key, final Object source, final boolean toolableOnly)
+      throws ErrorsException {
     if (instance == null) {
       return;
     }

     injector.callInContext(new ContextualCallable<Void>() {
       public Void call(InternalContext context) throws ErrorsException {
-        injectMembers(instance, errors, context, toolableOnly);
+        context.pushState(key, source);
+        try {
+          injectMembers(instance, errors, context, toolableOnly);
+        } finally {
+          context.popState();
+        }
         return null;
       }
     });
=======================================
--- /trunk/core/src/com/google/inject/internal/ProvidedByInternalFactory.java Sun Jun 5 11:32:05 2011 +++ /trunk/core/src/com/google/inject/internal/ProvidedByInternalFactory.java Sun Jun 12 14:54:43 2011
@@ -60,10 +60,15 @@
       throws ErrorsException {
     checkState(providerBinding != null, "not initialized");

-    errors = errors.withSource(providerKey);
-    Provider provider = providerBinding.getInternalFactory().get(
-        errors, context, dependency, true);
-    return circularGet(provider, errors, context, dependency, linked);
+    context.pushState(providerKey, providerBinding.getSource());
+    try {
+      errors = errors.withSource(providerKey);
+      Provider provider = providerBinding.getInternalFactory().get(
+          errors, context, dependency, true);
+      return circularGet(provider, errors, context, dependency, linked);
+    } finally {
+      context.popState();
+    }
   }

protected T provision(javax.inject.Provider<? extends T> provider, Errors errors,
=======================================
--- /trunk/core/src/com/google/inject/internal/ProviderInternalFactory.java Sun Jun 5 11:32:05 2011 +++ /trunk/core/src/com/google/inject/internal/ProviderInternalFactory.java Sun Jun 12 14:54:43 2011
@@ -65,7 +65,7 @@
     if (!provisionCallback.hasListeners()) {
       return provision(provider, errors, dependency, constructionContext);
     } else {
- return provisionCallback.provision(errors, new ProvisionCallback<T>() { + return provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
         public T call() throws ErrorsException {
return provision(provider, errors, dependency, constructionContext);
         }
=======================================
--- /trunk/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java Sun Jun 5 11:32:05 2011 +++ /trunk/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java Sun Jun 12 14:54:43 2011
@@ -21,6 +21,7 @@
 import com.google.inject.Key;
 import com.google.inject.ProvisionException;
 import com.google.inject.spi.ProvisionListener;
+import com.google.inject.spi.DependencyAndSource;

 /**
  * Intercepts provisions with a stack of listeners.
@@ -46,8 +47,9 @@
     return listeners.length > 0;
   }

- public T provision(Errors errors, ProvisionCallback<T> callable) throws ErrorsException {
-    Provision provision = new Provision(errors, callable);
+ public T provision(Errors errors, InternalContext context, ProvisionCallback<T> callable)
+      throws ErrorsException {
+    Provision provision = new Provision(errors, context, callable);
     RuntimeException caught = null;
     try {
       provision.provision();
@@ -77,14 +79,16 @@
private class Provision extends ProvisionListener.ProvisionInvocation<T> {

     final Errors errors;
+    final InternalContext context;
     final ProvisionCallback<T> callable;
     int index = -1;
     T result;
     ErrorsException exceptionDuringProvision;
     ProvisionListener erredListener;

-    public Provision(Errors errors, ProvisionCallback<T> callable) {
+ public Provision(Errors errors, InternalContext context, ProvisionCallback<T> callable) {
       this.callable = callable;
+      this.context = context;
       this.errors = errors;
     }

@@ -120,5 +124,10 @@
     public Key<T> getKey() {
       return key;
     }
+
+    @Override
+    public List<DependencyAndSource> getDependencyChain() {
+      return context.getDependencyChain();
+    }
   }
 }
=======================================
--- /trunk/core/src/com/google/inject/internal/SingleFieldInjector.java Thu Feb 11 14:07:07 2010 +++ /trunk/core/src/com/google/inject/internal/SingleFieldInjector.java Sun Jun 12 14:54:43 2011
@@ -28,7 +28,7 @@
   final Field field;
   final InjectionPoint injectionPoint;
   final Dependency<?> dependency;
-  final InternalFactory<?> factory;
+  final BindingImpl<?> binding;

public SingleFieldInjector(InjectorImpl injector, InjectionPoint injectionPoint, Errors errors)
       throws ErrorsException {
@@ -38,7 +38,7 @@

     // Ewwwww...
     field.setAccessible(true);
- factory = injector.getInternalFactory(dependency.getKey(), errors, JitLimitation.NO_JIT); + binding = injector.getBindingOrThrow(dependency.getKey(), errors, JitLimitation.NO_JIT);
   }

   public InjectionPoint getInjectionPoint() {
@@ -48,16 +48,16 @@
   public void inject(Errors errors, InternalContext context, Object o) {
     errors = errors.withSource(dependency);

-    Dependency previous = context.setDependency(dependency);
+ Dependency previous = context.pushDependency(dependency, binding.getSource());
     try {
-      Object value = factory.get(errors, context, dependency, false);
+ Object value = binding.getInternalFactory().get(errors, context, dependency, false);
       field.set(o, value);
     } catch (ErrorsException e) {
       errors.withSource(injectionPoint).merge(e.getErrors());
     } catch (IllegalAccessException e) {
throw new AssertionError(e); // a security manager is blocking us, we're hosed
     } finally {
-      context.setDependency(previous);
+      context.popStateAndSetDependency(previous);
     }
   }
 }
=======================================
--- /trunk/core/src/com/google/inject/internal/SingleParameterInjector.java Thu Feb 11 14:07:07 2010 +++ /trunk/core/src/com/google/inject/internal/SingleParameterInjector.java Sun Jun 12 14:54:43 2011
@@ -25,19 +25,19 @@
   private static final Object[] NO_ARGUMENTS = {};

   private final Dependency<T> dependency;
-  private final InternalFactory<? extends T> factory;
-
- SingleParameterInjector(Dependency<T> dependency, InternalFactory<? extends T> factory) {
+  private final BindingImpl<? extends T> binding;
+
+ SingleParameterInjector(Dependency<T> dependency, BindingImpl<? extends T> binding) {
     this.dependency = dependency;
-    this.factory = factory;
+    this.binding = binding;
   }

private T inject(Errors errors, InternalContext context) throws ErrorsException {
-    Dependency previous = context.setDependency(dependency);
+ Dependency previous = context.pushDependency(dependency, binding.getSource());
     try {
- return factory.get(errors.withSource(dependency), context, dependency, false); + return binding.getInternalFactory().get(errors.withSource(dependency), context, dependency, false);
     } finally {
-      context.setDependency(previous);
+      context.popStateAndSetDependency(previous);
     }
   }

=======================================
--- /trunk/core/src/com/google/inject/spi/ProvisionListener.java Sun Jun 5 11:32:05 2011 +++ /trunk/core/src/com/google/inject/spi/ProvisionListener.java Sun Jun 12 14:54:43 2011
@@ -16,6 +16,8 @@

 package com.google.inject.spi;

+import java.util.List;
+
 import com.google.inject.Key;
 import com.google.inject.Provider;
 import com.google.inject.Scope;
@@ -53,5 +55,8 @@

     /** Performs the provision, returning the object provisioned. */
     public abstract T provision();
+
+ /** Returns the dependency chain that led to this object being provisioned. */
+    public abstract List<DependencyAndSource> getDependencyChain();
   }
 }
=======================================
--- /trunk/core/test/com/google/inject/ProvisionListenerTest.java Sun Jun 5 11:32:05 2011 +++ /trunk/core/test/com/google/inject/ProvisionListenerTest.java Sun Jun 12 14:54:43 2011
@@ -21,6 +21,7 @@
 import static com.google.inject.name.Names.named;

 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;

 import junit.framework.TestCase;

@@ -29,6 +30,7 @@
 import com.google.inject.matcher.Matcher;
 import com.google.inject.matcher.Matchers;
 import com.google.inject.name.Named;
+import com.google.inject.spi.DependencyAndSource;
 import com.google.inject.spi.ProvisionListener;

 /**
@@ -314,7 +316,7 @@
     public <T> void onProvision(ProvisionInvocation<T> provision) {
       keys.add(provision.getKey());
       T provisioned = provision.provision();
- assertEquals(provision.getKey().getTypeLiteral().getRawType(), provisioned.getClass()); + assertEquals(provision.getKey().getRawType(), provisioned.getClass());
     }

     List<Key> getAndClear() {
@@ -350,4 +352,179 @@
       provision.provision();
     }
   }
-}
+
+  private static class ChainAsserter implements ProvisionListener {
+    private final List<Class<?>> provisionList;
+    private final List<Class<?>> expected;
+
+ public ChainAsserter(List<Class<?>> provisionList, Iterable<Class<?>> expected) {
+      this.provisionList = provisionList;
+      this.expected = ImmutableList.copyOf(expected);
+    }
+
+    public <T> void onProvision(ProvisionInvocation<T> provision) {
+      List<Class<?>> actual = Lists.newArrayList();
+      for (DependencyAndSource dep : provision.getDependencyChain()) {
+        actual.add(dep.getDependency().getKey().getRawType());
+      }
+      assertEquals(expected, actual);
+      provisionList.add(provision.getKey().getRawType());
+    }
+  }
+
+  private static Matcher<Object> keyMatcher(Class<?> clazz) {
+    return Matchers.only(Key.get(clazz));
+  }
+
+  @SuppressWarnings("unchecked")
+  public void testDependencyChain() {
+    final List<Class<?>> pList = Lists.newArrayList();
+    final List<Class<?>> totalList = Lists.newArrayList();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Instance.class).toInstance(new Instance());
+        bind(B.class).to(BImpl.class);
+        bind(D.class).toProvider(DP.class);
+
+        bindListener(Matchers.any(), new ProvisionListener() {
+          public <T> void onProvision(ProvisionInvocation<T> provision) {
+            totalList.add(provision.getKey().getRawType());
+          }
+        });
+
+        // Build up a list of asserters for our dependency chains.
+        ImmutableList.Builder<Class<?>> chain = ImmutableList.builder();
+
+        chain.add(Instance.class).add(A.class);
+ bindListener(keyMatcher(A.class), new ChainAsserter(pList, chain.build()));
+
+        chain.add(B.class).add(BImpl.class);
+ bindListener(keyMatcher(BImpl.class), new ChainAsserter(pList, chain.build()));
+
+        chain.add(C.class);
+ bindListener(keyMatcher(C.class), new ChainAsserter(pList, chain.build()));
+
+ // the chain has D before DP even though DP is provisioned & notified first
+        // because we do DP because of D, and need DP to provision D.
+        chain.add(D.class).add(DP.class);
+ bindListener(keyMatcher(D.class), new ChainAsserter(pList, chain.build())); + bindListener(keyMatcher(DP.class), new ChainAsserter(pList, chain.build()));
+
+        chain.add(E.class);
+ bindListener(keyMatcher(E.class), new ChainAsserter(pList, chain.build()));
+
+        chain.add(F.class);
+ bindListener(keyMatcher(F.class), new ChainAsserter(pList, chain.build()));
+      }
+      @Provides C c(D d) {
+        return new C() {};
+      }
+    });
+    Instance instance = injector.getInstance(Instance.class);
+    // make sure we're checking all of the chain asserters..
+ assertEquals(of(A.class, BImpl.class, C.class, DP.class, D.class, E.class, F.class),
+        pList);
+    // and make sure that nothing else was notified that we didn't expect.
+    assertEquals(totalList, pList);
+  }
+
+  public void testModuleRequestInjection() {
+    final AtomicBoolean notified = new AtomicBoolean();
+    Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        requestInjection(new Object() {
+          @Inject Foo foo;
+        });
+        bindListener(Matchers.any(),
+ new SpecialChecker(Foo.class, getClass().getName() + ".configure(", notified));
+      }
+    });
+    assertTrue(notified.get());
+  }
+
+  public void testToProviderInstance() {
+    final AtomicBoolean notified = new AtomicBoolean();
+    Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Object.class).toProvider(new Provider<Object>() {
+          @Inject Foo foo;
+          public Object get() {
+            return null;
+          }
+        });
+        bindListener(Matchers.any(),
+ new SpecialChecker(Foo.class, getClass().getName() + ".configure(", notified));
+      }
+    });
+    assertTrue(notified.get());
+  }
+
+  public void testInjectorInjectMembers() {
+    final Object object = new Object() {
+      @Inject Foo foo;
+    };
+    final AtomicBoolean notified = new AtomicBoolean();
+    Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bindListener(Matchers.any(),
+ new SpecialChecker(Foo.class, object.getClass().getName(), notified));
+      }
+    }).injectMembers(object);
+    assertTrue(notified.get());
+  }
+
+  private static class SpecialChecker implements ProvisionListener {
+    private final Class<?> notifyType;
+    private final String firstSource;
+    private final AtomicBoolean notified;
+
+ public SpecialChecker(Class<?> notifyType, String firstSource, AtomicBoolean notified) {
+      this.notifyType = notifyType;
+      this.firstSource = firstSource;
+      this.notified = notified;
+    }
+
+    public <T> void onProvision(ProvisionInvocation<T> provision) {
+      notified.set(true);
+      assertEquals(notifyType, provision.getKey().getRawType());
+      assertEquals(2, provision.getDependencyChain().size());
+
+ assertEquals(null, provision.getDependencyChain().get(0).getDependency()); + assertContains(provision.getDependencyChain().get(0).getBindingSource(), firstSource);
+
+      assertEquals(notifyType,
+ provision.getDependencyChain().get(1).getDependency().getKey().getRawType()); + assertContains(provision.getDependencyChain().get(1).getBindingSource(),
+          notifyType.getName() + ".class(");
+    }
+  }
+
+  private static class Instance {
+    @Inject A a;
+  }
+  private static class A {
+    @Inject A(B b) {}
+  }
+  private interface B {}
+  private static class BImpl implements B {
+    @Inject void inject(C c) {}
+  }
+  private interface C {}
+  private interface D {}
+  private static class DP implements Provider<D> {
+    @Inject Provider<E> ep;
+    public D get() {
+      ep.get();
+      return new D() {};
+    }
+  }
+  private static class E {
+    @SuppressWarnings("unused")
+    @Inject F f;
+  }
+  private static class F {}
+}

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