/* * Copyright (C) 2014 Google, Inc. * * 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 com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.auto.value.AutoValue; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.ListenableFuture; import dagger.Lazy; import dagger.MembersInjector; import dagger.Provides; import dagger.producers.Produced; import dagger.producers.Producer; import dagger.producers.internal.AbstractProducer; import java.util.List; 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.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; 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 javax.lang.model.type.TypeKind.DECLARED; import static javax.lang.model.util.ElementFilter.constructorsIn; /** * Represents a request for a key at an injection point. Parameters to {@link Inject} constructors * or {@link Provides} methods are examples of key requests. * * @author Gregory Kick * @since 2.0 */ // TODO(gak): Set bindings and the permutations thereof need to be addressed @AutoValue abstract class DependencyRequest { static final Function BINDING_KEY_FUNCTION = new Function() { @Override public BindingKey apply(DependencyRequest request) { return request.bindingKey(); } }; enum Kind { /** A default request for an instance. E.g.: {@code Blah} */ INSTANCE, /** A request for a {@link Provider}. E.g.: {@code Provider} */ PROVIDER, /** A request for a {@link Lazy}. E.g.: {@code Lazy} */ LAZY, /** A request for a {@link MembersInjector}. E.g.: {@code MembersInjector} */ MEMBERS_INJECTOR, /** A request for a {@link Producer}. E.g.: {@code Producer} */ PRODUCER, /** A request for a {@link Produced}. E.g.: {@code Produced} */ PRODUCED, /** * A request for a {@link ListenableFuture}. E.g.: {@code ListenableFuture}. * These can only be requested by component interfaces. */ FUTURE, } abstract Kind kind(); abstract Key key(); BindingKey bindingKey() { switch (kind()) { case INSTANCE: case LAZY: case PROVIDER: case PRODUCER: case PRODUCED: case FUTURE: return BindingKey.create(BindingKey.Kind.CONTRIBUTION, key()); case MEMBERS_INJECTOR: return BindingKey.create(BindingKey.Kind.MEMBERS_INJECTION, key()); default: throw new AssertionError(); } } abstract Element requestElement(); /** * Returns the possibly resolved type that contained the requesting element. For members injection * requests, this is the type itself. */ abstract DeclaredType enclosingType(); /** Returns true if this request allows null objects. */ abstract boolean isNullable(); /** * An optional name for this request when it's referred to in generated code. If absent, it will * use a name derived from {@link #requestElement}. */ abstract Optional overriddenVariableName(); /** * 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. */ static final class Factory { private final Elements elements; private final Key.Factory keyFactory; Factory(Elements elements, Key.Factory keyFactory) { this.elements = elements; this.keyFactory = keyFactory; } ImmutableSet forRequiredResolvedVariables(DeclaredType container, 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(container, variables.get(i), resolvedTypes.get(i))); } return builder.build(); } ImmutableSet forRequiredVariables( List variables) { return FluentIterable.from(variables) .transform( new Function() { @Override public DependencyRequest apply(VariableElement input) { return forRequiredVariable(input); } }) .toSet(); } /** * Creates a implicit {@link DependencyRequest} for {@code mapOfFactoryKey}, which will be used * to satisfy the {@code mapOfValueRequest}. * * @param mapOfValueRequest a request for {@code Map} * @param mapOfFactoryKey a key equivalent to {@code mapOfValueRequest}'s key, whose type is * {@code Map>} or {@code Map>} */ DependencyRequest forImplicitMapBinding( DependencyRequest mapOfValueRequest, Key mapOfFactoryKey) { checkNotNull(mapOfValueRequest); return new AutoValue_DependencyRequest( Kind.PROVIDER, mapOfFactoryKey, mapOfValueRequest.requestElement(), mapOfValueRequest.enclosingType(), false /* doesn't allow null */, Optional.absent()); } DependencyRequest forRequiredVariable(VariableElement variableElement) { return forRequiredVariable(variableElement, Optional.absent()); } DependencyRequest forRequiredVariable(VariableElement variableElement, Optional name) { checkNotNull(variableElement); TypeMirror type = variableElement.asType(); Optional qualifier = InjectionAnnotations.getQualifier(variableElement); return newDependencyRequest( variableElement, type, qualifier, getEnclosingType(variableElement), name); } DependencyRequest forRequiredResolvedVariable(DeclaredType container, VariableElement variableElement, TypeMirror resolvedType) { checkNotNull(variableElement); checkNotNull(resolvedType); Optional qualifier = InjectionAnnotations.getQualifier(variableElement); return newDependencyRequest( variableElement, resolvedType, qualifier, container, Optional.absent()); } 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, getEnclosingType(provisionMethod), Optional.absent()); } 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); DeclaredType container = getEnclosingType(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 new AutoValue_DependencyRequest( Kind.FUTURE, keyFactory.forQualifiedType( qualifier, Iterables.getOnlyElement(((DeclaredType) type).getTypeArguments())), productionMethod, container, false /* doesn't allow null */, Optional.absent()); } else { return newDependencyRequest( productionMethod, type, qualifier, container, Optional.absent()); } } DependencyRequest forComponentMembersInjectionMethod(ExecutableElement membersInjectionMethod, ExecutableType membersInjectionMethodType) { checkNotNull(membersInjectionMethod); checkNotNull(membersInjectionMethodType); Optional qualifier = InjectionAnnotations.getQualifier(membersInjectionMethod); checkArgument(!qualifier.isPresent()); TypeMirror returnType = membersInjectionMethodType.getReturnType(); if (returnType.getKind().equals(DECLARED) && MoreTypes.isTypeOf(MembersInjector.class, returnType)) { return new AutoValue_DependencyRequest( Kind.MEMBERS_INJECTOR, keyFactory.forMembersInjectedType( Iterables.getOnlyElement(((DeclaredType) returnType).getTypeArguments())), membersInjectionMethod, getEnclosingType(membersInjectionMethod), false /* doesn't allow null */, Optional.absent()); } else { return new AutoValue_DependencyRequest( Kind.MEMBERS_INJECTOR, keyFactory.forMembersInjectedType( Iterables.getOnlyElement(membersInjectionMethodType.getParameterTypes())), membersInjectionMethod, getEnclosingType(membersInjectionMethod), false /* doesn't allow null */, Optional.absent()); } } DependencyRequest forMembersInjectedType(DeclaredType type) { return new AutoValue_DependencyRequest( Kind.MEMBERS_INJECTOR, keyFactory.forMembersInjectedType(type), type.asElement(), type, false /* doesn't allow null */, Optional.absent()); } DependencyRequest forProductionComponentMonitorProvider() { TypeElement element = elements.getTypeElement(AbstractProducer.class.getCanonicalName()); for (ExecutableElement constructor : constructorsIn(element.getEnclosedElements())) { if (constructor.getParameters().size() == 2) { // the 2-arg constructor has the appropriate dependency as its first arg return forRequiredVariable(constructor.getParameters().get(0), Optional.of("monitor")); } } throw new AssertionError("expected 2-arg constructor in AbstractProducer"); } private DependencyRequest newDependencyRequest( Element requestElement, TypeMirror type, Optional qualifier, DeclaredType container, Optional name) { KindAndType kindAndType = extractKindAndType(type); if (kindAndType.kind().equals(Kind.MEMBERS_INJECTOR)) { checkArgument(!qualifier.isPresent()); } // Only instance types can be non-null -- all other requests are wrapped // inside something (e.g, Provider, Lazy, etc..). // TODO(sameb): should Produced/Producer always require non-nullable? boolean allowsNull = !kindAndType.kind().equals(Kind.INSTANCE) || ConfigurationAnnotations.getNullableType(requestElement).isPresent(); return new AutoValue_DependencyRequest( kindAndType.kind(), keyFactory.forQualifiedType(qualifier, kindAndType.type()), requestElement, container, allowsNull, name); } @AutoValue static abstract class KindAndType { abstract Kind kind(); abstract TypeMirror type(); } /** * Extracts the correct requesting type & kind out a request type. For example, if a user * requests {@code Provider}, this will return ({@link Kind#PROVIDER}, {@code Foo}). * * @throws TypeNotPresentException if {@code type}'s kind is {@link TypeKind#ERROR}, which may * mean that the type will be generated in a later round of processing */ static KindAndType extractKindAndType(TypeMirror type) { if (type.getKind().equals(TypeKind.ERROR)) { throw new TypeNotPresentException(type.toString(), null); } // We must check TYPEVAR explicitly before the below checks because calling // isTypeOf(..) on a TYPEVAR throws an exception (because it can't be // represented as a Class). if (type.getKind().equals(TypeKind.TYPEVAR)) { return new AutoValue_DependencyRequest_Factory_KindAndType(Kind.INSTANCE, type); } else if (isTypeOf(Provider.class, type)) { return new AutoValue_DependencyRequest_Factory_KindAndType(Kind.PROVIDER, Iterables.getOnlyElement(((DeclaredType) type).getTypeArguments())); } else if (isTypeOf(Lazy.class, type)) { return new AutoValue_DependencyRequest_Factory_KindAndType(Kind.LAZY, Iterables.getOnlyElement(((DeclaredType) type).getTypeArguments())); } else if (isTypeOf(MembersInjector.class, type)) { return new AutoValue_DependencyRequest_Factory_KindAndType(Kind.MEMBERS_INJECTOR, Iterables.getOnlyElement(((DeclaredType) type).getTypeArguments())); } else if (isTypeOf(Producer.class, type)) { return new AutoValue_DependencyRequest_Factory_KindAndType(Kind.PRODUCER, Iterables.getOnlyElement(((DeclaredType) type).getTypeArguments())); } else if (isTypeOf(Produced.class, type)) { return new AutoValue_DependencyRequest_Factory_KindAndType(Kind.PRODUCED, Iterables.getOnlyElement(((DeclaredType) type).getTypeArguments())); } else { return new AutoValue_DependencyRequest_Factory_KindAndType(Kind.INSTANCE, type); } } static DeclaredType getEnclosingType(Element element) { while (!MoreElements.isType(element)) { element = element.getEnclosingElement(); } return MoreTypes.asDeclared(element.asType()); } } }