1 /* 2 * Copyright (C) 2014 Google, Inc. 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 package dagger.internal.codegen; 17 18 import com.google.auto.common.MoreElements; 19 import com.google.auto.common.MoreTypes; 20 import com.google.auto.value.AutoValue; 21 import com.google.common.base.Function; 22 import com.google.common.base.Optional; 23 import com.google.common.collect.ComparisonChain; 24 import com.google.common.collect.FluentIterable; 25 import com.google.common.collect.ImmutableSet; 26 import com.google.common.collect.ImmutableSortedSet; 27 import com.google.common.collect.LinkedHashMultimap; 28 import com.google.common.collect.SetMultimap; 29 import java.util.ArrayList; 30 import java.util.Comparator; 31 import java.util.HashSet; 32 import java.util.List; 33 import java.util.Set; 34 import javax.inject.Inject; 35 import javax.lang.model.element.Element; 36 import javax.lang.model.element.ElementKind; 37 import javax.lang.model.element.ElementVisitor; 38 import javax.lang.model.element.ExecutableElement; 39 import javax.lang.model.element.TypeElement; 40 import javax.lang.model.element.VariableElement; 41 import javax.lang.model.type.DeclaredType; 42 import javax.lang.model.type.ExecutableType; 43 import javax.lang.model.type.TypeMirror; 44 import javax.lang.model.util.ElementKindVisitor6; 45 import javax.lang.model.util.Elements; 46 import javax.lang.model.util.Types; 47 48 import static com.google.auto.common.MoreElements.isAnnotationPresent; 49 import static com.google.common.base.Preconditions.checkArgument; 50 import static com.google.common.base.Preconditions.checkNotNull; 51 import static com.google.common.base.Preconditions.checkState; 52 import static javax.lang.model.element.Modifier.PRIVATE; 53 import static javax.lang.model.element.Modifier.STATIC; 54 55 /** 56 * Represents the full members injection of a particular type. This does not pay attention to 57 * injected members on supertypes. 58 * 59 * @author Gregory Kick 60 * @since 2.0 61 */ 62 @AutoValue 63 abstract class MembersInjectionBinding extends Binding { bindingElement()64 @Override abstract TypeElement bindingElement(); 65 66 /** The set of individual sites where {@link Inject} is applied. */ injectionSites()67 abstract ImmutableSortedSet<InjectionSite> injectionSites(); 68 parentInjectorRequest()69 abstract Optional<DependencyRequest> parentInjectorRequest(); 70 71 enum Strategy { 72 NO_OP, 73 INJECT_MEMBERS, 74 } 75 injectionStrategy()76 Strategy injectionStrategy() { 77 return injectionSites().isEmpty() ? Strategy.NO_OP : Strategy.INJECT_MEMBERS; 78 } 79 withoutParentInjectorRequest()80 MembersInjectionBinding withoutParentInjectorRequest() { 81 return new AutoValue_MembersInjectionBinding( 82 key(), 83 dependencies(), 84 implicitDependencies(), 85 bindingPackage(), 86 hasNonDefaultTypeParameters(), 87 bindingElement(), 88 injectionSites(), 89 Optional.<DependencyRequest>absent()); 90 } 91 92 @Override bindingType()93 protected Binding.Type bindingType() { 94 return Binding.Type.MEMBERS_INJECTION; 95 } 96 97 @AutoValue 98 abstract static class InjectionSite { 99 enum Kind { 100 FIELD, 101 METHOD, 102 } 103 kind()104 abstract Kind kind(); 105 element()106 abstract Element element(); 107 dependencies()108 abstract ImmutableSet<DependencyRequest> dependencies(); 109 indexAmongSiblingMembers(InjectionSite injectionSite)110 protected int indexAmongSiblingMembers(InjectionSite injectionSite) { 111 return injectionSite 112 .element() 113 .getEnclosingElement() 114 .getEnclosedElements() 115 .indexOf(injectionSite.element()); 116 } 117 } 118 119 static final class Factory { 120 private final Elements elements; 121 private final Types types; 122 private final Key.Factory keyFactory; 123 private final DependencyRequest.Factory dependencyRequestFactory; 124 Factory(Elements elements, Types types, Key.Factory keyFactory, DependencyRequest.Factory dependencyRequestFactory)125 Factory(Elements elements, Types types, Key.Factory keyFactory, 126 DependencyRequest.Factory dependencyRequestFactory) { 127 this.elements = checkNotNull(elements); 128 this.types = checkNotNull(types); 129 this.keyFactory = checkNotNull(keyFactory); 130 this.dependencyRequestFactory = checkNotNull(dependencyRequestFactory); 131 } 132 injectionSiteForInjectMethod( ExecutableElement methodElement, DeclaredType containingType)133 private InjectionSite injectionSiteForInjectMethod( 134 ExecutableElement methodElement, DeclaredType containingType) { 135 checkNotNull(methodElement); 136 checkArgument(methodElement.getKind().equals(ElementKind.METHOD)); 137 ExecutableType resolved = 138 MoreTypes.asExecutable(types.asMemberOf(containingType, methodElement)); 139 return new AutoValue_MembersInjectionBinding_InjectionSite( 140 InjectionSite.Kind.METHOD, 141 methodElement, 142 dependencyRequestFactory.forRequiredResolvedVariables( 143 containingType, methodElement.getParameters(), resolved.getParameterTypes())); 144 } 145 injectionSiteForInjectField( VariableElement fieldElement, DeclaredType containingType)146 private InjectionSite injectionSiteForInjectField( 147 VariableElement fieldElement, DeclaredType containingType) { 148 checkNotNull(fieldElement); 149 checkArgument(fieldElement.getKind().equals(ElementKind.FIELD)); 150 checkArgument(isAnnotationPresent(fieldElement, Inject.class)); 151 TypeMirror resolved = types.asMemberOf(containingType, fieldElement); 152 return new AutoValue_MembersInjectionBinding_InjectionSite( 153 InjectionSite.Kind.FIELD, 154 fieldElement, 155 ImmutableSet.of( 156 dependencyRequestFactory.forRequiredResolvedVariable( 157 containingType, fieldElement, resolved))); 158 } 159 160 /** Returns an unresolved version of this binding. */ unresolve(MembersInjectionBinding binding)161 MembersInjectionBinding unresolve(MembersInjectionBinding binding) { 162 checkState(binding.hasNonDefaultTypeParameters()); 163 DeclaredType unresolved = MoreTypes.asDeclared(binding.bindingElement().asType()); 164 return forInjectedType(unresolved, Optional.<TypeMirror>absent()); 165 } 166 167 /** Returns true if the type has some injected members in itself or any of its super classes. */ hasInjectedMembers(DeclaredType declaredType)168 boolean hasInjectedMembers(DeclaredType declaredType) { 169 return !getInjectionSites(declaredType).isEmpty(); 170 } 171 172 /** 173 * Returns a MembersInjectionBinding for the given type. If {@code resolvedType} is present, 174 * this will return a resolved binding, with the key & type resolved to the given type (using 175 * {@link Types#asMemberOf(DeclaredType, Element)}). 176 */ forInjectedType( DeclaredType declaredType, Optional<TypeMirror> resolvedType)177 MembersInjectionBinding forInjectedType( 178 DeclaredType declaredType, Optional<TypeMirror> resolvedType) { 179 // If the class this is injecting has some type arguments, resolve everything. 180 if (!declaredType.getTypeArguments().isEmpty() && resolvedType.isPresent()) { 181 DeclaredType resolved = MoreTypes.asDeclared(resolvedType.get()); 182 // Validate that we're resolving from the correct type. 183 checkState( 184 types.isSameType(types.erasure(resolved), types.erasure(declaredType)), 185 "erased expected type: %s, erased actual type: %s", 186 types.erasure(resolved), 187 types.erasure(declaredType)); 188 declaredType = resolved; 189 } 190 ImmutableSortedSet<InjectionSite> injectionSites = getInjectionSites(declaredType); 191 ImmutableSet<DependencyRequest> dependencies = 192 FluentIterable.from(injectionSites) 193 .transformAndConcat( 194 new Function<InjectionSite, Set<DependencyRequest>>() { 195 @Override 196 public Set<DependencyRequest> apply(InjectionSite input) { 197 return input.dependencies(); 198 } 199 }) 200 .toSet(); 201 202 Optional<DependencyRequest> parentInjectorRequest = 203 MoreTypes.nonObjectSuperclass(types, elements, declaredType) 204 .transform( 205 new Function<DeclaredType, DependencyRequest>() { 206 @Override 207 public DependencyRequest apply(DeclaredType input) { 208 return dependencyRequestFactory.forMembersInjectedType(input); 209 } 210 }); 211 212 Key key = keyFactory.forMembersInjectedType(declaredType); 213 TypeElement typeElement = MoreElements.asType(declaredType.asElement()); 214 return new AutoValue_MembersInjectionBinding( 215 key, 216 dependencies, 217 dependencies, 218 findBindingPackage(key), 219 hasNonDefaultTypeParameters(typeElement, key.type(), types), 220 typeElement, 221 injectionSites, 222 parentInjectorRequest); 223 } 224 getInjectionSites(DeclaredType declaredType)225 private ImmutableSortedSet<InjectionSite> getInjectionSites(DeclaredType declaredType) { 226 Set<InjectionSite> injectionSites = new HashSet<>(); 227 final List<TypeElement> ancestors = new ArrayList<>(); 228 SetMultimap<String, ExecutableElement> overriddenMethodMap = LinkedHashMultimap.create(); 229 for (Optional<DeclaredType> currentType = Optional.of(declaredType); 230 currentType.isPresent(); 231 currentType = MoreTypes.nonObjectSuperclass(types, elements, currentType.get())) { 232 final DeclaredType type = currentType.get(); 233 ancestors.add(MoreElements.asType(type.asElement())); 234 for (Element enclosedElement : type.asElement().getEnclosedElements()) { 235 Optional<InjectionSite> maybeInjectionSite = 236 injectionSiteVisitor.visit(enclosedElement, type); 237 if (maybeInjectionSite.isPresent()) { 238 InjectionSite injectionSite = maybeInjectionSite.get(); 239 if (shouldBeInjected(injectionSite.element(), overriddenMethodMap)) { 240 injectionSites.add(injectionSite); 241 } 242 if (injectionSite.kind() == InjectionSite.Kind.METHOD) { 243 ExecutableElement injectionSiteMethod = 244 MoreElements.asExecutable(injectionSite.element()); 245 overriddenMethodMap.put( 246 injectionSiteMethod.getSimpleName().toString(), injectionSiteMethod); 247 } 248 } 249 } 250 } 251 return ImmutableSortedSet.copyOf( 252 new Comparator<InjectionSite>() { 253 @Override 254 public int compare(InjectionSite left, InjectionSite right) { 255 return ComparisonChain.start() 256 // supertypes before subtypes 257 .compare( 258 ancestors.indexOf(right.element().getEnclosingElement()), 259 ancestors.indexOf(left.element().getEnclosingElement())) 260 // fields before methods 261 .compare(left.element().getKind(), right.element().getKind()) 262 // then sort by whichever element comes first in the parent 263 // this isn't necessary, but makes the processor nice and predictable 264 .compare( 265 left.indexAmongSiblingMembers(left), right.indexAmongSiblingMembers(right)) 266 .result(); 267 } 268 }, 269 injectionSites); 270 } 271 shouldBeInjected( Element injectionSite, SetMultimap<String, ExecutableElement> overriddenMethodMap)272 private boolean shouldBeInjected( 273 Element injectionSite, SetMultimap<String, ExecutableElement> overriddenMethodMap) { 274 if (!isAnnotationPresent(injectionSite, Inject.class) 275 || injectionSite.getModifiers().contains(PRIVATE) 276 || injectionSite.getModifiers().contains(STATIC)) { 277 return false; 278 } 279 280 if (injectionSite.getKind().isField()) { // Inject all fields (self and ancestors) 281 return true; 282 } 283 284 // For each method with the same name belonging to any descendant class, return false if any 285 // method has already overridden the injectionSite method. To decrease the number of methods 286 // that are checked, we store the already injected methods in a SetMultimap and only 287 // check the methods with the same name. 288 ExecutableElement injectionSiteMethod = MoreElements.asExecutable(injectionSite); 289 TypeElement injectionSiteType = MoreElements.asType(injectionSite.getEnclosingElement()); 290 for (ExecutableElement method : 291 overriddenMethodMap.get(injectionSiteMethod.getSimpleName().toString())) { 292 if (elements.overrides(method, injectionSiteMethod, injectionSiteType)) { 293 return false; 294 } 295 } 296 return true; 297 } 298 299 private final ElementVisitor<Optional<InjectionSite>, DeclaredType> injectionSiteVisitor = 300 new ElementKindVisitor6<Optional<InjectionSite>, DeclaredType>( 301 Optional.<InjectionSite>absent()) { 302 @Override 303 public Optional<InjectionSite> visitExecutableAsMethod( 304 ExecutableElement e, DeclaredType type) { 305 return Optional.of(injectionSiteForInjectMethod(e, type)); 306 } 307 308 @Override 309 public Optional<InjectionSite> visitVariableAsField( 310 VariableElement e, DeclaredType type) { 311 return (isAnnotationPresent(e, Inject.class) 312 && !e.getModifiers().contains(PRIVATE) 313 && !e.getModifiers().contains(STATIC)) 314 ? Optional.of(injectionSiteForInjectField(e, type)) 315 : Optional.<InjectionSite>absent(); 316 } 317 }; 318 } 319 } 320