Revision: c75677752630
Author:   Sam Berlin <[email protected]>
Date:     Thu May 31 16:54:04 2012
Log: Add Binder.requireAtInjectOnConstructors, to force Guice to require @Inject annotations on constructors.

Revision created by MOE tool push_codebase.
MOE_MIGRATION=4906

http://code.google.com/p/google-guice/source/detail?r=c75677752630

Added:
 /core/src/com/google/inject/spi/RequireAtInjectOnConstructorsOption.java
 /core/test/com/google/inject/RequireAtInjectOnConstructorsTest.java
Modified:
 /core/src/com/google/inject/Binder.java
 /core/src/com/google/inject/internal/BindingProcessor.java
 /core/src/com/google/inject/internal/ConstructorBindingImpl.java
 /core/src/com/google/inject/internal/Errors.java
 /core/src/com/google/inject/internal/InjectorImpl.java
 /core/src/com/google/inject/internal/InjectorOptionsProcessor.java
 /core/src/com/google/inject/spi/DefaultElementVisitor.java
 /core/src/com/google/inject/spi/ElementVisitor.java
 /core/src/com/google/inject/spi/Elements.java
 /core/src/com/google/inject/spi/InjectionPoint.java
 /core/test/com/google/inject/AllTests.java

=======================================
--- /dev/null
+++ /core/src/com/google/inject/spi/RequireAtInjectOnConstructorsOption.java Thu May 31 16:54:04 2012
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2012 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 static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.inject.Binder;
+import com.google.inject.Inject;
+
+/**
+ * A request to require explicit {@literal @}{@link Inject} annotations on constructors.
+ *
+ * @author [email protected] (Sam Berlin)
+ * @since 4.0
+ */
+public final class RequireAtInjectOnConstructorsOption implements Element {
+  private final Object source;
+
+  RequireAtInjectOnConstructorsOption(Object source) {
+    this.source = checkNotNull(source, "source");
+  }
+
+  public Object getSource() {
+    return source;
+  }
+
+  public void applyTo(Binder binder) {
+    binder.withSource(getSource()).requireAtInjectOnConstructors();
+  }
+
+  public <T> T acceptVisitor(ElementVisitor<T> visitor) {
+    return visitor.visit(this);
+  }
+}
=======================================
--- /dev/null
+++ /core/test/com/google/inject/RequireAtInjectOnConstructorsTest.java Thu May 31 16:54:04 2012
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2012 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 junit.framework.TestCase;
+
+/**
+ * Tests for {@link Binder#requireAtInjectOnConstructors()}
+ *
+ * @author [email protected] (Sam Berlin)
+ */
+public class RequireAtInjectOnConstructorsTest extends TestCase {
+
+  public void testNoCxtors_explicitBinding() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          bind(NoCxtors.class);
+          binder().requireAtInjectOnConstructors();
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(1, ce.getErrorMessages().size());
+      Asserts.assertContains(ce.getMessage(),
+ "1) Explicit @Inject annotations are required on constructors, but " + + NoCxtors.class.getName() + " has no constructors annotated with @Inject", + "at " + RequireAtInjectOnConstructorsTest.class.getName() + "$", "configure");
+    }
+  }
+
+  public void testNoCxtors_jitBinding() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        binder().requireAtInjectOnConstructors();
+      }
+    });
+    try {
+      injector.getInstance(NoCxtors.class);
+      fail();
+    } catch (ConfigurationException ce) {
+      Asserts.assertContains(ce.getMessage(),
+ "1) Explicit @Inject annotations are required on constructors, but " + + NoCxtors.class.getName() + " has no constructors annotated with @Inject",
+          "while locating " + NoCxtors.class.getName());
+    }
+  }
+
+  public void testNoCxtors_implicitBinding() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          bind(Interface.class).to(NoCxtors.class);
+          binder().requireAtInjectOnConstructors();
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(1, ce.getErrorMessages().size());
+      Asserts.assertContains(ce.getMessage(),
+ "1) Explicit @Inject annotations are required on constructors, but " + + NoCxtors.class.getName() + " has no constructors annotated with @Inject", + "at " + RequireAtInjectOnConstructorsTest.class.getName() + "$", "configure");
+    }
+  }
+
+  public void testNoCxtors_inheritedByPrivateModules() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          binder().requireAtInjectOnConstructors();
+          install(new PrivateModule() {
+            @Override
+            protected void configure() {
+              bind(NoCxtors.class);
+            }
+          });
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(1, ce.getErrorMessages().size());
+      Asserts.assertContains(ce.getMessage(),
+ "1) Explicit @Inject annotations are required on constructors, but " + + NoCxtors.class.getName() + " has no constructors annotated with @Inject", + "at " + RequireAtInjectOnConstructorsTest.class.getName() + "$", "configure");
+    }
+  }
+
+  public void testNoCxtors_accumulatesAllErrors() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          bind(NoCxtors.class);
+          bind(AnotherNoCxtors.class);
+          binder().requireAtInjectOnConstructors();
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(2, ce.getErrorMessages().size());
+      Asserts.assertContains(ce.getMessage(),
+ "1) Explicit @Inject annotations are required on constructors, but " + + NoCxtors.class.getName() + " has no constructors annotated with @Inject", + "at " + RequireAtInjectOnConstructorsTest.class.getName() + "$", "configure", + "2) Explicit @Inject annotations are required on constructors, but " + + AnotherNoCxtors.class.getName() + " has no constructors annotated with @Inject", + "at " + RequireAtInjectOnConstructorsTest.class.getName() + "$", "configure");
+    }
+  }
+
+  public void testNoCxtors_separateOptionsForPrivateModules() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          bind(AnotherNoCxtors.class);
+          install(new PrivateModule() {
+            @Override
+            protected void configure() {
+              binder().requireAtInjectOnConstructors();
+              bind(NoCxtors.class);
+            }
+          });
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+ // This is testing that the parent module doesn't fail because it isn't included
+      // in the error message.
+      assertEquals(1, ce.getErrorMessages().size());
+      Asserts.assertContains(ce.getMessage(),
+ "1) Explicit @Inject annotations are required on constructors, but " + + NoCxtors.class.getName() + " has no constructors annotated with @Inject", + "at " + RequireAtInjectOnConstructorsTest.class.getName() + "$", "configure");
+    }
+  }
+
+  public void testManyConstructorsButNoneWithAtInject() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          bind(ManyConstructors.class);
+          binder().requireAtInjectOnConstructors();
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(1, ce.getErrorMessages().size());
+      Asserts.assertContains(ce.getMessage(),
+ "1) Explicit @Inject annotations are required on constructors, but " + + ManyConstructors.class.getName() + " has no constructors annotated with @Inject", + "at " + RequireAtInjectOnConstructorsTest.class.getName() + "$", "configure");
+    }
+  }
+
+  public void testRequireAtInjectStillAllowsToConstructorBindings() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          try {
+            bind(ManyConstructors.class)
+                
.toConstructor(ManyConstructors.class.getDeclaredConstructor());
+          } catch (Exception e) {
+            throw new RuntimeException(e);
+          }
+          binder().requireAtInjectOnConstructors();
+        }
+      });
+    injector.getInstance(ManyConstructors.class);
+  }
+
+  private static interface Interface {}
+  private static class NoCxtors implements Interface {}
+  private static class AnotherNoCxtors {}
+  private static class ManyConstructors {
+    @SuppressWarnings("unused") ManyConstructors() {}
+    @SuppressWarnings("unused") ManyConstructors(String a) {}
+    @SuppressWarnings("unused") ManyConstructors(int a) {}
+  }
+}
=======================================
--- /core/src/com/google/inject/Binder.java     Sun Feb 26 18:23:19 2012
+++ /core/src/com/google/inject/Binder.java     Thu May 31 16:54:04 2012
@@ -470,4 +470,17 @@
    * @since 3.0
    */
   void disableCircularProxies();
-}
+
+  /**
+ * Requires that a {@literal @}{@link Inject} annotation exists on a constructor in order for + * Guice to consider it an eligible injectable class. By default, Guice will inject classes that + * have a no-args constructor if no {@literal @}{@link Inject} annotation exists on any
+   * constructor.
+   * <p>
+ * If the class is bound using {@link LinkedBindingBuilder#toConstructor}, Guice will still inject
+   * that constructor regardless of annotations.
+   *
+   * @since 4.0
+   */
+  void requireAtInjectOnConstructors();
+}
=======================================
--- /core/src/com/google/inject/internal/BindingProcessor.java Sun Feb 26 18:23:19 2012 +++ /core/src/com/google/inject/internal/BindingProcessor.java Thu May 31 16:54:04 2012
@@ -72,7 +72,7 @@
         prepareBinding();
         try {
ConstructorBindingImpl<T> onInjector = ConstructorBindingImpl.create(injector, key,
-              binding.getConstructor(), source, scoping, errors, false);
+ binding.getConstructor(), source, scoping, errors, false, false);
           scheduleInitialization(onInjector);
           putBinding(onInjector);
         } catch (ErrorsException e) {
=======================================
--- /core/src/com/google/inject/internal/ConstructorBindingImpl.java Sun Feb 26 18:23:19 2012 +++ /core/src/com/google/inject/internal/ConstructorBindingImpl.java Thu May 31 16:54:04 2012
@@ -23,6 +23,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.Binder;
 import com.google.inject.ConfigurationException;
+import com.google.inject.Inject;
 import com.google.inject.Key;
 import com.google.inject.TypeLiteral;
 import com.google.inject.internal.util.Classes;
@@ -71,7 +72,7 @@
    */
static <T> ConstructorBindingImpl<T> create(InjectorImpl injector, Key<T> key, InjectionPoint constructorInjector, Object source, Scoping scoping, Errors errors,
-      boolean failIfNotLinked)
+      boolean failIfNotLinked, boolean failIfNotExplicit)
       throws ErrorsException {
     int numErrors = errors.size();

@@ -96,6 +97,9 @@
     if (constructorInjector == null) {
       try {
constructorInjector = InjectionPoint.forConstructorOf(key.getTypeLiteral()); + if (failIfNotExplicit && !hasAtInject((Constructor) constructorInjector.getMember())) {
+          errors.atInjectRequired(rawType);
+        }
       } catch (ConfigurationException e) {
         throw errors.merge(e.getErrorMessages()).toException();
       }
@@ -120,6 +124,12 @@
     return new ConstructorBindingImpl<T>(
injector, key, source, scopedFactory, scoping, factoryFactory, constructorInjector);
   }
+
+  /** Returns true if the inject annotation is on the constructor. */
+  private static boolean hasAtInject(Constructor cxtor) {
+    return cxtor.isAnnotationPresent(Inject.class)
+        || cxtor.isAnnotationPresent(javax.inject.Inject.class);
+  }

@SuppressWarnings("unchecked") // the result type always agrees with the ConstructorInjector type public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
=======================================
--- /core/src/com/google/inject/internal/Errors.java Fri Sep 9 14:19:11 2011 +++ /core/src/com/google/inject/internal/Errors.java Thu May 31 16:54:04 2012
@@ -138,6 +138,13 @@
   public Errors jitDisabled(Key key) {
return addMessage("Explicit bindings are required and %s is not explicitly bound.", key);
   }
+
+  public Errors atInjectRequired(Class clazz) {
+    return addMessage(
+        "Explicit @Inject annotations are required on constructors,"
+        + " but %s has no constructors annotated with @Inject.",
+        clazz);
+  }

   public Errors converterReturnedNull(String stringValue, Object source,
       TypeLiteral<?> type, TypeConverterBinding typeConverterBinding) {
@@ -647,17 +654,17 @@

private static final Collection<Converter<?>> converters = ImmutableList.of(
       new Converter<Class>(Class.class) {
-        public String toString(Class c) {
+        @Override public String toString(Class c) {
           return c.getName();
         }
       },
       new Converter<Member>(Member.class) {
-        public String toString(Member member) {
+        @Override public String toString(Member member) {
           return Classes.toString(member);
         }
       },
       new Converter<Key>(Key.class) {
-        public String toString(Key key) {
+        @Override public String toString(Key key) {
           if (key.getAnnotationType() != null) {
             return key.getTypeLiteral() + " annotated with "
+ (key.getAnnotation() != null ? key.getAnnotation() : key.getAnnotationType());
=======================================
--- /core/src/com/google/inject/internal/InjectorImpl.java Sun Feb 26 18:23:19 2012 +++ /core/src/com/google/inject/internal/InjectorImpl.java Thu May 31 16:54:04 2012
@@ -71,11 +71,14 @@
     final Stage stage;
     final boolean jitDisabled;
     final boolean disableCircularProxies;
-
- InjectorOptions(Stage stage, boolean jitDisabled, boolean disableCircularProxies) {
+    final boolean atInjectRequired;
+
+ InjectorOptions(Stage stage, boolean jitDisabled, boolean disableCircularProxies,
+        boolean atInjectRequired) {
       this.stage = stage;
       this.jitDisabled = jitDisabled;
       this.disableCircularProxies = disableCircularProxies;
+      this.atInjectRequired = atInjectRequired;
     }

     @Override
@@ -84,6 +87,7 @@
           .add("stage", stage)
           .add("jitDisabled", jitDisabled)
           .add("disableCircularProxies", disableCircularProxies)
+          .add("atInjectRequired", atInjectRequired)
           .toString();
     }
   }
@@ -179,7 +183,7 @@
     if(isProvider(key)) {
       try {
// This is safe because isProvider above ensures that T is a Provider<?>
-        @SuppressWarnings("unchecked")
+        @SuppressWarnings({"unchecked", "cast"})
Key<?> providedKey = (Key<?>)getProvidedKey((Key)key, new Errors());
         if(getExistingBinding(providedKey) != null) {
           return getBinding(key);
@@ -651,7 +655,14 @@
     }


- return ConstructorBindingImpl.create(this, key, null, source, scoping, errors, jitBinding && options.jitDisabled);
+    return ConstructorBindingImpl.create(this,
+        key,
+        null, /* use default constructor */
+        source,
+        scoping,
+        errors,
+        jitBinding && options.jitDisabled,
+        options.atInjectRequired);
   }

   /**
@@ -813,7 +824,7 @@
     if (isProvider(key)) {
// These casts are safe. We know T extends Provider<X> and that given Key<Provider<X>>,
       // createProviderBinding() will return BindingImpl<Provider<X>>.
-      @SuppressWarnings("unchecked")
+      @SuppressWarnings({"unchecked", "cast"})
BindingImpl<T> binding = (BindingImpl<T>) createProviderBinding((Key) key, errors);
       return binding;
     }
@@ -822,7 +833,7 @@
     if (isMembersInjector(key)) {
// These casts are safe. T extends MembersInjector<X> and that given Key<MembersInjector<X>>, // createMembersInjectorBinding() will return BindingImpl<MembersInjector<X>>.
-      @SuppressWarnings("unchecked")
+      @SuppressWarnings({"unchecked", "cast"})
BindingImpl<T> binding = (BindingImpl<T>) createMembersInjectorBinding((Key) key, errors);
       return binding;
     }
=======================================
--- /core/src/com/google/inject/internal/InjectorOptionsProcessor.java Tue Jun 28 16:50:03 2011 +++ /core/src/com/google/inject/internal/InjectorOptionsProcessor.java Thu May 31 16:54:04 2012
@@ -22,6 +22,7 @@
 import com.google.inject.Stage;
 import com.google.inject.internal.InjectorImpl.InjectorOptions;
 import com.google.inject.spi.DisableCircularProxiesOption;
+import com.google.inject.spi.RequireAtInjectOnConstructorsOption;
 import com.google.inject.spi.RequireExplicitBindingsOption;

 /**
@@ -33,6 +34,7 @@

   private boolean disableCircularProxies = false;
   private boolean jitDisabled = false;
+  private boolean atInjectRequired = false;

   InjectorOptionsProcessor(Errors errors) {
     super(errors);
@@ -49,6 +51,12 @@
     jitDisabled = true;
     return true;
   }
+
+  @Override
+  public Boolean visit(RequireAtInjectOnConstructorsOption option) {
+    atInjectRequired = true;
+    return true;
+  }

   InjectorOptions getOptions(Stage stage, InjectorOptions parentOptions) {
     checkNotNull(stage, "stage must be set");
@@ -56,13 +64,15 @@
       return new InjectorOptions(
           stage,
           jitDisabled,
-          disableCircularProxies);
+          disableCircularProxies,
+          atInjectRequired);
     } else {
checkState(stage == parentOptions.stage, "child & parent stage don't match");
       return new InjectorOptions(
           stage,
           jitDisabled || parentOptions.jitDisabled,
-          disableCircularProxies || parentOptions.disableCircularProxies);
+          disableCircularProxies || parentOptions.disableCircularProxies,
+          atInjectRequired || parentOptions.atInjectRequired);
     }
   }

=======================================
--- /core/src/com/google/inject/spi/DefaultElementVisitor.java Sun Jun 5 11:32:05 2011 +++ /core/src/com/google/inject/spi/DefaultElementVisitor.java Thu May 31 16:54:04 2012
@@ -94,4 +94,8 @@
   public V visit(RequireExplicitBindingsOption option) {
     return visitOther(option);
   }
-}
+
+  public V visit(RequireAtInjectOnConstructorsOption option) {
+    return visitOther(option);
+  }
+}
=======================================
--- /core/src/com/google/inject/spi/ElementVisitor.java Sun Jun 5 11:32:05 2011 +++ /core/src/com/google/inject/spi/ElementVisitor.java Thu May 31 16:54:04 2012
@@ -17,6 +17,7 @@
 package com.google.inject.spi;

 import com.google.inject.Binding;
+import com.google.inject.Inject;

 /**
  * Visit elements.
@@ -105,4 +106,11 @@
    * @since 3.0
    */
   V visit(DisableCircularProxiesOption option);
-}
+
+  /**
+   * Visit a require explicit {@literal @}{@link Inject} command.
+   *
+   * @since 4.0
+   */
+  V visit(RequireAtInjectOnConstructorsOption option);
+}
=======================================
--- /core/src/com/google/inject/spi/Elements.java       Sun Feb 26 18:23:19 2012
+++ /core/src/com/google/inject/spi/Elements.java       Thu May 31 16:54:04 2012
@@ -315,6 +315,10 @@
     public void requireExplicitBindings() {
       elements.add(new RequireExplicitBindingsOption(getSource()));
     }
+
+    public void requireAtInjectOnConstructors() {
+      elements.add(new RequireAtInjectOnConstructorsOption(getSource()));
+    }

     public void expose(Key<?> key) {
       exposeInternal(key);
=======================================
--- /core/src/com/google/inject/spi/InjectionPoint.java Fri Sep 9 14:19:11 2011 +++ /core/src/com/google/inject/spi/InjectionPoint.java Thu May 31 16:54:04 2012
@@ -444,6 +444,7 @@
       this.field = field;
     }

+    @Override
     InjectionPoint toInjectionPoint() {
       return new InjectionPoint(declaringType, field, optional);
     }
@@ -463,6 +464,7 @@
       this.method = method;
     }

+    @Override
     InjectionPoint toInjectionPoint() {
       return new InjectionPoint(declaringType, method, optional);
     }
=======================================
--- /core/test/com/google/inject/AllTests.java  Thu Jul  7 17:34:16 2011
+++ /core/test/com/google/inject/AllTests.java  Thu May 31 16:54:04 2012
@@ -36,6 +36,7 @@
 import com.google.inject.util.NoopOverrideTest;
 import com.google.inject.util.ProvidersTest;
 import com.google.inject.util.TypesTest;
+
 import com.googlecode.guice.GuiceTck;
 import com.googlecode.guice.Jsr330Test;

@@ -92,6 +93,7 @@
     // ProxyFactoryTest is AOP-only
     suite.addTestSuite(ReflectionTest.class);
     suite.addTestSuite(RequestInjectionTest.class);
+    suite.addTestSuite(RequireAtInjectOnConstructorsTest.class);
     suite.addTestSuite(ScopesTest.class);
     suite.addTestSuite(SerializationTest.class);
     suite.addTestSuite(SuperclassTest.class);

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