1 /* 2 * Copyright (C) 2017 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 androidx.room.compiler.processing.XElementKt.isField; 20 import static androidx.room.compiler.processing.XElementKt.isMethod; 21 import static com.google.common.base.Preconditions.checkArgument; 22 import static dagger.internal.codegen.xprocessing.XElements.asField; 23 import static dagger.internal.codegen.xprocessing.XElements.asMethod; 24 import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement; 25 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName; 26 import static dagger.internal.codegen.xprocessing.XProcessingEnvs.javacOverrides; 27 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared; 28 import static dagger.internal.codegen.xprocessing.XTypes.nonObjectSuperclass; 29 import static java.util.Comparator.comparing; 30 31 import androidx.room.compiler.processing.XElement; 32 import androidx.room.compiler.processing.XFieldElement; 33 import androidx.room.compiler.processing.XMethodElement; 34 import androidx.room.compiler.processing.XMethodType; 35 import androidx.room.compiler.processing.XType; 36 import androidx.room.compiler.processing.XTypeElement; 37 import com.google.common.collect.ImmutableSortedSet; 38 import com.google.common.collect.LinkedHashMultimap; 39 import com.google.common.collect.SetMultimap; 40 import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite; 41 import dagger.internal.codegen.xprocessing.XElements; 42 import java.util.HashMap; 43 import java.util.HashSet; 44 import java.util.Map; 45 import java.util.Optional; 46 import java.util.Set; 47 import javax.inject.Inject; 48 49 /** A factory for {@link Binding} objects. */ 50 final class InjectionSiteFactory { 51 private final DependencyRequestFactory dependencyRequestFactory; 52 53 @Inject InjectionSiteFactory(DependencyRequestFactory dependencyRequestFactory)54 InjectionSiteFactory(DependencyRequestFactory dependencyRequestFactory) { 55 this.dependencyRequestFactory = dependencyRequestFactory; 56 } 57 58 /** Returns the injection sites for a type. */ getInjectionSites(XType type)59 ImmutableSortedSet<InjectionSite> getInjectionSites(XType type) { 60 checkArgument(isDeclared(type)); 61 Set<InjectionSite> injectionSites = new HashSet<>(); 62 InjectionSiteVisitor injectionSiteVisitor = new InjectionSiteVisitor(); 63 Map<XTypeElement, Integer> enclosingTypeElementOrder = new HashMap<>(); 64 Map<XElement, Integer> enclosedElementOrder = new HashMap<>(); 65 for (Optional<XType> currentType = Optional.of(type); 66 currentType.isPresent(); 67 currentType = nonObjectSuperclass(currentType.get())) { 68 XTypeElement typeElement = currentType.get().getTypeElement(); 69 enclosingTypeElementOrder.put(typeElement, enclosingTypeElementOrder.size()); 70 for (XElement enclosedElement : typeElement.getEnclosedElements()) { 71 injectionSiteVisitor 72 .visit(enclosedElement, currentType.get()) 73 .ifPresent( 74 injectionSite -> { 75 enclosedElementOrder.put(enclosedElement, enclosedElementOrder.size()); 76 injectionSites.add(injectionSite); 77 }); 78 } 79 } 80 return ImmutableSortedSet.copyOf( 81 // supertypes before subtypes 82 comparing( 83 InjectionSite::enclosingTypeElement, 84 comparing(enclosingTypeElementOrder::get).reversed()) 85 // fields before methods 86 .thenComparing(InjectionSite::kind) 87 // then sort by whichever element comes first in the parent 88 // this isn't necessary, but makes the processor nice and predictable 89 .thenComparing(InjectionSite::element, comparing(enclosedElementOrder::get)), 90 injectionSites); 91 } 92 93 private final class InjectionSiteVisitor { 94 private final SetMultimap<String, XMethodElement> subclassMethodMap = 95 LinkedHashMultimap.create(); 96 visit(XElement element, XType container)97 public Optional<InjectionSite> visit(XElement element, XType container) { 98 if (isMethod(element)) { 99 return visitMethod(asMethod(element), container); 100 } else if (isField(element)) { 101 return visitField(asField(element), container); 102 } 103 return Optional.empty(); 104 } 105 visitMethod(XMethodElement method, XType container)106 public Optional<InjectionSite> visitMethod(XMethodElement method, XType container) { 107 subclassMethodMap.put(getSimpleName(method), method); 108 if (!shouldBeInjected(method)) { 109 return Optional.empty(); 110 } 111 // This visitor assumes that subclass methods are visited before superclass methods, so we can 112 // skip any overridden method that has already been visited. To decrease the number of methods 113 // that are checked, we store the already injected methods in a SetMultimap and only check the 114 // methods with the same name. 115 XTypeElement enclosingType = closestEnclosingTypeElement(method); 116 for (XMethodElement subclassMethod : subclassMethodMap.get(getSimpleName(method))) { 117 if (method != subclassMethod && javacOverrides(subclassMethod, method, enclosingType)) { 118 return Optional.empty(); 119 } 120 } 121 XMethodType resolved = method.asMemberOf(container); 122 return Optional.of( 123 InjectionSite.method( 124 method, 125 dependencyRequestFactory.forRequiredResolvedVariables( 126 method.getParameters(), resolved.getParameterTypes()))); 127 } 128 visitField(XFieldElement field, XType container)129 public Optional<InjectionSite> visitField(XFieldElement field, XType container) { 130 if (!shouldBeInjected(field)) { 131 return Optional.empty(); 132 } 133 XType resolved = field.asMemberOf(container); 134 return Optional.of( 135 InjectionSite.field( 136 field, dependencyRequestFactory.forRequiredResolvedVariable(field, resolved))); 137 } 138 shouldBeInjected(XElement injectionSite)139 private boolean shouldBeInjected(XElement injectionSite) { 140 return InjectionAnnotations.hasInjectAnnotation(injectionSite) 141 && !XElements.isPrivate(injectionSite) 142 && !XElements.isStatic(injectionSite); 143 } 144 } 145 } 146