/* * 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.Optional; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import dagger.Provides; import javax.inject.Inject; 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.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 javax.lang.model.util.Types; 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 dagger.internal.codegen.InjectionAnnotations.getQualifier; import static dagger.internal.codegen.Scope.scopeOf; import static javax.lang.model.element.ElementKind.CONSTRUCTOR; import static javax.lang.model.element.ElementKind.FIELD; import static javax.lang.model.element.ElementKind.METHOD; /** * A value object representing the mechanism by which a {@link Key} can be provided. New instances * should be created using an instance of the {@link Factory}. * * @author Gregory Kick * @since 2.0 */ @AutoValue abstract class ProvisionBinding extends ContributionBinding { @Override Binding.Type bindingType() { return Binding.Type.PROVISION; } @Override abstract Scope scope(); static final class Factory { private final Elements elements; private final Types types; private final Key.Factory keyFactory; private final DependencyRequest.Factory dependencyRequestFactory; Factory(Elements elements, Types types, Key.Factory keyFactory, DependencyRequest.Factory dependencyRequestFactory) { this.elements = elements; this.types = types; this.keyFactory = keyFactory; this.dependencyRequestFactory = dependencyRequestFactory; } /** Returns an unresolved version of this binding. */ ProvisionBinding unresolve(ProvisionBinding binding) { checkState(binding.hasNonDefaultTypeParameters()); return forInjectConstructor((ExecutableElement) binding.bindingElement(), Optional.<TypeMirror>absent()); } /** * Returns a ProvisionBinding for the given element. If {@code resolvedType} is present, this * will return a resolved binding, with the key & type resolved to the given type (using * {@link Types#asMemberOf(DeclaredType, Element)}). */ ProvisionBinding forInjectConstructor(ExecutableElement constructorElement, Optional<TypeMirror> resolvedType) { checkNotNull(constructorElement); checkArgument(constructorElement.getKind().equals(CONSTRUCTOR)); checkArgument(isAnnotationPresent(constructorElement, Inject.class)); checkArgument(!getQualifier(constructorElement).isPresent()); ExecutableType cxtorType = MoreTypes.asExecutable(constructorElement.asType()); DeclaredType enclosingCxtorType = MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType()); // If the class this is constructing has some type arguments, resolve everything. if (!enclosingCxtorType.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(enclosingCxtorType)), "erased expected type: %s, erased actual type: %s", types.erasure(resolved), types.erasure(enclosingCxtorType)); cxtorType = MoreTypes.asExecutable(types.asMemberOf(resolved, constructorElement)); enclosingCxtorType = resolved; } Key key = keyFactory.forInjectConstructorWithResolvedType(enclosingCxtorType); checkArgument(!key.qualifier().isPresent()); ImmutableSet<DependencyRequest> dependencies = dependencyRequestFactory.forRequiredResolvedVariables(enclosingCxtorType, constructorElement.getParameters(), cxtorType.getParameterTypes()); Optional<DependencyRequest> membersInjectionRequest = membersInjectionRequest(enclosingCxtorType); Scope scope = Scope.scopeOf(constructorElement.getEnclosingElement()); TypeElement bindingTypeElement = MoreElements.asType(constructorElement.getEnclosingElement()); return new AutoValue_ProvisionBinding( key, constructorElement, dependencies, findBindingPackage(key), hasNonDefaultTypeParameters(bindingTypeElement, key.type(), types), Optional.<DeclaredType>absent(), Optional.<TypeElement>absent(), membersInjectionRequest, Kind.INJECTION, Provides.Type.UNIQUE, scope); } private static final ImmutableSet<ElementKind> MEMBER_KINDS = Sets.immutableEnumSet(METHOD, FIELD); private Optional<DependencyRequest> membersInjectionRequest(DeclaredType type) { TypeElement typeElement = MoreElements.asType(type.asElement()); if (!types.isSameType(elements.getTypeElement(Object.class.getCanonicalName()).asType(), typeElement.getSuperclass())) { return Optional.of(dependencyRequestFactory.forMembersInjectedType(type)); } for (Element enclosedElement : typeElement.getEnclosedElements()) { if (MEMBER_KINDS.contains(enclosedElement.getKind()) && (isAnnotationPresent(enclosedElement, Inject.class))) { return Optional.of(dependencyRequestFactory.forMembersInjectedType(type)); } } return Optional.absent(); } ProvisionBinding forProvidesMethod(ExecutableElement providesMethod, TypeMirror contributedBy) { checkNotNull(providesMethod); checkArgument(providesMethod.getKind().equals(METHOD)); checkArgument(contributedBy.getKind().equals(TypeKind.DECLARED)); Provides providesAnnotation = providesMethod.getAnnotation(Provides.class); checkArgument(providesAnnotation != null); DeclaredType declaredContainer = MoreTypes.asDeclared(contributedBy); ExecutableType resolvedMethod = MoreTypes.asExecutable(types.asMemberOf(declaredContainer, providesMethod)); Key key = keyFactory.forProvidesMethod(resolvedMethod, providesMethod); ImmutableSet<DependencyRequest> dependencies = dependencyRequestFactory.forRequiredResolvedVariables( declaredContainer, providesMethod.getParameters(), resolvedMethod.getParameterTypes()); Scope scope = Scope.scopeOf(providesMethod); return new AutoValue_ProvisionBinding( key, providesMethod, dependencies, findBindingPackage(key), false /* no non-default parameter types */, ConfigurationAnnotations.getNullableType(providesMethod), Optional.of(MoreTypes.asTypeElement(declaredContainer)), Optional.<DependencyRequest>absent(), Kind.PROVISION, providesAnnotation.type(), scope); } ProvisionBinding implicitMapOfProviderBinding(DependencyRequest mapOfValueRequest) { checkNotNull(mapOfValueRequest); Optional<Key> implicitMapOfProviderKey = keyFactory.implicitMapProviderKeyFrom(mapOfValueRequest.key()); checkArgument( implicitMapOfProviderKey.isPresent(), "%s is not a request for Map<K, V>", mapOfValueRequest); DependencyRequest implicitMapOfProviderRequest = dependencyRequestFactory.forImplicitMapBinding( mapOfValueRequest, implicitMapOfProviderKey.get()); return new AutoValue_ProvisionBinding( mapOfValueRequest.key(), implicitMapOfProviderRequest.requestElement(), ImmutableSet.of(implicitMapOfProviderRequest), findBindingPackage(mapOfValueRequest.key()), false /* no non-default parameter types */, Optional.<DeclaredType>absent(), Optional.<TypeElement>absent(), Optional.<DependencyRequest>absent(), Kind.SYNTHETIC, Provides.Type.MAP, scopeOf(implicitMapOfProviderRequest.requestElement())); } ProvisionBinding forComponent(TypeElement componentDefinitionType) { checkNotNull(componentDefinitionType); return new AutoValue_ProvisionBinding( keyFactory.forComponent(componentDefinitionType.asType()), componentDefinitionType, ImmutableSet.<DependencyRequest>of(), Optional.<String>absent(), false /* no non-default parameter types */, Optional.<DeclaredType>absent(), Optional.<TypeElement>absent(), Optional.<DependencyRequest>absent(), Kind.COMPONENT, Provides.Type.UNIQUE, Scope.unscoped()); } ProvisionBinding forComponentMethod(ExecutableElement componentMethod) { checkNotNull(componentMethod); checkArgument(componentMethod.getKind().equals(METHOD)); checkArgument(componentMethod.getParameters().isEmpty()); Scope scope = Scope.scopeOf(componentMethod); return new AutoValue_ProvisionBinding( keyFactory.forComponentMethod(componentMethod), componentMethod, ImmutableSet.<DependencyRequest>of(), Optional.<String>absent(), false /* no non-default parameter types */, ConfigurationAnnotations.getNullableType(componentMethod), Optional.<TypeElement>absent(), Optional.<DependencyRequest>absent(), Kind.COMPONENT_PROVISION, Provides.Type.UNIQUE, scope); } ProvisionBinding forSubcomponentBuilderMethod( ExecutableElement subcomponentBuilderMethod, TypeElement contributedBy) { checkNotNull(subcomponentBuilderMethod); checkArgument(subcomponentBuilderMethod.getKind().equals(METHOD)); checkArgument(subcomponentBuilderMethod.getParameters().isEmpty()); DeclaredType declaredContainer = asDeclared(contributedBy.asType()); return new AutoValue_ProvisionBinding( keyFactory.forSubcomponentBuilderMethod(subcomponentBuilderMethod, declaredContainer), subcomponentBuilderMethod, ImmutableSet.<DependencyRequest>of(), Optional.<String>absent(), false /* no non-default parameter types */, Optional.<DeclaredType>absent(), Optional.of(contributedBy), Optional.<DependencyRequest>absent(), Kind.SUBCOMPONENT_BUILDER, Provides.Type.UNIQUE, Scope.unscoped()); } } }