Binder.requestInjection, as implemented by Mike Ward.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@534 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/AbstractModule.java b/src/com/google/inject/AbstractModule.java
index 92cd0ba..62d1fb7 100644
--- a/src/com/google/inject/AbstractModule.java
+++ b/src/com/google/inject/AbstractModule.java
@@ -142,6 +142,13 @@
   }
 
   /**
+   * @see Binder#requestInjection(Object[])
+   */
+  protected void requestInjection(Object... objects) {
+    binder.requestInjection(objects);
+  }
+
+  /**
    * @see Binder#requestStaticInjection(Class[])
    */
   protected void requestStaticInjection(Class<?>... types) {
diff --git a/src/com/google/inject/Binder.java b/src/com/google/inject/Binder.java
index e35a732..dafbb74 100644
--- a/src/com/google/inject/Binder.java
+++ b/src/com/google/inject/Binder.java
@@ -225,6 +225,14 @@
   AnnotatedConstantBindingBuilder bindConstant();
 
   /**
+   * Upon successful creation, the {@link Injector} will inject instance fields
+   * and methods of the given objects.
+   *
+   * @param instances for which members will be injected
+   */
+  void requestInjection(Object... instances);
+
+  /**
    * Upon successful creation, the {@link Injector} will inject static fields
    * and methods in the given classes.
    *
diff --git a/src/com/google/inject/CommandProcessor.java b/src/com/google/inject/CommandProcessor.java
index 918cb57..4b806a7 100644
--- a/src/com/google/inject/CommandProcessor.java
+++ b/src/com/google/inject/CommandProcessor.java
@@ -24,6 +24,7 @@
 import com.google.inject.commands.Command;
 import com.google.inject.commands.ConvertToTypesCommand;
 import com.google.inject.commands.GetProviderCommand;
+import com.google.inject.commands.RequestInjectionCommand;
 import com.google.inject.commands.RequestStaticInjectionCommand;
 import com.google.inject.internal.Errors;
 import java.util.Iterator;
@@ -74,6 +75,10 @@
     return false;
   }
 
+  public Boolean visitRequestInjection(RequestInjectionCommand command) {
+    return false;
+  }
+
   public Boolean visitRequestStaticInjection(RequestStaticInjectionCommand command) {
     return false;
   }
diff --git a/src/com/google/inject/InjectorBuilder.java b/src/com/google/inject/InjectorBuilder.java
index b026a89..313fb51 100644
--- a/src/com/google/inject/InjectorBuilder.java
+++ b/src/com/google/inject/InjectorBuilder.java
@@ -55,7 +55,7 @@
   private final List<Command> commands = Lists.newArrayList();
 
   private BindCommandProcessor bindCommandProcesor;
-  private RequestStaticInjectionCommandProcessor requestStaticInjectionCommandProcessor;
+  private RequestInjectionCommandProcessor requestInjectionCommandProcessor;
 
   /**
    * @param stage we're running in. If the stage is {@link Stage#PRODUCTION}, we will eagerly load
@@ -147,10 +147,9 @@
     injector.index();
     stopwatch.resetAndLog("Binding indexing");
 
-    requestStaticInjectionCommandProcessor
-        = new RequestStaticInjectionCommandProcessor(errors);
-    requestStaticInjectionCommandProcessor
-        .processCommands(commands);
+    requestInjectionCommandProcessor
+        = new RequestInjectionCommandProcessor(errors, injector.outstandingInjections);
+    requestInjectionCommandProcessor.processCommands(commands);
     stopwatch.resetAndLog("Static injection");
   }
 
@@ -159,7 +158,7 @@
     bindCommandProcesor.runCreationListeners(injector);
     stopwatch.resetAndLog("Validation");
 
-    requestStaticInjectionCommandProcessor.validate(injector);
+    requestInjectionCommandProcessor.validate(injector);
     stopwatch.resetAndLog("Static validation");
 
     injector.validateOustandingInjections(errors);
@@ -175,7 +174,7 @@
   private void fulfillInjectionRequests() {
     futureInjector.initialize(injector);
 
-    requestStaticInjectionCommandProcessor.injectMembers(injector);
+    requestInjectionCommandProcessor.injectMembers(injector);
     stopwatch.resetAndLog("Static member injection");
 
     injector.fulfillOutstandingInjections(errors);
diff --git a/src/com/google/inject/RequestStaticInjectionCommandProcessor.java b/src/com/google/inject/RequestInjectionCommandProcessor.java
similarity index 80%
rename from src/com/google/inject/RequestStaticInjectionCommandProcessor.java
rename to src/com/google/inject/RequestInjectionCommandProcessor.java
index 132ff54..30a1551 100644
--- a/src/com/google/inject/RequestStaticInjectionCommandProcessor.java
+++ b/src/com/google/inject/RequestInjectionCommandProcessor.java
@@ -18,23 +18,29 @@
 
 import com.google.common.collect.Lists;
 import com.google.inject.InjectorImpl.SingleMemberInjector;
+import com.google.inject.commands.RequestInjectionCommand;
 import com.google.inject.commands.RequestStaticInjectionCommand;
 import com.google.inject.internal.Errors;
 import com.google.inject.internal.ErrorsException;
 import java.util.List;
+import java.util.Set;
 
 /**
- * Handles {@link Binder#requestStaticInjection} commands.
+ * Handles {@link Binder#requestInjection} and {@link Binder#requestStaticInjection} commands.
  *
  * @author crazybob@google.com (Bob Lee)
  * @author jessewilson@google.com (Jesse Wilson)
+ * @author mikeward@google.com (Mike Ward)
  */
-class RequestStaticInjectionCommandProcessor extends CommandProcessor {
+class RequestInjectionCommandProcessor extends CommandProcessor {
 
   private final List<StaticInjection> staticInjections = Lists.newArrayList();
+  private final Set<Object> outstandingInjections;
 
-  RequestStaticInjectionCommandProcessor(Errors errors) {
+  RequestInjectionCommandProcessor(Errors errors,
+      Set<Object> outstandingInjections) {
     super(errors);
+    this.outstandingInjections = outstandingInjections;
   }
 
   @Override public Boolean visitRequestStaticInjection(RequestStaticInjectionCommand command) {
@@ -44,6 +50,13 @@
     return true;
   }
 
+  @Override public Boolean visitRequestInjection(RequestInjectionCommand command) {
+    for (Object instance : command.getInstances()) {
+      outstandingInjections.add(instance);
+    }
+    return true;
+  }
+
   public void validate(InjectorImpl injector) {
     for (StaticInjection staticInjection : staticInjections) {
       staticInjection.validate(injector);
diff --git a/src/com/google/inject/commands/Command.java b/src/com/google/inject/commands/Command.java
index e87f67e..01e06cb 100644
--- a/src/com/google/inject/commands/Command.java
+++ b/src/com/google/inject/commands/Command.java
@@ -32,6 +32,7 @@
     V visitAddMessage(AddMessageCommand command);
     V visitBindInterceptor(BindInterceptorCommand command);
     V visitBindScope(BindScopeCommand command);
+    V visitRequestInjection(RequestInjectionCommand command);
     V visitRequestStaticInjection(RequestStaticInjectionCommand command);
     V visitBindConstant(BindConstantCommand command);
     V visitConvertToTypes(ConvertToTypesCommand command);
diff --git a/src/com/google/inject/commands/CommandRecorder.java b/src/com/google/inject/commands/CommandRecorder.java
index 42bb586..1ae4276 100644
--- a/src/com/google/inject/commands/CommandRecorder.java
+++ b/src/com/google/inject/commands/CommandRecorder.java
@@ -123,6 +123,10 @@
       commands.add(new BindScopeCommand(getSource(), annotationType, scope));
     }
 
+    public void requestInjection(Object... instances) {
+      commands.add(new RequestInjectionCommand(getSource(), instances));
+    }
+
     public void requestStaticInjection(Class<?>... types) {
       commands.add(new RequestStaticInjectionCommand(getSource(), types));
     }
diff --git a/src/com/google/inject/commands/CommandReplayer.java b/src/com/google/inject/commands/CommandReplayer.java
index a666737..05c668b 100644
--- a/src/com/google/inject/commands/CommandReplayer.java
+++ b/src/com/google/inject/commands/CommandReplayer.java
@@ -69,6 +69,11 @@
         return null;
       }
 
+      public Void visitRequestInjection(RequestInjectionCommand command) {
+        replayRequestInjection(binder, command);
+        return null;
+      }
+
       public Void visitRequestStaticInjection(RequestStaticInjectionCommand command) {
         replayRequestStaticInjection(binder, command);
         return null;
@@ -116,6 +121,13 @@
         command.getAnnotationType(), command.getScope());
   }
 
+  public void replayRequestInjection(final Binder binder,
+      final RequestInjectionCommand command) {
+    List<Object> objects = command.getInstances();
+    binder.withSource(command.getSource())
+        .requestInjection(objects.toArray());
+  }
+
   public void replayRequestStaticInjection(final Binder binder,
       final RequestStaticInjectionCommand command) {
     List<Class> types = command.getTypes();
diff --git a/src/com/google/inject/commands/DefaultCommandVisitor.java b/src/com/google/inject/commands/DefaultCommandVisitor.java
index cf98592..91139dd 100644
--- a/src/com/google/inject/commands/DefaultCommandVisitor.java
+++ b/src/com/google/inject/commands/DefaultCommandVisitor.java
@@ -62,6 +62,10 @@
     return visitCommand(command);
   }
 
+  public V visitRequestInjection(RequestInjectionCommand command) {
+    return visitCommand(command);
+  }
+
   public V visitRequestStaticInjection(
       RequestStaticInjectionCommand command) {
     return visitCommand(command);
diff --git a/src/com/google/inject/commands/RequestInjectionCommand.java b/src/com/google/inject/commands/RequestInjectionCommand.java
new file mode 100644
index 0000000..f47c799
--- /dev/null
+++ b/src/com/google/inject/commands/RequestInjectionCommand.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2008 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.commands;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
+/**
+ * Immutable snapshot of a request for injection.
+ *
+ * @author mikeward@google.com (Mike Ward)
+ */
+public final class RequestInjectionCommand implements Command {
+  private Object source;
+  private List<Object> instances;
+
+  public RequestInjectionCommand(Object source, Object[] instances) {
+    this.source = checkNotNull(source, "source");
+    this.instances = ImmutableList.of(instances);
+  }
+
+  public Object getSource() {
+    return source;
+  }
+
+  public List<Object> getInstances() {
+    return instances;
+  }
+
+  public <T> T acceptVisitor(Visitor<T> visitor) {
+    return visitor.visitRequestInjection(this);
+  }
+}
diff --git a/src/com/google/inject/commands/RequestStaticInjectionCommand.java b/src/com/google/inject/commands/RequestStaticInjectionCommand.java
index 564eead..b1ce316 100644
--- a/src/com/google/inject/commands/RequestStaticInjectionCommand.java
+++ b/src/com/google/inject/commands/RequestStaticInjectionCommand.java
@@ -17,10 +17,9 @@
 package com.google.inject.commands;
 
 
-import java.util.Arrays;
-import static java.util.Collections.unmodifiableList;
-import java.util.List;
 import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
 
 /**
  * Immutable snapshot of a request for static injection.
@@ -33,7 +32,7 @@
 
   RequestStaticInjectionCommand(Object source, Class[] types) {
     this.source = checkNotNull(source, "source");
-    this.types = unmodifiableList(Arrays.asList(types.clone()));
+    this.types = ImmutableList.of(types);
   }
 
   public Object getSource() {
diff --git a/test/com/google/inject/AllTests.java b/test/com/google/inject/AllTests.java
index dfc78e8..2c447d4 100644
--- a/test/com/google/inject/AllTests.java
+++ b/test/com/google/inject/AllTests.java
@@ -68,7 +68,7 @@
     suite.addTestSuite(ReflectionTest.class);
     suite.addTestSuite(ScopesTest.class);
     suite.addTestSuite(SerializationTest.class);
-    suite.addTestSuite(StaticInjectionTest.class);
+    suite.addTestSuite(RequestInjectionTest.class);
     suite.addTestSuite(SuperclassTest.class);
     suite.addTestSuite(TypeLiteralTest.class);
 
diff --git a/test/com/google/inject/RequestInjectionTest.java b/test/com/google/inject/RequestInjectionTest.java
new file mode 100644
index 0000000..18f6994
--- /dev/null
+++ b/test/com/google/inject/RequestInjectionTest.java
@@ -0,0 +1,104 @@
+/**
+ * Copyright (C) 2006 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 java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import junit.framework.TestCase;
+
+/**
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class RequestInjectionTest extends TestCase {
+
+  @Retention(RUNTIME)
+  @BindingAnnotation @interface ForField {}
+
+  @Retention(RUNTIME)
+  @BindingAnnotation @interface ForMethod {}
+
+  protected void setUp() throws Exception {
+    super.setUp();
+    HasInjections.staticField = 0;
+    HasInjections.staticMethod = null;
+  }
+
+  public void testInjectMembers() {
+    final HasInjections hi = new HasInjections();
+
+    Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bindConstant().annotatedWith(ForMethod.class).to("test");
+        bindConstant().annotatedWith(ForField.class).to(5);
+        requestInjection(hi);
+      }
+    });
+
+    assertEquals("test", hi.instanceMethod);
+    assertEquals(5, hi.instanceField);
+    assertNull(HasInjections.staticMethod);
+    assertEquals(0, HasInjections.staticField);
+  }
+
+  public void testInjectStatics() throws CreationException {
+    Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bindConstant().annotatedWith(ForMethod.class).to("test");
+        bindConstant().annotatedWith(ForField.class).to(5);
+        requestStaticInjection(HasInjections.class);
+      }
+    });
+
+    assertEquals("test", HasInjections.staticMethod);
+    assertEquals(5, HasInjections.staticField);
+  }
+  
+  public void testInjectMembersAndStatics() {
+    final HasInjections hi = new HasInjections();
+
+    Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bindConstant().annotatedWith(ForMethod.class).to("test");
+        bindConstant().annotatedWith(ForField.class).to(5);
+        requestStaticInjection(HasInjections.class);
+        requestInjection(hi);
+      }
+    });
+
+    assertEquals("test", hi.instanceMethod);
+    assertEquals(5, hi.instanceField);
+    assertEquals("test", HasInjections.staticMethod);
+    assertEquals(5, HasInjections.staticField);
+  }
+
+  static class HasInjections {
+
+    @Inject @ForField static int staticField;
+    @Inject @ForField int instanceField;
+
+    static String staticMethod;
+    String instanceMethod;
+
+    @Inject static void setStaticMethod(@ForMethod String staticMethod) {
+      HasInjections.staticMethod = staticMethod;
+    }
+
+    @Inject void setInstanceS(@ForMethod String instanceS) {
+      this.instanceMethod = instanceS;
+    }
+  }
+}
diff --git a/test/com/google/inject/StaticInjectionTest.java b/test/com/google/inject/StaticInjectionTest.java
deleted file mode 100644
index 6f1ac5f..0000000
--- a/test/com/google/inject/StaticInjectionTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Copyright (C) 2006 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 java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-import junit.framework.TestCase;
-
-/**
- * @author crazybob@google.com (Bob Lee)
- */
-public class StaticInjectionTest extends TestCase {
-
-  @Retention(RUNTIME)
-  @BindingAnnotation @interface I {}
-
-  @Retention(RUNTIME)
-  @BindingAnnotation @interface S {}
-
-  public void testInjectStatics() throws CreationException {
-    Guice.createInjector(new AbstractModule() {
-      protected void configure() {
-        bindConstant().annotatedWith(S.class).to("test");
-        bindConstant().annotatedWith(I.class).to(5);
-        requestStaticInjection(StaticInjectionTest.Static.class);
-      }
-    });
-
-    assertEquals("test", StaticInjectionTest.Static.s);
-    assertEquals(5, StaticInjectionTest.Static.i);
-  }
-
-  static class Static {
-
-    @Inject @I static int i;
-
-    static String s;
-
-    @Inject static void setS(@S String s) {
-      StaticInjectionTest.Static.s = s;
-    }
-  }
-}
diff --git a/test/com/google/inject/commands/CommandRecorderTest.java b/test/com/google/inject/commands/CommandRecorderTest.java
index d3136c9..47e69f2 100644
--- a/test/com/google/inject/commands/CommandRecorderTest.java
+++ b/test/com/google/inject/commands/CommandRecorderTest.java
@@ -552,6 +552,26 @@
     );
   }
 
+  public void testRequestInjection() {
+    final Object firstObject = new Object();
+    final Object secondObject = new Object();
+
+    checkModule(
+        new AbstractModule() {
+          protected void configure() {
+            requestInjection(firstObject, secondObject);
+          }
+        },
+
+        new FailingVisitor() {
+          @Override public Void visitRequestInjection(RequestInjectionCommand command) {
+            assertEquals(Arrays.asList(firstObject, secondObject), command.getInstances());
+            return null;
+          }
+        }
+    );
+  }
+
   public void testRequestStaticInjection() {
     checkModule(
         new AbstractModule() {
@@ -774,6 +794,10 @@
       throw new AssertionFailedError();
     }
 
+    public Void visitRequestInjection(RequestInjectionCommand command) {
+      throw new AssertionFailedError();
+    }
+
     public Void visitRequestStaticInjection(RequestStaticInjectionCommand command) {
       throw new AssertionFailedError();
     }