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