Java tutorial
/* * Copyright (C) 2016 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.asExecutable; import static com.google.common.base.Preconditions.checkArgument; import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom; import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock; import static dagger.internal.codegen.InjectionMethods.ProvisionMethod.requiresInjectionMethod; import static dagger.internal.codegen.TypeNames.rawTypeName; import com.google.auto.common.MoreTypes; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeName; import dagger.internal.codegen.InjectionMethods.ProvisionMethod; import dagger.model.DependencyRequest; import java.util.Optional; import java.util.function.Function; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; /** * A binding expression that invokes methods or constructors directly (without attempting to scope) * {@link dagger.model.RequestKind#INSTANCE} requests. */ final class SimpleMethodBindingExpression extends SimpleInvocationBindingExpression { private final CompilerOptions compilerOptions; private final ProvisionBinding provisionBinding; private final ComponentBindingExpressions componentBindingExpressions; private final MembersInjectionMethods membersInjectionMethods; private final ComponentRequirementExpressions componentRequirementExpressions; private final DaggerTypes types; private final DaggerElements elements; SimpleMethodBindingExpression(ResolvedBindings resolvedBindings, CompilerOptions compilerOptions, ComponentBindingExpressions componentBindingExpressions, MembersInjectionMethods membersInjectionMethods, ComponentRequirementExpressions componentRequirementExpressions, DaggerTypes types, DaggerElements elements) { super(resolvedBindings); this.compilerOptions = compilerOptions; this.provisionBinding = (ProvisionBinding) resolvedBindings.contributionBinding(); checkArgument(provisionBinding.implicitDependencies().isEmpty(), "framework deps are not currently supported"); checkArgument(provisionBinding.bindingElement().isPresent()); this.componentBindingExpressions = componentBindingExpressions; this.membersInjectionMethods = membersInjectionMethods; this.componentRequirementExpressions = componentRequirementExpressions; this.types = types; this.elements = elements; } @Override Expression getDependencyExpression(ClassName requestingClass) { ImmutableMap<DependencyRequest, Expression> arguments = ImmutableMap.copyOf(Maps .asMap(provisionBinding.dependencies(), request -> dependencyArgument(request, requestingClass))); Function<DependencyRequest, CodeBlock> argumentsFunction = request -> arguments.get(request).codeBlock(); return requiresInjectionMethod(provisionBinding, arguments.values().asList(), compilerOptions, requestingClass.packageName(), types) ? invokeInjectionMethod(argumentsFunction, requestingClass) : invokeMethod(argumentsFunction, requestingClass); } private Expression invokeMethod(Function<DependencyRequest, CodeBlock> argumentsFunction, ClassName requestingClass) { // TODO(dpb): align this with the contents of InlineMethods.create CodeBlock arguments = provisionBinding.dependencies().stream().map(argumentsFunction) .collect(toParametersCodeBlock()); ExecutableElement method = asExecutable(provisionBinding.bindingElement().get()); CodeBlock invocation; switch (method.getKind()) { case CONSTRUCTOR: invocation = CodeBlock.of("new $T($L)", constructorTypeName(requestingClass), arguments); break; case METHOD: CodeBlock module = moduleReference(requestingClass) .orElse(CodeBlock.of("$T", provisionBinding.bindingTypeElement().get())); invocation = CodeBlock.of("$L.$L($L)", module, method.getSimpleName(), arguments); break; default: throw new IllegalStateException(); } return Expression.create(simpleMethodReturnType(), invocation); } private TypeName constructorTypeName(ClassName requestingClass) { DeclaredType type = MoreTypes.asDeclared(provisionBinding.key().type()); TypeName typeName = TypeName.get(type); if (type.getTypeArguments().stream() .allMatch(t -> isTypeAccessibleFrom(t, requestingClass.packageName()))) { return typeName; } return rawTypeName(typeName); } private Expression invokeInjectionMethod(Function<DependencyRequest, CodeBlock> argumentsFunction, ClassName requestingClass) { return injectMembers(ProvisionMethod.invoke(provisionBinding, argumentsFunction, requestingClass, moduleReference(requestingClass), compilerOptions, elements)); } private Expression dependencyArgument(DependencyRequest dependency, ClassName requestingClass) { return componentBindingExpressions.getDependencyArgumentExpression(dependency, requestingClass); } private Expression injectMembers(CodeBlock instance) { if (provisionBinding.injectionSites().isEmpty()) { return Expression.create(simpleMethodReturnType(), instance); } // Java 7 type inference can't figure out that instance in // injectParameterized(Parameterized_Factory.newParameterized()) is Parameterized<T> and not // Parameterized<Object> if (!MoreTypes.asDeclared(provisionBinding.key().type()).getTypeArguments().isEmpty()) { TypeName keyType = TypeName.get(provisionBinding.key().type()); instance = CodeBlock.of("($T) ($T) $L", keyType, rawTypeName(keyType), instance); } MethodSpec membersInjectionMethod = membersInjectionMethods.getOrCreate(provisionBinding.key()); TypeMirror returnType = membersInjectionMethod.returnType.equals(TypeName.OBJECT) ? elements.getTypeElement(Object.class).asType() : provisionBinding.key().type(); return Expression.create(returnType, CodeBlock.of("$N($L)", membersInjectionMethod, instance)); } private Optional<CodeBlock> moduleReference(ClassName requestingClass) { return provisionBinding.requiresModuleInstance() ? provisionBinding.contributingModule().map(Element::asType).map(ComponentRequirement::forModule) .map(module -> componentRequirementExpressions.getExpression(module, requestingClass)) : Optional.empty(); } private TypeMirror simpleMethodReturnType() { return provisionBinding.contributedPrimitiveType().orElse(provisionBinding.key().type()); } }