More listener changes:
 - we now support user-supplied MembersInjectors. These execute before InjectionListeners, enabling a two-phase injection
 - renamed InjectableTypeListenerBinding to TypeListenerBinding

git-svn-id: https://google-guice.googlecode.com/svn/trunk@927 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/AbstractProcessor.java b/src/com/google/inject/AbstractProcessor.java
index 9a43034..1b447f8 100644
--- a/src/com/google/inject/AbstractProcessor.java
+++ b/src/com/google/inject/AbstractProcessor.java
@@ -20,14 +20,14 @@
 import com.google.inject.spi.Element;
 import com.google.inject.spi.ElementVisitor;
 import com.google.inject.spi.InjectionRequest;
+import com.google.inject.spi.MembersInjectorLookup;
 import com.google.inject.spi.Message;
 import com.google.inject.spi.PrivateElements;
 import com.google.inject.spi.ProviderLookup;
 import com.google.inject.spi.ScopeBinding;
 import com.google.inject.spi.StaticInjectionRequest;
 import com.google.inject.spi.TypeConverterBinding;
-import com.google.inject.spi.MembersInjectorLookup;
-import com.google.inject.spi.InjectableTypeListenerBinding;
+import com.google.inject.spi.TypeListenerBinding;
 import java.util.Iterator;
 import java.util.List;
 
@@ -116,7 +116,7 @@
     return false;
   }
 
-  public Boolean visit(InjectableTypeListenerBinding binding) {
+  public Boolean visit(TypeListenerBinding binding) {
     return false;
   }
 }
diff --git a/src/com/google/inject/EncounterImpl.java b/src/com/google/inject/EncounterImpl.java
index 468ee42..33f462e 100644
--- a/src/com/google/inject/EncounterImpl.java
+++ b/src/com/google/inject/EncounterImpl.java
@@ -36,6 +36,7 @@
 
   private final Errors errors;
   private final Lookups lookups;
+  private List<MembersInjector<? super T>> membersInjectors; // lazy
   private List<InjectionListener<? super T>> injectionListeners; // lazy
   private List<MethodAspect> aspects; // lazy
   private boolean valid = true;
@@ -55,13 +56,28 @@
         : ImmutableList.copyOf(aspects);
   }
 
+  public ImmutableList<MembersInjector<? super T>> getMembersInjectors() {
+    return membersInjectors == null
+        ? ImmutableList.<MembersInjector<? super T>>of()
+        : ImmutableList.copyOf(membersInjectors);
+  }
+
   public ImmutableList<InjectionListener<? super T>> getInjectionListeners() {
     return injectionListeners == null
         ? ImmutableList.<InjectionListener<? super T>>of()
         : ImmutableList.copyOf(injectionListeners);
   }
 
-  @SuppressWarnings("unchecked") // an InjectionListener<? super T> is an InjectionListener<T>
+  public void register(MembersInjector<? super T> membersInjector) {
+    checkState(valid, "Encounters may not be used after hear() returns.");
+
+    if (membersInjectors == null) {
+      membersInjectors = Lists.newArrayList();
+    }
+
+    membersInjectors.add(membersInjector);
+  }
+
   public void register(InjectionListener<? super T> injectionListener) {
     checkState(valid, "Encounters may not be used after hear() returns.");
 
diff --git a/src/com/google/inject/InheritingState.java b/src/com/google/inject/InheritingState.java
index 857b163..b1d8832 100644
--- a/src/com/google/inject/InheritingState.java
+++ b/src/com/google/inject/InheritingState.java
@@ -22,7 +22,7 @@
 import com.google.inject.internal.Maps;
 import com.google.inject.internal.MatcherAndConverter;
 import static com.google.inject.internal.Preconditions.checkNotNull;
-import com.google.inject.spi.InjectableTypeListenerBinding;
+import com.google.inject.spi.TypeListenerBinding;
 import java.lang.annotation.Annotation;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -45,7 +45,7 @@
   /*if[AOP]*/
   private final List<MethodAspect> methodAspects = Lists.newArrayList();
   /*end[AOP]*/
-  private final List<InjectableTypeListenerBinding> listenerBindings = Lists.newArrayList();
+  private final List<TypeListenerBinding> listenerBindings = Lists.newArrayList();
   private final WeakKeySet blacklistedKeys = new WeakKeySet();
   private final Object lock;
 
@@ -118,14 +118,14 @@
   }
   /*end[AOP]*/
 
-  public void addInjectableTypeListener(InjectableTypeListenerBinding listenerBinding) {
+  public void addTypeListener(TypeListenerBinding listenerBinding) {
     listenerBindings.add(listenerBinding);
   }
 
-  public List<InjectableTypeListenerBinding> getInjectableTypeListenerBindings() {
-    List<InjectableTypeListenerBinding> parentBindings = parent.getInjectableTypeListenerBindings();
-    List<InjectableTypeListenerBinding> result
-        = new ArrayList<InjectableTypeListenerBinding>(parentBindings.size() + 1);
+  public List<TypeListenerBinding> getTypeListenerBindings() {
+    List<TypeListenerBinding> parentBindings = parent.getTypeListenerBindings();
+    List<TypeListenerBinding> result
+        = new ArrayList<TypeListenerBinding>(parentBindings.size() + 1);
     result.addAll(parentBindings);
     result.addAll(listenerBindings);
     return result;
diff --git a/src/com/google/inject/InjectorShell.java b/src/com/google/inject/InjectorShell.java
index a03b9ab..c3e50b9 100644
--- a/src/com/google/inject/InjectorShell.java
+++ b/src/com/google/inject/InjectorShell.java
@@ -33,9 +33,9 @@
 import com.google.inject.spi.Dependency;
 import com.google.inject.spi.Element;
 import com.google.inject.spi.Elements;
-import com.google.inject.spi.InjectableTypeListenerBinding;
 import com.google.inject.spi.InjectionPoint;
 import com.google.inject.spi.PrivateElements;
+import com.google.inject.spi.TypeListenerBinding;
 import java.util.List;
 import java.util.logging.Logger;
 
@@ -147,9 +147,8 @@
       injector.constructionProxyFactory = new DefaultConstructionProxyFactory();
       end[NO_AOP]*/
 
-      new InjectableTypeListenerBindingProcessor(errors).process(injector, elements);
-      List<InjectableTypeListenerBinding> listenerBindings
-          = injector.state.getInjectableTypeListenerBindings();
+      new TypeListenerBindingProcessor(errors).process(injector, elements);
+      List<TypeListenerBinding> listenerBindings = injector.state.getTypeListenerBindings();
       injector.membersInjectorStore = new MembersInjectorStore(injector, listenerBindings);
       stopwatch.resetAndLog("TypeListeners creation");
 
diff --git a/src/com/google/inject/MembersInjectorImpl.java b/src/com/google/inject/MembersInjectorImpl.java
index a1ea77e..b437749 100644
--- a/src/com/google/inject/MembersInjectorImpl.java
+++ b/src/com/google/inject/MembersInjectorImpl.java
@@ -33,16 +33,19 @@
   private final TypeLiteral<T> typeLiteral;
   private final InjectorImpl injector;
   private final ImmutableList<SingleMemberInjector> memberInjectors;
+  private final ImmutableList<MembersInjector<? super T>> userMembersInjectors;
   private final ImmutableList<InjectionListener<? super T>> injectionListeners;
   private final ImmutableList<MethodAspect> addedAspects;
 
   MembersInjectorImpl(InjectorImpl injector, TypeLiteral<T> typeLiteral,
       ImmutableList<SingleMemberInjector> memberInjectors,
+      ImmutableList<MembersInjector<? super T>> userMembersInjectors,
       ImmutableList<InjectionListener<? super T>> injectionListeners,
       ImmutableList<MethodAspect> addedAspects) {
     this.injector = injector;
     this.typeLiteral = typeLiteral;
     this.memberInjectors = memberInjectors;
+    this.userMembersInjectors = userMembersInjectors;
     this.injectionListeners = injectionListeners;
     this.addedAspects = addedAspects;
   }
@@ -93,6 +96,14 @@
     for (SingleMemberInjector injector : memberInjectors) {
       injector.inject(errors, context, t);
     }
+
+    for (MembersInjector<? super T> userMembersInjector : userMembersInjectors) {
+      try {
+        userMembersInjector.injectMembers(t);
+      } catch (RuntimeException e) {
+        errors.errorInUserInjector(userMembersInjector, typeLiteral, e);
+      }
+    }
   }
 
   @Override public String toString() {
diff --git a/src/com/google/inject/MembersInjectorStore.java b/src/com/google/inject/MembersInjectorStore.java
index 470fc50..d298c6a 100644
--- a/src/com/google/inject/MembersInjectorStore.java
+++ b/src/com/google/inject/MembersInjectorStore.java
@@ -21,8 +21,8 @@
 import com.google.inject.internal.FailableCache;
 import com.google.inject.internal.ImmutableList;
 import com.google.inject.internal.Lists;
-import com.google.inject.spi.InjectableTypeListenerBinding;
 import com.google.inject.spi.InjectionPoint;
+import com.google.inject.spi.TypeListenerBinding;
 import java.lang.reflect.Field;
 import java.util.List;
 import java.util.Set;
@@ -34,7 +34,7 @@
  */
 class MembersInjectorStore {
   private final InjectorImpl injector;
-  private final ImmutableList<InjectableTypeListenerBinding> injectableTypeListenerBindings;
+  private final ImmutableList<TypeListenerBinding> typeListenerBindings;
 
   private final FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>> cache
       = new FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>>() {
@@ -45,9 +45,9 @@
   };
 
   MembersInjectorStore(InjectorImpl injector,
-      List<InjectableTypeListenerBinding> injectableTypeListenerBindings) {
+      List<TypeListenerBinding> typeListenerBindings) {
     this.injector = injector;
-    this.injectableTypeListenerBindings = ImmutableList.copyOf(injectableTypeListenerBindings);
+    this.typeListenerBindings = ImmutableList.copyOf(typeListenerBindings);
   }
 
   /**
@@ -55,7 +55,7 @@
    * aren't any type listeners.
    */
   public boolean hasTypeListeners() {
-    return !injectableTypeListenerBindings.isEmpty();
+    return !typeListenerBindings.isEmpty();
   }
 
   /**
@@ -84,7 +84,7 @@
     errors.throwIfNewErrors(numErrorsBefore);
 
     EncounterImpl<T> encounter = new EncounterImpl<T>(errors, injector.lookups);
-    for (InjectableTypeListenerBinding typeListener : injectableTypeListenerBindings) {
+    for (TypeListenerBinding typeListener : typeListenerBindings) {
       if (typeListener.getTypeMatcher().matches(type)) {
         try {
           typeListener.getListener().hear(type, encounter);
@@ -96,8 +96,8 @@
     encounter.invalidate();
     errors.throwIfNewErrors(numErrorsBefore);
 
-    return new MembersInjectorImpl<T>(injector, type, injectors, encounter.getInjectionListeners(),
-        encounter.getAspects());
+    return new MembersInjectorImpl<T>(injector, type, injectors, encounter.getMembersInjectors(),
+        encounter.getInjectionListeners(), encounter.getAspects());
   }
 
   /**
diff --git a/src/com/google/inject/ProxyFactory.java b/src/com/google/inject/ProxyFactory.java
index 5cd891b..c3c8a7a 100644
--- a/src/com/google/inject/ProxyFactory.java
+++ b/src/com/google/inject/ProxyFactory.java
@@ -141,8 +141,7 @@
   }
 
   /**
-   * Returns the interceptors that apply to the constructed type. When InjectableType.Listeners
-   * add additional interceptors, this builder will be thrown out and another created.n
+   * Returns the interceptors that apply to the constructed type.
    */
   public ImmutableMap<Method, List<MethodInterceptor>> getInterceptors() {
     return interceptors;
diff --git a/src/com/google/inject/State.java b/src/com/google/inject/State.java
index 7411aee..8a0eb4b 100644
--- a/src/com/google/inject/State.java
+++ b/src/com/google/inject/State.java
@@ -21,7 +21,7 @@
 import com.google.inject.internal.ImmutableList;
 import com.google.inject.internal.ImmutableSet;
 import com.google.inject.internal.MatcherAndConverter;
-import com.google.inject.spi.InjectableTypeListenerBinding;
+import com.google.inject.spi.TypeListenerBinding;
 import java.lang.annotation.Annotation;
 import java.util.List;
 import java.util.Map;
@@ -82,12 +82,11 @@
     }
     /*end[AOP]*/
 
-    public void addInjectableTypeListener(
-        InjectableTypeListenerBinding injectableTypeListenerBinding) {
+    public void addTypeListener(TypeListenerBinding typeListenerBinding) {
       throw new UnsupportedOperationException();
     }
 
-    public List<InjectableTypeListenerBinding> getInjectableTypeListenerBindings() {
+    public List<TypeListenerBinding> getTypeListenerBindings() {
       return ImmutableList.of();
     }
 
@@ -133,9 +132,9 @@
   List<MethodAspect> getMethodAspects();
   /*end[AOP]*/
 
-  void addInjectableTypeListener(InjectableTypeListenerBinding injectableTypeListenerBinding);
+  void addTypeListener(TypeListenerBinding typeListenerBinding);
 
-  List<InjectableTypeListenerBinding> getInjectableTypeListenerBindings();
+  List<TypeListenerBinding> getTypeListenerBindings();
 
   /**
    * Forbids the corresponding injector from creating a binding to {@code key}. Child injectors
diff --git a/src/com/google/inject/InjectableTypeListenerBindingProcessor.java b/src/com/google/inject/TypeListenerBindingProcessor.java
similarity index 72%
rename from src/com/google/inject/InjectableTypeListenerBindingProcessor.java
rename to src/com/google/inject/TypeListenerBindingProcessor.java
index 13217e5..01bc654 100644
--- a/src/com/google/inject/InjectableTypeListenerBindingProcessor.java
+++ b/src/com/google/inject/TypeListenerBindingProcessor.java
@@ -17,21 +17,21 @@
 package com.google.inject;
 
 import com.google.inject.internal.Errors;
-import com.google.inject.spi.InjectableTypeListenerBinding;
+import com.google.inject.spi.TypeListenerBinding;
 
 /**
  * Handles {@link Binder#bindListener} commands.
  *
  * @author jessewilson@google.com (Jesse Wilson)
  */
-class InjectableTypeListenerBindingProcessor extends AbstractProcessor {
+class TypeListenerBindingProcessor extends AbstractProcessor {
 
-  InjectableTypeListenerBindingProcessor(Errors errors) {
+  TypeListenerBindingProcessor(Errors errors) {
     super(errors);
   }
 
-  @Override public Boolean visit(InjectableTypeListenerBinding binding) {
-    injector.state.addInjectableTypeListener(binding);
+  @Override public Boolean visit(TypeListenerBinding binding) {
+    injector.state.addTypeListener(binding);
     return true;
   }
 }
\ No newline at end of file
diff --git a/src/com/google/inject/internal/Errors.java b/src/com/google/inject/internal/Errors.java
index 21e2f7e..fb1a10d 100644
--- a/src/com/google/inject/internal/Errors.java
+++ b/src/com/google/inject/internal/Errors.java
@@ -19,15 +19,16 @@
 import com.google.inject.ConfigurationException;
 import com.google.inject.CreationException;
 import com.google.inject.Key;
+import com.google.inject.MembersInjector;
 import com.google.inject.Provider;
 import com.google.inject.ProvisionException;
 import com.google.inject.Scope;
 import com.google.inject.TypeLiteral;
 import com.google.inject.spi.Dependency;
-import com.google.inject.spi.InjectableTypeListenerBinding;
 import com.google.inject.spi.InjectionListener;
 import com.google.inject.spi.InjectionPoint;
 import com.google.inject.spi.Message;
+import com.google.inject.spi.TypeListenerBinding;
 import java.io.PrintWriter;
 import java.io.Serializable;
 import java.io.StringWriter;
@@ -253,7 +254,7 @@
     return errorInUserCode(cause, "Error injecting method, %s", cause);
   }
 
-  public Errors errorNotifyingTypeListener(InjectableTypeListenerBinding listener,
+  public Errors errorNotifyingTypeListener(TypeListenerBinding listener,
       TypeLiteral<?> type, Throwable cause) {
     return errorInUserCode(cause,
         "Error notifying TypeListener %s (bound at %s) of %s.%n"
@@ -269,10 +270,16 @@
     return errorInUserCode(runtimeException, "Error in custom provider, %s", runtimeException);
   }
 
+  public Errors errorInUserInjector(
+      MembersInjector<?> listener, TypeLiteral<?> type, RuntimeException cause) {
+    return errorInUserCode(cause, "Error injecting %s using %s.%n"
+        + " Reason: %s", type, listener, cause);
+  }
+
   public Errors errorNotifyingInjectionListener(
-      InjectionListener listener, TypeLiteral<?> typeLiteral, RuntimeException cause) {
+      InjectionListener<?> listener, TypeLiteral<?> type, RuntimeException cause) {
     return errorInUserCode(cause, "Error notifying InjectionListener %s of %s.%n"
-        + " Reason: %s", listener, typeLiteral, cause);
+        + " Reason: %s", listener, type, cause);
   }
 
   public void exposedButNotBound(Key<?> key) {
diff --git a/src/com/google/inject/spi/DefaultElementVisitor.java b/src/com/google/inject/spi/DefaultElementVisitor.java
index 77c0e1d..cf8df75 100644
--- a/src/com/google/inject/spi/DefaultElementVisitor.java
+++ b/src/com/google/inject/spi/DefaultElementVisitor.java
@@ -79,7 +79,7 @@
     return visitOther(lookup);
   }
 
-  public V visit(InjectableTypeListenerBinding binding) {
+  public V visit(TypeListenerBinding binding) {
     return visitOther(binding);
   }
 }
diff --git a/src/com/google/inject/spi/ElementVisitor.java b/src/com/google/inject/spi/ElementVisitor.java
index 29bf111..fcff268 100644
--- a/src/com/google/inject/spi/ElementVisitor.java
+++ b/src/com/google/inject/spi/ElementVisitor.java
@@ -85,5 +85,5 @@
   /**
    * Visit an injectable type listener binding.
    */
-  V visit(InjectableTypeListenerBinding binding);
+  V visit(TypeListenerBinding binding);
 }
diff --git a/src/com/google/inject/spi/Elements.java b/src/com/google/inject/spi/Elements.java
index 68d7c05..15ce327 100644
--- a/src/com/google/inject/spi/Elements.java
+++ b/src/com/google/inject/spi/Elements.java
@@ -189,7 +189,7 @@
     }
 
     public void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher, TypeListener listener) {
-      elements.add(new InjectableTypeListenerBinding(getSource(), listener, typeMatcher));
+      elements.add(new TypeListenerBinding(getSource(), listener, typeMatcher));
     }
 
     public void requestStaticInjection(Class<?>... types) {
diff --git a/src/com/google/inject/spi/InterceptorBinding.java b/src/com/google/inject/spi/InterceptorBinding.java
index bec784c..2373786 100644
--- a/src/com/google/inject/spi/InterceptorBinding.java
+++ b/src/com/google/inject/spi/InterceptorBinding.java
@@ -34,7 +34,7 @@
  *         new MyTransactionInterceptor());</pre>
  *
  * or from an injectable type listener using {@link TypeEncounter#bindInterceptor(Matcher,
- * org.aopalliance.intercept.MethodInterceptor[]) InjectableType.Encounter.bindInterceptor()}.
+ * org.aopalliance.intercept.MethodInterceptor[]) TypeEncounter.bindInterceptor()}.
  *
  * @author jessewilson@google.com (Jesse Wilson)
  * @since 2.0
diff --git a/src/com/google/inject/spi/ModuleWriter.java b/src/com/google/inject/spi/ModuleWriter.java
index 8b97bbf..4c94845 100644
--- a/src/com/google/inject/spi/ModuleWriter.java
+++ b/src/com/google/inject/spi/ModuleWriter.java
@@ -109,7 +109,7 @@
         return null;
       }
 
-      public Void visit(InjectableTypeListenerBinding element) {
+      public Void visit(TypeListenerBinding element) {
         writeBindListener(binder, element);
         return null;
       }
@@ -138,7 +138,7 @@
   }
   /*end[AOP]*/
 
-  protected void writeBindListener(Binder binder, InjectableTypeListenerBinding element) {
+  protected void writeBindListener(Binder binder, TypeListenerBinding element) {
     binder.withSource(element.getSource())
         .bindListener(element.getTypeMatcher(), element.getListener());
   }
diff --git a/src/com/google/inject/spi/TypeEncounter.java b/src/com/google/inject/spi/TypeEncounter.java
index 31e07e4..b89750b 100644
--- a/src/com/google/inject/spi/TypeEncounter.java
+++ b/src/com/google/inject/spi/TypeEncounter.java
@@ -43,9 +43,8 @@
 
   /**
    * Records an exception for type {@code I}, the full details of which will be logged, and the
-   * message of which will be presented to the user at a later time. If your
-   * InjectableTypeListener calls something that you worry may fail, you should catch the
-   * exception and pass it to this method.
+   * message of which will be presented to the user at a later time. If your type listener calls
+   * something that you worry may fail, you should catch the exception and pass it to this method.
    */
   void addError(Throwable t);
 
@@ -89,11 +88,14 @@
   <T> MembersInjector<T> getMembersInjector(Class<T> type);
 
   /**
-   * Registers an injection listener for type {@code I}. Guice will notify the listener after
-   * injecting an instance of {@code I}. The order in which Guice will invoke listeners is
-   * unspecified.
-   *
-   * @param listener for injections into instances of type {@code I}
+   * Registers a members injector for type {@code I}. Guice will use the members injector after its
+   * performed its own injections on an instance of {@code I}.
+   */
+  void register(MembersInjector<? super I> membersInjector);
+
+  /**
+   * Registers an injection listener for type {@code I}. Guice will notify the listener after all
+   * injections have been performed on an instance of {@code I}.
    */
   void register(InjectionListener<? super I> listener);
 
diff --git a/src/com/google/inject/spi/TypeListener.java b/src/com/google/inject/spi/TypeListener.java
index 9461b39..f1b8017 100644
--- a/src/com/google/inject/spi/TypeListener.java
+++ b/src/com/google/inject/spi/TypeListener.java
@@ -37,7 +37,7 @@
    *
    * @param type encountered by Guice
    * @param encounter context of this encounter, enables reporting errors, registering injection
-   *     listeners and binding method interceptors for injectableType
+   *     listeners and binding method interceptors for {@code type}.
    *
    * @param <I> the injectable type
    */
diff --git a/src/com/google/inject/spi/InjectableTypeListenerBinding.java b/src/com/google/inject/spi/TypeListenerBinding.java
similarity index 76%
rename from src/com/google/inject/spi/InjectableTypeListenerBinding.java
rename to src/com/google/inject/spi/TypeListenerBinding.java
index 8cc3aed..00bf799 100644
--- a/src/com/google/inject/spi/InjectableTypeListenerBinding.java
+++ b/src/com/google/inject/spi/TypeListenerBinding.java
@@ -21,23 +21,21 @@
 import com.google.inject.matcher.Matcher;
 
 /**
- * Binds injectable types (picked using a Matcher) to an injectable type listener.
- * Registrations are created explicitly in a module using {@link
- * com.google.inject.Binder#bindListener(com.google.inject.matcher.Matcher, TypeListener)}
- * statements:
+ * Binds types (picked using a Matcher) to an type listener. Registrations are created explicitly in
+ * a module using {@link com.google.inject.Binder#bindListener(Matcher, TypeListener)} statements:
  *
  * <pre>
  *     register(only(new TypeLiteral&lt;PaymentService&lt;CreditCard>>() {}), listener);</pre>
  *
  * @author jessewilson@google.com (Jesse Wilson)
  */
-public final class InjectableTypeListenerBinding implements Element {
+public final class TypeListenerBinding implements Element {
 
-  final Object source;
-  final Matcher<? super TypeLiteral<?>> typeMatcher;
-  final TypeListener listener;
+  private final Object source;
+  private final Matcher<? super TypeLiteral<?>> typeMatcher;
+  private final TypeListener listener;
 
-  InjectableTypeListenerBinding(Object source, TypeListener listener,
+  TypeListenerBinding(Object source, TypeListener listener,
       Matcher<? super TypeLiteral<?>> typeMatcher) {
     this.source = source;
     this.listener = listener;