• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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