1 /* 2 * Copyright (C) 2021 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal.codegen.writing; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 import static dagger.internal.codegen.binding.SourceFiles.bindingTypeElementTypeVariableNames; 22 import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding; 23 import static dagger.internal.codegen.binding.SourceFiles.setFactoryClassName; 24 import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock; 25 import static dagger.internal.codegen.javapoet.TypeNames.DAGGER_PROVIDER; 26 import static dagger.internal.codegen.javapoet.TypeNames.FACTORY; 27 import static dagger.internal.codegen.javapoet.TypeNames.MAP_FACTORY; 28 import static dagger.internal.codegen.javapoet.TypeNames.PRODUCER; 29 import static dagger.internal.codegen.javapoet.TypeNames.PRODUCERS; 30 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom; 31 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared; 32 33 import androidx.room.compiler.processing.XType; 34 import com.google.common.collect.ImmutableList; 35 import com.squareup.javapoet.ClassName; 36 import com.squareup.javapoet.CodeBlock; 37 import com.squareup.javapoet.TypeVariableName; 38 import dagger.internal.codegen.base.SetType; 39 import dagger.internal.codegen.binding.Binding; 40 import dagger.internal.codegen.binding.BindingType; 41 import dagger.internal.codegen.binding.ContributionBinding; 42 import dagger.internal.codegen.javapoet.CodeBlocks; 43 44 /** Helper class for static member select creation. */ 45 final class StaticMemberSelects { 46 /** A {@link MemberSelect} for a factory of an empty map. */ emptyMapFactory(Binding binding)47 static MemberSelect emptyMapFactory(Binding binding) { 48 BindingType bindingType = binding.bindingType(); 49 ImmutableList<XType> typeParameters = 50 ImmutableList.copyOf(binding.key().type().xprocessing().getTypeArguments()); 51 return bindingType.equals(BindingType.PRODUCTION) 52 ? new ParameterizedStaticMethod( 53 PRODUCERS, typeParameters, CodeBlock.of("emptyMapProducer()"), PRODUCER) 54 : new ParameterizedStaticMethod( 55 MAP_FACTORY, typeParameters, CodeBlock.of("emptyMapProvider()"), DAGGER_PROVIDER); 56 } 57 58 /** 59 * A static member select for an empty set factory. Calls {@link 60 * dagger.internal.SetFactory#empty()}, {@link dagger.producers.internal.SetProducer#empty()}, or 61 * {@link dagger.producers.internal.SetOfProducedProducer#empty()}, depending on the set bindings. 62 */ emptySetFactory(ContributionBinding binding)63 static MemberSelect emptySetFactory(ContributionBinding binding) { 64 return new ParameterizedStaticMethod( 65 setFactoryClassName(binding), 66 ImmutableList.of(SetType.from(binding.key()).elementType()), 67 CodeBlock.of("empty()"), 68 FACTORY); 69 } 70 71 /** 72 * Returns a {@link MemberSelect} for the instance of a {@code create()} method on a factory with 73 * no arguments. 74 */ factoryCreateNoArgumentMethod(Binding binding)75 static MemberSelect factoryCreateNoArgumentMethod(Binding binding) { 76 checkArgument( 77 binding.bindingType().equals(BindingType.PROVISION), 78 "Invalid binding type: %s", 79 binding.bindingType()); 80 checkArgument( 81 binding.dependencies().isEmpty() && !binding.scope().isPresent(), 82 "%s should have no dependencies and be unscoped to create a no argument factory.", 83 binding); 84 85 ClassName factoryName = generatedClassNameForBinding(binding); 86 XType keyType = binding.key().type().xprocessing(); 87 if (isDeclared(keyType)) { 88 ImmutableList<TypeVariableName> typeVariables = bindingTypeElementTypeVariableNames(binding); 89 if (!typeVariables.isEmpty()) { 90 ImmutableList<XType> typeArguments = ImmutableList.copyOf(keyType.getTypeArguments()); 91 return new ParameterizedStaticMethod( 92 factoryName, typeArguments, CodeBlock.of("create()"), FACTORY); 93 } 94 } 95 return new StaticMethod(factoryName, CodeBlock.of("create()")); 96 } 97 98 private static final class StaticMethod extends MemberSelect { 99 private final CodeBlock methodCodeBlock; 100 StaticMethod(ClassName owningClass, CodeBlock methodCodeBlock)101 StaticMethod(ClassName owningClass, CodeBlock methodCodeBlock) { 102 super(owningClass, true); 103 this.methodCodeBlock = checkNotNull(methodCodeBlock); 104 } 105 106 @Override getExpressionFor(ClassName usingClass)107 CodeBlock getExpressionFor(ClassName usingClass) { 108 return owningClass().equals(usingClass) 109 ? methodCodeBlock 110 : CodeBlock.of("$T.$L", owningClass(), methodCodeBlock); 111 } 112 } 113 114 private static final class ParameterizedStaticMethod extends MemberSelect { 115 private final ImmutableList<XType> typeParameters; 116 private final CodeBlock methodCodeBlock; 117 private final ClassName rawReturnType; 118 ParameterizedStaticMethod( ClassName owningClass, ImmutableList<XType> typeParameters, CodeBlock methodCodeBlock, ClassName rawReturnType)119 ParameterizedStaticMethod( 120 ClassName owningClass, 121 ImmutableList<XType> typeParameters, 122 CodeBlock methodCodeBlock, 123 ClassName rawReturnType) { 124 super(owningClass, true); 125 this.typeParameters = typeParameters; 126 this.methodCodeBlock = methodCodeBlock; 127 this.rawReturnType = rawReturnType; 128 } 129 130 @Override getExpressionFor(ClassName usingClass)131 CodeBlock getExpressionFor(ClassName usingClass) { 132 boolean isAccessible = 133 typeParameters.stream().allMatch(t -> isTypeAccessibleFrom(t, usingClass.packageName())); 134 135 if (isAccessible) { 136 return CodeBlock.of( 137 "$T.<$L>$L", 138 owningClass(), 139 typeParameters.stream().map(CodeBlocks::type).collect(toParametersCodeBlock()), 140 methodCodeBlock); 141 } else { 142 return CodeBlock.of("(($T) $T.$L)", rawReturnType, owningClass(), methodCodeBlock); 143 } 144 } 145 } 146 StaticMemberSelects()147 private StaticMemberSelects() {} 148 } 149