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; 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 com.google.common.collect.Iterables.getOnlyElement; 24 import static dagger.internal.codegen.javapoet.TypeNames.lazyOf; 25 import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf; 26 import static dagger.internal.codegen.javapoet.TypeNames.producedOf; 27 import static dagger.internal.codegen.javapoet.TypeNames.producerOf; 28 import static dagger.internal.codegen.javapoet.TypeNames.providerOf; 29 import static dagger.internal.codegen.langmodel.DaggerTypes.checkTypePresent; 30 import static dagger.model.RequestKind.INSTANCE; 31 import static dagger.model.RequestKind.LAZY; 32 import static dagger.model.RequestKind.PRODUCED; 33 import static dagger.model.RequestKind.PRODUCER; 34 import static dagger.model.RequestKind.PROVIDER; 35 import static dagger.model.RequestKind.PROVIDER_OF_LAZY; 36 37 import com.google.auto.common.MoreTypes; 38 import com.google.common.collect.ImmutableMap; 39 import com.google.common.util.concurrent.ListenableFuture; 40 import com.squareup.javapoet.TypeName; 41 import dagger.Lazy; 42 import dagger.internal.codegen.langmodel.DaggerTypes; 43 import dagger.model.RequestKind; 44 import dagger.producers.Produced; 45 import dagger.producers.Producer; 46 import javax.inject.Provider; 47 import javax.lang.model.type.TypeMirror; 48 49 /** Utility methods for {@link RequestKind}s. */ 50 final class RequestKinds { 51 /** Returns the type of a request of this kind for a key with a given type. */ requestType(RequestKind requestKind, TypeMirror type, DaggerTypes types)52 static TypeMirror requestType(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 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 static RequestKind getRequestKind(TypeMirror type) { 106 checkTypePresent(type); 107 for (RequestKind kind : FRAMEWORK_CLASSES.keySet()) { 108 if (matchesKind(kind, type)) { 109 if (kind.equals(PROVIDER) && matchesKind(LAZY, extractKeyType(kind, type))) { 110 return PROVIDER_OF_LAZY; 111 } 112 return kind; 113 } 114 } 115 return INSTANCE; 116 } 117 118 /** 119 * Returns {@code true} if {@code type} is a parameterized type of {@code kind}'s {@link 120 * #frameworkClass(RequestKind) framework class}. 121 */ matchesKind(RequestKind kind, TypeMirror type)122 private static boolean matchesKind(RequestKind kind, TypeMirror type) { 123 return isType(type) 124 && isTypeOf(frameworkClass(kind), type) 125 && !asDeclared(type).getTypeArguments().isEmpty(); 126 } 127 128 /** 129 * Unwraps the framework class(es) of {@code requestKind} from {@code type}. If {@code 130 * requestKind} is {@link RequestKind#INSTANCE}, this acts as an identity function. 131 * 132 * @throws TypeNotPresentException if {@code type} is an {@link javax.lang.model.type.ErrorType}, 133 * which may mean that the type will be generated in a later round of processing 134 * @throws IllegalArgumentException if {@code type} is not wrapped with {@code requestKind}'s 135 * framework class(es). 136 */ extractKeyType(RequestKind requestKind, TypeMirror type)137 static TypeMirror extractKeyType(RequestKind requestKind, TypeMirror type) { 138 checkTypePresent(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) && isTypeOf(frameworkClass(requestKind), type)); 146 return getOnlyElement(MoreTypes.asDeclared(type).getTypeArguments()); 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 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 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 188 /** 189 * Returns true if {@code requestKind} is always derived from a {@link RequestKind#PROVIDER} 190 * instance. 191 */ isDerivedFromProvider(RequestKind requestKind)192 static boolean isDerivedFromProvider(RequestKind requestKind) { 193 switch (requestKind) { 194 case LAZY: 195 case PROVIDER_OF_LAZY: 196 return true; 197 default: 198 return false; 199 } 200 } 201 RequestKinds()202 private RequestKinds() {} 203 } 204