• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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