blob: eee6a0c3fd88d1fa8f5efa15cbd876ca1e4499e3 [file] [log] [blame]
/*
* Copyright (C) 2014 The Dagger Authors.
*
* 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 dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
import static dagger.internal.codegen.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
import com.google.auto.common.MoreElements;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Sets;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
import dagger.MembersInjector;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.inject.Inject;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.JavaFileObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class ComponentProcessorTest {
@Parameters(name = "{0}")
public static Collection<Object[]> parameters() {
return CompilerMode.TEST_PARAMETERS;
}
private final CompilerMode compilerMode;
public ComponentProcessorTest(CompilerMode compilerMode) {
this.compilerMode = compilerMode;
}
@Test public void doubleBindingFromResolvedModules() {
JavaFileObject parent = JavaFileObjects.forSourceLines("test.ParentModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"import java.util.List;",
"",
"@Module",
"abstract class ParentModule<A> {",
" @Provides List<A> provideListB(A a) { return null; }",
"}");
JavaFileObject child = JavaFileObjects.forSourceLines("test.ChildModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Module",
"class ChildNumberModule extends ParentModule<Integer> {",
" @Provides Integer provideInteger() { return null; }",
"}");
JavaFileObject another = JavaFileObjects.forSourceLines("test.AnotherModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"import java.util.List;",
"",
"@Module",
"class AnotherModule {",
" @Provides List<Integer> provideListOfInteger() { return null; }",
"}");
JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.BadComponent",
"package test;",
"",
"import dagger.Component;",
"import java.util.List;",
"",
"@Component(modules = {ChildNumberModule.class, AnotherModule.class})",
"interface BadComponent {",
" List<Integer> listOfInteger();",
"}");
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(parent, child, another, componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining("List<Integer> is bound multiple times");
assertThat(compilation)
.hadErrorContaining("@Provides List<Integer> ChildNumberModule.provideListB(Integer)");
assertThat(compilation)
.hadErrorContaining("@Provides List<Integer> AnotherModule.provideListOfInteger()");
}
@Test public void privateNestedClassWithWarningThatIsAnErrorInComponent() {
JavaFileObject outerClass = JavaFileObjects.forSourceLines("test.OuterClass",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class OuterClass {",
" @Inject OuterClass(InnerClass innerClass) {}",
"",
" private static final class InnerClass {",
" @Inject InnerClass() {}",
" }",
"}");
JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.BadComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component",
"interface BadComponent {",
" OuterClass outerClass();",
"}");
Compilation compilation =
compilerWithOptions(
compilerMode.javacopts().append("-Adagger.privateMemberValidation=WARNING"))
.compile(outerClass, componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining("Dagger does not support injection into private classes");
}
@Test public void simpleComponent() {
JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class SomeInjectableType {",
" @Inject SomeInjectableType() {}",
"}");
JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
"package test;",
"",
"import dagger.Component;",
"import dagger.Lazy;",
"import javax.inject.Provider;",
"",
"@Component",
"interface SimpleComponent {",
" SomeInjectableType someInjectableType();",
" Lazy<SomeInjectableType> lazySomeInjectableType();",
" Provider<SomeInjectableType> someInjectableTypeProvider();",
"}");
JavaFileObject generatedComponent =
compilerMode
.javaFileBuilder("test.DaggerSimpleComponent")
.addLines(
"package test;",
"",
"import dagger.Lazy;",
"import dagger.internal.DoubleCheck;",
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {")
.addLinesIn(
FAST_INIT_MODE,
" private volatile Provider<SomeInjectableType> someInjectableTypeProvider;")
.addLines(
" private DaggerSimpleComponent() {}",
"",
" public static Builder builder() {",
" return new Builder();",
" }",
"",
" public static SimpleComponent create() {",
" return new Builder().build();",
" }",
"",
" @Override",
" public SomeInjectableType someInjectableType() {",
" return new SomeInjectableType();",
" }",
"",
" @Override",
" public Lazy<SomeInjectableType> lazySomeInjectableType() {")
.addLinesIn(
DEFAULT_MODE, //
" return DoubleCheck.lazy(SomeInjectableType_Factory.create());")
.addLinesIn(
FAST_INIT_MODE,
" return DoubleCheck.lazy(someInjectableTypeProvider());")
.addLines(
" }",
"",
" @Override",
" public Provider<SomeInjectableType> someInjectableTypeProvider() {")
.addLinesIn(
DEFAULT_MODE, //
" return SomeInjectableType_Factory.create();")
.addLinesIn(
FAST_INIT_MODE, //
" Object local = someInjectableTypeProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(0);",
" someInjectableTypeProvider = (Provider<SomeInjectableType>) local;",
" }",
" return (Provider<SomeInjectableType>) local;")
.addLines(
" }",
"",
" static final class Builder {",
" private Builder() {}",
"",
" public SimpleComponent build() {",
" return new DaggerSimpleComponent();",
" }",
" }")
.addLinesIn(
FAST_INIT_MODE,
" private final class SwitchingProvider<T> implements Provider<T> {",
" private final int id;",
"",
" SwitchingProvider(int id) {",
" this.id = id;",
" }",
"",
" @SuppressWarnings(\"unchecked\")",
" @Override",
" public T get() {",
" switch (id) {",
" case 0: return (T) new SomeInjectableType();",
" default: throw new AssertionError(id);",
" }",
" }",
" }")
.build();
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(injectableTypeFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerSimpleComponent")
.hasSourceEquivalentTo(generatedComponent);
}
@Test public void componentWithScope() {
JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
"package test;",
"",
"import javax.inject.Inject;",
"import javax.inject.Singleton;",
"",
"@Singleton",
"final class SomeInjectableType {",
" @Inject SomeInjectableType() {}",
"}");
JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
"package test;",
"",
"import dagger.Component;",
"import dagger.Lazy;",
"import javax.inject.Provider;",
"import javax.inject.Singleton;",
"",
"@Singleton",
"@Component",
"interface SimpleComponent {",
" SomeInjectableType someInjectableType();",
" Lazy<SomeInjectableType> lazySomeInjectableType();",
" Provider<SomeInjectableType> someInjectableTypeProvider();",
"}");
JavaFileObject generatedComponent =
compilerMode
.javaFileBuilder("test.DaggerSimpleComponent")
.addLines(
"package test;",
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {")
.addLinesIn(
FAST_INIT_MODE,
" private volatile Object someInjectableType = new MemoizedSentinel();",
" private volatile Provider<SomeInjectableType> someInjectableTypeProvider;")
.addLinesIn(
DEFAULT_MODE,
" private Provider<SomeInjectableType> someInjectableTypeProvider;",
"",
" @SuppressWarnings(\"unchecked\")",
" private void initialize() {",
" this.someInjectableTypeProvider =",
" DoubleCheck.provider(SomeInjectableType_Factory.create());",
" }",
"")
.addLines(
" @Override", //
" public SomeInjectableType someInjectableType() {")
.addLinesIn(
FAST_INIT_MODE,
" Object local = someInjectableType;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
" local = someInjectableType;",
" if (local instanceof MemoizedSentinel) {",
" local = new SomeInjectableType();",
" someInjectableType =",
" DoubleCheck.reentrantCheck(someInjectableType, local);",
" }",
" }",
" }",
" return (SomeInjectableType) local;")
.addLinesIn(
DEFAULT_MODE, //
" return someInjectableTypeProvider.get();")
.addLines(
" }",
"",
" @Override",
" public Lazy<SomeInjectableType> lazySomeInjectableType() {")
.addLinesIn(
DEFAULT_MODE, //
" return DoubleCheck.lazy(someInjectableTypeProvider);")
.addLinesIn(
FAST_INIT_MODE,
" return DoubleCheck.lazy(someInjectableTypeProvider());")
.addLines(
" }",
"",
" @Override",
" public Provider<SomeInjectableType> someInjectableTypeProvider() {")
.addLinesIn(
FAST_INIT_MODE, //
" Object local = someInjectableTypeProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(0);",
" someInjectableTypeProvider = (Provider<SomeInjectableType>) local;",
" }",
" return (Provider<SomeInjectableType>) local;")
.addLinesIn(
DEFAULT_MODE, //
" return someInjectableTypeProvider;")
.addLines( //
" }")
.addLinesIn(
FAST_INIT_MODE,
" private final class SwitchingProvider<T> implements Provider<T> {",
" private final int id;",
"",
" SwitchingProvider(int id) {",
" this.id = id;",
" }",
"",
" @SuppressWarnings(\"unchecked\")",
" @Override",
" public T get() {",
" switch (id) {",
" case 0: return (T) DaggerSimpleComponent.this.someInjectableType();",
" default: throw new AssertionError(id);",
" }",
" }",
" }")
.build();
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(injectableTypeFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerSimpleComponent")
.containsElementsIn(generatedComponent);
}
@Test public void simpleComponentWithNesting() {
JavaFileObject nestedTypesFile = JavaFileObjects.forSourceLines("test.OuterType",
"package test;",
"",
"import dagger.Component;",
"import javax.inject.Inject;",
"",
"final class OuterType {",
" static class A {",
" @Inject A() {}",
" }",
" static class B {",
" @Inject A a;",
" }",
" @Component interface SimpleComponent {",
" A a();",
" void inject(B b);",
" }",
"}");
JavaFileObject generatedComponent =
compilerMode
.javaFileBuilder("test.DaggerOuterType_SimpleComponent")
.addLines(
"package test;",
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerOuterType_SimpleComponent",
" implements OuterType.SimpleComponent {",
" private DaggerOuterType_SimpleComponent() {}",
"",
" @Override",
" public OuterType.A a() {",
" return new OuterType.A();",
" }",
"",
" @Override",
" public void inject(OuterType.B b) {",
" injectB(b);",
" }",
"",
" @CanIgnoreReturnValue",
" private OuterType.B injectB(OuterType.B instance) {",
" OuterType_B_MembersInjector.injectA(instance, new OuterType.A());",
" return instance;",
" }",
"}")
.build();
Compilation compilation =
compilerWithOptions(compilerMode.javacopts()).compile(nestedTypesFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerOuterType_SimpleComponent")
.containsElementsIn(generatedComponent);
}
@Test public void componentWithModule() {
JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class A {",
" @Inject A(B b) {}",
"}");
JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
"package test;",
"",
"interface B {}");
JavaFileObject cFile = JavaFileObjects.forSourceLines("test.C",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class C {",
" @Inject C() {}",
"}");
JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Module",
"final class TestModule {",
" @Provides B b(C c) { return null; }",
"}");
JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
"package test;",
"",
"import dagger.Component;",
"import javax.inject.Provider;",
"",
"@Component(modules = TestModule.class)",
"interface TestComponent {",
" A a();",
"}");
JavaFileObject generatedComponent =
compilerMode
.javaFileBuilder("test.DaggerTestComponent")
.addLines(
"package test;",
"",
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final TestModule testModule;",
"",
" private DaggerTestComponent(TestModule testModuleParam) {",
" this.testModule = testModuleParam;",
" }",
"",
" private B b() {",
" return TestModule_BFactory.b(testModule, new C());",
" }",
"",
" @Override",
" public A a() {",
" return new A(b());",
" }",
"",
" static final class Builder {",
" private TestModule testModule;",
"",
" public Builder testModule(TestModule testModule) {",
" this.testModule = Preconditions.checkNotNull(testModule);",
" return this;",
" }",
"",
" public TestComponent build() {",
" if (testModule == null) {",
" this.testModule = new TestModule();",
" }",
" return new DaggerTestComponent(testModule);",
" }",
" }",
"}")
.build();
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(aFile, bFile, cFile, moduleFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerTestComponent")
.containsElementsIn(generatedComponent);
}
@Test
public void componentWithAbstractModule() {
JavaFileObject aFile =
JavaFileObjects.forSourceLines(
"test.A",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class A {",
" @Inject A(B b) {}",
"}");
JavaFileObject bFile =
JavaFileObjects.forSourceLines("test.B",
"package test;",
"",
"interface B {}");
JavaFileObject cFile =
JavaFileObjects.forSourceLines(
"test.C",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class C {",
" @Inject C() {}",
"}");
JavaFileObject moduleFile =
JavaFileObjects.forSourceLines(
"test.TestModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Module",
"abstract class TestModule {",
" @Provides static B b(C c) { return null; }",
"}");
JavaFileObject componentFile =
JavaFileObjects.forSourceLines(
"test.TestComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component(modules = TestModule.class)",
"interface TestComponent {",
" A a();",
"}");
JavaFileObject generatedComponent =
compilerMode
.javaFileBuilder("test.DaggerTestComponent")
.addLines(
"package test;",
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private B b() {",
" return TestModule_BFactory.b(new C());",
" }",
"",
" @Override",
" public A a() {",
" return new A(b());",
" }",
"}")
.build();
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(aFile, bFile, cFile, moduleFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerTestComponent")
.containsElementsIn(generatedComponent);
}
@Test public void transitiveModuleDeps() {
JavaFileObject always = JavaFileObjects.forSourceLines("test.AlwaysIncluded",
"package test;",
"",
"import dagger.Module;",
"",
"@Module",
"final class AlwaysIncluded {}");
JavaFileObject testModule = JavaFileObjects.forSourceLines("test.TestModule",
"package test;",
"",
"import dagger.Module;",
"",
"@Module(includes = {DepModule.class, AlwaysIncluded.class})",
"final class TestModule extends ParentTestModule {}");
JavaFileObject parentTest = JavaFileObjects.forSourceLines("test.ParentTestModule",
"package test;",
"",
"import dagger.Module;",
"",
"@Module(includes = {ParentTestIncluded.class, AlwaysIncluded.class})",
"class ParentTestModule {}");
JavaFileObject parentTestIncluded = JavaFileObjects.forSourceLines("test.ParentTestIncluded",
"package test;",
"",
"import dagger.Module;",
"",
"@Module(includes = AlwaysIncluded.class)",
"final class ParentTestIncluded {}");
JavaFileObject depModule = JavaFileObjects.forSourceLines("test.TestModule",
"package test;",
"",
"import dagger.Module;",
"",
"@Module(includes = {RefByDep.class, AlwaysIncluded.class})",
"final class DepModule extends ParentDepModule {}");
JavaFileObject refByDep = JavaFileObjects.forSourceLines("test.RefByDep",
"package test;",
"",
"import dagger.Module;",
"",
"@Module(includes = AlwaysIncluded.class)",
"final class RefByDep extends ParentDepModule {}");
JavaFileObject parentDep = JavaFileObjects.forSourceLines("test.ParentDepModule",
"package test;",
"",
"import dagger.Module;",
"",
"@Module(includes = {ParentDepIncluded.class, AlwaysIncluded.class})",
"class ParentDepModule {}");
JavaFileObject parentDepIncluded = JavaFileObjects.forSourceLines("test.ParentDepIncluded",
"package test;",
"",
"import dagger.Module;",
"",
"@Module(includes = AlwaysIncluded.class)",
"final class ParentDepIncluded {}");
JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component(modules = TestModule.class)",
"interface TestComponent {",
"}");
// Generated code includes all includes, but excludes the parent modules.
// The "always" module should only be listed once.
JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
"test.DaggerTestComponent",
"package test;",
"",
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" static final class Builder {",
"",
" @Deprecated",
" public Builder testModule(TestModule testModule) {",
" Preconditions.checkNotNull(testModule)",
" return this;",
" }",
"",
" @Deprecated",
" public Builder parentTestIncluded(ParentTestIncluded parentTestIncluded) {",
" Preconditions.checkNotNull(parentTestIncluded)",
" return this;",
" }",
"",
" @Deprecated",
" public Builder alwaysIncluded(AlwaysIncluded alwaysIncluded) {",
" Preconditions.checkNotNull(alwaysIncluded)",
" return this;",
" }",
"",
" @Deprecated",
" public Builder depModule(DepModule depModule) {",
" Preconditions.checkNotNull(depModule)",
" return this;",
" }",
"",
" @Deprecated",
" public Builder parentDepIncluded(ParentDepIncluded parentDepIncluded) {",
" Preconditions.checkNotNull(parentDepIncluded)",
" return this;",
" }",
"",
" @Deprecated",
" public Builder refByDep(RefByDep refByDep) {",
" Preconditions.checkNotNull(refByDep)",
" return this;",
" }",
"",
" public TestComponent build() {",
" return new DaggerTestComponent();",
" }",
" }",
"}");
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(
always,
testModule,
parentTest,
parentTestIncluded,
depModule,
refByDep,
parentDep,
parentDepIncluded,
componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerTestComponent")
.containsElementsIn(generatedComponent);
}
@Test
public void generatedTransitiveModule() {
JavaFileObject rootModule = JavaFileObjects.forSourceLines("test.RootModule",
"package test;",
"",
"import dagger.Module;",
"",
"@Module(includes = GeneratedModule.class)",
"final class RootModule {}");
JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component(modules = RootModule.class)",
"interface TestComponent {}");
assertThat(
compilerWithOptions(compilerMode.javacopts()).compile(rootModule, component))
.failed();
assertThat(
daggerCompiler(
new GeneratingProcessor(
"test.GeneratedModule",
"package test;",
"",
"import dagger.Module;",
"",
"@Module",
"final class GeneratedModule {}"))
.compile(rootModule, component))
.succeeded();
}
@Test
public void generatedModuleInSubcomponent() {
JavaFileObject subcomponent =
JavaFileObjects.forSourceLines(
"test.ChildComponent",
"package test;",
"",
"import dagger.Subcomponent;",
"",
"@Subcomponent(modules = GeneratedModule.class)",
"interface ChildComponent {}");
JavaFileObject component =
JavaFileObjects.forSourceLines(
"test.TestComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component",
"interface TestComponent {",
" ChildComponent childComponent();",
"}");
assertThat(
compilerWithOptions(compilerMode.javacopts()).compile(subcomponent, component))
.failed();
assertThat(
daggerCompiler(
new GeneratingProcessor(
"test.GeneratedModule",
"package test;",
"",
"import dagger.Module;",
"",
"@Module",
"final class GeneratedModule {}"))
.compile(subcomponent, component))
.succeeded();
}
@Test
public void subcomponentNotGeneratedIfNotUsedInGraph() {
JavaFileObject component =
JavaFileObjects.forSourceLines(
"test.Parent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component(modules = ParentModule.class)",
"interface Parent {",
" String notSubcomponent();",
"}");
JavaFileObject module =
JavaFileObjects.forSourceLines(
"test.Parent",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Module(subcomponents = Child.class)",
"class ParentModule {",
" @Provides static String notSubcomponent() { return new String(); }",
"}");
JavaFileObject subcomponent =
JavaFileObjects.forSourceLines(
"test.Child",
"package test;",
"",
"import dagger.Subcomponent;",
"",
"@Subcomponent",
"interface Child {",
" @Subcomponent.Builder",
" interface Builder {",
" Child build();",
" }",
"}");
JavaFileObject generatedComponent =
JavaFileObjects.forSourceLines(
"test.DaggerParent",
"package test;",
"",
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerParent implements Parent {",
"",
" private DaggerParent() {}",
"",
" public static Builder builder() {",
" return new Builder();",
" }",
"",
" public static Parent create() {",
" return new Builder().build();",
" }",
"",
" @Override",
" public String notSubcomponent() {",
" return ParentModule_NotSubcomponentFactory.notSubcomponent();",
" }",
"",
" static final class Builder {",
"",
" private Builder() {}",
"",
" @Deprecated",
" public Builder parentModule(ParentModule parentModule) {",
" Preconditions.checkNotNull(parentModule);",
" return this;",
" }",
"",
" public Parent build() {",
" return new DaggerParent();",
" }",
" }",
"}");
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(component, module, subcomponent);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerParent")
.hasSourceEquivalentTo(generatedComponent);
}
@Test
public void testDefaultPackage() {
JavaFileObject aClass = JavaFileObjects.forSourceLines("AClass", "class AClass {}");
JavaFileObject bClass = JavaFileObjects.forSourceLines("BClass",
"import javax.inject.Inject;",
"",
"class BClass {",
" @Inject BClass(AClass a) {}",
"}");
JavaFileObject aModule = JavaFileObjects.forSourceLines("AModule",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Module class AModule {",
" @Provides AClass aClass() {",
" return new AClass();",
" }",
"}");
JavaFileObject component = JavaFileObjects.forSourceLines("SomeComponent",
"import dagger.Component;",
"",
"@Component(modules = AModule.class)",
"interface SomeComponent {",
" BClass bClass();",
"}");
assertThat(
compilerWithOptions(compilerMode.javacopts())
.compile(aModule, aClass, bClass, component))
.succeeded();
}
@Test public void membersInjection() {
JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class SomeInjectableType {",
" @Inject SomeInjectableType() {}",
"}");
JavaFileObject injectedTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectedType",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class SomeInjectedType {",
" @Inject SomeInjectableType injectedField;",
" SomeInjectedType() {}",
"}");
JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
"package test;",
"",
"import dagger.Component;",
"import dagger.Lazy;",
"import javax.inject.Provider;",
"",
"@Component",
"interface SimpleComponent {",
" void inject(SomeInjectedType instance);",
" SomeInjectedType injectAndReturn(SomeInjectedType instance);",
"}");
JavaFileObject generatedComponent =
compilerMode
.javaFileBuilder("test.DaggerSimpleComponent")
.addLines(
"package test;",
"",
"import com.google.errorprone.annotations.CanIgnoreReturnValue;",
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" @Override",
" public void inject(SomeInjectedType instance) {",
" injectSomeInjectedType(instance);",
" }",
"",
" @Override",
" public SomeInjectedType injectAndReturn(SomeInjectedType instance) {",
" return injectSomeInjectedType(instance);",
" }",
"",
" @CanIgnoreReturnValue",
" private SomeInjectedType injectSomeInjectedType(SomeInjectedType instance) {",
" SomeInjectedType_MembersInjector.injectInjectedField(",
" instance, new SomeInjectableType());",
" return instance;",
" }",
"}")
.build();
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(injectableTypeFile, injectedTypeFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerSimpleComponent")
.containsElementsIn(generatedComponent);
}
@Test public void componentInjection() {
JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class SomeInjectableType {",
" @Inject SomeInjectableType(SimpleComponent component) {}",
"}");
JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
"package test;",
"",
"import dagger.Component;",
"import dagger.Lazy;",
"import javax.inject.Provider;",
"",
"@Component",
"interface SimpleComponent {",
" SomeInjectableType someInjectableType();",
" Provider<SimpleComponent> selfProvider();",
"}");
JavaFileObject generatedComponent =
JavaFileObjects.forSourceLines(
"test.DaggerSimpleComponent",
"package test;",
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" private Provider<SimpleComponent> simpleComponentProvider;",
"",
" @SuppressWarnings(\"unchecked\")",
" private void initialize() {",
" this.simpleComponentProvider = InstanceFactory.create((SimpleComponent) this);",
" }",
"",
" @Override",
" public SomeInjectableType someInjectableType() {",
" return new SomeInjectableType(this)",
" }",
"",
" @Override",
" public Provider<SimpleComponent> selfProvider() {",
" return simpleComponentProvider;",
" }",
"}");
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(injectableTypeFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerSimpleComponent")
.containsElementsIn(generatedComponent);
}
@Test public void membersInjectionInsideProvision() {
JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class SomeInjectableType {",
" @Inject SomeInjectableType() {}",
"}");
JavaFileObject injectedTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectedType",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class SomeInjectedType {",
" @Inject SomeInjectableType injectedField;",
" @Inject SomeInjectedType() {}",
"}");
JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component",
"interface SimpleComponent {",
" SomeInjectedType createAndInject();",
"}");
JavaFileObject generatedComponent =
compilerMode
.javaFileBuilder("test.DaggerSimpleComponent")
.addLines(
"package test;",
"",
"import com.google.errorprone.annotations.CanIgnoreReturnValue;",
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" @Override",
" public SomeInjectedType createAndInject() {",
" return injectSomeInjectedType(",
" SomeInjectedType_Factory.newInstance());",
" }",
"",
" @CanIgnoreReturnValue",
" private SomeInjectedType injectSomeInjectedType(SomeInjectedType instance) {",
" SomeInjectedType_MembersInjector.injectInjectedField(",
" instance, new SomeInjectableType());",
" return instance;",
" }",
"}")
.build();
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(injectableTypeFile, injectedTypeFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerSimpleComponent")
.containsElementsIn(generatedComponent);
}
@Test public void componentDependency() {
JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class A {",
" @Inject A() {}",
"}");
JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
"package test;",
"",
"import javax.inject.Inject;",
"import javax.inject.Provider;",
"",
"final class B {",
" @Inject B(Provider<A> a) {}",
"}");
JavaFileObject aComponentFile = JavaFileObjects.forSourceLines("test.AComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component",
"interface AComponent {",
" A a();",
"}");
JavaFileObject bComponentFile = JavaFileObjects.forSourceLines("test.AComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component(dependencies = AComponent.class)",
"interface BComponent {",
" B b();",
"}");
JavaFileObject generatedComponent =
compilerMode
.javaFileBuilder("test.DaggerBComponent")
.addLines(
"package test;",
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerBComponent implements BComponent {")
.addLinesIn(DEFAULT_MODE, " private Provider<A> aProvider;")
.addLinesIn(
FAST_INIT_MODE,
" private final AComponent aComponent;",
" private volatile Provider<A> aProvider;",
"",
" private DaggerBComponent(AComponent aComponentParam) {",
" this.aComponent = aComponentParam;",
" }",
"",
" private Provider<A> aProvider() {",
" Object local = aProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(0);",
" aProvider = (Provider<A>) local;",
" }",
" return (Provider<A>) local;",
" }")
.addLinesIn(
DEFAULT_MODE,
" @SuppressWarnings(\"unchecked\")",
" private void initialize(final AComponent aComponentParam) {",
" this.aProvider = new test_AComponent_a(aComponentParam);",
" }")
.addLines("", " @Override", " public B b() {")
.addLinesIn(DEFAULT_MODE, " return new B(aProvider);")
.addLinesIn(FAST_INIT_MODE, " return new B(aProvider());")
.addLines(
" }",
"",
" static final class Builder {",
" private AComponent aComponent;",
"",
" public Builder aComponent(AComponent aComponent) {",
" this.aComponent = Preconditions.checkNotNull(aComponent);",
" return this;",
" }",
"",
" public BComponent build() {",
" Preconditions.checkBuilderRequirement(aComponent, AComponent.class);",
" return new DaggerBComponent(aComponent);",
" }",
" }")
.addLinesIn(
DEFAULT_MODE,
" private static class test_AComponent_a implements Provider<A> {",
" private final AComponent aComponent;",
" ",
" test_AComponent_a(AComponent aComponent) {",
" this.aComponent = aComponent;",
" }",
" ",
" @Override()",
" public A get() {",
" return Preconditions.checkNotNullFromComponent(aComponent.a());",
" }",
" }",
"}")
.addLinesIn(
FAST_INIT_MODE,
" private final class SwitchingProvider<T> implements Provider<T> {",
" @SuppressWarnings(\"unchecked\")",
" @Override",
" public T get() {",
" switch (id) {",
" case 0:",
" return (T)",
" Preconditions.checkNotNullFromComponent(",
" DaggerBComponent.this.aComponent.a());",
" default:",
" throw new AssertionError(id);",
" }",
" }",
" }")
.build();
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(aFile, bFile, aComponentFile, bComponentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerBComponent")
.containsElementsIn(generatedComponent);
}
@Test public void moduleNameCollision() {
JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
"package test;",
"",
"public final class A {}");
JavaFileObject otherAFile = JavaFileObjects.forSourceLines("other.test.A",
"package other.test;",
"",
"public final class A {}");
JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Module",
"public final class TestModule {",
" @Provides A a() { return null; }",
"}");
JavaFileObject otherModuleFile = JavaFileObjects.forSourceLines("other.test.TestModule",
"package other.test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Module",
"public final class TestModule {",
" @Provides A a() { return null; }",
"}");
JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
"package test;",
"",
"import dagger.Component;",
"import javax.inject.Provider;",
"",
"@Component(modules = {TestModule.class, other.test.TestModule.class})",
"interface TestComponent {",
" A a();",
" other.test.A otherA();",
"}");
JavaFileObject generatedComponent =
JavaFileObjects.forSourceLines(
"test.DaggerTestComponent",
"package test;",
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final TestModule testModule;",
" private final other.test.TestModule testModule2;",
"",
" private DaggerTestComponent(",
" TestModule testModuleParam,",
" other.test.TestModule testModule2Param) {",
" this.testModule = testModuleParam;",
" this.testModule2 = testModule2Param;",
" }",
"",
" @Override",
" public A a() {",
" return TestModule_AFactory.a(testModule);",
" }",
"",
" @Override",
" public other.test.A otherA() {",
" return other.test.TestModule_AFactory.a(testModule2);",
" }",
"",
" static final class Builder {",
" private TestModule testModule;",
" private other.test.TestModule testModule2;",
"",
" public Builder testModule(TestModule testModule) {",
" this.testModule = Preconditions.checkNotNull(testModule);",
" return this;",
" }",
"",
" public Builder testModule(other.test.TestModule testModule) {",
" this.testModule2 = Preconditions.checkNotNull(testModule);",
" return this;",
" }",
"",
" public TestComponent build() {",
" if (testModule == null) {",
" this.testModule = new TestModule();",
" }",
" if (testModule2 == null) {",
" this.testModule2 = new other.test.TestModule();",
" }",
" return new DaggerTestComponent(testModule, testModule2);",
" }",
" }",
"}");
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(aFile, otherAFile, moduleFile, otherModuleFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerTestComponent")
.containsElementsIn(generatedComponent);
}
@Test public void ignoresDependencyMethodsFromObject() {
JavaFileObject injectedTypeFile =
JavaFileObjects.forSourceLines(
"test.InjectedType",
"package test;",
"",
"import javax.inject.Inject;",
"import javax.inject.Provider;",
"",
"final class InjectedType {",
" @Inject InjectedType(",
" String stringInjection,",
" int intInjection,",
" AComponent aComponent,",
" Class<AComponent> aClass) {}",
"}");
JavaFileObject aComponentFile =
JavaFileObjects.forSourceLines(
"test.AComponent",
"package test;",
"",
"class AComponent {",
" String someStringInjection() {",
" return \"injectedString\";",
" }",
"",
" int someIntInjection() {",
" return 123;",
" }",
"",
" Class<AComponent> someClassInjection() {",
" return AComponent.class;",
" }",
"",
" @Override",
" public String toString() {",
" return null;",
" }",
"",
" @Override",
" public int hashCode() {",
" return 456;",
" }",
"",
" @Override",
" public AComponent clone() {",
" return null;",
" }",
"}");
JavaFileObject bComponentFile =
JavaFileObjects.forSourceLines(
"test.AComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component(dependencies = AComponent.class)",
"interface BComponent {",
" InjectedType injectedType();",
"}");
JavaFileObject generatedComponent =
JavaFileObjects.forSourceLines(
"test.DaggerBComponent",
"package test;",
"",
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerBComponent implements BComponent {",
" private final AComponent aComponent;",
"",
" private DaggerBComponent(AComponent aComponentParam) {",
" this.aComponent = aComponentParam;",
" }",
"",
" @Override",
" public InjectedType injectedType() {",
" return new InjectedType(",
" Preconditions.checkNotNullFromComponent(",
" aComponent.someStringInjection()),",
" aComponent.someIntInjection(),",
" aComponent,",
" Preconditions.checkNotNullFromComponent(",
" aComponent.someClassInjection()));",
" }",
"}");
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(injectedTypeFile, aComponentFile, bComponentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerBComponent")
.containsElementsIn(generatedComponent);
}
@Test public void resolutionOrder() {
JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class A {",
" @Inject A(B b) {}",
"}");
JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class B {",
" @Inject B(C c) {}",
"}");
JavaFileObject cFile = JavaFileObjects.forSourceLines("test.C",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class C {",
" @Inject C() {}",
"}");
JavaFileObject xFile = JavaFileObjects.forSourceLines("test.X",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class X {",
" @Inject X(C c) {}",
"}");
JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
"package test;",
"",
"import dagger.Component;",
"import javax.inject.Provider;",
"",
"@Component",
"interface TestComponent {",
" A a();",
" C c();",
" X x();",
"}");
JavaFileObject generatedComponent =
compilerMode
.javaFileBuilder("test.DaggerTestComponent")
.addLines(
"package test;",
"",
IMPORT_GENERATED_ANNOTATION,
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private B b() {",
" return new B(new C());",
" }",
"",
" @Override",
" public A a() {",
" return new A(b());",
" }",
"",
" @Override",
" public C c() {",
" return new C();",
" }",
"",
" @Override",
" public X x() {",
" return new X(new C());",
" }",
"}")
.build();
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(aFile, bFile, cFile, xFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerTestComponent")
.containsElementsIn(generatedComponent);
}
@Test public void simpleComponent_redundantComponentMethod() {
JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class SomeInjectableType {",
" @Inject SomeInjectableType() {}",
"}");
JavaFileObject componentSupertypeAFile = JavaFileObjects.forSourceLines("test.SupertypeA",
"package test;",
"",
"import dagger.Component;",
"import dagger.Lazy;",
"import javax.inject.Provider;",
"",
"@Component",
"interface SupertypeA {",
" SomeInjectableType someInjectableType();",
"}");
JavaFileObject componentSupertypeBFile = JavaFileObjects.forSourceLines("test.SupertypeB",
"package test;",
"",
"import dagger.Component;",
"import dagger.Lazy;",
"import javax.inject.Provider;",
"",
"@Component",
"interface SupertypeB {",
" SomeInjectableType someInjectableType();",
"}");
JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
"package test;",
"",
"import dagger.Component;",
"import dagger.Lazy;",
"import javax.inject.Provider;",
"",
"@Component",
"interface SimpleComponent extends SupertypeA, SupertypeB {",
"}");
JavaFileObject generatedComponent =
JavaFileObjects.forSourceLines(
"test.DaggerSimpleComponent",
"package test;",
"",
IMPORT_GENERATED_ANNOTATION,
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" private DaggerSimpleComponent() {}",
"",
" public static Builder builder() {",
" return new Builder();",
" }",
"",
" public static SimpleComponent create() {",
" return new Builder().build();",
" }",
"",
" @Override",
" public SomeInjectableType someInjectableType() {",
" return new SomeInjectableType();",
" }",
"",
" static final class Builder {",
" private Builder() {}",
"",
" public SimpleComponent build() {",
" return new DaggerSimpleComponent();",
" }",
" }",
"}");
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(
injectableTypeFile,
componentSupertypeAFile,
componentSupertypeBFile,
componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerSimpleComponent")
.hasSourceEquivalentTo(generatedComponent);
}
@Test public void simpleComponent_inheritedComponentMethodDep() {
JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class SomeInjectableType {",
" @Inject SomeInjectableType() {}",
"}");
JavaFileObject componentSupertype = JavaFileObjects.forSourceLines("test.Supertype",
"package test;",
"",
"import dagger.Component;",
"",
"@Component",
"interface Supertype {",
" SomeInjectableType someInjectableType();",
"}");
JavaFileObject depComponentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component",
"interface SimpleComponent extends Supertype {",
"}");
JavaFileObject generatedComponent =
JavaFileObjects.forSourceLines(
"test.DaggerSimpleComponent",
"package test;",
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" @Override",
" public SomeInjectableType someInjectableType() {",
" return new SomeInjectableType();",
" }",
"}");
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(injectableTypeFile, componentSupertype, depComponentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerSimpleComponent")
.containsElementsIn(generatedComponent);
}
@Test public void wildcardGenericsRequiresAtProvides() {
JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class A {",
" @Inject A() {}",
"}");
JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
"package test;",
"",
"import javax.inject.Inject;",
"import javax.inject.Provider;",
"",
"final class B<T> {",
" @Inject B(T t) {}",
"}");
JavaFileObject cFile = JavaFileObjects.forSourceLines("test.C",
"package test;",
"",
"import javax.inject.Inject;",
"import javax.inject.Provider;",
"",
"final class C {",
" @Inject C(B<? extends A> bA) {}",
"}");
JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
"package test;",
"",
"import dagger.Component;",
"import dagger.Lazy;",
"import javax.inject.Provider;",
"",
"@Component",
"interface SimpleComponent {",
" C c();",
"}");
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(aFile, bFile, cFile, componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
"test.B<? extends test.A> cannot be provided without an @Provides-annotated method");
}
// https://github.com/google/dagger/issues/630
@Test
public void arrayKeyRequiresAtProvides() {
JavaFileObject component =
JavaFileObjects.forSourceLines(
"test.TestComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component",
"interface TestComponent {",
" String[] array();",
"}");
Compilation compilation =
compilerWithOptions(compilerMode.javacopts()).compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining("String[] cannot be provided without an @Provides-annotated method");
}
@Test
public void componentImplicitlyDependsOnGeneratedType() {
JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class SomeInjectableType {",
" @Inject SomeInjectableType(GeneratedType generatedType) {}",
"}");
JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component",
"interface SimpleComponent {",
" SomeInjectableType someInjectableType();",
"}");
Compilation compilation =
daggerCompiler(
new GeneratingProcessor(
"test.GeneratedType",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class GeneratedType {",
" @Inject GeneratedType() {}",
"}"))
.withOptions(compilerMode.javacopts())
.compile(injectableTypeFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation).generatedSourceFile("test.DaggerSimpleComponent");
}
@Test
public void componentSupertypeDependsOnGeneratedType() {
JavaFileObject componentFile =
JavaFileObjects.forSourceLines(
"test.SimpleComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component",
"interface SimpleComponent extends SimpleComponentInterface {}");
JavaFileObject interfaceFile =
JavaFileObjects.forSourceLines(
"test.SimpleComponentInterface",
"package test;",
"",
"interface SimpleComponentInterface {",
" GeneratedType generatedType();",
"}");
Compilation compilation =
daggerCompiler(
new GeneratingProcessor(
"test.GeneratedType",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class GeneratedType {",
" @Inject GeneratedType() {}",
"}"))
.withOptions(compilerMode.javacopts())
.compile(componentFile, interfaceFile);
assertThat(compilation).succeeded();
assertThat(compilation).generatedSourceFile("test.DaggerSimpleComponent");
}
/**
* We warn when generating a {@link MembersInjector} for a type post-hoc (i.e., if Dagger wasn't
* invoked when compiling the type). But Dagger only generates {@link MembersInjector}s for types
* with {@link Inject @Inject} constructors if they have any injection sites, and it only
* generates them for types without {@link Inject @Inject} constructors if they have local
* (non-inherited) injection sites. So make sure we warn in only those cases where running the
* Dagger processor actually generates a {@link MembersInjector}.
*/
@Test
public void unprocessedMembersInjectorNotes() {
Compilation compilation =
javac()
.withOptions(
compilerMode
.javacopts()
.append(
"-Xlint:-processing",
"-Adagger.warnIfInjectionFactoryNotGeneratedUpstream=enabled"))
.withProcessors(
new ElementFilteringComponentProcessor(
Predicates.not(
element ->
MoreElements.getPackage(element)
.getQualifiedName()
.contentEquals("test.inject"))))
.compile(
JavaFileObjects.forSourceLines(
"test.TestComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component(modules = TestModule.class)",
"interface TestComponent {",
" void inject(test.inject.NoInjectMemberNoConstructor object);",
" void inject(test.inject.NoInjectMemberWithConstructor object);",
" void inject(test.inject.LocalInjectMemberNoConstructor object);",
" void inject(test.inject.LocalInjectMemberWithConstructor object);",
" void inject(test.inject.ParentInjectMemberNoConstructor object);",
" void inject(test.inject.ParentInjectMemberWithConstructor object);",
"}"),
JavaFileObjects.forSourceLines(
"test.TestModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Module",
"class TestModule {",
" @Provides static Object object() {",
" return \"object\";",
" }",
"}"),
JavaFileObjects.forSourceLines(
"test.inject.NoInjectMemberNoConstructor",
"package test.inject;",
"",
"public class NoInjectMemberNoConstructor {",
"}"),
JavaFileObjects.forSourceLines(
"test.inject.NoInjectMemberWithConstructor",
"package test.inject;",
"",
"import javax.inject.Inject;",
"",
"public class NoInjectMemberWithConstructor {",
" @Inject NoInjectMemberWithConstructor() {}",
"}"),
JavaFileObjects.forSourceLines(
"test.inject.LocalInjectMemberNoConstructor",
"package test.inject;",
"",
"import javax.inject.Inject;",
"",
"public class LocalInjectMemberNoConstructor {",
" @Inject Object object;",
"}"),
JavaFileObjects.forSourceLines(
"test.inject.LocalInjectMemberWithConstructor",
"package test.inject;",
"",
"import javax.inject.Inject;",
"",
"public class LocalInjectMemberWithConstructor {",
" @Inject LocalInjectMemberWithConstructor() {}",
" @Inject Object object;",
"}"),
JavaFileObjects.forSourceLines(
"test.inject.ParentInjectMemberNoConstructor",
"package test.inject;",
"",
"import javax.inject.Inject;",
"",
"public class ParentInjectMemberNoConstructor",
" extends LocalInjectMemberNoConstructor {}"),
JavaFileObjects.forSourceLines(
"test.inject.ParentInjectMemberWithConstructor",
"package test.inject;",
"",
"import javax.inject.Inject;",
"",
"public class ParentInjectMemberWithConstructor",
" extends LocalInjectMemberNoConstructor {",
" @Inject ParentInjectMemberWithConstructor() {}",
"}"));
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
.hadNoteContaining(
"Generating a MembersInjector for "
+ "test.inject.LocalInjectMemberNoConstructor. "
+ "Prefer to run the dagger processor over that class instead.");
assertThat(compilation)
.hadNoteContaining(
"Generating a MembersInjector for "
+ "test.inject.LocalInjectMemberWithConstructor. "
+ "Prefer to run the dagger processor over that class instead.");
assertThat(compilation)
.hadNoteContaining(
"Generating a MembersInjector for "
+ "test.inject.ParentInjectMemberWithConstructor. "
+ "Prefer to run the dagger processor over that class instead.");
assertThat(compilation).hadNoteCount(3);
}
@Test
public void scopeAnnotationOnInjectConstructorNotValid() {
JavaFileObject aScope =
JavaFileObjects.forSourceLines(
"test.AScope",
"package test;",
"",
"import javax.inject.Scope;",
"",
"@Scope",
"@interface AScope {}");
JavaFileObject aClass =
JavaFileObjects.forSourceLines(
"test.AClass",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class AClass {",
" @Inject @AScope AClass() {}",
"}");
Compilation compilation =
compilerWithOptions(compilerMode.javacopts()).compile(aScope, aClass);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining("@Scope annotations are not allowed on @Inject constructors")
.inFile(aClass)
.onLine(6);
}
@Test
public void unusedSubcomponents_dontResolveExtraBindingsInParentComponents() {
JavaFileObject foo =
JavaFileObjects.forSourceLines(
"test.Foo",
"package test;",
"",
"import javax.inject.Inject;",
"import javax.inject.Singleton;",
"",
"@Singleton",
"class Foo {",
" @Inject Foo() {}",
"}");
JavaFileObject module =
JavaFileObjects.forSourceLines(
"test.TestModule",
"package test;",
"",
"import dagger.Module;",
"",
"@Module(subcomponents = Pruned.class)",
"class TestModule {}");
JavaFileObject component =
JavaFileObjects.forSourceLines(
"test.Parent",
"package test;",
"",
"import dagger.Component;",
"import javax.inject.Singleton;",
"",
"@Singleton",
"@Component(modules = TestModule.class)",
"interface Parent {}");
JavaFileObject prunedSubcomponent =
JavaFileObjects.forSourceLines(
"test.Pruned",
"package test;",
"",
"import dagger.Subcomponent;",
"",
"@Subcomponent",
"interface Pruned {",
" @Subcomponent.Builder",
" interface Builder {",
" Pruned build();",
" }",
"",
" Foo foo();",
"}");
JavaFileObject generated =
JavaFileObjects.forSourceLines(
"test.DaggerParent",
"package test;",
"",
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerParent implements Parent {",
" private DaggerParent() {",
" }",
"",
" public static Builder builder() {",
" return new Builder();",
" }",
"",
" public static Parent create() {",
" return new Builder().build();",
" }",
"",
" static final class Builder {",
" private Builder() {}",
"",
" @Deprecated",
" public Builder testModule(TestModule testModule) {",
" Preconditions.checkNotNull(testModule);",
" return this;",
" }",
"",
" public Parent build() {",
" return new DaggerParent();",
" }",
" }",
"}");
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(foo, module, component, prunedSubcomponent);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerParent")
.hasSourceEquivalentTo(generated);
}
@Test
public void bindsToDuplicateBinding_bindsKeyIsNotDuplicated() {
JavaFileObject firstModule =
JavaFileObjects.forSourceLines(
"test.FirstModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Module",
"abstract class FirstModule {",
" @Provides static String first() { return \"first\"; }",
"}");
JavaFileObject secondModule =
JavaFileObjects.forSourceLines(
"test.SecondModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Module",
"abstract class SecondModule {",
" @Provides static String second() { return \"second\"; }",
"}");
JavaFileObject bindsModule =
JavaFileObjects.forSourceLines(
"test.BindsModule",
"package test;",
"",
"import dagger.Binds;",
"import dagger.Module;",
"",
"@Module",
"abstract class BindsModule {",
" @Binds abstract Object bindToDuplicateBinding(String duplicate);",
"}");
JavaFileObject component =
JavaFileObjects.forSourceLines(
"test.TestComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component(modules = {FirstModule.class, SecondModule.class, BindsModule.class})",
"interface TestComponent {",
" Object notDuplicated();",
"}");
Compilation compilation =
daggerCompiler().compile(firstModule, secondModule, bindsModule, component);
assertThat(compilation).failed();
assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
.hadErrorContaining("String is bound multiple times")
.inFile(component)
.onLineContaining("interface TestComponent");
}
@Test
public void nullIncorrectlyReturnedFromNonNullableInlinedProvider() {
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(
JavaFileObjects.forSourceLines(
"test.TestModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Module",
"public abstract class TestModule {",
" @Provides static String nonNullableString() { return \"string\"; }",
"}"),
JavaFileObjects.forSourceLines(
"test.InjectsMember",
"package test;",
"",
"import javax.inject.Inject;",
"",
"public class InjectsMember {",
" @Inject String member;",
"}"),
JavaFileObjects.forSourceLines(
"test.TestComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component(modules = TestModule.class)",
"interface TestComponent {",
" String nonNullableString();",
" void inject(InjectsMember member);",
"}"));
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
.generatedSourceFile("test.TestModule_NonNullableStringFactory")
.containsElementsIn(
JavaFileObjects.forSourceLines(
"test.TestModule_NonNullableStringFactory",
"package test;",
"",
GENERATED_CODE_ANNOTATIONS,
"public final class TestModule_NonNullableStringFactory",
" implements Factory<String> {",
" @Override",
" public String get() {",
" return nonNullableString();",
" }",
"",
" public static String nonNullableString() {",
" return Preconditions.checkNotNullFromProvides(",
" TestModule.nonNullableString());",
" }",
"}"));
JavaFileObject generatedComponent =
compilerMode
.javaFileBuilder("test.DaggerTestComponent")
.addLines(
"package test;",
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @Override",
" public String nonNullableString() {",
" return TestModule_NonNullableStringFactory.nonNullableString());",
" }",
"",
" @Override",
" public void inject(InjectsMember member) {",
" injectInjectsMember(member);",
" }",
"",
" @CanIgnoreReturnValue",
" private InjectsMember injectInjectsMember(InjectsMember instance) {",
" InjectsMember_MembersInjector.injectMember(instance,",
" TestModule_NonNullableStringFactory.nonNullableString());",
" return instance;",
" }",
"}")
.build();
assertThat(compilation)
.generatedSourceFile("test.DaggerTestComponent")
.containsElementsIn(generatedComponent);
}
@Test
public void nullCheckingIgnoredWhenProviderReturnsPrimitive() {
Compilation compilation =
compilerWithOptions(compilerMode.javacopts())
.compile(
JavaFileObjects.forSourceLines(
"test.TestModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Module",
"public abstract class TestModule {",
" @Provides static int primitiveInteger() { return 1; }",
"}"),
JavaFileObjects.forSourceLines(
"test.InjectsMember",
"package test;",
"",
"import javax.inject.Inject;",
"",
"public class InjectsMember {",
" @Inject Integer member;",
"}"),
JavaFileObjects.forSourceLines(
"test.TestComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component(modules = TestModule.class)",
"interface TestComponent {",
" Integer nonNullableInteger();",
" void inject(InjectsMember member);",
"}"));
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
.generatedSourceFile("test.TestModule_PrimitiveIntegerFactory")
.containsElementsIn(
JavaFileObjects.forSourceLines(
"test.TestModule_PrimitiveIntegerFactory",
"package test;",
"",
GENERATED_CODE_ANNOTATIONS,
"public final class TestModule_PrimitiveIntegerFactory",
" implements Factory<Integer> {",
"",
" @Override",
" public Integer get() {",
" return primitiveInteger();",
" }",
"",
" public static int primitiveInteger() {",
" return TestModule.primitiveInteger();",
" }",
"}"));
JavaFileObject generatedComponent =
compilerMode
.javaFileBuilder("test.DaggerTestComponent")
.addLines(
"package test;",
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @Override",
" public Integer nonNullableInteger() {",
" return TestModule.primitiveInteger();",
" }",
"",
" @Override",
" public void inject(InjectsMember member) {",
" injectInjectsMember(member);",
" }",
"",
" @CanIgnoreReturnValue",
" private InjectsMember injectInjectsMember(InjectsMember instance) {",
" InjectsMember_MembersInjector.injectMember(",
" instance, TestModule.primitiveInteger());",
" return instance;",
" }",
"}")
.build();
assertThat(compilation)
.generatedSourceFile("test.DaggerTestComponent")
.containsElementsIn(generatedComponent);
}
@Test
public void privateMethodUsedOnlyInChildDoesNotUseQualifiedThis() {
JavaFileObject parent =
JavaFileObjects.forSourceLines(
"test.Parent",
"package test;",
"",
"import dagger.Component;",
"import javax.inject.Singleton;",
"",
"@Singleton",
"@Component(modules=TestModule.class)",
"interface Parent {",
" Child child();",
"}");
JavaFileObject testModule =
JavaFileObjects.forSourceLines(
"test.TestModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"import javax.inject.Singleton;",
"",
"@Module",
"abstract class TestModule {",
" @Provides @Singleton static Number number() {",
" return 3;",
" }",
"",
" @Provides static String string(Number number) {",
" return number.toString();",
" }",
"}");
JavaFileObject child =
JavaFileObjects.forSourceLines(
"test.Child",
"package test;",
"",
"import dagger.Subcomponent;",
"",
"@Subcomponent",
"interface Child {",
" String string();",
"}");
JavaFileObject expectedPattern =
JavaFileObjects.forSourceLines(
"test.DaggerParent",
"package test;",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerParent implements Parent {",
" private String string() {",
" return TestModule_StringFactory.string(numberProvider.get());",
" }",
"}");
Compilation compilation = daggerCompiler().compile(parent, testModule, child);
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
.generatedSourceFile("test.DaggerParent")
.containsElementsIn(expectedPattern);
}
@Test
public void componentMethodInChildCallsComponentMethodInParent() {
JavaFileObject supertype =
JavaFileObjects.forSourceLines(
"test.Supertype",
"package test;",
"",
"interface Supertype {",
" String string();",
"}");
JavaFileObject parent =
JavaFileObjects.forSourceLines(
"test.Parent",
"package test;",
"",
"import dagger.Component;",
"import javax.inject.Singleton;",
"",
"@Singleton",
"@Component(modules=TestModule.class)",
"interface Parent extends Supertype {",
" Child child();",
"}");
JavaFileObject testModule =
JavaFileObjects.forSourceLines(
"test.TestModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"import javax.inject.Singleton;",
"",
"@Module",
"abstract class TestModule {",
" @Provides @Singleton static Number number() {",
" return 3;",
" }",
"",
" @Provides static String string(Number number) {",
" return number.toString();",
" }",
"}");
JavaFileObject child =
JavaFileObjects.forSourceLines(
"test.Child",
"package test;",
"",
"import dagger.Subcomponent;",
"",
"@Subcomponent",
"interface Child extends Supertype {}");
JavaFileObject expectedPattern =
JavaFileObjects.forSourceLines(
"test.DaggerParent",
"package test;",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerParent implements Parent {",
" private final class ChildImpl implements Child {",
" @Override",
" public String string() {",
" return DaggerParent.this.string();",
" }",
" }",
"}");
Compilation compilation = daggerCompiler().compile(supertype, parent, testModule, child);
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
.generatedSourceFile("test.DaggerParent")
.containsElementsIn(expectedPattern);
}
@Test
public void justInTimeAtInjectConstructor_hasGeneratedQualifier() {
JavaFileObject injected =
JavaFileObjects.forSourceLines(
"test.Injected",
"package test;",
"",
"import javax.inject.Inject;",
"",
"class Injected {",
" @Inject Injected(@GeneratedQualifier String string) {}",
"}");
JavaFileObject module =
JavaFileObjects.forSourceLines(
"test.TestModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Module",
"interface TestModule {",
" @Provides",
" static String unqualified() {",
" return new String();",
" }",
"",
" @Provides",
" @GeneratedQualifier",
" static String qualified() {",
" return new String();",
" }",
"}");
JavaFileObject component =
JavaFileObjects.forSourceLines(
"test.TestComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component(modules = TestModule.class)",
"interface TestComponent {",
" Injected injected();",
"}");
JavaFileObject generatedComponent =
JavaFileObjects.forSourceLines(
"test.DaggerTestComponent",
"package test;",
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @Override",
" public Injected injected() {",
// Ensure that the qualified @Provides method is used. It's also probably more likely
// that if the qualifier type hasn't been generated, a duplicate binding error will be
// reported, since the annotation won't be recognized as a qualifier and instead as an
// ordinary annotation.
" return new Injected(TestModule_QualifiedFactory.qualified());",
" }",
"}");
Compilation compilation =
daggerCompiler(
new GeneratingProcessor(
"test.GeneratedQualifier",
"package test;",
"",
"import static java.lang.annotation.RetentionPolicy.RUNTIME;",
"",
"import java.lang.annotation.Retention;",
"import javax.inject.Qualifier;",
"",
"@Retention(RUNTIME)",
"@Qualifier",
"@interface GeneratedQualifier {}"))
.compile(injected, module, component);
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
.generatedSourceFile("test.DaggerTestComponent")
.containsElementsIn(generatedComponent);
}
@Test
public void moduleHasGeneratedQualifier() {
JavaFileObject module =
JavaFileObjects.forSourceLines(
"test.TestModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Module",
"interface TestModule {",
" @Provides",
" static String unqualified() {",
" return new String();",
" }",
"",
" @Provides",
" @GeneratedQualifier",
" static String qualified() {",
" return new String();",
" }",
"}");
JavaFileObject component =
JavaFileObjects.forSourceLines(
"test.TestComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component(modules = TestModule.class)",
"interface TestComponent {",
" String unqualified();",
"}");
JavaFileObject generatedComponent =
JavaFileObjects.forSourceLines(
"test.DaggerTestComponent",
"package test;",
"",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @Override",
" public String unqualified() {",
// Ensure that the unqualified @Provides method is used. It's also probably more likely
// if the qualifier hasn't been generated, a duplicate binding exception will be thrown
// since the annotation won't be considered a qualifier
" return TestModule_UnqualifiedFactory.unqualified();",
" }",
"}");
Compilation compilation =
daggerCompiler(
new GeneratingProcessor(
"test.GeneratedQualifier",
"package test;",
"",
"import static java.lang.annotation.RetentionPolicy.RUNTIME;",
"",
"import java.lang.annotation.Retention;",
"import javax.inject.Qualifier;",
"",
"@Retention(RUNTIME)",
"@Qualifier",
"@interface GeneratedQualifier {}"))
.compile(module, component);
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
.generatedSourceFile("test.DaggerTestComponent")
.containsElementsIn(generatedComponent);
}
@Test
public void publicComponentType() {
JavaFileObject publicComponent =
JavaFileObjects.forSourceLines(
"test.PublicComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component",
"public interface PublicComponent {}");
Compilation compilation = daggerCompiler().compile(publicComponent);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerPublicComponent")
.hasSourceEquivalentTo(
JavaFileObjects.forSourceLines(
"test.DaggerPublicComponent",
"package test;",
"",
IMPORT_GENERATED_ANNOTATION,
"",
GENERATED_CODE_ANNOTATIONS,
"public final class DaggerPublicComponent implements PublicComponent {",
" private DaggerPublicComponent() {}",
"",
" public static Builder builder() {",
" return new Builder();",
" }",
"",
" public static PublicComponent create() {",
" return new Builder().build();",
" }",
"",
" public static final class Builder {",
" private Builder() {}",
"",
" public PublicComponent build() {",
" return new DaggerPublicComponent();",
" }",
" }",
"}"));
}
/**
* A {@link ComponentProcessor} that excludes elements using a {@link Predicate}.
*/
private static final class ElementFilteringComponentProcessor extends AbstractProcessor {
private final ComponentProcessor componentProcessor = new ComponentProcessor();
private final Predicate<? super Element> filter;
/**
* Creates a {@link ComponentProcessor} that only processes elements that match {@code filter}.
*/
public ElementFilteringComponentProcessor(Predicate<? super Element> filter) {
this.filter = filter;
}
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
componentProcessor.init(processingEnv);
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return componentProcessor.getSupportedAnnotationTypes();
}
@Override
public SourceVersion getSupportedSourceVersion() {
return componentProcessor.getSupportedSourceVersion();
}
@Override
public Set<String> getSupportedOptions() {
return componentProcessor.getSupportedOptions();
}
@Override
public boolean process(
Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
return componentProcessor.process(
annotations,
new RoundEnvironment() {
@Override
public boolean processingOver() {
return roundEnv.processingOver();
}
@Override
public Set<? extends Element> getRootElements() {
return Sets.filter(roundEnv.getRootElements(), filter);
}
@Override
public Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a) {
return Sets.filter(roundEnv.getElementsAnnotatedWith(a), filter);
}
@Override
public Set<? extends Element> getElementsAnnotatedWith(TypeElement a) {
return Sets.filter(roundEnv.getElementsAnnotatedWith(a), filter);
}
@Override
public boolean errorRaised() {
return roundEnv.errorRaised();
}
});
}
}
}