/* * Copyright (C) 2021 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.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static dagger.internal.codegen.binding.SourceFiles.bindingTypeElementTypeVariableNames; import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding; import static dagger.internal.codegen.binding.SourceFiles.setFactoryClassName; import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock; import static dagger.internal.codegen.javapoet.TypeNames.DAGGER_PROVIDER; import static dagger.internal.codegen.javapoet.TypeNames.FACTORY; import static dagger.internal.codegen.javapoet.TypeNames.MAP_FACTORY; import static dagger.internal.codegen.javapoet.TypeNames.PRODUCER; import static dagger.internal.codegen.javapoet.TypeNames.PRODUCERS; import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom; import static dagger.internal.codegen.xprocessing.XTypes.isDeclared; import androidx.room.compiler.processing.XType; import com.google.common.collect.ImmutableList; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.TypeVariableName; import dagger.internal.codegen.base.SetType; import dagger.internal.codegen.binding.Binding; import dagger.internal.codegen.binding.BindingType; import dagger.internal.codegen.binding.ContributionBinding; import dagger.internal.codegen.javapoet.CodeBlocks; /** Helper class for static member select creation. */ final class StaticMemberSelects { /** A {@link MemberSelect} for a factory of an empty map. */ static MemberSelect emptyMapFactory(Binding binding) { BindingType bindingType = binding.bindingType(); ImmutableList typeParameters = ImmutableList.copyOf(binding.key().type().xprocessing().getTypeArguments()); return bindingType.equals(BindingType.PRODUCTION) ? new ParameterizedStaticMethod( PRODUCERS, typeParameters, CodeBlock.of("emptyMapProducer()"), PRODUCER) : new ParameterizedStaticMethod( MAP_FACTORY, typeParameters, CodeBlock.of("emptyMapProvider()"), DAGGER_PROVIDER); } /** * A static member select for an empty set factory. Calls {@link * dagger.internal.SetFactory#empty()}, {@link dagger.producers.internal.SetProducer#empty()}, or * {@link dagger.producers.internal.SetOfProducedProducer#empty()}, depending on the set bindings. */ static MemberSelect emptySetFactory(ContributionBinding binding) { return new ParameterizedStaticMethod( setFactoryClassName(binding), ImmutableList.of(SetType.from(binding.key()).elementType()), CodeBlock.of("empty()"), FACTORY); } /** * Returns a {@link MemberSelect} for the instance of a {@code create()} method on a factory with * no arguments. */ static MemberSelect factoryCreateNoArgumentMethod(Binding binding) { checkArgument( binding.bindingType().equals(BindingType.PROVISION), "Invalid binding type: %s", binding.bindingType()); checkArgument( binding.dependencies().isEmpty() && !binding.scope().isPresent(), "%s should have no dependencies and be unscoped to create a no argument factory.", binding); ClassName factoryName = generatedClassNameForBinding(binding); XType keyType = binding.key().type().xprocessing(); if (isDeclared(keyType)) { ImmutableList typeVariables = bindingTypeElementTypeVariableNames(binding); if (!typeVariables.isEmpty()) { ImmutableList typeArguments = ImmutableList.copyOf(keyType.getTypeArguments()); return new ParameterizedStaticMethod( factoryName, typeArguments, CodeBlock.of("create()"), FACTORY); } } return new StaticMethod(factoryName, CodeBlock.of("create()")); } private static final class StaticMethod extends MemberSelect { private final CodeBlock methodCodeBlock; StaticMethod(ClassName owningClass, CodeBlock methodCodeBlock) { super(owningClass, true); this.methodCodeBlock = checkNotNull(methodCodeBlock); } @Override CodeBlock getExpressionFor(ClassName usingClass) { return owningClass().equals(usingClass) ? methodCodeBlock : CodeBlock.of("$T.$L", owningClass(), methodCodeBlock); } } private static final class ParameterizedStaticMethod extends MemberSelect { private final ImmutableList typeParameters; private final CodeBlock methodCodeBlock; private final ClassName rawReturnType; ParameterizedStaticMethod( ClassName owningClass, ImmutableList typeParameters, CodeBlock methodCodeBlock, ClassName rawReturnType) { super(owningClass, true); this.typeParameters = typeParameters; this.methodCodeBlock = methodCodeBlock; this.rawReturnType = rawReturnType; } @Override CodeBlock getExpressionFor(ClassName usingClass) { boolean isAccessible = typeParameters.stream().allMatch(t -> isTypeAccessibleFrom(t, usingClass.packageName())); if (isAccessible) { return CodeBlock.of( "$T.<$L>$L", owningClass(), typeParameters.stream().map(CodeBlocks::type).collect(toParametersCodeBlock()), methodCodeBlock); } else { return CodeBlock.of("(($T) $T.$L)", rawReturnType, owningClass(), methodCodeBlock); } } } private StaticMemberSelects() {} }