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; 18 19 import static com.google.auto.common.MoreElements.asType; 20 import static com.google.auto.common.MoreTypes.asTypeElement; 21 import static com.google.common.base.Preconditions.checkArgument; 22 import static com.google.common.collect.Iterables.getOnlyElement; 23 import static dagger.internal.codegen.ComponentAnnotation.subcomponentAnnotation; 24 import static dagger.internal.codegen.ComponentCreatorAnnotation.creatorAnnotationsFor; 25 import static dagger.internal.codegen.ComponentDescriptor.isComponentContributionMethod; 26 import static dagger.internal.codegen.ConfigurationAnnotations.enclosedAnnotatedTypes; 27 import static dagger.internal.codegen.ConfigurationAnnotations.isSubcomponentCreator; 28 import static dagger.internal.codegen.DaggerStreams.toImmutableSet; 29 import static dagger.internal.codegen.InjectionAnnotations.getQualifier; 30 import static dagger.internal.codegen.Scopes.productionScope; 31 import static dagger.internal.codegen.Scopes.scopesOf; 32 import static javax.lang.model.type.TypeKind.DECLARED; 33 import static javax.lang.model.type.TypeKind.VOID; 34 import static javax.lang.model.util.ElementFilter.methodsIn; 35 36 import com.google.auto.common.MoreTypes; 37 import com.google.common.collect.ImmutableBiMap; 38 import com.google.common.collect.ImmutableMap; 39 import com.google.common.collect.ImmutableSet; 40 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor; 41 import dagger.internal.codegen.langmodel.DaggerElements; 42 import dagger.internal.codegen.langmodel.DaggerTypes; 43 import dagger.model.Scope; 44 import java.util.Optional; 45 import java.util.function.Function; 46 import javax.inject.Inject; 47 import javax.lang.model.element.ExecutableElement; 48 import javax.lang.model.element.TypeElement; 49 import javax.lang.model.type.DeclaredType; 50 import javax.lang.model.type.ExecutableType; 51 import javax.lang.model.type.TypeMirror; 52 53 /** A factory for {@link ComponentDescriptor}s. */ 54 final class ComponentDescriptorFactory { 55 private final DaggerElements elements; 56 private final DaggerTypes types; 57 private final DependencyRequestFactory dependencyRequestFactory; 58 private final ModuleDescriptor.Factory moduleDescriptorFactory; 59 60 @Inject ComponentDescriptorFactory( DaggerElements elements, DaggerTypes types, DependencyRequestFactory dependencyRequestFactory, ModuleDescriptor.Factory moduleDescriptorFactory)61 ComponentDescriptorFactory( 62 DaggerElements elements, 63 DaggerTypes types, 64 DependencyRequestFactory dependencyRequestFactory, 65 ModuleDescriptor.Factory moduleDescriptorFactory) { 66 this.elements = elements; 67 this.types = types; 68 this.dependencyRequestFactory = dependencyRequestFactory; 69 this.moduleDescriptorFactory = moduleDescriptorFactory; 70 } 71 72 /** Returns a descriptor for a root component type. */ rootComponentDescriptor(TypeElement typeElement)73 ComponentDescriptor rootComponentDescriptor(TypeElement typeElement) { 74 return create( 75 typeElement, 76 checkAnnotation( 77 typeElement, 78 ComponentAnnotation::rootComponentAnnotation, 79 "must have a component annotation")); 80 } 81 82 /** Returns a descriptor for a subcomponent type. */ subcomponentDescriptor(TypeElement typeElement)83 ComponentDescriptor subcomponentDescriptor(TypeElement typeElement) { 84 return create( 85 typeElement, 86 checkAnnotation( 87 typeElement, 88 ComponentAnnotation::subcomponentAnnotation, 89 "must have a subcomponent annotation")); 90 } 91 92 /** 93 * Returns a descriptor for a fictional component based on a module type in order to validate its 94 * bindings. 95 */ moduleComponentDescriptor(TypeElement typeElement)96 ComponentDescriptor moduleComponentDescriptor(TypeElement typeElement) { 97 return create( 98 typeElement, 99 ComponentAnnotation.fromModuleAnnotation( 100 checkAnnotation( 101 typeElement, ModuleAnnotation::moduleAnnotation, "must have a module annotation"))); 102 } 103 checkAnnotation( TypeElement typeElement, Function<TypeElement, Optional<A>> annotationFunction, String message)104 private static <A> A checkAnnotation( 105 TypeElement typeElement, 106 Function<TypeElement, Optional<A>> annotationFunction, 107 String message) { 108 return annotationFunction 109 .apply(typeElement) 110 .orElseThrow(() -> new IllegalArgumentException(typeElement + " " + message)); 111 } 112 create( TypeElement typeElement, ComponentAnnotation componentAnnotation)113 private ComponentDescriptor create( 114 TypeElement typeElement, ComponentAnnotation componentAnnotation) { 115 ImmutableSet<ComponentRequirement> componentDependencies = 116 componentAnnotation.dependencyTypes().stream() 117 .map(ComponentRequirement::forDependency) 118 .collect(toImmutableSet()); 119 120 ImmutableMap.Builder<ExecutableElement, ComponentRequirement> dependenciesByDependencyMethod = 121 ImmutableMap.builder(); 122 123 for (ComponentRequirement componentDependency : componentDependencies) { 124 for (ExecutableElement dependencyMethod : 125 methodsIn(elements.getAllMembers(componentDependency.typeElement()))) { 126 if (isComponentContributionMethod(elements, dependencyMethod)) { 127 dependenciesByDependencyMethod.put(dependencyMethod, componentDependency); 128 } 129 } 130 } 131 132 // Start with the component's modules. For fictional components built from a module, start with 133 // that module. 134 ImmutableSet<TypeElement> modules = 135 componentAnnotation.isRealComponent() 136 ? componentAnnotation.modules() 137 : ImmutableSet.of(typeElement); 138 139 ImmutableSet<ModuleDescriptor> transitiveModules = 140 moduleDescriptorFactory.transitiveModules(modules); 141 142 ImmutableSet.Builder<ComponentDescriptor> subcomponentsFromModules = ImmutableSet.builder(); 143 for (ModuleDescriptor module : transitiveModules) { 144 for (SubcomponentDeclaration subcomponentDeclaration : module.subcomponentDeclarations()) { 145 TypeElement subcomponent = subcomponentDeclaration.subcomponentType(); 146 subcomponentsFromModules.add(subcomponentDescriptor(subcomponent)); 147 } 148 } 149 150 ImmutableSet.Builder<ComponentMethodDescriptor> componentMethodsBuilder = 151 ImmutableSet.builder(); 152 ImmutableBiMap.Builder<ComponentMethodDescriptor, ComponentDescriptor> 153 subcomponentsByFactoryMethod = ImmutableBiMap.builder(); 154 ImmutableBiMap.Builder<ComponentMethodDescriptor, ComponentDescriptor> 155 subcomponentsByBuilderMethod = ImmutableBiMap.builder(); 156 if (componentAnnotation.isRealComponent()) { 157 ImmutableSet<ExecutableElement> unimplementedMethods = 158 elements.getUnimplementedMethods(typeElement); 159 for (ExecutableElement componentMethod : unimplementedMethods) { 160 ComponentMethodDescriptor componentMethodDescriptor = 161 getDescriptorForComponentMethod(typeElement, componentAnnotation, componentMethod); 162 componentMethodsBuilder.add(componentMethodDescriptor); 163 componentMethodDescriptor 164 .subcomponent() 165 .ifPresent( 166 subcomponent -> { 167 // If the dependency request is present, that means the method returns the 168 // subcomponent factory. 169 if (componentMethodDescriptor.dependencyRequest().isPresent()) { 170 subcomponentsByBuilderMethod.put(componentMethodDescriptor, subcomponent); 171 } else { 172 subcomponentsByFactoryMethod.put(componentMethodDescriptor, subcomponent); 173 } 174 }); 175 } 176 } 177 178 // Validation should have ensured that this set will have at most one element. 179 ImmutableSet<DeclaredType> enclosedCreators = 180 creatorAnnotationsFor(componentAnnotation).stream() 181 .flatMap( 182 creatorAnnotation -> 183 enclosedAnnotatedTypes(typeElement, creatorAnnotation).stream()) 184 .collect(toImmutableSet()); 185 Optional<ComponentCreatorDescriptor> creatorDescriptor = 186 enclosedCreators.isEmpty() 187 ? Optional.empty() 188 : Optional.of( 189 ComponentCreatorDescriptor.create( 190 getOnlyElement(enclosedCreators), elements, types, dependencyRequestFactory)); 191 192 ImmutableSet<Scope> scopes = scopesOf(typeElement); 193 if (componentAnnotation.isProduction()) { 194 scopes = ImmutableSet.<Scope>builder().addAll(scopes).add(productionScope(elements)).build(); 195 } 196 197 return new AutoValue_ComponentDescriptor( 198 componentAnnotation, 199 typeElement, 200 componentDependencies, 201 transitiveModules, 202 dependenciesByDependencyMethod.build(), 203 scopes, 204 subcomponentsFromModules.build(), 205 subcomponentsByFactoryMethod.build(), 206 subcomponentsByBuilderMethod.build(), 207 componentMethodsBuilder.build(), 208 creatorDescriptor); 209 } 210 getDescriptorForComponentMethod( TypeElement componentElement, ComponentAnnotation componentAnnotation, ExecutableElement componentMethod)211 private ComponentMethodDescriptor getDescriptorForComponentMethod( 212 TypeElement componentElement, 213 ComponentAnnotation componentAnnotation, 214 ExecutableElement componentMethod) { 215 ComponentMethodDescriptor.Builder descriptor = 216 ComponentMethodDescriptor.builder(componentMethod); 217 218 ExecutableType resolvedComponentMethod = 219 MoreTypes.asExecutable( 220 types.asMemberOf(MoreTypes.asDeclared(componentElement.asType()), componentMethod)); 221 TypeMirror returnType = resolvedComponentMethod.getReturnType(); 222 if (returnType.getKind().equals(DECLARED) && !getQualifier(componentMethod).isPresent()) { 223 TypeElement returnTypeElement = asTypeElement(returnType); 224 if (subcomponentAnnotation(returnTypeElement).isPresent()) { 225 // It's a subcomponent factory method. There is no dependency request, and there could be 226 // any number of parameters. Just return the descriptor. 227 return descriptor.subcomponent(subcomponentDescriptor(returnTypeElement)).build(); 228 } 229 if (isSubcomponentCreator(returnTypeElement)) { 230 descriptor.subcomponent( 231 subcomponentDescriptor(asType(returnTypeElement.getEnclosingElement()))); 232 } 233 } 234 235 switch (componentMethod.getParameters().size()) { 236 case 0: 237 checkArgument( 238 !returnType.getKind().equals(VOID), 239 "component method cannot be void: %s", 240 componentMethod); 241 descriptor.dependencyRequest( 242 componentAnnotation.isProduction() 243 ? dependencyRequestFactory.forComponentProductionMethod( 244 componentMethod, resolvedComponentMethod) 245 : dependencyRequestFactory.forComponentProvisionMethod( 246 componentMethod, resolvedComponentMethod)); 247 break; 248 249 case 1: 250 checkArgument( 251 returnType.getKind().equals(VOID) 252 || MoreTypes.equivalence() 253 .equivalent(returnType, resolvedComponentMethod.getParameterTypes().get(0)), 254 "members injection method must return void or parameter type: %s", 255 componentMethod); 256 descriptor.dependencyRequest( 257 dependencyRequestFactory.forComponentMembersInjectionMethod( 258 componentMethod, resolvedComponentMethod)); 259 break; 260 261 default: 262 throw new IllegalArgumentException( 263 "component method has too many parameters: " + componentMethod); 264 } 265 266 return descriptor.build(); 267 } 268 } 269