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 androidx.room.compiler.codegen.compat.XConverters.toJavaPoet; 20 import static com.google.common.base.Preconditions.checkArgument; 21 import static com.google.common.base.Preconditions.checkNotNull; 22 import static dagger.internal.codegen.binding.SourceFiles.bindingTypeElementTypeVariableNames; 23 import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding; 24 import static dagger.internal.codegen.binding.SourceFiles.setFactoryClassName; 25 import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock; 26 import static dagger.internal.codegen.javapoet.TypeNames.DAGGER_PROVIDER; 27 import static dagger.internal.codegen.javapoet.TypeNames.FACTORY; 28 import static dagger.internal.codegen.javapoet.TypeNames.MAP_FACTORY; 29 import static dagger.internal.codegen.javapoet.TypeNames.PRODUCER; 30 import static dagger.internal.codegen.javapoet.TypeNames.PRODUCERS; 31 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom; 32 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared; 33 34 import androidx.room.compiler.processing.XType; 35 import com.google.common.collect.ImmutableList; 36 import com.squareup.javapoet.ClassName; 37 import com.squareup.javapoet.CodeBlock; 38 import com.squareup.javapoet.TypeVariableName; 39 import dagger.internal.codegen.base.SetType; 40 import dagger.internal.codegen.binding.Binding; 41 import dagger.internal.codegen.binding.BindingType; 42 import dagger.internal.codegen.binding.MultiboundMapBinding; 43 import dagger.internal.codegen.binding.MultiboundSetBinding; 44 import dagger.internal.codegen.javapoet.CodeBlocks; 45 46 /** Helper class for static member select creation. */ 47 final class StaticMemberSelects { 48 /** A {@link MemberSelect} for a factory of an empty map. */ emptyMapFactory(MultiboundMapBinding binding)49 static MemberSelect emptyMapFactory(MultiboundMapBinding binding) { 50 BindingType bindingType = binding.bindingType(); 51 ImmutableList<XType> typeParameters = 52 ImmutableList.copyOf(binding.key().type().xprocessing().getTypeArguments()); 53 return bindingType.equals(BindingType.PRODUCTION) 54 ? new ParameterizedStaticMethod( 55 PRODUCERS, typeParameters, CodeBlock.of("emptyMapProducer()"), PRODUCER) 56 : new ParameterizedStaticMethod( 57 MAP_FACTORY, typeParameters, CodeBlock.of("emptyMapProvider()"), DAGGER_PROVIDER); 58 } 59 60 /** 61 * A static member select for an empty set factory. Calls {@link 62 * dagger.internal.SetFactory#empty()}, {@link dagger.producers.internal.SetProducer#empty()}, or 63 * {@link dagger.producers.internal.SetOfProducedProducer#empty()}, depending on the set bindings. 64 */ emptySetFactory(MultiboundSetBinding binding)65 static MemberSelect emptySetFactory(MultiboundSetBinding binding) { 66 return new ParameterizedStaticMethod( 67 toJavaPoet(setFactoryClassName(binding)), 68 ImmutableList.of(SetType.from(binding.key()).elementType()), 69 CodeBlock.of("empty()"), 70 FACTORY); 71 } 72 73 /** 74 * Returns a {@link MemberSelect} for the instance of a {@code create()} method on a factory with 75 * no arguments. 76 */ factoryCreateNoArgumentMethod(Binding binding)77 static MemberSelect factoryCreateNoArgumentMethod(Binding binding) { 78 checkArgument( 79 binding.bindingType().equals(BindingType.PROVISION), 80 "Invalid binding type: %s", 81 binding.bindingType()); 82 checkArgument( 83 binding.dependencies().isEmpty() && !binding.scope().isPresent(), 84 "%s should have no dependencies and be unscoped to create a no argument factory.", 85 binding); 86 87 ClassName factoryName = toJavaPoet(generatedClassNameForBinding(binding)); 88 XType keyType = binding.key().type().xprocessing(); 89 if (isDeclared(keyType)) { 90 ImmutableList<TypeVariableName> typeVariables = bindingTypeElementTypeVariableNames(binding); 91 if (!typeVariables.isEmpty()) { 92 ImmutableList<XType> typeArguments = ImmutableList.copyOf(keyType.getTypeArguments()); 93 return new ParameterizedStaticMethod( 94 factoryName, typeArguments, CodeBlock.of("create()"), FACTORY); 95 } 96 } 97 return new StaticMethod(factoryName, CodeBlock.of("create()")); 98 } 99 100 private static final class StaticMethod extends MemberSelect { 101 private final CodeBlock methodCodeBlock; 102 StaticMethod(ClassName owningClass, CodeBlock methodCodeBlock)103 StaticMethod(ClassName owningClass, CodeBlock methodCodeBlock) { 104 super(owningClass, true); 105 this.methodCodeBlock = checkNotNull(methodCodeBlock); 106 } 107 108 @Override getExpressionFor(ClassName usingClass)109 CodeBlock getExpressionFor(ClassName usingClass) { 110 return owningClass().equals(usingClass) 111 ? methodCodeBlock 112 : CodeBlock.of("$T.$L", owningClass(), methodCodeBlock); 113 } 114 } 115 116 private static final class ParameterizedStaticMethod extends MemberSelect { 117 private final ImmutableList<XType> typeParameters; 118 private final CodeBlock methodCodeBlock; 119 private final ClassName rawReturnType; 120 ParameterizedStaticMethod( ClassName owningClass, ImmutableList<XType> typeParameters, CodeBlock methodCodeBlock, ClassName rawReturnType)121 ParameterizedStaticMethod( 122 ClassName owningClass, 123 ImmutableList<XType> typeParameters, 124 CodeBlock methodCodeBlock, 125 ClassName rawReturnType) { 126 super(owningClass, true); 127 this.typeParameters = typeParameters; 128 this.methodCodeBlock = methodCodeBlock; 129 this.rawReturnType = rawReturnType; 130 } 131 132 @Override getExpressionFor(ClassName usingClass)133 CodeBlock getExpressionFor(ClassName usingClass) { 134 boolean isAccessible = 135 typeParameters.stream().allMatch(t -> isTypeAccessibleFrom(t, usingClass.packageName())); 136 137 if (isAccessible) { 138 return CodeBlock.of( 139 "$T.<$L>$L", 140 owningClass(), 141 typeParameters.stream().map(CodeBlocks::type).collect(toParametersCodeBlock()), 142 methodCodeBlock); 143 } else { 144 return CodeBlock.of("(($T) $T.$L)", rawReturnType, owningClass(), methodCodeBlock); 145 } 146 } 147 } 148 StaticMemberSelects()149 private StaticMemberSelects() {} 150 } 151