/* * 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.MoreTypes.isTypeOf; 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.base.RequestKinds.extractKeyType; import static dagger.internal.codegen.base.RequestKinds.frameworkClass; import static dagger.internal.codegen.base.RequestKinds.getRequestKind; import static dagger.internal.codegen.binding.ConfigurationAnnotations.getNullableType; import static dagger.internal.codegen.langmodel.DaggerTypes.unwrapType; import static dagger.model.RequestKind.FUTURE; import static dagger.model.RequestKind.INSTANCE; import static dagger.model.RequestKind.MEMBERS_INJECTION; import static dagger.model.RequestKind.PRODUCER; import static dagger.model.RequestKind.PROVIDER; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.ListenableFuture; import dagger.Lazy; import dagger.internal.codegen.base.MapType; import dagger.internal.codegen.base.OptionalType; import dagger.model.DependencyRequest; import dagger.model.Key; import dagger.model.RequestKind; import java.util.List; import java.util.Optional; import javax.inject.Inject; import javax.inject.Provider; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeMirror; /** * Factory for {@link DependencyRequest}s. * *

Any factory method may throw {@link TypeNotPresentException} if a type is not available, which * may mean that the type will be generated in a later round of processing. */ public final class DependencyRequestFactory { private final KeyFactory keyFactory; private final InjectionAnnotations injectionAnnotations; @Inject DependencyRequestFactory(KeyFactory keyFactory, InjectionAnnotations injectionAnnotations) { this.keyFactory = keyFactory; this.injectionAnnotations = injectionAnnotations; } ImmutableSet forRequiredResolvedVariables( List variables, List resolvedTypes) { checkState(resolvedTypes.size() == variables.size()); ImmutableSet.Builder builder = ImmutableSet.builder(); for (int i = 0; i < variables.size(); i++) { builder.add(forRequiredResolvedVariable(variables.get(i), resolvedTypes.get(i))); } return builder.build(); } /** * Creates synthetic dependency requests for each individual multibinding contribution in {@code * multibindingContributions}. */ ImmutableSet forMultibindingContributions( Key multibindingKey, Iterable multibindingContributions) { ImmutableSet.Builder requests = ImmutableSet.builder(); for (ContributionBinding multibindingContribution : multibindingContributions) { requests.add(forMultibindingContribution(multibindingKey, multibindingContribution)); } return requests.build(); } /** Creates a synthetic dependency request for one individual {@code multibindingContribution}. */ private DependencyRequest forMultibindingContribution( Key multibindingKey, ContributionBinding multibindingContribution) { checkArgument( multibindingContribution.key().multibindingContributionIdentifier().isPresent(), "multibindingContribution's key must have a multibinding contribution identifier: %s", multibindingContribution); return DependencyRequest.builder() .kind(multibindingContributionRequestKind(multibindingKey, multibindingContribution)) .key(multibindingContribution.key()) .build(); } // TODO(b/28555349): support PROVIDER_OF_LAZY here too private static final ImmutableSet WRAPPING_MAP_VALUE_FRAMEWORK_TYPES = ImmutableSet.of(PROVIDER, PRODUCER); private RequestKind multibindingContributionRequestKind( Key multibindingKey, ContributionBinding multibindingContribution) { switch (multibindingContribution.contributionType()) { case MAP: MapType mapType = MapType.from(multibindingKey); for (RequestKind kind : WRAPPING_MAP_VALUE_FRAMEWORK_TYPES) { if (mapType.valuesAreTypeOf(frameworkClass(kind))) { return kind; } } // fall through case SET: case SET_VALUES: return INSTANCE; case UNIQUE: throw new IllegalArgumentException( "multibindingContribution must be a multibinding: " + multibindingContribution); } throw new AssertionError(multibindingContribution.toString()); } DependencyRequest forRequiredResolvedVariable( VariableElement variableElement, TypeMirror resolvedType) { checkNotNull(variableElement); checkNotNull(resolvedType); // Ban @Assisted parameters, they are not considered dependency requests. checkArgument(!AssistedInjectionAnnotations.isAssistedParameter(variableElement)); Optional qualifier = injectionAnnotations.getQualifier(variableElement); return newDependencyRequest(variableElement, resolvedType, qualifier); } public DependencyRequest forComponentProvisionMethod( ExecutableElement provisionMethod, ExecutableType provisionMethodType) { checkNotNull(provisionMethod); checkNotNull(provisionMethodType); checkArgument( provisionMethod.getParameters().isEmpty(), "Component provision methods must be empty: %s", provisionMethod); Optional qualifier = injectionAnnotations.getQualifier(provisionMethod); return newDependencyRequest(provisionMethod, provisionMethodType.getReturnType(), qualifier); } public DependencyRequest forComponentProductionMethod( ExecutableElement productionMethod, ExecutableType productionMethodType) { checkNotNull(productionMethod); checkNotNull(productionMethodType); checkArgument( productionMethod.getParameters().isEmpty(), "Component production methods must be empty: %s", productionMethod); TypeMirror type = productionMethodType.getReturnType(); Optional qualifier = injectionAnnotations.getQualifier(productionMethod); // Only a component production method can be a request for a ListenableFuture, so we // special-case it here. if (isTypeOf(ListenableFuture.class, type)) { return DependencyRequest.builder() .kind(FUTURE) .key(keyFactory.forQualifiedType(qualifier, unwrapType(type))) .requestElement(productionMethod) .build(); } else { return newDependencyRequest(productionMethod, type, qualifier); } } DependencyRequest forComponentMembersInjectionMethod( ExecutableElement membersInjectionMethod, ExecutableType membersInjectionMethodType) { checkNotNull(membersInjectionMethod); checkNotNull(membersInjectionMethodType); Optional qualifier = injectionAnnotations.getQualifier(membersInjectionMethod); checkArgument(!qualifier.isPresent()); TypeMirror membersInjectedType = getOnlyElement(membersInjectionMethodType.getParameterTypes()); return DependencyRequest.builder() .kind(MEMBERS_INJECTION) .key(keyFactory.forMembersInjectedType(membersInjectedType)) .requestElement(membersInjectionMethod) .build(); } DependencyRequest forProductionImplementationExecutor() { return DependencyRequest.builder() .kind(PROVIDER) .key(keyFactory.forProductionImplementationExecutor()) .build(); } DependencyRequest forProductionComponentMonitor() { return DependencyRequest.builder() .kind(PROVIDER) .key(keyFactory.forProductionComponentMonitor()) .build(); } /** * Returns a synthetic request for the present value of an optional binding generated from a * {@link dagger.BindsOptionalOf} declaration. */ DependencyRequest forSyntheticPresentOptionalBinding(Key requestKey, RequestKind kind) { Optional key = keyFactory.unwrapOptional(requestKey); checkArgument(key.isPresent(), "not a request for optional: %s", requestKey); return DependencyRequest.builder() .kind(kind) .key(key.get()) .isNullable( allowsNull(getRequestKind(OptionalType.from(requestKey).valueType()), Optional.empty())) .build(); } private DependencyRequest newDependencyRequest( Element requestElement, TypeMirror type, Optional qualifier) { RequestKind requestKind = getRequestKind(type); return DependencyRequest.builder() .kind(requestKind) .key(keyFactory.forQualifiedType(qualifier, extractKeyType(type))) .requestElement(requestElement) .isNullable(allowsNull(requestKind, getNullableType(requestElement))) .build(); } /** * Returns {@code true} if a given request element allows null values. {@link * RequestKind#INSTANCE} requests must be annotated with {@code @Nullable} in order to allow null * values. All other request kinds implicitly allow null values because they are are wrapped * inside {@link Provider}, {@link Lazy}, etc. */ private boolean allowsNull(RequestKind kind, Optional nullableType) { return nullableType.isPresent() || !kind.equals(INSTANCE); } }