/* * Copyright (C) 2014 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.codegen.binding; import static com.google.auto.common.MoreElements.asType; import static com.google.auto.common.MoreElements.asVariable; import static com.google.auto.common.MoreElements.isAnnotationPresent; import static com.google.common.base.Preconditions.checkNotNull; import static dagger.internal.codegen.base.MoreAnnotationValues.getStringValue; import static dagger.internal.codegen.binding.SourceFiles.memberInjectedFieldSignatureForVariable; import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType; import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror; import static javax.lang.model.element.Modifier.STATIC; import static javax.lang.model.util.ElementFilter.constructorsIn; import com.google.auto.common.AnnotationMirrors; import com.google.auto.common.SuperficialValidation; import com.google.common.base.Equivalence; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableSet; import dagger.internal.InjectedFieldSignature; import dagger.internal.codegen.extension.DaggerCollectors; import dagger.internal.codegen.extension.DaggerStreams; import dagger.internal.codegen.kotlin.KotlinMetadataUtil; import dagger.internal.codegen.langmodel.DaggerElements; import java.util.Optional; import java.util.stream.Stream; import javax.inject.Inject; import javax.inject.Qualifier; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.util.ElementFilter; /** Utilities relating to annotations defined in the {@code javax.inject} package. */ public final class InjectionAnnotations { private static final Equivalence EQUIVALENCE = AnnotationMirrors.equivalence(); private final DaggerElements elements; private final KotlinMetadataUtil kotlinMetadataUtil; @Inject InjectionAnnotations(DaggerElements elements, KotlinMetadataUtil kotlinMetadataUtil) { this.elements = elements; this.kotlinMetadataUtil = kotlinMetadataUtil; } public Optional getQualifier(Element e) { if (!SuperficialValidation.validateElement(e)) { throw new TypeNotPresentException(e.toString(), null); } checkNotNull(e); ImmutableCollection qualifierAnnotations = getQualifiers(e); switch (qualifierAnnotations.size()) { case 0: return Optional.empty(); case 1: return Optional.of(qualifierAnnotations.iterator().next()); default: throw new IllegalArgumentException( e + " was annotated with more than one @Qualifier annotation"); } } public ImmutableCollection getQualifiers(Element element) { ImmutableSet qualifiers = AnnotationMirrors.getAnnotatedAnnotations(element, Qualifier.class); if (element.getKind() == ElementKind.FIELD // static injected fields are not supported, no need to get qualifier from kotlin metadata && !element.getModifiers().contains(STATIC) && isAnnotationPresent(element, Inject.class) && kotlinMetadataUtil.hasMetadata(element)) { return Stream.concat( qualifiers.stream(), getQualifiersForKotlinProperty(asVariable(element)).stream()) .map(EQUIVALENCE::wrap) // Wrap in equivalence to deduplicate .distinct() .map(Wrapper::get) .collect(DaggerStreams.toImmutableList()); } else { return qualifiers.asList(); } } /** Returns the constructors in {@code type} that are annotated with {@link Inject}. */ public static ImmutableSet injectedConstructors(TypeElement type) { return FluentIterable.from(constructorsIn(type.getEnclosedElements())) .filter(constructor -> isAnnotationPresent(constructor, Inject.class)) .toSet(); } /** * Gets the qualifiers annotation of a Kotlin Property. Finding these annotations involve finding * the synthetic method for annotations as described by the Kotlin metadata or finding the * corresponding MembersInjector method for the field, which also contains the qualifier * annotation. */ private ImmutableCollection getQualifiersForKotlinProperty( VariableElement fieldElement) { // TODO(bcorso): Consider moving this to KotlinMetadataUtil if (kotlinMetadataUtil.isMissingSyntheticPropertyForAnnotations(fieldElement)) { // If we detect that the synthetic method for annotations is missing, possibly due to the // element being from a compiled class, then find the MembersInjector that was generated // for the enclosing class and extract the qualifier information from it. TypeElement membersInjector = elements.getTypeElement( membersInjectorNameForType(asType(fieldElement.getEnclosingElement()))); if (membersInjector != null) { String memberInjectedFieldSignature = memberInjectedFieldSignatureForVariable(fieldElement); // TODO(danysantiago): We have to iterate over all the injection methods for every qualifier // look up. Making this N^2 when looking through all the injected fields. :( return ElementFilter.methodsIn(membersInjector.getEnclosedElements()).stream() .filter( method -> getAnnotationMirror(method, InjectedFieldSignature.class) .map(annotation -> getStringValue(annotation, "value")) .map(memberInjectedFieldSignature::equals) // If a method is not an @InjectedFieldSignature method then filter it out .orElse(false)) .collect(DaggerCollectors.toOptional()) .map(this::getQualifiers) .orElseThrow( () -> new IllegalStateException( String.format( "No matching InjectedFieldSignature for %1$s. This likely means that " + "%1$s was compiled with an older, incompatible version of " + "Dagger. Please update all Dagger dependencies to the same " + "version.", memberInjectedFieldSignature))); } else { throw new IllegalStateException( "No MembersInjector found for " + fieldElement.getEnclosingElement()); } } else { return kotlinMetadataUtil.getSyntheticPropertyAnnotations(fieldElement, Qualifier.class); } } }