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