Java tutorial
/* * Copyright (C) 2017 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; import static com.google.auto.common.MoreElements.isAnnotationPresent; import static com.google.auto.common.MoreTypes.asDeclared; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Iterables.getOnlyElement; import static dagger.internal.codegen.Binding.hasNonDefaultTypeParameters; import static dagger.internal.codegen.ComponentDescriptor.isComponentProductionMethod; import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType; import static dagger.internal.codegen.ContributionBinding.bindingKindForMultibindingKey; import static dagger.internal.codegen.DaggerElements.DECLARATION_ORDER; import static dagger.internal.codegen.DaggerStreams.toImmutableSet; import static dagger.internal.codegen.InjectionAnnotations.getQualifier; import static dagger.internal.codegen.MapKeys.getMapKey; import static dagger.internal.codegen.MoreAnnotationMirrors.wrapOptionalInEquivalence; import static dagger.internal.codegen.Scopes.uniqueScopeOf; import static dagger.model.BindingKind.BOUND_INSTANCE; import static dagger.model.BindingKind.COMPONENT; import static dagger.model.BindingKind.COMPONENT_DEPENDENCY; import static dagger.model.BindingKind.COMPONENT_PRODUCTION; import static dagger.model.BindingKind.COMPONENT_PROVISION; import static dagger.model.BindingKind.DELEGATE; import static dagger.model.BindingKind.INJECTION; import static dagger.model.BindingKind.MEMBERS_INJECTOR; import static dagger.model.BindingKind.OPTIONAL; import static dagger.model.BindingKind.PRODUCTION; import static dagger.model.BindingKind.PROVISION; import static dagger.model.BindingKind.SUBCOMPONENT_BUILDER; import static javax.lang.model.element.ElementKind.CONSTRUCTOR; import static javax.lang.model.element.ElementKind.METHOD; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.STATIC; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.SetMultimap; import dagger.Module; import dagger.internal.codegen.MembersInjectionBinding.InjectionSite; import dagger.internal.codegen.ProductionBinding.ProductionKind; import dagger.model.DependencyRequest; import dagger.model.Key; import dagger.model.RequestKind; import dagger.producers.Produced; import dagger.producers.Producer; import java.util.ArrayList; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.BiFunction; import javax.inject.Inject; import javax.inject.Provider; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ElementVisitor; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementKindVisitor6; /** A factory for {@link Binding} objects. */ final class BindingFactory { private final DaggerTypes types; private final KeyFactory keyFactory; private final DependencyRequestFactory dependencyRequestFactory; private final DaggerElements elements; @Inject BindingFactory(DaggerTypes types, DaggerElements elements, KeyFactory keyFactory, DependencyRequestFactory dependencyRequestFactory) { this.types = types; this.elements = elements; this.keyFactory = keyFactory; this.dependencyRequestFactory = dependencyRequestFactory; } /** * Returns an {@link dagger.model.BindingKind#INJECTION} binding. * * @param constructorElement the {@code @Inject}-annotated constructor * @param resolvedType the parameterized type if the constructor is for a generic class and the * binding should be for the parameterized type */ // TODO(dpb): See if we can just pass the parameterized type and not also the constructor. ProvisionBinding injectionBinding(ExecutableElement constructorElement, Optional<TypeMirror> resolvedType) { checkArgument(constructorElement.getKind().equals(CONSTRUCTOR)); checkArgument(isAnnotationPresent(constructorElement, Inject.class)); checkArgument(!getQualifier(constructorElement).isPresent()); ExecutableType constructorType = MoreTypes.asExecutable(constructorElement.asType()); DeclaredType constructedType = MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType()); // If the class this is constructing has some type arguments, resolve everything. if (!constructedType.getTypeArguments().isEmpty() && resolvedType.isPresent()) { DeclaredType resolved = MoreTypes.asDeclared(resolvedType.get()); // Validate that we're resolving from the correct type. checkState(types.isSameType(types.erasure(resolved), types.erasure(constructedType)), "erased expected type: %s, erased actual type: %s", types.erasure(resolved), types.erasure(constructedType)); constructorType = MoreTypes.asExecutable(types.asMemberOf(resolved, constructorElement)); constructedType = resolved; } Key key = keyFactory.forInjectConstructorWithResolvedType(constructedType); ImmutableSet<DependencyRequest> provisionDependencies = dependencyRequestFactory .forRequiredResolvedVariables(constructorElement.getParameters(), constructorType.getParameterTypes()); ProvisionBinding.Builder builder = ProvisionBinding.builder().contributionType(ContributionType.UNIQUE) .bindingElement(constructorElement).key(key).provisionDependencies(provisionDependencies) .injectionSites(getInjectionSites(constructedType)).kind(INJECTION) .scope(uniqueScopeOf(constructorElement.getEnclosingElement())); TypeElement bindingTypeElement = MoreElements.asType(constructorElement.getEnclosingElement()); if (hasNonDefaultTypeParameters(bindingTypeElement, key.type(), types)) { builder.unresolved(injectionBinding(constructorElement, Optional.empty())); } return builder.build(); } /** * Returns a {@link dagger.model.BindingKind#PROVISION} binding for a {@code @Provides}-annotated * method. * * @param contributedBy the installed module that declares or inherits the method */ ProvisionBinding providesMethodBinding(ExecutableElement providesMethod, TypeElement contributedBy) { return setMethodBindingProperties(ProvisionBinding.builder(), providesMethod, contributedBy, keyFactory.forProvidesMethod(providesMethod, contributedBy), this::providesMethodBinding) .kind(PROVISION).scope(uniqueScopeOf(providesMethod)) .nullableType(getNullableType(providesMethod)).build(); } /** * Returns a {@link dagger.model.BindingKind#PRODUCTION} binding for a {@code @Produces}-annotated * method. * * @param contributedBy the installed module that declares or inherits the method */ ProductionBinding producesMethodBinding(ExecutableElement producesMethod, TypeElement contributedBy) { // TODO(beder): Add nullability checking with Java 8. ProductionBinding.Builder builder = setMethodBindingProperties(ProductionBinding.builder(), producesMethod, contributedBy, keyFactory.forProducesMethod(producesMethod, contributedBy), this::producesMethodBinding).kind(PRODUCTION) .productionKind(ProductionKind.fromProducesMethod(producesMethod)) .thrownTypes(producesMethod.getThrownTypes()) .executorRequest(dependencyRequestFactory.forProductionImplementationExecutor()) .monitorRequest(dependencyRequestFactory.forProductionComponentMonitor()); return builder.build(); } private <C extends ContributionBinding, B extends ContributionBinding.Builder<C, B>> B setMethodBindingProperties( B builder, ExecutableElement method, TypeElement contributedBy, Key key, BiFunction<ExecutableElement, TypeElement, C> create) { checkArgument(method.getKind().equals(METHOD)); ExecutableType methodType = MoreTypes .asExecutable(types.asMemberOf(MoreTypes.asDeclared(contributedBy.asType()), method)); if (!types.isSameType(methodType, method.asType())) { builder.unresolved(create.apply(method, MoreElements.asType(method.getEnclosingElement()))); } return builder.contributionType(ContributionType.fromBindingMethod(method)).bindingElement(method) .contributingModule(contributedBy).key(key) .dependencies(dependencyRequestFactory.forRequiredResolvedVariables(method.getParameters(), methodType.getParameterTypes())) .wrappedMapKeyAnnotation(wrapOptionalInEquivalence(getMapKey(method))); } /** * Returns a {@link dagger.model.BindingKind#MULTIBOUND_MAP} or {@link * dagger.model.BindingKind#MULTIBOUND_SET} binding given a set of multibinding contribution * bindings. * * @param key a key that may be satisfied by a multibinding */ ContributionBinding syntheticMultibinding(Key key, Iterable<ContributionBinding> multibindingContributions) { ContributionBinding.Builder<?, ?> builder = multibindingRequiresProduction(key, multibindingContributions) ? ProductionBinding.builder() : ProvisionBinding.builder(); return builder.contributionType(ContributionType.UNIQUE).key(key) .dependencies(dependencyRequestFactory.forMultibindingContributions(key, multibindingContributions)) .kind(bindingKindForMultibindingKey(key)).build(); } private boolean multibindingRequiresProduction(Key key, Iterable<ContributionBinding> multibindingContributions) { if (MapType.isMap(key)) { MapType mapType = MapType.from(key); if (mapType.valuesAreTypeOf(Producer.class) || mapType.valuesAreTypeOf(Produced.class)) { return true; } } else if (SetType.isSet(key) && SetType.from(key).elementsAreTypeOf(Produced.class)) { return true; } return Iterables.any(multibindingContributions, binding -> binding.bindingType().equals(BindingType.PRODUCTION)); } /** Returns a {@link dagger.model.BindingKind#COMPONENT} binding for the component. */ ProvisionBinding componentBinding(TypeElement componentDefinitionType) { checkNotNull(componentDefinitionType); return ProvisionBinding.builder().contributionType(ContributionType.UNIQUE) .bindingElement(componentDefinitionType).key(keyFactory.forType(componentDefinitionType.asType())) .kind(COMPONENT).build(); } /** * Returns a {@link dagger.model.BindingKind#COMPONENT_DEPENDENCY} binding for a component's * dependency. */ ProvisionBinding componentDependencyBinding(ComponentRequirement dependency) { checkNotNull(dependency); return ProvisionBinding.builder().contributionType(ContributionType.UNIQUE) .bindingElement(dependency.typeElement()).key(keyFactory.forType(dependency.type())) .kind(COMPONENT_DEPENDENCY).build(); } /** * Returns a {@link dagger.model.BindingKind#COMPONENT_PROVISION} or {@link * dagger.model.BindingKind#COMPONENT_PRODUCTION} binding for a method on a component's * dependency. * * @param componentDescriptor the component with the dependency, not the dependency that has the * method */ ContributionBinding componentDependencyMethodBinding(ComponentDescriptor componentDescriptor, ExecutableElement dependencyMethod) { checkArgument(dependencyMethod.getKind().equals(METHOD)); checkArgument(dependencyMethod.getParameters().isEmpty()); ContributionBinding.Builder<?, ?> builder; if (componentDescriptor.kind().isProducer() && isComponentProductionMethod(elements, dependencyMethod)) { builder = ProductionBinding.builder().key(keyFactory.forProductionComponentMethod(dependencyMethod)) .kind(COMPONENT_PRODUCTION).thrownTypes(dependencyMethod.getThrownTypes()); } else { builder = ProvisionBinding.builder().key(keyFactory.forComponentMethod(dependencyMethod)) .nullableType(getNullableType(dependencyMethod)).kind(COMPONENT_PROVISION) .scope(uniqueScopeOf(dependencyMethod)); } return builder.contributionType(ContributionType.UNIQUE).bindingElement(dependencyMethod).build(); } /** * Returns a {@link dagger.model.BindingKind#BOUND_INSTANCE} binding for a * {@code @BindsInstance}-annotated builder method. */ ProvisionBinding boundInstanceBinding(ComponentRequirement requirement, ExecutableElement method) { checkArgument(method.getKind().equals(METHOD)); checkArgument(method.getParameters().size() == 1); return ProvisionBinding.builder().contributionType(ContributionType.UNIQUE).bindingElement(method) .key(requirement.key().get()).nullableType(getNullableType(getOnlyElement(method.getParameters()))) .kind(BOUND_INSTANCE).build(); } /** * Returns a {@link dagger.model.BindingKind#SUBCOMPONENT_BUILDER} binding declared by a component * method that returns a subcomponent builder. Use {{@link * #subcomponentCreatorBinding(ImmutableSet)}} for bindings declared using {@link * Module#subcomponents()}. * * @param component the component that declares or inherits the method */ ProvisionBinding subcomponentCreatorBinding(ExecutableElement subcomponentCreatorMethod, TypeElement component) { checkArgument(subcomponentCreatorMethod.getKind().equals(METHOD)); checkArgument(subcomponentCreatorMethod.getParameters().isEmpty()); Key key = keyFactory.forSubcomponentCreatorMethod(subcomponentCreatorMethod, asDeclared(component.asType())); return ProvisionBinding.builder().contributionType(ContributionType.UNIQUE) .bindingElement(subcomponentCreatorMethod).key(key).kind(SUBCOMPONENT_BUILDER).build(); } /** * Returns a {@link dagger.model.BindingKind#SUBCOMPONENT_BUILDER} binding declared using {@link * Module#subcomponents()}. */ ProvisionBinding subcomponentCreatorBinding(ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations) { SubcomponentDeclaration subcomponentDeclaration = subcomponentDeclarations.iterator().next(); return ProvisionBinding.builder().contributionType(ContributionType.UNIQUE) .key(subcomponentDeclaration.key()).kind(SUBCOMPONENT_BUILDER).build(); } /** * Returns a {@link dagger.model.BindingKind#DELEGATE} binding. * * @param delegateDeclaration the {@code @Binds}-annotated declaration * @param actualBinding the binding that satisfies the {@code @Binds} declaration */ ContributionBinding delegateBinding(DelegateDeclaration delegateDeclaration, ContributionBinding actualBinding) { switch (actualBinding.bindingType()) { case PRODUCTION: return buildDelegateBinding(ProductionBinding.builder().nullableType(actualBinding.nullableType()), delegateDeclaration, Producer.class); case PROVISION: return buildDelegateBinding( ProvisionBinding.builder().scope(uniqueScopeOf(delegateDeclaration.bindingElement().get())) .nullableType(actualBinding.nullableType()), delegateDeclaration, Provider.class); case MEMBERS_INJECTION: // fall-through to throw } throw new AssertionError("bindingType: " + actualBinding); } /** * Returns a {@link dagger.model.BindingKind#DELEGATE} binding used when there is no binding that * satisfies the {@code @Binds} declaration. */ ContributionBinding unresolvedDelegateBinding(DelegateDeclaration delegateDeclaration) { return buildDelegateBinding( ProvisionBinding.builder().scope(uniqueScopeOf(delegateDeclaration.bindingElement().get())), delegateDeclaration, Provider.class); } private ContributionBinding buildDelegateBinding(ContributionBinding.Builder<?, ?> builder, DelegateDeclaration delegateDeclaration, Class<?> frameworkType) { return builder.contributionType(delegateDeclaration.contributionType()) .bindingElement(delegateDeclaration.bindingElement().get()) .contributingModule(delegateDeclaration.contributingModule().get()) .key(keyFactory.forDelegateBinding(delegateDeclaration, frameworkType)) .dependencies(delegateDeclaration.delegateRequest()) .wrappedMapKeyAnnotation(delegateDeclaration.wrappedMapKey()).kind(DELEGATE).build(); } /** * Returns an {@link dagger.model.BindingKind#OPTIONAL} binding for {@code key}. * * @param requestKind the kind of request for the optional binding * @param underlyingKeyBindings the possibly empty set of bindings that exist in the component for * the underlying (non-optional) key */ ContributionBinding syntheticOptionalBinding(Key key, RequestKind requestKind, ResolvedBindings underlyingKeyBindings) { ContributionBinding.Builder<?, ?> builder = syntheticOptionalBindingBuilder(requestKind, underlyingKeyBindings).contributionType(ContributionType.UNIQUE).key(key).kind(OPTIONAL); if (!underlyingKeyBindings.isEmpty()) { builder.dependencies(dependencyRequestFactory.forSyntheticPresentOptionalBinding(key, requestKind)); } return builder.build(); } private ContributionBinding.Builder<?, ?> syntheticOptionalBindingBuilder(RequestKind requestKind, ResolvedBindings underlyingKeyBindings) { return !underlyingKeyBindings.isEmpty() && (underlyingKeyBindings.bindingTypes().contains(BindingType.PRODUCTION) || requestKind.equals(RequestKind.PRODUCER) // handles producerFromProvider cases || requestKind.equals(RequestKind.PRODUCED)) // handles producerFromProvider cases ? ProductionBinding.builder() : ProvisionBinding.builder(); } /** Returns a {@link dagger.model.BindingKind#MEMBERS_INJECTOR} binding. */ ProvisionBinding membersInjectorBinding(Key key, MembersInjectionBinding membersInjectionBinding) { return ProvisionBinding.builder().key(key).contributionType(ContributionType.UNIQUE).kind(MEMBERS_INJECTOR) .bindingElement(MoreTypes.asTypeElement(membersInjectionBinding.key().type())) .provisionDependencies(membersInjectionBinding.dependencies()) .injectionSites(membersInjectionBinding.injectionSites()).build(); } /** * Returns a {@link dagger.model.BindingKind#MEMBERS_INJECTION} binding. * * @param resolvedType if {@code declaredType} is a generic class and {@code resolvedType} is a * parameterization of that type, the returned binding will be for the resolved type */ // TODO(dpb): See if we can just pass one nongeneric/parameterized type. MembersInjectionBinding membersInjectionBinding(DeclaredType declaredType, Optional<TypeMirror> resolvedType) { // If the class this is injecting has some type arguments, resolve everything. if (!declaredType.getTypeArguments().isEmpty() && resolvedType.isPresent()) { DeclaredType resolved = asDeclared(resolvedType.get()); // Validate that we're resolving from the correct type. checkState(types.isSameType(types.erasure(resolved), types.erasure(declaredType)), "erased expected type: %s, erased actual type: %s", types.erasure(resolved), types.erasure(declaredType)); declaredType = resolved; } ImmutableSortedSet<InjectionSite> injectionSites = getInjectionSites(declaredType); ImmutableSet<DependencyRequest> dependencies = injectionSites.stream() .flatMap(injectionSite -> injectionSite.dependencies().stream()).collect(toImmutableSet()); Key key = keyFactory.forMembersInjectedType(declaredType); TypeElement typeElement = MoreElements.asType(declaredType.asElement()); return new AutoValue_MembersInjectionBinding(key, dependencies, typeElement, hasNonDefaultTypeParameters(typeElement, key.type(), types) ? Optional.of(membersInjectionBinding(asDeclared(typeElement.asType()), Optional.empty())) : Optional.empty(), injectionSites); } private final ElementVisitor<Optional<InjectionSite>, DeclaredType> injectionSiteVisitor = new ElementKindVisitor6<Optional<InjectionSite>, DeclaredType>( Optional.empty()) { @Override public Optional<InjectionSite> visitExecutableAsMethod(ExecutableElement e, DeclaredType type) { return Optional.of(injectionSiteForInjectMethod(e, type)); } @Override public Optional<InjectionSite> visitVariableAsField(VariableElement e, DeclaredType type) { return (isAnnotationPresent(e, Inject.class) && !e.getModifiers().contains(PRIVATE) && !e.getModifiers().contains(STATIC)) ? Optional.of(injectionSiteForInjectField(e, type)) : Optional.empty(); } }; private ImmutableSortedSet<InjectionSite> getInjectionSites(DeclaredType declaredType) { Set<InjectionSite> injectionSites = new HashSet<>(); List<TypeElement> ancestors = new ArrayList<>(); SetMultimap<String, ExecutableElement> overriddenMethodMap = LinkedHashMultimap.create(); for (Optional<DeclaredType> currentType = Optional.of(declaredType); currentType .isPresent(); currentType = types.nonObjectSuperclass(currentType.get())) { DeclaredType type = currentType.get(); ancestors.add(MoreElements.asType(type.asElement())); for (Element enclosedElement : type.asElement().getEnclosedElements()) { Optional<InjectionSite> maybeInjectionSite = injectionSiteVisitor.visit(enclosedElement, type); if (maybeInjectionSite.isPresent()) { InjectionSite injectionSite = maybeInjectionSite.get(); if (shouldBeInjected(injectionSite.element(), overriddenMethodMap)) { injectionSites.add(injectionSite); } if (injectionSite.kind().equals(InjectionSite.Kind.METHOD)) { ExecutableElement injectionSiteMethod = MoreElements.asExecutable(injectionSite.element()); overriddenMethodMap.put(injectionSiteMethod.getSimpleName().toString(), injectionSiteMethod); } } } } return ImmutableSortedSet.copyOf( // supertypes before subtypes Comparator .comparing((InjectionSite injectionSite) -> ancestors .indexOf(injectionSite.element().getEnclosingElement())) .reversed() // fields before methods .thenComparing(injectionSite -> injectionSite.element().getKind()) // then sort by whichever element comes first in the parent // this isn't necessary, but makes the processor nice and predictable .thenComparing(InjectionSite::element, DECLARATION_ORDER), injectionSites); } private boolean shouldBeInjected(Element injectionSite, SetMultimap<String, ExecutableElement> overriddenMethodMap) { if (!isAnnotationPresent(injectionSite, Inject.class) || injectionSite.getModifiers().contains(PRIVATE) || injectionSite.getModifiers().contains(STATIC)) { return false; } if (injectionSite.getKind().isField()) { // Inject all fields (self and ancestors) return true; } // For each method with the same name belonging to any descendant class, return false if any // method has already overridden the injectionSite method. To decrease the number of methods // that are checked, we store the already injected methods in a SetMultimap and only // check the methods with the same name. ExecutableElement injectionSiteMethod = MoreElements.asExecutable(injectionSite); TypeElement injectionSiteType = MoreElements.asType(injectionSite.getEnclosingElement()); for (ExecutableElement method : overriddenMethodMap.get(injectionSiteMethod.getSimpleName().toString())) { if (elements.overrides(method, injectionSiteMethod, injectionSiteType)) { return false; } } return true; } private InjectionSite injectionSiteForInjectMethod(ExecutableElement methodElement, DeclaredType containingType) { checkNotNull(methodElement); checkArgument(methodElement.getKind().equals(ElementKind.METHOD)); ExecutableType resolved = MoreTypes.asExecutable(types.asMemberOf(containingType, methodElement)); return new AutoValue_MembersInjectionBinding_InjectionSite(InjectionSite.Kind.METHOD, methodElement, dependencyRequestFactory.forRequiredResolvedVariables(methodElement.getParameters(), resolved.getParameterTypes())); } private InjectionSite injectionSiteForInjectField(VariableElement fieldElement, DeclaredType containingType) { checkNotNull(fieldElement); checkArgument(fieldElement.getKind().equals(ElementKind.FIELD)); checkArgument(isAnnotationPresent(fieldElement, Inject.class)); TypeMirror resolved = types.asMemberOf(containingType, fieldElement); return new AutoValue_MembersInjectionBinding_InjectionSite(InjectionSite.Kind.FIELD, fieldElement, ImmutableSet.of(dependencyRequestFactory.forRequiredResolvedVariable(fieldElement, resolved))); } }