| /* |
| * Copyright (C) 2015 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.writing; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static com.squareup.javapoet.MethodSpec.constructorBuilder; |
| import static com.squareup.javapoet.MethodSpec.methodBuilder; |
| import static com.squareup.javapoet.TypeSpec.classBuilder; |
| import static dagger.internal.codegen.javapoet.TypeNames.providerOf; |
| import static dagger.internal.codegen.writing.ComponentImplementation.TypeSpecKind.COMPONENT_PROVISION_FACTORY; |
| import static javax.lang.model.element.Modifier.FINAL; |
| import static javax.lang.model.element.Modifier.PRIVATE; |
| import static javax.lang.model.element.Modifier.PUBLIC; |
| import static javax.lang.model.element.Modifier.STATIC; |
| |
| import com.google.auto.common.MoreTypes; |
| import com.squareup.javapoet.ClassName; |
| import com.squareup.javapoet.CodeBlock; |
| import com.squareup.javapoet.MethodSpec; |
| import com.squareup.javapoet.TypeName; |
| import dagger.internal.codegen.binding.BindingGraph; |
| import dagger.internal.codegen.binding.ComponentRequirement; |
| import dagger.internal.codegen.binding.ContributionBinding; |
| import dagger.internal.codegen.binding.ProvisionBinding; |
| import dagger.internal.codegen.compileroption.CompilerOptions; |
| import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression; |
| import javax.lang.model.element.Element; |
| |
| /** |
| * A {@link javax.inject.Provider} creation expression for a provision method on a component's |
| * {@linkplain dagger.Component#dependencies()} dependency}. |
| */ |
| // TODO(dpb): Resolve with DependencyMethodProducerCreationExpression. |
| final class DependencyMethodProviderCreationExpression |
| implements FrameworkInstanceCreationExpression { |
| |
| private final ComponentImplementation componentImplementation; |
| private final ComponentRequirementExpressions componentRequirementExpressions; |
| private final CompilerOptions compilerOptions; |
| private final BindingGraph graph; |
| private final ContributionBinding binding; |
| |
| DependencyMethodProviderCreationExpression( |
| ContributionBinding binding, |
| ComponentImplementation componentImplementation, |
| ComponentRequirementExpressions componentRequirementExpressions, |
| CompilerOptions compilerOptions, |
| BindingGraph graph) { |
| this.binding = checkNotNull(binding); |
| this.componentImplementation = checkNotNull(componentImplementation); |
| this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions); |
| this.compilerOptions = checkNotNull(compilerOptions); |
| this.graph = checkNotNull(graph); |
| } |
| |
| @Override |
| public CodeBlock creationExpression() { |
| // TODO(sameb): The Provider.get() throws a very vague NPE. The stack trace doesn't |
| // help to figure out what the method or return type is. If we include a string |
| // of the return type or method name in the error message, that can defeat obfuscation. |
| // We can easily include the raw type (no generics) + annotation type (no values), |
| // using .class & String.format -- but that wouldn't be the whole story. |
| // What should we do? |
| CodeBlock invocation = |
| ComponentProvisionBindingExpression.maybeCheckForNull( |
| (ProvisionBinding) binding, |
| compilerOptions, |
| CodeBlock.of( |
| "$N.$N()", dependency().variableName(), provisionMethod().getSimpleName())); |
| ClassName dependencyClassName = ClassName.get(dependency().typeElement()); |
| TypeName keyType = TypeName.get(binding.key().type()); |
| MethodSpec.Builder getMethod = |
| methodBuilder("get") |
| .addAnnotation(Override.class) |
| .addModifiers(PUBLIC) |
| .returns(keyType) |
| .addStatement("return $L", invocation); |
| if (binding.nullableType().isPresent()) { |
| getMethod.addAnnotation(ClassName.get(MoreTypes.asTypeElement(binding.nullableType().get()))); |
| } |
| componentImplementation.addType( |
| COMPONENT_PROVISION_FACTORY, |
| classBuilder(factoryClassName()) |
| .addSuperinterface(providerOf(keyType)) |
| .addModifiers(PRIVATE, STATIC) |
| .addField(dependencyClassName, dependency().variableName(), PRIVATE, FINAL) |
| .addMethod( |
| constructorBuilder() |
| .addParameter(dependencyClassName, dependency().variableName()) |
| .addStatement("this.$1L = $1L", dependency().variableName()) |
| .build()) |
| .addMethod(getMethod.build()) |
| .build()); |
| return CodeBlock.of( |
| "new $T($L)", |
| factoryClassName(), |
| componentRequirementExpressions.getExpressionDuringInitialization( |
| dependency(), componentImplementation.name())); |
| } |
| |
| private ClassName factoryClassName() { |
| String factoryName = |
| ClassName.get(dependency().typeElement()).toString().replace('.', '_') |
| + "_" |
| + binding.bindingElement().get().getSimpleName(); |
| return componentImplementation.name().nestedClass(factoryName); |
| } |
| |
| private ComponentRequirement dependency() { |
| return graph.componentDescriptor().getDependencyThatDefinesMethod(provisionMethod()); |
| } |
| |
| private Element provisionMethod() { |
| return binding.bindingElement().get(); |
| } |
| } |