1/*
2 * Copyright (C) 2014 Google, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package dagger.internal.codegen;
17
18import com.google.auto.common.MoreElements;
19import com.google.auto.common.MoreTypes;
20import com.google.auto.value.AutoValue;
21import com.google.common.base.Optional;
22import com.google.common.base.Predicate;
23import com.google.common.collect.FluentIterable;
24import com.google.common.collect.ImmutableList;
25import com.google.common.collect.ImmutableMap;
26import com.google.common.collect.ImmutableSet;
27import com.google.common.util.concurrent.ListenableFuture;
28import dagger.Component;
29import dagger.Lazy;
30import dagger.MembersInjector;
31import dagger.Module;
32import dagger.Subcomponent;
33import dagger.producers.ProductionComponent;
34import java.lang.annotation.Annotation;
35import java.util.EnumSet;
36import java.util.LinkedHashSet;
37import java.util.List;
38import java.util.Map;
39import java.util.Set;
40import java.util.concurrent.Executor;
41import javax.inject.Provider;
42import javax.lang.model.element.AnnotationMirror;
43import javax.lang.model.element.ExecutableElement;
44import javax.lang.model.element.TypeElement;
45import javax.lang.model.type.DeclaredType;
46import javax.lang.model.type.ExecutableType;
47import javax.lang.model.type.TypeMirror;
48import javax.lang.model.util.ElementFilter;
49import javax.lang.model.util.Elements;
50import javax.lang.model.util.Types;
51
52import static com.google.auto.common.MoreElements.getAnnotationMirror;
53import static com.google.auto.common.MoreElements.isAnnotationPresent;
54import static com.google.common.base.Preconditions.checkArgument;
55import static com.google.common.base.Verify.verify;
56import static com.google.common.collect.Iterables.getOnlyElement;
57import static dagger.internal.codegen.ConfigurationAnnotations.enclosedBuilders;
58import static dagger.internal.codegen.ConfigurationAnnotations.getComponentDependencies;
59import static dagger.internal.codegen.ConfigurationAnnotations.getComponentModules;
60import static dagger.internal.codegen.ConfigurationAnnotations.isComponent;
61import static javax.lang.model.type.TypeKind.DECLARED;
62import static javax.lang.model.type.TypeKind.VOID;
63
64/**
65 * The logical representation of a {@link Component} or {@link ProductionComponent} definition.
66 *
67 * @author Gregory Kick
68 * @since 2.0
69 */
70@AutoValue
71abstract class ComponentDescriptor {
72  ComponentDescriptor() {}
73
74  enum Kind {
75    COMPONENT(Component.class, Component.Builder.class, true),
76    SUBCOMPONENT(Subcomponent.class, Subcomponent.Builder.class, false),
77    PRODUCTION_COMPONENT(ProductionComponent.class, ProductionComponent.Builder.class, true);
78
79    private final Class<? extends Annotation> annotationType;
80    private final Class<? extends Annotation> builderType;
81    private final boolean isTopLevel;
82
83    /**
84     * Returns the kind of an annotated element if it is annotated with one of the
85     * {@linkplain #annotationType() annotation types}.
86     *
87     * @throws IllegalArgumentException if the element is annotated with more than one of the
88     *     annotation types
89     */
90    static Optional<Kind> forAnnotatedElement(TypeElement element) {
91      Set<Kind> kinds = EnumSet.noneOf(Kind.class);
92      for (Kind kind : values()) {
93        if (MoreElements.isAnnotationPresent(element, kind.annotationType())) {
94          kinds.add(kind);
95        }
96      }
97      checkArgument(
98          kinds.size() <= 1, "%s cannot be annotated with more than one of %s", element, kinds);
99      return Optional.fromNullable(getOnlyElement(kinds, null));
100    }
101
102    Kind(
103        Class<? extends Annotation> annotationType,
104        Class<? extends Annotation> builderType,
105        boolean isTopLevel) {
106      this.annotationType = annotationType;
107      this.builderType = builderType;
108      this.isTopLevel = isTopLevel;
109    }
110
111    Class<? extends Annotation> annotationType() {
112      return annotationType;
113    }
114
115    Class<? extends Annotation> builderAnnotationType() {
116      return builderType;
117    }
118
119    boolean isTopLevel() {
120      return isTopLevel;
121    }
122  }
123
124  abstract Kind kind();
125
126  abstract AnnotationMirror componentAnnotation();
127
128  /**
129   * The type (interface or abstract class) that defines the component. This is the element to which
130   * the {@link Component} annotation was applied.
131   */
132  abstract TypeElement componentDefinitionType();
133
134  /**
135   * The set of component dependencies listed in {@link Component#dependencies}.
136   */
137  abstract ImmutableSet<TypeElement> dependencies();
138
139  /**
140   * The set of {@link ModuleDescriptor modules} declared directly in {@link Component#modules}.
141   * Use {@link #transitiveModules} to get the full set of modules available upon traversing
142   * {@link Module#includes}.
143   */
144  abstract ImmutableSet<ModuleDescriptor> modules();
145
146  /**
147   * Returns the set of {@link ModuleDescriptor modules} declared in {@link Component#modules} and
148   * those reachable by traversing {@link Module#includes}.
149   *
150   * <p>Note that for subcomponents this <em>will not</em> include descriptors for any modules that
151   * are declared in parent components.
152   */
153  ImmutableSet<ModuleDescriptor> transitiveModules() {
154    Set<ModuleDescriptor> transitiveModules = new LinkedHashSet<>();
155    for (ModuleDescriptor module : modules()) {
156      addTransitiveModules(transitiveModules, module);
157    }
158    return ImmutableSet.copyOf(transitiveModules);
159  }
160
161  ImmutableSet<TypeElement> transitiveModuleTypes() {
162    return FluentIterable.from(transitiveModules())
163        .transform(ModuleDescriptor.getModuleElement())
164        .toSet();
165  }
166
167  private static Set<ModuleDescriptor> addTransitiveModules(
168      Set<ModuleDescriptor> transitiveModules, ModuleDescriptor module) {
169    if (transitiveModules.add(module)) {
170      for (ModuleDescriptor includedModule : module.includedModules()) {
171        addTransitiveModules(transitiveModules, includedModule);
172      }
173    }
174    return transitiveModules;
175  }
176
177  /**
178   * An index of the type to which this component holds a reference (the type listed in
179   * {@link Component#dependencies} or {@link ProductionComponent#dependencies} as opposed to the
180   * enclosing type) for each method from a component dependency that can be used for binding.
181   */
182  abstract ImmutableMap<ExecutableElement, TypeElement> dependencyMethodIndex();
183
184  /**
185   * The element representing {@link Executor}, if it should be a dependency of this component.
186   */
187  abstract Optional<TypeElement> executorDependency();
188
189  /**
190   * The scope of the component.
191   */
192  abstract Scope scope();
193
194  abstract ImmutableMap<ComponentMethodDescriptor, ComponentDescriptor> subcomponents();
195
196  abstract ImmutableSet<ComponentMethodDescriptor> componentMethods();
197
198  // TODO(gak): Consider making this non-optional and revising the
199  // interaction between the spec & generation
200  abstract Optional<BuilderSpec> builderSpec();
201
202  @AutoValue
203  static abstract class ComponentMethodDescriptor {
204    abstract ComponentMethodKind kind();
205    abstract Optional<DependencyRequest> dependencyRequest();
206    abstract ExecutableElement methodElement();
207
208    /**
209     * A predicate that passes for {@link ComponentMethodDescriptor}s of a given kind.
210     */
211    static Predicate<ComponentMethodDescriptor> isOfKind(final ComponentMethodKind kind) {
212      return new Predicate<ComponentMethodDescriptor>() {
213        @Override
214        public boolean apply(ComponentMethodDescriptor descriptor) {
215          return kind.equals(descriptor.kind());
216        }
217      };
218    }
219  }
220
221  enum ComponentMethodKind {
222    PROVISON,
223    PRODUCTION,
224    MEMBERS_INJECTION,
225    SUBCOMPONENT,
226    SUBCOMPONENT_BUILDER,
227  }
228
229  @AutoValue
230  static abstract class BuilderSpec {
231    abstract TypeElement builderDefinitionType();
232    abstract Map<TypeElement, ExecutableElement> methodMap();
233    abstract ExecutableElement buildMethod();
234    abstract TypeMirror componentType();
235  }
236
237  static final class Factory {
238    private final Elements elements;
239    private final Types types;
240    private final DependencyRequest.Factory dependencyRequestFactory;
241    private final ModuleDescriptor.Factory moduleDescriptorFactory;
242
243    Factory(
244        Elements elements,
245        Types types,
246        DependencyRequest.Factory dependencyRequestFactory,
247        ModuleDescriptor.Factory moduleDescriptorFactory) {
248      this.elements = elements;
249      this.types = types;
250      this.dependencyRequestFactory = dependencyRequestFactory;
251      this.moduleDescriptorFactory = moduleDescriptorFactory;
252    }
253
254    /**
255     * Returns a component descriptor for a type annotated with either {@link Component @Component}
256     * or {@link ProductionComponent @ProductionComponent}.
257     */
258    ComponentDescriptor forComponent(TypeElement componentDefinitionType) {
259      Optional<Kind> kind = Kind.forAnnotatedElement(componentDefinitionType);
260      checkArgument(
261          kind.isPresent() && kind.get().isTopLevel(),
262          "%s must be annotated with @Component or @ProductionComponent",
263          componentDefinitionType);
264      return create(componentDefinitionType, kind.get());
265    }
266
267    private ComponentDescriptor create(TypeElement componentDefinitionType, Kind kind) {
268      DeclaredType declaredComponentType = MoreTypes.asDeclared(componentDefinitionType.asType());
269      AnnotationMirror componentMirror =
270          getAnnotationMirror(componentDefinitionType, kind.annotationType())
271              .or(getAnnotationMirror(componentDefinitionType, Subcomponent.class))
272              .get();
273      ImmutableSet<TypeElement> componentDependencyTypes =
274          isComponent(componentDefinitionType)
275              ? MoreTypes.asTypeElements(getComponentDependencies(componentMirror))
276              : ImmutableSet.<TypeElement>of();
277
278      ImmutableMap.Builder<ExecutableElement, TypeElement> dependencyMethodIndex =
279          ImmutableMap.builder();
280
281      for (TypeElement componentDependency : componentDependencyTypes) {
282        List<ExecutableElement> dependencyMethods =
283            ElementFilter.methodsIn(elements.getAllMembers(componentDependency));
284        for (ExecutableElement dependencyMethod : dependencyMethods) {
285          if (isComponentContributionMethod(elements, dependencyMethod)) {
286            dependencyMethodIndex.put(dependencyMethod, componentDependency);
287          }
288        }
289      }
290
291      Optional<TypeElement> executorDependency =
292          kind.equals(Kind.PRODUCTION_COMPONENT)
293              ? Optional.of(elements.getTypeElement(Executor.class.getCanonicalName()))
294              : Optional.<TypeElement>absent();
295
296      ImmutableSet.Builder<ModuleDescriptor> modules = ImmutableSet.builder();
297      for (TypeMirror moduleIncludesType : getComponentModules(componentMirror)) {
298        modules.add(moduleDescriptorFactory.create(MoreTypes.asTypeElement(moduleIncludesType)));
299      }
300      if (kind.equals(Kind.PRODUCTION_COMPONENT)) {
301        modules.add(descriptorForMonitoringModule(componentDefinitionType));
302      }
303
304      ImmutableSet<ExecutableElement> unimplementedMethods =
305          Util.getUnimplementedMethods(elements, componentDefinitionType);
306
307      ImmutableSet.Builder<ComponentMethodDescriptor> componentMethodsBuilder =
308          ImmutableSet.builder();
309
310      ImmutableMap.Builder<ComponentMethodDescriptor, ComponentDescriptor> subcomponentDescriptors =
311          ImmutableMap.builder();
312      for (ExecutableElement componentMethod : unimplementedMethods) {
313        ExecutableType resolvedMethod =
314            MoreTypes.asExecutable(types.asMemberOf(declaredComponentType, componentMethod));
315        ComponentMethodDescriptor componentMethodDescriptor =
316            getDescriptorForComponentMethod(componentDefinitionType, kind, componentMethod);
317        componentMethodsBuilder.add(componentMethodDescriptor);
318        switch (componentMethodDescriptor.kind()) {
319          case SUBCOMPONENT:
320            subcomponentDescriptors.put(
321                componentMethodDescriptor,
322                create(
323                    MoreElements.asType(MoreTypes.asElement(resolvedMethod.getReturnType())),
324                    Kind.SUBCOMPONENT));
325            break;
326          case SUBCOMPONENT_BUILDER:
327            subcomponentDescriptors.put(
328                componentMethodDescriptor,
329                create(
330                    MoreElements.asType(
331                        MoreTypes.asElement(resolvedMethod.getReturnType()).getEnclosingElement()),
332                    Kind.SUBCOMPONENT));
333            break;
334          default: // nothing special to do for other methods.
335        }
336
337      }
338
339      ImmutableList<DeclaredType> enclosedBuilders = kind.builderAnnotationType() == null
340          ? ImmutableList.<DeclaredType>of()
341          : enclosedBuilders(componentDefinitionType, kind.builderAnnotationType());
342      Optional<DeclaredType> builderType =
343          Optional.fromNullable(getOnlyElement(enclosedBuilders, null));
344
345      Scope scope = Scope.scopeOf(componentDefinitionType);
346      return new AutoValue_ComponentDescriptor(
347          kind,
348          componentMirror,
349          componentDefinitionType,
350          componentDependencyTypes,
351          modules.build(),
352          dependencyMethodIndex.build(),
353          executorDependency,
354          scope,
355          subcomponentDescriptors.build(),
356          componentMethodsBuilder.build(),
357          createBuilderSpec(builderType));
358    }
359
360    private ComponentMethodDescriptor getDescriptorForComponentMethod(TypeElement componentElement,
361        Kind componentKind,
362        ExecutableElement componentMethod) {
363      ExecutableType resolvedComponentMethod = MoreTypes.asExecutable(types.asMemberOf(
364          MoreTypes.asDeclared(componentElement.asType()), componentMethod));
365      TypeMirror returnType = resolvedComponentMethod.getReturnType();
366      if (returnType.getKind().equals(DECLARED)) {
367        if (MoreTypes.isTypeOf(Provider.class, returnType)
368            || MoreTypes.isTypeOf(Lazy.class, returnType)) {
369          return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor(
370              ComponentMethodKind.PROVISON,
371              Optional.of(dependencyRequestFactory.forComponentProvisionMethod(componentMethod,
372                  resolvedComponentMethod)),
373              componentMethod);
374        } else if (MoreTypes.isTypeOf(MembersInjector.class, returnType)) {
375          return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor(
376              ComponentMethodKind.MEMBERS_INJECTION,
377              Optional.of(dependencyRequestFactory.forComponentMembersInjectionMethod(
378                  componentMethod,
379                  resolvedComponentMethod)),
380              componentMethod);
381        } else if (isAnnotationPresent(MoreTypes.asElement(returnType), Subcomponent.class)) {
382          return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor(
383              ComponentMethodKind.SUBCOMPONENT,
384              Optional.<DependencyRequest>absent(),
385              componentMethod);
386        } else if (isAnnotationPresent(MoreTypes.asElement(returnType),
387            Subcomponent.Builder.class)) {
388          return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor(
389              ComponentMethodKind.SUBCOMPONENT_BUILDER,
390              Optional.<DependencyRequest>absent(),
391              componentMethod);
392        }
393      }
394
395      // a typical provision method
396      if (componentMethod.getParameters().isEmpty()
397          && !componentMethod.getReturnType().getKind().equals(VOID)) {
398        switch (componentKind) {
399          case COMPONENT:
400          case SUBCOMPONENT:
401            return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor(
402                ComponentMethodKind.PROVISON,
403                Optional.of(dependencyRequestFactory.forComponentProvisionMethod(componentMethod,
404                    resolvedComponentMethod)),
405                componentMethod);
406          case PRODUCTION_COMPONENT:
407            return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor(
408                ComponentMethodKind.PRODUCTION,
409                Optional.of(dependencyRequestFactory.forComponentProductionMethod(componentMethod,
410                    resolvedComponentMethod)),
411                componentMethod);
412          default:
413            throw new AssertionError();
414        }
415      }
416
417      List<? extends TypeMirror> parameterTypes = resolvedComponentMethod.getParameterTypes();
418      if (parameterTypes.size() == 1
419          && (returnType.getKind().equals(VOID)
420              || MoreTypes.equivalence().equivalent(returnType, parameterTypes.get(0)))) {
421        return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor(
422            ComponentMethodKind.MEMBERS_INJECTION,
423            Optional.of(dependencyRequestFactory.forComponentMembersInjectionMethod(
424                componentMethod,
425                resolvedComponentMethod)),
426            componentMethod);
427      }
428
429      throw new IllegalArgumentException("not a valid component method: " + componentMethod);
430    }
431
432    private Optional<BuilderSpec> createBuilderSpec(Optional<DeclaredType> builderType) {
433      if (!builderType.isPresent()) {
434        return Optional.absent();
435      }
436      TypeElement element = MoreTypes.asTypeElement(builderType.get());
437      ImmutableSet<ExecutableElement> methods = Util.getUnimplementedMethods(elements, element);
438      ImmutableMap.Builder<TypeElement, ExecutableElement> map = ImmutableMap.builder();
439      ExecutableElement buildMethod = null;
440      for (ExecutableElement method : methods) {
441        if (method.getParameters().isEmpty()) {
442          buildMethod = method;
443        } else {
444          ExecutableType resolved =
445              MoreTypes.asExecutable(types.asMemberOf(builderType.get(), method));
446          map.put(MoreTypes.asTypeElement(getOnlyElement(resolved.getParameterTypes())), method);
447        }
448      }
449      verify(buildMethod != null); // validation should have ensured this.
450      return Optional.<BuilderSpec>of(new AutoValue_ComponentDescriptor_BuilderSpec(element,
451          map.build(), buildMethod, element.getEnclosingElement().asType()));
452    }
453
454    /**
455     * Returns a descriptor for a generated module that handles monitoring for production
456     * components. This module is generated in the {@link MonitoringModuleProcessingStep}.
457     *
458     * @throws TypeNotPresentException if the module has not been generated yet. This will cause the
459     *     processor to retry in a later processing round.
460     */
461    private ModuleDescriptor descriptorForMonitoringModule(TypeElement componentDefinitionType) {
462      String generatedMonitorModuleName =
463          SourceFiles.generatedMonitoringModuleName(componentDefinitionType).canonicalName();
464      TypeElement monitoringModule = elements.getTypeElement(generatedMonitorModuleName);
465      if (monitoringModule == null) {
466        throw new TypeNotPresentException(generatedMonitorModuleName, null);
467      }
468      return moduleDescriptorFactory.create(monitoringModule);
469    }
470  }
471
472  static boolean isComponentContributionMethod(Elements elements, ExecutableElement method) {
473    return method.getParameters().isEmpty()
474        && !method.getReturnType().getKind().equals(VOID)
475        && !elements.getTypeElement(Object.class.getCanonicalName())
476            .equals(method.getEnclosingElement());
477  }
478
479  static boolean isComponentProductionMethod(Elements elements, ExecutableElement method) {
480    return isComponentContributionMethod(elements, method)
481        && MoreTypes.isTypeOf(ListenableFuture.class, method.getReturnType());
482  }
483}
484