1 /* 2 * Copyright (C) 2014 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.binding; 18 19 import static androidx.room.compiler.processing.XElementKt.isConstructor; 20 import static androidx.room.compiler.processing.XElementKt.isMethod; 21 import static androidx.room.compiler.processing.XElementKt.isMethodParameter; 22 import static androidx.room.compiler.processing.XElementKt.isTypeElement; 23 import static dagger.internal.codegen.model.BindingKind.MEMBERS_INJECTOR; 24 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName; 25 26 import androidx.room.compiler.processing.XElement; 27 import com.google.auto.value.AutoValue; 28 import com.google.common.base.CaseFormat; 29 import com.google.common.base.Preconditions; 30 import com.squareup.javapoet.ClassName; 31 import com.squareup.javapoet.ParameterizedTypeName; 32 import com.squareup.javapoet.TypeName; 33 import dagger.internal.codegen.base.MapType; 34 import dagger.internal.codegen.javapoet.TypeNames; 35 import java.util.Optional; 36 37 /** 38 * A value object that represents a field in the generated Component class. 39 * 40 * <p>Examples: 41 * 42 * <ul> 43 * <li>{@code Provider<String>} 44 * <li>{@code Producer<Widget>} 45 * <li>{@code Provider<Map<SomeMapKey, MapValue>>}. 46 * </ul> 47 */ 48 @AutoValue 49 public abstract class FrameworkField { 50 51 /** 52 * Creates a framework field. 53 * 54 * @param fieldType the type of the framework field (e.g., {@code Provider<Foo>}). 55 * @param fieldName the base name of the field. The name of the raw type of the field will be 56 * added as a suffix 57 */ create(TypeName fieldType, String fieldName)58 public static FrameworkField create(TypeName fieldType, String fieldName) { 59 Preconditions.checkState( 60 fieldType instanceof ClassName || fieldType instanceof ParameterizedTypeName, 61 "Can only create a field with a class name or parameterized type name"); 62 String suffix = ((ClassName) TypeNames.rawTypeName(fieldType)).simpleName(); 63 return new AutoValue_FrameworkField( 64 fieldType, 65 fieldName.endsWith(suffix) ? fieldName : fieldName + suffix); 66 } 67 68 /** 69 * A framework field for a {@link ContributionBinding}. 70 * 71 * @param frameworkClass if present, the field will use this framework class instead of the normal 72 * one for the binding's type. 73 */ forBinding( ContributionBinding binding, Optional<ClassName> frameworkClassName)74 public static FrameworkField forBinding( 75 ContributionBinding binding, Optional<ClassName> frameworkClassName) { 76 return create( 77 fieldType(binding, frameworkClassName.orElse(binding.frameworkType().frameworkClassName())), 78 frameworkFieldName(binding)); 79 } 80 fieldType(ContributionBinding binding, ClassName frameworkClassName)81 private static TypeName fieldType(ContributionBinding binding, ClassName frameworkClassName) { 82 if (binding.contributionType().isMultibinding()) { 83 return ParameterizedTypeName.get(frameworkClassName, binding.contributedType().getTypeName()); 84 } 85 86 // If the binding key type is a Map<K, Provider<V>>, we need to change field type to a raw 87 // type. This is because it actually needs to be changed to Map<K, dagger.internal.Provider<V>>, 88 // but that gets into assignment issues when the field is passed to methods that expect 89 // Map<K, javax.inject.Provider<V>>. We could add casts everywhere, but it is easier to just 90 // make the field itself a raw type. 91 if (MapType.isMapOfProvider(binding.contributedType())) { 92 return frameworkClassName; 93 } 94 95 return ParameterizedTypeName.get( 96 frameworkClassName, binding.key().type().xprocessing().getTypeName()); 97 } 98 frameworkFieldName(ContributionBinding binding)99 private static String frameworkFieldName(ContributionBinding binding) { 100 if (binding.bindingElement().isPresent()) { 101 String name = bindingElementName(binding.bindingElement().get()); 102 return binding.kind().equals(MEMBERS_INJECTOR) ? name + "MembersInjector" : name; 103 } 104 return KeyVariableNamer.name(binding.key()); 105 } 106 bindingElementName(XElement bindingElement)107 private static String bindingElementName(XElement bindingElement) { 108 if (isConstructor(bindingElement)) { 109 return bindingElementName(bindingElement.getEnclosingElement()); 110 } else if (isMethod(bindingElement)) { 111 return getSimpleName(bindingElement); 112 } else if (isTypeElement(bindingElement)) { 113 return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, getSimpleName(bindingElement)); 114 } else if (isMethodParameter(bindingElement)) { 115 return getSimpleName(bindingElement); 116 } else { 117 throw new IllegalArgumentException("Unexpected binding " + bindingElement); 118 } 119 } 120 type()121 public abstract TypeName type(); 122 name()123 public abstract String name(); 124 } 125