1package dagger.internal.codegen;
2
3import com.google.auto.common.MoreTypes;
4import com.google.auto.value.AutoValue;
5import com.google.common.base.Function;
6import com.google.common.base.Optional;
7import com.google.common.collect.ImmutableSet;
8import dagger.Module;
9import dagger.Provides;
10import dagger.producers.ProducerModule;
11import dagger.producers.Produces;
12import java.util.LinkedHashSet;
13import java.util.Set;
14import javax.lang.model.element.AnnotationMirror;
15import javax.lang.model.element.ExecutableElement;
16import javax.lang.model.element.TypeElement;
17import javax.lang.model.type.TypeMirror;
18import javax.lang.model.util.Elements;
19
20import static com.google.auto.common.MoreElements.getAnnotationMirror;
21import static com.google.auto.common.MoreElements.isAnnotationPresent;
22import static com.google.common.base.Verify.verify;
23import static dagger.internal.codegen.ConfigurationAnnotations.getModuleIncludes;
24import static dagger.internal.codegen.Util.componentCanMakeNewInstances;
25import static javax.lang.model.type.TypeKind.DECLARED;
26import static javax.lang.model.type.TypeKind.NONE;
27import static javax.lang.model.util.ElementFilter.methodsIn;
28
29@AutoValue
30abstract class ModuleDescriptor {
31  static final Function<ModuleDescriptor, TypeElement> getModuleElement() {
32    return new Function<ModuleDescriptor, TypeElement>() {
33      @Override public TypeElement apply(ModuleDescriptor input) {
34        return input.moduleElement();
35      }
36    };
37  }
38
39  abstract AnnotationMirror moduleAnnotation();
40
41  abstract TypeElement moduleElement();
42
43  abstract ImmutableSet<ModuleDescriptor> includedModules();
44
45  abstract ImmutableSet<ContributionBinding> bindings();
46
47  enum DefaultCreationStrategy {
48    PASSED,
49    CONSTRUCTED,
50  }
51
52  abstract DefaultCreationStrategy defaultCreationStrategy();
53
54  static final class Factory {
55    private final Elements elements;
56    private final ProvisionBinding.Factory provisionBindingFactory;
57    private final ProductionBinding.Factory productionBindingFactory;
58
59    Factory(
60        Elements elements,
61        ProvisionBinding.Factory provisionBindingFactory,
62        ProductionBinding.Factory productionBindingFactory) {
63      this.elements = elements;
64      this.provisionBindingFactory = provisionBindingFactory;
65      this.productionBindingFactory = productionBindingFactory;
66    }
67
68    ModuleDescriptor create(TypeElement moduleElement) {
69      AnnotationMirror moduleAnnotation = getModuleAnnotation(moduleElement).get();
70
71      ImmutableSet.Builder<ContributionBinding> bindings = ImmutableSet.builder();
72      for (ExecutableElement moduleMethod : methodsIn(elements.getAllMembers(moduleElement))) {
73        if (isAnnotationPresent(moduleMethod, Provides.class)) {
74          bindings.add(
75              provisionBindingFactory.forProvidesMethod(moduleMethod, moduleElement.asType()));
76        }
77        if (isAnnotationPresent(moduleMethod, Produces.class)) {
78          bindings.add(
79              productionBindingFactory.forProducesMethod(moduleMethod, moduleElement.asType()));
80        }
81      }
82
83      DefaultCreationStrategy defaultCreationStrategy =
84          (componentCanMakeNewInstances(moduleElement)
85              && moduleElement.getTypeParameters().isEmpty())
86                  ? ModuleDescriptor.DefaultCreationStrategy.CONSTRUCTED
87                  : ModuleDescriptor.DefaultCreationStrategy.PASSED;
88
89      return new AutoValue_ModuleDescriptor(
90          moduleAnnotation,
91          moduleElement,
92          ImmutableSet.copyOf(
93              collectIncludedModules(new LinkedHashSet<ModuleDescriptor>(), moduleElement)),
94          bindings.build(),
95          defaultCreationStrategy);
96    }
97
98    private static Optional<AnnotationMirror> getModuleAnnotation(TypeElement moduleElement) {
99      return getAnnotationMirror(moduleElement, Module.class)
100          .or(getAnnotationMirror(moduleElement, ProducerModule.class));
101    }
102
103    private Set<ModuleDescriptor> collectIncludedModules(
104        Set<ModuleDescriptor> includedModules, TypeElement moduleElement) {
105      TypeMirror superclass = moduleElement.getSuperclass();
106      if (!superclass.getKind().equals(NONE)) {
107        verify(superclass.getKind().equals(DECLARED));
108        TypeElement superclassElement = MoreTypes.asTypeElement(superclass);
109        if (!superclassElement.getQualifiedName().contentEquals(Object.class.getCanonicalName())) {
110          collectIncludedModules(includedModules, superclassElement);
111        }
112      }
113      Optional<AnnotationMirror> moduleAnnotation = getModuleAnnotation(moduleElement);
114      if (moduleAnnotation.isPresent()) {
115        for (TypeMirror moduleIncludesType : getModuleIncludes(moduleAnnotation.get())) {
116          includedModules.add(create(MoreTypes.asTypeElement(moduleIncludesType)));
117        }
118      }
119      return includedModules;
120    }
121  }
122}
123