Author: limpbizkit
Date: Sat Mar 28 11:25:35 2009
New Revision: 913

Added:
    trunk/src/com/google/inject/LookupProcessor.java
       - copied, changed from r906,  
/trunk/src/com/google/inject/ProviderLookupProcessor.java
    trunk/src/com/google/inject/spi/InjectableType.java
    trunk/test/com/google/inject/MembersInjectorTest.java
Removed:
    trunk/src/com/google/inject/ProviderLookupProcessor.java
Modified:
    trunk/src/com/google/inject/ConstructorInjectorStore.java
    trunk/src/com/google/inject/InjectorBuilder.java
    trunk/src/com/google/inject/InjectorImpl.java
    trunk/src/com/google/inject/MembersInjector.java
    trunk/src/com/google/inject/internal/Errors.java
    trunk/src/com/google/inject/spi/Elements.java
    trunk/src/com/google/inject/spi/MembersInjectorLookup.java
    trunk/src/com/google/inject/spi/ModuleWriter.java
    trunk/src/com/google/inject/spi/ProviderLookup.java
    trunk/test/com/google/inject/AllTests.java
    trunk/test/com/google/inject/InjectableTypeListenerTest.java
    trunk/test/com/google/inject/spi/ElementsTest.java

Log:
Injector.getMembersInjector() and Binder.getMembersInjector() APIs both  
implemented and tested.

Still need to make it possible to inject a MembersInjector<T> anywhere,  
just like how we already support injecting a Provider<T> anywhere.

Modified: trunk/src/com/google/inject/ConstructorInjectorStore.java
==============================================================================
--- trunk/src/com/google/inject/ConstructorInjectorStore.java   (original)
+++ trunk/src/com/google/inject/ConstructorInjectorStore.java   Sat Mar 28  
11:25:35 2009
@@ -32,8 +32,6 @@
  import com.google.inject.spi.Message;
  import java.lang.reflect.Method;
  import java.util.List;
-import java.util.Map;
-import java.util.Set;
  import org.aopalliance.intercept.MethodInterceptor;

  /**
@@ -86,7 +84,7 @@

      ProxyFactory<T> proxyFactory = new ProxyFactory<T>(injectionPoint,  
injector.methodAspects);
      EncounterImpl<T> encounter = new EncounterImpl<T>();
-    InjectableType<T> injectableType = new InjectableTypeImpl<T>(
+    InjectableType<T> injectableType = new InjectableType<T>(
          injectionPoint, type, injectableMembers,  
proxyFactory.getInterceptors());

      for (InjectableTypeListenerBinding typeListener :  
injectableTypeListenerBindings) {
@@ -103,7 +101,7 @@
      if (encounter.hasAddedAspects()) {
        proxyFactory = new ProxyFactory<T>(
            injectionPoint, concat(injector.methodAspects,  
encounter.aspects));
-      injectableType = new InjectableTypeImpl<T>(
+      injectableType = new InjectableType<T>(
            injectionPoint, type, injectableMembers,  
proxyFactory.getInterceptors());
      }

@@ -174,41 +172,4 @@
      }
    }

-  static class InjectableTypeImpl<T> implements InjectableType<T> {
-    private final InjectionPoint injectionPoint;
-    private final TypeLiteral<T> type;
-    private final Set<InjectionPoint> injectableMembers;
-    private final Map<Method, List<MethodInterceptor>> methodInterceptors;
-
-    InjectableTypeImpl(InjectionPoint injectionPoint, TypeLiteral<T> type,
-        Set<InjectionPoint> injectableMembers,
-        Map<Method, List<MethodInterceptor>> methodInterceptors) {
-      this.injectionPoint = injectionPoint;
-      this.type = type;
-      this.injectableMembers = injectableMembers;
-      this.methodInterceptors = methodInterceptors;
-    }
-
-    public TypeLiteral<T> getType() {
-      return type;
-    }
-
-    public InjectionPoint getInjectableConstructor() {
-      return injectionPoint;
-    }
-
-    public Set<InjectionPoint> getInjectableMembers() throws  
ConfigurationException {
-      return injectableMembers;
-    }
-
-    /*if[AOP]*/
-    public Map<Method, List<MethodInterceptor>> getMethodInterceptors() {
-      return methodInterceptors;
-    }
-    /*end[AOP]*/
-
-    @Override public String toString() {
-      return type.toString();
-    }
-  }
  }

Modified: trunk/src/com/google/inject/InjectorBuilder.java
==============================================================================
--- trunk/src/com/google/inject/InjectorBuilder.java    (original)
+++ trunk/src/com/google/inject/InjectorBuilder.java    Sat Mar 28 11:25:35  
2009
@@ -137,7 +137,7 @@
      initializer.validateOustandingInjections(errors);
      stopwatch.resetAndLog("Instance member validation");

-    new ProviderLookupProcessor(errors).process(shells);
+    new LookupProcessor(errors).process(shells);
      stopwatch.resetAndLog("Provider verification");

      for (InjectorShell shell : shells) {

Modified: trunk/src/com/google/inject/InjectorImpl.java
==============================================================================
--- trunk/src/com/google/inject/InjectorImpl.java       (original)
+++ trunk/src/com/google/inject/InjectorImpl.java       Sat Mar 28 11:25:35 2009
@@ -726,54 +726,36 @@
    /** Cached constructor injectors for each type */
    FailableCache<TypeLiteral<?>, ConstructorInjector<?>> constructors;

-  void injectMembers(Errors errors, Object o, InternalContext context,
-      List<SingleMemberInjector> injectors)
-      throws ErrorsException {
-    for (SingleMemberInjector injector : injectors) {
-      injector.inject(errors, context, o);
-    }
+  @SuppressWarnings("unchecked") // the members injector type is  
consistent with instance's type
+  public void injectMembers(Object instance) {
+    MembersInjector membersInjector =  
getMembersInjector(instance.getClass());
+    membersInjector.injectMembers(instance);
    }

-  // Not test-covered
-  public void injectMembers(final Object o) {
-    Errors errors = new Errors(o.getClass());
-
-    // configuration/validation stuff throws ConfigurationException
-    List<SingleMemberInjector> injectors;
+  public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T>  
typeLiteral) {
+    Errors errors = new Errors(typeLiteral);
      try {
-      injectors = this.injectors.get(TypeLiteral.get(o.getClass()),  
errors);
+      return getMembersInjectorOrThrow(typeLiteral, errors);
      } catch (ErrorsException e) {
        throw new  
ConfigurationException(errors.merge(e.getErrors()).getMessages());
      }
-
-    // injection can throw ProvisionException
-    try {
-      injectMembersOrThrow(errors, o, injectors);
-    } catch (ErrorsException e) {
-      errors.merge(e.getErrors());
-    }
-
-    errors.throwProvisionExceptionIfErrorsExist();
-  }
-
-  public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T>  
typeLiteral) {
-    throw new UnsupportedOperationException("TODO");
    }

    public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
-    throw new UnsupportedOperationException("TODO");
+    return getMembersInjector(TypeLiteral.get(type));
    }

-  public void injectMembersOrThrow(final Errors errors, final Object o,
-      final List<SingleMemberInjector> injectors)
-      throws ErrorsException {
-    if (o == null) {
+  public void injectMembersOrThrow(final Errors errors, final Object  
instance,
+      final List<SingleMemberInjector> injectors) throws ErrorsException {
+    if (instance == null) {
        return;
      }

      callInContext(new ContextualCallable<Void>() {
        public Void call(InternalContext context) throws ErrorsException {
-        injectMembers(errors, o, context, injectors);
+        for (SingleMemberInjector injector : injectors) {
+          injector.inject(errors, context, instance);
+        }
          return null;
        }
      });
@@ -810,6 +792,23 @@

        @Override public String toString() {
          return factory.toString();
+      }
+    };
+  }
+
+  <T> MembersInjector<T> getMembersInjectorOrThrow(TypeLiteral<T> type,  
final Errors errors)
+      throws ErrorsException {
+    final ImmutableList<SingleMemberInjector> memberInjectors =  
injectors.get(type, errors);
+
+    return new MembersInjector<T>() {
+      public void injectMembers(T instance) {
+        try {
+          injectMembersOrThrow(errors, instance, memberInjectors);
+        } catch (ErrorsException e) {
+          errors.merge(e.getErrors());
+        }
+
+        errors.throwProvisionExceptionIfErrorsExist();
        }
      };
    }

Copied: trunk/src/com/google/inject/LookupProcessor.java (from r906,  
/trunk/src/com/google/inject/ProviderLookupProcessor.java)
==============================================================================
--- /trunk/src/com/google/inject/ProviderLookupProcessor.java   (original)
+++ trunk/src/com/google/inject/LookupProcessor.java    Sat Mar 28 11:25:35  
2009
@@ -18,25 +18,38 @@

  import com.google.inject.internal.Errors;
  import com.google.inject.internal.ErrorsException;
+import com.google.inject.spi.MembersInjectorLookup;
  import com.google.inject.spi.ProviderLookup;

  /**
- * Handles {...@link Binder#getProvider} commands.
+ * Handles {...@link Binder#getProvider} and {...@link  
Binder#getMembersInjector(TypeLiteral)} commands.
   *
   * @author [email protected] (Bob Lee)
   * @author [email protected] (Jesse Wilson)
   */
-class ProviderLookupProcessor extends AbstractProcessor {
+class LookupProcessor extends AbstractProcessor {

-  ProviderLookupProcessor(Errors errors) {
+  LookupProcessor(Errors errors) {
      super(errors);
    }

-  @Override public <T> Boolean visit(ProviderLookup<T> command) {
+  @Override public <T> Boolean visit(MembersInjectorLookup<T> lookup) {
+    try {
+      MembersInjector<T> membersInjector
+          = injector.getMembersInjectorOrThrow(lookup.getTypeLiteral(),  
errors);
+      lookup.initializeDelegate(membersInjector);
+    } catch (ErrorsException e) {
+      errors.merge(e.getErrors()); // TODO: source
+    }
+
+    return true;
+  }
+
+  @Override public <T> Boolean visit(ProviderLookup<T> lookup) {
      // ensure the provider can be created
      try {
-      Provider<T> provider = injector.getProviderOrThrow(command.getKey(),  
errors);
-      command.initializeDelegate(provider);
+      Provider<T> provider = injector.getProviderOrThrow(lookup.getKey(),  
errors);
+      lookup.initializeDelegate(provider);
      } catch (ErrorsException e) {
        errors.merge(e.getErrors()); // TODO: source
      }

Modified: trunk/src/com/google/inject/MembersInjector.java
==============================================================================
--- trunk/src/com/google/inject/MembersInjector.java    (original)
+++ trunk/src/com/google/inject/MembersInjector.java    Sat Mar 28 11:25:35  
2009
@@ -36,7 +36,7 @@
     * performing constructor injection), so if you're able to let Guice  
create all your objects for
     * you, you'll never need to use this method.
     *
-   * @param instance to inject members on
+   * @param instance to inject members on. May be {...@code null}.
     */
    void injectMembers(T instance);
  }

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    Sat Mar 28 11:25:35  
2009
@@ -578,6 +578,9 @@
      } else if (source instanceof Member) {
        formatter.format("  at %s%n", StackTraceElements.forMember((Member)  
source));

+    } else if (source instanceof TypeLiteral) {
+      formatter.format("  while locating %s%n", source);
+
      } else if (source instanceof Key) {
        Key<?> key = (Key<?>) source;
        formatter.format("  while locating %s%n", convert(key));

Modified: trunk/src/com/google/inject/spi/Elements.java
==============================================================================
--- trunk/src/com/google/inject/spi/Elements.java       (original)
+++ trunk/src/com/google/inject/spi/Elements.java       Sat Mar 28 11:25:35 2009
@@ -20,6 +20,7 @@
  import com.google.inject.Binder;
  import com.google.inject.Binding;
  import com.google.inject.Key;
+import com.google.inject.MembersInjector;
  import com.google.inject.Module;
  import com.google.inject.PrivateBinder;
  import com.google.inject.PrivateModule;
@@ -27,7 +28,6 @@
  import com.google.inject.Scope;
  import com.google.inject.Stage;
  import com.google.inject.TypeLiteral;
-import com.google.inject.MembersInjector;
  import com.google.inject.binder.AnnotatedBindingBuilder;
  import com.google.inject.binder.AnnotatedConstantBindingBuilder;
  import com.google.inject.binder.AnnotatedElementBuilder;
@@ -178,12 +178,26 @@
        elements.add(new InjectionRequest<T>(getSource(), type, instance));
      }

-    public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T>  
typeLiteral) {
-      throw new UnsupportedOperationException("TODO");
+    public <T> MembersInjector<T> getMembersInjector(final TypeLiteral<T>  
typeLiteral) {
+      final MembersInjectorLookup<T> element
+          = new MembersInjectorLookup<T>(getSource(), typeLiteral);
+      elements.add(element);
+      return new MembersInjector<T>() {
+        public void injectMembers(T instance) {
+          MembersInjector<T> delegate = element.getDelegate();
+          checkState(delegate != null,
+              "This MembersInjector cannot be used until the Injector has  
been created.");
+          delegate.injectMembers(instance);
+        }
+
+        @Override public String toString() {
+          return "MembersInjector<" + typeLiteral + ">";
+        }
+      };
      }

      public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
-      throw new UnsupportedOperationException("TODO");
+      return getMembersInjector(TypeLiteral.get(type));
      }

      public void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher,
@@ -252,13 +266,13 @@
      }

      public <T> Provider<T> getProvider(final Key<T> key) {
-      final ProviderLookup<T> command = new ProviderLookup<T>(getSource(),  
key);
-      elements.add(command);
+      final ProviderLookup<T> element = new ProviderLookup<T>(getSource(),  
key);
+      elements.add(element);
        return new Provider<T>() {
          public T get() {
-          Provider<T> delegate = command.getDelegate();
+          Provider<T> delegate = element.getDelegate();
            checkState(delegate != null,
-              "This provider cannot be used until the Injector has been  
created.");
+              "This Provider cannot be used until the Injector has been  
created.");
            return delegate.get();
          }


Added: trunk/src/com/google/inject/spi/InjectableType.java
==============================================================================
--- (empty file)
+++ trunk/src/com/google/inject/spi/InjectableType.java Sat Mar 28 11:25:35  
2009
@@ -0,0 +1,222 @@
+/**
+ * Copyright (C) 2009 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 com.google.inject.Key;
+import com.google.inject.MembersInjector;
+import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+import com.google.inject.internal.ImmutableMap;
+import com.google.inject.matcher.Matcher;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.aopalliance.intercept.MethodInterceptor;
+
+/**
+ * Represents an injectable type. A type is injectable if:
+ *
+ * <ol type="a">
+ *   <li>Guice can inject its constructor, or</li>
+ *   <li>Guice can inject the methods and fields of a pre-existing  
instance of the type</li>
+ * </ol>
+ *
+ * <p>Note: Despite generic type erasure, Guice keeps track of full types,  
so it can and does treat
+ * {...@code Foo<String>} and {...@code Foo<Integer>} as distinct injectable  
types.
+ *
+ * @author [email protected] (Bob Lee)
+ * @author [email protected] (Jesse Wilson)
+ * @since 2.0
+ */
+public final class InjectableType<T> {
+  private final InjectionPoint injectableConstructor;
+  private final TypeLiteral<T> type;
+  private final Set<InjectionPoint> injectableMembers;
+  private final ImmutableMap<Method, List<MethodInterceptor>>  
methodInterceptors;
+
+  public InjectableType(InjectionPoint injectableConstructor,  
TypeLiteral<T> type,
+      Set<InjectionPoint> injectableMembers,
+      Map<Method, List<MethodInterceptor>> methodInterceptors) {
+    this.injectableConstructor = injectableConstructor;
+    this.type = type;
+    this.injectableMembers = injectableMembers;
+    this.methodInterceptors = ImmutableMap.copyOf(methodInterceptors);
+  }
+
+  /**
+   * Returns the type literal.
+   */
+  public TypeLiteral<T> getType() {
+    return type;
+  }
+
+  /**
+   * Returns the injection point representing the contructor or {...@code  
null} if no injectable
+   * constructor is present
+   */
+  public InjectionPoint getInjectableConstructor() {
+    return injectableConstructor;
+  }
+
+  /**
+   * Returns the instance methods and fields of {...@code T} that can be  
injected.
+   *
+   * @return a possibly empty set of injection points. The set has a  
specified iteration order.
+   *     All fields are returned and then all methods. Within the fields,  
supertype fields are
+   *     returned before subtype fields. Similarly, supertype methods are  
returned before subtype
+   *     methods.
+   */
+  public Set<InjectionPoint> getInjectableMembers() {
+    return injectableMembers;
+  }
+
+  /*if[AOP]*/
+  /**
+   * Returns the interceptors applied to each method, in the order that  
they will be applied.
+   * These only apply when Guice instantiates {...@code I}.
+   *
+   * @return a possibly empty map
+   */
+  public Map<Method, List<MethodInterceptor>> getMethodInterceptors() {
+    return methodInterceptors;
+  }
+  /*end[AOP]*/
+
+  @Override public String toString() {
+    return type.toString();
+  }
+
+  /**
+   * Listens for Guice to encounter injectable types. If a given type has  
its constructor injected
+   * in one situation but only its methods and fields injected in another,  
Guice will notify
+   * this listener once.
+   *
+   * <p>Useful for extra type checking, {...@linkplain  
Encounter#register(InjectionListener)
+   * registering injection listeners}, and {...@linkplain  
Encounter#bindInterceptor(
+   * com.google.inject.matcher.Matcher,  
org.aopalliance.intercept.MethodInterceptor[])
+   * binding method interceptors}.
+   */
+  public interface Listener {
+
+    /**
+     * Invoked when Guice encounters a new type eligible for constructor  
or members injection.
+     * Called during injector creation (or afterwords if Guice encounters  
a type at run time and
+     * creates a JIT binding).
+     *
+     * @param injectableType encountered by Guice
+     * @param encounter context of this encounter, enables reporting  
errors, registering injection
+     *  listeners and binding method interceptors for injectableType
+     *
+     * @param <I> the injectable type
+     */
+    <I> void hear(InjectableType<I> injectableType, Encounter<I>  
encounter);
+
+  }
+
+  /**
+   * Context of the injectable type encounter. Enables reporting errors,  
registering injection
+   * listeners and binding method interceptors for injectable type {...@code  
I}.
+   *
+   * @param <I> the injectable type encountered
+   */
+  public interface Encounter<I> {
+
+    /**
+     * Records an error message for type {...@code I} which will be presented  
to the user at a later
+     * time. Unlike throwing an exception, this enable us to continue  
configuring the Injector and
+     * discover more errors. Uses {...@link String#format(String, Object[])}  
to insert the arguments
+     * into the message.
+     */
+    void addError(String message, Object... arguments);
+
+    /**
+     * 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.
+     */
+    void addError(Throwable t);
+
+    /**
+     * Records an error message to be presented to the user at a later  
time.
+     */
+    void addError(Message message);
+
+    /**
+     * Returns the provider used to obtain instances for the given  
injection key. The returned
+     * provider will not be valid until the injector has been created. The  
provider will throw an
+     * {...@code IllegalStateException} if you try to use it beforehand.
+     */
+    <T> Provider<T> getProvider(Key<T> key);
+
+    /**
+     * Returns the provider used to obtain instances for the given  
injection type. The returned
+     * provider will not be valid until the injetor has been created. The  
provider will throw an
+     * {...@code IllegalStateException} if you try to use it beforehand.
+     */
+    <T> Provider<T> getProvider(Class<T> type);
+
+    /**
+     * Returns the members injector used to inject dependencies into  
methods and fields on instances
+     * of the given type {...@code T}. The returned members injector will not  
be valid until the main
+     * injector has been created. The members injector will throw an  
{...@code IllegalStateException}
+     * if you try to use it beforehand.
+     *
+     * @param typeLiteral type to get members injector for
+     */
+    <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral);
+
+    /**
+     * Returns the members injector used to inject dependencies into  
methods and fields on instances
+     * of the given type {...@code T}. The returned members injector will not  
be valid until the main
+     * injector has been created. The members injector will throw an  
{...@code IllegalStateException}
+     * if you try to use it beforehand.
+     *
+     * @param type type to get members injector for
+     */
+    <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}
+     */
+    void register(InjectionListener<? super I> listener);
+
+    /*if[AOP]*/
+    /**
+     * Binds method interceptor[s] to methods matched in type {...@code I}  
and its supertypes. A
+     * method is eligible for interception if:
+     *
+     * <ul>
+     *  <li>Guice created the instance the method is on</li>
+     *  <li>Neither the enclosing type nor the method is final</li>
+     *  <li>And the method is package-private or more accessible</li>
+     * </ul>
+     *
+     * @param methodMatcher matches methods the interceptor should apply  
to. For
+     *     example: {...@code annotatedWith(Transactional.class)}.
+     * @param interceptors to bind
+     */
+    void bindInterceptor(Matcher<? super Method> methodMatcher,
+        org.aopalliance.intercept.MethodInterceptor... interceptors);
+    /*end[AOP]*/
+  }
+}

Modified: trunk/src/com/google/inject/spi/MembersInjectorLookup.java
==============================================================================
--- trunk/src/com/google/inject/spi/MembersInjectorLookup.java  (original)
+++ trunk/src/com/google/inject/spi/MembersInjectorLookup.java  Sat Mar 28  
11:25:35 2009
@@ -16,8 +16,9 @@

  package com.google.inject.spi;

-import com.google.inject.MembersInjector;
  import com.google.inject.Binder;
+import com.google.inject.MembersInjector;
+import com.google.inject.TypeLiteral;
  import static com.google.inject.internal.Preconditions.checkNotNull;
  import static com.google.inject.internal.Preconditions.checkState;

@@ -34,12 +35,12 @@
  public final class MembersInjectorLookup<T> implements Element {

    private final Object source;
-  private final InjectableType<T> injectableType;
+  private final TypeLiteral<T> typeLiteral;
    private MembersInjector<T> delegate;

-  MembersInjectorLookup(Object source, InjectableType<T> injectableType) {
+  MembersInjectorLookup(Object source, TypeLiteral<T> typeLiteral) {
      this.source = checkNotNull(source, "source");
-    this.injectableType = checkNotNull(injectableType, "injectableType");
+    this.typeLiteral = checkNotNull(typeLiteral, "typeLiteral");
    }

    public Object getSource() {
@@ -47,10 +48,10 @@
    }

    /**
-   * Gets the injectable type containing the members to be injected.
+   * Gets the type containing the members to be injected.
     */
-  public InjectableType<T> getInjectableType() {
-    return injectableType;
+  public TypeLiteral<T> getTypeLiteral() {
+    return typeLiteral;
    }

    public <T> T acceptVisitor(ElementVisitor<T> visitor) {
@@ -60,18 +61,15 @@
    /**
     * Sets the actual members injector.
     *
-   * @param delegate members injector
     * @throws IllegalStateException if the delegate is already set
-   * @throws NullPointerException if the delegate is null
     */
    public void initializeDelegate(MembersInjector<T> delegate) {
      checkState(this.delegate == null, "delegate already initialized");
-    checkNotNull(delegate, "delegate");
-    this.delegate = delegate;
+    this.delegate = checkNotNull(delegate, "delegate");
    }

    public void applyTo(Binder binder) {
-     
binder.withSource(getSource()).getMembersInjector(injectableType.getType());
+    binder.withSource(getSource()).getMembersInjector(typeLiteral);
    }

    /**

Modified: trunk/src/com/google/inject/spi/ModuleWriter.java
==============================================================================
--- trunk/src/com/google/inject/spi/ModuleWriter.java   (original)
+++ trunk/src/com/google/inject/spi/ModuleWriter.java   Sat Mar 28 11:25:35  
2009
@@ -19,6 +19,7 @@
  import com.google.inject.Binder;
  import com.google.inject.Binding;
  import com.google.inject.Key;
+import com.google.inject.MembersInjector;
  import com.google.inject.Module;
  import com.google.inject.PrivateBinder;
  import com.google.inject.Provider;
@@ -103,8 +104,9 @@
          return null;
        }

-      public <T> Void visit(MembersInjectorLookup<T> lookup) {
-        throw new UnsupportedOperationException("TODO");
+      public <T> Void visit(MembersInjectorLookup<T> element) {
+        writeGetMembersInjector(binder, element);
+        return null;
        }

        public Void visit(InjectableTypeListenerBinding binding) {
@@ -263,5 +265,11 @@
    protected <T> void writeGetProvider(Binder binder, ProviderLookup<T>  
element) {
      Provider<T> provider =  
binder.withSource(element.getSource()).getProvider(element.getKey());
      element.initializeDelegate(provider);
+  }
+
+  protected <T> void writeGetMembersInjector(Binder binder,  
MembersInjectorLookup<T> element) {
+    MembersInjector<T> membersInjector
+        =  
binder.withSource(element.getSource()).getMembersInjector(element.getTypeLiteral());
+    element.initializeDelegate(membersInjector);
    }
  }

Modified: trunk/src/com/google/inject/spi/ProviderLookup.java
==============================================================================
--- trunk/src/com/google/inject/spi/ProviderLookup.java (original)
+++ trunk/src/com/google/inject/spi/ProviderLookup.java Sat Mar 28 11:25:35  
2009
@@ -57,14 +57,11 @@
    /**
     * Sets the actual provider.
     *
-   * @param delegate provider
     * @throws IllegalStateException if the delegate is already set
-   * @throws NullPointerException if the delegate is null
     */
    public void initializeDelegate(Provider<T> delegate) {
      checkState(this.delegate == null, "delegate already initialized");
-    checkNotNull(delegate, "delegate");
-    this.delegate = delegate;
+    this.delegate = checkNotNull(delegate, "delegate");
    }

    public void applyTo(Binder binder) {

Modified: trunk/test/com/google/inject/AllTests.java
==============================================================================
--- trunk/test/com/google/inject/AllTests.java  (original)
+++ trunk/test/com/google/inject/AllTests.java  Sat Mar 28 11:25:35 2009
@@ -62,6 +62,7 @@
      suite.addTestSuite(KeyTest.class);
      suite.addTestSuite(LoggerInjectionTest.class);
      // MethodInterceptionTest is AOP-only
+    suite.addTestSuite(MembersInjectorTest.class);
      suite.addTestSuite(ModulesTest.class);
      suite.addTestSuite(ModuleTest.class);
      suite.addTestSuite(NullableInjectionPointTest.class);

Modified: trunk/test/com/google/inject/InjectableTypeListenerTest.java
==============================================================================
--- trunk/test/com/google/inject/InjectableTypeListenerTest.java        
(original)
+++ trunk/test/com/google/inject/InjectableTypeListenerTest.java        Sat Mar 
28  
11:25:35 2009
@@ -47,6 +47,30 @@
      };
    }

+  final InjectableType.Listener failingInjectableTypeListener = new  
InjectableType.Listener() {
+    int failures = 0;
+
+    public <I> void hear(InjectableType<I> injectableType, Encounter<I>  
encounter) {
+      throw new ClassCastException("whoops, failure #" + (++failures));
+    }
+
+    @Override public String toString() {
+      return "clumsy";
+    }
+  };
+
+  final InjectionListener<Object> failingInjectionListener = new  
InjectionListener<Object>() {
+    int failures = 0;
+
+    public void afterInjection(Object injectee) {
+      throw new ClassCastException("whoops, failure #" + (++failures));
+    }
+
+    @Override public String toString() {
+      return "goofy";
+    }
+  };
+
    public void testTypeListenersAreFired() throws NoSuchMethodException {
      final Constructor<A> aConstructor = A.class.getDeclaredConstructor();
      final AtomicInteger firedCount = new AtomicInteger();
@@ -133,22 +157,10 @@
    }

    public void testInjectableTypeListenerThrows() {
-    final InjectableType.Listener clumsyListener = new  
InjectableType.Listener() {
-      int failures = 0;
-
-      public <I> void hear(InjectableType<I> injectableType, Encounter<I>  
encounter) {
-        throw new ClassCastException("whoops, failure #" + (++failures));
-      }
-
-      @Override public String toString() {
-        return "clumsy";
-      }
-    };
-
      try {
        Guice.createInjector(new AbstractModule() {
          protected void configure() {
-          bindListener(any(), clumsyListener);
+          bindListener(any(), failingInjectableTypeListener);
            bind(B.class);
            bind(C.class);
          }
@@ -168,7 +180,7 @@

      Injector injector = Guice.createInjector(new AbstractModule() {
        protected void configure() {
-        bindListener(any(), clumsyListener);
+        bindListener(any(), failingInjectableTypeListener);
        }
      });
      try {
@@ -199,23 +211,11 @@
    }

    public void testInjectionListenerThrows() {
-    final InjectionListener<Object> injectionListener = new  
InjectionListener<Object>() {
-      int failures = 0;
-
-      public void afterInjection(Object injectee) {
-        throw new ClassCastException("whoops, failure #" + (++failures));
-      }
-
-      @Override public String toString() {
-        return "goofy";
-      }
-    };
-
      Injector injector = Guice.createInjector(new AbstractModule() {
        protected void configure() {
          bindListener(any(), new InjectableType.Listener() {
            public <I> void hear(InjectableType<I> injectableType,  
Encounter<I> encounter) {
-            encounter.register(injectionListener);
+            encounter.register(failingInjectionListener);
            }
          });
          bind(B.class);
@@ -254,6 +254,20 @@

      // non-constructed types do not participate
      assertSame(Stage.DEVELOPMENT, injector.getInstance(Stage.class));
+  }
+
+  public void testInjectMembersInjectableTypeListener() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        protected void configure() {
+          getMembersInjector(A.class);
+          bindListener(any(), failingInjectableTypeListener);
+        }
+      });
+      fail();
+    } catch (CreationException expected) {
+      assertContains(expected.getMessage(), "foo");
+    }
    }

    static class A {

Added: trunk/test/com/google/inject/MembersInjectorTest.java
==============================================================================
--- (empty file)
+++ trunk/test/com/google/inject/MembersInjectorTest.java       Sat Mar 28  
11:25:35 2009
@@ -0,0 +1,169 @@
+/**
+ * Copyright (C) 2009 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;
+
+import static com.google.inject.Asserts.assertContains;
+import java.util.concurrent.atomic.AtomicReference;
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+/**
+ * @author [email protected] (Jesse Wilson)
+ */
+public class MembersInjectorTest extends TestCase {
+
+  private static final A<C> uninjectableA = new A<C>() {
+    @Override void doNothing() {
+      throw new AssertionFailedError();
+    }
+  };
+
+  private static final B uninjectableB = new B() {
+    @Override void doNothing() {
+      throw new AssertionFailedError();
+    }
+  };
+
+  private static final C myFavouriteC = new C();
+
+  public void testMembersInjectorFromBinder() {
+    final AtomicReference<MembersInjector<A<C>>> aMembersInjectorReference
+        = new AtomicReference<MembersInjector<A<C>>>();
+    final AtomicReference<MembersInjector<B>> bMembersInjectorReference
+        = new AtomicReference<MembersInjector<B>>();
+
+    Guice.createInjector(new AbstractModule() {
+      @Override protected void configure() {
+        MembersInjector<A<C>> aMembersInjector = getMembersInjector(new  
TypeLiteral<A<C>>() {});
+        try {
+          aMembersInjector.injectMembers(uninjectableA);
+          fail();
+        } catch (IllegalStateException expected) {
+          assertContains(expected.getMessage(),
+              "This MembersInjector cannot be used until the Injector has  
been created.");
+        }
+
+        MembersInjector<B> bMembersInjector = getMembersInjector(B.class);
+        try {
+          bMembersInjector.injectMembers(uninjectableB);
+          fail();
+        } catch (IllegalStateException expected) {
+          assertContains(expected.getMessage(),
+              "This MembersInjector cannot be used until the Injector has  
been created.");
+        }
+
+        aMembersInjectorReference.set(aMembersInjector);
+        bMembersInjectorReference.set(bMembersInjector);
+
+        bind(C.class).toInstance(myFavouriteC);
+      }
+    });
+
+    A<C> injectableA = new A<C>();
+    aMembersInjectorReference.get().injectMembers(injectableA);
+    assertSame(myFavouriteC, injectableA.t);
+    assertSame(myFavouriteC, injectableA.b.c);
+
+    B injectableB = new B();
+    bMembersInjectorReference.get().injectMembers(injectableB);
+    assertSame(myFavouriteC, injectableB.c);
+
+    B anotherInjectableB = new B();
+    bMembersInjectorReference.get().injectMembers(anotherInjectableB);
+    assertSame(myFavouriteC, anotherInjectableB.c);
+  }
+
+  public void testMembersInjectorFromInjector() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(C.class).toInstance(myFavouriteC);
+      }
+    });
+
+    MembersInjector<A<C>> aMembersInjector
+        = injector.getMembersInjector(new TypeLiteral<A<C>>() {});
+    MembersInjector<B> bMembersInjector =  
injector.getMembersInjector(B.class);
+
+    A<C> injectableA = new A<C>();
+    aMembersInjector.injectMembers(injectableA);
+    assertSame(myFavouriteC, injectableA.t);
+    assertSame(myFavouriteC, injectableA.b.c);
+
+    B injectableB = new B();
+    bMembersInjector.injectMembers(injectableB);
+    assertSame(myFavouriteC, injectableB.c);
+
+    B anotherInjectableB = new B();
+    bMembersInjector.injectMembers(anotherInjectableB);
+    assertSame(myFavouriteC, anotherInjectableB.c);
+  }
+
+  public void testMembersInjectorWithNonInjectedTypes() {
+    Injector injector = Guice.createInjector();
+
+    MembersInjector<NoInjectedMembers> membersInjector
+        = injector.getMembersInjector(NoInjectedMembers.class);
+
+    membersInjector.injectMembers(new NoInjectedMembers());
+    membersInjector.injectMembers(new NoInjectedMembers());
+  }
+
+  public void testInjectionFailure() {
+    Injector injector = Guice.createInjector();
+
+    MembersInjector<InjectionFailure> membersInjector
+        = injector.getMembersInjector(InjectionFailure.class);
+
+    try {
+      membersInjector.injectMembers(new InjectionFailure());
+      fail();
+    } catch (ProvisionException expected) {
+      assertContains(expected.getMessage(),
+          "1) Error injecting method, java.lang.ClassCastException:  
whoops, failure #1");
+    }
+  }
+
+  public void testInjectionAppliesToSpecifiedType() {
+    Injector injector = Guice.createInjector();
+
+    MembersInjector<Object> membersInjector =  
injector.getMembersInjector(Object.class);
+    membersInjector.injectMembers(new InjectionFailure());
+  }
+
+  static class A<T> {
+    @Inject B b;
+    @Inject T t;
+    @Inject void doNothing() {}
+  }
+
+  static class B {
+    @Inject C c;
+    @Inject void doNothing() {}
+  }
+
+  static class C {}
+
+  static class NoInjectedMembers {}
+
+  static class InjectionFailure {
+    int failures = 0;
+
+    @Inject void fail() {
+      throw new ClassCastException("whoops, failure #" + (++failures));
+    }
+  }
+}

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  Sat Mar 28 11:25:35  
2009
@@ -20,13 +20,16 @@
  import static com.google.inject.Asserts.assertContains;
  import com.google.inject.Binding;
  import com.google.inject.BindingAnnotation;
+import com.google.inject.Inject;
  import com.google.inject.Key;
+import com.google.inject.MembersInjector;
  import com.google.inject.Module;
  import com.google.inject.PrivateBinder;
  import com.google.inject.Provider;
  import com.google.inject.Scope;
  import com.google.inject.Scopes;
  import com.google.inject.Singleton;
+import com.google.inject.Stage;
  import com.google.inject.TypeLiteral;
  import com.google.inject.binder.AnnotatedBindingBuilder;
  import com.google.inject.binder.AnnotatedConstantBindingBuilder;
@@ -613,11 +616,12 @@
      checkModule(
          new AbstractModule() {
            protected void configure() {
-            Provider<String> keyGetProvider =  
getProvider(Key.get(String.class, SampleAnnotation.class));
+            Provider<String> keyGetProvider
+                = getProvider(Key.get(String.class,  
SampleAnnotation.class));
              try {
                keyGetProvider.get();
              } catch (IllegalStateException e) {
-              assertEquals("This provider cannot be used until the  
Injector has been created.",
+              assertEquals("This Provider cannot be used until the  
Injector has been created.",
                    e.getMessage());
              }

@@ -625,14 +629,14 @@
              try {
                typeGetProvider.get();
              } catch (IllegalStateException e) {
-              assertEquals("This provider cannot be used until the  
Injector has been created.",
+              assertEquals("This Provider cannot be used until the  
Injector has been created.",
                    e.getMessage());
              }
            }
          },

          new FailingElementVisitor() {
-          @Override public Void visit(ProviderLookup command) {
+          @Override public <T> Void visit(ProviderLookup<T> command) {
              assertEquals(Key.get(String.class, SampleAnnotation.class),  
command.getKey());
              assertNull(command.getDelegate());
              return null;
@@ -640,7 +644,7 @@
          },

          new FailingElementVisitor() {
-          @Override public Void visit(ProviderLookup command) {
+          @Override public <T> Void visit(ProviderLookup<T> command) {
              assertEquals(Key.get(String.class), command.getKey());
              assertNull(command.getDelegate());
              return null;
@@ -649,6 +653,49 @@
      );
    }

+  public void testGetMembersInjector() {
+    checkModule(
+        new AbstractModule() {
+          protected void configure() {
+            MembersInjector<A<String>> typeMembersInjector
+                = getMembersInjector(new TypeLiteral<A<String>>() {});
+            try {
+              typeMembersInjector.injectMembers(new A<String>());
+            } catch (IllegalStateException e) {
+              assertEquals(
+                  "This MembersInjector cannot be used until the Injector  
has been created.",
+                  e.getMessage());
+            }
+
+            MembersInjector<String> classMembersInjector =  
getMembersInjector(String.class);
+            try {
+              classMembersInjector.injectMembers("hello");
+            } catch (IllegalStateException e) {
+              assertEquals(
+                  "This MembersInjector cannot be used until the Injector  
has been created.",
+                  e.getMessage());
+            }
+          }
+        },
+
+        new FailingElementVisitor() {
+          @Override public <T> Void visit(MembersInjectorLookup<T>  
command) {
+            assertEquals(new TypeLiteral<A<String>>() {},  
command.getTypeLiteral());
+            assertNull(command.getDelegate());
+            return null;
+          }
+        },
+
+        new FailingElementVisitor() {
+          @Override public <T> Void visit(MembersInjectorLookup<T>  
command) {
+            assertEquals(TypeLiteral.get(String.class),  
command.getTypeLiteral());
+            assertNull(command.getDelegate());
+            return null;
+          }
+        }
+    );
+  }
+
    public void testRequestInjection() {
      final Object firstObject = new Object();
      final Object secondObject = new Object();
@@ -1004,4 +1051,8 @@
    public @interface SampleAnnotation { }

    public enum CoinSide { HEADS, TAILS }
+
+  static class A<T> {
+    @Inject Stage stage;
+  }
  }

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