• 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.base;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
21 import static dagger.internal.codegen.javapoet.TypeNames.lazyOf;
22 import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
23 import static dagger.internal.codegen.javapoet.TypeNames.producedOf;
24 import static dagger.internal.codegen.javapoet.TypeNames.producerOf;
25 import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
26 import static dagger.internal.codegen.model.RequestKind.LAZY;
27 import static dagger.internal.codegen.model.RequestKind.PRODUCED;
28 import static dagger.internal.codegen.model.RequestKind.PRODUCER;
29 import static dagger.internal.codegen.model.RequestKind.PROVIDER;
30 import static dagger.internal.codegen.xprocessing.XProcessingEnvs.wrapType;
31 import static dagger.internal.codegen.xprocessing.XTypes.checkTypePresent;
32 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
33 import static dagger.internal.codegen.xprocessing.XTypes.isTypeOf;
34 import static dagger.internal.codegen.xprocessing.XTypes.unwrapType;
35 
36 import androidx.room.compiler.processing.XProcessingEnv;
37 import androidx.room.compiler.processing.XType;
38 import com.google.common.collect.ImmutableMap;
39 import com.squareup.javapoet.ClassName;
40 import com.squareup.javapoet.TypeName;
41 import dagger.internal.codegen.javapoet.TypeNames;
42 import dagger.internal.codegen.model.RequestKind;
43 
44 /** Utility methods for {@link RequestKind}s. */
45 public final class RequestKinds {
46   /** Returns the type of a request of this kind for a key with a given type. */
requestType( RequestKind requestKind, XType type, XProcessingEnv processingEnv)47   public static XType requestType(
48       RequestKind requestKind, XType type, XProcessingEnv processingEnv) {
49     switch (requestKind) {
50       case INSTANCE:
51         return type;
52 
53       case PROVIDER_OF_LAZY:
54         return wrapType(TypeNames.PROVIDER, requestType(LAZY, type, processingEnv), processingEnv);
55 
56       case FUTURE:
57         return wrapType(TypeNames.LISTENABLE_FUTURE, type, processingEnv);
58 
59       default:
60         return wrapType(frameworkClassName(requestKind), type, processingEnv);
61     }
62   }
63 
64   /** Returns the type of a request of this kind for a key with a given type. */
requestTypeName(RequestKind requestKind, TypeName keyType)65   public static TypeName requestTypeName(RequestKind requestKind, TypeName keyType) {
66     switch (requestKind) {
67       case INSTANCE:
68         return keyType;
69 
70       case PROVIDER:
71         return providerOf(keyType);
72 
73       case LAZY:
74         return lazyOf(keyType);
75 
76       case PROVIDER_OF_LAZY:
77         return providerOf(lazyOf(keyType));
78 
79       case PRODUCER:
80         return producerOf(keyType);
81 
82       case PRODUCED:
83         return producedOf(keyType);
84 
85       case FUTURE:
86         return listenableFutureOf(keyType);
87 
88       default:
89         throw new AssertionError(requestKind);
90     }
91   }
92 
93   private static final ImmutableMap<RequestKind, ClassName> FRAMEWORK_CLASSES =
94       ImmutableMap.of(
95           PROVIDER, TypeNames.PROVIDER,
96           LAZY, TypeNames.LAZY,
97           PRODUCER, TypeNames.PRODUCER,
98           PRODUCED, TypeNames.PRODUCED);
99 
100   /** Returns the {@link RequestKind} that matches the wrapping types (if any) of {@code type}. */
getRequestKind(XType type)101   public static RequestKind getRequestKind(XType type) {
102     checkTypePresent(type);
103     if (!isDeclared(type) || type.getTypeArguments().isEmpty()) {
104       // If the type is not a declared type (i.e. class or interface) with type arguments, then we
105       // know it can't be a parameterized type of one of the framework classes, so return INSTANCE.
106       return RequestKind.INSTANCE;
107     }
108 
109     if (isTypeOf(type, TypeNames.PROVIDER) && isTypeOf(unwrapType(type), TypeNames.LAZY)) {
110       return RequestKind.PROVIDER_OF_LAZY;
111     }
112 
113     return FRAMEWORK_CLASSES.keySet().stream()
114         .filter(kind -> isTypeOf(type, FRAMEWORK_CLASSES.get(kind)))
115         .collect(toOptional())
116         .orElse(RequestKind.INSTANCE);
117   }
118 
119   /**
120    * Unwraps the framework class(es) of {@code requestKind} from {@code type}. If {@code
121    * requestKind} is {@link RequestKind#INSTANCE}, this acts as an identity function.
122    *
123    * @throws TypeNotPresentException if {@code type} is an {@link javax.lang.model.type.ErrorType},
124    *     which may mean that the type will be generated in a later round of processing
125    * @throws IllegalArgumentException if {@code type} is not wrapped with {@code requestKind}'s
126    *     framework class(es).
127    */
extractKeyType(XType type)128   public static XType extractKeyType(XType type) {
129     return extractKeyType(getRequestKind(type), type);
130   }
131 
extractKeyType(RequestKind requestKind, XType type)132   private static XType extractKeyType(RequestKind requestKind, XType type) {
133     switch (requestKind) {
134       case INSTANCE:
135         return type;
136       case PROVIDER_OF_LAZY:
137         return extractKeyType(LAZY, extractKeyType(PROVIDER, type));
138       default:
139         return unwrapType(type);
140     }
141   }
142 
143   /**
144    * A dagger- or {@code javax.inject}-defined class for {@code requestKind} that that can wrap
145    * another type but share the same {@link dagger.internal.codegen.model.Key}.
146    *
147    * <p>For example, {@code Provider<String>} and {@code Lazy<String>} can both be requested if a
148    * key exists for {@code String}; they all share the same key.
149    *
150    * <p>This concept is not well defined and should probably be removed and inlined into the cases
151    * that need it. For example, {@link RequestKind#PROVIDER_OF_LAZY} has <em>2</em> wrapping
152    * classes, and {@link RequestKind#FUTURE} is wrapped with a {@link ListenableFuture}, but for
153    * historical/implementation reasons has not had an associated framework class.
154    */
frameworkClassName(RequestKind requestKind)155   public static ClassName frameworkClassName(RequestKind requestKind) {
156     checkArgument(
157         FRAMEWORK_CLASSES.containsKey(requestKind), "no framework class for %s", requestKind);
158     return FRAMEWORK_CLASSES.get(requestKind);
159   }
160 
161   /**
162    * Returns {@code true} if requests for {@code requestKind} can be satisfied by a production
163    * binding.
164    */
canBeSatisfiedByProductionBinding(RequestKind requestKind)165   public static boolean canBeSatisfiedByProductionBinding(RequestKind requestKind) {
166     switch (requestKind) {
167       case INSTANCE:
168       case PROVIDER:
169       case LAZY:
170       case PROVIDER_OF_LAZY:
171       case MEMBERS_INJECTION:
172         return false;
173       case PRODUCER:
174       case PRODUCED:
175       case FUTURE:
176         return true;
177     }
178     throw new AssertionError();
179   }
180 
RequestKinds()181   private RequestKinds() {}
182 }
183