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