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.MoreElements.asVariable; 21 import static com.google.auto.common.MoreElements.isAnnotationPresent; 22 import static com.google.common.base.Preconditions.checkNotNull; 23 import static dagger.internal.codegen.base.MoreAnnotationValues.getStringValue; 24 import static dagger.internal.codegen.binding.SourceFiles.memberInjectedFieldSignatureForVariable; 25 import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType; 26 import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror; 27 import static javax.lang.model.element.Modifier.STATIC; 28 import static javax.lang.model.util.ElementFilter.constructorsIn; 29 30 import com.google.auto.common.AnnotationMirrors; 31 import com.google.auto.common.SuperficialValidation; 32 import com.google.common.base.Equivalence; 33 import com.google.common.base.Equivalence.Wrapper; 34 import com.google.common.collect.FluentIterable; 35 import com.google.common.collect.ImmutableCollection; 36 import com.google.common.collect.ImmutableSet; 37 import dagger.internal.InjectedFieldSignature; 38 import dagger.internal.codegen.extension.DaggerCollectors; 39 import dagger.internal.codegen.extension.DaggerStreams; 40 import dagger.internal.codegen.kotlin.KotlinMetadataUtil; 41 import dagger.internal.codegen.langmodel.DaggerElements; 42 import java.util.Optional; 43 import java.util.stream.Stream; 44 import javax.inject.Inject; 45 import javax.inject.Qualifier; 46 import javax.lang.model.element.AnnotationMirror; 47 import javax.lang.model.element.Element; 48 import javax.lang.model.element.ElementKind; 49 import javax.lang.model.element.ExecutableElement; 50 import javax.lang.model.element.TypeElement; 51 import javax.lang.model.element.VariableElement; 52 import javax.lang.model.util.ElementFilter; 53 54 /** Utilities relating to annotations defined in the {@code javax.inject} package. */ 55 public final class InjectionAnnotations { 56 57 private static final Equivalence<AnnotationMirror> EQUIVALENCE = AnnotationMirrors.equivalence(); 58 59 private final DaggerElements elements; 60 private final KotlinMetadataUtil kotlinMetadataUtil; 61 62 @Inject InjectionAnnotations(DaggerElements elements, KotlinMetadataUtil kotlinMetadataUtil)63 InjectionAnnotations(DaggerElements elements, KotlinMetadataUtil kotlinMetadataUtil) { 64 this.elements = elements; 65 this.kotlinMetadataUtil = kotlinMetadataUtil; 66 } 67 getQualifier(Element e)68 public Optional<AnnotationMirror> getQualifier(Element e) { 69 if (!SuperficialValidation.validateElement(e)) { 70 throw new TypeNotPresentException(e.toString(), null); 71 } 72 checkNotNull(e); 73 ImmutableCollection<? extends AnnotationMirror> qualifierAnnotations = getQualifiers(e); 74 switch (qualifierAnnotations.size()) { 75 case 0: 76 return Optional.empty(); 77 case 1: 78 return Optional.<AnnotationMirror>of(qualifierAnnotations.iterator().next()); 79 default: 80 throw new IllegalArgumentException( 81 e + " was annotated with more than one @Qualifier annotation"); 82 } 83 } 84 getQualifiers(Element element)85 public ImmutableCollection<? extends AnnotationMirror> getQualifiers(Element element) { 86 ImmutableSet<? extends AnnotationMirror> qualifiers = 87 AnnotationMirrors.getAnnotatedAnnotations(element, Qualifier.class); 88 if (element.getKind() == ElementKind.FIELD 89 // static injected fields are not supported, no need to get qualifier from kotlin metadata 90 && !element.getModifiers().contains(STATIC) 91 && isAnnotationPresent(element, Inject.class) 92 && kotlinMetadataUtil.hasMetadata(element)) { 93 return Stream.concat( 94 qualifiers.stream(), getQualifiersForKotlinProperty(asVariable(element)).stream()) 95 .map(EQUIVALENCE::wrap) // Wrap in equivalence to deduplicate 96 .distinct() 97 .map(Wrapper::get) 98 .collect(DaggerStreams.toImmutableList()); 99 } else { 100 return qualifiers.asList(); 101 } 102 } 103 104 /** Returns the constructors in {@code type} that are annotated with {@link Inject}. */ injectedConstructors(TypeElement type)105 public static ImmutableSet<ExecutableElement> injectedConstructors(TypeElement type) { 106 return FluentIterable.from(constructorsIn(type.getEnclosedElements())) 107 .filter(constructor -> isAnnotationPresent(constructor, Inject.class)) 108 .toSet(); 109 } 110 111 /** 112 * Gets the qualifiers annotation of a Kotlin Property. Finding these annotations involve finding 113 * the synthetic method for annotations as described by the Kotlin metadata or finding the 114 * corresponding MembersInjector method for the field, which also contains the qualifier 115 * annotation. 116 */ getQualifiersForKotlinProperty( VariableElement fieldElement)117 private ImmutableCollection<? extends AnnotationMirror> getQualifiersForKotlinProperty( 118 VariableElement fieldElement) { 119 // TODO(bcorso): Consider moving this to KotlinMetadataUtil 120 if (kotlinMetadataUtil.isMissingSyntheticPropertyForAnnotations(fieldElement)) { 121 // If we detect that the synthetic method for annotations is missing, possibly due to the 122 // element being from a compiled class, then find the MembersInjector that was generated 123 // for the enclosing class and extract the qualifier information from it. 124 TypeElement membersInjector = 125 elements.getTypeElement( 126 membersInjectorNameForType(asType(fieldElement.getEnclosingElement()))); 127 if (membersInjector != null) { 128 String memberInjectedFieldSignature = memberInjectedFieldSignatureForVariable(fieldElement); 129 // TODO(danysantiago): We have to iterate over all the injection methods for every qualifier 130 // look up. Making this N^2 when looking through all the injected fields. :( 131 return ElementFilter.methodsIn(membersInjector.getEnclosedElements()).stream() 132 .filter( 133 method -> 134 getAnnotationMirror(method, InjectedFieldSignature.class) 135 .map(annotation -> getStringValue(annotation, "value")) 136 .map(memberInjectedFieldSignature::equals) 137 // If a method is not an @InjectedFieldSignature method then filter it out 138 .orElse(false)) 139 .collect(DaggerCollectors.toOptional()) 140 .map(this::getQualifiers) 141 .orElseThrow( 142 () -> 143 new IllegalStateException( 144 String.format( 145 "No matching InjectedFieldSignature for %1$s. This likely means that " 146 + "%1$s was compiled with an older, incompatible version of " 147 + "Dagger. Please update all Dagger dependencies to the same " 148 + "version.", 149 memberInjectedFieldSignature))); 150 } else { 151 throw new IllegalStateException( 152 "No MembersInjector found for " + fieldElement.getEnclosingElement()); 153 } 154 } else { 155 return kotlinMetadataUtil.getSyntheticPropertyAnnotations(fieldElement, Qualifier.class); 156 } 157 } 158 } 159