Java tutorial
/* * 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; import static com.google.common.base.CaseFormat.LOWER_CAMEL; import static com.google.common.base.CaseFormat.UPPER_CAMEL; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Verify.verify; import static com.google.common.collect.Iterables.getOnlyElement; import static com.squareup.javapoet.MethodSpec.constructorBuilder; import static com.squareup.javapoet.MethodSpec.methodBuilder; import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder; import static com.squareup.javapoet.TypeSpec.classBuilder; import static dagger.internal.codegen.AbstractComponentWriter.InitializationState.DELEGATED; import static dagger.internal.codegen.AbstractComponentWriter.InitializationState.INITIALIZED; import static dagger.internal.codegen.AbstractComponentWriter.InitializationState.UNINITIALIZED; import static dagger.internal.codegen.AnnotationSpecs.Suppression.RAWTYPES; import static dagger.internal.codegen.AnnotationSpecs.Suppression.UNCHECKED; import static dagger.internal.codegen.BindingKey.contribution; import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock; import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE; import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION; import static dagger.internal.codegen.ErrorMessages.CANNOT_RETURN_NULL_FROM_NON_NULLABLE_COMPONENT_METHOD; import static dagger.internal.codegen.MapKeys.getMapKeyExpression; import static dagger.internal.codegen.MemberSelect.emptyFrameworkMapFactory; import static dagger.internal.codegen.MemberSelect.emptySetProvider; import static dagger.internal.codegen.MemberSelect.localField; import static dagger.internal.codegen.MemberSelect.noOpMembersInjector; import static dagger.internal.codegen.MemberSelect.staticMethod; import static dagger.internal.codegen.Util.*; import static dagger.internal.codegen.MoreAnnotationMirrors.getTypeValue; import static dagger.internal.codegen.Scope.reusableScope; import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames; import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding; import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType; import static dagger.internal.codegen.TypeNames.DELEGATE_FACTORY; import static dagger.internal.codegen.TypeNames.DOUBLE_CHECK; import static dagger.internal.codegen.TypeNames.INSTANCE_FACTORY; import static dagger.internal.codegen.TypeNames.LISTENABLE_FUTURE; import static dagger.internal.codegen.TypeNames.MAP_FACTORY; import static dagger.internal.codegen.TypeNames.MAP_OF_PRODUCED_PRODUCER; import static dagger.internal.codegen.TypeNames.MAP_OF_PRODUCER_PRODUCER; import static dagger.internal.codegen.TypeNames.MAP_PRODUCER; import static dagger.internal.codegen.TypeNames.MAP_PROVIDER_FACTORY; import static dagger.internal.codegen.TypeNames.MEMBERS_INJECTORS; import static dagger.internal.codegen.TypeNames.PRODUCER; import static dagger.internal.codegen.TypeNames.REFERENCE_RELEASING_PROVIDER; import static dagger.internal.codegen.TypeNames.REFERENCE_RELEASING_PROVIDER_MANAGER; import static dagger.internal.codegen.TypeNames.SET_FACTORY; import static dagger.internal.codegen.TypeNames.SET_OF_PRODUCED_PRODUCER; import static dagger.internal.codegen.TypeNames.SET_PRODUCER; import static dagger.internal.codegen.TypeNames.SINGLE_CHECK; import static dagger.internal.codegen.TypeNames.TYPED_RELEASABLE_REFERENCE_MANAGER_DECORATOR; import static dagger.internal.codegen.TypeNames.providerOf; import static dagger.internal.codegen.TypeSpecs.addSupertype; import static javax.lang.model.element.Modifier.ABSTRACT; 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 static javax.lang.model.type.TypeKind.DECLARED; import static javax.lang.model.type.TypeKind.VOID; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.common.base.Joiner; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeVariableName; import dagger.internal.DelegateFactory; import dagger.internal.InstanceFactory; import dagger.internal.MapFactory; import dagger.internal.MapProviderFactory; import dagger.internal.Preconditions; import dagger.internal.SetFactory; import dagger.internal.TypedReleasableReferenceManagerDecorator; import dagger.internal.codegen.ComponentDescriptor.BuilderRequirementMethod; import dagger.internal.codegen.ComponentDescriptor.BuilderSpec; import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor; import dagger.producers.Produced; import dagger.producers.Producer; import dagger.producers.internal.MapOfProducerProducer; import dagger.producers.internal.MapProducer; import dagger.producers.internal.SetOfProducedProducer; import dagger.producers.internal.SetProducer; import dagger.releasablereferences.CanReleaseReferences; import dagger.releasablereferences.ForReleasableReferences; import dagger.releasablereferences.ReleasableReferenceManager; import dagger.releasablereferences.TypedReleasableReferenceManager; import java.util.Arrays; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.*; import java.util.stream.Collectors; import javax.inject.Provider; import javax.lang.model.element.*; 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 javax.tools.Diagnostic; /** Creates the implementation class for a component or subcomponent. */ abstract class AbstractComponentWriter implements HasBindingMembers { private static final String NOOP_BUILDER_METHOD_JAVADOC = "This module is declared, but an instance is not used in the component. This method is a " + "no-op. For more, see https://google.github.io/dagger/unused-modules.\n"; // TODO(dpb): Make all these fields private after refactoring is complete. protected final Elements elements; protected final Types types; protected final Key.Factory keyFactory; protected final CompilerOptions compilerOptions; private boolean forTests; protected final ClassName name; protected final BindingGraph graph; protected final ImmutableMap<ComponentDescriptor, String> subcomponentNames; private final Map<BindingKey, InitializationState> initializationStates = new HashMap<>(); protected final TypeSpec.Builder component; private final UniqueNameSet componentFieldNames = new UniqueNameSet(); private final Map<BindingKey, MemberSelect> memberSelects = new HashMap<>(); private final Map<BindingKey, MemberSelect> producerFromProviderMemberSelects = new HashMap<>(); private final RequestFulfillmentRegistry requestFulfillmentRegistry; protected final MethodSpec.Builder constructor = constructorBuilder(); protected Optional<ClassName> builderName = Optional.empty(); private Map<Key, String> delegateFieldNames = new HashMap<>(); private final OptionalFactories optionalFactories; private boolean done; /** * For each component requirement, the builder field. This map is empty for subcomponents that do * not use a builder. */ private ImmutableMap<ComponentRequirement, FieldSpec> builderFields = ImmutableMap.of(); /** * For each component requirement, the member select for the component field that holds it. * * <p>Fields are written for all requirements for subcomponents that do not use a builder, and for * any requirement that is reused from a subcomponent of this component. */ protected final Map<ComponentRequirement, MemberSelect> componentContributionFields = Maps.newHashMap(); /** * The member-selects for {@link dagger.internal.ReferenceReleasingProviderManager} fields, * indexed by their {@link CanReleaseReferences @CanReleaseReferences} scope. */ private ImmutableMap<Scope, MemberSelect> referenceReleasingProviderManagerFields; private List<MethodSpec> initializationMethods; AbstractComponentWriter(Types types, Elements elements, Key.Factory keyFactory, CompilerOptions compilerOptions, ClassName name, BindingGraph graph, ImmutableMap<ComponentDescriptor, String> subcomponentNames, OptionalFactories optionalFactories, boolean forTests) { this.types = types; this.elements = elements; this.keyFactory = keyFactory; this.compilerOptions = compilerOptions; this.forTests = forTests; this.component = classBuilder(name); this.name = name; this.graph = graph; this.subcomponentNames = subcomponentNames; this.optionalFactories = optionalFactories; this.requestFulfillmentRegistry = new RequestFulfillmentRegistry(graph.resolvedBindings(), this); } protected AbstractComponentWriter(AbstractComponentWriter parent, ClassName name, BindingGraph graph, boolean forTests) { this(parent.types, parent.elements, parent.keyFactory, parent.compilerOptions, name, graph, parent.subcomponentNames, parent.optionalFactories, forTests); } protected final ClassName componentDefinitionTypeName() { return ClassName.get(graph.componentType()); } /** * Returns an expression that evaluates to an instance of the requirement, looking for either a * builder field or a component field. */ private CodeBlock getComponentContributionExpression(ComponentRequirement componentRequirement) { if (builderFields.containsKey(componentRequirement)) { return CodeBlock.of("builder.$N", builderFields.get(componentRequirement)); } else { Optional<CodeBlock> codeBlock = getOrCreateComponentRequirementFieldExpression(componentRequirement); checkState(codeBlock.isPresent(), "no builder or component field for %s", componentRequirement); return codeBlock.get(); } } /** * Returns an expression for a component requirement field. Adds a field the first time one is * requested for a requirement if this component's builder has a field for it. */ protected Optional<CodeBlock> getOrCreateComponentRequirementFieldExpression( ComponentRequirement componentRequirement) { MemberSelect fieldSelect = componentContributionFields.get(componentRequirement); if (fieldSelect == null) { if (!builderFields.containsKey(componentRequirement)) { return Optional.empty(); } FieldSpec componentField = componentField(TypeName.get(componentRequirement.type()), simpleVariableName(componentRequirement.typeElement())).addModifiers(PRIVATE, FINAL).build(); component.addField(componentField); constructor.addCode("this.$N = builder.$N;", componentField, builderFields.get(componentRequirement)); fieldSelect = localField(name, componentField.name); componentContributionFields.put(componentRequirement, fieldSelect); } return Optional.of(fieldSelect.getExpressionFor(name)); } /** * Creates a {@link FieldSpec.Builder} with a unique name based off of {@code name}. */ protected final FieldSpec.Builder componentField(TypeName type, String name) { return FieldSpec.builder(type, componentFieldNames.getUniqueName(name)); } protected CodeBlock getMemberSelectExpression(BindingKey key) { return getMemberSelect(key).getExpressionFor(name); } @Override public MemberSelect getMemberSelect(BindingKey key) { return memberSelects.get(key); } /** * Returns the initialization state of the factory field for a binding key in this component. */ protected InitializationState getInitializationState(BindingKey bindingKey) { return initializationStates.containsKey(bindingKey) ? initializationStates.get(bindingKey) : UNINITIALIZED; } private void setInitializationState(BindingKey bindingKey, InitializationState state) { initializationStates.put(bindingKey, state); } /** * The member-select expression for the {@link dagger.internal.ReferenceReleasingProviderManager} * object for a scope. */ protected CodeBlock getReferenceReleasingProviderManagerExpression(Scope scope) { return referenceReleasingProviderManagerFields.get(scope).getExpressionFor(name); } /** * Constructs a {@link TypeSpec.Builder} that models the {@link BindingGraph} for this component. * This is only intended to be called once (and will throw on successive invocations). If the * component must be regenerated, use a new instance. */ final TypeSpec.Builder write() { checkState(!done, "ComponentWriter has already been generated."); decorateComponent(); addBuilder(); addFactoryMethods(); addReferenceReleasingProviderManagerFields(); addFrameworkFields(); postAddBuildMethod(); initializeFrameworkTypes(); implementInterfaceMethods(); addSubcomponents(); if (forTests) { implementProvisionMethodsForDebug(); } component.addMethod(constructor.build()); if (graph.componentDescriptor().kind().isTopLevel()) { optionalFactories.addMembers(component); } done = true; return component; } private void implementProvisionMethodsForDebug() { List<ResolvedBindings> result = this.graph.resolvedBindings().values().stream() .filter(resolvedBindings -> !resolvedBindings.ownedContributionBindings().isEmpty()) .filter(resolvedBindings -> resolvedBindings.contributionBinding() .bindingType() == BindingType.PROVISION) .filter(resolvedBindings -> Util.bindingCanBeProvidedInTest(resolvedBindings.contributionBinding())) .filter(resolvedBindings -> !resolvedBindings.isMultibindingContribution()) .filter(resolvedBindings -> !resolvedBindings.contributionBinding().contributedType().toString() .contains("DispatchingAndroidInjector")) .filter(resolvedBindings -> !resolvedBindings.contributionBinding().contributedType().toString() .equals(graph.application().get().toString())) .collect(Collectors.toList()); result.forEach(this::implementProvisionMethodForDebug); final List<ResolvedBindings> subcomponentBuilderBindings = this.graph.resolvedBindings().values().stream() .filter(resolvedBindings -> !resolvedBindings.ownedContributionBindings().isEmpty()) .filter(resolvedBindings -> resolvedBindings.contributionBinding() .bindingType() == BindingType.PROVISION) .filter(resolvedBindings -> resolvedBindings.contributionBinding() .bindingKind() == ContributionBinding.Kind.SUBCOMPONENT_BUILDER) .collect(Collectors.toList()); subcomponentBuilderBindings.forEach(this::implementSubcomponentBuilderBindingForDebug); } private void implementSubcomponentBuilderBindingForDebug(ResolvedBindings resolvedBindings) { final ComponentDescriptor subcomponentDescriptor = graph.componentDescriptor().subcomponentsByBuilderType() .get(MoreTypes.asTypeElement(resolvedBindings.contributionBinding().key().type())); String subcomponentName = subcomponentNames.get(subcomponentDescriptor); final ClassName subcomponent = this.name.nestedClass("Test" + subcomponentName + "Impl"); final MethodSpec.Builder builder = MethodSpec.methodBuilder("get" + subcomponentName); builder.addModifiers(Modifier.PUBLIC); final CodeBlock memberSelectExpression = getMemberSelectExpression( BindingKey.contribution(resolvedBindings.key())); final Optional<TypeMirror> param = getParameterFromSeedInstanceMethod( subcomponentDescriptor.componentDefinitionType()); if (param.isPresent()) { final TypeMirror injecteeParam = param.get(); builder.addParameter(ClassName.get(injecteeParam), "instance"); builder.addStatement("return ($T) $L.get().seedInstance(instance).build()", subcomponent, memberSelectExpression); } else { builder.addStatement("return ($T) $L.get().build()", subcomponent, memberSelectExpression); } builder.returns(subcomponent); this.component.addMethod(builder.build()); } private Optional<TypeMirror> getParameterFromSeedInstanceMethod(TypeElement element) { final List<? extends TypeMirror> typeMirrors = types.directSupertypes(element.asType()); if (typeMirrors.isEmpty()) { return Optional.empty(); } for (TypeMirror typeMirror : typeMirrors) { final DeclaredType declaredType = (DeclaredType) typeMirror; if (declaredType.toString().contains("AndroidInjector")) { if (declaredType.getTypeArguments().size() == 1) { return Optional.of(declaredType.getTypeArguments().get(0)); } } } return Optional.empty(); } private void implementProvisionMethodForDebug(ResolvedBindings resolvedBindings) { final FieldSpec frameworkField = createFrameworkField(resolvedBindings, Optional.empty()); final MethodSpec.Builder builder = MethodSpec .methodBuilder(Util.getProvisionMethodName(resolvedBindings.contributionBinding())); builder.addModifiers(Modifier.PUBLIC); builder.returns(frameworkField.type); final CodeBlock memberSelectExpression = getMemberSelectExpression( BindingKey.contribution(resolvedBindings.key())); builder.addStatement("return $L", memberSelectExpression); this.component.addMethod(builder.build()); } /** * Adds Javadoc, modifiers, supertypes, and annotations to the component implementation class * declaration. */ protected abstract void decorateComponent(); /** * Adds a builder type. */ protected void addBuilder() { builderName = Optional.of(builderName()); TypeSpec.Builder componentBuilder = createBuilder(builderName.get().simpleName()).addModifiers(PUBLIC, FINAL); Optional<BuilderSpec> builderSpec = graph.componentDescriptor().builderSpec(); if (builderSpec.isPresent()) { addSupertype(componentBuilder, builderSpec.get().builderDefinitionType()); } else { componentBuilder.addMethod(constructorBuilder().addModifiers(PRIVATE).build()); } builderFields = addBuilderFields(componentBuilder); addBuildMethod(componentBuilder, builderSpec); addBuilderMethods(componentBuilder, builderSpec); addBuilderClass(componentBuilder.build()); constructor.addParameter(builderName.get(), "builder"); constructor.addStatement("assert builder != null"); } /** * Adds {@code builder} as a nested builder class. Root components and subcomponents will nest * this in different classes. */ protected abstract void addBuilderClass(TypeSpec builder); /** * Adds fields for each of the {@linkplain BindingGraph#componentRequirements component * requirements}. Regardless of builder spec, there is always one field per requirement. */ private ImmutableMap<ComponentRequirement, FieldSpec> addBuilderFields(TypeSpec.Builder componentBuilder) { UniqueNameSet builderFieldNames = new UniqueNameSet(); ImmutableMap.Builder<ComponentRequirement, FieldSpec> builderFields = ImmutableMap.builder(); for (ComponentRequirement componentRequirement : graph.componentRequirements()) { String contributionName = builderFieldNames.getUniqueName(componentRequirement.variableName()); FieldSpec builderField = FieldSpec .builder(TypeName.get(componentRequirement.type()), contributionName, PRIVATE).build(); componentBuilder.addField(builderField); builderFields.put(componentRequirement, builderField); } for (ContributionBinding contributionBinding : graph.delegateRequirements()) { createDelegateFieldAndMethod(builderName(), componentBuilder, contributionBinding, delegateFieldNames, false); } return builderFields.build(); } /** Adds the build method to the builder. */ private void addBuildMethod(TypeSpec.Builder componentBuilder, Optional<BuilderSpec> builderSpec) { MethodSpec.Builder buildMethod; if (builderSpec.isPresent()) { ExecutableElement specBuildMethod = builderSpec.get().buildMethod(); // Note: we don't use the specBuildMethod.getReturnType() as the return type // because it might be a type variable. We make use of covariant returns to allow // us to return the component type, which will always be valid. buildMethod = methodBuilder(specBuildMethod.getSimpleName().toString()).addAnnotation(Override.class); } else { buildMethod = methodBuilder("build"); } buildMethod.returns(componentDefinitionTypeName()).addModifiers(PUBLIC); for (Map.Entry<ComponentRequirement, FieldSpec> builderFieldEntry : builderFields.entrySet()) { FieldSpec builderField = builderFieldEntry.getValue(); switch (builderFieldEntry.getKey().nullPolicy(elements, types)) { case NEW: buildMethod.addCode("if ($1N == null) { this.$1N = new $2T(); }", builderField, builderField.type); break; case THROW: buildMethod.addCode("if ($N == null) { throw new $T($T.class.getCanonicalName() + $S); }", builderField, IllegalStateException.class, TypeNames.rawTypeName(builderField.type), " must be set"); break; case ALLOW: break; default: throw new AssertionError(builderFieldEntry.getKey()); } } buildMethod.addStatement("return new $T(this)", name); componentBuilder.addMethod(buildMethod.build()); } /** * Adds the methods that set each of parameters on the builder. If the {@link BuilderSpec} is * present, it will tailor the methods to match the spec. */ private void addBuilderMethods(TypeSpec.Builder componentBuilder, Optional<BuilderSpec> builderSpec) { ImmutableSet<ComponentRequirement> componentRequirements = graph.componentRequirements(); if (builderSpec.isPresent()) { UniqueNameSet parameterNames = new UniqueNameSet(); for (BuilderRequirementMethod requirementMethod : builderSpec.get().requirementMethods()) { ComponentRequirement builderRequirement = requirementMethod.requirement(); ExecutableElement specMethod = requirementMethod.method(); MethodSpec.Builder builderMethod = addBuilderMethodFromSpec(specMethod); VariableElement parameterElement = Iterables.getOnlyElement(specMethod.getParameters()); String parameterName = parameterNames.getUniqueName(parameterElement.getSimpleName()); TypeName argType = parameterElement.asType().getKind().isPrimitive() // Primitives need to use the original (unresolved) type to avoid boxing. ? TypeName.get(parameterElement.asType()) // Otherwise we use the full resolved type. : TypeName.get(builderRequirement.type()); builderMethod.addParameter(argType, parameterName); if (componentRequirements.contains(builderRequirement)) { // required type builderMethod.addStatement("this.$N = $L", builderFields.get(builderRequirement), builderRequirement.nullPolicy(elements, types) .equals(ComponentRequirement.NullPolicy.ALLOW) ? parameterName : CodeBlock.of("$T.checkNotNull($L)", Preconditions.class, parameterName)); addBuilderMethodReturnStatementForSpec(specMethod, builderMethod); } else if (graph.ownedModuleTypes().contains(builderRequirement.typeElement())) { // owned, but not required builderMethod.addJavadoc(NOOP_BUILDER_METHOD_JAVADOC); addBuilderMethodReturnStatementForSpec(specMethod, builderMethod); } else { // neither owned nor required, so it must be an inherited module builderMethod.addStatement("throw new $T($T.format($S, $T.class.getCanonicalName()))", UnsupportedOperationException.class, String.class, "%s cannot be set because it is inherited from the enclosing component", TypeNames.rawTypeName(TypeName.get(builderRequirement.type()))); } componentBuilder.addMethod(builderMethod.build()); } } else { for (ComponentRequirement componentRequirement : graph.availableDependencies()) { String componentRequirementName = simpleVariableName(componentRequirement.typeElement()); MethodSpec.Builder builderMethod = methodBuilder(componentRequirementName) .returns(builderName.get()).addModifiers(PUBLIC) .addParameter(ClassName.get(componentRequirement.type()), componentRequirementName); if (componentRequirements.contains(componentRequirement)) { builderMethod.addStatement("this.$N = $T.checkNotNull($L)", builderFields.get(componentRequirement), Preconditions.class, componentRequirementName); } else { builderMethod.addStatement("$T.checkNotNull($L)", Preconditions.class, componentRequirementName); builderMethod.addJavadoc("@deprecated " + NOOP_BUILDER_METHOD_JAVADOC); builderMethod.addAnnotation(Deprecated.class); } builderMethod.addStatement("return this"); componentBuilder.addMethod(builderMethod.build()); } } } public void postAddBuildMethod() { } private void addBuilderMethodReturnStatementForSpec(ExecutableElement specMethod, MethodSpec.Builder builderMethod) { if (!specMethod.getReturnType().getKind().equals(VOID)) { builderMethod.addStatement("return this"); } } private MethodSpec.Builder addBuilderMethodFromSpec(ExecutableElement method) { TypeMirror returnType = method.getReturnType(); MethodSpec.Builder builderMethod = methodBuilder(method.getSimpleName().toString()) .addAnnotation(Override.class) .addModifiers(Sets.difference(method.getModifiers(), ImmutableSet.of(ABSTRACT))); // If the return type is void, we add a method with the void return type. // Otherwise we use the generated builder name and take advantage of covariant returns // (so that we don't have to worry about setter methods that return type variables). if (!returnType.getKind().equals(TypeKind.VOID)) { builderMethod.returns(builderName.get()); } return builderMethod; } /** * Creates the builder class. */ protected abstract TypeSpec.Builder createBuilder(String builderName); protected abstract ClassName builderName(); /** * Adds component factory methods. */ protected abstract void addFactoryMethods(); /** * Adds a {@link dagger.internal.ReferenceReleasingProviderManager} field for every {@link * CanReleaseReferences @ReleasableReferences} scope for which {@linkplain * #requiresReleasableReferences(Scope) one is required}. */ private void addReferenceReleasingProviderManagerFields() { ImmutableMap.Builder<Scope, MemberSelect> fields = ImmutableMap.builder(); for (Scope scope : graph.componentDescriptor().releasableReferencesScopes()) { if (requiresReleasableReferences(scope)) { FieldSpec field = referenceReleasingProxyManagerField(scope); component.addField(field); fields.put(scope, localField(name, field.name)); } } referenceReleasingProviderManagerFields = fields.build(); } /** * Returns {@code true} if {@code scope} {@linkplain CanReleaseReferences can release its * references} and there is a dependency request in the component for any of * * <ul> * <li>{@code @ForReleasableReferences(scope)} {@link ReleasableReferenceManager} * <li>{@code @ForReleasableReferences(scope)} {@code TypedReleasableReferenceManager<M>}, where * {@code M} is the releasable-references metatadata type for {@code scope} * <li>{@code Set<ReleasableReferenceManager>} * <li>{@code Set<TypedReleasableReferenceManager<M>>}, where {@code M} is the metadata type for * the scope * </ul> */ private boolean requiresReleasableReferences(Scope scope) { if (!scope.canReleaseReferences()) { return false; } if (graphHasContributionBinding(keyFactory.forReleasableReferenceManager(scope)) || graphHasContributionBinding(keyFactory.forSetOfReleasableReferenceManagers())) { return true; } for (AnnotationMirror metadata : scope.releasableReferencesMetadata()) { if (graphHasContributionBinding( keyFactory.forTypedReleasableReferenceManager(scope, metadata.getAnnotationType())) || graphHasContributionBinding( keyFactory.forSetOfTypedReleasableReferenceManagers(metadata.getAnnotationType()))) { return true; } } return false; } private boolean graphHasContributionBinding(Key key) { return graph.resolvedBindings().containsKey(contribution(key)); } private FieldSpec referenceReleasingProxyManagerField(Scope scope) { return componentField(REFERENCE_RELEASING_PROVIDER_MANAGER, UPPER_CAMEL.to(LOWER_CAMEL, scope.scopeAnnotationElement().getSimpleName() + "References")) .addModifiers(PRIVATE, FINAL) .initializer("new $T($T.class)", REFERENCE_RELEASING_PROVIDER_MANAGER, scope.scopeAnnotationElement()) .addJavadoc("The manager that releases references for the {@link $T} scope.\n", scope.scopeAnnotationElement()) .build(); } private void addFrameworkFields() { graph.resolvedBindings().values().forEach(this::addField); /*for (ContributionBinding contributionBinding : graph.delegateRequirements()) { createDelegateField(component, contributionBinding, delegateFieldNames); }*/ } private void addField(ResolvedBindings resolvedBindings) { BindingKey bindingKey = resolvedBindings.bindingKey(); // If the binding can be satisfied with a static method call without dependencies or state, // no field is necessary. Optional<MemberSelect> staticMemberSelect = staticMemberSelect(resolvedBindings); if (staticMemberSelect.isPresent()) { memberSelects.put(bindingKey, staticMemberSelect.get()); return; } // No field needed if there are no owned bindings. if (resolvedBindings.ownedBindings().isEmpty()) { return; } // TODO(gak): get rid of the field for unscoped delegated bindings FieldSpec frameworkField = addFrameworkField(resolvedBindings, Optional.empty()); memberSelects.put(bindingKey, localField(name, frameworkField.name)); } /** * Adds a field representing the resolved bindings, optionally forcing it to use a particular * framework class (instead of the class the resolved bindings would typically use). */ private FieldSpec addFrameworkField(ResolvedBindings resolvedBindings, Optional<ClassName> frameworkClass) { FieldSpec field = createFrameworkField(resolvedBindings, frameworkClass); component.addField(field); return field; } private FieldSpec createFrameworkField(ResolvedBindings resolvedBindings, Optional<ClassName> frameworkClass) { boolean useRawType = useRawType(resolvedBindings); FrameworkField contributionBindingField = FrameworkField.forResolvedBindings(resolvedBindings, frameworkClass); FieldSpec.Builder contributionField = componentField( useRawType ? contributionBindingField.type().rawType : contributionBindingField.type(), contributionBindingField.name()); if (useRawType) { contributionField.addAnnotation(AnnotationSpecs.suppressWarnings(RAWTYPES)); } return contributionField.build(); } private boolean useRawType(ResolvedBindings resolvedBindings) { return useRawType(resolvedBindings.bindingPackage()); } private boolean useRawType(Binding binding) { return useRawType(binding.bindingPackage()); } private boolean useRawType(Optional<String> bindingPackage) { return bindingPackage.isPresent() && !bindingPackage.get().equals(name.packageName()); } /** * If {@code resolvedBindings} is an unscoped provision binding with no factory arguments or a * no-op members injection binding, then we don't need a field to hold its factory. In that case, * this method returns the static member select that returns the factory or no-op members * injector. */ private static Optional<MemberSelect> staticMemberSelect(ResolvedBindings resolvedBindings) { BindingKey bindingKey = resolvedBindings.bindingKey(); switch (bindingKey.kind()) { case CONTRIBUTION: ContributionBinding contributionBinding = resolvedBindings.contributionBinding(); if (contributionBinding.factoryCreationStrategy().equals(SINGLETON_INSTANCE) && !contributionBinding.scope().isPresent()) { switch (contributionBinding.bindingKind()) { case SYNTHETIC_MULTIBOUND_MAP: BindingType bindingType = contributionBinding.bindingType(); MapType mapType = MapType.from(contributionBinding.key()); return Optional.of(emptyFrameworkMapFactory(bindingType, mapType.keyType(), mapType.unwrappedValueType(bindingType.frameworkClass()))); case SYNTHETIC_MULTIBOUND_SET: return Optional.of(emptySetFactoryStaticMemberSelect(contributionBinding.bindingType(), contributionBinding.key())); case INJECTION: case PROVISION: if (bindingKey.key().type().getKind().equals(DECLARED)) { ImmutableList<TypeVariableName> typeVariables = bindingTypeElementTypeVariableNames( contributionBinding); if (!typeVariables.isEmpty()) { List<? extends TypeMirror> typeArguments = ((DeclaredType) bindingKey.key().type()) .getTypeArguments(); return Optional.of(MemberSelect.parameterizedFactoryCreateMethod( generatedClassNameForBinding(contributionBinding), typeArguments)); } } // fall through default: return Optional.of(staticMethod(generatedClassNameForBinding(contributionBinding), CodeBlock.of("create()"))); } } break; case MEMBERS_INJECTION: Optional<MembersInjectionBinding> membersInjectionBinding = resolvedBindings.membersInjectionBinding(); if (membersInjectionBinding.isPresent() && membersInjectionBinding.get().injectionSites().isEmpty()) { return Optional.of(noOpMembersInjector(membersInjectionBinding.get().key().type())); } break; default: throw new AssertionError(); } return Optional.empty(); } /** * A static member select for an empty set factory. Calls {@link SetFactory#empty()}, {@link * SetProducer#empty()}, or {@link SetOfProducedProducer#empty()}, depending on the set * bindings. */ private static MemberSelect emptySetFactoryStaticMemberSelect(BindingType bindingType, Key key) { return emptySetProvider(setFactoryClassName(bindingType, key), SetType.from(key)); } /** * The {@link Set} factory class name appropriate for set bindings. * * <ul> * <li>{@link SetFactory} for provision bindings. * <li>{@link SetProducer} for production bindings for {@code Set<T>}. * <li>{@link SetOfProducedProducer} for production bindings for {@code Set<Produced<T>>}. * </ul> */ private static ClassName setFactoryClassName(BindingType bindingType, Key key) { if (bindingType.equals(BindingType.PROVISION)) { return SET_FACTORY; } else { SetType setType = SetType.from(key); return setType.elementsAreTypeOf(Produced.class) ? SET_OF_PRODUCED_PRODUCER : SET_PRODUCER; } } /** * The {@link Map}-of-value factory class name appropriate for map bindings. * * <ul> * <li>{@link MapFactory} for provision bindings. * <li>{@link MapProducer} for production bindings. * </ul> */ private static ClassName mapFactoryClassName(ContributionBinding binding) { switch (binding.bindingType()) { case PRODUCTION: return MapType.from(binding.key()).valuesAreTypeOf(Produced.class) ? MAP_OF_PRODUCED_PRODUCER : MAP_PRODUCER; case PROVISION: case MEMBERS_INJECTION: return MAP_FACTORY; default: throw new AssertionError(binding.toString()); } } /** * The {@link Map}-of-framework factory class name appropriate for map bindings. * * <ul> * <li>{@link MapProviderFactory} for provision bindings. * <li>{@link MapOfProducerProducer} for production bindings. * </ul> */ private static ClassName frameworkMapFactoryClassName(BindingType bindingType) { return bindingType.equals(BindingType.PRODUCTION) ? MAP_OF_PRODUCER_PRODUCER : MAP_PROVIDER_FACTORY; } private void implementInterfaceMethods() { Set<MethodSignature> interfaceMethods = Sets.newHashSet(); final ImmutableSet<ComponentMethodDescriptor> componentMethodDescriptors = graph.componentDescriptor() .componentMethods(); for (ComponentMethodDescriptor componentMethod : componentMethodDescriptors) { if (componentMethod.dependencyRequest().isPresent()) { DependencyRequest interfaceRequest = componentMethod.dependencyRequest().get(); ExecutableElement methodElement = MoreElements.asExecutable(componentMethod.methodElement()); ExecutableType requestType = MoreTypes.asExecutable( types.asMemberOf(MoreTypes.asDeclared(graph.componentType().asType()), methodElement)); MethodSignature signature = MethodSignature .fromExecutableType(methodElement.getSimpleName().toString(), requestType); if (!interfaceMethods.contains(signature)) { interfaceMethods.add(signature); MethodSpec.Builder interfaceMethod = methodSpecForComponentMethod(methodElement, requestType); RequestFulfillment fulfillment = requestFulfillmentRegistry .getRequestFulfillment(interfaceRequest.bindingKey()); CodeBlock codeBlock = fulfillment.getSnippetForDependencyRequest(interfaceRequest, name); switch (interfaceRequest.kind()) { case MEMBERS_INJECTOR: List<? extends VariableElement> parameters = methodElement.getParameters(); if (!parameters.isEmpty()) { Name parameterName = Iterables.getOnlyElement(methodElement.getParameters()) .getSimpleName(); interfaceMethod.addStatement("$L.injectMembers($L)", codeBlock, parameterName); if (!requestType.getReturnType().getKind().equals(VOID)) { interfaceMethod.addStatement("return $L", parameterName); } break; } // fall through default: final MemberSelect memberSelect = memberSelects .get(BindingKey.membersInjection(interfaceRequest.key())); if (memberSelect != null) { final CodeBlock expressionFor = memberSelect.getExpressionFor(name); final Optional<String> variableName = interfaceRequest.overriddenVariableName(); String name = variableName.isPresent() ? variableName.get() : "obj"; interfaceMethod.addStatement("$T $L = $L", ClassName.get(interfaceRequest.key().type()), name, codeBlock); interfaceMethod.addStatement("$L.injectMembers($L)", expressionFor, name); if (!requestType.getReturnType().getKind().equals(VOID)) { interfaceMethod.addStatement("return $L", name); } } else { interfaceMethod.addStatement("return $L", codeBlock); } /*CodeBlock.Builder builder = CodeBlock.builder(); final boolean supportsTestDelegate = !componentMethod.methodElement().getReturnType().toString().equals(void.class.getName()); if (supportsTestDelegate) { final String fieldName = getDelegateFieldName(interfaceRequest.key()); builder.beginControlFlow("if ($L != null)", CodeBlock.of(fieldName)) .add("return $L.get($L)", CodeBlock.of(fieldName), codeBlock) .nextControlFlow("else"); }*/ /*if (supportsTestDelegate) { builder.endControlFlow(); }*/ break; } component.addMethod(interfaceMethod.build()); } } } } private MethodSpec.Builder methodSpecForComponentMethod(ExecutableElement method, ExecutableType methodType) { String methodName = method.getSimpleName().toString(); MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(methodName); methodBuilder.addAnnotation(Override.class); Set<Modifier> modifiers = EnumSet.copyOf(method.getModifiers()); modifiers.remove(Modifier.ABSTRACT); methodBuilder.addModifiers(modifiers); methodBuilder.returns(TypeName.get(methodType.getReturnType())); List<? extends VariableElement> parameters = method.getParameters(); List<? extends TypeMirror> resolvedParameterTypes = methodType.getParameterTypes(); verify(parameters.size() == resolvedParameterTypes.size()); for (int i = 0; i < parameters.size(); i++) { VariableElement parameter = parameters.get(i); TypeName type = TypeName.get(resolvedParameterTypes.get(i)); String name = parameter.getSimpleName().toString(); Set<Modifier> parameterModifiers = parameter.getModifiers(); ParameterSpec.Builder parameterBuilder = ParameterSpec.builder(type, name) .addModifiers(parameterModifiers.toArray(new Modifier[0])); methodBuilder.addParameter(parameterBuilder.build()); } for (TypeMirror thrownType : method.getThrownTypes()) { methodBuilder.addException(TypeName.get(thrownType)); } return methodBuilder; } private void addSubcomponents() { for (BindingGraph subgraph : graph.subgraphs()) { ComponentMethodDescriptor componentMethodDescriptor = graph.componentDescriptor() .subcomponentsByFactoryMethod().inverse().get(subgraph.componentDescriptor()); SubcomponentWriter subcomponent = new SubcomponentWriter(this, Optional.ofNullable(componentMethodDescriptor), subgraph, forTests); component.addType(subcomponent.write().build()); } } private static final int INITIALIZATIONS_PER_INITIALIZE_METHOD = 100; private void initializeFrameworkTypes() { ImmutableList.Builder<CodeBlock> codeBlocks = ImmutableList.builder(); for (BindingKey bindingKey : graph.resolvedBindings().keySet()) { initializeFrameworkType(bindingKey).ifPresent(codeBlocks::add); } List<List<CodeBlock>> partitions = Lists.partition(codeBlocks.build(), INITIALIZATIONS_PER_INITIALIZE_METHOD); initializationMethods = new ArrayList<>(); UniqueNameSet methodNames = new UniqueNameSet(); for (List<CodeBlock> partition : partitions) { String methodName = methodNames.getUniqueName("initialize"); MethodSpec.Builder initializeMethod = methodBuilder(methodName).addModifiers(PRIVATE) /* TODO(gak): Strictly speaking, we only need the suppression here if we are also * initializing a raw field in this method, but the structure of this code makes it * awkward to pass that bit through. This will be cleaned up when we no longer * separate fields and initilization as we do now. */ .addAnnotation(AnnotationSpecs.suppressWarnings(UNCHECKED)) .addCode(CodeBlocks.concat(partition)); if (builderName.isPresent()) { initializeMethod.addParameter(builderName.get(), "builder", FINAL); constructor.addStatement("$L(builder)", methodName); } else { constructor.addStatement("$L()", methodName); } final MethodSpec method = initializeMethod.build(); initializationMethods.add(method); component.addMethod(method); } } /*private CodeBlock initDelegateFields() { List<CodeBlock> codeBlocks = new ArrayList<>(); for (ContributionBinding contributionBinding : graph.delegateRequirements()) { try { final String delegateFieldName = Util.getDelegateFieldName(contributionBinding.key()); codeBlocks.add(CodeBlock.of("this.$L = builder.$L;", delegateFieldName, delegateFieldName)); }catch(Exception e){} } return CodeBlocks.concat(codeBlocks); }*/ /** * Returns a single code block representing the initialization of the framework type. * * <p>Note that this must be a single code block because initialization code blocks can be invoked * from any place in any order. By requiring a single code block (often of concatenated code * blocks) we ensure that things like local variables always behave as expected by the * initialization logic. */ private Optional<CodeBlock> initializeFrameworkType(BindingKey bindingKey) { // If the field is inherited or the member select is static, don't initialize. MemberSelect memberSelect = getMemberSelect(bindingKey); if (memberSelect.staticMember() || !memberSelect.owningClass().equals(name)) { return Optional.empty(); } switch (bindingKey.kind()) { case CONTRIBUTION: return initializeContributionBinding(bindingKey); case MEMBERS_INJECTION: return initializeMembersInjectionBinding(bindingKey); default: throw new AssertionError(); } } private Optional<CodeBlock> initializeContributionBinding(BindingKey bindingKey) { ContributionBinding binding = graph.resolvedBindings().get(bindingKey).contributionBinding(); /* We have some duplication in the branches below b/c initializeDeferredDependencies must be * called before we get the code block that initializes the member. */ switch (binding.factoryCreationStrategy()) { case DELEGATE: CodeBlock delegatingCodeBlock = CodeBlock.of("($T) $L", binding.bindingType().frameworkClass(), getMemberSelect(Iterables.getOnlyElement(binding.explicitDependencies()).bindingKey()) .getExpressionFor(name)); return Optional.of(CodeBlocks.concat(ImmutableList.of(initializeDeferredDependencies(binding), initializeMember(bindingKey, decorateForScope(delegatingCodeBlock, binding.scope()))))); case SINGLETON_INSTANCE: if (!binding.scope().isPresent()) { return Optional.empty(); } // fall through case CLASS_CONSTRUCTOR: return Optional.of(CodeBlocks.concat(ImmutableList.of(initializeDeferredDependencies(binding), initializeMember(bindingKey, initializeFactoryForContributionBinding(binding))))); default: throw new AssertionError(); } } private Optional<CodeBlock> initializeMembersInjectionBinding(BindingKey bindingKey) { MembersInjectionBinding binding = graph.resolvedBindings().get(bindingKey).membersInjectionBinding().get(); if (binding.injectionSites().isEmpty()) { return Optional.empty(); } return Optional.of(CodeBlocks.concat(ImmutableList.of(initializeDeferredDependencies(binding), initializeMember(bindingKey, initializeMembersInjectorForBinding(binding))))); } /** * Initializes any dependencies of the given binding that need to be instantiated, i.e., as we get * to them during normal initialization. */ private CodeBlock initializeDeferredDependencies(Binding binding) { return CodeBlocks.concat(ImmutableList.of(initializeDelegateFactoriesForUninitializedDependencies(binding), initializeProducersFromProviderDependencies(binding))); } /** * Initializes delegate factories for any dependencies of {@code binding} that are uninitialized * because of a dependency cycle. */ private CodeBlock initializeDelegateFactoriesForUninitializedDependencies(Binding binding) { ImmutableList.Builder<CodeBlock> initializations = ImmutableList.builder(); final ImmutableSet<BindingKey> bindingKeys = FluentIterable.from(binding.dependencies()) .transform(DependencyRequest::bindingKey).toSet(); for (BindingKey dependencyKey : bindingKeys) { if (!getMemberSelect(dependencyKey).staticMember() && getInitializationState(dependencyKey).equals(UNINITIALIZED)) { final CodeBlock codeBlock = CodeBlock.of("this.$L = new $T();", getMemberSelectExpression(dependencyKey), DELEGATE_FACTORY); initializations.add(codeBlock); setInitializationState(dependencyKey, DELEGATED); } } return CodeBlocks.concat(initializations.build()); } private CodeBlock initializeProducersFromProviderDependencies(Binding binding) { ImmutableList.Builder<CodeBlock> initializations = ImmutableList.builder(); for (FrameworkDependency frameworkDependency : binding.frameworkDependencies()) { ResolvedBindings resolvedBindings = graph.resolvedBindings().get(frameworkDependency.bindingKey()); if (resolvedBindings.frameworkClass().equals(Provider.class) && frameworkDependency.frameworkClass().equals(Producer.class)) { MemberSelect memberSelect = producerFromProviderMemberSelects.get(frameworkDependency.bindingKey()); if (memberSelect != null) { continue; } FieldSpec frameworkField = addFrameworkField(resolvedBindings, Optional.of(PRODUCER)); memberSelect = localField(name, frameworkField.name); producerFromProviderMemberSelects.put(frameworkDependency.bindingKey(), memberSelect); initializations.add(CodeBlock.of("this.$L = $L;", memberSelect.getExpressionFor(name), requestFulfillmentRegistry.getRequestFulfillment(frameworkDependency.bindingKey()) .getSnippetForFrameworkDependency(frameworkDependency, name))); } } return CodeBlocks.concat(initializations.build()); } private CodeBlock initializeMember(BindingKey bindingKey, CodeBlock initializationCodeBlock) { ImmutableList.Builder<CodeBlock> initializations = ImmutableList.builder(); CodeBlock memberSelect = getMemberSelectExpression(bindingKey); CodeBlock delegateFactoryVariable = delegateFactoryVariableExpression(bindingKey); if (getInitializationState(bindingKey).equals(DELEGATED)) { initializations.add( CodeBlock.of("$1T $2L = ($1T) $3L;", DELEGATE_FACTORY, delegateFactoryVariable, memberSelect)); } initializations.add(CodeBlock.of("this.$L = $L;", memberSelect, initializationCodeBlock)); if (getInitializationState(bindingKey).equals(DELEGATED)) { initializations .add(CodeBlock.of("$L.setDelegatedProvider($L);", delegateFactoryVariable, memberSelect)); } setInitializationState(bindingKey, INITIALIZED); return CodeBlocks.concat(initializations.build()); } private CodeBlock delegateFactoryVariableExpression(BindingKey key) { return CodeBlock.of("$LDelegate", getMemberSelectExpression(key).toString().replace('.', '_')); } private CodeBlock initializeFactoryForContributionBinding(ContributionBinding binding) { TypeName bindingKeyTypeName = TypeName.get(binding.key().type()); final String delegateFieldName = delegateFieldNames.get(binding.key()); switch (binding.bindingKind()) { case COMPONENT: return CodeBlock.of("$T.<$T>create($L)", INSTANCE_FACTORY, bindingKeyTypeName, bindingKeyTypeName.equals(componentDefinitionTypeName()) ? "this" : getComponentContributionExpression( ComponentRequirement.forDependency(binding.key().type()))); case COMPONENT_PROVISION: { TypeElement dependencyType = dependencyTypeForBinding(binding); String dependencyVariable = simpleVariableName(dependencyType); String componentMethod = binding.bindingElement().get().getSimpleName().toString(); CodeBlock callFactoryMethod = CodeBlock.of("$L.$L()", dependencyVariable, componentMethod); // TODO(sameb): This throws a very vague NPE right now. 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? /*<<<<<<< HEAD:compiler/src/main/java/dagger/internal/codegen/AbstractComponentWriter.java CodeBlock.Builder getMethodBodyBuilder = CodeBlock.builder(); getMethodBodyBuilder.add(getCodeBlock(binding, callFactoryMethod)); return CodeBlock.of( Joiner.on('\n') .join( "new $1T<$2T>() {", " private final $5T $6L = $3L;", " $4L@Override public $2T get() {", " $7L", " }", "}"), FACTORY, bindingKeyTypeName, getComponentContributionExpression( ComponentRequirement.forDependency(dependencyType.asType())), nullableAnnotation(binding.nullableType()), TypeName.get(dependencyType.asType()), dependencyVariable, getMethodBodyBuilder.build()); =======*/ CodeBlock getMethodBody = binding.nullableType().isPresent() || compilerOptions.nullableValidationKind().equals(Diagnostic.Kind.WARNING) ? CodeBlock.of("return $L;", callFactoryMethod) : CodeBlock.of("return $T.checkNotNull($L, $S);", Preconditions.class, callFactoryMethod, CANNOT_RETURN_NULL_FROM_NON_NULLABLE_COMPONENT_METHOD); ClassName dependencyClassName = ClassName.get(dependencyType); String factoryName = dependencyClassName.toString().replace('.', '_') + "_" + componentMethod; MethodSpec.Builder getMethod = methodBuilder("get").addAnnotation(Override.class).addModifiers(PUBLIC) .returns(bindingKeyTypeName).addCode(getMethodBody); if (binding.nullableType().isPresent()) { getMethod.addAnnotation(ClassName.get(MoreTypes.asTypeElement(binding.nullableType().get()))); } component.addType(TypeSpec.classBuilder(factoryName).addSuperinterface(providerOf(bindingKeyTypeName)) .addModifiers(PRIVATE, STATIC).addField(dependencyClassName, dependencyVariable, PRIVATE, FINAL) .addMethod(constructorBuilder().addParameter(dependencyClassName, dependencyVariable) .addStatement("this.$1L = $1L", dependencyVariable).build()) .addMethod(getMethod.build()).build()); return CodeBlock.of("new $L($L)", factoryName, getComponentContributionExpression( ComponentRequirement.forDependency(dependencyType.asType()))); //>>>>>>> upstream/master:java/dagger/internal/codegen/AbstractComponentWriter.java } case SUBCOMPONENT_BUILDER: final CodeBlock expression = getMemberSelectExpression( BindingKey.contribution(Key.builder(graph.application().get()).build())); String subcomponentName = subcomponentNames.get(graph.componentDescriptor().subcomponentsByBuilderType() .get(MoreTypes.asTypeElement(binding.key().type()))); return CodeBlock.of( Joiner.on('\n').join("new $1L<$2T>() {", " @Override public $2T get() {", " return $4L.get().$5L(new $3LBuilder());", " }", "}"), // TODO(ronshapiro): Until we remove Factory, fully qualify the import so it doesn't // conflict with dagger.android.ActivityInjector.Factory /* 1 */ "dagger.internal.Factory", /* 2 */ bindingKeyTypeName, /* 3 */ subcomponentName, /* 4 */ expression, /* 5 */ "decorate" + subcomponentName); case BUILDER_BINDING: final CodeBlock parameter = getComponentContributionExpression( ComponentRequirement.forBinding(binding)); CodeBlock parameterDecision; if (delegateFieldName != null && bindingSupportsTestDelegate(binding)) { parameterDecision = CodeBlock.of("builder.$L == null ? $L : builder.$L.get()", delegateFieldName, parameter, delegateFieldName); } else { parameterDecision = CodeBlock.of("$L", parameter); } return CodeBlock.of("$T.$L($L)", InstanceFactory.class, binding.nullableType().isPresent() ? "createNullable" : "create", parameterDecision); case INJECTION: case PROVISION: { List<CodeBlock> arguments = Lists.newArrayListWithCapacity(binding.explicitDependencies().size() + 2); if (binding.requiresModuleInstance()) { arguments.add(getComponentContributionExpression( ComponentRequirement.forModule(binding.contributingModule().get().asType()))); } arguments.addAll(getDependencyArguments(binding)); if (delegateFieldName != null && bindingSupportsTestDelegate(binding)) { arguments.add(0, CodeBlock.of("builder.$L", delegateFieldName)); } CodeBlock factoryCreate = CodeBlock.of("$T.create($L)", generatedClassNameForBinding(binding), makeParametersCodeBlock(arguments)); // If scoping a parameterized factory for an @Inject class, Java 7 cannot always infer the // type properly, so cast to a raw framework type before scoping. if (binding.bindingKind().equals(INJECTION) && binding.unresolved().isPresent() && binding.scope().isPresent()) { factoryCreate = CodeBlock.of("($T) $L", binding.bindingType().frameworkClass(), factoryCreate); } return decorateForScope(factoryCreate, binding.scope()); } case COMPONENT_PRODUCTION: { TypeElement dependencyType = dependencyTypeForBinding(binding); return CodeBlock.of( Joiner.on('\n').join("new $1T<$2T>() {", " private final $6T $7L = $4L;", " @Override public $3T<$2T> get() {", " return $7L.$5L();", " }", "}"), /* 1 */ PRODUCER, /* 2 */ TypeName.get(binding.key().type()), /* 3 */ LISTENABLE_FUTURE, /* 4 */ getComponentContributionExpression( ComponentRequirement.forDependency(dependencyType.asType())), /* 5 */ binding.bindingElement().get().getSimpleName(), /* 6 */ TypeName.get(dependencyType.asType()), /* 7 */ simpleVariableName(dependencyType)); } case PRODUCTION: { List<CodeBlock> arguments = Lists.newArrayListWithCapacity(binding.dependencies().size() + 2); if (binding.requiresModuleInstance()) { arguments.add(getComponentContributionExpression( ComponentRequirement.forModule(binding.contributingModule().get().asType()))); } arguments.addAll(getDependencyArguments(binding)); return CodeBlock.of("new $T($L)", generatedClassNameForBinding(binding), makeParametersCodeBlock(arguments)); } case SYNTHETIC_MAP: FrameworkDependency frameworkDependency = getOnlyElement(binding.frameworkDependencies()); return CodeBlock.of("$T.create($L)", mapFactoryClassName(binding), requestFulfillmentRegistry.getRequestFulfillment(frameworkDependency.bindingKey()) .getSnippetForFrameworkDependency(frameworkDependency, name)); case SYNTHETIC_MULTIBOUND_SET: return initializeFactoryForSetMultibinding(binding); case SYNTHETIC_MULTIBOUND_MAP: return initializeFactoryForMapMultibinding(binding); case SYNTHETIC_RELEASABLE_REFERENCE_MANAGER: return initializeFactoryForSyntheticReleasableReferenceManagerBinding(binding); case SYNTHETIC_RELEASABLE_REFERENCE_MANAGERS: return initializeFactoryForSyntheticSetOfReleasableReferenceManagers(binding); case SYNTHETIC_OPTIONAL_BINDING: return initializeFactoryForSyntheticOptionalBinding(binding); default: throw new AssertionError(binding); } } private CodeBlock getCodeBlock(ContributionBinding binding, CodeBlock callFactoryMethod) { return binding.nullableType().isPresent() || compilerOptions.nullableValidationKind().equals(Diagnostic.Kind.WARNING) ? CodeBlock.of("return $L;", callFactoryMethod) : CodeBlock.of("return $T.checkNotNull($L, $S);", Preconditions.class, callFactoryMethod, CANNOT_RETURN_NULL_FROM_NON_NULLABLE_COMPONENT_METHOD); } private TypeElement dependencyTypeForBinding(ContributionBinding binding) { return graph.componentDescriptor().dependencyMethodIndex().get(binding.bindingElement().get()); } private CodeBlock decorateForScope(CodeBlock factoryCreate, Optional<Scope> maybeScope) { if (!maybeScope.isPresent()) { return factoryCreate; } Scope scope = maybeScope.get(); if (requiresReleasableReferences(scope)) { return CodeBlock.of("$T.create($L, $L)", REFERENCE_RELEASING_PROVIDER, factoryCreate, getReferenceReleasingProviderManagerExpression(scope)); } else { return CodeBlock.of("$T.provider($L)", scope.equals(reusableScope(elements)) ? SINGLE_CHECK : DOUBLE_CHECK, factoryCreate); } } private CodeBlock initializeMembersInjectorForBinding(MembersInjectionBinding binding) { return binding.injectionSites().isEmpty() ? CodeBlock.of("$T.noOp()", MEMBERS_INJECTORS) : CodeBlock.of("$T.create($L)", membersInjectorNameForType(binding.membersInjectedType()), makeParametersCodeBlock(getDependencyArguments(binding))); /* switch (binding.injectionStrategy()) { case NO_OP: return CodeBlock.of("$T.noOp()", MEMBERS_INJECTORS); case INJECT_MEMBERS: final ImmutableList<CodeBlock> arguments = getDependencyArguments(binding); return CodeBlock.of( "$T.create($L)", membersInjectorNameForType(binding.membersInjectedType()), makeParametersCodeBlock(arguments)); default: throw new AssertionError(); } */ } /** * The expressions that represent factory arguments for the dependencies of a binding. */ private ImmutableList<CodeBlock> getDependencyArguments(Binding binding) { ImmutableList.Builder<CodeBlock> parameters = ImmutableList.builder(); for (FrameworkDependency frameworkDependency : binding.frameworkDependencies()) { parameters.add(getDependencyArgument(frameworkDependency)); } return parameters.build(); } /** Returns the expression to use as an argument for a dependency. */ private CodeBlock getDependencyArgument(FrameworkDependency frameworkDependency) { BindingKey requestedKey = frameworkDependency.bindingKey(); ResolvedBindings resolvedBindings = graph.resolvedBindings().get(requestedKey); if (resolvedBindings.frameworkClass().equals(Provider.class) && frameworkDependency.frameworkClass().equals(Producer.class)) { return producerFromProviderMemberSelects.get(requestedKey).getExpressionFor(name); } else { RequestFulfillment requestFulfillment = requestFulfillmentRegistry.getRequestFulfillment(requestedKey); return requestFulfillment.getSnippetForFrameworkDependency(frameworkDependency, name); } } private CodeBlock initializeFactoryForSetMultibinding(ContributionBinding binding) { CodeBlock.Builder builder = CodeBlock.builder().add("$T.", setFactoryClassName(binding.bindingType(), binding.key())); boolean useRawTypes = useRawType(binding); if (!useRawTypes) { SetType setType = SetType.from(binding.key()); builder.add("<$T>", setType.elementsAreTypeOf(Produced.class) ? setType.unwrappedElementType(Produced.class) : setType.elementType()); } int individualProviders = 0; int setProviders = 0; CodeBlock.Builder builderMethodCalls = CodeBlock.builder(); for (FrameworkDependency frameworkDependency : binding.frameworkDependencies()) { ContributionType contributionType = graph.resolvedBindings().get(frameworkDependency.bindingKey()) .contributionType(); String methodName; String methodNameSuffix = frameworkDependency.frameworkClass().getSimpleName(); switch (contributionType) { case SET: individualProviders++; methodName = "add" + methodNameSuffix; break; case SET_VALUES: setProviders++; methodName = "addCollection" + methodNameSuffix; break; default: throw new AssertionError(frameworkDependency + " is not a set multibinding"); } builderMethodCalls.add(".$L($L)", methodName, potentiallyCast(useRawTypes, frameworkDependency.frameworkClass(), getDependencyArgument(frameworkDependency))); } builder.add("builder($L, $L)", individualProviders, setProviders); builder.add(builderMethodCalls.build()); return builder.add(".build()").build(); } private CodeBlock initializeFactoryForMapMultibinding(ContributionBinding binding) { ImmutableList<FrameworkDependency> frameworkDependencies = binding.frameworkDependencies(); ImmutableList.Builder<CodeBlock> codeBlocks = ImmutableList.builder(); MapType mapType = MapType.from(binding.key().type()); CodeBlock.Builder builderCall = CodeBlock.builder().add("$T.", frameworkMapFactoryClassName(binding.bindingType())); boolean useRawTypes = useRawType(binding); if (!useRawTypes) { builderCall.add("<$T, $T>", TypeName.get(mapType.keyType()), TypeName.get(mapType.unwrappedValueType(binding.bindingType().frameworkClass()))); } builderCall.add("builder($L)", frameworkDependencies.size()); codeBlocks.add(builderCall.build()); for (FrameworkDependency frameworkDependency : frameworkDependencies) { BindingKey bindingKey = frameworkDependency.bindingKey(); ContributionBinding contributionBinding = graph.resolvedBindings().get(bindingKey) .contributionBinding(); CodeBlock value = potentiallyCast(useRawTypes, frameworkDependency.frameworkClass(), getDependencyArgument(frameworkDependency)); codeBlocks.add( CodeBlock.of(".put($L, $L)", getMapKeyExpression(contributionBinding.mapKey().get()), value)); } codeBlocks.add(CodeBlock.of(".build()")); return CodeBlocks.concat(codeBlocks.build()); } private CodeBlock potentiallyCast(boolean shouldCast, Class<?> classToCast, CodeBlock notCasted) { if (!shouldCast) { return notCasted; } return CodeBlock.of("($T) $L", classToCast, notCasted); } /** * Initializes the factory for a {@link * ContributionBinding.Kind#SYNTHETIC_RELEASABLE_REFERENCE_MANAGER} binding. * * <p>The {@code get()} method just returns the component field with the {@link * dagger.internal.ReferenceReleasingProviderManager} object. */ private CodeBlock initializeFactoryForSyntheticReleasableReferenceManagerBinding(ContributionBinding binding) { // The scope is the value of the @ForReleasableReferences annotation. Scope scope = forReleasableReferencesAnnotationValue(binding.key().qualifier().get()); CodeBlock managerExpression; if (MoreTypes.isTypeOf(TypedReleasableReferenceManager.class, binding.key().type())) { /* The key's type is TypedReleasableReferenceManager<M>, so return * new TypedReleasableReferenceManager(field, metadata). */ TypeMirror metadataType = MoreTypes.asDeclared(binding.key().type()).getTypeArguments().get(0); managerExpression = typedReleasableReferenceManagerDecoratorExpression( getReferenceReleasingProviderManagerExpression(scope), scope.releasableReferencesMetadata(metadataType).get()); } else { // The key's type is ReleasableReferenceManager, so return the field as is. managerExpression = getReferenceReleasingProviderManagerExpression(scope); } TypeName keyType = TypeName.get(binding.key().type()); return CodeBlock .of("$L", anonymousClassBuilder("").addSuperinterface(providerOf(keyType)) .addMethod(methodBuilder("get").addAnnotation(Override.class).addModifiers(PUBLIC) .returns(keyType).addCode("return $L;", managerExpression).build()) .build()); } /** * Initializes the factory for a {@link * ContributionBinding.Kind#SYNTHETIC_RELEASABLE_REFERENCE_MANAGERS} binding. * * <p>A binding for {@code Set<ReleasableReferenceManager>} will include managers for all * reference-releasing scopes. A binding for {@code Set<TypedReleasableReferenceManager<M>>} will * include managers for all reference-releasing scopes whose metadata type is {@code M}. */ private CodeBlock initializeFactoryForSyntheticSetOfReleasableReferenceManagers(ContributionBinding binding) { Key key = binding.key(); SetType keyType = SetType.from(key); ImmutableList.Builder<CodeBlock> managerExpressions = ImmutableList.builder(); for (Map.Entry<Scope, MemberSelect> entry : referenceReleasingProviderManagerFields.entrySet()) { Scope scope = entry.getKey(); CodeBlock releasableReferenceManagerExpression = entry.getValue().getExpressionFor(name); if (keyType.elementsAreTypeOf(ReleasableReferenceManager.class)) { managerExpressions.add(releasableReferenceManagerExpression); } else if (keyType.elementsAreTypeOf(TypedReleasableReferenceManager.class)) { TypeMirror metadataType = keyType.unwrappedElementType(TypedReleasableReferenceManager.class); Optional<AnnotationMirror> metadata = scope.releasableReferencesMetadata(metadataType); if (metadata.isPresent()) { managerExpressions.add(typedReleasableReferenceManagerDecoratorExpression( releasableReferenceManagerExpression, metadata.get())); } } else { throw new IllegalArgumentException("inappropriate key: " + binding); } } TypeName keyTypeName = TypeName.get(key.type()); return CodeBlock.of("$L", anonymousClassBuilder("").addSuperinterface(providerOf(keyTypeName)) .addMethod(methodBuilder("get").addAnnotation(Override.class).addModifiers(PUBLIC) .returns(keyTypeName).addCode("return new $T($T.asList($L));", HashSet.class, Arrays.class, makeParametersCodeBlock(managerExpressions.build())) .build()) .build()); } /** * Returns an expression that evaluates to a {@link TypedReleasableReferenceManagerDecorator} that * decorates the {@code managerExpression} to supply {@code metadata}. */ private CodeBlock typedReleasableReferenceManagerDecoratorExpression(CodeBlock managerExpression, AnnotationMirror metadata) { return CodeBlock.of("new $T($L, $L)", ParameterizedTypeName.get(TYPED_RELEASABLE_REFERENCE_MANAGER_DECORATOR, TypeName.get(metadata.getAnnotationType())), managerExpression, new AnnotationExpression(metadata).getAnnotationInstanceExpression()); } private Scope forReleasableReferencesAnnotationValue(AnnotationMirror annotation) { checkArgument(MoreTypes.isTypeOf(ForReleasableReferences.class, annotation.getAnnotationType())); return Scope .scope(MoreElements.asType(MoreTypes.asDeclared(getTypeValue(annotation, "value")).asElement())); } /** * Returns an expression that initializes a {@link Provider} or {@link Producer} for an optional * binding. */ private CodeBlock initializeFactoryForSyntheticOptionalBinding(ContributionBinding binding) { if (binding.explicitDependencies().isEmpty()) { verify(binding.bindingType().equals(BindingType.PROVISION), "Absent optional bindings should be provisions: %s", binding); return optionalFactories.absentOptionalProvider(binding); } else { return optionalFactories.presentOptionalFactory(binding, getOnlyElement(getDependencyArguments(binding))); } } public static String simpleVariableName(Element typeElement) { return UPPER_CAMEL.to(LOWER_CAMEL, typeElement.getSimpleName().toString()); } /** * Initialization state for a factory field. */ enum InitializationState { /** The field is {@code null}. */ UNINITIALIZED, /** The field is set to a {@link DelegateFactory}. */ DELEGATED, /** The field is set to an undelegated factory. */ INITIALIZED; } }