diff --git a/toothpick-compiler/src/test/java/toothpick/compiler/memberinjector/FieldMemberInjectorTest.java b/toothpick-compiler/src/test/java/toothpick/compiler/memberinjector/FieldMemberInjectorTest.java index 24bcf10a..211e50f6 100644 --- a/toothpick-compiler/src/test/java/toothpick/compiler/memberinjector/FieldMemberInjectorTest.java +++ b/toothpick-compiler/src/test/java/toothpick/compiler/memberinjector/FieldMemberInjectorTest.java @@ -120,6 +120,46 @@ public void testNamedFieldInjection_whenUsingQualifierAnnotation() { .generatesSources(expectedSource); } + @Test + public void testNamedFieldInjection_whenUsingQualifierAnnotationAsInnerClass() { + JavaFileObject source = JavaFileObjects.forSourceString("test.TestFieldInjection", Joiner.on('\n').join(// + "package test;", // + "import javax.inject.Inject;", // + "import javax.inject.Named;", // + "import javax.inject.Qualifier;", // + "public class TestFieldInjection {", // + " @Inject @Bar Foo foo;", // + " public TestFieldInjection() {}", // + "", // + " @Qualifier", // + " @interface Bar {}", // + "}", // + "class Foo {}" + )); + + JavaFileObject expectedSource = JavaFileObjects.forSourceString("test/TestFieldInjection__MemberInjector", Joiner.on('\n').join(// + "package test;", // + "", // + "import java.lang.Override;", // + "import toothpick.MemberInjector;", // + "import toothpick.Scope;", // + "", // + "public final class TestFieldInjection__MemberInjector implements MemberInjector {", // + " @Override", // + " public void inject(TestFieldInjection target, Scope scope) {", // + " target.foo = scope.getInstance(Foo.class, \"test.TestFieldInjection.Bar\");", // + " }", // + "}" // + )); + + assert_().about(javaSource()) + .that(source) + .processedWith(memberInjectorProcessors()) + .compilesWithoutError() + .and() + .generatesSources(expectedSource); + } + @Test public void testNamedFieldInjection_whenUsingNonQualifierAnnotation() { JavaFileObject source = JavaFileObjects.forSourceString("test.TestFieldInjection", Joiner.on('\n').join(// diff --git a/toothpick-runtime/src/main/java/toothpick/ScopeImpl.java b/toothpick-runtime/src/main/java/toothpick/ScopeImpl.java index 07b7353b..def77437 100644 --- a/toothpick-runtime/src/main/java/toothpick/ScopeImpl.java +++ b/toothpick-runtime/src/main/java/toothpick/ScopeImpl.java @@ -1,5 +1,6 @@ package toothpick; +import java.lang.annotation.Annotation; import toothpick.config.Binding; import toothpick.config.Module; import toothpick.configuration.ConfigurationHolder; @@ -46,7 +47,7 @@ public ScopeImpl(Object name) { @Override public T getInstance(Class clazz) { - return getInstance(clazz, null); + return getInstance(clazz, (String) null); } @Override @@ -62,9 +63,17 @@ public T getInstance(Class clazz, String name) { return t; } + @Override + public T getInstance(Class clazz, Class name) { + if (name == null) { + return getInstance(clazz, (String) null); + } + return getInstance(clazz, name.getCanonicalName()); + } + @Override public Provider getProvider(Class clazz) { - return getProvider(clazz, null); + return getProvider(clazz, (String) null); } @Override @@ -73,9 +82,17 @@ public Provider getProvider(Class clazz, String name) { return new ThreadSafeProviderImpl<>(this, clazz, name, false); } + @Override + public Provider getProvider(Class clazz, Class name) { + if (name == null) { + return getProvider(clazz, (String) null); + } + return getProvider(clazz, name.getCanonicalName()); + } + @Override public Lazy getLazy(Class clazz) { - return getLazy(clazz, null); + return getLazy(clazz, (String) null); } @Override @@ -84,6 +101,14 @@ public Lazy getLazy(Class clazz, String name) { return new ThreadSafeProviderImpl<>(this, clazz, name, true); } + @Override + public Lazy getLazy(Class clazz, Class name) { + if (name == null) { + return getLazy(clazz, (String) null); + } + return getLazy(clazz, name.getCanonicalName()); + } + @Override public synchronized void installTestModules(Module... modules) { if (hasTestModules) { diff --git a/toothpick-runtime/src/test/java/toothpick/getInstance/NamedInstanceCreation.java b/toothpick-runtime/src/test/java/toothpick/getInstance/NamedInstanceCreation.java index 39ad7f63..7b8979ef 100644 --- a/toothpick-runtime/src/test/java/toothpick/getInstance/NamedInstanceCreation.java +++ b/toothpick-runtime/src/test/java/toothpick/getInstance/NamedInstanceCreation.java @@ -45,17 +45,19 @@ public void testNamedInjection_shouldNotBeConfusedWithUnNamedInjection_whenUsing //WHEN Foo instance = scope.getInstance(Foo.class, "bar"); Foo instance2 = scope.getInstance(Foo.class, "bar"); - Foo instance3 = scope.getInstance(Foo.class, FooName.class.getName()); - Foo instance4 = scope.getInstance(Foo.class); + Foo instance3 = scope.getInstance(Foo.class, FooName.class.getCanonicalName()); + Foo instance4 = scope.getInstance(Foo.class, FooName.class); + Foo instance5 = scope.getInstance(Foo.class); //THEN assertThat(instance, is(namedFooInstance)); assertThat(instance2, is(namedFooInstance)); assertThat(instance3, is(namedFooInstance)); - assertThat(instance4, notNullValue()); + assertThat(instance4, is(namedFooInstance)); + assertThat(instance5, notNullValue()); assertThat(instance, sameInstance(instance2)); assertThat(instance, sameInstance(instance3)); - assertThat(instance, not(sameInstance(instance4))); + assertThat(instance, not(sameInstance(instance5))); } @Test @@ -67,15 +69,17 @@ public void testNamedProviderInjection_shouldNotBeConfusedWithUnNamedInjection() //WHEN Provider provider = scope.getProvider(Foo.class, "bar"); Provider provider2 = scope.getProvider(Foo.class, "bar"); - Provider provider3 = scope.getProvider(Foo.class, FooName.class.getName()); - Provider provider4 = scope.getProvider(Foo.class); + Provider provider3 = scope.getProvider(Foo.class, FooName.class.getCanonicalName()); + Provider provider4 = scope.getProvider(Foo.class, FooName.class); + Provider provider5 = scope.getProvider(Foo.class); //THEN assertThat(provider.get(), is(namedFooInstance)); assertThat(provider2.get(), is(namedFooInstance)); assertThat(provider3.get(), is(namedFooInstance)); - assertThat(provider4.get(), notNullValue()); - assertThat(provider, not(sameInstance(provider4))); + assertThat(provider4.get(), is(namedFooInstance)); + assertThat(provider5.get(), notNullValue()); + assertThat(provider, not(sameInstance(provider5))); } @Test(expected = IllegalArgumentException.class) diff --git a/toothpick/src/main/java/toothpick/Scope.java b/toothpick/src/main/java/toothpick/Scope.java index 333fc1dc..cdf2ce8a 100644 --- a/toothpick/src/main/java/toothpick/Scope.java +++ b/toothpick/src/main/java/toothpick/Scope.java @@ -125,6 +125,22 @@ public interface Scope { */ T getInstance(Class clazz, String name); + /** + * Returns the instance of {@code clazz} named {@code name} if one is scoped in the current + * scope, or its ancestors. If there is no such instance, the factory associated + * to the clazz will be used to produce the instance. + * All {@link javax.inject.Inject} annotated fields of the instance are injected after creation. + * + * @param clazz the class for which to obtain an instance in the scope of this scope. + * @param name the name of this instance, if it's null then a unnamed binding is used, otherwise the associated named binding is used. + * Note that the name here is denotated by an annotation class, which must be annotated by {link Qualifier}. + * This is strictly equivalent to use the canonical name of this class as a string. + * @param the type of {@code clazz}. + * @return a scoped instance or a new one produced by the factory associated to {@code clazz}. + * @see toothpick.config.Binding + */ + T getInstance(Class clazz, Class name); + /** * Requests a provider via an unnamed binding. * @@ -148,6 +164,23 @@ public interface Scope { */ Provider getProvider(Class clazz, String name); + /** + * Returns a named {@code Provider} named {@code name} of {@code clazz} if one is scoped in the current + * scope, or its ancestors. If there is no such provider, the factory associated + * to the clazz will be used to create one. + * All {@link javax.inject.Inject} annotated fields of the instance are injected after creation. + * + * @param clazz the class for which to obtain a provider in the scope of this scope. + * @param name the name of this instance, if it's null then a unnamed binding is used, otherwise the associated named binding is used. + * Note that the name here is denotated by an annotation class, which must be annotated by {link Qualifier}. + * This is strictly equivalent to use the canonical name of this class as a string. + * @param the type of {@code clazz}. + * @return a scoped provider or a new one using the factory associated to {@code clazz}. + * Returned providers are thread safe. + * @see toothpick.config.Module + */ + Provider getProvider(Class clazz, Class name); + /** * Requests a Lazy via an unnamed binding. * @@ -173,6 +206,25 @@ public interface Scope { */ Lazy getLazy(Class clazz, String name); + /** + * Returns a {@code Lazy} named {@code name} of {@code clazz} if one provider is scoped in the current + * scope, or its ancestors. If there is no such provider, the factory associated + * to the clazz will be used to create one. + * All {@link javax.inject.Inject} annotated fields of the instance are injected after creation. + * If the {@code clazz} is annotated with {@link javax.inject.Singleton} then the created provider + * will be scoped in the root scope of the current scope. + * + * @param clazz the class for which to obtain a lazy in the scope of this scope. + * @param name the name of this instance, if it's null then a unnamed binding is used, otherwise the associated named binding is used. + * Note that the name here is denotated by an annotation class, which must be annotated by {link Qualifier}. + * This is strictly equivalent to use the canonical name of this class as a string. + * @param the type of {@code clazz}. + * @return a scoped lazy or a new one using the factory associated to {@code clazz}. + * Returned lazies are thread safe. + * @see toothpick.config.Module + */ + Lazy getLazy(Class clazz, Class name); + /** * Allows to install modules. * diff --git a/toothpick/src/main/java/toothpick/config/Binding.java b/toothpick/src/main/java/toothpick/config/Binding.java index b93187b5..5757454b 100644 --- a/toothpick/src/main/java/toothpick/config/Binding.java +++ b/toothpick/src/main/java/toothpick/config/Binding.java @@ -32,7 +32,7 @@ public Binding withName(Class annotationClassWithQu String.format("Only qualifier annotation annotations can be used to define a binding name. Add @Qualifier to %s", annotationClassWithQualifierAnnotation)); } - this.name = annotationClassWithQualifierAnnotation.getName(); + this.name = annotationClassWithQualifierAnnotation.getCanonicalName(); return this; }