1 /* 2 * Copyright (C) 2015 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.getPackage; 20 import static com.google.auto.common.MoreElements.isAnnotationPresent; 21 import static com.google.common.base.CaseFormat.LOWER_CAMEL; 22 import static com.google.common.base.CaseFormat.UPPER_CAMEL; 23 import static com.google.common.base.Verify.verify; 24 import static com.google.common.collect.Iterables.transform; 25 import static dagger.internal.codegen.DaggerStreams.toImmutableSet; 26 import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation; 27 import static dagger.internal.codegen.SourceFiles.classFileName; 28 import static dagger.internal.codegen.Util.reentrantComputeIfAbsent; 29 import static dagger.internal.codegen.langmodel.DaggerElements.isAnnotationPresent; 30 import static javax.lang.model.type.TypeKind.DECLARED; 31 import static javax.lang.model.type.TypeKind.NONE; 32 import static javax.lang.model.util.ElementFilter.methodsIn; 33 34 import com.google.auto.common.MoreElements; 35 import com.google.auto.common.MoreTypes; 36 import com.google.auto.value.AutoValue; 37 import com.google.auto.value.extension.memoized.Memoized; 38 import com.google.common.collect.ImmutableSet; 39 import com.google.common.graph.Traverser; 40 import com.google.errorprone.annotations.CanIgnoreReturnValue; 41 import com.squareup.javapoet.ClassName; 42 import dagger.Binds; 43 import dagger.BindsOptionalOf; 44 import dagger.Module; 45 import dagger.Provides; 46 import dagger.internal.codegen.langmodel.DaggerElements; 47 import dagger.model.Key; 48 import dagger.multibindings.Multibinds; 49 import dagger.producers.Produces; 50 import java.util.HashMap; 51 import java.util.LinkedHashSet; 52 import java.util.Map; 53 import java.util.Set; 54 import javax.inject.Inject; 55 import javax.inject.Singleton; 56 import javax.lang.model.element.ExecutableElement; 57 import javax.lang.model.element.TypeElement; 58 import javax.lang.model.type.TypeMirror; 59 60 @AutoValue 61 abstract class ModuleDescriptor { 62 moduleElement()63 abstract TypeElement moduleElement(); 64 includedModules()65 abstract ImmutableSet<TypeElement> includedModules(); 66 bindings()67 abstract ImmutableSet<ContributionBinding> bindings(); 68 69 /** The multibinding declarations contained in this module. */ multibindingDeclarations()70 abstract ImmutableSet<MultibindingDeclaration> multibindingDeclarations(); 71 72 /** The {@link Module#subcomponents() subcomponent declarations} contained in this module. */ subcomponentDeclarations()73 abstract ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations(); 74 75 /** The {@link Binds} method declarations that define delegate bindings. */ delegateDeclarations()76 abstract ImmutableSet<DelegateDeclaration> delegateDeclarations(); 77 78 /** The {@link BindsOptionalOf} method declarations that define optional bindings. */ optionalDeclarations()79 abstract ImmutableSet<OptionalBindingDeclaration> optionalDeclarations(); 80 81 /** The kind of the module. */ kind()82 abstract ModuleKind kind(); 83 84 /** Returns all of the bindings declared in this module. */ 85 @Memoized allBindingDeclarations()86 ImmutableSet<BindingDeclaration> allBindingDeclarations() { 87 return ImmutableSet.<BindingDeclaration>builder() 88 .addAll(bindings()) 89 .addAll(delegateDeclarations()) 90 .addAll(multibindingDeclarations()) 91 .addAll(optionalDeclarations()) 92 .addAll(subcomponentDeclarations()) 93 .build(); 94 } 95 96 /** Returns the keys of all bindings declared by this module. */ allBindingKeys()97 ImmutableSet<Key> allBindingKeys() { 98 return allBindingDeclarations().stream().map(BindingDeclaration::key).collect(toImmutableSet()); 99 } 100 101 @Singleton 102 static final class Factory implements ClearableCache { 103 private final DaggerElements elements; 104 private final BindingFactory bindingFactory; 105 private final MultibindingDeclaration.Factory multibindingDeclarationFactory; 106 private final DelegateDeclaration.Factory bindingDelegateDeclarationFactory; 107 private final SubcomponentDeclaration.Factory subcomponentDeclarationFactory; 108 private final OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory; 109 private final Map<TypeElement, ModuleDescriptor> cache = new HashMap<>(); 110 111 @Inject Factory( DaggerElements elements, BindingFactory bindingFactory, MultibindingDeclaration.Factory multibindingDeclarationFactory, DelegateDeclaration.Factory bindingDelegateDeclarationFactory, SubcomponentDeclaration.Factory subcomponentDeclarationFactory, OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory)112 Factory( 113 DaggerElements elements, 114 BindingFactory bindingFactory, 115 MultibindingDeclaration.Factory multibindingDeclarationFactory, 116 DelegateDeclaration.Factory bindingDelegateDeclarationFactory, 117 SubcomponentDeclaration.Factory subcomponentDeclarationFactory, 118 OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory) { 119 this.elements = elements; 120 this.bindingFactory = bindingFactory; 121 this.multibindingDeclarationFactory = multibindingDeclarationFactory; 122 this.bindingDelegateDeclarationFactory = bindingDelegateDeclarationFactory; 123 this.subcomponentDeclarationFactory = subcomponentDeclarationFactory; 124 this.optionalBindingDeclarationFactory = optionalBindingDeclarationFactory; 125 } 126 create(TypeElement moduleElement)127 ModuleDescriptor create(TypeElement moduleElement) { 128 return reentrantComputeIfAbsent(cache, moduleElement, this::createUncached); 129 } 130 createUncached(TypeElement moduleElement)131 ModuleDescriptor createUncached(TypeElement moduleElement) { 132 ImmutableSet.Builder<ContributionBinding> bindings = ImmutableSet.builder(); 133 ImmutableSet.Builder<DelegateDeclaration> delegates = ImmutableSet.builder(); 134 ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations = 135 ImmutableSet.builder(); 136 ImmutableSet.Builder<OptionalBindingDeclaration> optionalDeclarations = 137 ImmutableSet.builder(); 138 139 for (ExecutableElement moduleMethod : methodsIn(elements.getAllMembers(moduleElement))) { 140 if (isAnnotationPresent(moduleMethod, Provides.class)) { 141 bindings.add(bindingFactory.providesMethodBinding(moduleMethod, moduleElement)); 142 } 143 if (isAnnotationPresent(moduleMethod, Produces.class)) { 144 bindings.add(bindingFactory.producesMethodBinding(moduleMethod, moduleElement)); 145 } 146 if (isAnnotationPresent(moduleMethod, Binds.class)) { 147 delegates.add(bindingDelegateDeclarationFactory.create(moduleMethod, moduleElement)); 148 } 149 if (isAnnotationPresent(moduleMethod, Multibinds.class)) { 150 multibindingDeclarations.add( 151 multibindingDeclarationFactory.forMultibindsMethod(moduleMethod, moduleElement)); 152 } 153 if (isAnnotationPresent(moduleMethod, BindsOptionalOf.class)) { 154 optionalDeclarations.add( 155 optionalBindingDeclarationFactory.forMethod(moduleMethod, moduleElement)); 156 } 157 } 158 159 return new AutoValue_ModuleDescriptor( 160 moduleElement, 161 ImmutableSet.copyOf(collectIncludedModules(new LinkedHashSet<>(), moduleElement)), 162 bindings.build(), 163 multibindingDeclarations.build(), 164 subcomponentDeclarationFactory.forModule(moduleElement), 165 delegates.build(), 166 optionalDeclarations.build(), 167 ModuleKind.forAnnotatedElement(moduleElement).get()); 168 } 169 170 /** Returns all the modules transitively included by given modules, including the arguments. */ transitiveModules(Iterable<TypeElement> modules)171 ImmutableSet<ModuleDescriptor> transitiveModules(Iterable<TypeElement> modules) { 172 return ImmutableSet.copyOf( 173 Traverser.forGraph( 174 (ModuleDescriptor module) -> transform(module.includedModules(), this::create)) 175 .depthFirstPreOrder(transform(modules, this::create))); 176 } 177 178 @CanIgnoreReturnValue collectIncludedModules( Set<TypeElement> includedModules, TypeElement moduleElement)179 private Set<TypeElement> collectIncludedModules( 180 Set<TypeElement> includedModules, TypeElement moduleElement) { 181 TypeMirror superclass = moduleElement.getSuperclass(); 182 if (!superclass.getKind().equals(NONE)) { 183 verify(superclass.getKind().equals(DECLARED)); 184 TypeElement superclassElement = MoreTypes.asTypeElement(superclass); 185 if (!superclassElement.getQualifiedName().contentEquals(Object.class.getCanonicalName())) { 186 collectIncludedModules(includedModules, superclassElement); 187 } 188 } 189 moduleAnnotation(moduleElement) 190 .ifPresent( 191 moduleAnnotation -> { 192 includedModules.addAll(moduleAnnotation.includes()); 193 includedModules.addAll(implicitlyIncludedModules(moduleElement)); 194 }); 195 return includedModules; 196 } 197 198 // @ContributesAndroidInjector generates a module that is implicitly included in the enclosing 199 // module implicitlyIncludedModules(TypeElement moduleElement)200 private ImmutableSet<TypeElement> implicitlyIncludedModules(TypeElement moduleElement) { 201 TypeElement contributesAndroidInjector = 202 elements.getTypeElement("dagger.android.ContributesAndroidInjector"); 203 if (contributesAndroidInjector == null) { 204 return ImmutableSet.of(); 205 } 206 return methodsIn(moduleElement.getEnclosedElements()).stream() 207 .filter(method -> isAnnotationPresent(method, contributesAndroidInjector.asType())) 208 .map(method -> elements.checkTypePresent(implicitlyIncludedModuleName(method))) 209 .collect(toImmutableSet()); 210 } 211 implicitlyIncludedModuleName(ExecutableElement method)212 private String implicitlyIncludedModuleName(ExecutableElement method) { 213 return getPackage(method).getQualifiedName() 214 + "." 215 + classFileName(ClassName.get(MoreElements.asType(method.getEnclosingElement()))) 216 + "_" 217 + LOWER_CAMEL.to(UPPER_CAMEL, method.getSimpleName().toString()); 218 } 219 220 @Override clearCache()221 public void clearCache() { 222 cache.clear(); 223 } 224 } 225 } 226