• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Dagger Authors.
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  */
16 
17 package dagger.internal.codegen.binding;
18 
19 import static com.google.auto.common.MoreElements.isAnnotationPresent;
20 import static com.google.common.collect.Sets.immutableEnumSet;
21 import static dagger.internal.codegen.extension.DaggerStreams.stream;
22 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
23 import static dagger.internal.codegen.extension.DaggerStreams.valuesOf;
24 import static java.util.EnumSet.allOf;
25 
26 import com.google.common.collect.ImmutableSet;
27 import dagger.Component;
28 import dagger.Module;
29 import dagger.Subcomponent;
30 import dagger.producers.ProducerModule;
31 import dagger.producers.ProductionComponent;
32 import dagger.producers.ProductionSubcomponent;
33 import java.lang.annotation.Annotation;
34 import java.util.Optional;
35 import javax.lang.model.element.TypeElement;
36 
37 /** Enumeration of the different kinds of components. */
38 public enum ComponentKind {
39   /** {@code @Component} */
40   COMPONENT(Component.class, true, false),
41 
42   /** {@code @Subcomponent} */
43   SUBCOMPONENT(Subcomponent.class, false, false),
44 
45   /** {@code @ProductionComponent} */
46   PRODUCTION_COMPONENT(ProductionComponent.class, true, true),
47 
48   /** {@code @ProductionSubcomponent} */
49   PRODUCTION_SUBCOMPONENT(ProductionSubcomponent.class, false, true),
50 
51   /**
52    * Kind for a descriptor that was generated from a {@link Module} instead of a component type in
53    * order to validate the module's bindings.
54    */
55   MODULE(Module.class, true, false),
56 
57   /**
58    * Kind for a descriptor was generated from a {@link ProducerModule} instead of a component type
59    * in order to validate the module's bindings.
60    */
61   PRODUCER_MODULE(ProducerModule.class, true, true),
62   ;
63 
64   private static final ImmutableSet<ComponentKind> ROOT_COMPONENT_KINDS =
65       valuesOf(ComponentKind.class)
66           .filter(kind -> !kind.isForModuleValidation())
67           .filter(kind -> kind.isRoot())
68           .collect(toImmutableSet());
69 
70   private static final ImmutableSet<ComponentKind> SUBCOMPONENT_KINDS =
71       valuesOf(ComponentKind.class)
72           .filter(kind -> !kind.isForModuleValidation())
73           .filter(kind -> !kind.isRoot())
74           .collect(toImmutableSet());
75 
76   /** Returns the set of kinds for root components. */
rootComponentKinds()77   public static ImmutableSet<ComponentKind> rootComponentKinds() {
78     return ROOT_COMPONENT_KINDS;
79   }
80 
81   /** Returns the set of kinds for subcomponents. */
subcomponentKinds()82   public static ImmutableSet<ComponentKind> subcomponentKinds() {
83     return SUBCOMPONENT_KINDS;
84   }
85 
86   /** Returns the annotations for components of the given kinds. */
annotationsFor( Iterable<ComponentKind> kinds)87   public static ImmutableSet<Class<? extends Annotation>> annotationsFor(
88       Iterable<ComponentKind> kinds) {
89     return stream(kinds).map(ComponentKind::annotation).collect(toImmutableSet());
90   }
91 
92   /** Returns the set of component kinds the given {@code element} has annotations for. */
getComponentKinds(TypeElement element)93   public static ImmutableSet<ComponentKind> getComponentKinds(TypeElement element) {
94     return valuesOf(ComponentKind.class)
95         .filter(kind -> isAnnotationPresent(element, kind.annotation()))
96         .collect(toImmutableSet());
97   }
98 
99   /**
100    * Returns the kind of an annotated element if it is annotated with one of the {@linkplain
101    * #annotation() annotations}.
102    *
103    * @throws IllegalArgumentException if the element is annotated with more than one of the
104    *     annotations
105    */
forAnnotatedElement(TypeElement element)106   public static Optional<ComponentKind> forAnnotatedElement(TypeElement element) {
107     ImmutableSet<ComponentKind> kinds = getComponentKinds(element);
108     if (kinds.size() > 1) {
109       throw new IllegalArgumentException(
110           element + " cannot be annotated with more than one of " + annotationsFor(kinds));
111     }
112     return kinds.stream().findAny();
113   }
114 
115   private final Class<? extends Annotation> annotation;
116   private final boolean isRoot;
117   private final boolean production;
118 
ComponentKind( Class<? extends Annotation> annotation, boolean isRoot, boolean production)119   ComponentKind(
120       Class<? extends Annotation> annotation,
121       boolean isRoot,
122       boolean production) {
123     this.annotation = annotation;
124     this.isRoot = isRoot;
125     this.production = production;
126   }
127 
128   /** Returns the annotation that marks a component of this kind. */
annotation()129   public Class<? extends Annotation> annotation() {
130     return annotation;
131   }
132 
133   /** Returns the kinds of modules that can be used with a component of this kind. */
legalModuleKinds()134   public ImmutableSet<ModuleKind> legalModuleKinds() {
135     return isProducer()
136         ? immutableEnumSet(allOf(ModuleKind.class))
137         : immutableEnumSet(ModuleKind.MODULE);
138   }
139 
140   /** Returns the kinds of subcomponents a component of this kind can have. */
legalSubcomponentKinds()141   public ImmutableSet<ComponentKind> legalSubcomponentKinds() {
142     return isProducer()
143         ? immutableEnumSet(PRODUCTION_SUBCOMPONENT)
144         : immutableEnumSet(SUBCOMPONENT, PRODUCTION_SUBCOMPONENT);
145   }
146 
147   /**
148    * Returns {@code true} if the descriptor is for a root component (not a subcomponent) or is for
149    * {@linkplain #isForModuleValidation() module-validation}.
150    */
isRoot()151   public boolean isRoot() {
152     return isRoot;
153   }
154 
155   /** Returns true if this is a production component. */
isProducer()156   public boolean isProducer() {
157     return production;
158   }
159 
160   /** Returns {@code true} if the descriptor is for a module in order to validate its bindings. */
isForModuleValidation()161   public boolean isForModuleValidation() {
162     switch (this) {
163       case MODULE:
164       case PRODUCER_MODULE:
165         return true;
166       default:
167         // fall through
168     }
169     return false;
170   }
171 }
172