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